c#WinForm uses OpencvSharp to realize ROI area extraction

I have been learning OpencvSharp by myself for a while (I am currently working in C#, so I learned Opencvsharp, vs2015, opencvsharp3), and I have gained a lot. Now I will share my gains in the learning process.

Image processing is a very common problem, but for most of the time, we often don't need to process the whole picture, but only a part of it, which involves the extraction of ROI (Region of interest). My current method of extracting ROI is to use the mask Mask method. The specific idea is: during the image operation, define an empty Mask of the same size, that is, all are 0, and then draw the outline of the ROI we want on the Mask, and fill the inside, and a new Mask will be obtained. , this new Mask is only non-zero in the ROI area, and the rest of the elements are 0, and then use Cv2.BoundingRect() to find the smallest rectangle containing the outline of the ROI area, and separate the two images of the original image and the Mask The minimum rectangular part is proposed, and finally call the Cv2.BitwiseAnd() method. Generally speaking, an image is ANDed with itself, and the output is still its own image. After putting on the mask Mask, it will only The output image is in the non-zero area of ​​the Mask (that is, the ROI we need), so that our ROI extraction is realized.

Next, let’s share several common ROI region extractions.

   Part of the code is as follows:

Mainly used variables

         /// 放在yVars.ImgOptions中 
   public struct ROIMatt   
            {
                public static string Image;//原图  
                public static bool IsSelectRegion = false;

                public static int step; //ROI区域移动步长
                public static int angel; // 旋转一次 angel±=step; 旋转角度 

                public static yDirections direct = yDirections.NULL;
                public static yROIRegionType ROIType = yROIRegionType.Rectangle;

                // 矩形ROI
                // 矩形四个点坐标 都是相对于图像的坐标 而不是相对于picturebox的坐标
                // 矩形四个点相对位置 刚开始确定矩形时就这样 经过旋转后位置变 但相对位置还是这样
                //  1  2      // 按顺时针数的点   1-->2-->4-->3-->1-->2-->4-->3-->1
                //  3  4
                public static OpenCvSharp.Point rectFirstPoint = new OpenCvSharp.Point();
                public static OpenCvSharp.Point rectSecondPoint = new OpenCvSharp.Point();
                public static OpenCvSharp.Point rectThirdPoint = new OpenCvSharp.Point();
                public static OpenCvSharp.Point rectFourthPoint = new OpenCvSharp.Point(); 
                public static double rectWidth = 0;
                public static double rectHeight = 0;

                // 圆形ROI
                public static OpenCvSharp.Point cirCenter = new OpenCvSharp.Point(0.0,0.0); // 圆心
                public static int cirRadious = 0; // 半径
                // 椭圆ROI
                public static OpenCvSharp.Point elpCenter = new OpenCvSharp.Point(0.0,0.0); // 椭圆中心点
                public static double elpAngel = 0.0;//椭圆倾斜角度
                public static double elpLongAxis = 0.0; // 长轴
                public static double elpShortAxis = 0.0; // 短轴
            }

      The SizeMode of my picturebox is StretchImage, which may look a little different from the desired result, but it is actually the same.

   The first is the most common rectangle.

      For a regular rectangle, we can directly define the ROI area of ​​the image

 public Mat(Mat m, Rect roi);

The image defined in this way is the designated area of ​​the original image m. But for the tilted matrix, RotatedRect, you need to use a mask. The method and result of extracting ROI are as follows:

        public static void ImgMattingRect()
        {
            Mat pic = new Mat(yVars.ImgOptions.ROIMatt.Image);
            Mat mask = Mat.Zeros(pic.Size(), MatType.CV_8UC1);
            OpenCvSharp.Point2f[] coutours = new OpenCvSharp.Point2f[4];
            coutours[0] = yVars.ImgOptions.ROIMatt.rectFirstPoint;
            coutours[1] = yVars.ImgOptions.ROIMatt.rectSecondPoint;
            coutours[2] = yVars.ImgOptions.ROIMatt.rectFourthPoint;
            coutours[3] = yVars.ImgOptions.ROIMatt.rectThirdPoint;

            List<OpenCvSharp.Point> listt = new List<OpenCvSharp.Point>();
            for (int i = 0; i < coutours.Count(); i++)
            {
                listt.Add(new OpenCvSharp.Point(coutours[i].X, coutours[i].Y));
            }

            List<List<OpenCvSharp.Point>> pp = new List<List<OpenCvSharp.Point>>() { listt };

            Cv2.FillPoly(mask, pp, new Scalar(255, 255, 255)); 
            OpenCvSharp.Rect rect = Cv2.BoundingRect(coutours);
            Mat src = new Mat(pic, rect);
            Mat maskROI = new Mat(mask, rect);
            Mat picOut = new Mat();
            Cv2.BitwiseAnd(src, src, picOut, maskROI);
            Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);

            yVars.ImgOptions.ROIMatt.rectFirstPoint = new OpenCvSharp.Point(0, 0);
            yVars.ImgOptions.ROIMatt.rectSecondPoint = new OpenCvSharp.Point(0, 0);
            yVars.ImgOptions.ROIMatt.rectThirdPoint = new OpenCvSharp.Point(0, 0);
            yVars.ImgOptions.ROIMatt.rectFourthPoint = new OpenCvSharp.Point(0, 0);
        }

Scaling, translation and rotation only need to change the coordinates of the four vertices of the rectangle. The methods are the same so I won’t go into details.

circular ROI area,

