zoukankan      html  css  js  c++  java
  • opencv cv.findContours 函数详解 图像轮廓层级 图像轮廓检索方式详解

    函数 cv.findContours

    contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )

    1. 参数1:源图像
    2. 参数2:轮廓的检索方式,这篇文章主要讲解这个参数
    3. 参数3:一般用 cv.CHAIN_APPROX_SIMPLE,就表示用尽可能少的像素点表示轮廓
    4. contours:图像轮廓坐标,是一个链表
    5. hierarchy:[Next, Previous, First Child, Parent],文中有详细解释

    我们使用cv.findContours()寻找轮廓时,参数2表示轮廓的检索方式(RetrievalModes),当我们传入的是cv.RETR_TREE,它表示什么意思呢?另外,函数返回值hierarchy有什么用途呢?下面我们就来研究下这两个问题。

    理解轮廓层级

    轮廓层级展示
    图中总共有8条轮廓,2和2a分别表示外层和里层的轮廓,3和3a也是一样。从图中看得出来:

    1. 轮廓0/1/2是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
    2. 轮廓2a是轮廓2的子轮廓,反过来说2是2a的父轮廓。轮廓2a算一个等级:1级
    3. 同样3是2a的子轮廓,轮廓3处于一个等级:2级
    4. 类似的,3a是3的子轮廓,等等…………
      这里面OpenCV关注的就是两个概念:同一轮廓等级和轮廓间的子属关系。

    如果我们打印出cv.findContours()函数的返回值hierarchy,会发现它是一个包含4个值的数组:[Next, Previous, First Child, Parent]
    Next:与当前轮廓处于同一层级的下一条轮廓
    举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next=1;同理,对轮廓1来说,Next=2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next=-1。
    Previous:与当前轮廓处于同一层级的上一条轮廓
    跟前面一样,对于轮廓1来说,Previous=0;对于轮廓2,Previous=1;对于轮廓2a,没有上一条轮廓了,所以Previous=-1。
    First Child:当前轮廓的第一条子轮廓
    比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child=2a;对轮廓3,First Child=3a。
    Parent:当前轮廓的父轮廓
    比如2a的父轮廓是2,Parent=2;轮廓2没有父轮廓,所以Parent=-1。

    OpenCV中找到的轮廓序号跟前面讲的不同
    opencv中的轮廓序号
    现在既然我们了解了轮廓层级的概念,那么类似cv.RETR_TREE的轮廓寻找方式又是啥意思呢?

    轮廓寻找方式

    OpenCV中有四种轮廓寻找方式RetrievalModes,下面分别来看下:

    1. RETR_LIST

    这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级。这样,hierarchy中的后两个值[First Child, Parent]都为-1。比如同样的图,我们使用cv.RETR_LIST来寻找轮廓:

    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 2)
    print(hierarchy)
    # 结果如下
    [[[ 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]]]
    

    因为没有从属关系,所以轮廓0的下一条是1,1的下一条是2……

    如果你不需要轮廓层级信息的话,cv.RETR_LIST更推荐使用,因为性能更好

    2. RETR_TREE

    cv.RETR_TREE就是之前我们一直在使用的方式,它会完整建立轮廓的层级从属关系,前面已经详细说明过了。

    3. RETR_EXTERNAL

    这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的3条0级轮廓:

    实验讲解 RETR_EXTERNAL

    contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, 2)
    print(len(contours), hierarchy, sep='
    ')
    # 结果如下
    3
    [[[ 1 -1 -1 -1]
      [ 2  0 -1 -1]
      [-1  1 -1 -1]]]
    

    实验结果

    最外层轮廓展示

    4. RETR_CCOMP

    相比之下cv.RETR_CCOMP比较难理解,它把所有的轮廓只分为2个层级,不是外层的就是里层的。结合代码和图片,我们来理解下:

    实验讲解 RETR_CCOMP

    contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, 2)
    print(hierarchy)
    # 结果如下
    [[[ 1 -1 -1 -1]
      [ 2  0 -1 -1]
      [ 4  1  3 -1]
      [-1 -1 -1  2]
      [ 6  2  5 -1]
      [-1 -1 -1  4]
      [ 7  4 -1 -1]
      [-1  6 -1 -1]]]
    

    实验结果

    2个层级轮廓图解
    使用这个参数找到的轮廓序号与之前不同。
    图中括号里面1代表外层轮廓,2代表里层轮廓。比如说对于轮廓2,Next就是4,Previous是1,它有里层的轮廓3,所以First Child=3,但因为只有两个层级,它本身就是外层轮廓,所以Parent=-1。大家可以针对其他的轮廓自己验证一下。

    实验:绘制图像轮廓

    import cv2 as cv
    import numpy
    
    # 1.读入图片
    img = cv.imread('test_contours.jpg')
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    contours, thresh = cv.threshold(img_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
    
    # 2.寻找轮廓
    contours, hierarchy = cv.findContours(thresh, cv.RETR_LIST, 2)
    
    print(len(contours),hierarchy)
    
    # 3.绘制轮廓
    cv.drawContours(img, contours, -1, (0, 0, 255), 2)
    
    cv.imshow('result',img)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    实验结果

    给图像轮廓加红色边框

  • 相关阅读:
    20201029-1 每周例行报告
    20201022-1 每周例行报告
    20201015-3 每周例行报告
    作业要求 20201008-1 每周例行报告
    《机器学习》第二次作业——第四章学习记录和心得
    实验 7:OpenDaylight 实验——Python 中的 REST API 调用
    实验 6:OpenDaylight 实验——OpenDaylight 及 Postman 实现流表下发
    实验 5:OpenFlow 协议分析和 OpenDaylight 安装
    实验 4:Open vSwitch 实验——Mininet 中使用 OVS 命令
    实验 3:Mininet 实验——测量路径的损耗率
  • 原文地址:https://www.cnblogs.com/wojianxin/p/12602490.html
Copyright © 2011-2022 走看看