OpenCV 图像轮廓检测

发布于:2025-09-02 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

一、轮廓检测基础概念

二、核心 API 详解:cv2.findContours ()

参数说明:

返回值说明:

三、轮廓检测实战步骤

1. 图像预处理(灰度化与二值化)

2. 查找轮廓

3. 绘制轮廓

四、轮廓的常用属性与操作

1. 轮廓面积与周长

2. 轮廓筛选与排序

3. 轮廓的外接形状

4. 轮廓近似

五、总结


在计算机视觉领域,轮廓检测是一项基础且重要的技术,它能够帮助我们识别图像中的物体边界,为后续的图像分析、目标识别等任务提供关键信息。本文将基于 OpenCV 库,详细介绍轮廓检测的原理、常用 API 及实战应用。


一、轮廓检测基础概念

轮廓可以理解为图像中具有相同颜色或灰度的连续点组成的曲线,它是物体边界的有效表达方式。在进行轮廓检测前,有一个重要的前提:需要将图片处理为二值图像(像素值只为 0 和 255),这是因为轮廓检测主要基于图像的边缘信息,二值化处理能简化后续的检测过程。


二、核心 API 详解:cv2.findContours ()

OpenCV 提供了 cv2.findContours() 函数用于检测图像轮廓,其语法格式如下:

参数说明:

  1. img:需要进行轮廓检测的输入图像(必须是二值图像)

  2. mode:轮廓的检索模式,常用的有以下四种:

    • cv2.RETR_EXTERNAL:只检测外轮廓,忽略所有子轮廓
    • cv2.RETR_LIST:检测所有轮廓,但不建立等级关系
    • cv2.RETR_CCOMP:返回所有轮廓,仅建立两级层次结构(外轮廓为第 1 级,内部中空轮廓为第 2 级)
    • cv2.RETR_TREE:返回所有轮廓,建立完整的层级组织结构
  3. method:轮廓的近似方法:

    • cv2.CHAIN_APPROX_NONE:存储所有的轮廓点
    • cv2.CHAIN_APPROX_SIMPLE:压缩模式,只保留轮廓方向的终点坐标(如矩形仅保留 4 个顶点)

返回值说明:

  • image:处理后的图像(与输入图像相关)
  • contours:包含所有轮廓的列表,每个轮廓是由边界点坐标 (x,y) 组成的 numpy 数组
  • hierarchy:轮廓的层次结构,每个元素是包含 4 个值的数组 [Next, Previous, First Child, Parent],分别表示:
    • Next:同一层级的下一条轮廓
    • Previous:同一层级的上一条轮廓
    • First Child:当前轮廓的第一条子轮廓
    • Parent:当前轮廓的父轮廓

注意:在新版 OpenCV(如 4.x 版本)中,该函数返回值为 (contours, hierarchy),可使用 contours = cv2.findContours(...)[-2] 兼容不同版本。


三、轮廓检测实战步骤

1. 图像预处理(灰度化与二值化)

轮廓检测前必须进行预处理,将彩色图像转为灰度图,再进一步转为二值图:

import cv2

# 读取原图
phone = cv2.imread('phone.png')
# 转为灰度图
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
# 二值化处理(阈值 120,最大值 255,二值化方式)
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)

# 显示处理结果
cv2.imshow('phone_gray', phone_gray)
cv2.imshow('phone_binary', phone_binary)
cv2.waitKey(0)

2. 查找轮廓

使用 cv2.findContours() 函数检测轮廓:

# 查找轮廓(建立完整层级,压缩轮廓点)
_, contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 打印轮廓信息
print("轮廓层次结构:", hierarchy)
print("轮廓数量:", len(contours))
轮廓层次结构: [[[ 6 -1  1 -1]
  [ 2 -1 -1  0]
  [ 3  1 -1  0]
  [ 4  2 -1  0]
  [ 5  3 -1  0]
  [-1  4 -1  0]
  [ 7  0 -1 -1]
  [ 8  6 -1 -1]
  [-1  7 -1 -1]]]
轮廓数量: 9

3. 绘制轮廓

通过 cv2.drawContours() 函数可以将检测到的轮廓绘制在图像上:

