本文首发于“小白学视觉”微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究!


经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。

由于噪声和光照的影响,物体的轮廓会出现不规则的形状,根据不规则的轮廓形状不利于对图像内容进行分析,此时需要将物体的轮廓拟合成规则的几何形状,根据需求可以将图像轮廓拟合成矩形、多边形等。本小节将介绍OpenCV 4中提供的轮廓外接多边形函数,实现图像中轮廓的形状拟合。

矩形是常见的几何形状,矩形的处理和分析方法也较为简单,OpenCV 4提供了两个函数求取轮廓外接矩形,分别是求取轮廓最大外接矩形的boundingRect()函数和求取轮廓最小外接矩形的minAreaRect()函数。

寻找轮廓外接最大矩形就是寻找轮廓X方向和Y方向两端的像素,该矩形长和宽分别与图像的两个轴平行。boundingRect()函数可以实现这个功能,该函数的函数原型在代码清单7-19中给出。

代码清单7-19 boundingRect()函数原型
1.  Rect cv::boundingRect(InputArray  array)
  • array: 输入的灰度图像或者2D点集,数据类型为vector或者Mat。

该函数可以求取包含输入图像中物体轮廓或者2D点集的最大外接矩形,函数只有一个参数,可以是灰度图像或者2D点集,灰度图像的参数类型为Mat,2D点集的参数类型为vector或者Mat。该函数的返回值是一个Rect类型的变量,该变量可以直接用rectangle()函数绘制矩形。返回值共有四个参数,前两个参数是最大外接矩形左上角第一个像素的坐标,后两个参数分别表示最大外接矩形的宽和高。

最小外接矩形的四个边都与轮廓相交,该矩形的旋转角度与轮廓的形状有关,多数情况下矩形的四个边不与图像的两个轴平行。minAreaRect()函数可以求取轮廓的最小外接矩形,该函数的函数原型在代码清单7-20中给出。

代码清单7-20 minAreaRect()函数原型
1.  RotatedRect cv::minAreaRect(InputArray  points)
  • points:输入的2D点集合

该函数可以根据输入的2D点集合计算最小的外接矩形,函数的返回值是RotatedRect类型的变量,含有矩形的中心位置、矩形的宽和高和矩形旋转的角度。RotatedRect类具有两个重要的方法和属性,可以输出矩形的四个顶点和中心坐标。输出四个顶点坐标的方法是points(),假设RotatedRect类的变量为rrect,可以通过rrect.points(points)命令进行读取,其中坐标存放的变量是Point2f类型的数组。输出矩形中心坐标的属性是center,假设RotatedRect类的变量为rrect,可以通过opt=rrect.center命令进行读取,其中坐标存放的变量是Point2f类型的变量。

为了了解两个外接矩形函数的使用方法,代码清单7-21中给出了提取轮廓外接矩形的示例程序。程序中首先利用Canny算法提取图像边缘,之后通过膨胀算法将邻近的边缘连接成一个连通域,然后提取图像的轮廓,并提取每一个轮廓的最大外接矩形和最小外接矩形,最后在图像中绘制出矩形轮廓,程序的运行结果在图7-20给出。

代码清单7-21 myRect.cpp计算轮廓外接矩形
1.  #include <opencv2/opencv.hpp>
2.  #include <iostream>
3.  #include <vector>
4.
5.  using namespace cv;
6.  using namespace std;
7.
8.  int main()
9.  {10.     Mat img = imread("stuff.jpg");
11.     if (img.empty())
12.     {13.         cout << "请确认图像文件名称是否正确" << endl;
14.         return -1;
15.     }
16.     Mat img1, img2;
17.     img.copyTo(img1);  //深拷贝用来绘制最大外接矩形
18.     img.copyTo(img2);  //深拷贝迎来绘制最小外接矩形
19.     imshow("img", img);
20.
21.     // 去噪声与二值化
22.     Mat canny;
23.     Canny(img, canny, 80, 160, 3, false);
24.     imshow("", canny);
25.
26.     //膨胀运算,将细小缝隙填补上
27.     Mat kernel = getStructuringElement(0, Size(3, 3));
28.     dilate(canny, canny, kernel);
29.
30.     // 轮廓发现与绘制
31.     vector<vector<Point>> contours;
32.     vector<Vec4i> hierarchy;
33.     findContours(canny, contours, hierarchy, 0, 2, Point());
34.
35.     //寻找轮廓的外接矩形
36.     for (int n = 0; n < contours.size(); n++)
37.     {38.         // 最大外接矩形
39.         Rect rect = boundingRect(contours[n]);
40.         rectangle(img1, rect, Scalar(0, 0, 255), 2, 8, 0);
41.
42.         // 最小外接矩形
43.         RotatedRect rrect = minAreaRect(contours[n]);
44.         Point2f points[4];
45.         rrect.points(points);  //读取最小外接矩形的四个顶点
46.         Point2f cpt = rrect.center;  //最小外接矩形的中心
47.
48.         // 绘制旋转矩形与中心位置
49.         for (int i = 0; i < 4; i++)
50.         {51.             if (i == 3)
52.             {53.                 line(img2, points[i], points[0], Scalar(0, 255, 0), 2, 8, 0);
54.                 break;
55.             }
56.             line(img2, points[i], points[i + 1], Scalar(0, 255, 0), 2, 8, 0);
57.         }
58.         //绘制矩形的中心
59.         circle(img, cpt, 2, Scalar(255, 0, 0), 2, 8, 0);
60.     }
61.     //输出绘制外接矩形的结果
62.     imshow("max", img1);
63.     imshow("min", img2);
64.     waitKey(0);
65.     return 0;
66. }

