【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级

OpenCV Python 轮廓层次

【目标】

  • 学习轮廓的层次关系

在前几个课程里面,学习了 cv2.findContours() 函数, 传递了参数 Contour Retrieval Mode . 通常是 cv.RETR_LIST or cv.RETR_TREE 工作的很好,但是他们是什么意思呢?
hierarchy 到底是什么呢?
在某些情况下,有些形状是在其他形状内部,就像层级一样。我们称上层的为 parent,内部的为 child。表达这种“父-子”关系形状轮廓称之为 Hierarchy

一个例子如下:

【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第1张图片

在这个图像中,有许多形状,我们标号为 0~522a 表明外部轮廓和内部轮廓。

0,1,2 都是最外层的轮廓,层级为0

轮廓 22a 的父轮廓,2a2 的子轮廓,定义为层级1;轮廓 3 是轮廓 2a 的子轮廓。

轮廓 4 是 轮廓 3a 的第一个子轮廓。

OpenCV 用 [Next, Previous, First_Child, Parent] 表示轮廓层级关系信息。

Next: 下一个的意思就是同一层级里下一个轮廓。如果第 0 个轮廓的下一个轮廓是 1 ,那么 Next = 1; 如果同一层级没有下一个轮廓,简单的就设置 Next = -1

Previous: 同一层级里前一个轮廓,与 Next 相反;同样,没有前一个轮廓,也设置为 -1

First_Child: 第一个子轮廓,不需要过多解释,对于轮廓 2 来说, 2a 是其子轮廓;对于轮廓 3a 来说,有两个子轮廓,轮廓 4 是第一个子轮廓。

Parent: 父轮廓的索引,与 First_Child 相反。对于轮廓 4 和轮廓 5 来说,他们的父轮廓都是 3a;

如果没有父轮廓或子轮廓,都设置为 -1

所以,我们已经直到了OpenCV里的轮廓层级关系,cv.RETR_LIST, cv.RETR_TREE, cv.RETR_CCOMP, cv.RETR_EXTERNAL 等等是什么意思呢?

1. RETR_LIST

这是4个标志里最简单的,提取所有的轮廓并存储下来,没有父子关系,在这里,他们都是同一个级别。

所以,第 3 个和第 4 个参数始终为 -1;但是很明显,下一个和前一个都是对应的值。例如存储的 层次信息如下:

