OpenCV计算机视觉实战(13)——轮廓检测详解
OpenCV计算机视觉实战(13)——轮廓检测详解
- 0. 前言
- 1. 轮廓层级关系解析
- 1.1 轮廓层级
- 1.1 实现过程
- 2. 凸包检测与缺陷分析
- 2.1 应用场景
- 2.2 实现过程
- 3. 轮廓特征计算
- 3.1 应用场景
- 3.2 实现过程
- 小结
- 系列链接
0. 前言
在计算机视觉中,轮廓检测是图像分析的基础操作之一。本节将深入探讨 OpenCV
中的高级轮廓处理技术,包括轮廓层级关系解析、凸包检测与缺陷分析,以及轮廓特征的计算(面积、周长、矩)。
1. 轮廓层级关系解析
在复杂图像中,轮廓可能存在嵌套关系,例如一个轮廓包含另一个轮廓。OpenCV
的 cv2.findContours
函数可以返回这些轮廓的层级结构信息,帮助我们理解轮廓之间的父子关系。
1.1 轮廓层级
在实际项目中,轮廓往往并不是简单的“独立”几何图形。它们可能相互包含、重叠或形成复杂的嵌套结构。比如在文档扫描里,文字区域里又包含孔洞与装饰图案。正确解析轮廓的层级关系,可以帮助我们:
- 区分外轮廓与内孔洞:识别对象边界和内部“空洞”,如识别文字
O
与0
- 实现多级过滤:根据父子关系,只保留特定层级的轮廓,从而去除噪声和无关小轮廓
- 构建分层标注:用于图像分割后分层标注,实现更细粒度的语义分割
1.1 实现过程
- 读取图像并二值化:将输入图转为灰度后阈值化
- 调用
findContours
:使用RETR_TREE
模式获取所有轮廓及其完整层级 - 解析 hierarchy 数组:
hierarchy[i] = [Next, Prev, First_Child, Parent]
- 绘制时根据层级上色:例如父轮廓红色,子轮廓绿色,孙轮廓蓝色。
import cv2
import numpy as np# 1. 读取并预处理
image = cv2.imread('1.jpeg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV)# 2. 查找轮廓与层级
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE
)# 3. 根据层级绘制不同颜色
output = image.copy()
for idx, cnt in enumerate(contours):depth = 0# 计算该轮廓的层级深度parent = hierarchy[0][idx][3]while parent != -1:depth += 1parent = hierarchy[0][parent][3]color = tuple(int(c) for c in np.random.randint(0,255,3))cv2.drawContours(output, [cnt], -1, color, 2)cv2.putText(output, str(depth), tuple(cnt[0][0]),cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)# 4. 显示
cv2.imshow('Hierarchy Visualization', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键函数解析:
cv2.findContours(image, mode, method)
mode=cv2.RETR_TREE
:检索所有轮廓并重构完整层级树- 返回值
hierarchy
是形如(N, 4)
的数组,元素含义[Next, Prev, First_Child, Parent]
cv2.drawContours(img, contours, idx, color, thickness)
:根据索引绘制特定轮廓,结合层级信息可对不同深度轮廓着色
2. 凸包检测与缺陷分析
凸包 (Convex Hull
) 可将目标“包裹”成凸多边形,而凸缺陷 (Convexity Defects
) 则指出轮廓相对于其凸包的凹陷区域,可用于手势识别(检测手指缝隙)、缺陷检测等场景。
2.1 应用场景
凸包与凸缺陷分析,在复杂形状识别、品质检测、手势交互等场景有着广泛用途。例如:
- 手势识别:计算手掌轮廓的凸缺陷数目,可直接对应手指数量,从而判断张开几个手指
- 质量检测:在工业零件检测中,通过凸缺陷分析检测物件边缘的破损、打磨不良或凹陷
- 形状简化:凸包可以作为形状外廓的简化表示,用于快速近似、碰撞检测等
2.2 实现过程
- 选用合适的轮廓
- 对多目标图像,可按面积、外接矩形长宽比先筛选,再做凸包分析,减少误操作
- 计算凸包
- 使用
cv2.convexHull
,获取包络点和索引
- 使用
- 计算凸缺陷
cv2.convexityDefects
输入轮廓与索引,返回凹陷起点、终点、最远点及深度
- 可视化技巧
- 在最远点处显示
depth/256
数值,直观了解凹陷程度 - 用不同颜色区分浅凹陷(黄色)、深凹陷(红色),辅助质量分级
- 在最远点处显示
import cv2
import numpy as np# 1. 读取并二值化
img = cv2.imread('10.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)# 2. 找到轮廓并选最大轮廓
cnts, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = max(cnts, key=cv2.contourArea)# 3. 计算凸包(返回点索引)
hull_idx = cv2.convexHull(cnt, returnPoints=False)
# 4. 计算凸缺陷
defects = cv2.convexityDefects(cnt, hull_idx)# 5. 可视化
output = img.copy()
# 绘制凸包
hull_pts = cv2.convexHull(cnt)
cv2.drawContours(output, [hull_pts], -1, (0,255,0), 2)
# 标记缺陷
if defects is not None:for i in range(defects.shape[0]):s,e,f,d = defects[i,0]start = tuple(cnt[s][0])end = tuple(cnt[e][0])far = tuple(cnt[f][0])# 画出缺陷深度线cv2.line(output, start, far, (0,0,255), 2)cv2.line(output, far, end, (0,0,255), 2)# 标记最远点cv2.circle(output, far, 5, (255,0,0), -1)cv2.imshow('Convex Hull & Defects', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键函数解析:
cv2.convexHull(points, returnPoints=False)
returnPoints=False
时返回 索引数组,便于后续convexityDefects
计算- 否则返回凸包顶点坐标
cv2.convexityDefects(contour, hull)
- 输入轮廓与凸包索引,输出形如
(M,1,4)
数组,每项[startIdx, endIdx, farthestPtIdx, fixptDepth]
fixptDepth
表示凹陷深度,单位是像素 × 256
- 输入轮廓与凸包索引,输出形如
3. 轮廓特征计算
轮廓的面积、周长、几何矩 (Moments
) 是最基础且常用的特征,能辅助进行形状筛选、中心定位、长宽比和主方向计算等多种后续操作。
3.1 应用场景
- 形状筛选:通过面积与周长比(圆度指标)过滤非目标形状
- 定位与对齐:用几何矩计算质心,指导机器人手臂抓取或裁剪中心对齐
- 姿态估计:拟合最小外接矩形或椭圆,获取主方向角度,用于旋转校正
3.2 实现过程
- 读取并提取轮廓
- 计算面积
cv2.contourArea(contour)
- 计算周长
cv2.arcLength(contour, True)
- 计算矩与几何中心
cv2.moments(contour)
,并用cx = M10/M00
、cy = M01/M00
得到质心
- 拟合椭圆 / 矩形
- 可选
cv2.fitEllipse
、cv2.minAreaRect
进一步获取长宽比与方向
- 可选
import cv2
import numpy as np# 1. 读取与轮廓提取
img = cv2.imread('10.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
cnts, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)output = img.copy()
for cnt in cnts:# 2. 面积与周长area = cv2.contourArea(cnt)peri = cv2.arcLength(cnt, True)# 3. 计算几何矩与质心M = cv2.moments(cnt)if M['m00'] != 0:cx = int(M['m10']/M['m00'])cy = int(M['m01']/M['m00'])else:cx, cy = 0, 0# 4. 拟合最小外接矩形rect = cv2.minAreaRect(cnt) # (center (x,y), (w,h), angle)box = cv2.boxPoints(rect).astype(int) # 转为四个顶点# 5. 绘制与标注cv2.drawContours(output, [cnt], -1, (0,255,0), 2)cv2.drawContours(output, [box], -1, (255,0,0), 2)cv2.circle(output, (cx, cy), 4, (0,0,255), -1)cv2.putText(output, f"A={int(area)} L={int(peri)}", (cx-50, cy-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 1)cv2.imshow('Contour Features', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键函数解析:
cv2.contourArea(contour)
:计算轮廓包围区域的像素面积cv2.arcLength(contour, closed)
:计算轮廓周长,closed=True
表示首尾相连cv2.moments(contour)
:返回字典{'m00', 'm10', 'm01', ...}
,m00
即面积,m10/m00
,m01/m00
为质心坐标cv2.minAreaRect(contour)
:拟合旋转矩形,返回中心、尺寸、旋转角度cv2.boxPoints(rect)
:将minAreaRect
输出转换为四个顶点坐标,便于绘制
小结
轮廓不仅是图像边缘的集合,更是图像理解的“骨架”。在本节中,我们深入探讨了轮廓层级的结构关系、凸包与缺陷的形状分析技巧,以及面积、周长、几何矩等轮廓特征的提取与应用。这些技术为图像分析提供了更丰富、更高维的语义信息。
系列链接
OpenCV计算机视觉实战(1)——计算机视觉简介
OpenCV计算机视觉实战(2)——环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)——计算机图像处理基础
OpenCV计算机视觉实战(4)——计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)——图像基础操作全解析
OpenCV计算机视觉实战(6)——经典计算机视觉算法
OpenCV计算机视觉实战(7)——色彩空间详解
OpenCV计算机视觉实战(8)——图像滤波详解
OpenCV计算机视觉实战(9)——阈值化技术详解
OpenCV计算机视觉实战(10)——形态学操作详解
OpenCV计算机视觉实战(11)——边缘检测详解
OpenCV计算机视觉实战(12)——图像金字塔与特征缩放