OpenCV的轮廓查找有C版本和C++版本,当轮廓比较复杂的时候,例如嵌入多层轮廓,如果方法不当那么很容易会漏处理一些轮廓。本文介绍了复杂轮廓场景下的几种主要的查找轮廓和颜色填充方法。

1:cvFindContours函数介绍

int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), int

mode=CV_RETR_LIST,  int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );

image: 8比特单通道的源二值图像。非零像素作为1处理,0像素保存不变。从一个灰度图像得到二值图像的函数有:cvThreshold,cvAdaptiveThreshold和cvCanny。

storage: 返回轮廓的容器。

first_contour: 输出参数,用于存储指向第一个外接轮廓。

header_size: header序列的尺寸.如果选择method = CV_CHAIN_CODE, 则header_size >= sizeof(CvChain);其他,则 header_size >= sizeof(CvContour)。

mode:

CV_RETR_EXTERNAL:只检索最外面的轮廓;
CV_RETR_LIST:检索所有的轮廓,并将其放入list中;
CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。

method: 边缘近似方法(除了CV_RETR_RUNS使用内置的近似,其他模式均使用此设定的近似算法)。可取值如下:

CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CV_CHAIN_APPROX_NONE:将所有的连码点,转换成点。
CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一种。
CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。

offset:偏移量,用于移动所有轮廓点。当轮廓是从图像的ROI提取的,并且需要在整个图像中分析时,这个参数将很有用。

讨论部分cvDrawContours中的案例显示了任何使用轮廓检测连通区域。轮廓可以用于形状分析和目标识别——可以参考文件夹OpenCV sample中的squares.c

2:找轮廓及颜色填充

方法1:树形遍历

由contours的h_next和 v_next指针组成一个庞大的树形结构,如下面的图示,其中蓝色表示v_next,绿色表示h_next。如果不了解contours结构的话,很容易仅仅遍历v_next或者h_next,这样实际上会漏掉一些需要处理的轮廓。要全部遍历所有轮廓需要编写不少代码,这个还没搜索到直接用这种方式遍历轮廓的代码。需要注意的是,很多博文介绍的利用h_next或者v_next的方法都是会缺失不少轮廓的,轮廓的复杂会导致整个树也变的很复杂,所以建议还是不要用这种方式来遍历轮廓。我一开始用的就是这种方法,应该说是琢磨了好长时间,才知道为什么这种方法的轮廓数总是偏少。

方法2:cvFindNextContour方法

本方法是利用了轮廓扫描器CvContourScanner,使用cvStartFindContours、cvFindNextContour、cvEndFindContours函数获取从外到内的轮廓,获取一层轮廓进行一次颜色的填充。具体后面使用的原始轮廓图如下所示:

#include “opencv/cv.h”
#include “opencv/highgui.h”
int main(int argc, char **argv)
{

IplImage * pSrcImg= cvLoadImage(“E:\\11.jpg”);
IplImage * pGrayImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pThresholdImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pDstImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,3);

cvSetZero(pDstImg);
srand((int)time(0));   
CvSeq * contours = 0;
CvMemStorage * storage=cvCreateMemStorage(0);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );

cvCvtColor(pSrcImg,pGrayImg,CV_BGR2GRAY);
cvNot(pGrayImg,pGrayImg);
cvThreshold(pGrayImg,pThresholdImg,100,255,CV_THRESH_BINARY);
cvSet(pDstImg,color);

CvContourScanner scanner = cvStartFindContours(pThresholdImg, storage,
                                    sizeof(CvContour),CV_RETR_TREE ,CV_CHAIN_APPROX_NONE);
while (contours=cvFindNextContour(scanner))
{

color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvDrawContours(pDstImg, contours, color,color, 0,CV_FILLED);

};
contours= cvEndFindContours(&scanner);

cvSaveImage(“dst.jpg”,pDstImg);
cvReleaseImage(&pDstImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pThresholdImg);
cvReleaseMemStorage(&storage);
return 0;

}

方法3:cvNextTreeNode方法

本方法是利用cvFindContours,先找到所有轮廓,再使用树节点的迭代器CvTreeNodeIterator的函数cvNextTreeNode,获取从外到内的轮廓,并进行颜色的填充。

