3.3 轮廓层级
总目录:1.1 OpenCV介绍与学习路线 | 第三章:边缘、轮廓与几何 | 编号:3.3
← 上一节:3.2 查找与绘制轮廓 | 下一节:3.4 轮廓几何特征 →
3.3 轮廓层级
一张白纸上画一个黑色圆环,圆环外边界是轮廓,里面的孔也是轮廓。谁包着谁,就是轮廓层级要表达的关系。
一、为什么轮廓会有父子关系
外框
└─ 内孔
└─ 孔里的小岛
只找最外面的物体时不需要层级;要区分边框、孔洞和内部图案时,层级非常有用。
二、hierarchy 的四个数字
findContours 返回的层级常见形状:
print(hierarchy.shape) # (1, N, 4)
- 1:外层套了一个多余的大括号(OpenCV 历史遗留)
- N:一共找到 N 个轮廓
- 4:每 1 个轮廓,配套 4 个层级信息:
[Next, Previous, First_Child, Parent]
hierarchy[0]:把最外层那个没用的1维度剥掉,得到形状(N,4),也就是 N 个轮廓,每个轮廓 4 个信息。
第 i 个轮廓:
next_i, prev_i, child_i, parent_i = hierarchy[0, i]
| 位置 | 含义 | -1 表示 |
|---|---|---|
[0] next |
同层的下一个轮廓 | 没有 |
[1] prev |
同层的上一个轮廓 | 没有 |
[2] first_child |
第一个子轮廓 | 没有孩子 |
[3] parent |
父轮廓 | 自己是最外层 |
它不是“深度数组”,而是用索引串起的一棵树。

图里一共7 条轮廓:
- 绿色外框:轮廓 0(顶层)
- 红色大框:轮廓 1(绿色的子)
- 红色中框:轮廓 2(红色大框的子)
- 红色小框:轮廓 3(红色中框的子)
- 蓝色大框:轮廓 4(绿色的子,和红色大框
轮廓1是同一层级) - 蓝色中框:轮廓 5(蓝色大框的子)
- 蓝色小框:轮廓 6(蓝色中框的子)
轮廓0【绿色外框】: [Next=-1, Prev=-1, FirstChild=1, Parent=-1]
轮廓1【红大】: [Next=4, Prev=-1, FirstChild=2, Parent=0]
轮廓2【红中】: [Next=-1, Prev=-1, FirstChild=3, Parent=1]
轮廓3【红小】: [Next=-1, Prev=-1, FirstChild=-1, Parent=2]
轮廓4【蓝大】: [Next=-1, Prev=1, FirstChild=5, Parent=0]
轮廓5【蓝中】: [Next=-1, Prev=-1, FirstChild=6, Parent=4]
轮廓6【蓝小】: [Next=-1, Prev=-1, FirstChild=-1, Parent=5]
关于顺序 用 RETR TREE,会给所有检测到的轮廓分配编号 编号顺序 = findContours 算法沿着图像扫描、第一次碰到轮廓边界的顺序。 这个遍历策略叫深度优先遍历 DFS(RETR_TREE 底层就是 DFS):
碰到一个轮廓 A → 立刻深入 A 里面,把 A 所有子、孙子全部找完,分配完编号; 全部挖干净之后,才回到 A 的父节点,继续找 A 的 Next 兄弟。
三、四种检索模式
RETR=Retrieve检索
RETR_EXTERNAL
只保留最外层轮廓:
contours, hierarchy = cv2.findContours(
binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
目标互不嵌套、只关心外轮廓时最省心。
RETR_LIST
找出全部轮廓,但不建立父子关系。所有轮廓在 hierarchy 里相当于同层。
RETR_CCOMP
组织为两级:外部边界与孔洞。若孔洞里又有物体,该物体会回到顶层,而不是继续形成任意深度的树。
RETR_TREE
保留完整嵌套关系:父、子、孙都能追踪。
只要最外层 → EXTERNAL
全要但不关心关系 → LIST
只需外层/孔洞两级 → CCOMP
要完整包含关系 → TREE
四、怎样筛父轮廓和子轮廓
只处理最外层
for i, contour in enumerate(contours):
parent = hierarchy[0, i, 3]
if parent != -1:
continue
# contour 是最外层轮廓
只处理某个父轮廓的直接孩子
border_index = 0
for i, contour in enumerate(contours):
if hierarchy[0, i, 3] == border_index:
# contour 是 0 号轮廓的直接子轮廓
pass
遍历一条子链
child = hierarchy[0, border_index, 2]
while child != -1:
contour = contours[child]
child = hierarchy[0, child, 0] # 同层下一个
五、不要把“内部”简单等同于“目标”
原笔记里有一种写法:父轮廓是 -1 就当边框跳过,其余全当内部目标。这只在二值图拓扑刚好符合预期时成立。
现实中还会出现:
- 字母
O自己带孔洞; - 反光把一个目标切出子轮廓;
- 前景背景反转后,父子含义跟着翻转;
- 多层嵌套时,孙轮廓不一定是你要的目标。
所以层级只提供“谁包含谁”,最终还要结合:
- 面积范围;
- 位置是否落在边框 ROI 内;
- 颜色或灰度;
- 形状特征;
- 深度是奇数还是偶数。
六、参考
← 上一节:3.2 查找与绘制轮廓 | 下一节:3.4 轮廓几何特征 →
留言区
不收集任何个人信息;昵称只绑定浏览器里的随机 Cookie。