Contours OpenCv问题
第一个问题:
问题:假如我有如下一张图,我要把边上两个小点去除,又要保留大轮廓内部的空洞,怎么办?
Fig 1.1
函数原型:
- C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
- C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())
这两个重载函数只有一个hierarchy的差别。说明如下:
测试: 用以下代码测试一下 contours 和 hierarchy 是怎么对轮廓进行标号的?选择mode = CV_RETR_CCOMP 双层结构。
- #include <opencv2\opencv.hpp>
- #include <time.h>
- using namespace std;
- using namespace cv;
- //测试findContours,drawContours函数用法
- bool verify(vector<Point> input, int min, int max);
- int main()
- {
- Mat src = imread("test.bmp", 1);
- Mat dst = Mat::zeros(src.size(), src.type());
- //cout<<src.channels(); //画图软件生成3通道图
- Mat gray;
- cvtColor(src, gray, CV_BGR2GRAY);
- Mat thre;
- threshold(gray, thre, 127, 255, CV_THRESH_BINARY );
- //thre = gray > 1; //这种写法可以替代上面那一句
- imshow("thre", thre);
- imwrite("thre.bmp", thre);
- vector< vector<Point> > contours;
- vector<Vec4i> hierarchy;
- findContours(thre, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
- //计算每个轮廓的面积
- float temp = 0;
- for(int i=0; i<contours.size(); ++i) {
- temp = fabs(contourArea(contours[i]));
- cout<<"i="<<i<<" area="<<temp<<endl;
- }
- //我要把单独的小轮廓去掉,而保留大轮廓内部的孔洞
- int idx = 0;
- for( ; idx >=0; idx = hierarchy[idx][0])
- {
- if(verify(contours[idx], 200, 90000)) {
- Scalar color( rand()&255, rand()&255, rand()&255);
- drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy);
- }
- }
- imshow("dst",dst);
- imwrite("dst.bmp", dst);
- while( 1 )
- {
- int key = waitKey(0); //获取键盘按键
- if( (key & 255) == 27 ) //判断ESC是否按下,若按下便退出
- {
- cout << "程序退出...........\n";
- break;
- }
- }
- return 0;
- }
- bool verify(vector<Point> input, int min, int max) {
- float area = fabs(contourArea(input));
- if((area>min) & (area<max))
- return true;
- else
- return false;
- }
结果图:
![](/assets/blank.gif)
我计算了每个轮廓的面积, 判断面积在指定范围内就把该轮廓画出来。在查找轮廓的时候,我只查找 hierarchy[i][0],就是不去处理子轮廓。 发现已经达到要求了。
分析:
contours 有6个,hierarchy 也有6个,对应测试图中总共6个内外轮廓。
hierarchy[i] 表示第 i 个轮廓,有四个值 hierarchy[i][0], hierarchy[i][1], hierarchy[i][2], hierarchy[i][3], 分别对应next, previous, first child, paraent. 它们的值是轮廓编号,如果没有响应的轮廓则等于-1. 从局部变量里面查看,这些值是这样子的:
0: | 2 | -1 | 1 | -1 |
1: | -1 | -1 | -1 | 0 |
2: | 4 | 0 | 3 | -1 |
3: | -1 | -1 | -1 | 2 |
4: | 5 | 2 | -1 | -1 |
5: | -1 | 4 | -1 | -1 |
因此推测出轮廓序号是这样子的:
这里面关键的一点是:填充序号为0和2的轮廓的时候,CV_FILLED, 并没有把内轮廓也填充掉,而是填充了环形区域!
如果我把 mode 改成 CV_RETR_LIST, 单层轮廓。
代码和测试结果图:
findContours(thre, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
- for(int i=0; i<contours.size(); ++i) {
- if(verify(contours[i], 200, 90000)) {
- Scalar color( rand()&255, rand()&255, rand()&255);
- drawContours(dst, contours, i, color, CV_FILLED, 8, hierarchy);
- }
- }
Fig 1.4
果真就把内部轮廓也一起填充了。
轮廓重新排列,根据面积和 hierarchy 的值确定轮廓是这样排序的:
先排内部轮廓,再排外部轮廓(可能是随机的)。
Fig 1.5
第二个问题:contours.erase() 函数怎么用
把上面的代码该成:
- int idx = 0;
- for( ; idx >=0; idx = hierarchy[idx][0])
- {
- if(verify(contours[idx], 0, 200)) {
- contours.erase(contours.begin() + idx);
- }
- }
这样用会报错,当idx = 5 时, 已经不存在 contours[5]; 因为执行 contours.erase() 之后,该 contour 会被清除,后面的 contour 会补位,contours.size()的值变小了。也就是,contours 和 hierarchy 的序号不同步了。
只用contours 序号不会错, 但是我又想要实现最开始给出的那个功能,就要用到hierarchy的信息。怎么办呢?
暂时没有找出办法。
如果有迭代器,contours.erase()是可以用的:
- vector<vector<Point>> contours;
- vector<Vec4i> hierarchy;
- findContours(thre, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
- vector<vector<Point>>::const_iterator it = contours.begin();
- while(it != contours.end())
- {
- float area = contourArea(*it);
- float perimeter = fabs(arcLength(*it, true));
- cout << area << endl << "\t" <<perimeter<<endl;
- if(area<2000)
- it = contours.erase(it);
- else
- it++;
- }
it 指向 vector<vector<Point>>结构,即 contours 的结构(多个contour),它最初指向首地址,每次 it++, 它就向后增加一个 contour 的内存大小,指向了下一个contour。
当执行 contours.erase(it)之后,该contour被删除了,it 的位置在原地没有变,后面所有的轮廓都向前补位,同时contours.end() 的位置也就向前补了一位,处理完所有轮廓后就满足了 it = contours.end(), 然后循环退出。
第三个问题:轮廓面积计算结果和像素点总数的关系
findContours() 提取轮廓, contourArea() 计算面积,得到的结果跟我数像素点个数是不一样的。
测试图:
Fig 3.1
测试代码和结果:
- findContours(thre, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
- //计算每个轮廓的面积
- float temp = 0;
- for(int i=0; i<contours.size(); ++i) {
- temp = fabs(contourArea(contours[i]));
- cout<<"i="<<i<<" Contour_Area="<<temp<<endl;
- }
- //直接计算面积
- float temp1 = countNonZero(thre);
- cout<<"NonZero="<<temp1<<endl;
Fig 3.2
只有一个轮廓,通过提取轮廓再求轮廓内部面积得到100, 直接数连通域像素点个数得到121。差别还是很大的。多次试验发现,该算法是取连通域边界像素中心点,连接起来,成为一个轮廓。测试图中的正方形边长为11个像素点,Contour_Area = 10*10=100; NonZero = 11*11 = 121. 示意图如下:
如果提取轮廓再算面积是 3*3=9; 而连通域像素点个数是4*4=16;
因此,countArea() 函数算出来的轮廓面积常常出现 “0“值就可以理解了,比如有的轮廓厚度只有两像素,都是边缘线,那计算出来的厚度就等于0;
第四个问题:findContours() method 参数的影响
method = CV_CHAIN_APPROX_NONE 存储所有的轮廓点,也就是任意两个相邻的轮廓点8领域相连。存储的轮廓点都是紧挨着的,不会跳过。
method = CV_CHAIN_APPROX_SIMPLE 压缩水平、垂直或斜对角方向的片段,连接两个端点,只存储两个端点像素点。比如矩形,只存储四个点。
测试图片:
Fig 4.1
Fig 4.2
两种method的计算结果竟然相同。只是contours[i] 里面存储的点数不一样。边界点存储的少了难道不会丢失信息吗?drawContours 画出来的边界是否完全一样?
Fig 4.3
放大了看边界也是一样的,计算了两者面积也是一样的。
既然这样,CV_CHAIN_APPROX_SIMPLE 还可以减少存储,CV_CHAIN_APPROX_NONE 用在哪里呢?
第五个问题:findContours() 提取轮廓 drawContours() 再画出轮廓,面积和原来的会不会变化?
仍用图4.1做测试,结果:
说明连着用这两个函数不会改变原来的形状,相当于复制了一遍。
第六个问题:计算外轮廓面积是环形面积吗?会把内轮廓一块算上吗?
测试图:
Fig 6.1
图中的两个大圆是一样的。
findContours(thre, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
findContours(thre, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
虽然排序不同,但是不同参数下的面积是一样的,说明计算外轮廓包围面积的时候是实心的,不会把内轮廓抠掉。
如果我想得到环形的面积,要用外轮廓-内轮廓。
好了,实验到这里。下一步可以去看 contours.cpp 源码。手册上面说了,主要算法叫“ Suzuki85” (铃木算法?)。
Contours OpenCv问题相关推荐
- OpenCV 学习笔记03 boundingRect、minAreaRect、minEnclosingCircle、boxPoints、int0、circle、rectangle函数的用法...
函数中的代码是部分代码,详细代码在最后 1 cv2.boundingRect 作用:矩形边框(boundingRect),用于计算图像一系列点的外部矩形边界. cv2.boundingRect(arr ...
- 基于代数距离的椭圆拟合
问题 给定离散点集Xi=(xi,yi),i=1,2,...NX_i=(x_i,y_i) ,i=1,2,...NXi=(xi,yi),i=1,2,...N,我们希望找到误差最小的椭圆去拟合这些离散 ...
- 美美的圣诞树画出来-CoCube
2022年圣诞节到来啦,很高兴这次我们又能一起度过~ CSDN诚邀各位技术er分享关于圣诞节的各种技术创意,展现你与众不同的精彩!参与本次投稿即可获得[话题达人]勋章+[圣诞快乐]定制勋章(1年1次, ...
- 解决:cv2.error: OpenCV(4.6.0) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\contours.cpp
解决:cv2.error: OpenCV(4.6.0) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\contours.cpp ...
- Opencv 图像处理-Contours函数提取轮廓及感兴趣区域ROI的必用且实用操作技巧-(涵盖Contours的一切使用基础,附代码段)
需求目的:一般都是做项目时使用opencv的findcontours和drawcontours搭配使用抓取图像内感兴趣区域. 1.Contours函数轮廓点大小排序 当使用findco ...
- opencv contours的问题
第一个问题: 问题:假如我有如下一张图,我要把边上两个小点去除,又要保留大轮廓内部的空洞,怎么办? Fig 1.1 函数原型: C++: void findContours(InputOutputAr ...
- openCV Contours详解
在OpenCV中处理结构分析和形状描述(Structural Analysis and Shape Descriptors),大部分跟contours相关. 轮廓线就是一条连接所有边界点的曲线,其实也 ...
- opencv中关于轮廓检测识别Contours及相关函数的介绍
最近在用vs和opencv库在做图像处理的项目,关于轮廓识别部分,我查阅了一些资料, 现结合自己的理解整理出来,希望能对你有用. 1.contours概述 在利用openCV对图像进行处理时,我们可能 ...
- Contours in OpenCV
翻译自 Contour in OpenCV Contours Contours 可以简单的理解为一条连通连续点的曲线(沿着边缘),有同样的颜色或者强度.在进行形状分析,目标检测和识别时很有用处. 为了 ...
最新文章
- 区域经济、地理信息、互联网三者交叉之行业背景分析
- 高效使用Google
- python匿名函数lambda_python的匿名函数lambda解释及用法
- 如果BigDecimal是答案,那肯定是一个奇怪的问题
- C++ 输出单个字符
- 淮海工学院期末考试Oracle,淮海工学院大一物理期末试卷
- python go rpc_Go实现简易RPC框架的方法步骤
- Android 系统(231)--OTA对要发布的编译版本进行签名
- SysUtils.UpperCase、SysUtils.LowerCase - 大小写转换
- spring cloud构建java版 b2b2c o2o电子商务云商平台
- 梦笔记2021-03-05
- java对象的序列化和反序列化_Java对对象的序列化和反序列化详解
- java 网络编程发展过程以及nio的特点
- python任务栏显示网速_win10状态栏显示网速小工具_超好用
- 项目实施中的风险控制与管理
- 函数式编程-Either函子篇
- 设计模式笔记-----七大原则
- 国产CAD软件对于AutoCAD,更适合哪种?
- BinaryWriter
- Java中的IO技术使用总结
热门文章
- 封装navbar组件
- Suterusu对话Waves | 隐私, DeFi繁荣的关键
- maven install 本地jar包
- 用python画小猪佩奇动画片全集_教你用Python画小猪佩奇
- java集合类(collection)
- 【活动预告】Cocos2d-x即将首次赴台办沙龙 让你满载而归
- mybatis中@Results,@ResultMap注解使用
- 2022年轻薄笔记本推荐:对屏幕有高需求的学生党,看过来
- win11如何安装msi文件
- php微信公众号首次关注自动回复,PHP_PHP微信开发之文本自动回复,首先,先去微信公众平台注册 - phpStudy...