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


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

图像的轮廓不仅能够提供物体的边缘,而且还能提供物体边缘之间的层次关系以及拓扑关系。我们可以将图像轮廓发现简单理解为带有结构关系的边缘检测,这种结构关系可以表明图像中连通域或者某些区域之间的关系。图7-14为一个具有4个不连通边缘的二值化图像,由外到内依次为0号、1号、2号、3号条边缘。为了描述不同轮廓之间的结构关系,定义由外到内的轮廓级别越来越低,也就是高一层级的轮廓包围着较低层级的轮廓,被同一个轮廓包围的多个不互相包含的轮廓是同一层级轮廓。例如在图7-14中,0号轮廓层级比1号和第2号轮廓的层及都要高,2号轮廓包围着3号轮廓,因此2号轮廓的层级要高于3号轮廓。

图7-14 图像轮廓序号

为了更够更好的表明各个轮廓之间的层级关系,常用4个参数来描述不同层级之间的结构关系,这4个参数分别是:同层下一个轮廓索引、同层上一个轮廓索引、下一层第一个子轮廓索引和上层父轮廓索引。根据这种描述方式,图7-14中0号轮廓没有同级轮廓和父轮廓需要用-1表示,其第一个子轮廓为1号轮廓,因此可以用[−1−11−1]\left[ {\begin{array}{c}{ - 1}&{ - 1}&1&{ - 1}\end{array}} \right][−1​−1​1​−1​]描述该轮廓的结构。1号轮廓的下一个同级轮廓为2号轮廓但是没有上一个同级轮廓用-1表示,父轮廓为0号轮廓,第一个子轮廓为3号轮廓,因此可以用[2−130]\left[ {\begin{array}{c}2&{ - 1}&3&0 \end{array}} \right][2​−1​3​0​]描述该轮廓结构。2号轮廓和3号轮廓同样可以用这样的方式构建结构关系描述子。图7-14中不同轮廓之间的层级关系可以用图7-15表示。

图7-15 图7-14中不同轮廓之间的结构关系

OpenCV 4提供了可以在二值图像中检测图像中所有轮廓并生成不同轮廓结构关系描述子的findContours()函数,该函数的函数原型在代码清单7-11中给出。

代码清单7-11 findContours()函数原型1
1.  void cv::findContours(InputArray  image,
2.                            OutputArrayOfArrays  contours,
3.                            OutputArray  hierarchy,
4.                            int  mode,
5.                            int  method,
6.                            Point  offset = Point()
7.                            )
  • image:输入图像,数据类型为CV_8U的单通道灰度图像或者二值化图像。
  • contours:检测到的轮廓,每个轮廓中存放着像素的坐标。
  • hierarchy:轮廓结构关系描述向量。
  • mode:轮廓检测模式标志,可以选择的参数在表7-2给出。
  • method:轮廓逼近方法标志,可以选择的参数在表7-3给出。
  • offset:每个轮廓点移动的可选偏移量。这个参数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

该函数主要用于检测图像中的轮廓信息,并输出各个轮廓之间的结构信息。函数的第一个参数是待检测轮廓的输入图像,从理论上讲检测图像轮廓需要是二值化图像,但是该函数会对非0像素视为1,0像素保持不变,因此该参数能够接受非二值化的灰度图像。由于该函数默认二值化操作不能保持图像主要的内容,因此常需要对图像进行预处理,利用threshold()函数或者adaptiveThreshold()函数根据需求进行二值化。第二个参数用于存放检测到的轮廓,数据类型为vector<vector>,每个轮廓中存放着属于该轮廓的像素坐标。函数的第三个参数用于存放各个轮廓之间的结构信息,数据类型为vector,数据的尺寸与检测到的轮廓数目相同,每个轮廓结构信息中第1个数据表示同层下一个轮廓索引、第2个数据表示同层上一个轮廓索引、第3个数据表示下一层第一个子轮廓索引、第4个数据表示上层父轮廓索引。函数第四个参数是轮廓检测模式的标志,可以选择的参数及含义在表7-2给出。函数第五个参数是选择轮廓逼近方法的标志,可以选择的参数及含义在表7-3给出。函数最后一个参数是每个轮廓点移动的可选偏移量。这个函数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

表7-2 findContours()函数轮廓检测模式标志可选择参数