图7-20 myRect.cpp程序运行结果

有时候用矩形逼近轮廓会造成较大的误差,例如图7-20中对于圆形轮廓的逼近矩形围成的面积比真实轮廓面积大,如果寻找逼近轮廓的多边形,那么多边形围成的面积会更加接近真实的圆形轮廓面积。OpenCV 4提供了approxPolyDP()函数用于寻找逼近轮廓的多边形,该函数的函数原型在代码清单7-22中给出。

代码清单7-22 approxPolyDP()函数原型
1.  void cv::approxPolyDP(InputArray  curve,
2.                            OutputArray  approxCurve,
3.                            double  epsilon,
4.                            bool  closed
5.                            )
  • curve:输入轮廓像素点。
  • approxCurve:多边形逼近结果,以多边形顶点坐标的形式给出。
  • epsilon:逼近的精度,即原始曲线和逼近曲线之间的最大距离。
  • closed:逼近曲线是否为封闭曲线的标志, true表示曲线封闭,即最后一个顶点与第一个顶点相连。

该函数根据输入的轮廓得到最佳的逼近多边形。函数的第一个参数是输入的轮廓2D像素点,数据类型是vector或者Mat。第二个参数是多边形的逼近结果,以多边形顶点坐标的形式输出,是CV_32SC2类型的N×1的Mat类矩阵,可以通过输出结果的顶点数目初步判断轮廓的几何形状。第三个参数是多边形逼近时的精度,即原始曲线和逼近曲线之间的最大距离。第四个参数是逼近曲线是否为封闭曲线的标志, true表示曲线封闭,即最后一个顶点与第一个顶点相连。

为了了解该函数用法,在代码清单7-23中给出了对多个轮廓进行多边形逼近的示例程序。程序中首先提取了图像的边缘,然后对边缘进行腐蚀运算将靠近的边缘变成一个连通域,之后对边缘结果进行轮廓检测,并对每个轮廓进行多边形逼近,将逼近结果绘制在原图像中,并通过判断逼近多边形的顶点数目识别轮廓的形状,程序运行结果在图7-21给出。

