OpenCV计算机视觉实战(13)——轮廓检测详解

发布于:2025-06-27 ⋅ 阅读:(15) ⋅ 点赞:(0)

0. 前言

在计算机视觉中,轮廓检测是图像分析的基础操作之一。本节将深入探讨 OpenCV 中的高级轮廓处理技术,包括轮廓层级关系解析、凸包检测与缺陷分析,以及轮廓特征的计算(面积、周长、矩)。

1. 轮廓层级关系解析

在复杂图像中,轮廓可能存在嵌套关系,例如一个轮廓包含另一个轮廓。OpenCVcv2.findContours 函数可以返回这些轮廓的层级结构信息,帮助我们理解轮廓之间的父子关系。

1.1 轮廓层级

在实际项目中,轮廓往往并不是简单的“独立”几何图形。它们可能相互包含、重叠或形成复杂的嵌套结构。比如在文档扫描里,文字区域里又包含孔洞与装饰图案。正确解析轮廓的层级关系,可以帮助我们:

  • 区分外轮廓与内孔洞:识别对象边界和内部“空洞”,如识别文字 O0
  • 实现多级过滤:根据父子关系,只保留特定层级的轮廓,从而去除噪声和无关小轮廓
  • 构建分层标注:用于图像分割后分层标注,实现更细粒度的语义分割

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 += 1
        parent = 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/M00cy = M01/M00 得到质心
  • 拟合椭圆 / 矩形
    • 可选 cv2.fitEllipsecv2.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)——图像金字塔与特征缩放


网站公告

今日签到

点亮在社区的每一天
去签到