c#-OpenCvSharp-roi area (interception and masking) with source code

 Summary and optimization based on the ROI screenshots and masking operations of the first two blogs. The knowledge points involved are explained in the two articles. I will not go into details here. This article is only a summary.

OpenCvSharp-mouse frame selection to intercept region of interest (ROI)-source code attached

c#-OpenCvSharp-mask operation (with source code)

Only adding a mask operation to the mouse movement event, I feel that there is still room for optimization in the code, and I will go back and continue optimizing it later. If you readers want to optimize the code, you can contact me to communicate and learn together. As a beginner, I still have a lot to learn.

The following is the code

using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Point = OpenCvSharp.Point;

namespace 绘制ROI_掩码_
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Mat src;// 存储图像的Mat对象

        //选择图像文件
        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Image Files(*.jpg;*.png*;*.bmp*)|*.jpg;*.png*;*.bmp";
            if (ofd.ShowDialog() != DialogResult.OK)
                return;
            string imagePath = ofd.FileName;
            src = Cv2.ImRead(imagePath, ImreadModes.AnyColor);
            Cv2.ImShow("src image", src);
        }


        static Mat tempMat; // 用于临时存储原始图像
        static Point sp = new Point(-1, -1); // 起始点坐标
        static Point ep = new Point(-1, -1); // 终点坐标

        private void button2_Click(object sender, EventArgs e)
        {
            if (src == null)
            {
                MessageBox.Show("请先选择图像文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 创建鼠标回调函数
            MouseCallback draw = new MouseCallback(DrawRectangle);


            // 复制图像
            tempMat = new Mat(src.Size(), src.Type());
            Cv2.CopyTo(src, tempMat);

            // 分两步将Mat对象转换为IntPtr,用于传递给回调函数。
            // 将Mat对象src作为参数传递给Alloc方法,以创建一个GCHandle对象,并将其与srcImage关联起来
            System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.Alloc(src);

            // 再将GCHandle对象转换为IntPtr,即从GCHandle对象中获取对象所在的内存地址。
            IntPtr ptr = System.Runtime.InteropServices.GCHandle.ToIntPtr(handle);

            // 设置鼠标回调函数,将ptr作为额外的用户数据,通过Cv2.SetMouseCallback方法传递给鼠标回调函数DrawRectangle
            Cv2.SetMouseCallback("src image", draw, ptr);
            Cv2.WaitKey();
        }

        // 鼠标回调函数DrawRectangle,它是一个委托类型MouseCallback的实现
        public static void DrawRectangle(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userData)
        {
            // 获取图像数据 从userData指针中获取用户数据,并将其转换为GCHandle对象
            System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(userData);
            Mat src = (Mat)handle.Target;

            // 鼠标左键按下事件
            if (@event == MouseEventTypes.LButtonDown)
            {
                sp.X = x;
                sp.Y = y;
                Console.WriteLine("起点坐标 ({0},{1})", sp.X, sp.Y);
            }
            // 鼠标左键抬起事件
            else if (@event == MouseEventTypes.LButtonUp)
            {
                ep.X = x;
                ep.Y = y;
                Console.WriteLine("终点坐标 ({0},{1})", ep.X, ep.Y);

                // 绘制矩形
                if (ep.X > sp.X && ep.Y > sp.Y)
                {
                    // Cv2.Rectangle方法用于在图像(Mat对象)上绘制矩形 
                    // Cv2.Rectangle(Mat img, Point pt1, Point pt2, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8, int shift = 0);
                    Cv2.Rectangle(src, sp, ep, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias, 0);

                    // 用SubMat()提取子区域,该代码用来显示截取出来的图片
                    Cv2.ImShow("ROI区域", src.SubMat(sp.Y, ep.Y, sp.X, ep.X));

                    // 显示绘制矩形后的图像
                    Cv2.ImShow("src image", src);

                    // 完成绘制矩形后,sp.X和sp.Y重置为-1。确保下一次绘制矩形时能够正确记录起点坐标
                    sp.X = -1;
                    sp.Y = -1;
                }
            }


            // 鼠标移动事件 (加入掩码操作部分)
            else if (@event == MouseEventTypes.MouseMove && sp.X > 0 && sp.Y > 0)
            {
                ep.X = x;
                ep.Y = y;

                if (ep.X > sp.X && ep.Y > sp.Y)
                {
                    // 创建临时图像并将原始图像复制到临时图像中
                    Mat tempMat = new Mat();  
                    src.CopyTo(tempMat);     

                    // 计算矩形区域
                    Rect roi = new Rect(sp.X, sp.Y, ep.X - sp.X, ep.Y - sp.Y);
                   
                    // 创建掩码并将矩形区域设为非零值
                    Mat mask = new Mat(src.Size(), MatType.CV_8UC1, Scalar.All(0));

                    // 将矩形区域在掩码中设为非零值(255),这个矩形区域将被用于后续的掩码操作
                    mask[roi].SetTo(new Scalar(255));
                   
                    // 应用掩码并显示结果
                    Mat result = new Mat();  // 创建一个空的Mat对象,用于存储掩码操作后的结果
                    tempMat.CopyTo(result, mask);

                    // 显示经过掩码操作后的图像 result,即只显示矩形区域内的内容,其他区域被掩盖
                    Cv2.ImShow("Masked Image", result);
                   
                }
            }

        }
    }
}

Guess you like

Origin blog.csdn.net/m0_55074196/article/details/132064485