OpenCV 4.11.0
开源计算机视觉
|
上一教程: 轮廓:更多函数
在前面几篇关于轮廓的文章中,我们使用了OpenCV提供的几个与轮廓相关的函数。但是当我们使用cv.findContours() 函数在图像中查找轮廓时,我们传递了一个参数,轮廓检索模式。我们通常传递cv.RETR_LIST 或cv.RETR_TREE,效果很好。但这究竟意味着什么?
此外,在输出中,我们得到了三个数组,第一个是图像,第二个是我们的轮廓,还有一个我们命名为hierarchy的输出(请查看前面文章中的代码)。但我们从未在任何地方使用过这个hierarchy。那么这个hierarchy是什么?它有什么用?它与前面提到的函数参数有什么关系?
这就是我们将在本文中讨论的内容。
通常我们使用cv.findContours() 函数来检测图像中的物体,对吧?有时物体在不同的位置。但在某些情况下,一些形状在其他形状内部。就像嵌套图形一样。在这种情况下,我们将外部图形称为父级,内部图形称为子级。这样,图像中的轮廓彼此之间存在某种关系。我们可以指定一个轮廓如何与另一个轮廓连接,例如,它是某个其他轮廓的子级,还是父级等等。这种关系的表示称为层次结构。
考虑下面的示例图像
在这张图像中,有一些形状,我已将其编号为0-5。2 和 2a 表示最外层框的外部和内部轮廓。
这里,轮廓 0、1、2 是外部或最外层的。我们可以说,它们位于层次结构-0中,或者简单地说它们位于相同的层次结构级别。
接下来是轮廓-2a。它可以被认为是轮廓-2 的子级(或者反过来,轮廓-2 是轮廓-2a 的父级)。所以让它在层次结构-1中。同样,轮廓-3 是轮廓-2a 的子级,它位于下一个层次结构中。最后,轮廓 4、5 是轮廓-3a 的子级,它们位于最后一个层次结构级别。根据我给盒子编号的方式,我会说轮廓-4 是轮廓-3a 的第一个子级(也可以是轮廓-5)。
我提到这些是为了理解诸如相同的层次结构级别、外部轮廓、子轮廓、父轮廓、第一个子级等术语。现在让我们进入OpenCV。
因此,每个轮廓都有关于它属于哪个层次结构、谁是它的子级、谁是它的父级等信息。OpenCV将其表示为一个包含四个值的数组:[Next,Previous,First_Child,Parent]
例如,取我们图片中的轮廓-0。在其同一级别上的下一个轮廓是谁?它是轮廓-1。所以简单地将 Next = 1。同样对于轮廓-1,下一个是轮廓-2。所以 Next = 2。
轮廓-2 怎么样?在同一级别上没有下一个轮廓。所以简单地将 Next = -1。轮廓-4 怎么样?它与轮廓-5 在同一级别。所以它的下一个轮廓是轮廓-5,所以 Next = 5。
这与上面相同。轮廓-1 的前一个轮廓是同一级别上的轮廓-0。同样对于轮廓-2,它是轮廓-1。对于轮廓-0,没有前一个,所以将其设置为 -1。
不需要任何解释。对于轮廓-2,子级是轮廓-2a。因此它获得轮廓-2a 的相应索引值。轮廓-3a 怎么样?它有两个子级。但我们只取第一个子级。它是轮廓-4。所以对于轮廓-3a,First_Child = 4。
这恰好与First_Child相反。对于轮廓-4 和轮廓-5,父轮廓都是轮廓-3a。对于轮廓-3a,它是轮廓-3,依此类推。
所以现在我们了解了OpenCV中使用的层次结构样式,我们可以借助上面给出的相同图像来检查OpenCV中的轮廓检索模式。即cv.RETR_LIST、cv.RETR_TREE、cv.RETR_CCOMP、cv.RETR_EXTERNAL 等标志是什么意思?
这是四个标志中最简单的一个(从解释的角度来看)。它只是检索所有轮廓,但不创建任何父子关系。父母和孩子在这个规则下是平等的,它们只是轮廓。即它们都属于相同的层次结构级别。
因此,这里层次结构数组中的第 3 个和第 4 个项始终为 -1。但显然,Next 和 Previous 项将具有它们对应的值。
如果您使用此标志,它只返回极端外部标志。所有子轮廓都被遗留下来。我们可以说,在这个规则下,每个家庭中只有长子得到照顾。它不关心家庭的其他成员)。
此标志检索所有轮廓并将它们排列到一个两级层次结构中。即对象的外部轮廓(即其边界)位于层次结构-1 中。对象内部的孔的轮廓(如果有)位于层次结构-2 中。如果其中有任何对象,则其轮廓仅再次放置在层次结构-1 中。它的孔在层次结构-2 中,依此类推。
只考虑一下黑色背景上“大的白色零”的图像。零的外圆属于第一层次结构,零的内圆属于第二层次结构。
我们可以用一个简单的图像来解释它。在这里,我已经用红色标记了轮廓的顺序,用绿色标记了它们所属的层次结构(1 或 2)。顺序与OpenCV检测轮廓的顺序相同。
所以考虑第一个轮廓,即轮廓-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 下同一层次结构中没有下一个轮廓。所以没有 Next。前一个是轮廓-1。没有子级,父级是轮廓-0。所以数组是 [-1,1,-1,0]。
轮廓 - 3:层次结构-1 中的下一个是轮廓-5。前一个是轮廓-0。子级是轮廓-4,没有父级。所以数组是 [5,0,4,-1]。
轮廓 - 4:它位于轮廓-3下的层次结构2,并且没有兄弟节点。因此没有下一个,没有上一个,没有子节点,父节点是轮廓-3。所以数组是[-1,-1,-1,3]。
这是最终的结果,完美先生。它检索所有轮廓并创建一个完整的家族层次结构列表。它甚至会告诉你谁是爷爷、父亲、儿子、孙子,甚至更远…… :)。
例如,我使用了上面的图像,为cv.RETR_TREE重写了代码,根据OpenCV给出的结果重新排序轮廓,并对其进行分析。再次说明,红色字母表示轮廓编号,绿色字母表示层次结构顺序。
以轮廓-0为例:它位于层次结构-0。相同层次结构中的下一个轮廓是轮廓-7。没有之前的轮廓。子节点是轮廓-1。并且没有父节点。所以数组是[7,-1,1,-1]。
以轮廓-2为例:它位于层次结构-1。同一级别没有轮廓。没有上一个。子节点是轮廓-2。父节点是轮廓-0。所以数组是[-1,-1,2,0]。