标志参数 简记 含义
RETR_EXTERNAL 0 只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]= hierarchy[i][2]=-1
RETR_LIST 1 提取所有轮廓,并且放置在list中。检测的轮廓不建立等级关系。
RETR_CCOMP 2 提取所有轮廓,并且将其组织为双层结构。顶层为连通域的外围边界,次层为孔的内层边界。
RETR_TREE 3 提取所有轮廓,并重新建立网状的轮廓结构。

表7-3 findContours()函数轮廓逼近方法标志可选择参数

标志参数 简记 含义
CHAIN_APPROX_NONE 1 获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1,即max⁡(abs(x1−x2),abs(y2−y1))==1\max ({\rm{abs}}({x_1} - {x_2}),{\rm{abs}}({y_2} - {y_1})) = = 1max(abs(x1​−x2​),abs(y2​−y1​))==1。
CHAIN_APPROX_SIMPLE 2 压缩水平方向、垂直方向和对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保持轮廓信息。
CHAIN_APPROX_TC89_L1 3 使用The-Chinl链逼近算法中的一个。
CHAIN_APPROX_TC89_KCOS 4 使用The-Chinl链逼近算法中的一个。

有时我们只需要检测图像的轮廓,并不关心轮廓之间的结构关系信息,此时轮廓之间的结构关系变量会造成内存资源的浪费,因此OpenCV 4提供了findContours()函数的另一种函数原型,可以不输出轮廓之间的结构关系信息,该种函数原型在代码清单7-12中给出。

代码清单7-12 findContours()函数原型2
1.  void cv::findContours(InputArray  image,
2.                            OutputArrayOfArrays  contours,
3.                            int  mode,
4.                            int  method,
5.                            Point  offset = Point()
6.                            )
  • image:输入图像,数据类型为CV_8U的单通道灰度图像或者二值化图像。
  • contours:检测到的轮廓,每个轮廓中存放着像素的坐标。
  • mode:轮廓检测模式标志,可以选择的参数在表7-2给出。
  • method:轮廓逼近方法标志,可以选择的参数在表7-3给出。
  • offset:每个轮廓点移动的可选偏移量。这个函数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

提取了图像轮廓后,为了能够直观的查看轮廓检测的结果,OpenCV 4提供了显示轮廓的drawContours()函数,该函数的函数原型在代码清单7-13中给出。

代码清单7-13 drawContours()函数原型
1.  void cv::drawContours(InputOutputArray  image,
2.                            InputArrayOfArrays  contours,
3.                            int   contourIdx,
4.                            const Scalar &  color,
5.                            int  thickness = 1,
6.                            int  lineType = LINE_8,
7.                            InputArray  hierarchy = noArray(),
8.                            int  maxLevel = INT_MAX,
9.                            Point  offset = Point()
10.                           )
  • image:绘制轮廓的目标图像。
  • contours:所有将要绘制的轮廓
  • contourIdx:要绘制的轮廓的数目,如果是负数,则绘制所有的轮廓。
  • color:绘制轮廓的颜色。
  • thickness:绘制轮廓的线条粗细,如果参数为负数,则绘制轮廓的内部,默认参数值为1.
  • lineType:边界线连接的类型,可以选择参数在表7-4给出,默认参数值为LINE_8。
  • hierarchy:可选的结构关系信息,默认值为noArray()。
  • maxLevel:表示用于绘制轮廓的最大等级,默认值为INT_MAX。
  • offset:可选的轮廓偏移参数,按指定的移动距离绘制所有的轮廓。

该函数用于绘制findContours()函数检测到的图像轮廓。函数的第一个参数为绘制轮廓的图像,根据需求该参数可以是单通道的灰度图像或者三通道的彩色图像。第二个参数是所有将要绘制的轮廓,数据类型为vector<vector>。第三个参数是要绘制的轮廓数目,该参数的数值与第二个参数相对应,应小于所有轮廓的数目,如果该参数值为负数,则绘制所有的轮廓。第四个参数是绘制轮廓的颜色,对于单通道的灰度图像用Scalar(x)赋值,对于三通道的彩色图像用Scalar(x,y,z)赋值。第五个参数是边界线的连接类型,可以选择的参数在表7-4给出,默认参数值为LINE_8。第六个参数是可选的结构关系信息,默认值为noArray()。第七个参数表示绘制轮廓的最大等级,参数值如果为0,则仅绘制指定的轮廓;如果为1,则该函数绘制轮廓和所有嵌套轮廓;如果为2,则该函数绘制轮廓以及所有嵌套轮廓和所有嵌套到嵌套轮廓的轮廓,以此类推,默认值为INT_MAX。函数最后一个参数是可选的轮廓偏移参数,按指定的移动距离绘制所有的轮廓。

