一、轮廓检测基础
1.1 什么是轮廓?
轮廓可以简单理解为连续的点组成的曲线,这些点具有相同的颜色或灰度值。在计算机视觉中,轮廓是形状分析和物体检测与识别的关键基础。通过轮廓分析,我们可以识别物体的形状、计算其面积和周长,并进行进一步的图像处理。
1.2 核心API:cv2.findContours()
image, contours, hierarchy = cv2.findContours(img, mode, method)
参数详解:
| img | 需要检测的原图(必须是二值图像) |
| mode | 轮廓检索模式 |
| method | 轮廓近似方法 |
mode(检索模式)对比:
| cv2.RETR_EXTERNAL | 只检测外轮廓,忽略所有子轮廓 |
| cv2.RETR_LIST | 不建立等级关系,所有轮廓同属一级 |
| cv2.RETR_CCOMP | 建立两级组织结构(外轮廓为1级,内孔为2级) |
| cv2.RETR_TREE | 建立完整的树形组织结构(最常用) |
method(近似方法)对比:
| cv2.CHAIN_APPROX_NONE | 存储所有轮廓点 |
| cv2.CHAIN_APPROX_SIMPLE | 压缩存储,只保留关键点(如矩形只存4个点) |
返回值说明:
- image:处理后的原图
- contours:所有轮廓的列表,每个轮廓以numpy数组存储边界点坐标
- hierarchy:轮廓层次结构 [Next, Previous, First Child, Parent]
1.3 完整示例:轮廓检测与绘制
在绘制轮廓之前,我们先了解 cv2.drawContours() 函数的详细参数:
cv2.drawContours(image, contours, contourIdx, color, thickness=None,
lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数详解:
| image | 要在其上绘制轮廓的输入图像 |
| contours | 轮廓列表,通常由 cv2.findContours() 函数返回 |
| contourIdx | 要绘制的轮廓的索引。如果为负数(如 -1),则绘制所有轮廓 |
| color | 轮廓的颜色,以 BGR 格式表示。例如,(0, 255, 0) 表示绿色 |
| thickness | 轮廓线的粗细。默认值为 1 |
| lineType | 轮廓线的类型。默认值为 cv2.LINE_8 |
| hierarchy | 轮廓层次结构。通常由 cv2.findContours() 函数返回 |
| maxLevel | 绘制的最大轮廓层级。默认值为 None,表示绘制所有层级 |
| offset | 轮廓点的偏移量。默认值为 None |
完整示例代码:
import cv2
# 1. 读取图片并转为灰度图
phone = cv2.imread('phone.png')
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
# 2. 二值化处理(关键步骤!)
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 3. 检测轮廓
contours = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[–2]
print(f"检测到 {len(contours)} 个轮廓")
# 4. 绘制轮廓
image_copy = phone.copy()
cv2.drawContours(
image=image_copy,
contours=contours,
contourIdx=–1, # -1 表示绘制所有轮廓
color=(0, 255, 0), # BGR格式,绿色
thickness=2
)
cv2.imshow('Contours', image_copy)
cv2.waitKey(0)
二、轮廓特征分析
2.1 轮廓面积与周长
# 计算面积
area = cv2.contourArea(contours[0])
print(f"面积: {area}")
# 计算周长(closed=True 表示闭合)
perimeter = cv2.arcLength(contours[0], closed=True)
print(f"周长: {perimeter}")
2.2 根据面积筛选轮廓
# 筛选面积大于10000的轮廓
large_contours = []
for c in contours:
if cv2.contourArea(c) > 10000:
large_contours.append(c)
# 按面积排序,取最大的
sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)
largest_contour = sorted_contours[0]
2.3 外接矩形与最小外接圆
cnt = contours[6] # 取第7个轮廓
# 外接矩形
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(phone, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 最小外接圆
(x, y), radius = cv2.minEnclosingCircle(cnt)
cv2.circle(phone, (int(x), int(y)), int(radius), (0, 255, 0), 2)
三、轮廓近似
轮廓近似是多边形拟合的过程,用更少的点来表示轮廓,对于形状识别和压缩非常有用。
3.1 核心API:cv2.approxPolyDP()
approx = cv2.approxPolyDP(curve, epsilon, closed)
参数说明:
- curve:输入轮廓
- epsilon:近似精度(越小越接近原轮廓,越大越粗糙)
- closed:是否闭合
3.2 完整示例
import cv2
phone = cv2.imread('phone.png')
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
ret, phone_thresh = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
contours = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[–2]
# epsilon = 0.0001 * 轮廓周长(精度非常高)
epsilon = 0.0001 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
print(f"原始轮廓点数: {contours[0].shape}")
print(f"近似后轮廓点数: {approx.shape}")
# 绘制近似轮廓(绿色)
phone_new = phone.copy()
cv2.drawContours(phone_new, [approx], –1, (0, 255, 0), 3)
cv2.imshow('Approx', phone_new)
cv2.waitKey(0)
四、实战案例:花朵轮廓检测与近似
4.1 作业要求
给定图片 hua.png,在同一窗口内完成:
- 用红色画出花的外部轮廓
- 用绿色画出其近似轮廓(ε = 0.005 × 周长)
4.2 代码实现
import cv2
# 读取并转为灰度图
hua = cv2.imread('hua.png')
hua_gray = cv2.cvtColor(hua, cv2.COLOR_BGR2GRAY)
# 二值化处理(注意使用 THRESH_TOZERO_INV)
ret, hua_binary = cv2.threshold(hua_gray, 240, 255, cv2.THRESH_TOZERO_INV)
cv2.imshow('Binary', hua_binary)
cv2.waitKey(0)
# 轮廓检测(只检测外轮廓)
contours = cv2.findContours(hua_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[–2]
print(f"检测到 {len(contours)} 个轮廓")
# 近似处理
epsilon = 0.005 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
# 绘制结果
image_copy = hua.copy()
cv2.drawContours(image_copy, contours, –1, (0, 0, 255), 2) # 红色 – 原始轮廓
cv2.drawContours(image_copy, [approx], –1, (0, 255, 0), 3) # 绿色 – 近似轮廓
cv2.imshow('Final Result', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
五、模板匹配
模板匹配是一种在图像中寻找与模板最相似区域的技术。
5.1 核心API:cv2.matchTemplate()
result = cv2.matchTemplate(image, templ, method)
匹配方法对比:
TM_SQDIFF_NORMED:归一化平方差匹配法,匹配越好,值越小;匹配越差,值越大。
TM_CCORR_NORMED :归一化相关匹配法,数值越大表明匹配程度越好。
TM_CCOEFF_NORMED :归一化相关系数匹配法,数值越大表明匹配程度越好。
| TM_SQDIFF / TM_SQDIFF_NORMED | 平方差 | 值最小 |
| TM_CCORR / TM_CCORR_NORMED | 相关性 | 值最大 |
| TM_CCOEFF / TM_CCOEFF_NORMED | 相关系数 | 值最大 |
5.2 完整示例
import cv2
kele = cv2.imread('kele.png')
template = cv2.imread('template.png')
h, w = template.shape[:2]
# 模板匹配(使用归一化相关系数)
res = cv2.matchTemplate(kele, template, cv2.TM_CCOEFF_NORMED)
# 获取最佳匹配位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 绘制矩形框
cv2.rectangle(kele, top_left, bottom_right, (0, 255, 0), 2)
cv2.imshow('Result', kele)
cv2.waitKey(0)
六、物体追踪(实时)
基于CSRT追踪器的实时物体追踪示例:
import cv2
# 创建CSRT追踪器
track = cv2.TrackerCSRT_create()
tracking = False
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
frame = cv2.flip(frame, 1) # 水平翻转,更自然
if not ret:
break
# 按 's' 键选择追踪目标
if cv2.waitKey(1) == ord('s'):
tracking = True
roi = cv2.selectROI('Tracking', frame, showCrosshair=False)
track.init(frame, roi)
if tracking:
success, box = track.update(frame)
if success:
x, y, w, h = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow('Tracking', frame)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
七、总结
本文详细介绍了OpenCV中轮廓检测与模板匹配的核心技术,包括:
这些技术是计算机视觉和图像处理的基础,广泛应用于物体识别、形状分析、目标跟踪等领域。建议读者结合实际项目多加练习,深入理解每个参数的含义和适用场景。
网硕互联帮助中心


评论前必须登录!
注册