【OpenCV学习笔记26】- OpenCV中的轮廓 - 轮廓层次结构

本文详细解析OpenCV中的轮廓层次结构,介绍了RETR_LIST、RETR_TREE、RETR_CCOMP和RETR_EXTERNAL四种检索模式,并通过实例展示它们在不同场景的应用。
摘要由CSDN通过智能技术生成

这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子,也会给出自己根据官方的例子完成的更改代码,同样彩蛋的实现也会结合多个知识点一起实现一些小功能,来帮助我们对学会的知识点进行结合应用。
如果有喜欢我笔记的请麻烦帮我关注、点赞、评论。谢谢诸位。

学习笔记:
学习笔记目录里面会收录我关于OpenCV系列学习笔记博文,大家如果有什么不懂的可以通过阅读我的学习笔记进行学习。
【OpenCV学习笔记】- 学习笔记目录

内容

  • 了解轮廓的层次结构,即Contours中的父子关系。

理论

在最近几篇关于轮廓的文章中,我们使用了 OpenCV 提供的几个与轮廓相关的函数。但是当我们使用 cv.findContours() 函数找到图像中的轮廓时,我们传递了一个参数Contour Retrieval Mode。我们通常传递 cv.RETR_LISTcv.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 中的层次结构表示

每个轮廓都有自己的信息,包括它的层次结构、谁是其子级、谁是其父级等。OpenCV 将其表示为包含四个值的数组:[Next、Previous、First_Child、Parent]

  1. Next 表示相同等级的下一个轮廓。例如,在我们的图片中取轮廓 0。谁是同一级别的下一个轮廓?它是轮廓1。因此,只需输入 Next = 1。类似地,对于 轮廓1,下一个是轮廓-2。所以Next = 2。
    轮廓2呢?同一级别中没有下一个轮廓。简单地说,输入 Next = -1。轮廓4呢?与轮廓5处于同一水平面。所以它的下一个轮廓是轮廓5,所以Next = 5。
  2. Previous 表示相同轮廓级别的上一个轮廓。与上面相同。轮廓1 的上一个轮廓是同一级别的轮廓 0。同样,对于轮廓 2,上一个轮廓是轮廓 1。而对于轮廓0,没有前一个,所以把它设为-1。
  3. First_Child 表示其第一个子轮廓。无需解释。对于轮廓2,子级是轮廓2a。这样就得到了轮廓2a对应的索引值。轮廓 3a 怎么样?它有两个孩子。但我们只带第一个孩子。它是轮廓4。因此轮廓 3a 的 First_Child = 4。
  4. Parent 代表示其父代轮廓的索引。它与First_Child正好相反。对于轮廓 4 和轮廓 5,父轮廓都是轮廓 3a。对于轮廓 3a,它是轮廓 3,依此类推。

注意 如果没有子级或父级,则该字段设置为-1。

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

轮廓检索模式

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

如果不使用任何层次结构功能,这是在代码中使用的不错选择。

2.RETR_EXTERNAL

如果使用此标志,则仅返回极端的外部标志。保留所有子轮廓。可以说,根据这条法律,每个家庭中只有最年长的人受到照顾。它不关心家庭的其他成员:)。(我个人理解是只返回所有最外部的轮廓)

那么,在我们的图像中,有多少个极端的外部轮廓?即在等级0级别?只有3个,即轮廓0,1,2,对吗?现在尝试使用该标志查找轮廓。在此,赋予每个元素的值也与上述相同。与上面的结果进行比较。以下是我得到的:

命令行/控制台输出:

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

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

3.RETR_CCOMP

该标志检索所有轮廓并将它们排列为2级层次结构。即,对象的外部轮廓(即其边界)位于层次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下的相同层次结构中没有下一个轮廓。所以没有下一步。上一个是轮廓1。没有子级,父级的轮廓为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

这就是最后一个人,完美先生。它检索所有轮廓并创建完整的家庭层次结构列表。它甚至告诉我们,谁是爷爷、父亲、儿子、孙子,甚至更远……😃。

例如,我拍摄了上面的图像,重写了 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]]])

总结

通过上面的轮廓检索模式说明,我们了解了每个模式的检索结果分布。在不同场景下我们可以采用不同的轮廓检索模式。
例如:

RETR_TREE模式: 我们需要针对每个轮廓进行分析并且需要知道轮廓层级的时候,可以通过这种模式获取每一个轮廓及层级所属;

RETR_LIST模式: 我们需要对每个轮廓进行特征提取,并且不需要知道轮廓层级的时候,可以使用该模式;

RETR_EXTERNAL模式: 我们仅需要检测内容外部轮廓的时候,可以采用该模式检索最外层的轮廓;

RETR_CCOMP模式: 抱歉,我还没有想好使用场景。希望以后可以学习到。

  • 33
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜七天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值