• 基于OpenCV的轮廓检测(3)


    1.目标

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

    2.原理

    在上几篇关于轮廓的文章中,我们使用了OpenCV提供的几个与轮廓相关的函数。但当我们使用cv.findContours()函数发现图像的轮廓时,
    我们传递一个参数,轮廓检索模式。我们通常传递cv.RETR_LIST或 cv.RETR_TREE,它工作得很好。但它到底是什么意思呢?
    同样,在输出中,我们获得了三个数组,第一个是图像,第二个是轮廓,还有一个输出,我们将其命名为层次结构。
    但我们从未在任何地方使用过这个层次。那么这个层次是什么呢?

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

    2.1 层次结构

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

    在这幅图片中,有几个形状,我已经从0 - 5编号。2和2a表示最外面边框的外部和内部轮廓。
    这里,轮廓0,1,2是最外的。我们可以说,它们在层次结构0中,或者简单地说,它们在相同的层次结构级别中。
    其次是轮廓2a。它可以被认为是轮廓2的子轮廓(或者,轮廓2是轮廓2a的父轮廓)。让它在等级1中。同样轮廓3是
    轮廓2的子轮廓,下一个层次等级。最后,等轮廓4和5是轮廓3a的子轮廓,它们位于最后的层级中。从我给边框编号的方式来看,
    我可以说轮廓4是轮廓3a的第一个子轮廓(它也可以是轮廓5)。
    请添加图片描述

    2.2 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,child是轮廓2a。所以它得到了轮廓2a对应的索引值。轮廓3a呢?它有两个child。
    但我们只需要第一个child。这是轮廓4。所以轮廓3a First_Child = 4。

    Parent表示其父轮廓线的索引。
    它正好与First_Child相反。对于轮廓4和轮廓5,父轮廓都是轮廓3a。对于轮廓3a,父轮廓是轮廓3,以此类推。

    如果没有子轮廓或父轮廓,该轮廓的子轮廓或父轮廓将被视为-1

    cv.RETR_LIST,cv.RETR_TREE,cv.RETR_CCOMP,cv.RETR_EXTERNAL等等的轮廓检索模式意思是什么?

    3. 轮廓检索模式

    3.1 RETR_LIST

    这是四个标志中最简单的一个(从解释的角度)。它只是检索所有轮廓,但不创建任何父子关系。在这个规则下,父轮廓和子轮廓是平等的,他们只是轮廓。它们都属于同一个等级。
    [Next, Previous, First_Child, Parent]中的第三和第四项总是-1。但显然,Next和Previous将有相应的值。
    下面是我得到的结果,每一行是对应轮廓的层次细节。例如,第一行对应轮廓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]]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果您不使用任何层次结构特性,这是好选择。

    3.2 RETR_EXTERNAL

    只保留最外层的轮廓,任何子轮廓都被抛弃
    在我们的图像中,有多少外轮廓?即在0层次结构的轮廓?只有3,即轮廓0,1,2,对吗?现在试着用这种检索模式找到轮廓。以下是我得到的结果:

    # >>> hierarchy
    # array([[[ 1, -1, -1, -1],
    #         [ 2,  0, -1, -1],
    #         [-1,  1, -1, -1]]])
    
    • 1
    • 2
    • 3
    • 4

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

    3.3 RETR_CCOMP

    这个标志检索所有的轮廓,并将它们排列成一个2级的层次结构。即物体的外部轮廓(即边界)被置于层次1中。而物体内部的孔的轮廓
    (如果有的话)被放置在层次2中。如果有任何物体在它里面,它的轮廓被再次放置在层次1中等等。
    我们可以用一张简单的图片来解释。在这里,我用红色标记了轮廓的顺序和用绿色(1或2)表示它们所属的层次。顺序与OpenCV检测轮廓的顺序相同。
    考虑第一条轮廓,即轮廓0。这是层次1。它有两个洞,轮廓1和2,它们属于等级2。对于轮廓0,相同层次的下一个轮廓是轮廓3。
    而且之前没有。第一个是child,是层级2中的轮廓1。它没有父节点,因为它处于层次1中。它的层次数组是[3,-1,1,-1]
    对于轮廓1。它属于等级2。下一个在相同层次是轮廓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,它没有兄弟。所以下一个=-1,上一个=-1,子轮廓=-1,父轮廓都是轮廓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]]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    请添加图片描述

    3.4 RETR_TREE

    它检索所有轮廓并创建一个完整的家族层次结构列表。它甚至讲述了谁是爷爷,谁是父亲,谁是儿子,谁是孙子,甚至是更远的……😃。
    例如,我用上面的图片,使用cv.RETR_TREE,根据OpenCV给出的结果重新排序轮廓并分析。同样,红色字母表示轮廓数,绿色字母表示等级顺序。
    以轮廓0为例:它位于层次结构0中。下一条轮廓是轮廓7。没有以前的轮廓。子轮廓是轮廓1。和没有父轮廓。数组是[7,-1,1,-1]
    以轮廓2为例:它位于层次1中。没有轮廓在同一层次上。没有前一个。子轮廓是c轮廓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]]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    请添加图片描述

    参考目录

    https://docs.opencv.org/4.x/d9/d8b/tutorial_py_contours_hierarchy.html

  • 相关阅读:
    win11 defender关闭以及恢复
    ZED 2i 双目-IMU标定
    web前端期末大作业:美食文化网页设计与实现——美食餐厅三级(HTML+CSS+JavaScript)
    如何理解ROS的ros::NodeHandle,学习ROS一年后的体会
    Debian下Hadoop集群安装
    web初识
    leetcode每天5题-Day42
    40. 干货系列从零用Rust编写负载均衡及代理,websocket的实现
    QT:绘图
    护眼灯买什么样的好?好用又实惠的护眼台灯推荐
  • 原文地址:https://blog.csdn.net/weixin_43229348/article/details/125980766