opencv学习笔记(三十五)查找轮廓

    xiaoxiao2021-03-25  106

    现在终于可以讨论轮廓的问题了。首先我们需要了解轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线。表示方法可能根据不同情况而有所不同。有多种方法可以表示曲线。在OpenCV中一般用序列来存储轮廓信息。序列中的每一个元素是曲线中一个点的位置。关于序列表示的轮廓细节将在后面讨论,现在只要简单把轮廓想像为使用CvSeq表示的一系列的点就可以了。

    1、cvFindCantours()寻找轮廓 函数cvFindCantours()从二值图像中寻找轮廓。cvFindContours()处理的图像可以是从cvCanny()函数得到的有边缘像素的图像,或者是从cvThreshold()及cvAdaptiveThreshold()得到的图像,这时的边缘是正和负区域之间的边界。 cvFindContours()书上的定义1 int cvFindContours( IplImage* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), CvContourRetrievalMode mode=CV_RETR_LIST, CvChainApproxMethod method=CV_CHAIN_APPROX_SIMPLE ); 参数: 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:将所有的连码点,转换成点。存储所有的轮廓点,相邻的两个点的像素位置差不超过1。 CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分,例如一个矩形轮廓只需4个点来保存轮廓信息。 CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一种。 CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。 mode到底是什么意思 http://blog.csdn.net/lu597203933/article/details/14489225 在这片博文里面,说明了使用各个参数时,各个轮廓之间的关系。 轮廓分为外轮廓和内轮廓 如下图:外轮廓以c开头 内轮廓以h开头。 CV_RETR_EXTERNAL:first = c0 其他两种太复杂,没明白。

    2、重要:怎样找到自己想要的轮廓 上面还没说,找到了一个网站,说的很明白: http://www.tuicool.com/articles/NFVJRr 轮廓(contour)可以简单理解为一段连续的像素点。比如一个长方形的边,比如一条线,比如一个点,都属于轮廓。而轮廓之间有一定的层级关系,以下图为例: 主要说明以下概念: external & internal:对于最大的包围盒而言,2 是外部轮廓(external),2a 是内部轮廓(internal)。 parent & child:2 是 2a 的父轮廓(parent),2a 是 2 的子轮廓(child),3 是 2a 的子轮廓,同理,3a 是 3 的子轮廓,4 和 5 都是 3a 的子轮廓。 external | outermost:0、1、2 都属于最外围轮廓(outermost)。 hierarchy level:0、1、2 是同一层级(same hierarchy),都属于 hierarchy-0 ,它们的第一层子轮廓属于 hierarchy-1 。 first child:4 是 3a 的第一个子轮廓(first child)。实际上 5 也可以,这个看个人喜好了。

    在 OpenCV 中,通过一个数组表达轮廓的层级关系: [Next, Previous, First_Child, Parent] Next:同一层级的下一个轮廓。在上图中, 0 的 Next 就是 1 ,1 的 Next 就是 2 ,2 的 Next 是 -1 ,表示没有下一个同级轮廓。 Previous:同一层级的上一个轮廓。比如 5 的 Previous 是 4, 1 的 Previous 就是 0 ,0 的 Previous 是 -1 。 First_Child:第一个子轮廓,比如 2 的 First_Child 就是 2a ,像 3a 这种有两个 Child ,只取第一个,比如选择 4 作为 First_Child 。 Parent:父轮廓,比如 4 和 5 的 Parent 都是 3a ,3a 的 Parent 是 3 。

    findContours 是寻找轮廓的函数,其中的参数: mode:边缘检测的模式,包括: CV_RETR_EXTERNAL:只检索最大的外部轮廓(extreme outer),没有层级关系,只取根节点的轮廓。 关于CV_RETR_EXTERNAL 从上面可以看出来,为什么用这个参数不管什么图片的检测结果都是1了。 CV_RETR_LIST:检索所有轮廓,但是没有 Parent 和 Child 的层级关系,所有轮廓都是同级的。 CV_RETR_CCOMP:检索所有轮廓,并且按照二级结构组织:外轮廓和内轮廓。以前面的大图为例,0、1、2、3、4、5 都属于第0层,2a 和 3a 都属于第1层。 CV_RETR_TREE:检索所有轮廓,并且按照嵌套关系组织层级。以前面的大图为例,0、1、2 属于第0层,2a 属于第1层,3 属于第2层,3a 属于第3层,4、5 属于第4层。

    3、关于CV_RETR_CCOMP的辨析实例: 上面说了,CV_RETR_CCOMP:检索所有轮廓,并且按照二级结构组织:外轮廓和内轮廓。 http://bbs.csdn.net/topics/391935739 所以,如果图像是没有hole的,如: 这时,如果在cvFindcontours中的参数为CV_RETR_CCOMP,提取的结果是空的,因为对于CV_RETR_CCOMP,它是提取hole和最外框之间的外轮廓和内轮廓,此图没有内轮廓,所以没结果。 此时需要使用参数CV_RETR_LIST,就可以将轮廓提取出来: 如果图像为: 用CV_RETR_LIST提取出的轮廓为: 用CV_RETR_CCOMP提取出的轮廓为:

    4、重点:自己关于CV_RETR_CCOMP的辨析 要想使用cvFindContours()得到自己想要的轮廓计数结果,所有的“孔洞”必须是白色的,背景必须是黑色的。 可以拿下面两幅图自己试一下。

    5、自己写的查找轮廓实例:

    #include "highgui.h" #include "cv.h" void count(IplImage* image) { int Number_Object =0; //定义目标对象数量 CvMemStorage *stor = 0; CvSeq * cont = 0; CvContourScanner contour_scanner; CvSeq * a_contour= 0; stor = cvCreateMemStorage(0); cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), stor); Number_Object = cvFindContours(image, stor, &cont, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, cvPoint(0,0) ); //找到所有轮廓 printf("Number_Object: %d\n", Number_Object); } /*主函数*/ int main(int argc, char ** argv) { IplImage* img=cvLoadImage("10.jpg"); cvNamedWindow("Example1",CV_WINDOW_AUTOSIZE); cvShowImage("Example1",img); IplImage* dst = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1); cvCvtColor(img,dst,CV_BGR2GRAY); cvShowImage("GRAY",dst); count(dst); cvWaitKey(0); cvDestroyAllWindows(); return 0; }

    程序用到的图片

    转载请注明原文地址: https://ju.6miu.com/read-26180.html

    最新回复(0)