OpenCV  4.10.0
开源计算机视觉
加载中...
搜索中...
无匹配项
轮廓层次结构

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

目标

这一次,我们了解有关轮廓层次结构的内容,即轮廓中的父子关系。

理论

几篇有关轮廓的最新文章中,我们操作过 OpenCV 提供的几个与轮廓相关的函数。但是,当我们使用 cv.findContours() 函数找到图像中的轮廓时,我们传递了一个参数,轮廓检索模式。我们通常传递 cv.RETR_LISTcv.RETR_TREE,效果不错。但这实际上是什么意思?

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

这就是我们在本文中要处理的内容。

什么是等级结构?

我们通常使用 cv.findContours() 函数来检测图像中的对象,对吧?有时对象位于不同的位置。但在某些情况下,某些形状位于其他形状内。就像嵌套图形一样。在这种情况下,我们将外部的称为 parent,将内部的称为 child。通过这种方式,图像中的轮廓会相互之间存在某些关系。而且,我们可以指定一个轮廓如何与另一个轮廓连接,例如,它是否为某个其他轮廓的子对象,或者它是否为父对象等。这种关系的表示形式称为 等级结构

考虑以下示例图像

图像

在这个图像中,有几个形状,我用 0-5 对它们进行了编号。2 和 2a 表示最外围框的外部和内部轮廓。

在此,轮廓 0、1、2 是 外部或最外部 的。我们可以说,它们处于 hierarchy-0,或者说,它们处于 相同的层次结构级别

接下来是 contour-2a。它可以被看作是 contour-2孩子(或者相反,contour-2 是 contour-2a 的父级)。因此,让它处于 层次结构-1 中。类似地,contour-3 是 contour-2 的孩子并且它属于下一个层次结构。最后,contour 4、5 是 contour-3a 的孩子,它们属于最后一个层次结构级别。从我给盒子编号的方式,我想说 contour-4 是 contour-3a 的第一个孩子(也可能是 contour-5)。

我提到这些是为了帮助理解以下术语:相同层次结构级别外部contour孩子contour父级contour第一个孩子等。现在,我们来了解 OpenCV。

OpenCV 中的层次结构表示

因此,每个 contour 都拥有有关其层次结构、其孩子是谁、其父级是谁等的信息。OpenCV 将它表示为一个包含四个值:[Next,Previous,First_Child,Parent] 的数组。

*“Next 表示处于相同层次结构级别的下一个 contour”。*

例如,取我们图片中的 contour-0。它的下一contour 是什么?它是 contour-1。因此,简单来说,Next = 1。类似地,对于 Contour-1,下一个是 contour-2。因此,Next = 2。

Contour-2 怎么样?在同一级别没有下一个 contour。所以,简单来说,Next = -1。Contour-4 怎么样?它与 contour-5 处于同一级别。所以,它的下一个 contour 是 contour-5,因此,Next = 5。

*“Previous 表示处于相同层次结构级别的上一个 contour”。*

与上面相同。contour-1 的上一个 contour 是处于同一级别的 contour-0。类似地,对于 contour-2,是 contour-1。对于 contour-0,没有上一个,因此,将其设为 -1。

*“First_Child 表示其第一个孩子 contour”。*

无需解释。对于 contour-2,孩子是 contour-2a。因此,它获取 contour-2a 的相应索引值。Contour-3a 怎么样?它有两个孩子。但是,我们只取第一个孩子。它是 contour-4。因此,对于 contour-3a,First_Child = 4。

*“Parent 表示其父级 contour 的索引”。*

它与 First_Child 相反。对于 contour-4 和 contour-5,父 contour 都是 contour-3a。对于 contour-3a,是 contour-3,以此类推。

注意
如果没有孩子或父级,则该字段使用 -1

