OpenCV函数cvFindContours


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


提取轮廓在OpenCV里有一个函数  cvFindContours 
<p></p><div class="dp-highlighter bg_cpp" style="font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; width: 700.90625px; overflow-y: hidden; overflow-x: auto; padding-top: 1px; position: relative; margin: 18px 0px !important; background-color: rgb(231, 229, 220);"><div class="bar" style="padding-left: 45px;"><div class="tools" style="padding: 3px 8px 10px 10px; font-stretch: normal; font-size: 9px; line-height: normal; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; color: silver; border-left-width: 3px; border-left-style: solid; border-left-color: rgb(108, 226, 108); background-color: rgb(248, 248, 248);"><strong>[cpp]</strong> <a target=_blank href="http://blog.csdn.net/augusdi/article/details/9000893#" class="ViewSource" title="view plain" style="color: rgb(160, 160, 160); text-decoration: none; border: none; padding: 1px; margin: 0px 10px 0px 0px; font-size: 9px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-attachment: initial; background-color: inherit; background-size: initial; background-origin: initial; background-clip: initial; background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><span data-mod="popu_168"> <a target=_blank href="http://blog.csdn.net/augusdi/article/details/9000893#" class="CopyToClipboard" title="copy" style="color: rgb(160, 160, 160); text-decoration: none; border: none; padding: 1px; margin: 0px 10px 0px 0px; font-size: 9px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-attachment: initial; background-color: inherit; background-size: initial; background-origin: initial; background-clip: initial; background-position: 0% 0%; background-repeat: no-repeat;">copy</a></span><div style="position: absolute; left: 522px; top: 439px; width: 18px; height: 18px; z-index: 99;"></div><span data-mod="popu_169"> </span></div></div><ol start="1" class="dp-cpp" style="padding: 0px; border: none; color: rgb(92, 92, 92); margin: 0px 0px 1px 45px !important; background-color: rgb(255, 255, 255);"><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;"><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">int</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,</span><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">int</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> header_size=</span><span class="keyword" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 102, 153); font-weight: bold; background-color: inherit;">sizeof</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">(CvContour),</span><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">int</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> mode=CV_RETR_LIST,</span><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">int</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );  </span></span></li></ol></div><p></p>
这个函数用起来很方便,但是随着你使用的深入,你会发现有一些迷惑在这里。比如当你提取轮廓时只需要最外围的一个轮廓,但是你会发现当轮廓画出来时是好几个;当你需要找一个最大轮廓时却发现找出来的却根本就不是你想要的那个。带着这样问题我们再来仔细看看 cvFindContours这个函数。
下边的是一位仁兄写的测试程序和测试图片,说明提取轮廓的两种方法及绘制轮廓中最大等级分析 问题,非常感谢他的分享,原文戳 这里
[cpp]  view plain  copy
  1. /************************************************************************/      
  2. /* 提取轮廓两种方法对比及绘制轮廓'最大等级'分析                         */      
  3. /************************************************************************/      
  4. #include "stdafx.h"      
  5. #include "cv.h"      
  6. #include "highgui.h"      
  7.       
  8. int main()      
  9. {      
  10.     IplImage* img = cvLoadImage("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);      
  11.     IplImage* img_temp = cvCreateImage(cvGetSize(img), 8, 1);      
  12.   
  13.     cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);      
  14.   
  15.     CvMemStorage* mem_storage = cvCreateMemStorage(0);      
  16.     CvSeq *first_contour = NULL, *c = NULL;      
  17.   
  18.     //      
  19.     // 1、      
  20.     cvNamedWindow("contour1");      
  21.     cvCopyImage(img, img_temp);      
  22.     double t = (double)cvGetTickCount();    
  23.     cvFindContours(img_temp, mem_storage, &first_contour);      
  24.     cvZero(img_temp);      
  25.     cvDrawContours(      
  26.         img_temp,       
  27.         first_contour,      
  28.         cvScalar(100),      
  29.         cvScalar(100),      
  30.         1      
  31.         );      
  32.     t = (double)cvGetTickCount() - t;     
  33.     cvShowImage("contour1", img_temp);      
  34.   
  35.     printf("run1 = %gms\n", t/(cvGetTickFrequency()*1000.));      
  36.   
  37.     cvClearMemStorage(mem_storage);      
  38.   
  39.     //      
  40.     // 2、      
  41.     cvNamedWindow("contour2");      
  42.     cvCopyImage(img, img_temp);      
  43.     t = (double)cvGetTickCount();    
  44.     CvContourScanner scanner = cvStartFindContours(img_temp, mem_storage);      
  45.     while (cvFindNextContour(scanner));      
  46.     first_contour = cvEndFindContours(&scanner);      
  47.           
  48.     cvZero(img_temp);      
  49.     cvDrawContours(      
  50.         img_temp,       
  51.         first_contour,      
  52.         cvScalar(100),      
  53.         cvScalar(100),      
  54.         1      
  55.         );      
  56.     t = (double)cvGetTickCount() - t;     
  57.     cvShowImage("contour2", img_temp);      
  58.         
  59.     printf("run2 = %gms\n", t/(cvGetTickFrequency()*1000.));      
  60.           
  61.     cvClearMemStorage(mem_storage);      
  62.     cvReleaseImage(&img);      
  63.     cvReleaseImage(&img_temp);      
  64.       
  65.     cvWaitKey();      
  66.     
  67.     /************************************************************************/      
  68.     /* 经测试 run1 = 16.1431ms run2 = 15.8677ms (参考)  
  69.        不过可以肯定这两中算法时间复杂度是相同的                                     */      
  70.     /************************************************************************/      
  71.           
  72.     //      
  73.     // 上述两种方法完成了对轮廓的提取,如想绘制轮廓都得配合cvDrawContours来使用      
  74.     // 而cvDrawContours 函数第5个参数为 max_level 经查ICVL含义如下:      
  75.     //      
  76.     // 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。      
  77.     // 如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,      
  78.     // 函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。      
  79.     //      
  80.     // 相信好多读者初次都无法理解等级的含义,而且测试时候输入>=1 的整数效果几乎一样      
  81.     // 只有提取轮廓时候的提取模式设为 CV_RETR_CCOMP CV_RETR_TREE 时这个参数才有意义      
  82.     //      
  83.     // 经查FindContours 函数里面这样介绍提取模式(mode)的这两个参数:      
  84.     // CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。       
  85.     // CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy       
  86.     //       
  87.     // 下面用第一种方法进行测试      
  88.       
  89.     cvNamedWindow("contour_test");      
  90.     cvNamedWindow("contour_raw");      
  91.     img = cvLoadImage("contour.jpg", CV_LOAD_IMAGE_GRAYSCALE);      
  92.     cvShowImage("contour_raw", img);      
  93.     cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);      
  94.     img_temp = cvCloneImage(img);      
  95.     cvFindContours(      
  96.         img_temp,       
  97.         mem_storage,       
  98.         &first_contour,      
  99.         sizeof(CvContour),      
  100.         CV_RETR_CCOMP           //#1 需更改区域      
  101.         );      
  102.       
  103.     cvZero(img_temp);      
  104.     cvDrawContours(      
  105.         img_temp,       
  106.         first_contour,      
  107.         cvScalar(100),      
  108.         cvScalar(100),      
  109.         1                       //#2 需更改区域      
  110.         );      
  111.     cvShowImage("contour_test", img_temp);      
  112.     /************************************************************************/      
  113.     /* (1, 2) = (CV_RETR_CCOMP, 1)  如图1   
  114.        (1, 2) = (CV_RETR_CCOMP, 2)  如图2   
  115.        (1, 2) = (CV_RETR_TREE, 1)   如图3   
  116.        (1, 2) = (CV_RETR_TREE, 2)   如图4   
  117.        (1, 2) = (CV_RETR_TREE, 6)   如图5   
  118.        经分析CV_RETR_CCOMP 只把图像分为两个层次,顶层和次层,一等级轮廓只匹配与其最接近   
  119.        的内侧轮廓即2等级   
  120.        CV_RETR_TREE 则从轮廓外到内按等级1 - n 全部分配           
  121.        CV_RETR_LIST 全部轮廓均为1级                        */      
  122.     /************************************************************************/      
  123.       
  124.     cvWaitKey();      
  125.     cvReleaseImage(&img);      
  126.     cvReleaseImage(&img_temp);      
  127.     cvReleaseMemStorage(&mem_storage);      
  128.     cvDestroyAllWindows();      
  129.     return 0;      
  130. }  

原图

图一

图二


图三

图四

图五

这是OpenCV的经典一个例子:

[cpp]  view plain  copy
  1. #include "cv.h"  
  2. #include "cxcore.h"  
  3. #include "highgui.h"  
  4. #include <math.h>  
  5. #endif  
  6.    
  7. #pragma   comment(lib,"cv.lib")    
  8. #pragma   comment(lib,"highgui.lib")    
  9. #pragma   comment(lib,"cxcore.lib")  
  10.   
  11. #define w 500  
  12. int levels = 3;  
  13. CvSeq* contours = 0;  
  14.    
  15. void on_trackbar(int pos)  
  16. {  
  17.     IplImage* cnt_img = cvCreateImage( cvSize(w,w), 8, 3 );  
  18.     CvSeq* _contours = contours;  
  19.     int _levels = levels - 3;  
  20.     if( _levels <= 0 ) // get to the nearest face to make it look more funny  
  21.         _contours = _contours->h_next->h_next->h_next->h_next->h_next->h_next->h_next->v_next->h_next->h_next;  
  22. //_contours = _contours->v_next;  
  23.     cvZero( cnt_img );  
  24.     cvDrawContours( cnt_img, _contours, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels);//, 3, CV_AA, cvPoint(0,0) );  
  25.     /*_levels: 
  26. 3,所有外轮廓及包含的内轮廓及里面的内轮廓 
  27. 2:所有外轮廓及包含的内轮廓 
  28. 1:所有外轮廓 
  29. 0,第一个外轮廓 
  30. -1:第一个外轮廓及包含的内轮廓 
  31. -2:第一个外轮廓及包含的内轮廓及里面的内轮廓 
  32.  
  33.  
  34.    _contours->h_next:同级的下一个轮廓 
  35. _contours->v_next父级下的下层区域; 
  36. */  
  37. cvShowImage( "contours", cnt_img );  
  38.     cvReleaseImage( &cnt_img );  
  39. }  
  40.    
  41. int main( int argc, char** argv )  
  42. {  
  43.     int i, j;  
  44.     CvMemStorage* storage = cvCreateMemStorage(0);  
  45.     IplImage* img = cvCreateImage( cvSize(w,w), 8, 1 );  
  46.    
  47.     cvZero( img );  
  48.    
  49.     for( i=0; i < 6; i++ )  
  50.     {  
  51.         int dx = (i%2)*250 - 30;//0%2=0;  
  52.         int dy = (i/2)*150;  
  53.         CvScalar white = cvRealScalar(255);  
  54.         CvScalar black = cvRealScalar(0);  
  55.    
  56.         if( i == 0 )  
  57.         {  
  58.             for( j = 0; j <= 10; j++ )  
  59.             {  
  60.                 double angle = (j+5)*CV_PI/21;  
  61.                 cvLine(img, cvPoint(cvRound(dx+100+j*10-80*cos(angle)),  
  62.                     cvRound(dy+100-90*sin(angle))),  
  63.                     cvPoint(cvRound(dx+100+j*10-30*cos(angle)),  
  64.                     cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);  
  65.             }  
  66.         }  
  67.    
  68.         cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(100,70), 0, 0, 360, white, -1, 8, 0 );  
  69.         cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );  
  70.         cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );  
  71.         cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );  
  72.         cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );  
  73.         cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );  
  74.         cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );  
  75.         cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(10,5), 0, 0, 360, black, -1, 8, 0 );  
  76.         cvEllipse( img, cvPoint(dx+150, dy+150), cvSize(40,10), 0, 0, 360, black, -1, 8, 0 );  
  77.         cvEllipse( img, cvPoint(dx+27, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );  
  78.         cvEllipse( img, cvPoint(dx+273, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );  
  79.     }  
  80.    
  81.     cvNamedWindow( "image", 1 );  
  82.     cvShowImage( "image", img );  
  83.    
  84.     cvFindContours( img, storage, &contours, sizeof(CvContour),  
  85.                     2, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );  
  86.    
  87.     // comment this out if you do not want approximation  
  88.     contours = cvApproxPoly( contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 );  
  89.  //cvApproxPoly:                                                 逼近方法     精度 逼近曲线是否封闭  
  90.   
  91.   
  92.     cvNamedWindow( "contours", 1 );  
  93.     cvCreateTrackbar( "levels+3""contours", &levels, 7, on_trackbar );  
  94.    
  95.     on_trackbar(0);  
  96.     cvWaitKey(0);  
  97.     cvReleaseMemStorage( &storage );  
  98.     cvReleaseImage( &img );  
  99.    
  100.     return 0;  
  101. }  

主要还是理解下int mode=CV_RETR_LIST,int method=CV_CHAIN_APPROX_SIMPLE,CvPoint offset=cvPoint(0,0));

当mode 为CV_RETR_CCOMP 只把图像分为两个层次,顶层和次层,一等级轮廓只匹配与其最接近  ;

cvDrawContours 函数第5个参数为 max_level=0时,笑脸图像会显示第一个找到的轮廓,左边的白色耳朵一只;

max_level=1时,所有白色区域的轮廓都会被显示出来,因为他们都属于等级1;

max_level=2时;每个白色区域里面的黑色区域会被显示出来,可能一个白色区域下面有多个黑色区域,但他们都是同级的;

这里你要注意的的是每个白色区域下的黑色区域,如脸下面有4个黑色区域,白色眼珠下有一个黑色区域,这个黑色区域与脸下的那三个区域时同级的,也就是说他不属于脸的内区域,他是白色眼珠的内区域;

当mode为       CV_RETR_LIST 全部轮廓均为1级

8
0
 
 
我的同类文章
更多文章
猜你在找
C++ 单元测试(GoogleTest)
《C语言/C++学习指南》加密解密篇(安全相关算法)
C语言系列之 字符串压缩算法与结构体初探
C语言系列之 数组与算法实战
网络赚钱靠谱项目推荐
opencv中cvFindContours函数两个测试程序
Opencv中cvFindContours函数使用
OpenCV函数cvFindContours
OpenCV通过cvFindContours与cvDrawCountours函数查找轮廓
OpenCV函数cvFindContours轮廓提取
查看评论
5楼  liukailun09 2016-04-01 09:40发表 [回复]
学习了,图一到图五很明了,一看就知道那几个参数应该怎么用了。
4楼  松下问童子 2015-09-23 17:47发表 [回复]
十分感谢楼主分享。今天尝试了FindContours()和DrawContours()不同的参数,发现结果让我很困惑,楼主解决了我的一部分困惑,目前还在理清当中。
3楼  tuling56 2014-11-19 14:29发表 [回复]
轮廓是如何分级的?还是没讲明白
2楼  Augusdi 2013-11-13 20:25发表 [回复]
请看看OpenCV提供的例子吧,有你说的这个方面的代码
1楼  CarsonGP 2013-09-08 21:24发表 [回复] [引用] [举报]
谢谢,学习啦,我想问下我是要搭建双目视觉系统,我要做特征提取,先滤波,然后进行轮廓提取,我想问下,接下来该干什么呢?在立体匹配之前。。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值