4.5. 等高线层次

4.5.1. 目标

这一次,我们学习了轮廓的层次结构,即轮廓中的父子关系。

4.5.2. 理论

在最后几篇关于等高线的文章中,我们使用了OpenCV提供的几个与等高线相关的函数。但是当我们用 cv2.查找目录() 函数,我们传递了一个参数, 轮廓提取模式 . 我们通常通过 cv2.RETR_LISTcv2.RETR_TREE 而且效果不错。但这到底是什么意思?

另外,在输出中,我们得到了三个数组,第一个是图像,第二个是轮廓,还有一个输出,我们称之为 等级制度 (请检查以前文章中的代码)。但我们从来没有用过这种等级制度。那么这个等级制度是什么?它是为了什么?它与前面提到的函数参数有什么关系?

这就是我们在这篇文章中要讨论的问题。

什么是等级制度?

通常我们使用 cv2.查找目录() 检测图像中对象的功能,对吗?有时对象位于不同的位置。但在某些情况下,某些形状在其他形状内。就像嵌套的图形一样。在这种情况下,我们称外部为 起源 而内在的 小孩 . 这样,图像中的轮廓就有了某种联系。我们可以指定一个轮廓是如何相互连接的,比如,它是其他轮廓的子轮廓,还是父轮廓等等 等级制度 .

请考虑下面的示例图像:

在这幅图中,有一些形状是我从中编号出来的 0-5 . 2和2a 表示最外面盒子的外部和内部轮廓。

在这里,等高线0,1,2是 外部或最外层 . 我们可以说,他们在 hierarchy-0 或者只是他们在 相同的层次结构级别 .

下一个来了 contour-2a . 它可以被认为是 child of contour-2 (或者相反,轮廓-2是轮廓-2a的母体)。所以让它进来吧 hierarchy-1 . 同样,contour-3是contour-2的子级,它位于下一个层次中。最后,等高线4、5是等高线3a的子级,它们位于最后一个层次级别。从我给盒子编号的方式来看,我可以说contour-4是contour-3a的第一个子代(也可以是contour-5)。

我提到这些是为了理解 相同的层次结构级别外部轮廓儿童轮廓母轮廓第一个孩子 等等。现在让我们进入OpenCV。

OpenCV中的层次表示

因此,每个等高线都有自己的关于它是什么层次结构、谁是它的子级、谁是它的父级等的信息。OpenCV将它表示为一个由四个值组成的数组: [[Next, Previous, First_Child, Parent]]

“Next表示同一层次上的下一个轮廓。”

例如,在我们的图片中取轮廓-0。下一个轮廓是谁?它是轮廓-1。简单地说 Next = 1 . 同样,对于Contour-1,下一个是Contour-2。所以 Next = 2 .

轮廓2呢?在同一级别上没有下一个轮廓。简单地说 Next = -1 . 轮廓4呢?它与轮廓-5处于同一水平。所以它的下一个轮廓是轮廓-5,所以 Next = 5 .

“Previous表示相同层次上的前一个轮廓。”

同上。等高线-1的上一个等高线是同一级别的等高线-0。与contour-2类似,它是contour-1。对于contour-0,没有前一个,所以将其设为-1。

“First_Child denotes its first child contour.”

不需要任何解释。对于轮廓-2,child是轮廓-2a,所以它得到轮廓-2a对应的索引值,那么轮廓-3a呢?它有两个孩子。但我们只带第一个孩子。它是轮廓-4。所以 First_Child = 4 轮廓-3a。

“Parent表示其父轮廓的索引。”

正好相反 First_Child . 等高线-4和等高线-5的母等高线均为等高线-3a。等高线-3a的母等高线为等高线-3,依此类推。

注意

如果没有子字段或父字段,则该字段将被视为-1

所以现在我们了解了OpenCV中使用的层次结构样式,我们可以借助上面给出的相同图像检查OpenCV中的轮廓检索模式。例如cv2.RETR_LIST、cv2.RETR_TREE、cv2.RETR_CCOMP、cv2.RETR_EXTERNAL等标志是什么意思?

4.5.3. 轮廓提取模式

1. 检索列表

这是四个标志中最简单的一个(从解释的角度)。它只检索所有轮廓,但不创建任何父子关系。 父母和孩子在这条规则下是平等的,他们只是轮廓 . 它们都属于同一层次。

所以在这里,层次数组中的第3和第4项总是-1。但显然,下一个和上一个术语将有其相应的值。你自己检查一下,然后核实一下。

