什么是轮廓(Contour)
在 OpenCV 中,轮廓是一组连续的点构成的曲线,用于表示图像中某一目标的边界。它基于二值图像搜寻边界,因此轮廓检测通常配合阈值或边缘检测一起使用。
轮廓在图像处理中的典型用途包括:
- 目标检测与定位
- 形状匹配
- 物体面积/周长测量
- 应用如目标计数、分割、识别
OpenCV 将轮廓视为 numpy 数组的集合,每个轮廓是一个坐标点序列。
轮廓提取原理
输入图像必须是二值图或边缘图
cv2.findContours() 依赖 黑白清晰的边界,它通过扫描二值图寻找像素连接,形成线条式轮廓。
常用输入:
- 二值图:
cv2.threshold() - 边缘图:
cv2.Canny()
查找方式(Retrieval Mode)
OpenCV 提供不同的轮廓结构方式:
| 模式 | 含义 |
|---|---|
cv2.RETR_EXTERNAL |
仅提取最外层轮廓 |
cv2.RETR_LIST |
提取所有轮廓,不建立层级关系 |
cv2.RETR_TREE |
提取所有轮廓,并生成完整层级树结构 |
cv2.RETR_CCOMP |
提取所有,将外轮廓与洞作为不同层组织 |
轮廓逼近方式(Approximation Mode)
| 模式 | 含义 |
|---|---|
cv2.CHAIN_APPROX_NONE |
保存所有边界点 |
cv2.CHAIN_APPROX_SIMPLE |
去除冗余点,仅保存直线拐点(常用) |
绘制轮廓(DrawContours )
绘制使用:
python
cv2.drawContours(image, contours, index, color, thickness)
参数说明:
contours:轮廓列表index:选择第几个轮廓;-1表示全部绘制color:绘制颜色thickness:线条粗细,-1表示填充轮廓内区域
OpenCV 绘制轮廓本质上是将点序列连成多段线,从而形成封闭区域。
轮廓常用属性计算
周长(Arc Length)
python
cv2.arcLength(contour, closed=True)
面积(Area)
python
cv2.contourArea(contour)
外接矩形
- 普通外接矩形:
cv2.boundingRect() - 最小外接矩形(任意倾斜):
cv2.minAreaRect()
最小包围圆
python
cv2.minEnclosingCircle()
轮廓逼近(Douglas-Peucker)
python
approx = cv2.approxPolyDP(contour, epsilon, True)
用于判断形状(三角形、矩形等)。
示例
读取 → 灰度化 → 二值化 → 轮廓提取 → 绘制 → 计算面积/周长 → 轮廓逼近
python
import cv2
import numpy as np
# =========================
# 1. 自己画一个图片
# =========================
img = np.zeros((400, 400, 3), dtype=np.uint8) # 黑色背景
# 画一个白色矩形
cv2.rectangle(img, (50, 50), (150, 150), (255, 255, 255), -1)
# 画一个白色圆
cv2.circle(img, (300, 100), 50, (255, 255, 255), -1)
# 画一个白色多边形(三角形)
pts = np.array([[200, 250], [300, 350], [100, 350]], np.int32)
cv2.fillPoly(img, [pts], (255, 255, 255))
# 转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# =========================
# 2. 二值化
# =========================
ret, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
# =========================
# 3. 查找轮廓
# =========================
contours, hierarchy = cv2.findContours(
thresh,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
print("轮廓数量:", len(contours))
# =========================
# 4. 绘制所有轮廓
# =========================
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)
# =========================
# 5. 遍历轮廓并分析
# =========================
for i, cnt in enumerate(contours):
area = cv2.contourArea(cnt)
length = cv2.arcLength(cnt, True)
print(f"轮廓 {i}: 面积={area:.2f}, 周长={length:.2f}")
# 轮廓逼近
epsilon = 0.02 * length
approx = cv2.approxPolyDP(cnt, epsilon, True)
# 绘制逼近轮廓(红色)
cv2.drawContours(img, [approx], -1, (0, 0, 255), 2)
# 绘制外接矩形(蓝色)
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
# =========================
# 6. 显示结果
# =========================
cv2.imshow("Binary", thresh)
cv2.imshow("Contours Result", img)
cvcv2.waitKey(0)
cv2.destroyAllWindows()
执行结果:
