【第19节】OpenCV的层次结构
目标
现在我们要学习轮廓的层次结构了,比如轮廓之间的父子关系。
原理
在前面的内容中我们使用函数CV2.findContours来查找轮廓,我们需要传入一个参数:轮廓提取模式( Contour_ Retrieval Mode )。我们总是把它设置为CV2.RETR_ LIST或者是CV2.RETR_ _TREE,效果还可以。但是它们到底代表什么呢?
同时,我们得到的结果包含3个数组,第一个图像,第二个是轮廓,第三E
个是层次结构。但是我们从来没有用过层次结构。层次结构是用来干嘛的呢?
层次结构与轮廓提取模式有什么关系呢?
这就是我们本节要讲的。
1、什么是层次结构
通常我们使用函数CV2.findContours在图片中查找一个对象。有时对象可能位于不同的位置。还有些情况,一个形状在另外-个形状的内部。这种情况下我们称外部的形状为父,内部的形状为子。按照这种方式分类,一幅图像中的所有轮廓之间就建立父子关系。这样我们就可以确定-个轮廓与其他轮廓是怎样连接的,比如它是不是某个轮廓的子轮廓,或者是父轮廓。这种关系就成为组织结构
下图就是一个简单的例子:

在这幅图像中,我给这几个形状编号为0-5。2和2a分别代表最外边矩形.
的外轮廓和内轮廓。
在这里边轮廓0,1, 2在外部或最外边。我们可以称他们为(组织结构)
0级,简单来说就是他们属于同一级。
接下来轮廓2a。我们把它当成轮廓2的子轮廓。它就成为(组织结构)第1级。同样轮廓3是轮廓2的子轮廓,成为(组织结构)第3级。最后轮廓4,5是轮廓3a的子轮廓,成为(组织结构)4级(最后一级)。按照这种方式给这些形状编号,我们可以说轮廓4是轮廓3a的子轮廓(当然轮廓5也是)。
我说这么多就是为了解释层次结构,外轮廓,子轮廓,父轮廓,子轮廓等。
现在让我们进入OpenCV吧。
2、OpenCV中层次结构
不管层次结构是什么样的,每一个轮廓都包含自己的信息:谁是父,谁是子等。OpenCV使用一个含有四个元素的数组表示。[Next, Previous,First_ Child, Parent]。
Next表示同一级组织结构中的下一个轮廓。
以上图中的轮廓0为例,轮廓1就是他的Next。同样,轮廓1的Next是2, Next=2。那轮廓2呢?在同一级没有Next。这时Next=-1。而轮廓4的Next为5,所以它的Next=5。
Previous表示同一级结构中的前一个轮廓。与前面一样,轮廓1的Previous为轮廓0,轮廓2的Previous为轮廓1。轮廓0没有Previous,所以Previous=-1。
First Child表示它的第一个子轮廓。
没有必要再解释了,轮廓2的子轮廓为2a。所以它的First_ _Child 为2a。那轮廓3a呢?它有两个子轮廓。但是我们只要第一个子轮廓,所以是轮廓4 (按照从上往下,从左往右的顺序排序)。
Parent表示它的父轮廓。
与First_ _Child刚好相反。轮廓4和5的父轮廓是轮廓3a。而轮廓3a的父轮廓是3。
注意:如果没有父或者子,值为-1。
现在我们了解了OpenCV中的轮廓组织结构。我们还是根据上边的图片再学习一下OpenCV中的轮廓检索模式。
CV2.RETR_ LIST,CV2.RETR _TREE,CV2.RETR_ CCOMP,CV2.RETR_ EXTERNAL
到底代表什么意思?
3、轮廓检索模式
RETR_ LIST从解释的角度来看,这中应是最简单的。它只是提取所有的轮廓,而不去创建任何父子关系。换句话说就是“人人平等",它们属于同一级组织轮廓。
所以在这种情况下,组织结构数组的第三和第四个数都是-1。但是,很明显,Next和Previous要有对应的值,你可以自己试着看看。
下面就是我得到的结果,每一行是对应轮廓的组织结构细节。例如,第一行对应的是轮廓0。下一个轮廓为1,所以Next=1。前面没有其他轮廓,所以Previous=0。接下来的两个参数就是-1,与刚才我们说的一样。
如果你不关心轮廓之间的关系,这是一个非常好的选择。
RETR_ EXTERNAL如果你选择这种模式的话,只会返回最外边的的轮廓,所有的子轮廓都会被忽略掉。
所以在上图中使用这种模式的话只会返回最外边的轮廓(第0级):轮廓0,1, 2。下面是我选择这种模式得到的结果:
当你只想得到最外边的轮廓时,你可以选择这种模式。这在有些情况下很有用。
RETR_CCOMP在这种模式下会返回所有的轮廓并将轮廓分为两级组织结构。例如,一个对象的外轮廓为第1级组织结构。而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓又是第1级组织结构。空洞的组织结构为第2级。
想象一下一副黑底白字的图像,图像中是数字0。0的外边界属于第一级组织结构,0的内部属于第2级组织结构。
我们可以以下图为例简单介绍一下。我们已经用红色数字为这些轮廓编号,并用绿色数字代表它们的组织结构。顺序与OpenCV检测轮廓的顺序一直。

现在我们考虑轮廓0,它的组织结构为第1级。其中有两个空洞1和2,它们属于第2级组织结构。所以对于轮廓0来说跟他属于同一级组织结构的下一个 ( Next) 是轮廓3,并且没有Previous。 它的Fist_ _Child 为轮廓1,组织结构为2。由于它是第1级,所以没有父轮廓。因此它的组织结构数组为[3,-1, 1, -1]。
现在是轮廓1,它是第2级。处于同一级的下一个轮廓为2。没有Previous,也没有Child, (因为是第2级所以有父轮廓)父轮廓是0。所以数组是[2,-1, -1, 0]。
轮廓2:它是第2级。在同一级的组织结构中没有Next。Previous 为轮廓1。没有子,父轮廓为0,所以数组是[-1, 1, -1, 0]
轮廓.3:它是第1级。在同一级的组织结构中Next为5。Previous为轮廓0。子为4,没有父轮廓,所以数组是[5,0, 4, -1]
轮廓4:它是第2级。在同-级的组织结构中没有Next。没有Previous,没有子,父轮廓为3,所以数组是[-1, -1,-1, 3]
下面是我得到的答案:
RETR_ TREE终于到最后一个了, 也是最完美的一个。这种模式下会返回所有轮廓,并且创建一个完整的组织结构列表。它甚至会告诉你谁是爷爷,爸.爸,儿子,孙子等。
还是以上图为例,使用这种模式,对OpenCV返回的结果重新排序并分析它,红色数字是边界的序号,绿色是组织结构。

轮廓0的组织结构为0,同一级中Next为7,没有Previous。子轮廓是1,没有父轮廓。所以数组是[7, -1, 1, -1]。轮廓1的组织结构为1,同一级中没有其他,没有Previous。子轮廓是2,父轮廓为0。所以数组是[-1, -1, 2, 0]。剩下的自己试试计算一下吧。下面是结果: