OpenCV函数cvFindContours

提取轮廓在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 = 0void 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息队列控制灯代码_代码实现RabbitMQ死信队列的创建

‍‍前言&#xff1a;‍‍之前有写过死信队列的使用场景以及通过管控台创建死信。这次就通过代码实现死信队列的创建&#xff0c;同时也分享一下RabbitMQ封装的类。准备&#xff1a;1. 先准备一个死信队列(最后用来消费)的参数配置&#xff0c;包括虚拟机&#xff0c;交换机&am…

平板电脑可以插u盘吗_电视TV盒子安装app的六个方法,u盘/电脑/手机都可以安装...

点击上方蓝字 关注我们首先&#xff0c;要看盒子是否支持安装第三方app&#xff0c;这期只讲支持第三方app安装的安卓系统盒子&#xff0c;iOS盒子不讲。怎么看电视盒子能不能安装第三方&#xff1f;很简单&#xff0c;方法很多&#xff0c;最常见就是用装有apk文件的u盘连接盒…

drive es 软件兼容_某知名软件被完美修改!对不住了!

⭐星标Or 置顶⭐右上角三点-设为星标(置顶)今天给大家介绍两款超好用的免费神器&#xff01;支持&#xff0c;请点亮文章底部“在看” ?搜图神器绿化版安卓 / 9M这是一款装机必备的搜图应用&#xff0c;支持图片搜索&#xff0c;百度识图&#xff0c;谷歌识图&#xff0c;拍照…

5g pdu session_运营商下架4G套餐,用户被5G!

近日&#xff0c;网上突然出现诸多报道&#xff0c;称三大运营商为了推广5G套餐&#xff0c;开始下架4G套餐&#xff0c;用户办理套餐时只能选择价格相对较高的5G套餐。看到这些报道很多网友坐不住了&#xff0c;这难道要倒逼用户改用5G吗&#xff1f;要知道&#xff0c;三大运…

OpenCV图像处理形态学操作腐蚀Erode与膨胀Dilate

通俗的说&#xff1a;膨胀算法使图像扩大一圈。腐蚀算法使二值图像减小一圈。腐蚀&#xff1a;删除对象边界的某些像素膨胀&#xff1a;给图像中的对象边界添加像素算法&#xff1a;从图像处理角度看&#xff0c;二值图像的腐蚀和膨胀就是将一个小型二值图&#xff08;结构元素…

台达asda-b2伺服驱动器说明书_台达解决方案提升粉末冶金液压机的控制精度

项目背景粉末冶金液压机是一种利用液体压力来传递能量&#xff0c;实现各种压力加工工艺的机床&#xff0c;适用于各种金属及非金属粉末的压制成形&#xff0c;如铁基、铜基、钨、钛、锰、硼等。某生产企业所应用的粉末冶金液压机采用以台达油电伺服驱动器VFD-VJ系列为核心的控…

常州win8如何禁用应用商店_Win8系统当中Windows defnedder安全软件应该如何禁用?...

Win8系统如何禁用defender&#xff1f;Windows defender安全软件但是win8系统自带的&#xff0c;但有时该软件也会与其他软件发生冲突&#xff0c;那么这个时候应该如何禁用Windows defender安全软件呢&#xff1f;接下来就为大家分享win8系统禁用defender的操作方法。操作方法…

Django 的工程创建

1.环境安装 1. 创建虚拟环境 mkvirtualenv django_py3_1.11 -p python3注意需要联网 2. 安装Django 使用django 1.11.11版本&#xff0c;注意需要联网 pip install django1.11.113. 复习虚拟环境和pip的命令 # 虚拟环境 mkvirtualenv # 创建虚拟环境 rmvirtualenv # 删除虚拟…

python提取网页中p标签中的内容_使用Python进行爬虫的初学者指南

前言爬虫是一种从网站上抓取大量数据的自动化方法。即使是复制和粘贴你喜欢的网站上的引用或行&#xff0c;也是一种web抓取的形式。大多数网站不允许你保存他们网站上的数据供你使用。因此&#xff0c;唯一的选择是手动复制数据&#xff0c;这将消耗大量时间&#xff0c;甚至可…

OpenCv的连通域操作

