opencv_contours学习笔记(一)

来源:互联网 发布:清华大学新网络学堂 编辑:程序博客网 时间:2024/06/11 02:14

轮廓contours

什么叫轮廓呢?大概就是能以某种方式连续连接起来的一系列点所组成的一个整体,当然了,点也能看做是一种特殊的轮廓吧。

了解到,利用cv的库函数findcontours提取出来的轮廓是有一定的内在关系的,比如说内外轮廓的包含,父子关系等等,可以通过这些关系来提取出我们真正所需要的轮廓。

而cv的库函数Canny所提取出来的轮廓则是没有所谓的内在关系的。

那么,以下图为例,来说明findcontours提取出来的轮廓的具体关系
这里写图片描述

1、对于最大的矩形而言,B是其外轮廓,B’是其内轮廓
2、对元素B是B’的父轮廓,所以B’就是B的子轮廓。C就是B’的子轮廓,D和E都是C’的子轮廓,而D或E其中一个是C’的第一个子轮廓。
3、A和B都属于最外围的轮廓。它们是同一层级,均属于 hierarchy[0]

在opencv中,通过一个数组来表达轮廓的层级关系

[Next, Previous, Parent, First_Child]

1、Next表示同一层级的下一个轮廓。比如A的Next是B,但B的Next没有了,用-1表示。
2、Previous表示同一层级的上一个轮廓。比如B的上一层级是A,但A的上一层级没有了,也用-1表示。
3、父子关系,前面已经提过了。

好的,那么现在对findcontours的轮廓已经有了一个基本的认识,现在来看下API接口。

findContours( InputOutputArray image,         //参数1              OutputArrayOfArrays contours,   //参数2              OutputArray hierarchy,          //参数3              int mode,                       //参数4              int method,                     //参数5              Point offset=Point());          //参数6

·参数1:单通道的图像,可以是灰度图,一般是经过canny、laplace等边缘检测后得到的二值图像。

·参数2:定义为

vector<vector<Point>> contours

这是一个向量,向量内的每一个元素,分别是一组连续的点(Point(x,y))构成的点的集合所组成的向量。也就是说,一组点(Point(x,y))构成的集合就是一个轮廓,轮廓的个数就是向量contours的总数。

·参数3:定义为

vector<Vec4i> hierarchy

这是一个向量,向量内的每个元素与轮廓向量coutours内的元素一一对应,hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓的Next、Previous、father、child轮廓的索引编号。如果当前第i个轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或子轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。

·参数4:mode定义轮廓的检索模式,有些面的参数可以选择:
1)

CV_RETR_EXTERNAL       //只检测最外围轮廓,包含在外轮廓内的内轮廓被忽略

2)

CV_RETR_LIST   //检测所有轮廓。但轮廓之间不建立等级关系,彼此相互独立。               //也就是在此模式下不存在父轮廓和子轮廓,hierarchy的[i][3],[i][4]均为-1

3)

CV_RETR_CCOMP  //检测所有轮廓。但轮廓之间只有两个等级关系,外围为顶层,外层内的轮廓都属于它的子轮廓

4)

CV_RETR_TREE  //检测所有轮廓,轮廓之间建立一个完整的关系,就如同前面讲的例子一样。

·参数5:定义轮廓近似方法method
1)

CV_CHAIN_APPROX_NONE   //保存物体边界上所有连续的轮廓点到coutours向量内

2)

CV_CHAIN_APPROX_SIMPLE //仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不保留

·参数6:一般就用默认的啦,后面需要再做学习吧,大概就是一个偏移量。让轮廓往别的地方去。

噢,BTW,需要用一个绘制轮廓的API来将提取的轮廓绘制出来

    for (int i = 0; i< contours.size(); i++)    {        drawContours(contoursImage, contours, i, Scalar(255), 1, 8, hierarchy);    }

特别注意这个i是需要绘制的边缘索引,如果全部绘制则为-1

好,基本知识也知道了,API也懂了,那来做个试验吧。
首先上原图:
这里写图片描述

method参数为CV_CHAIN_APPROX_NONE

将检测到的所有轮廓保存至contours向量中

实验前我遇到一个问题,就是你不提前把边缘提取出来的话边缘图的最外层会被默认画出来,你在边缘图上只能看到最外层的一个框框。。。所以最好先用canny或者什么方法来先把原图的边缘先提取出来。

下面改变mode参数:
1)当mode参数为CV_RETR_EXTERNAL
这个参数只检测最外围轮廓,包含在外轮廓内的内轮廓被忽略,那么表现在结果上你猜猜是什么?没错,那几个原点是会被忽略掉的。结果如图:
这里写图片描述
2)当mode参数为CV_RETR_LIST
这个参数检测所有轮廓。但轮廓之间不建立等级关系,彼此相互独立。也就是在此模式下不存在父轮廓和子轮廓,hierarchy的[i][3],[i][4]均为-1
3)当mode参数为CV_RETR_CCOMP 和CV_RETR_TREE,就是检测所有轮廓,只不过层级不一样,这个就不把实验放出来了。

实验代码在此下方,你可以下下来自己改参数来观察实验结果,并自己发散一下。
http://download.csdn.net/download/csdn_dzh/10142914