findContours && drawContours

先来看看findContours的声明


[cpp] view plain copy
print ?
  1. findContours( InputOutputArray image, OutputArrayOfArrays contours,  
  2.           OutputArray hierarchy, int mode,  
  3.               int method, Point offset=Point());  
findContours( InputOutputArray image, OutputArrayOfArrays contours,
	      OutputArray hierarchy, int mode,
              int method, Point offset=Point());

[cpp] view plain copy
print ?
  1. findContours( InputOutputArray image, OutputArrayOfArrays contours,  
  2.               int mode, int method, Point offset=Point());  
findContours( InputOutputArray image, OutputArrayOfArrays contours,
              int mode, int method, Point offset=Point());


   image:输入图像。8-bit的单通道二值图像,非零的像素都会被当作1。

   contours:检测到的轮廓。是一个向量,向量的每个元素都是一个轮廓。因此,这个向量的每个元素仍是一个向量。即


[cpp] view plain copy


print?

  1. vector > contours;  

vector > contours;

    hierarchy:各个轮廓的继承关系。hierarchy也是一个向量,长度和contours相等,每个元素和contours的元素对应。hierarchy的每个元素是一个包含四个整型数的向量。即:

[cpp] view plain copy


print?

  1. vector hierarchy; //Vec4i is a vector contains four number of int  

vector hierarchy; //Vec4i is a vector contains four number of int

    hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3],分别表示的是第i条轮廓(contours[i])的下一条,前一条,包含的第一条轮廓(第一条子轮廓)和包含他的轮廓(父轮廓)。

    mod:检测轮廓的方法。有四种方法。

    —CV_RETR_EXTERNAL:只检测外轮廓。忽略轮廓内部的洞。

    —CV_RETR_LIST:检测所有轮廓,但不建立继承(包含)关系。

    —CV_RETR_TREE:检测所有轮廓,并且建立所有的继承(包含)关系。也就是说用CV_RETR_EXTERNAL和CV_RETR_LIST方法的时候hierarchy这个变量是没用的,因为前者没有包含关系,找到的都是外轮廓,后者仅仅是找到所哟的轮廓但并不把包含关系区分。用TREE这种检测方法的时候我们的hierarchy这个参数才是有意义的。事实上,应用前两种方法的时候,我们就用findContours这个函数的第二种声明了。

    —CV_RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。外轮廓放到顶层,外轮廓包含的第一层内轮廓放到底层,如果内轮廓还包含轮廓,那就把这些内轮廓放到顶层去。

    method:表示一条轮廓的方法。

    – CV_CHAIN_APPROX_NONE:把轮廓上所有的点存储。

    – CV_CHAIN_APPROX_SIMPLE:只存储水平,垂直,对角直线的起始点。对drawContours函数来说,这两种方法没有区别。

    – CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:实现的“Teh-Chin chain approximation algorithm.”这个不太懂。他们的论文:Teh, C.H. and Chin, R.T., On the Detection of Dominant Points on Digital Curve. PAMI 11 8, pp 859-872 (1989)

    offset:Optional offset by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context。懒得翻译了,直接把参考手册原文拿过来了。


     函数将白色区域当作前景物体。所以找轮廓找到的是白色区域的轮廓。

     这个函数有一个特点,如果白色区域延伸到了图像边界,那么图像的边界也是被当作轮廓的一部分。测试如下:

[cpp] view plain copy


print?

  1. //read as gray image  
  2. Mat im=imread("G:\\sample.png",0);  
  3. if(!im.data) {cout<<"Can't find image!";return -1;}  
  4.   
  5. //change the image to binary by setting a threshold  
  6. threshold(im,im,120,255,THRESH_BINARY);  
  7.   
  8. vector > contours;  
  9. vector hierarchy;  
  10. findContours(im,contours,hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);  
  11.   
  12. Mat contoursImage(im.rows,im.cols,CV_8U,Scalar(255));  
  13. for(int i=0;i
  14.     if(hierarchy[i][3]!=-1) drawContours(contoursImage,contours,i,Scalar(0),3);  
  15. }  

//read as gray image Mat im=imread("G:\\sample.png",0); if(!im.data) {cout<<"Can't find image!";return -1;} //change the image to binary by setting a threshold threshold(im,im,120,255,THRESH_BINARY); vector > contours; vector hierarchy; findContours(im,contours,hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE); Mat contoursImage(im.rows,im.cols,CV_8U,Scalar(255)); for(int i=0;i

     这是测试代码,注意,这里我们用的是CV_RETR_CCOMP这个方法。代码实现了找到图像的轮廓,并且,如果该轮廓有父轮廓就画出来

findContours && drawContours_第1张图片

这是测试用的图像。

findContours && drawContours_第2张图片findContours && drawContours_第3张图片
左边是运行结果,右边是把所有的轮廓画出来。可见,白色区域的轮廓包含了整个图像的边界。

并且有一点令人疑惑的是,原图中的那几个白色小圆圈应该也有父轮廓吧,至少是包含在黑色长方形中的。其实这几个小白色的区域是有父轮廓的,只不过它们是子轮廓的子轮廓,所以就被安排到第一级上了。

当我们变换参数为CV_RETR_TREE的时候,我们可以看到白色的圆圈的轮廓又出来了。

findContours && drawContours_第4张图片







 在OpenCV中当我们使用一些基于连通域或者基于边沿信息的时候,往往会对contour进行处理除了利用findContours函数寻找连通域之外,也需要对连通域进行填充等操作这里介绍下drawContours函数,自己也是不断摸索和查阅了opencv相关手册

drawContours    在图像中绘制外部和内部的轮廓。
drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(),int maxLevel=INT_MAX, Point offset=Point())
image: 是最终需要被填充的图像;
contours:是得到的一系列点的集合;
contourIdx:是指定某个contours的点集;
color:被填充的颜色,单色可以设置为Scalar(255)等;
thickness: 所画Contour的线条宽度,如果为负或CV_FILLED则绘制所有的Contours;
lineType: 线的连通性;
hierarchy:可选层次信息结构,这里面是findContours所的到的基于Contours的层级信息;
maxLevel: 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓
offset:照给出的偏移量移动每一个轮廓点坐标.当轮廓是从某些感兴趣区域(ROI)中提取的然后需要在运算中考虑ROI偏移量时,将会用到这个参数。

      比如我通过findContours函数得到了相应的vector > vecContours点集信息,要把这些点集信息画出来
binImage为要画的图片,binImage事先已经声明好了,例子如下:

[cpp] view plain copy
print ?
  1. binImage.setTo(0);  
  2. for ( size_t i = 0; i < vecContours.size(); i++ ){  
  3.     cv::drawContours( binImage, vecContours, i, Scalar(255), CV_FILLED, 8, vector(), 0, Point() );  
  4. }  

你可能感兴趣的:(study_C++)