由于项目需要&#xff0c;要对图像中的最大连通域进行标定&#xff0c;并且存储。首先需要使用cvFindCountour对边缘进行标定&#xff0c;其实它的原理就是连通域的边缘提取&#xff1b;其次就是对连通域进行大小判断找出最大的连通域&#xff1b;最后当然就是进行Rect并且ROI了…

DAY5网络

1.用select实现TCP客户端程序 #include <head.h> #define PORT 9999 // 服务器端口号 #define IP "192.168.125.74" // 服务器IP地址#define CIP "192.168.125.74" // 客户端 #define CPORT 6666int main(int argc, const char* argv[]) {// 创建套…

pyCharm最新2018激活码

最近开始研究python&#xff0c;因公司需要获取一些数据&#xff0c;比如电影名&#xff0c;航班名&#xff0c;航班号&#xff0c;列车号等等。所以让我来做一个爬虫去获取数据。所以我们就从这篇博文开始吧&#xff01;&#xff01;&#xff01; 首先告诉大家激活方式&#x…

提高电脑反应速度_设计师笔记本电脑推荐——视觉系ThinkBook 15p创造本

说到设计师笔记本电脑推荐&#xff0c;非近期大热的高颜值、高性能、更出彩的ThinkBook 15p创造本莫属了。作为专为时尚青年量身定做的品牌系列&#xff0c;ThinkBook一直都以“锐意破局”、“以思考重构世界”的理念征服着广大优秀的年轻创意用户&#xff0c;此次推出的全新产…

C语言 system函数

Windows函数windows操作系统下system () 函数详解&#xff08;主要是在C语言中的应用&#xff09; 函数名&#xff1a; system功 能&#xff1a; 发出一个DOS命令用 法&#xff1a; int system(char *command);system函数已经被收录在标准c库中&#xff0c;可以直接调用程序例…

javascript option 菜单图标_MacBook上神奇的Option键

不知道昨天的(Mac上Command键的妙用)有没有帮助到大家呢&#xff1f;今天我们就来讲一讲Mac上的Option键。Option作为Mac上四大控制键(Shift&#xff0c;Command&#xff0c;Control&#xff0c;Option)之一&#xff0c;Option在单独使用的情况下&#xff0c;很多时候是发挥调出…

OpenCV中cvResize函数图象放缩

图像大小变换void cvResize( const CvArr* src, CvArr* dst, int interpolationCV_INTER_LINEAR );src&#xff1a;输入图像. dst&#xff1a;输出图像. interpolation&#xff1a;差值方法: • CV_INTER_NN - 最近邻差值, • CV_INTER_LINEAR - 双线性差值 (缺省使用) • CV_…

学生电脑哪个牌子好_家用医用酒精棉球哪个牌子好,酒精棉片哪个牌子好

酒精制品是现在生活中常用的消毒剂&#xff0c;包括酒精消毒液、酒精洗手液、酒精喷雾剂、酒精湿巾、酒精棉球和酒精棉片等。这些酒精制品各有各的作用&#xff0c;市面上的品牌也比较多。下面就来介绍家用医用酒精棉球哪个牌子好。1、家用医用酒精棉球哪个牌子好目前在市面上使…

OpenCV图像的轮廓的匹配

一个跟轮廓相关的最常用到的功能是匹配两个轮廓.如果有两个轮廓,如何比较它们;或者如何比较一个轮廓和另一个抽象模板.矩比较两个轮廓最简洁的方式是比较他们的轮廓矩.这里先简短介绍一个矩的含义.简单的说,矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个…

Java Servlet 过滤器与 springmvc 拦截器的区别?

前言&#xff1a;在工作中&#xff0c;遇到需要记录日志的情况&#xff0c;不知道该选择过滤器还是拦截器&#xff0c;故总结了一下。 servlet 过滤器 定义 java过滤器能够对目标资源的请求和响应进行截取。过滤器的工作方式分为四种 应用场景 可以通过 doFilter 方法的 reques…

surface pro 6 黑苹果_微软Surface新款超薄触控笔抢鲜评测

微软Surface新款超薄触控笔抢鲜评测中文名&#xff1a;微软 Surface 超薄触控笔英文名&#xff1a;Surface Slim Pen日文名&#xff1a;Surface スリム ペン颜色&#xff1a;典雅黑&#xff08;只有这一个颜色&#xff09;中文官方链接&#xff1a;https://www.microsoftstore.…