Opencv轮廓检测findContours分析(层次结构)

Opencv 轮廓检测相关api文档

  • opencv2的c++接口

官方文档相关api

Finds contours in a binary image.

在二值图像中找到轮廓

C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())

parameters:

  • image – Source, an 8-bit single-channel image. Non-zero pixels are treated as 1’s. Zero pixels remain 0’s, so the image is treated as binary . You can use compare() , inRange() , threshold() , adaptiveThreshold() , Canny() , and others to create a binary image out of a grayscale or color one. The function modifies the image while extracting the contours. If mode equals to CV_RETR_CCOMP or CV_RETR_FLOODFILL, the input can also be a 32-bit integer image of labels (CV_32SC1).

    输入图像是8位单通道的图像(256级灰度图)。其中像素点的非0灰度值被当成1,0值保持0。所以输入图像被当成一个二值图像对待。可以用compare() , inRange() , threshold() , adaptiveThreshold() , Canny() 或者其他方法来从灰度图或者彩色图中生成二值图像。该函数在提取轮廓的过程中会改变图像。如果模式(mode)CV_RETR_CCOMP 或者 CV_RETR_FLOODFILL,输入图像也可以是32位的整型图像(CV_32SC1)。

  • contours – Detected contours. Each contour is stored as a vector of points.

    找到的轮廓,其中每个轮廓会被存储为vector<Point>,所以contours的类型就是vector<vector<Point>>

  • hierarchy – Optional output vector, containing information about the image topology. It has as many elements as the number of contours. For each i-th contour contours[i] , the elements hierarchy[i][0] hiearchy[i][1] , hiearchy[i][2] , and hiearchy[i][3] are set to 0-based indices in contours of the next and previous contours at the same hierarchical level, the first child contour and the parent contour, respectively. If for the contour[i] there are no next, previous, parent, or nested contours, the corresponding elements of hierarchy[i] will be negative.

    层次结构,可选的输出向量,包含关于图像的拓扑结构信息。其具有跟轮廓 数相同的元素个数,类型为vector<Vec4i>。对每个轮廓,contours[i],其他的懒得翻译了,也不懂他讲的什么意思,只有看了源码后估计才能够知道。

  • mode –Contour retrieval mode

    检索轮廓的模式。

    • CV_RETR_EXTERNAL retrieves only the extreme outer contours. It sets hierarchy[i][2]=hierarchy[i][3]=-1 for all the contours.就是只提取最外层轮廓,其他的懒得翻译
    • CV_RETR_LIST retrieves all of the contours without establishing any hierarchical relationships.提取所有轮廓,但是不建立任何层次关系。。。关于这个层次关系。。有点懵逼。有时间看下实现。
    • CV_RETR_CCOMP retrieves all of the contours and organizes them into a two-level hierarchy. At the top level, there are external boundaries of the components. At the second level, there are boundaries of the holes. If there is another contour inside a hole of a connected component, it is still put at the top level.检索所有轮廓并且将组织成为两个级别层次,顶层是外部边界,第二层,是洞的边界。然后如果,有另外一个轮廓在一个连通部分的洞中,放到顶层。有毒。。。
    • CV_RETR_TREE retrieves all of the contours and reconstructs a full hierarchy of nested contours. This full hierarchy is built and shown in the OpenCV contours.c demo。检索所欲的轮廓,并且生成完整的嵌套的轮廓层次结构,,真的有毒。完整的层次结构在。。。。。。。。。懒得翻译了。不过可以看看效果。
  • method –Contour approximation method

    方法,轮廓估计的方法。

    • CV_CHAIN_APPROX_NONE stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is, max(abs(x1-x2),abs(y2-y1))==1.存储所有的轮廓点,就是说,任何一个包含一两个点的子序列(不改变顺序索引的连续的),两个点,要么是水平,要么是垂直,要么是对角的邻点。也就是说两个像素坐标的分别的差值的绝对值最大值等于1.。。妈的,真的有毒,说了这么一堆,不就是两个点在坐标上相邻吗。。
    • CV_CHAIN_APPROX_SIMPLE compresses horizontal, vertical, and diagonal segments and leaves only their end points. For example, an up-right rectangular contour is encoded with 4 points.压缩水平垂直对角等,只存储结束点什么的,比如矩形就只存储4个点。
    • CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS applies one of the flavors of the Teh-Chin chain approximation algorithm. See [TehChin89] for details.不翻译了,大概是什么近似算法。
  • 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.可选的偏移,就是简单的平移,特别是在做了ROI步骤之后有用。

翻译完了官方文档真不太知道什么意思,特别是什么层次结构,说的简直有毒。跑例子看一下。

还有一个相关的api

Draws contours outlines or filled contours.

画轮廓

void 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() )