代码清单7-23 myApproxPolyDP.cpp多边形轮廓拟合
1.  #include <opencv2/opencv.hpp>
2.  #include <iostream>
3.  #include <vector>
4.
5.  using namespace cv;
6.  using namespace std;
7.
8.  //绘制轮廓函数
9.  void drawapp(Mat result, Mat img2)
10. {11.     for (int i = 0; i < result.rows; i++)
12.     {13.         //最后一个坐标点与第一个坐标点连接
14.         if (i == result.rows - 1)
15.         {16.             Vec2i point1 = result.at<Vec2i>(i);
17.             Vec2i point2 = result.at<Vec2i>(0);
18.             line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
19.             break;
20.         }
21.         Vec2i point1 = result.at<Vec2i>(i);
22.         Vec2i point2 = result.at<Vec2i>(i + 1);
23.         line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
24.     }
25. }
26.
27. int main()
28. {29.     Mat img = imread("approx.png");
30.     if (img.empty())
31.     {32.         cout << "请确认图像文件名称是否正确" << endl;
33.         return -1;
34.     }
35.     // 边缘检测
36.     Mat canny;
37.     Canny(img, canny, 80, 160, 3, false);
38.     //膨胀运算
39.     Mat kernel = getStructuringElement(0, Size(3, 3));
40.     dilate(canny, canny, kernel);
41.
42.     // 轮廓发现与绘制
43.     vector<vector<Point>> contours;
44.     vector<Vec4i> hierarchy;
45.     findContours(canny, contours, hierarchy, 0, 2, Point());
46.
47.     //绘制多边形
48.     for (int t = 0; t < contours.size(); t++)
49.     {50.         //用最小外接矩形求取轮廓中心
51.         RotatedRect rrect = minAreaRect(contours[t]);
52.         Point2f center = rrect.center;
53.         circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0);
54.
55.         Mat result;
56.         approxPolyDP(contours[t], result, 4, true);  //多边形拟合
57.         drawapp(result, img);
58.         cout << "corners : " << result.rows << endl;
59.         //判断形状和绘制轮廓
60.         if (result.rows == 3)
61.         {62.             putText(img, "triangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
63.         }
64.         if (result.rows == 4)
65.         {66.             putText(img, "rectangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
67.         }
68.         if (result.rows == 8)
69.         {70.             putText(img, "poly-8", center, 0, 1, Scalar(0, 255, 0), 1, 8);
71.         }
72.         if (result.rows > 12)
73.         {74.             putText(img, "circle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
75.         }
76.     }
77.     imshow("result", img);
78.     waitKey(0);
79.     return 0;
80. }

图7-21 myApproxPolyDP.cpp程序中多边形拟合结果

OpenCV 4开发详解
往期推荐
【OpenCV 4开发详解】窗口交互操作
【OpenCV 4开发详解】图像直方图绘制
【OpenCV 4开发详解】直方图操作
【OpenCV 4开发详解】直方图应用
【OpenCV 4开发详解】图像模板匹配
【OpenCV 4开发详解】图像卷积
【OpenCV 4开发详解】图像噪声的种类与生成
【OpenCV 4开发详解】均值滤波
【OpenCV 4开发详解】方框滤波
【OpenCV 4开发详解】高斯滤波
【OpenCV 4开发详解】可分离滤波
【OpenCV 4开发详解】中值滤波
【OpenCV 4开发详解】边缘检测原理
【OpenCV 4开发详解】Scharr算子
【OpenCV 4开发详解】Laplacian算子
【OpenCV 4开发详解】Canny算法
【OpenCV 4开发详解】图像距离变换
【OpenCV 4开发详解】图像连通域分析
【OpenCV 4开发详解】图像腐蚀
【OpenCV 4开发详解】图像膨胀
【OpenCV 4开发详解】形态学应用
【OpenCV 4开发详解】检测直线
【OpenCV 4开发详解】直线拟合
【OpenCV 4开发详解】直线检测
【OpenCV 4开发详解】轮廓发现与绘制
【OpenCV 4开发详解】轮廓面积与长度
经过几个月的努力,市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》将春节后由人民邮电出版社发行。如果小伙伴觉得内容有帮助,希望到时候多多支持!
关注小白的小伙伴可以提前看到书中的内容,我们创建了学习交流群,欢迎各位小伙伴添加小白微信加入交流群,添加小白时请备注“学习OpenCV 4”。

【OpenCV 4开发详解】轮廓外接多边形相关推荐

  1. 【OpenCV 4开发详解】轮廓面积与长度

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  2. 【OpenCV 4开发详解】轮廓发现与绘制

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  3. 【OpenCV 4开发详解】点集拟合

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  4. 【OpenCV 4开发详解】图像腐蚀

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  5. 【OpenCV 4开发详解】图像上绘制几何图形

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  6. 【OpenCV 4开发详解】分割图像——分水岭法

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  7. 【OpenCV 4开发详解】QR二维码检测

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  8. 【OpenCV 4开发详解】深度神经网络应用实例

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  9. 【OpenCV 4开发详解】图像修复

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

最新文章

  1. Mysql练习题14-至少有5名直接下属的经理
  2. 013_SpringBoot视图层技术thymeleaf-迭代遍历
  3. 关于职场和职业发展的一些心得
  4. alsa 测试 linux_Linux低延迟服务器系统调优
  5. 程序员每天该做的事情
  6. [NodeJs] 你有使用过npx吗?它主要解决什么问题?
  7. java输入某年某个季度_Java获取某年某季度的第一天出错
  8. mongodb最多数据库_2020 年3 月数据库排行:前 10 整体下行,出新技术了?
  9. 利用爬虫获取网上医院药品价格信息 (上)
  10. 链上合约(On-chain) 和 链下合约(Off-chain) 是什么 区别
  11. 2017年c语言试题,2017年计算机二级C语言试题
  12. Android11.0系统去掉桌面谷歌搜索栏
  13. 手把手教学endnote设置毕业论文参考文献格式(特别完整)
  14. 计算机桌面来回闪烁,电脑桌面图标一直闪
  15. 孔雀优化算法(POA)——(含MATLAB代码)
  16. Excel中导入Unix格式时间戳小技巧
  17. graphpad画生存曲线怎么样去掉删失点_手把手教你用graphpadprism绘制生存曲线
  18. PLC 200 Smart模拟量输入输出编程应用
  19. 离散傅里叶变换-DFT(FFT基础)
  20. 电镀废水处理工艺 离子交换树脂回收镍 硫酸镍经济效益越来越高 电镀废水如何获利 含重金属废水处理工艺

热门文章

  1. 喜得爱女,吴恩达深情撰文:欢迎你来到新世界!
  2. 50万奖金+京东数科offer,JDD-2018全球总决赛冠军诞生
  3. 去中心化已成大势所趋,手机的中枢地位岌岌可危?
  4. 技术 | Bengio终结Theano不是偶然,其性能早在Keras支持的四大框架中垫底
  5. 经典面试题:ES如何做到亿级数据查询毫秒级返回?
  6. 学 Redis ,至少要看看这篇!7000 字小结
  7. 淘宝服务端高并发分布式架构演进之路
  8. 【串讲总结】RNN、LSTM、GRU、ConvLSTM、ConvGRU、ST-LSTM
  9. 国务院发文!这些专业,将被撤销!
  10. 获国家杰青的北大教授,被985本科生嫌弃本科学校太差!被网友怒怼!