#include “opencv/cv.h”
#include “opencv/highgui.h”

int main(int argc, char **argv)
{

IplImage * pSrcImg= cvLoadImage(“E:\\11.jpg”);
IplImage * pGrayImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pThresholdImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pDstImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,3);

cvSetZero(pDstImg);
srand((int)time(0));
CvSeq * contours = 0;
CvMemStorage * storage=cvCreateMemStorage(0);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );

cvCvtColor(pSrcImg,pGrayImg,CV_BGR2GRAY);
cvNot(pGrayImg,pGrayImg);
cvThreshold(pGrayImg,pThresholdImg,100,255,CV_THRESH_BINARY);
                                               cvFindContours(pThresholdImg,storage,&contours,sizeof(CvContour),
CV_RETR_TREE ,CV_CHAIN_APPROX_NONE);
cvSet(pDstImg,color);

CvTreeNodeIterator iterator; 
cvInitTreeNodeIterator(&iterator,contours,3); 
while( 0 != (contours = (CvSeq*)cvNextTreeNode(&iterator)) ) 
{

color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvDrawContours(pDstImg, contours, color,color, 0,CV_FILLED);

}
cvSaveImage(“dst.jpg”,pDstImg);
cvReleaseImage(&pDstImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pThresholdImg);
cvReleaseMemStorage(&storage); 
return 0;

}

方法4: C++的iterator方法

本方法是利用OpenCV的C++函数findContours,获取从外到内的轮廓,并利用const_iterator 来逐个找到所有的轮廓。从编码的效果来看,最开始的轮廓是最外延的轮廓,然后逐渐找内部的轮廓,所以进行颜色填充的时候,不会出现外面的大轮廓覆盖掉内部的小轮廓的问题。

#include “opencv/cv.h”
#include “opencv/highgui.h”

using namespace cv;

int main( int argc, char** argv )
{

vector<Vec4i> hierarchy;
vector<vector<Point> > contours;
vector<vector<Point>>::const_iterator itContours;

srand((int)time(0));
Mat src = imread(“E:\\11.jpg”,0);
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );

src = src > 100;
findContours( src, contours, hierarchy, CV_RETR_TREE,CV_CHAIN_APPROX_NONE );

itContours=contours.begin();
int i=0;
for(;itContours!=contours.end();++itContours)
{

color=cvScalar( rand()&255, rand()&255, rand()&255 );
drawContours( dst,contours ,i, color, CV_FILLED );
i++;

}
imwrite(“dst.jpg”,dst);

}

必须采用for(;itContours!=contours.end();++itContours)的循环方式。

如果采用for( ; idx >= 0; idx = hierarchy[idx][0] )的循环方式,会导致大的轮廓颜色覆盖掉其内部小的轮廓颜色。

结果

如下为两次运行的效果图,其中C语言版本不会将整个图片的外延作为一个轮廓,所以这部分颜色要额外先填充。

从编码效率来看,C++版本的编码效率要高于C版本,因为不需要考虑很多的内存的释放等问题。

参考资料:

http://baike.baidu.com/view/4111188.htm

http://blog.csdn.net/augusdi/article/details/9000276

http://blog.csdn.net/augusdi/article/details/9000893

http://blog.csdn.net/timidsmile/article/details/8519751

声明:

如果转载了本文,也请注明转载出处:http://www.cvrobot.net/opencv-find-and-draw-contours/