表7-4 drawContours()函数中可选线型

标志参数 简记 含义
LINE_4 1 4连通线型
LINE_8 3 8连通线型
LINE_AA 4 抗锯齿线型

为了了解图像轮廓检测和绘制相关函数的使用,在代码清单7-14中给出了检测图像中的轮廓和绘制轮廓的示例程序。程序中不仅绘制了物体的轮廓,还输出了图像所有轮廓的结构关系信息。程序绘制的轮廓信息在图7-16给出,所有轮廓结构关系信息在图7-17给出,同时根据结果绘制了直观的结构关系。

代码清单7-14 myContours.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.     system("color F0");  //更改输出界面颜色
11.     Mat img = imread("coins.jpg");
12.     if (img.empty())
13.     {14.         cout << "请确认图像文件名称是否正确" << endl;
15.         return -1;
16.     }
17.     imshow("原图", img);
18.     Mat gray, binary;
19.     cvtColor(img, gray, COLOR_BGR2GRAY);  //转化成灰度图
20.     GaussianBlur(gray, gray, Size(9, 9), 2, 2);  //平滑滤波
21.     threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU);  //自适应二值化
22.
23.     // 轮廓发现与绘制
24.     vector<vector<Point>> contours;  //轮廓
25.     vector<Vec4i> hierarchy;  //存放轮廓结构变量
26.     findContours(binary, contours, hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE, Point());
27.     //绘制轮廓
28.     for (int t = 0; t < contours.size(); t++)
29.     {30.         drawContours(img, contours, t, Scalar(0, 0, 255), 2, 8);
31.     }
32.     //输出轮廓结构描述子
33.     for (int i = 0; i < hierarchy.size(); i++)
34.     {35.         cout << hierarchy[i] << endl;
36.     }
37.
38.     //显示结果
39.     imshow("轮廓检测结果", img);
40.     waitKey(0);
41.     return 0;
42. }

图7-16 myContours.cpp程序轮廓检测结果 图7-17 myContours.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开发详解】中值滤波
【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开发详解】轮廓发现与绘制相关推荐

  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开发详解】形态学应用

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

  8. 【OpenCV 4开发详解】图像膨胀

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

  9. 【OpenCV 4开发详解】Canny算法

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

最新文章

  1. react 统一字段验证_如何使用React的受控输入进行即时表单字段验证
  2. linux -- framebuff驱动总结
  3. 列注释_简单5步了解相关矩阵的注释热图
  4. 才云开源 Nirvana:Golang REST API框架
  5. show slave status\G中的Read_Master_Log_Pos和Relay_Log_Pos的(大小)关系
  6. 读书笔记 Effective C++: 02 构造析构赋值运算
  7. 天空的颜色和大气散射
  8. ARM中断分析之一:中断控制器和CPU、外设的关系
  9. 2017-2018-2 1723《程序设计与数据结构》第八周作业 实验二 第一周结对编程 总结...
  10. 关于.NET CF的底层资料(幻灯片)
  11. ACM学习历程—HDU5396 Expression(递推 计数)
  12. MySQL 8 新特性之持久化全局变量的修改
  13. android 获取屏幕宽高
  14. python的入门TCP编程
  15. 【2.Delphi语法基础】7.程序异常处理
  16. 凯撒密码加密算法python_想偷WiFi?万能钥匙不行?试试python一键破解!|wifi|python|profile|算法|无线网卡...
  17. 计算机病毒存于什么,计算机病毒存在于哪里
  18. 阅读引擎开源项目调研总结
  19. win10下itunes安装失败的解决方法
  20. ES数据的操作(二)

热门文章

  1. 特朗普“模仿”奥巴马?进阶版换脸技术DeepFakes来了
  2. 一个TensorFlow的新模型,Kaparthy小哥用了都说好
  3. 推荐一款神级 API 接口管理神器
  4. MySQL 用 limit 为什么会影响性能?
  5. 阿里巴巴为什么不建议直接使用 Async 注解?
  6. 排名前 16 的 Java 工具类,哪个你没用过?
  7. 麦肯锡:优秀数据科学家的5个特征!
  8. 神经网络的可解释性综述!
  9. 从源码到实战:BERT模型训练营
  10. ICCV2021 | SMCA:即插即用的共同注意力模型,可使DETR收敛加速10倍