OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
轮廓层级

上一个教程: 轮廓:更多函数

目标

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

理论

在前面几篇关于轮廓的文章中,我们已经使用了 OpenCV 提供的几个与轮廓相关的函数。但是当我们使用 cv.findContours() 函数在图像中查找轮廓时,我们传递了一个参数,轮廓检索模式。我们通常传递 cv.RETR_LISTcv.RETR_TREE,并且效果很好。但它实际上意味着什么?

此外,在输出中,我们得到了三个数组,第一个是图像,第二个是我们的轮廓,还有一个我们命名为 hierarchy 的输出(请查看之前文章中的代码)。但我们从未在任何地方使用过这个 hierarchy。那么这个 hierarchy 是什么?它有什么用?它与前面提到的函数参数有什么关系?

这就是我们将在本文中讨论的内容。

什么是层次结构?

通常我们使用 cv.findContours() 函数来检测图像中的对象,对吗?有时对象位于不同的位置。但在某些情况下,一些形状位于其他形状内部。就像嵌套图形一样。在这种情况下,我们称外部形状为 父级,内部形状为 子级。这样,图像中的轮廓彼此之间存在一些关系。我们可以指定一个轮廓如何连接到另一个轮廓,例如,它是某个其他轮廓的子级,还是父级等。这种关系的表示称为 层次结构

考虑下面的示例图像

image

在此图像中,有一些形状,我已将其编号为 0-52 和 2a 表示最外层框的外部和内部轮廓。

在这里,轮廓 0,1,2 是 外部或最外层。我们可以说,它们位于 层次结构-0 中,或者简称为 相同的层次级别

接下来是 轮廓-2a。它可以被认为是 轮廓-2 的子级(或以相反的方式,轮廓-2 是轮廓-2a 的父级)。因此,让它位于 层次结构-1 中。类似地,轮廓-3 是轮廓-2a 的子级,它位于下一个层次结构中。最后,轮廓 4,5 是轮廓-3a 的子级,它们位于最后一个层次结构级别中。从我编号框的方式来看,我会说轮廓-4 是轮廓-3a 的第一个子级(也可以是轮廓-5)。

我提到这些事情是为了理解诸如 相同的层次级别外部轮廓子轮廓父轮廓第一个子级 等术语。现在让我们进入 OpenCV。

OpenCV 中的层次结构表示

因此,每个轮廓都有自己的信息,说明它属于哪个层次结构,谁是它的子级,谁是它的父级等等。OpenCV 将其表示为一个包含四个值的数组:[下一个,上一个,第一个子级,父级]

“Next 表示同一层级级别的下一个轮廓。”

例如,以我们图片中的轮廓-0 为例。与其在同一级别中的下一个轮廓是谁?是轮廓-1。所以只需将 Next = 1。类似地,对于轮廓-1,下一个是轮廓-2。所以 Next = 2。

轮廓-2 呢?在同一级别中没有下一个轮廓。所以只需将 Next = -1。轮廓-4 呢?它与轮廓-5 位于同一级别。因此,它的下一个轮廓是轮廓-5,所以 Next = 5。

“上一个表示同一层次级别中的上一个轮廓。”

与上面相同。轮廓-1 的上一个轮廓是同一级别中的轮廓-0。类似地,对于轮廓-2,它是轮廓-1。对于轮廓-0,没有上一个轮廓,所以将其设置为 -1。

“First_Child 表示它的第一个子轮廓。”

无需任何解释。对于轮廓-2,子级是轮廓-2a。因此,它获得轮廓-2a 的对应索引值。轮廓-3a 呢?它有两个子级。但我们只取第一个子级。它是轮廓-4。因此,轮廓-3a 的 First_Child = 4。

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

它与 First_Child 正好相反。对于轮廓-4 和轮廓-5,父轮廓都是轮廓-3a。对于轮廓-3a,它是轮廓-3,依此类推。

注意
如果没有子轮廓或父轮廓,则该字段设置为 -1

所以现在我们了解了 OpenCV 中使用的层次结构样式,我们可以借助上面给出的同一图像检查 OpenCV 中的轮廓检索模式。即 cv.RETR_LISTcv.RETR_TREEcv.RETR_CCOMPcv.RETR_EXTERNAL 等标志意味着什么?

轮廓检索模式

1. RETR_LIST

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

因此,层次结构数组中的第 3 个和第 4 个术语始终为 -1。但很明显,“下一个”和“上一个”术语将具有其对应的值。

2. RETR_EXTERNAL

如果使用此标志,它将仅返回最外层的标志。所有子轮廓都将被留下。我们可以说,在此规则下,只照顾每个家庭中最年长的人。它不关心家庭的其他成员)

3. RETR_CCOMP

此标志检索所有轮廓并将它们排列到 2 级层次结构中。即对象的外部轮廓(即其边界)放置在层次结构-1 中。对象内部孔的轮廓(如果有)放置在层次结构-2 中。如果其中有任何对象,则其轮廓仅再次放置在层次结构-1 中。它的孔在层次结构-2 中,依此类推。

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

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

image

因此,考虑第一个轮廓,即轮廓-0。它位于层次结构-1 中。它有两个孔,轮廓 1 和 2,它们属于层次结构-2。因此,对于轮廓-0,同一层次结构级别中的下一个轮廓是轮廓-3。没有上一个。它的第一个子级是层次结构-2 中的轮廓-1。它没有父级,因为它位于层次结构-1 中。因此,它的层次结构数组是 [3,-1,1,-1]

现在以轮廓-1 为例。它位于层次结构-2 中。同一层次结构中的下一个(在轮廓-1 的父级下)是轮廓-2。没有上一个。没有子级,但父级是轮廓-0。因此,数组为 [2,-1,-1,0]。

类似地,轮廓-2:它位于层次结构-2 中。轮廓-0 下同一层次结构中没有下一个轮廓。因此,没有下一个。上一个是轮廓-1。没有子级,父级是轮廓-0。因此,数组为 [-1,1,-1,0]。

轮廓 - 3:层次结构-1 中的下一个是轮廓-5。上一个是轮廓-0。子级是轮廓-4,没有父级。因此,数组为 [5,0,4,-1]。

轮廓 - 4:它位于轮廓-3 下的层次结构 2 中,并且没有同级。因此,没有下一个,没有上一个,没有子级,父级是轮廓-3。因此,数组为 [-1,-1,-1,3]。

4. RETR_TREE

这是最后一位,完美先生。它检索所有轮廓并创建一个完整的家庭层次结构列表。它甚至会告诉你谁是爷爷,父亲,儿子,孙子,甚至更远... :)

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

image

以轮廓-0 为例:它位于层次结构-0 中。同一层次结构中的下一个轮廓是轮廓-7。没有上一个轮廓。子级是轮廓-1。没有父级。因此,数组为 [7,-1,1,-1]。

以轮廓-2 为例:它位于层次结构-1 中。同一级别中没有轮廓。没有上一个。子级是轮廓-2。父级是轮廓-0。因此,数组为 [-1,-1,2,0]。