4.5. 等高线层次¶
4.5.1. 目标¶
这一次,我们学习了轮廓的层次结构,即轮廓中的父子关系。
4.5.2. 理论¶
在最后几篇关于等高线的文章中,我们使用了OpenCV提供的几个与等高线相关的函数。但是当我们用 cv2.查找目录() 函数,我们传递了一个参数, 轮廓提取模式 . 我们通常通过 cv2.RETR_LIST 或 cv2.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]]])