65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2015 年 1 月 3 日

CPOL

2分钟阅读

viewsIcon

70054

downloadIcon

3768

Emgu CV - 使用鼠标在尺寸不相同的图像中选择 ROI(感兴趣区域)。本技巧使用 Emgu CV 框架和 C# .NET 语言编写。

引言

本技巧介绍了一种使用鼠标在尺寸不相同的相同图像上选择 ROI(感兴趣区域)的方法。该产品基于 C# .NET 语言的 Emgu CV 框架开发。之所以创建本技巧,是因为许多开发者在使用 ImageBox PictureBox 渲染和操作图像中的感兴趣区域时感到困惑,因此我决定准备一个技巧,让读者了解如何在输入图像和输出图像尺寸不相同的情况下选择 ROI。希望本技巧对您有所帮助。请分享!祝您阅读愉快,一切顺利。

背景

为了更好地理解该产品的理由,请阅读 Emgu CV 论坛中以下帖子。

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/

© . All rights reserved.