我想写一个可以测量在显微镜下检查过的标本碎片的应用程序。我认为最好的方法是捕获图像并在样本的选定部分上绘制,然后以像素为单位计算绘制的线的值(然后再将该值转换为适当的单位)。
是否有什么东西可以帮助解决已经实施的问题,或者有什么工具/软件包或可以进行此类计算的东西?
如果其他编程语言允许以更简单的方式或以某种方式解决该问题,我也将乐于学习其他编程语言的解决方案。
答案 0 :(得分:1)
这是测量winforms中绘制到图像上的分段线的非常基本的示例。
它使用PictureBox
来显示图像,使用Label
来显示当前结果,并且很好地添加了两个Buttons
以清除所有点并撤消/删除最后一个点一个。
我收集到List<Point>
中的像素位置:
List<Point> points = new List<Point>();
两个编辑按钮非常简单:
private void btn_Clear_Click(object sender, EventArgs e)
{
points.Clear();
pictureBox1.Invalidate();
show_Length();
}
private void btn_Undo_Click(object sender, EventArgs e)
{
if (points.Any())points.Remove(points.Last());
pictureBox1.Invalidate();
show_Length();
}
请注意,每当点集合发生变化时,如何通过使图像无效来触发Paint
事件。
其余代码也很简单;我调用一个函数来计算和显示所有段长度的总和。请注意,我至少需要两个点,然后才能显示第一行。
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
points.Add(e.Location);
pictureBox1.Invalidate();
show_Length();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (points.Count > 1) e.Graphics.DrawLines(Pens.Red, points.ToArray());
}
void show_Length()
{
lbl_len.Text = (pointsF.Count) + " point(s), no segments. " ;
if (!(points.Count > 1)) return;
double len = 0;
for (int i = 1; i < points.Count; i++)
{
len += Math.Sqrt((points[i-1].X - points[i].X) * (points[i-1].X - points[i].X)
+ (points[i-1].Y - points[i].Y) * (points[i-1].Y - points[i].Y));
}
lbl_len.Text = (points.Count-1) + " segments, " + (int) len + " pixels";
}
一些注意事项:
图像显示时没有任何缩放。 PictureBox
具有SizeMode
属性,可简化缩放显示。在这种情况下,我建议不要存储鼠标的直接像素位置,而应存储“未缩放”的值,并使用“已缩放”的值列表进行显示。这样,您可以放大和缩小,但仍将点固定在正确的位置。
为此,您应该使用List<PointF>
来保持精度。
缩放时通过放大PictureBox
,也许在将其嵌套在Panel
中之后,请确保将宽高比保持等于Image
的宽高比,或者确保进行full calculation的宽高比左侧或顶部的多余空间;在SizeMode.Normal
中,图片将始终与 TopLeft 齐平放置,但在其他模式下,图片并不总是如此。
对于实际距离(即物理距离)的计算,只需除以实际dpi值即可。
让我们看看我们在做什么:
更新:
要想有机会创建Clos拟合和更高的精度,我们显然需要放大图像。
以下是必要的更改:
我们添加了一个“浮点数”列表:
List<PointF> pointsF = new List<PointF>();
并使用它在鼠标向下存储未缩放的鼠标位置:
pointsF.Add( scaled( e.Location, false));
我们用points
替换了pointsF
的所有其他出现。
Paint
事件始终会计算缩放到当前缩放级别的点:
if (pointsF.Count > 1)
{
points = pointsF.Select(x => Point.Round(scaled(x, true))).ToList();
e.Graphics.DrawLines(Pens.Red, points.ToArray());
}
进行缩放的函数如下:
PointF scaled(PointF p, bool scaled)
{
float z = scaled ? 1f * zoom : 1f / zoom;
return new PointF(p.X * z, p.Y * z);
}
它使用类级别变量float zoom = 1f;
,该变量在跟踪栏的Clientsize
事件中与图片框的Scroll
一起设置:
private void trackBar1_Scroll(object sender, EventArgs e)
{
List<float> zooms = new List<float>()
{ 0.1f, 0.2f, 0.5f, 0.75f, 1f, 2, 3, 4, 6, 8, 10};
zoom = zooms[trackBar1.Value];
int w = (int)(pictureBox2.Image.Width * zoom);
int h = (int)(pictureBox2.Image.Height * zoom);
pictureBox2.ClientSize = new Size(w, h);
lbl_zoom.Text = "zoom: " + (zoom*100).ToString("0.0");
}
图片框嵌套在Panel
开启的AutoScroll
内。现在我们可以在添加细分时进行缩放和滚动: