提取轮廓在OpenCV里有一个函数 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) );
这个函数用起来很方便,但是随着你使用的深入,你会发现有一些迷惑在这里。比如当你提取轮廓时只需要最外围的一个轮廓,但是你会发现当轮廓画出来时是好几个;当你需要找一个最大轮廓时却发现找出来的却根本就不是你想要的那个。带着这样问题我们再来仔细看看cvFindContours这个函数。
下边的是一位仁兄写的测试程序和测试图片,说明提取轮廓的两种方法及绘制轮廓中最大等级分析的问题,非常感谢他的分享,原文戳这里:
/************************************************************************/    /* 提取轮廓两种方法对比及绘制轮廓'最大等级'分析                         */    /************************************************************************/    #include "stdafx.h"    #include "cv.h"    #include "highgui.h"        int main()    {        IplImage* img = cvLoadImage("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);        IplImage* img_temp = cvCreateImage(cvGetSize(img), 8, 1);        cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);        CvMemStorage* mem_storage = cvCreateMemStorage(0);        CvSeq *first_contour = NULL, *c = NULL;        //        // 1、        cvNamedWindow("contour1");        cvCopyImage(img, img_temp);        double t = (double)cvGetTickCount();      cvFindContours(img_temp, mem_storage, &first_contour);        cvZero(img_temp);        cvDrawContours(            img_temp,             first_contour,            cvScalar(100),            cvScalar(100),            1            );        t = (double)cvGetTickCount() - t;       cvShowImage("contour1", img_temp);        printf("run1 = %gms\n", t/(cvGetTickFrequency()*1000.));        cvClearMemStorage(mem_storage);        //        // 2、        cvNamedWindow("contour2");        cvCopyImage(img, img_temp);        t = (double)cvGetTickCount();      CvContourScanner scanner = cvStartFindContours(img_temp, mem_storage);        while (cvFindNextContour(scanner));        first_contour = cvEndFindContours(&scanner);                cvZero(img_temp);        cvDrawContours(            img_temp,             first_contour,            cvScalar(100),            cvScalar(100),            1            );        t = (double)cvGetTickCount() - t;       cvShowImage("contour2", img_temp);              printf("run2 = %gms\n", t/(cvGetTickFrequency()*1000.));                cvClearMemStorage(mem_storage);        cvReleaseImage(&img);        cvReleaseImage(&img_temp);            cvWaitKey();          /************************************************************************/        /* 经测试 run1 = 16.1431ms run2 = 15.8677ms (参考)        不过可以肯定这两中算法时间复杂度是相同的                                     */        /************************************************************************/                //        // 上述两种方法完成了对轮廓的提取,如想绘制轮廓都得配合cvDrawContours来使用        // 而cvDrawContours 函数第5个参数为 max_level 经查ICVL含义如下:        //        // 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。        // 如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,        // 函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。        //        // 相信好多读者初次都无法理解等级的含义,而且测试时候输入>=1 的整数效果几乎一样        // 只有提取轮廓时候的提取模式设为 CV_RETR_CCOMP CV_RETR_TREE 时这个参数才有意义        //        // 经查FindContours 函数里面这样介绍提取模式(mode)的这两个参数:        // CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。         // CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy         //         // 下面用第一种方法进行测试            cvNamedWindow("contour_test");        cvNamedWindow("contour_raw");        img = cvLoadImage("contour.jpg", CV_LOAD_IMAGE_GRAYSCALE);        cvShowImage("contour_raw", img);        cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);        img_temp = cvCloneImage(img);        cvFindContours(            img_temp,             mem_storage,             &first_contour,            sizeof(CvContour),            CV_RETR_CCOMP           //#1 需更改区域            );            cvZero(img_temp);        cvDrawContours(            img_temp,             first_contour,            cvScalar(100),            cvScalar(100),            1                       //#2 需更改区域            );        cvShowImage("contour_test", img_temp);        /************************************************************************/        /* (1, 2) = (CV_RETR_CCOMP, 1)  如图1         (1, 2) = (CV_RETR_CCOMP, 2)  如图2         (1, 2) = (CV_RETR_TREE, 1)   如图3         (1, 2) = (CV_RETR_TREE, 2)   如图4         (1, 2) = (CV_RETR_TREE, 6)   如图5         经分析CV_RETR_CCOMP 只把图像分为两个层次,顶层和次层,一等级轮廓只匹配与其最接近         的内侧轮廓即2等级         CV_RETR_TREE 则从轮廓外到内按等级1 - n 全部分配                 CV_RETR_LIST 全部轮廓均为1级                        */        /************************************************************************/            cvWaitKey();        cvReleaseImage(&img);        cvReleaseImage(&img_temp);        cvReleaseMemStorage(&mem_storage);        cvDestroyAllWindows();        return 0;    }

原图

图一

图二

图三

图四

图五

这是OpenCV的经典一个例子:

#include "cv.h"#include "cxcore.h"#include "highgui.h"#include <math.h>#endif #pragma   comment(lib,"cv.lib")  #pragma   comment(lib,"highgui.lib")  #pragma   comment(lib,"cxcore.lib")#define w 500int levels = 3;CvSeq* contours = 0; void on_trackbar(int pos){    IplImage* cnt_img = cvCreateImage( cvSize(w,w), 8, 3 );    CvSeq* _contours = contours;    int _levels = levels - 3;    if( _levels <= 0 ) // get to the nearest face to make it look more funny        _contours = _contours->h_next->h_next->h_next->h_next->h_next->h_next->h_next->v_next->h_next->h_next;//_contours = _contours->v_next;    cvZero( cnt_img );    cvDrawContours( cnt_img, _contours, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels);//, 3, CV_AA, cvPoint(0,0) );    /*_levels:3,所有外轮廓及包含的内轮廓及里面的内轮廓2:所有外轮廓及包含的内轮廓1:所有外轮廓0,第一个外轮廓-1:第一个外轮廓及包含的内轮廓-2:第一个外轮廓及包含的内轮廓及里面的内轮廓   _contours->h_next:同级的下一个轮廓_contours->v_next父级下的下层区域;*/cvShowImage( "contours", cnt_img );    cvReleaseImage( &cnt_img );} int main( int argc, char** argv ){    int i, j;    CvMemStorage* storage = cvCreateMemStorage(0);    IplImage* img = cvCreateImage( cvSize(w,w), 8, 1 );     cvZero( img );     for( i=0; i < 6; i++ )    {        int dx = (i%2)*250 - 30;//0%2=0;        int dy = (i/2)*150;        CvScalar white = cvRealScalar(255);        CvScalar black = cvRealScalar(0);         if( i == 0 )        {            for( j = 0; j <= 10; j++ )            {                double angle = (j+5)*CV_PI/21;                cvLine(img, cvPoint(cvRound(dx+100+j*10-80*cos(angle)),                    cvRound(dy+100-90*sin(angle))),                    cvPoint(cvRound(dx+100+j*10-30*cos(angle)),                    cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);            }        }         cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(100,70), 0, 0, 360, white, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(10,5), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+150, dy+150), cvSize(40,10), 0, 0, 360, black, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+27, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );        cvEllipse( img, cvPoint(dx+273, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );    }     cvNamedWindow( "image", 1 );    cvShowImage( "image", img );     cvFindContours( img, storage, &contours, sizeof(CvContour),                    2, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );     // comment this out if you do not want approximation    contours = cvApproxPoly( contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 ); //cvApproxPoly:                                                 逼近方法     精度 逼近曲线是否封闭    cvNamedWindow( "contours", 1 );    cvCreateTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );     on_trackbar(0);    cvWaitKey(0);    cvReleaseMemStorage( &storage );    cvReleaseImage( &img );     return 0;}

主要还是理解下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级

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

