【OpenCV 4开发详解】轮廓外接多边形
本文首发于“小白学视觉”微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究!
经过几个月的努力,小白终于完成了市面上第一本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开发详解】轮廓外接多边形相关推荐
- 【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开发详解】QR二维码检测
本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...
- 【OpenCV 4开发详解】深度神经网络应用实例
本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...
- 【OpenCV 4开发详解】图像修复
本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...
最新文章
- Mysql练习题14-至少有5名直接下属的经理
- 013_SpringBoot视图层技术thymeleaf-迭代遍历
- 关于职场和职业发展的一些心得
- alsa 测试 linux_Linux低延迟服务器系统调优
- 程序员每天该做的事情
- [NodeJs] 你有使用过npx吗?它主要解决什么问题?
- java输入某年某个季度_Java获取某年某季度的第一天出错
- mongodb最多数据库_2020 年3 月数据库排行:前 10 整体下行,出新技术了?
- 利用爬虫获取网上医院药品价格信息 (上)
- 链上合约(On-chain) 和 链下合约(Off-chain) 是什么 区别
- 2017年c语言试题,2017年计算机二级C语言试题
- Android11.0系统去掉桌面谷歌搜索栏
- 手把手教学endnote设置毕业论文参考文献格式(特别完整)
- 计算机桌面来回闪烁,电脑桌面图标一直闪
- 孔雀优化算法(POA)——(含MATLAB代码)
- Excel中导入Unix格式时间戳小技巧
- graphpad画生存曲线怎么样去掉删失点_手把手教你用graphpadprism绘制生存曲线
- PLC 200 Smart模拟量输入输出编程应用
- 离散傅里叶变换-DFT(FFT基础)
- 电镀废水处理工艺 离子交换树脂回收镍 硫酸镍经济效益越来越高 电镀废水如何获利 含重金属废水处理工艺