OpenCV Python 轮廓层次
【目标】
- 学习轮廓的层次关系
在前几个课程里面,学习了 cv2.findContours() 函数, 传递了参数 Contour Retrieval Mode . 通常是 cv.RETR_LIST or cv.RETR_TREE 工作的很好,但是他们是什么意思呢?
hierarchy 到底是什么呢?
在某些情况下,有些形状是在其他形状内部,就像层级一样。我们称上层的为 parent,内部的为 child。表达这种“父-子”关系形状轮廓称之为 Hierarchy 。
一个例子如下:
在这个图像中,有许多形状,我们标号为 0~5
, 2
和 2a
表明外部轮廓和内部轮廓。
0,1,2
都是最外层的轮廓,层级为0
。
轮廓 2
是 2a
的父轮廓,2a
是 2
的子轮廓,定义为层级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
,
如下图所示:
绿色数字表示层级,外层的橙色表示轮廓序号;
轮廓 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
这是最后一个标志,也是最完美的。检索所有轮廓并创建一个家族,告诉谁是儿子,谁是孙子,谁是父亲,谁是祖父等等。
结果如下:
对于轮廓 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]]]
【代码】
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]]]