OpenCV函数cvFindContours相关推荐

  1. opencv使用cvFindContours提取联通域

    转自:http://hi.baidu.com/irmosgarden/blog/item/8ce0174c54b307fad72afcbc.html // m_imgFeature为黑白目标图像,白色 ...

  2. Opencv函数手册

    内容包含了Opencv的各种函数的使用中英文详细介绍.唯一的缺点是因为没有顺利,无法检索快速找出来所需要啊的函数. 有木有大神,知道怎样快速的检索Opencv函数的调用介绍?

  3. csharp通过dll调用opencv函数,图片作为参数

    [blog 项目实战派]csharp通过dll调用opencv函数,图片作为参数          ​一直想做着方面的研究,但是因为这个方面的知识过于小众,也是由于自己找资料的能力比较弱,知道今天才找 ...

  4. OpenCV函数 Canny 检测边缘

    OpenCV函数 Canny 检测边缘. 原理 Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的 最优算法, 最优边缘 ...

  5. OpenCV函数 Laplacian 算子实现

    OpenCV函数 Laplacian 实现 Laplacian 算子 的离散模拟. 原理 前一节我们学习了 Sobel 算子 ,其基础来自于一个事实,即在边缘部分,像素值出现"跳跃" ...

  6. OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例(附完整代码)

    OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例 OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例 OpenCV使用Sobel或Scharr O ...

  7. 【OpenCV函数】轮廓提取;轮廓绘制;轮廓面积;外接矩形

    FindContours 在二值图像中寻找轮廓  int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_cont ...

  8. 【OpenCV】OpenCV函数精讲之 -- 图像容器Mat

    Mat是一个类,由两部分组成: 矩阵头(包含矩阵尺寸.存储方法.存储地址等信息) 指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针 OpenCV中,每个Mat对象有自己的 ...

  9. OpenCV函数remap详解

    OpenCV函数remap详解 remap的作用是将原影像映射到目标影像的函数. 这是OpenCV文档中的说明. 但这个描述给人的感觉是,云里雾里,到底是需要计算目标到原的映射关系,还是原到目标的映射 ...

最新文章

  1. loadrunner关联点总结
  2. Python3.5 Day1作业:实现用户密码登录,输错三次锁定。
  3. 亿佰特物联网无线模块通信技术分析
  4. 大油井隐藏的箱子_房子买的小户型,4招变大不止一倍
  5. 深入浅出Docker(四):Docker的集成测试部署之道
  6. 【2031】求一元三次方程的解
  7. paip.环境设置 mybatis ibatis cfg 环境设置
  8. 【CSS】制作ICO图标
  9. HttpStatus详解
  10. rfc3315_DHCPv6-RFC3315(中文).pdf
  11. Nature子刊:中国学生读完大学后,批判性思维和学术技能出现全面下降
  12. 兑换记录html页面,兑换码记录.html
  13. 2010考研数学二第(11)题——高阶导数
  14. 各类排序算法汇总及动画演示(C语言)
  15. 加密算法与大数据安全
  16. iQOO Z6和iQOOZ6x的区别 选哪个好
  17. 百度前CTO刘建国任搜索网站爱帮网CEO
  18. 顶级销售人员经常使用的 6 种销售技巧
  19. 房地产360全景的制作方法
  20. 图片文件转base64和base64转图片文件

热门文章

  1. Sesame 2.7.0 发布,Java 的 RDF 开发包
  2. FusionChart完全入门手册8
  3. C# 的Delegate(委托)
  4. .NET Core Docker使用初入篇
  5. 发布任务PHP源码,Thinkphp5新威客任务平台源码
  6. myabtis 数字+逗号 传参问题 $和#
  7. 图数据库neo4j安装、neo4j使用
  8. 用table展示树形结构数据_复习一下数据结构(二)——2.2 树形索引(23树)
  9. nginx 反向proxy多个 tomcat,负载均衡
  10. (JAVA)String类之比较方法(2)