下面是我得到的结果,每一行是对应轮廓的层次细节。对于eg,第一行对应于轮廓0。下一个轮廓是轮廓1。所以Next=1。没有先前的轮廓,因此previous=0。剩下的两个,如前所述,是-1。

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [ 3,  1, -1, -1],
        [ 4,  2, -1, -1],
        [ 5,  3, -1, -1],
        [ 6,  4, -1, -1],
        [ 7,  5, -1, -1],
        [-1,  6, -1, -1]]])

如果不使用任何层次结构功能,这是在代码中使用的好选择。

2. 外部检索

如果使用此标志,则仅返回极端外部标志。所有的儿童轮廓都被留下了。 我们可以说,根据这项法律,每个家庭只有长辈得到照顾。它不关心其他家庭成员:) .

那么,在我们的图像中,有多少极端的外部轮廓?ie处于0级?。只有3,即轮廓0,1,2,对吗?现在试着用这个标志找到轮廓。在这里,给每个元素的值与上面相同。将其与上述结果进行比较。下面是我得到的:

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [-1,  1, -1, -1]]])

如果只想提取外部轮廓,可以使用此标志。在某些情况下可能有用。

3. 混凝土

此标志检索所有轮廓并将其排列为2级层次结构。ie对象的外部轮廓(即其边界)放置在层次-1中。并且对象内部的孔(如果有)的轮廓被放置在层次-2中。如果其中有任何对象,则其轮廓仅在层次1中再次放置。以及它在第二层的洞等等。

只需考虑一个黑色背景上的“大白零点”图像。零的外圆属于第一层次,零的内圆属于第二层次。

我们可以用一个简单的图像来解释。在这里,我用红色标记轮廓的顺序,以及它们所属的层次结构,用绿色标记(1或2)。顺序与OpenCV检测轮廓的顺序相同。

所以考虑第一个轮廓,即轮廓-0。它是层次结构-1。它有两个孔,轮廓1和2,它们属于层次2。所以对于contour-0,同一层次中的下一个contour是contour-3。以前也没有。它的第一个子是第二层中的contour-1。它没有父级,因为它在层次结构1中。所以它的层次数组是 [3,-1,1,-1]

现在取轮廓-1。它在第二层。下一个在同一层次中(在contour-1的父项下)是contour-2。没有上一个。没有子级,但父级是contour-0。所以数组是 [2,-1,-1,0] .

同样,轮廓-2:它在层次-2中。在contour-0下,同一层次中没有下一个轮廓。所以没有下一个。前面是轮廓-1。没有子级,父级为contour-0。所以数组是 [-1,1,-1,0] .

轮廓-3:层次1中的下一个是轮廓-5。前面是轮廓-0。子对象是轮廓-4,没有父对象。所以数组是 [5,0,4,-1] .

轮廓-4:它在轮廓-3下的层次2中,没有同级。所以没有下一个,没有上一个,没有孩子,父母是轮廓-3。所以数组是 [-1,-1,-1,3] .

剩下的你可以加满。这是我得到的最后答案:

>>> hierarchy
array([[[ 3, -1,  1, -1],
        [ 2, -1, -1,  0],
        [-1,  1, -1,  0],
        [ 5,  0,  4, -1],
        [-1, -1, -1,  3],
        [ 7,  3,  6, -1],
        [-1, -1, -1,  5],
        [ 8,  5, -1, -1],
        [-1,  7, -1, -1]]])

4. RETR_TREE

这是最后一个人,完美先生。它检索所有轮廓并创建完整的族层次结构列表。 它甚至告诉我们,谁是爷爷、爸爸、儿子、孙子,甚至更远的……:) .

例如,我拍摄了上面的图像,重写了cv2.RETR_TREE的代码,根据OpenCV给出的结果对轮廓重新排序并进行分析。同样,红色字母表示轮廓数,绿色字母表示层次顺序。

取轮廓-0:它在层次-0中。同一层次中的下一个轮廓是轮廓7。没有以前的轮廓。孩子是轮廓-1。没有父母。所以数组是 [7,-1,1,-1] .

以轮廓2为例:它在层次1中。同一水平面上没有轮廓。没有上一个。孩子是轮廓-2。父级为contour-0。所以数组是 [-1,-1,2,0] .

剩下的,试试你自己。以下是完整答案:

>>> hierarchy
array([[[ 7, -1,  1, -1],
        [-1, -1,  2,  0],
        [-1, -1,  3,  1],
        [-1, -1,  4,  2],
        [-1, -1,  5,  3],
        [ 6, -1, -1,  4],
        [-1,  5, -1,  4],
        [ 8,  0, -1, -1],
        [-1,  7, -1, -1]]])

4.5.4. 额外资源

4.5.5. 练习