使用 Cyotek.ImageBox 在 C# winform 上用鼠标绘制轮廓
draw contours with mouse on C# winform with Cyotek.ImageBox
我想要的功能是让用户用鼠标在图像上画出几个物体的轮廓。在图像上显示轮廓,并能够导出轮廓数据。我在这里使用 cyotek.imagebox https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox,一个用于显示图像的自定义控件。
这是我的方法。
private List<List<Point>> contours = new List<List<Point>>(); //a list to store all contours
private List<Point> contour_temp= new List<Point>(); //a list to store the contour the user is drawing
private bool mousedown // a flag that change when mouse up/mouse down event is triggered
private void imageBox_MouseDown(object sender, MouseEventArgs e)
{
mousedown = true;
}
private void imageBox_MouseUp(object sender, MouseEventArgs e)
{
mousedown = false;
}
private void imageBox_MouseMove(object sender, MouseEventArgs e)
{
//drawing occurs when mouse is down, a contour is finish drawing when mouse is up
// when a contour finish drawing. it will be added to the contours list and the contour temp will be clear
if (draw_on) // a flag for this function to be active
{
if (contour_temp.Count > 0)
{
if (mousedown)
{
if (imageBox.IsPointInImage(e.Location)) //function cyotek.imagebox provides , it translate the mouse location to pixel location
{
Point p = this.imageBox.PointToImage(e.Location);
if (p != contour_temp[contour_temp.Count - 1])
contour_temp.Add(p);
}
}
else
{
if (contour_temp.Count > 2)
{
contours.Add(contour_temp);
contour_temp.Clear();
}
}
}
else
{
if (mousedown)
{
contour_temp = new List<Point>();
Point p = this.imageBox.PointToImage(e.Location);
contour_temp.Add(p);
}
}
imageBox.Invalidate();
}
}
private void imageBox_Paint(object sender, PaintEventArgs e)
{
//at the painting function, always paint all the contours stored
//if mouse is down, print the temporary contour the user is currently drawing
foreach (List<Point> contour in contours)
{
Point p0 = contour[0];
foreach (Point p1 in contour)
{
e.Graphics.DrawLine(new Pen(Color.Red), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1));//Cyotek.ImageBox provided, get the correct point even after zooming
p0 = p1;
} //draw all contours
}
if(draw_on && mousedown && contour_temp.Count>0)
{
Point p0 = contour_temp[0];
foreach (Point p1 in contour_temp)
{
e.Graphics.DrawLine(new Pen(Color.Green), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1));
p0 = p1;
}// draw the contour user is drawing
}
}
绘图 contour_temp 部分工作正常。但是当轮廓完成绘制时程序崩溃,在 'mscorlib.dll' 上抛出 System.ArgumentOutOfRangeException,
经过一些测试,我发现绘画功能中绘制所有轮廓的东西
在某种程度上是错误的。我对异常做了一些研究,说索引
列表超出范围。但是当我使用 "foreach" 时怎么会发生呢?
问题是由于这里的这些行造成的:
contours.Add(contour_temp);
contour_temp.Clear();
在第一种情况下,您要将临时点列表 contour_temp
添加到主列表 contours
,但由于 List<T>
是 reference type,您我们没有添加点的副本,而是添加了对 same 列表的引用 - 然后您将其清除,导致 contours
实际上只包含空列表。但是您的绘画例程期望这些列表始终包含至少一个点,因此在尝试绘画时 ArgumentOutOfRangeException
因为您试图在不存在任何项目的列表中获取第一项。
我通过将第一行更改为
来修复您的演示程序
contours.Add(new List<Point>(contour_temp));
这将创建列表的副本,从而保留点。我用这个修正测试了你的演示程序,它工作正常。
希望这对您有所帮助。感谢您使用 ImageBox!
我想要的功能是让用户用鼠标在图像上画出几个物体的轮廓。在图像上显示轮廓,并能够导出轮廓数据。我在这里使用 cyotek.imagebox https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox,一个用于显示图像的自定义控件。 这是我的方法。
private List<List<Point>> contours = new List<List<Point>>(); //a list to store all contours
private List<Point> contour_temp= new List<Point>(); //a list to store the contour the user is drawing
private bool mousedown // a flag that change when mouse up/mouse down event is triggered
private void imageBox_MouseDown(object sender, MouseEventArgs e)
{
mousedown = true;
}
private void imageBox_MouseUp(object sender, MouseEventArgs e)
{
mousedown = false;
}
private void imageBox_MouseMove(object sender, MouseEventArgs e)
{
//drawing occurs when mouse is down, a contour is finish drawing when mouse is up
// when a contour finish drawing. it will be added to the contours list and the contour temp will be clear
if (draw_on) // a flag for this function to be active
{
if (contour_temp.Count > 0)
{
if (mousedown)
{
if (imageBox.IsPointInImage(e.Location)) //function cyotek.imagebox provides , it translate the mouse location to pixel location
{
Point p = this.imageBox.PointToImage(e.Location);
if (p != contour_temp[contour_temp.Count - 1])
contour_temp.Add(p);
}
}
else
{
if (contour_temp.Count > 2)
{
contours.Add(contour_temp);
contour_temp.Clear();
}
}
}
else
{
if (mousedown)
{
contour_temp = new List<Point>();
Point p = this.imageBox.PointToImage(e.Location);
contour_temp.Add(p);
}
}
imageBox.Invalidate();
}
}
private void imageBox_Paint(object sender, PaintEventArgs e)
{
//at the painting function, always paint all the contours stored
//if mouse is down, print the temporary contour the user is currently drawing
foreach (List<Point> contour in contours)
{
Point p0 = contour[0];
foreach (Point p1 in contour)
{
e.Graphics.DrawLine(new Pen(Color.Red), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1));//Cyotek.ImageBox provided, get the correct point even after zooming
p0 = p1;
} //draw all contours
}
if(draw_on && mousedown && contour_temp.Count>0)
{
Point p0 = contour_temp[0];
foreach (Point p1 in contour_temp)
{
e.Graphics.DrawLine(new Pen(Color.Green), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1));
p0 = p1;
}// draw the contour user is drawing
}
}
绘图 contour_temp 部分工作正常。但是当轮廓完成绘制时程序崩溃,在 'mscorlib.dll' 上抛出 System.ArgumentOutOfRangeException, 经过一些测试,我发现绘画功能中绘制所有轮廓的东西 在某种程度上是错误的。我对异常做了一些研究,说索引 列表超出范围。但是当我使用 "foreach" 时怎么会发生呢?
问题是由于这里的这些行造成的:
contours.Add(contour_temp);
contour_temp.Clear();
在第一种情况下,您要将临时点列表 contour_temp
添加到主列表 contours
,但由于 List<T>
是 reference type,您我们没有添加点的副本,而是添加了对 same 列表的引用 - 然后您将其清除,导致 contours
实际上只包含空列表。但是您的绘画例程期望这些列表始终包含至少一个点,因此在尝试绘画时 ArgumentOutOfRangeException
因为您试图在不存在任何项目的列表中获取第一项。
我通过将第一行更改为
来修复您的演示程序contours.Add(new List<Point>(contour_temp));
这将创建列表的副本,从而保留点。我用这个修正测试了你的演示程序,它工作正常。
希望这对您有所帮助。感谢您使用 ImageBox!