Methods as below:

        public static void ImgMattingCircle()
        {
            Mat mm = new Mat(yVars.ImgOptions.ROIMatt.Image);
            Mat mask = Mat.Zeros(mm.Size(), MatType.CV_8UC3);

            Cv2.Circle(mask, yVars.ImgOptions.ROIMatt.cirCenter, yVars.ImgOptions.ROIMatt.cirRadious, Scalar.Red, 1, LineTypes.AntiAlias);
            Cv2.FloodFill(mask, yVars.ImgOptions.ROIMatt.cirCenter, Scalar.Red);

            mask.ConvertTo(mask, MatType.CV_8UC1);

            int xx = yVars.ImgOptions.ROIMatt.cirCenter.X - yVars.ImgOptions.ROIMatt.cirRadious;
            int yy = yVars.ImgOptions.ROIMatt.cirCenter.Y - yVars.ImgOptions.ROIMatt.cirRadious;
            int rr = 2 * yVars.ImgOptions.ROIMatt.cirRadious;
            // 圆的外接正方形
            Rect rect = new Rect(new OpenCvSharp.Point(xx, yy), new OpenCvSharp.Size(rr, rr));

            Mat src = new Mat(mm, rect);
            Mat maskRoI = new Mat(mask, rect);

            Cv2.CvtColor(maskRoI, maskRoI, ColorConversionCodes.BGR2GRAY);

            Mat picOut = new Mat();
            Cv2.BitwiseAnd(src, src, picOut, maskRoI);

            Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);

            yVars.ImgOptions.ROIMatt.cirCenter = new OpenCvSharp.Point(0, 0);
            yVars.ImgOptions.ROIMatt.cirRadious = 0;
        }

Show results:

 When moving a circular ROI, only the coordinates of the center of the circle change and the radius remains unchanged. When zooming, only the radius is changed, and the center of the circle remains unchanged. Be careful not to exceed the image boundary when moving.

Ellipse ROI

There are two ways to draw an ellipse in Opencvsharp

        //
        // 摘要:
        //     Draws simple or thick elliptic arc or fills ellipse sector
        //
        // 参数:
        //   img:
        //     Image.
        //
        //   box:
        //     The enclosing box of the ellipse drawn
        //
        //   color:
        //     Ellipse color.
        //
        //   thickness:
        //     Thickness of the ellipse boundary. [By default this is 1]
        //
        //   lineType:
        //     Type of the ellipse boundary. [By default this is LineType.Link8]
        public static void Ellipse(InputOutputArray img, RotatedRect box, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8);
        //
        // 摘要:
        //     Draws simple or thick elliptic arc or fills ellipse sector
        //
        // 参数:
        //   img:
        //     Image.
        //
        //   center:
        //     Center of the ellipse.
        //
        //   axes:
        //     Length of the ellipse axes.
        //
        //   angle:
        //     Rotation angle.
        //
        //   startAngle:
        //     Starting angle of the elliptic arc.
        //
        //   endAngle:
        //     Ending angle of the elliptic arc.
        //
        //   color:
        //     Ellipse color.
        //
        //   thickness:
        //     Thickness of the ellipse arc. [By default this is 1]
        //
        //   lineType:
        //     Type of the ellipse boundary. [By default this is LineType.Link8]
        //
        //   shift:
        //     Number of fractional bits in the center coordinates and axes' values. [By default
        //     this is 0]
        public static void Ellipse(InputOutputArray img, Point center, Size axes, double angle, double startAngle, double endAngle, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8, int shift = 0);

We adopt the first method, that is, we can convert the ellipse into a RotatedRect, as long as we change it to an ellipse when drawing the RotatedRect, we can return to the ROI extraction of the first rectangle, the code is as follows:

        public static void ImgMattingEllipse()
        {
            Mat mm = new Mat(yVars.ImgOptions.ROIMatt.Image);
            Mat mask = Mat.Zeros(mm.Size(), MatType.CV_8UC3);

            RotatedRect rorect = new RotatedRect(yVars.ImgOptions.ROIMatt.elpCenter, new Size2f(yVars.ImgOptions.ROIMatt.elpLongAxis, yVars.ImgOptions.ROIMatt.elpShortAxis), (float)yVars.ImgOptions.ROIMatt.elpAngel);
            Cv2.Ellipse(mask, rorect, Scalar.Red);

            Mat gray = new Mat();
            Cv2.CvtColor(mask, gray, ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(gray, gray, 100, 255, ThresholdTypes.Otsu);

            OpenCvSharp.Point[][] contours;
            HierarchyIndex[] hierarchly;
            Cv2.FindContours(gray, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));

            Rect rect = Cv2.BoundingRect(contours[0]);

            Cv2.FloodFill(mask, yVars.ImgOptions.ROIMatt.elpCenter, Scalar.Red);

            mask.ConvertTo(mask, MatType.CV_8UC1);

            Mat src = new Mat(mm, rect);
            Mat maskRoI = new Mat(mask, rect);
            Cv2.CvtColor(maskRoI, maskRoI, ColorConversionCodes.BGR2GRAY);
            Mat picOut = new Mat();
            Cv2.BitwiseAnd(src, src, picOut, maskRoI);

            Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);
        }

The experimental results are as follows:

 Operations such as translation, zooming, and rotation can be regarded as operations on RotatedRect.

If there is any problem, I hope you can correct me. Welcome to discuss and advise.


  

Guess you like

Origin blog.csdn.net/Iawfy_/article/details/123275118