contribute上次修改:2026-09-29

3.2 查找与绘制轮廓

总目录:1.1 OpenCV介绍与学习路线 | 第三章:边缘、轮廓与几何 | 编号:3.2

← 上一节:3.1 Canny边缘检测 | 下一节:3.3 轮廓层级 →


3.2 查找与绘制轮廓

边缘图告诉我们“哪里变化大”,轮廓则把相连的边界点组织成一组一组的曲线。这样我们才能对每个目标分别算面积、周长和中心。

一、边缘与轮廓有什么区别

Canny 输出:一张黑白边缘图
findContours 输出:多个点集组成的 Python 列表

一张边缘图里可能有 20 个闭合形状,findContours 会尽量把它们拆成 20 组点。轮廓是数据结构,不是一张图。

二、findContours:从二值图提取边界点

contours, hierarchy = cv2.findContours(
    binary,
    cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE,
)

输入通常是 8 位单通道二值图。OpenCV 把非零区域视为前景,因此常见情况是“白目标、黑背景”。

if binary.ndim != 2:
    raise ValueError("findContours 常规输入应为单通道二值图")

三、检索模式与近似方法

先记两个最常用组合:

# 只要最外层目标
cv2.RETR_EXTERNAL

# 要保留父子嵌套
cv2.RETR_TREE

完整层级放在 3.3 轮廓层级。

点保存方法:

方法 含义
CHAIN_APPROX_NONE 保存边界上的大量点
CHAIN_APPROX_SIMPLE 压缩直线段,只保留必要端点

矩形的一条直边没必要记几百个像素点,所以日常优先 CHAIN_APPROX_SIMPLE。

四、轮廓列表和点集形状

print(type(contours))       # tuple 或 list 风格的轮廓集合,依绑定版本而定
print(len(contours))
print(contours[0].shape)    # 常见 (N, 1, 2)

每个轮廓是一个 NumPy 点集。(N, 1, 2) 的解释见 四、轮廓点为什么是 (N, 1, 2)。

按面积过滤小噪声:

large_contours = [
    cnt for cnt in contours
    if cv2.contourArea(cnt) >= 500
]

面积阈值依赖分辨率和目标距离,500 只是示例。

五、drawContours:把轮廓画出来

result = image.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
  • -1:画全部轮廓;
  • 0:只画第 0 个轮廓;
  • thickness=-1:填充轮廓内部。

逐个画并标索引:

for index, contour in enumerate(contours):
    color = (0, 255, 0)
    cv2.drawContours(result, contours, index, color, 2)

    x, y, w, h = cv2.boundingRect(contour)
    cv2.putText(result, str(index), (x, max(20, y - 5)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)

这对理解 hierarchy 很有帮助:图上能看到轮廓编号,数组里的父子索引就不再是天书。

最小完整示例

import cv2

image = cv2.imread("shapes.png")
if image is None:
    raise FileNotFoundError("shapes.png")

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)

contours, hierarchy = cv2.findContours(
    binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)

result = image.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)

cv2.imshow("Binary", binary)
cv2.imshow("Contours", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

六、参考


← 上一节:3.1 Canny边缘检测 | 下一节:3.3 轮廓层级 →

留言区

不收集任何个人信息;昵称只绑定浏览器里的随机 Cookie。