前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >opencv(4.5.3)-python(二十二)--轮廓线层次结构

opencv(4.5.3)-python(二十二)--轮廓线层次结构

作者头像
用户9875047
发布2023-02-26 15:12:34
5970
发布2023-02-26 15:12:34
举报
文章被收录于专栏:机器视觉全栈er

翻译及二次校对:cvtutorials.com

目标

这一次,我们学习了轮廓的层次结构,即轮廓的父子关系。

理论

在过去的几篇关于轮廓线的文章中,我们已经使用了OpenCV提供的几个与轮廓线有关的函数。但是当我们使用cv.findContours()函数在图像中找到轮廓时,我们传递了一个参数,即轮廓检索模式。我们通常传递cv.RETR_LIST或cv.RETR_TREE,而且效果不错。但它实际上是什么意思?

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

这就是我们将在本文中讨论的问题。

什么是层次结构?

通常我们使用cv.findContours()函数来检测图像中的物体,有时物体在不同的位置。但在某些情况下,有些形状是在其他形状里面的。就像嵌套的图形。在这种情况下,我们称外部的为父,内部的为子。这样一来,图像中的轮廓就有了一些相互之间的关系。我们可以指定一个轮廓是如何相互连接的,比如,它是另一个轮廓的孩子,或者它是一个父母等等。这种关系的表现形式被称为层次结构(Hierarchy)。

请看下面的一个例子:

在这张图片中,有几个形状,我把它们编号为0-5。2和2a表示最外层盒子的外部和内部轮廓线。

这里,0,1,2是外部或最外层的轮廓。我们可以说,它们是在层次结构0中,或者简单地说,它们是在同一层次中。

接下来是轮廓2a。它可以被认为是轮廓线2的孩子(或者反过来说,轮廓线2是轮廓线2a的父母)。因此,让它在层次结构1中。同样地,轮廓3是轮廓2的孩子,它属于下一个层次。最后,轮廓线4、5是轮廓线3a的子女,它们位于最后一个层次。从我给盒子编号的方式来看,我认为轮廓线4是轮廓线3a的第一个孩子(也可以是轮廓线5)。

我提到这些东西是为了理解像同一层次结构水平、外部轮廓、子轮廓、父轮廓、第一子轮廓等术语。现在让我们来了解一下OpenCV。

OpenCV中的层次结构表示法

所以每个轮廓都有自己的信息,关于它是什么层次,谁是它的孩子,谁是它的父母等等。OpenCV将其表示为一个由四个值组成的数组。[下一个,上一个,第一个孩子,父母] 。

"下一个表示同一层次的下一个轮廓 "。

例如,以我们图片中的轮廓线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。所以First_Child = 4为轮廓线3a。

"父代表示其父代轮廓的索引 "。

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

注意:如果没有子代或父代,该字段将被视为-1。

现在我们知道了OpenCV中使用的层次结构风格,我们可以在上面给出的相同图片的帮助下检查OpenCV中的轮廓检索模式,即像cv.RETR_LIST, cv.RETR_TREE, cv.RETR_CCOMP, cv.RETR_EXTERNAL等标志是什么意思?

轮廓线检索模式

  1. 1. RETR_LIST

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

所以在这里,层次结构数组中的第三和第四项总是-1。但是很明显,下一个和上一个项会有其相应的值。你可以自己检查并验证一下。

下面是我得到的结果,每一行都是相应轮廓的层次结构细节。例如,第一行对应的是轮廓线0,下一个轮廓线是轮廓线1,所以Next=1。没有上一个轮廓,所以Previous=-1。而剩下的两个,如前所述,是-1。

代码语言:javascript
复制
>>> 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]]])

如果你不使用任何层次结构特征,这是在你的代码中使用的好选择。

  1. 1. RETR_EXTERNAL

如果你使用这个标志,它只返回最外部标志。所有child的轮廓都被留下了。我们可以说,在这个法则下,每个家庭中只有最年长的人得到照顾。它并不关心家庭的其他成员。

那么,在我们的图像中,有多少个最外轮廓?即在层次0的水平?只有3个,即0,1,2号轮廓线,对吗?现在试着用这个标志找到这些轮廓。在这里,给每个元素的值也和上面一样。将其与上述结果进行比较。下面是我得到的结果。

代码语言:javascript
复制
>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [-1,  1, -1, -1]]])

如果你想只提取外轮廓,你可以使用这个标志。这在某些情况下可能是有用的。

  1. 1. RETR_CCOMP

这个标志检索所有的轮廓线,并将它们排列成一个2级的层次结构。物体内部的孔洞轮廓(如果有的话)被放在层次结构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下的同一层次中没有下一个轮廓。所以没有下一个。上一个是轮廓1。没有子代,父代是轮廓0。所以数组是[-1,1,-1,0]。

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

轮廓4 : 它在层次结构2中位于轮廓3之下,没有兄弟姐妹。所以没有下一个,没有上一个,没有子代,父代是轮廓3。所以数组是[-1,-1,-1,3]。

剩下的你可以填满。这就是我得到的最终答案。

代码语言:javascript
复制
>>> 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]]])
  1. 1. RETR_TREE

RETR_TREE检索了所有的轮廓线,并创建了一个完整的家庭层次列表。它甚至可以告诉你,谁是爷爷、父亲、儿子、孙子,甚至更多...:)。

例如,我取了上面的图片,重写了cv.RETR_TREE的代码,按照OpenCV给出的结果重新排列了轮廓线,并进行了分析。同样,红色的字母给出了轮廓线的编号,绿色的字母给出了层次的顺序。

以0号轮廓线为例:它在第0层。同一层次中的下一个轮廓是轮廓7。没有前一个轮廓线。子代是轮廓线1。也没有父代。所以数组是[7,-1,1,-1]。

拿轮廓线2来说:它在层次结构1中。在同一层次中没有轮廓线。没有前一个。子代是轮廓线3。父代是轮廓线1。所以数组是[-1,-1,3,1]。

剩下的,自己试试。下面是完整的答案。

代码语言:javascript
复制
>>> 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]]])
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器视觉全栈er 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档