• 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/
  • 相关阅读:
    cocos2d-x 2.2 移植wp8遇到的坑
    程序员简单却激荡的一年
    关于manacher
    关于Tarjan
    洛谷 P4013 数字梯形问题
    洛谷 P2633 Count on a tree
    洛谷 P1709 隐藏口令Hidden Password
    洛谷 P3112 后卫马克Guard Mark
    洛谷 P1174 打砖块
    洛谷1903 数颜色
  • 原文地址:https://www.cnblogs.com/kiba/p/14497894.html
Copyright © 2020-2023  润新知