[[  [ 1, -1, -1, -1],  # contours- 0
    [ 2,  0, -1, -1],  # contours- 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]]] # contours- 7

2. RETR_EXTERNAL

如果使用这个标志,那么只提取轮廓的最外层轮廓,所有子轮廓将不被存储,换句话说,一个轮廓有子轮廓和孙轮廓,一直向上回溯到该父轮廓没有父轮廓,才保留下来(一个轮廓家族留下最老的 )。

上图中,只有 0, 1, 2 才是最外层(最老的,没有父轮廓,层级为0)。

[[  [ 1, -1, -1, -1],   # contours- 0
    [ 2,  0, -1, -1],   # contours- 1
    [-1,  1, -1, -1]]]  # contours- 2

3. RETR_CCOMP

使用该标志时,将轮廓赋值为 2 个级别。最外层的级别为 1, 内部的层级为 2; 如果内部还有其他内部层级,循环使用级别 1 和 2

如下图所示:

【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第2张图片

绿色数字表示层级,外层的橙色表示轮廓序号;

轮廓 0 的层级为 1, 有两个洞(层级都是2),分别是轮廓 1 和轮廓 2,对于轮廓 0 来说,下一个轮廓是轮廓 3, 没有上一个轮廓; [3, -1, 1, -1]

[[  [ 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

这是最后一个标志,也是最完美的。检索所有轮廓并创建一个家族,告诉谁是儿子,谁是孙子,谁是父亲,谁是祖父等等。

结果如下:

【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第3张图片

对于轮廓 0 来说,层级为 0, 下一个同级轮廓为 7, 没有上一个轮廓,子轮廓为 1, 没有父轮廓,所以存储为

[7, -1, 1, -1]

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

【代码】

【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第4张图片
【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第5张图片
【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第6张图片
【OpenCV-Python】教程:3-9 轮廓(5)轮廓层级_第7张图片

import cv2 

img = cv2.imread("contours.png", 0)
imgcolor = cv2.imread("contours.png", 1)
ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# RETR_LIST
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
colorRETR_LIST = imgcolor.copy()

print("\n")
print("RETR_LIST 轮廓的个数:", len(contours))
print("RETR_LIST 层级关系如下: ")
print(hierarchy)

font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(len(contours)):
    # print("contour ", i, " contours pt =  ", contours[i][0, 0])
    # print("contour ", i, " hierarchy = ", hierarchy[0][i])
    textstr = str(i) + '-' + str(hierarchy[0][i])
    showpt = (contours[i][0, 0, 0] - 3, contours[i][0, 0, 1] + 3)
    cv2.putText(colorRETR_LIST, textstr, showpt, font, 0.4,
                (0, 0, 255), 1, cv2.LINE_AA)
cv2.imshow("colorRETR_LIST", colorRETR_LIST)
    
# RETR_EXTERNAL
contours, hierarchy = cv2.findContours(
    thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
colorRETR_EXTERNAL = imgcolor.copy()

print("\n")
print("RETR_EXTERNAL 轮廓的个数:", len(contours))
print("RETR_EXTERNAL 层级关系如下: ")
print(hierarchy)

font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(len(contours)):
    textstr = str(i) + '-' + str(hierarchy[0][i])
    showpt = (contours[i][0, 0, 0] - 3, contours[i][0, 0, 1] + 3)
    cv2.putText(colorRETR_EXTERNAL, textstr, showpt, font, 0.4,
                (0, 0, 255), 1, cv2.LINE_AA)
cv2.imshow("colorRETR_EXTERNAL", colorRETR_EXTERNAL)


# RETR_CCOMP
contours, hierarchy = cv2.findContours(
    thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
colorRETR_CCOMP = imgcolor.copy()

print("\n")
print("RETR_CCOMP 轮廓的个数:", len(contours))
print("RETR_CCOMP 层级关系如下: ")
print(hierarchy)

font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(len(contours)):
    textstr = str(i) + '-' + str(hierarchy[0][i])
    showpt = (contours[i][0, 0, 0] - 3, contours[i][0, 0, 1] + 3)
    cv2.putText(colorRETR_CCOMP, textstr, showpt, font, 0.4,
                (0, 0, 255), 1, cv2.LINE_AA)
cv2.imshow("colorRETR_CCOMP", colorRETR_CCOMP)    
            
            
# RETR_TREE
contours, hierarchy = cv2.findContours(
    thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
colorRETR_TREE = imgcolor.copy()

print("\n")
print("RETR_TREE 轮廓的个数:", len(contours))
print("RETR_TREE 层级关系如下: ")
print(hierarchy)

font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(len(contours)):
    textstr = str(i) + '-' + str(hierarchy[0][i])
    showpt = (contours[i][0, 0, 0] - 3, contours[i][0, 0, 1] + 3)
    cv2.putText(colorRETR_TREE, textstr, showpt, font, 0.4,
                (0, 0, 255), 1, cv2.LINE_AA)
cv2.imshow("colorRETR_TREE", colorRETR_TREE)


# cv2.imshow("img", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出结果如下:

RETR_LIST 轮廓的个数: 10
RETR_LIST 层级关系如下: 
[[[ 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]
  [ 8  6 -1 -1]
  [ 9  7 -1 -1]
  [-1  8 -1 -1]]]


RETR_EXTERNAL 轮廓的个数: 5
RETR_EXTERNAL 层级关系如下: 
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [-1  3 -1 -1]]]


RETR_CCOMP 轮廓的个数: 10
RETR_CCOMP 层级关系如下: 
[[[ 2 -1  1 -1]
  [-1 -1 -1  0]
  [ 3  0 -1 -1]
  [ 5  2  4 -1]
  [-1 -1 -1  3]
  [ 7  3  6 -1]
  [-1 -1 -1  5]
  [ 8  5 -1 -1]
  [ 9  7 -1 -1]
  [-1  8 -1 -1]]]


RETR_TREE 轮廓的个数: 10
RETR_TREE 层级关系如下: 
[[[ 1 -1 -1 -1]
  [ 7  0  2 -1]
  [-1 -1  3  1]
  [ 5 -1  4  2]
  [-1 -1 -1  3]
  [-1  3  6  2]
  [-1 -1 -1  5]
  [ 8  1 -1 -1]
  [ 9  7 -1 -1]
  [-1  8 -1 -1]]]

【参考】

  1. OpenCV官方文档

你可能感兴趣的:(#,OpenCV,opencv,python,计算机视觉)