# 函数语法
# cv2.drawContours(image, contours, contourIdx, color, thickness)

# 复制原图避免修改原图
image_copy = phone.copy()
# 绘制所有轮廓(contourIdx=-1),绿色(0,255,0),线宽 2
cv2.drawContours(image_copy, contours, -1, (0,255,0), 2)
cv2.imshow('contours_result', image_copy)
cv2.waitKey(0)


四、轮廓的常用属性与操作

1. 轮廓面积与周长

  • 轮廓面积:使用 cv2.contourArea() 计算
  • 轮廓周长:使用 cv2.arcLength() 计算(参数 closed=True 表示闭合轮廓)
# 计算第一个轮廓的面积
area_0 = cv2.contourArea(contours[0])
print("第一个轮廓的面积:", area_0)

# 计算第一个轮廓的周长
length = cv2.arcLength(contours[0], closed=True)
print("第一个轮廓的周长:", length)
第一个轮廓的面积: 50716.5
第一个轮廓的周长: 952.4377244710922

2. 轮廓筛选与排序

可以根据轮廓面积等属性筛选特定轮廓,或对轮廓进行排序:

# 筛选面积大于 10000 的轮廓
large_contours = []
for cnt in contours:
    if cv2.contourArea(cnt) > 10000:
        large_contours.append(cnt)

# 绘制筛选后的轮廓
image_copy = phone.copy()
cv2.drawContours(image_copy, large_contours, -1, (0,255,0), 3)
cv2.imshow('contours_larger_10000', image_copy)
cv2.waitKey(0)

# 按面积降序排序,取最大面积的轮廓
largest_cnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]
image_copy = phone.copy()
cv2.drawContours(image_copy, [largest_cnt], -1, (0,255,0), 3)
cv2.imshow('largest_contour', image_copy)
cv2.waitKey(0)

3. 轮廓的外接形状

可以计算轮廓的外接圆和最小外接矩形:

# 以外接圆为例(选取第三个轮廓)
cnt = contours[2]
# 计算外接圆
(x, y), r = cv2.minEnclosingCircle(cnt)
# 绘制外接圆
phone_circle = cv2.circle(phone, (int(x), int(y)), int(r), (0,255,0), 2)
cv2.imshow('contour_circle', phone_circle)
cv2.waitKey(0)

# 计算最小外接矩形
x, y, w, h = cv2.boundingRect(cnt)
# 绘制矩形
phone_rect = cv2.rectangle(phone, (x, y), (x+w, y+h), (0,255,0), 2)
cv2.imshow('contour_rectangle', phone_rect)
cv2.waitKey(0)

4. 轮廓近似

使用 cv2.approxPolyDP() 函数可以对轮廓进行近似处理,简化轮廓形状:

# 函数语法
# approx = cv2.approxPolyDP(curve, epsilon, closed)

# 计算近似精度(取轮廓周长的 0.01 倍)
epsilon = 0.01 * cv2.arcLength(contours[0], True)
# 对第一个轮廓进行近似
approx = cv2.approxPolyDP(contours[0], epsilon, True)

# 对比原始轮廓与近似轮廓的点数量
print("原始轮廓点数量:", contours[0].shape[0])
print("近似轮廓点数量:", approx.shape[0])

# 绘制近似轮廓
phone_new = phone.copy()
cv2.drawContours(phone_new, [approx], -1, (0,255,0), 3)
cv2.imshow('contour_approx', phone_new)
cv2.waitKey(0)
原始轮廓点数量: 235
近似轮廓点数量: 8

其中,epsilon 参数决定了近似精度:值越小,近似结果越接近原始轮廓;值越大,轮廓越简化。


五、总结

本文详细介绍了 OpenCV 中轮廓检测的核心技术,包括轮廓检测的预处理步骤、cv2.findContours() 函数的使用、轮廓绘制方法以及轮廓的各种属性计算与操作(面积、周长、筛选、排序、外接形状、轮廓近似等)。

轮廓检测在目标识别、图像分割、物体测量等领域有着广泛的应用,掌握这些基础操作是进行更复杂计算机视觉任务的前提。实际应用中,需要根据具体场景选择合适的轮廓检索模式和近似方法,以达到最佳的检测效果。