C#使用OpenCV剪切图像中的圆形和矩形

前言

本文sql教程主要介绍java基础教程如何使用OpenCV剪切python基础教程图像中的圆形和矩形。

准备工作

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

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

C#使用OpenCV剪切图像中的圆形和矩形

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

使用OPenCV剪切矩形

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

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

1

2

3

4

5

using Emgu.CV;

using Emgu.CV.CvEnum;

using Emgu.CV.Structure;

using System.Drawing;

using System.Windows.Forms;

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

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

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

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

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。

1

2

3

4

5

6

7

8

9

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);

    }

}

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

C#使用OpenCV剪切图像中的圆形和矩形

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

测试结果如下图所示:

C#使用OpenCV剪切图像中的圆形和矩形

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

使用OPenCV剪切圆形

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

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

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

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

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

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

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剪切图像中的圆形和矩形

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

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

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

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

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

上一篇:Monoxide: Scale out Blockchains with Asynchronous Consensus Zones


下一篇:QT QGraphicsView 在鼠标点击处进行放大缩小