如何测量绘制在图像上的线的长度? C#

时间:2018-07-02 14:34:06

标签: c# wpf

我想写一个可以测量在显微镜下检查过的标本碎片的应用程序。我认为最好的方法是捕获图像并在样本的选定部分上绘制,然后以像素为单位计算绘制的线的值(然后再将该值转换为适当的单位)。

是否有什么东西可以帮助解决已经实施的问题,或者有什么工具/软件包或可以进行此类计算的东西?

如果其他编程语言允许以更简单的方式或以某种方式解决该问题,我也将乐于学习其他编程语言的解决方案。

1 个答案:

答案 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值即可。

让我们看看我们在做什么:

enter image description here

更新

要想有机会创建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内。现在我们可以在添加细分时进行缩放和滚动:

enter image description here