OpenCV的轮廓查找和填充相关推荐

  1. 基于OpencV的轮廓填充算法在3D打印机中的应用

    在这之前,我们需要了解一下SLC文件的格式,只有对格式有一点了解,我们才能做接下来的工作,首先SLC文件中是通过描述各层中的多段线来描述整个模型的,多段线之间两两相连.对单个轮廓来说,最后一点必须等于 ...

  2. 【OpenCv】图像的轮廓查找

    1 原理   边界或者轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度.轮廓在形状分析和物体的检测和识别中很有用. 在机器视觉领域最常用的轮廓查找的算法之一是 Moore ...

  3. 使用Python,OpenCV进行涂鸦(绘制文字、线、圆、矩形、椭圆、多边形轮廓、多边形填充、箭头~)

    使用Python,OpenCV进行涂鸦(绘制文字.线.圆.矩形.椭圆.多边形轮廓.多边形填充.箭头) 1. 效果图 2. 原理 2.1 绘制线:cv2.line(canvas, (300, 0), ( ...

  4. python使用opencv查找轮廓_(八)OpenCV-Python学习—轮廓查找,绘制和拟合

    针对物体轮廓,opencv还提供了一些相关的函数,来处理轮廓查找,绘制,拟合,以及计算轮廓周长和面积等,详细介绍如下: 1. 寻找和绘制轮廓 opencv的findContours()能寻找图片中的轮 ...

  5. opencv 轮廓查找, 凸包,最小外接矩形,最小外接圆,最小外接椭圆

    本章内容: 1. 轮廓查找 2. 绘制轮廓 3. 凸包 4.最小外接矩形 5.最小外接圆 6.最小外接椭圆 1.搜索轮廓 2.绘制轮廓 输出结果 3.凸包 输出结果 4.最小外接矩形 输出结果 5.最 ...

  6. openCV 轮廓查找-测量物体尺寸

    一,利用openCV的findContours轮廓查找功能,用已知物体的尺寸(比如硬币)作为参考,根据实际尺寸与像素尺寸的比列,求出图片中物体的实际大小.存在的问题有两个: 图片的阴影导致轮廓不准确, ...

  7. Python机器视觉--OpenCV进阶(核心)--图像轮廓查找识别,绘制图像轮廓与图像轮廓的面积周长计算

    1.图像轮廓查找识别与绘制图像轮廓 1.1 什么是图像轮廓 图像轮廓是具有相同颜色或灰度的连续点的曲线. 轮廓在形状分析和物体的检测和识别中很有用. 轮廓的作用: 用于图形分析 物体的识别和检测 注意 ...

  8. OpenCV使用findContours查找轮廓和相关函数——C++

    OpenCV使用findContours查找轮廓和相关函数--C++ findContours函数的使用 findContours函数的使用 //查找前景的区域 vector<vector< ...

  9. OPENCV图像轮廓检测

    前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像 ...

最新文章

  1. CNN、RNN、GAN都是什么?终于有人讲明白了
  2. python3 操作redis
  3. tensorflow打印模型图_从Tensorflow模型文件中解析并显示网络结构图(pb模型篇)...
  4. php原生sql语法,thinkphp执行原生SQL语句的实现方法
  5. 判断字符串是否构成回文_构成字符串回文的最小删除数
  6. (二)容器从入门到深入-初识Kubernetes
  7. PHP类: SEO必备的伪原创工具 (文章重写)
  8. linux mdamd工具安装,ubuntu 13.04amd64安装 wine 1.5 office2010
  9. 【超参数寻优】交叉验证(Cross Validation)超参数寻优的python实现:多参数寻优
  10. 2018-2019-1 20189204《Linux内核原理与分析》第一周作业
  11. python语言中strike_基于Python的XSS测试工具XSStrike使用方法
  12. java计算机毕业设计HTML5旅游网站源码+mysql数据库+系统+lw文档+部署
  13. 语音增强论文翻译:2017_SEGAN: Speech Enhancement Generative Adversarial Network
  14. python中1 100的质数_Python求解1到100之间的质数
  15. python 爬虫 中乱码问题0xb5 和b'\x1f\x8b\x08
  16. java实现学生信息管理系统
  17. 基于Python实现的学生信息管理系统
  18. StarUML 3 中文文档 活动图
  19. 基于3D关节点的人体动作识别综述(转)
  20. 【Linux】报错:cp: omitting directory...

热门文章

  1. 新风控如何避免错失“白户”?
  2. 那些你踩过的币圈投资陷阱(下)
  3. 如何构建 FinTech 科学反欺诈体系|架构师实践日
  4. 无约束最优化方法-牛顿法
  5. Uber创始人:一个优秀创业者应具八种特质
  6. 雨敲窗python_雨敲窗Python:类
  7. 深入理解分布式技术 - 结合RocketMQ和Kafka理解MQ的两种经典模式_P2P模式和发布订阅模式
  8. OS - MMAP初探
  9. Java was started but returned exit code=13
  10. 简单分析算法的时间复杂度