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这个描述还是很准确的,嵌套式的。大概如下图所示。

结构
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

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