Parameters:
image – Destination image.
contours – All the input contours. Each contour is stored as a point vector.
contourIdx – Parameter indicating a contour to draw. If it is negative, all the contours are drawn.
color – Color of the contours.
thickness – Thickness of lines the contours are drawn with. If it is negative (for example, thickness=CV_FILLED ), the contour interiors are drawn.
lineType – Line connectivity. See line() for details.
hierarchy – Optional information about hierarchy. It is only needed if you want to draw only some of the contours (see maxLevel ).
maxLevel – Maximal level for drawn contours. If it is 0, only the specified contour is drawn. If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This parameter is only taken into account when there is hierarchy available.
offset – Optional contour shift parameter. Shift all the drawn contours by the specified \texttt{offset}=(dx,dy) .
contour – Pointer to the first contour.
externalColor – Color of external contours.
holeColor – Color of internal contours (holes).大概解释在这里,没什么好翻译的。

现在开始分析层次结构,直接看他的描述有毒

从官方代码开始

#include "cv.h"
#include "highgui.h"

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    Mat src;
    // the first command-line parameter must be a filename of the binary
    // (black-n-white) image
    if( argc != 2 || !(src=imread(argv[1], 0)).data)
        return -1;

    Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);

    src = src > 1;
    namedWindow( "Source", 1 );
    imshow( "Source", src );

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours( src, contours, hierarchy,
        CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

    // iterate through all the top-level contours,
    // draw each connected component with its own random color
    int idx = 0;
    for( ; idx >= 0; idx = hierarchy[idx][0] )
    {
        Scalar color( rand()&255, rand()&255, rand()&255 );
        drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );
    }

    namedWindow( "Components", 1 );
    imshow( "Components", dst );
    waitKey(0);
}

程序很简单,其能够达到的效果是将连通的部分用随机颜色填充。

效果图如下

填充效果

确实将分别连通的两个部分填充,很神奇,肯定是用到了那个层次结构,对层次结构进行分析。

那么我先讲检测到的所有轮廓分别用不同颜色画出来。代码很简单,就是不停在两点之间画线。

    for (size_t i = 0; i < contours[0].size(); i++)
    {
        line(dst, contours[0][i], contours[0][(i + 1) % contours[0].size()], Scalar(0, 255, 0), 1, 8);
    }
    for (size_t i = 0; i < contours[1].size(); i++)
    {
        line(dst, contours[1][i], contours[1][(i + 1) % contours[1].size()], Scalar(0, 0, 255), 1, 8);
    }

    for (size_t i = 0; i < contours[2].size(); i++)
    {
        line(dst, contours[2][i], contours[2][(i + 1) % contours[2].size()], Scalar(255, 0, 0), 1, 8);
    }
    for (size_t i = 0; i < contours[3].size(); i++)
    {
        line(dst, contours[3][i], contours[3][(i + 1) % contours[3].size()], Scalar(255, 255, 255), 1, 8);
    }

由于之前调试就知道检测出来了4条,所以这里才这样写简单些。
结果图

各个轮廓
  • 绿色第一条
  • 红色第二条
  • 蓝色底三条
  • 白色第四条

这个颜色记住,后面分析有用。

现在开始再重新看之前的层次的定义。

hierarchy[i][0] hiearchy[i][1] , hiearchy[i][2] , and hiearchy[i][3] are set to 0-based indices in contours of the next and previous contours at the same hierarchical level, the first child contour and the parent contour, respectively.
这里主要有两个概念,或者说两个层次,一个是直接上一个下一个,好有一个是父轮廓和子轮廓,很神奇,但是好像有点那个意思了,有嵌套的感觉。再继续分析。把变量都拿出来分析。

contours
hierarchy

可以看到0轮廓的下一条轮廓是2轮廓,2轮廓的下一条-1表示没有了。
同时2轮廓的上一条是0,0轮廓的上一条是-1表示没有。所以这就是最外层的轮廓。

再分析,0轮廓的子轮廓是1轮廓,1轮廓的父轮廓当然是0轮廓,再看图


各个轮廓

0、1轮廓分别是绿色和红色,确实有明显的包含关系。到这里就很清楚了。

相当于一个树形结构,上一条下一条针对同一个级别的轮廓,父子轮廓就表示洞类似的结构。

之前源程序的填充方法也可以很简单的想处理,判断是否是父子轮廓之间就行。

到这里轮廓检测的基础部分基本完成,主要其实就是层次结构的分析,nested这个描述还是很准确的,嵌套式的。大概如下图所示。

结构

推荐阅读更多精彩内容

  • 谢谢你, 彼时赋予我这个时刻 你应该有个好梦, 一个让你不愿苏醒的美梦, 可我又怕我是一阵风, 把你的梦吹碎了, ...
    二兜二年阅读 157评论 0 3
  • ( 张志军是从基层报道组写稿写出来的优秀人才。他当过邯郸电台台长、总编,现任邯郸记协主席,出过多部新闻专著,成就辉...
    邯郸赵金海阅读 665评论 2 0
  • 不知道大家有没有这样的时候,学习太累不学了吧,那么你以后没有好工作,生活不好就不要抱怨,因为这是你该得的。 ...
    蓝果果阅读 238评论 0 0