因此,现在我们知道了 OpenCV 中使用的层次结构样式,借助上面给出的同一图片,我们可以查看 OpenCV 中的 Contour 检索模式。例如,cv.RETR_LISTcv.RETR_TREEcv.RETR_CCOMPcv.RETR_EXTERNAL 等标志是什么意思?

Contour 检索模式

1. RETR_LIST

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

所以,此处层次结构数组中的第 3 项和第 4 项始终为-1。但显然,下一个和上一个项将有其相应的值。只需自行检查即可验证。

下述是我获得的结果,每行是相应轮廓的层次结构详细信息。例如,第一行对应轮廓 0。下一个轮廓是轮廓 1。所以,下一个 = 1。没有上一个轮廓,所以上一个 = -1。而剩下的两个,如前所述,为 -1。

>>> 层次结构
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. RETR_EXTERNAL

如果你使用此标记,它只返回最外部的标志。所有子轮廓都将被抛在后面。我们可以说,根据此规则,只关注每个家庭中的最年长者。它不在乎家庭的其他成员 :)

所以,在我们的图像中,有多少个最外部轮廓?即在层次-0 级别?只有 3 个,即轮廓 0、1、2,对吗?现在尝试使用此标志查找轮廓。在此处,给予每个元素的值也与上文所述相同。将它与上述结果进行比较。以下是我的获得内容

>>> 层次结构
array([[[ 1, -1, -1, -1],
[ 2, 0, -1, -1],
[-1, 1, -1, -1]]])

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

3. RETR_CCOMP

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

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

我们可以使用一个简单的图像对此进行解释。在此处,我已用红色标记轮廓的顺序,并用绿色标记它们所属的层次(1 或 2)。顺序与 OpenCV 检测轮廓的顺序相同。

图像

所以,考虑第一个轮廓,即轮廓-0。它属于层次-1。它有两个孔,轮廓 1 和 2,它们属于层次-2。所以对于轮廓-0,同一层次级别中的下一个轮廓是轮廓-3。没有上一个。而 its first is child is contour-1 in hierarchy-2. It has no parent, because it is in hierarchy-1. So its hierarchy array is [3,-1,1,-1]

现在采用 contour-1。它位于层级-2 中。它在相同层级中的下一个(在 contour-1 的父代下)是 contour-2。没有上一个。没有子代,但父代是 contour-0。因此数组是 [2,-1,-1,0]。

类似 contour-2:它位于层级-2 中。在 contour-0 下的相同层级中没有下一个轮廓。所以没有 Next。上一个是 contour-1。没有子代,父代是 contour-0。所以数组是 [-1,1,-1,0]。

轮廓 - 3:层级 1 中的下一个是轮廓-5。上一个是轮廓-0。子代是轮廓-4 且没有父代。所以数组是 [5,0,4,-1]。

轮廓 - 4:它位于轮廓-3 下的层级 2 中,且没有同胞。所以没有下一个,没有上一个,没有子代,父代是轮廓-3。所以数组是 [-1,-1,-1,3]。

剩余部分你可以自己填写。这是我得到的最终答案

>>> 层次结构
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

而且它是最终环节,Mr.Perfect。它检索所有轮廓并创建一个完整的系列层级列表。它甚至会告诉谁是祖父、父亲、儿子、孙子和更远亲属…… :)。

例如,我选取了以上图片,为 cv.RETR_TREE 重写代码,根据 OpenCV 给出的结果重新排序轮廓并对其进行分析。红字再次表示轮廓数量,绿字表示层级顺序。

图像

采用 contour-0:它位于层级-0 中。相同层级中的下一个轮廓是 contour-7。没有上一个轮廓。子代是 contour-1。没有父代。所以数组是 [7,-1,1,-1]。

采用 contour-2:它位于层级-1 中。在相同层级中没有轮廓。没有上一个。子代是 contour-3。父代是 contour-1。所以数组是 [-1,-1,3,1]。

剩余部分,请自己尝试。以下为完整答案

>>> 层次结构
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]]])

其他资源

练习