OpenCV-Python系列之轮廓分层

python

本次教程我们仍将讨论OpenCV的轮廓的知识——轮廓的层次结构,也就是轮廓的父子关系。

在之前的教程中,我们已经研究了一些与OpenCV所提供的轮廓有关的函数。但是当我们用cv.findContours()函数找到图像的轮廓时,我们已经传入了一个参数,轮廓检索模式(Contour Retrieval Mod)。我们通常传入cv.RETR_LIST或cv. RETR_TREE,它运行的很好。但它到底是什么意思呢?

同样,在输出中,我们得到了三个数组,首先是图像,其次是我们的轮廓,还有一个输出,我们把它命名为层次结构。但我们从未在任何地方使用这种层次结构。那么这个层次结构是什么,它是用来干什么的呢?它与前面提到的函数参数的关系是什么?

我们在之前使用cv.findContours()函数来检测图像中的对象,对吗?有时物体在不同的位置。但在某些情况下,有些形状是在其他形状中,就像嵌套数据。

在这种情况下,我们将外部称为父类,而内部类称为子类。这样,图像中的轮廓就会有一些相互关系。我们可以指定一个轮廓是如何相互连接的,比如,它是其他轮廓的子结点,或者是父结点等等,这种关系的表示被称为层次结构。

现在我们用一张图片来理解一下:

在这个图像中,有一些形状是我从0-5中编号的。2和2a表示最外层的外部和内部的轮廓。

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

其次是 轮廓-2a。可以把它看作是 轮廓-2 的子类(或者相反的,轮廓-2 是 轮廓-2a 的父类)。所以让它在等级-1中。类似的,轮廓-3 是 轮廓-2 的子,它在下一个层次结构中。最后,轮廓-4、5 是 轮廓-3a 的子类,他们进入了最后的等级等级。从我编号的方式,我想说,轮廓-4 是 轮廓-3a 的第一个子类(它也可以是 轮廓-5)。

OpenCV中的层次结构表示

每个轮廓都有它自己关于它是什么层级,谁是它的子类,谁是它的父类等等的信息。OpenCV将它表示为四个值的数组:[ 下一个(Next),前一个(Previous),第一个子类(First_Child),父类(Parent) ]

Next表示同一个层次的下一个轮廓

例如,在我们的图片中取一个 轮廓-0 。那么谁是下一个等级?是 轮廓-1 ,所以 Next=1 。对于 轮廓-1 ,下一个就是 轮廓-2,所以 Next=2。

轮廓-2 呢?在同一水平上没有下一个轮廓,所以 Next=-1 。轮廓-4呢?和 轮廓-5 在同一个层级,所以下一个轮廓就是 轮廓-5, Next=5。

Previous表示同一个层次的前一个轮廓

和上面一样。轮廓-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中使用相同的图像来检查OpenCV中的轮廓检索模式。

轮廓检索模式

我们在之前遗留的关于轮廓检测函数的内部参数问题,现在将正式解答。

cv2.RETR_LIST

这是四个标志中最简单的一个。它只检索所有的轮廓,但不创建任何父子关系。在这条规则下,父类和子类是平等的,他们只是轮廓。它们都属于同一个层次结构。

这里,层级数组中的第3和第4项总是-1。但很明显,下一项和之前的项会有相应的值。

下面是得到的结果,每一行都是对应轮廓的层次结构细节。例如,第一行对应于轮廓-0。下一个轮廓是轮廓-1。所以Next= 1。没有之前的轮廓,所以Previous=-1。剩下的两个,正如前面说的,它是-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]]])

cv2.RETR_EXTERNAL

如果使用这个标志,它只返回极端的外部标志。所有的子类轮廓都被遗忘了。

那么,在我们的图像中,有多少个极端的外轮廓?在结构层次-0水平?。只有3个,即轮廓,0,1,2,对吧?现在试着用这个标志来找到轮廓,。在这里,每个元素的值与上面的值相同。将其与上面的结果进行比较。下面是我得到的:

>>> hierarchy

array([[[ 1, -1, -1, -1],

[ 2, 0, -1, -1],

[-1, 1, -1, -1]]])

通常如果在我们只想提取外部轮廓的情况下,我们可以使用这个标志。

cv2.RETR_CCOMP

这个标记检索所有的轮廓,并将它们安排到一个2级的层次结构中。物体的外部轮廓(即它的边界)被放置在一个层级-1中。物体内部的孔(如果有的话)被放置在等级2中。如果里面有任何物体,它的轮廓只会被放在一个等级1中。以及它的洞放置在等级2上等等。

只要想想黑色背景下的“大白零”的图像就可以了。0的外圆属于第一个层次,而0的内圆属于第二级。

我们可以用一个简单的图像来解释它。在这里,我用橙黄色色表示了轮廓的顺序和它们所属的层次,用绿色的颜色(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 中。没有下一个轮廓,前一个是 轮廓-1。没有子类,父类是 轮廓-0 。所以数组是 [ -1,1,-1,0 ]。

轮廓-3:在 层次结构-1 中的下一个是 轮廓-5 。前一个是 轮廓-0 。子类是 轮廓-4 并且没有父类。所以数组是[ 5,0,4,-1 ]。

轮廓-4:在 层次结构-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]]])

cv2.RETR_TREE

它检索所有的轮廓并创建一个完整的家族层次结构列表。它甚至告诉我们,谁是爷爷,父亲,儿子,孙子,或者更甚……)。

举例来说,我取了上面的图片,重写了代码,传入cv.RETR_TREE,根据OpenCV给出的结果重新排序并对其进行分析。再一次,红色的字母给出了轮廓和绿色的字母表示等级顺序。

轮廓-0:它在 层次结构-0 中,同一层级的下一个轮廓是 轮廓-7 。没有前一个轮廓,第一个子类是 轮廓-1,没有父类。所以数组是 [ 7,-1,1,-1 ]

轮廓-2:它在 层次结构-1 中。同层级没有其他的轮廓。没有前一个轮廓,子类是 轮廓-3 。父类是 轮廓-1 。所以数组是 [ -1,-1,3,1 ]

>>> 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]]])

现在我们来总结一下:

cv2.findContours() 中Contour Retrieval Mode参数

RETR_LIST 它获取所有轮廓,但是不建立父子关系,他们都是一个层级

RETR_EXTERNAL 它返回最外层的。所有孩子轮廓都不要

RETR_CCOMP 这个模式获取所有轮廓并且把他们组织到一个2层结构里,对象的轮廓外边界在等级1里,轮廓内沿(如果有的话)放在层级2里。如果别的对象在它里面,里面的对象轮廓还是放在层级1里,它的内沿在层级2.

RETR_TREE 它取回所有的轮廓并且创建完整的家族层级列表。

本次教程是OpenCV轮廓系列的最后一次教程,可以说,OpenCV的轮廓是基础部分中极为重要的部分,务必需要掌握。

以上是 OpenCV-Python系列之轮廓分层 的全部内容, 来源链接: utcz.com/z/387991.html

回到顶部