zoukankan      html  css  js  c++  java
  • C#使用OpenCV剪切图像中的圆形和矩形

    前言

    本文主要介绍如何使用OpenCV剪切图像中的圆形和矩形。

    准备工作

    首先创建一个Wpf项目——WpfOpenCV,这里版本使用Framework4.7.2。

    然后使用Nuget搜索【Emgu.CV】,如下图。

    这里的Emgu.CV选择4.3.0.3890版本,然后安装Emgu.CV和Emgu.CV.runtime.windows。

    使用OPenCV剪切矩形

    现在,我们进入项目,进行OPenCV的调用。

    首先引入命名空间,如下:

    using Emgu.CV;
    using Emgu.CV.CvEnum;
    using Emgu.CV.Structure;
    using System.Drawing;
    using System.Windows.Forms;
    

    然后编写矩形剪切函数——CutRectangleImage。

    函数里,我们先将图像进行缩放,这样可以有效的减少检测到的矩形数量。

    再将图片处理成灰度模式,然后再高斯模糊,再边缘化。

    然后,我们就可以在图片里查找图形轮廓了,当轮廓有三个顶点,那么它是三角形,如果有四个顶点,那么它是四边形;我们要截取矩形,所以这里要加一个角度的判断,四个角必须都在80-100度之间。

    取到了顶点后,在依据顶点剪切图片就可以了。

    下面是截取矩形的代码,代码中只截取了宽度最大的那个矩形。

    public void CutRectangleImage(string imagePath)
    {
        Image<Bgr, Byte> src = new Image<Bgr, byte>(imagePath);
        int scale = 1;
        if (src.Width > 500)
        {
            scale = 2;
        }
        if (src.Width > 1000)
        {
            scale = 10;
        }
        if (src.Width > 10000)
        {
            scale = 100;
        }
        var size = new Size(src.Width / scale, src.Height / scale);
        Image<Bgr, Byte> srcNewSize = new Image<Bgr, byte>(size);
        CvInvoke.Resize(src, srcNewSize, size);
        //将图像转换为灰度
        UMat grayImage = new UMat(); 
        CvInvoke.CvtColor(srcNewSize, grayImage, ColorConversion.Bgr2Gray);
        //使用高斯滤波去除噪声
        CvInvoke.GaussianBlur(grayImage, grayImage, new Size(3, 3), 3);
        UMat cannyEdges = new UMat();
        CvInvoke.Canny(grayImage, cannyEdges, 60, 180);//通过边缘化,然后取出轮廓
         
        #region 取三角形和矩形的顶点坐标
        List<Triangle2DF> triangleList = new List<Triangle2DF>();
        List<RotatedRect> boxList = new List<RotatedRect>(); //旋转的矩形框
    ​
        using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
        {
            CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
            int count = contours.Size;
            for (int i = 0; i < count; i++)
            {
                using (VectorOfPoint contour = contours[i])
                using (VectorOfPoint approxContour = new VectorOfPoint())
                {
                    CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.08, true);
                    //仅考虑面积大于50的轮廓
                    if (CvInvoke.ContourArea(approxContour, false) > 50)
                    {
                        if (approxContour.Size == 3) //轮廓有3个顶点:三角形
                        {
                            System.Drawing.Point[] pts = approxContour.ToArray();
                            triangleList.Add(new Triangle2DF(pts[0], pts[1], pts[2]));
                        }
                        else if (approxContour.Size == 4) //轮廓有4个顶点
                        {
                            #region 检测角度,如果角度都在 [80, 100] 之间,则为矩形
                            bool isRectangle = true;
                            System.Drawing.Point[] pts = approxContour.ToArray();
                            LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true);
    ​
                            for (int j = 0; j < edges.Length; j++)
                            {
                                double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
                                if (angle < 80 || angle > 100)
                                {
                                    isRectangle = false;
                                    break;
                                }
                            }
                            #endregion
                            if (isRectangle) boxList.Add(CvInvoke.MinAreaRect(approxContour));
                        }
                    }
                }
            }
        }
        #endregion
            
        #region 保存剪切的最大的矩形图片  
        Rectangle rectangle = new Rectangle(0, 0, src.Width, src.Height);
        int maxWidth = 0;
        //boxList = boxList.Where(p => p.Size.Width > 300).ToList();
        for (int i = 0; i < boxList.Count(); i++)
        {
            RotatedRect box = boxList[i];
            Rectangle rectangleTemp = box.MinAreaRect();
            //这里对取到的顶点坐标进行了加宽,因为矩形可能存在角度,这里没有进行角度旋转,所以加宽了取值范围就可以取到完整的图了
            rectangleTemp = new Rectangle(rectangleTemp.X * scale, rectangleTemp.Y * scale, rectangleTemp.Width * scale + scale, rectangleTemp.Height * scale + scale);
          
            //取最大的矩形图片
            if (rectangleTemp.Width > maxWidth)
            {
                maxWidth = rectangleTemp.Width;
                rectangle = rectangleTemp;
            }
        }
        src.Draw(rectangle, new Bgr(System.Drawing.Color.Red), 4);//在图片中画线
        CvInvoke.Imwrite("原始图片.bmp", src); //保存原始图片
        CvInvoke.cvSetImageROI(src.Ptr, rectangle);//设置兴趣点—ROI(region of interest )
        var clone = src.Clone(); 
        CvInvoke.Imwrite("剪切的矩形图片.bmp", clone); //保存结果图  
        #endregion
        src.Dispose();
        srcNewSize.Dispose();
        grayImage.Dispose();
    }
    

    然后编写一个打开文件的函数,在成功打开文件后调用CutRectangleImage。

    private void btnRectangle_Click(object sender, RoutedEventArgs e)
    {
        System.Windows.Forms.OpenFileDialog frm = new System.Windows.Forms.OpenFileDialog();
        frm.Filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|All files(*.*)|*.*";
        if (frm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            CutRectangleImage(frm.FileName);
        } 
    }
    

    然后运行项目,点击剪切矩形文件。

    然后到debug文件夹下,查看结果。

    测试结果如下图所示:

    图中红线为检测到矩形后,手动画上去的矩形轮廓。

    使用OPenCV剪切圆形

    编写矩形剪切函数——CutCircleImage。

    函数里,我们依然先将图像进行缩放,为了有效的减少检测到的圆形数量。

    再将图片处理成灰度模式,然后再高斯模糊。

    然后再使用霍夫圆检测函数,获取圆的圆心和半径。

    最后再根据圆心和半径计算出最小矩形,然后将圆剪切并保存。

    代码如下:

    public void CutCircleImage(string imagePath)
    { 
        Image<Bgr, Byte> src = new Image<Bgr, byte>(imagePath);
      
        int scale = 1;
        if (src.Width > 500)
        {
            scale = 2;
        }
        if (src.Width > 1000)
        {
            scale = 10;
        }
        if (src.Width > 10000)
        {
            scale = 100;
        } 
        var size = new Size(src.Width / scale, src.Height / scale);
        Image<Bgr, Byte> srcNewSize = new Image<Bgr, byte>(size);
        CvInvoke.Resize(src, srcNewSize, size);
        //将图像转换为灰度
        UMat grayImage = new UMat();
        CvInvoke.CvtColor(srcNewSize, grayImage, ColorConversion.Bgr2Gray); 
        //使用高斯滤波去除噪声
        CvInvoke.GaussianBlur(grayImage, grayImage, new Size(3, 3), 3); 
        //霍夫圆检测
        CircleF[] circles = CvInvoke.HoughCircles(grayImage, HoughModes.Gradient, 2.0, 200.0, 100.0, 180.0, 5);
      
        Rectangle rectangle = new Rectangle();
        float maxRadius = 0;
        foreach (CircleF circle in circles)
        {
            var center = circle.Center;//圆心
            var radius = circle.Radius;//半径
            if (radius > maxRadius)
            {
                maxRadius = radius;
                rectangle = new Rectangle((int)(center.X - radius) * scale,
                    (int)(center.Y - radius) * scale,
                    (int)radius * 2 * scale + scale,
                    (int)radius * 2 * scale + scale);
            }
            srcNewSize.Draw(circle, new Bgr(System.Drawing.Color.Blue), 4);
    ​
        }
        CvInvoke.Imwrite("原始图片.bmp", srcNewSize); //保存原始图片
        if (maxRadius == 0)
        {
            MessageBox.Show("没有圆形");
        }
        CvInvoke.cvSetImageROI(srcNewSize.Ptr, rectangle);//设置兴趣点—ROI(region of interest )
        var clone = srcNewSize.Clone();
        CvInvoke.Imwrite("剪切的圆形图片.bmp", clone); //保存结果图  
        src.Dispose();
        srcNewSize.Dispose();
        grayImage.Dispose();
    }
    

    运行项目进行测试,结果如下:

    ----------------------------------------------------------------------------------------------------

    到此,C#使用OpenCV剪切图像中的圆形和矩形就已经介绍完了。

    代码已经传到Github上了,欢迎大家下载。

    Github地址: https://github.com/kiba518/OpenCv_CutImage

    ----------------------------------------------------------------------------------------------------

    注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
    若您觉得这篇文章还不错,请点击下方的推荐】,非常感谢!

    https://www.cnblogs.com/kiba/p/14497894.html

     

    https://www.cnblogs.com/kiba/
  • 相关阅读:
    POJ 2018 二分
    873. Length of Longest Fibonacci Subsequence
    847. Shortest Path Visiting All Nodes
    838. Push Dominoes
    813. Largest Sum of Averages
    801. Minimum Swaps To Make Sequences Increasing
    790. Domino and Tromino Tiling
    764. Largest Plus Sign
    Weekly Contest 128
    746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/kiba/p/14497894.html
Copyright © 2011-2022 走看看