Emgu CV - 使用鼠标选择 ROI(感兴趣区域) - C#





5.00/5 (3投票s)
Emgu CV - 使用鼠标在尺寸不相同的图像中选择 ROI(感兴趣区域)。本技巧使用 Emgu CV 框架和 C# .NET 语言编写。
引言
本技巧介绍了一种使用鼠标在尺寸不相同的相同图像上选择 ROI(感兴趣区域)的方法。该产品基于 C# .NET 语言的 Emgu CV 框架开发。之所以创建本技巧,是因为许多开发者在使用 ImageBox
或 PictureBox
渲染和操作图像中的感兴趣区域时感到困惑,因此我决定准备一个技巧,让读者了解如何在输入图像和输出图像尺寸不相同的情况下选择 ROI。希望本技巧对您有所帮助。请分享!祝您阅读愉快,一切顺利。
背景
为了更好地理解该产品的理由,请阅读 Emgu CV 论坛中以下帖子。
- http://www.emgu.com/forum/viewtopic.php?f=7&t=2861
- http://www.emgu.com/forum/viewtopic.php?f=7&t=3683
- http://www.emgu.com/forum/viewtopic.php?f=6&t=3258
- http://www.emgu.com/forum/viewtopic.php?f=3&t=121
- http://www.emgu.com/forum/viewtopic.php?f=7&t=2857
- http://csharphelper.com/blog/2014/10/select-parts-of-a-scaled-image-picturebox-different-sizemode-values-c/
Using the Code
以下是返回图像实际坐标的主要函数。此函数对于通过鼠标实现 ROI 选择至关重要,并且在 csharphelper
网站上找到。
/// <summary>
/// Convert the coordinates for the image's SizeMode.
/// </summary>
/// http://csharphelper.com/blog/2014/10/select-parts-of-a-scaled-image-picturebox-different-sizemode-values-c/</a>
/// http://csharphelper.com/blog/2014/10/select-parts-of-a-scaled-image-picturebox-different-sizemode-values-c/</a>
/// <param name="pic"></param>
/// <param name="X0">out X coordinate</param>
/// <param name="Y0">out Y coordinate</param>
/// <param name="x">atual coordinate</param>
/// <param name="y">atual coordinate</param>
public static void ConvertCoordinates(PictureBox pic,
out int X0, out int Y0, int x, int y)
{
int pic_hgt = pic.ClientSize.Height;
int pic_wid = pic.ClientSize.Width;
int img_hgt = pic.Image.Height;
int img_wid = pic.Image.Width;
X0 = x;
Y0 = y;
switch (pic.SizeMode)
{
case PictureBoxSizeMode.AutoSize:
case PictureBoxSizeMode.Normal:
// These are okay. Leave them alone.
break;
case PictureBoxSizeMode.CenterImage:
X0 = x - (pic_wid - img_wid) / 2;
Y0 = y - (pic_hgt - img_hgt) / 2;
break;
case PictureBoxSizeMode.StretchImage:
X0 = (int)(img_wid * x / (float)pic_wid);
Y0 = (int)(img_hgt * y / (float)pic_hgt);
break;
case PictureBoxSizeMode.Zoom:
float pic_aspect = pic_wid / (float)pic_hgt;
float img_aspect = img_wid / (float)img_wid;
if (pic_aspect > img_aspect)
{
// The PictureBox is wider/shorter than the image.
Y0 = (int)(img_hgt * y / (float)pic_hgt);
// The image fills the height of the PictureBox.
// Get its width.
float scaled_width = img_wid * pic_hgt / img_hgt;
float dx = (pic_wid - scaled_width) / 2;
X0 = (int)((x - dx) * img_hgt / (float)pic_hgt);
}
else
{
// The PictureBox is taller/thinner than the image.
X0 = (int)(img_wid * x / (float)pic_wid);
// The image fills the height of the PictureBox.
// Get its height.
float scaled_height = img_hgt * pic_wid / img_wid;
float dy = (pic_hgt - scaled_height) / 2;
Y0 = (int)((y - dy) * img_wid / pic_wid);
}
break;
}
}
将以下事件分配给表单的事件。
#region EVENTOS PICTURE BOX | DEFINIÇÃO DE ROI
private Point RectStartPoint;
private Rectangle Rect = new Rectangle();
private Rectangle RealImageRect = new Rectangle();
private Brush selectionBrush = new SolidBrush(Color.FromArgb(128, 64, 64, 64));
private int thickness = 3;
/// <summary>
/// Start Rectangle
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Determine the initial rectangle coordinates...
RectStartPoint = e.Location;
Invalidate();
}
/// <summary>
/// Draw Rectangle
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
#region SETS COORDINATES AT INPUT IMAGE BOX
int X0, Y0;
Utilities.ConvertCoordinates(imageBoxInput, out X0, out Y0, e.X, e.Y);
labelPostionXY.Text = "Last Position: X:" + X0 + " Y:" + Y0;
//Coordinates at input picture box
if (e.Button != MouseButtons.Left)
return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RectStartPoint.X, tempEndPoint.X),
Math.Min(RectStartPoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RectStartPoint.X - tempEndPoint.X),
Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
#endregion
#region SETS COORDINATES AT REAL IMAGE
//Coordinates at real image - Create ROI
Utilities.ConvertCoordinates(imageBoxInput, out X0, out Y0,
RectStartPoint.X, RectStartPoint.Y);
int X1, Y1;
Utilities.ConvertCoordinates(imageBoxInput, out X1, out Y1, tempEndPoint.X, tempEndPoint.Y);
RealImageRect.Location = new Point(
Math.Min(X0, X1),
Math.Min(Y0, Y1));
RealImageRect.Size = new Size(
Math.Abs(X0 - X1),
Math.Abs(Y0 - Y1));
imgEntrada = new Image<Bgr, byte>("lena.jpg");
imgEntrada.Draw(RealImageRect, new Bgr(Color.Red), thickness);
imageBoxOutputROI.Image = imgEntrada;
#endregion
((PictureBox)sender).Invalidate();
}
/// <summary>
/// Desenha retângulo
/// </summary>
/// http://stackoverflow.com/questions/11088154/graphics-fillrectangle-except-specified-area-net-gdi
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
// Draw the rectangle...
if (imageBoxInput.Image != null)
{
if (Rect != null && Rect.Width > 0 && Rect.Height > 0)
{
//Seleciona a ROI
e.Graphics.SetClip(Rect, System.Drawing.Drawing2D.CombineMode.Exclude);
e.Graphics.FillRectangle(selectionBrush, new Rectangle
(0, 0, ((PictureBox)sender).Width, ((PictureBox)sender).Height));
//e.Graphics.FillRectangle(selectionBrush, Rect);
}
}
}
private void pictureBox_MouseUp(object sender, MouseEventArgs e)
{
//Define ROI. Valida altura e largura para evitar index range exception.
if (RealImageRect.Width > 0 && RealImageRect.Height > 0)
{
imgEntrada.ROI = RealImageRect;
imageBoxROI.Image = imgEntrada;
}
}
private void pictureBox_MouseDoubleClick(object sender, MouseEventArgs e)
{
Rect = new Rectangle();
((PictureBox)sender).Invalidate();
}
#endregion
要在本技巧中成功构建附带的应用程序,请引用项目中的 Emgu CV DLL,以防止它们以黄色突出显示的感叹号显示。下图显示了项目中正确引用了 DLL。
另一个值得一提的点是,存在与 C++ 代码链接的包装 DLL,必须引用指向输出目录的应用程序。以下是在 Visual Studio 2013 中应用配置的示例。
关注点
我花了一周时间研究使用鼠标实现 ROI 的方法,但没有成功。一周后没有解决问题,我返回研究,并在半小时内解决了它。
参考
本技巧基于 Emgu CV 官方论坛中的疑问。我将以下网站用作研究来源:http://csharphelper.com/blog/2014/10/select-parts-of-a-scaled-image-picturebox-different-sizemode-values-c/