当前位置: 首页 > ai >正文

计算机视觉(九):图像轮廓

在计算机视觉(Computer Vision, CV)中,图像轮廓(Image Contour)是图像中物体边界的重要表现形式。它不仅能描述物体的形状特征,还能为目标识别、目标检测、图像分割、场景理解、三维重建等任务提供重要依据。与像素级的边缘检测相比,轮廓更强调封闭的边界、整体性和几何结构。

基本概念

定义

在图像处理中,轮廓通常指图像中前景目标与背景之间的边界线。它是一个闭合曲线,可以用像素点序列来表示。

  • 对于二值图像,轮廓是前景与背景的交界线。
  • 对于灰度图像,轮廓常通过边缘检测或阈值分割得到。

轮廓与边缘的区别

  • 边缘(Edge):灰度强度发生显著变化的位置,可能是不连续的。
  • 轮廓(Contour):物体的完整边界,更强调闭合性和结构性。

数学表示

若一个二值目标区域记为 R,则其轮廓 C 可表示为:

在这里插入图片描述

即边界点是区域中与背景相邻的像素点集合。

提取方法

轮廓提取的目标是将图像中物体的边界点序列化,形成闭合曲线。

1. 阈值分割 + 轮廓提取

  • 先通过全局阈值(如 Otsu 算法)或自适应阈值将图像转为二值图。
  • 然后使用轮廓追踪算法(如 OpenCV findContours)提取边界。
  • 优点:简单高效,适合对比度清晰的目标。
  • 缺点:对噪声和光照敏感。

2. 边缘检测 + 轮廓连接

  • 常用边缘算子:Sobel、Canny、Laplacian。
  • 检测到边缘后,通过连通域分析、霍夫变换或形态学操作获得闭合轮廓。
  • 适合复杂纹理和低对比度图像。

3. 形态学方法

  • 利用腐蚀(Erode)、膨胀(Dilate)、开运算、闭运算等形态学操作去噪或填洞。
  • 提取骨架(Skeletonization)后再追踪轮廓。
  • 常用于噪声较多的工业检测任务。

4. 主动轮廓模型(Snake)

  • 通过能量函数(包含图像梯度约束和曲线光滑性约束)使轮廓曲线自动收缩到物体边界。
  • 优点:轮廓连续性好,可处理复杂目标。
  • 缺点:依赖初始轮廓位置,计算复杂。

5. 基于深度学习的方法

  • 使用语义分割(如 U-Net、Mask R-CNN)获得物体区域,再提取轮廓。
  • 优点:鲁棒性强,适应复杂场景。
  • 缺点:需要大量训练数据。

轮廓的表示与特征

提取到轮廓后,需要对其进行表示和特征化,便于后续处理。

1. 边界链码(Chain Code)

  • 以起点为基准,用 4 邻域或 8 邻域的方向序列描述轮廓。
  • 优点:存储紧凑。
  • 缺点:不平移不变。

2. 多边形逼近

  • 用折线逼近复杂轮廓,例如 Douglas-Peucker 算法。
  • 常用于简化轮廓,提高计算效率。

3. 矩(Moments)

  • 几何矩:描述面积、质心。
  • Hu 不变矩:在旋转、缩放、平移下保持不变,适合形状识别。

4. 傅里叶描述子

  • 将轮廓点序列看作复数序列,做傅里叶变换。
  • 低频分量表示整体形状,高频分量表示细节。
  • 可用于形状匹配。

5. 曲率与角点

  • 曲率较大的点通常对应物体的特征角点。
  • 在目标识别和姿态估计中很重要。

OpenCV中的轮廓查找

核心函数

cv2.findContours()函数

查找轮廓。

contours, hierarchy = cv2.findContours(image, mode, method)

参数解析:

  • image: 输入的二值图像。注意: 此函数会修改输入图像,通常建议传入一个副本。
  • mode: 轮廓检索模式,决定了如何组织轮廓的层级关系。
    • cv2.RETR_EXTERNAL: 只检索最外层的轮廓。
    • cv2.RETR_LIST: 检索所有轮廓,但不建立任何层级关系。
    • cv2.RETR_CCOMP: 检索所有轮廓,并将它们组织成两级层次结构(外部轮廓和孔洞)。
    • cv2.RETR_TREE: 检索所有轮廓,并建立完整的轮廓层次结构树。
  • method: 轮廓近似方法,决定如何存储轮廓点。
    • cv2.CHAIN_APPROX_NONE: 存储轮廓上所有的点。
    • cv2.CHAIN_APPROX_SIMPLE: 只存储轮廓的端点,例如,一个矩形只存储四个角点。

返回值:

  • contours: 找到的所有轮廓的列表。每个轮廓都是一个 NumPy 数组,包含了该轮廓的所有点。
  • hierarchy: 一个可选的层级信息数组。它告诉我们每个轮廓的父轮廓、子轮廓、上一个轮廓和下一个轮廓。

cv2.drawContours()函数

在图像上绘制找到的轮廓。

cv2.drawContours(image, contours, contourIdx, color, thickness)

参数解析:

  • image: 要在上面绘制轮廓的图像。
  • contours: 轮廓列表,通常是 cv2.findContours() 的返回值。
  • contourIdx: 轮廓的索引,指定绘制哪个轮廓。如果要绘制所有轮廓,传入 -1
  • color: 轮廓的颜色,以 (B, G, R) 元组形式表示。
  • thickness: 轮廓线的粗细。如果传入 -1cv2.FILLED,则轮廓内部会被填充。

示例

查找并绘制图像中的轮廓

import cv2
import numpy as np# 1. 读取图像并转换为灰度图
image = cv2.imread('shape.png') 
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 2. 图像阈值化
# 将图像转换为二值图像,大于127的像素变为255,否则变为0
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 3. 查找轮廓
# 检索所有轮廓,并使用简单的近似方法
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 4. 在原图上绘制轮廓
# 绘制所有轮廓,颜色为绿色,粗细为2
output_image = image.copy()
cv2.drawContours(output_image, contours, -1, (0, 255, 0), 2)# 5. 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Binary Image', thresh)
cv2.imshow('Contours', output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

执行效果:

在这里插入图片描述

形状分析与测量

通过计算轮廓的面积、周长等属性来了解物体的基本几何信息。

import cv2
import numpy as np# 1. 创建一个二值图像
img = np.zeros((300, 300), dtype=np.uint8)
# 画一个圆,填充它
cv2.circle(img, (150, 150), 80, 255, -1)# 2. 查找轮廓
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 获取第一个也是唯一的轮廓
cnt = contours[0]# 3. 计算轮廓属性
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True) # True 表示轮廓是闭合的#print(f"轮廓面积: {area}")
#print(f"轮廓周长: {perimeter}")# 4. 绘制轮廓的边界框和最小外接圆
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (100), 2)(center_x, center_y), radius = cv2.minEnclosingCircle(cnt)
center = (int(center_x), int(center_y))
radius = int(radius)
cv2.circle(img, center, radius, (100), 2)cv2.imshow("Shape Analysis", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

执行效果:

在这里插入图片描述

形状匹配

形状匹配用于比较两个轮廓的相似度。这在物体识别、缺陷检测等任务中非常有用。cv2.matchShapes 函数是实现这一功能的关键。

import cv2
import numpy as np# 1. 定义两个形状(轮廓)
# 第一个形状:矩形
rect = np.zeros((200, 200), dtype=np.uint8)
cv2.rectangle(rect, (50, 50), (150, 150), 255, -1)
contours1, _ = cv2.findContours(rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 第二个形状:旋转的矩形
rotated_rect = np.zeros((200, 200), dtype=np.uint8)
M = cv2.getRotationMatrix2D((100, 100), 45, 1) # 旋转45度
rotated_rect = cv2.warpAffine(rect, M, (200, 200))
contours2, _ = cv2.findContours(rotated_rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 2. 匹配两个形状
# cv2.CONTOURS_MATCH_I1 是一个常用的方法
match_score = cv2.matchShapes(contours1[0], contours2[0], cv2.CONTOURS_MATCH_I1, 0)print(f"result: {match_score}")
# 分数越低,两个形状越相似。理想情况下,完全相同的形状得分为0。
# 即使形状经过旋转、缩放或平移,matchShapes函数也能很好地工作。cv2.imshow("Original", rect)
cv2.imshow("Rotated", rotated_rect)
cv2.waitKey(0)
cv2.destroyAllWindows()

执行结果:

在这里插入图片描述

基于轮廓的图像分割与对象提取

找到轮廓后,可以用它来创建一个掩模(Mask),以精确地从原始图像中提取出物体。

import cv2
import numpy as np# 1. 读取彩色图像
image = cv2.imread('object.jpg')# 2. 转到 HSV 颜色空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 3. 定义颜色范围(这里以绿色圆为例)
lower_green = np.array([40, 50, 50])
upper_green = np.array([80, 255, 255])# 4. 生成掩模
mask = cv2.inRange(hsv, lower_green, upper_green)# 5. 提取物体
extracted_object = cv2.bitwise_and(image, image, mask=mask)# 6. 显示
cv2.imshow("Original", image)
cv2.imshow("Mask", mask)
cv2.imshow("Extracted Object", extracted_object)
cv2.waitKey(0)
cv2.destroyAllWindows()

执行效果:

在这里插入图片描述

轮廓近似与多边形拟合

轮廓可能包含成千上万个点,为了简化形状并减少计算量,可以使用多边形拟合。

import cv2
import numpy as np# 1. 创建一个形状(这里是一个圆,但可以用于任何复杂的形状)
img = np.zeros((300, 300), dtype=np.uint8)
cv2.circle(img, (150, 150), 100, 255, -1)# 2. 查找轮廓
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 使用CHAIN_APPROX_NONE来获取所有点
original_contour = contours[0]# 3. 轮廓近似
# 参数 epsilon 决定了近似的精度,值越小,近似越接近原始轮廓
epsilon = 0.04 * cv2.arcLength(original_contour, True)
approx_contour = cv2.approxPolyDP(original_contour, epsilon, True)# 4. 绘制原始轮廓和近似后的轮廓
# 原始轮廓(红色)
original_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.drawContours(original_img, [original_contour], -1, (0, 0, 255), 2)# 近似轮廓(绿色)
approx_img = np.zeros_like(original_img)
cv2.drawContours(approx_img, [approx_contour], -1, (0, 255, 0), 2)
cv2.imshow("Original vs. Approximated", np.hstack([original_img, approx_img]))
cv2.waitKey(0)
cv2.destroyAllWindows()

执行效果:

在这里插入图片描述

http://www.xdnf.cn/news/20172.html

相关文章:

  • 破局功能割裂、成本高昂、协同低效,遨游天通卫星电话实现一机多能
  • Adobe Illustrator(Ai) 2022矢量设计软件的安装教程与下载地址
  • 【Python自动化】 21.3 Pandas Series 核心数据结构完全指南
  • 如何使显示器在笔记本盖上盖子时还能正常运转
  • windows找不到gpedit.msc(本地组策略编辑器)
  • Docker容器安全最佳实践:镜像扫描、权限控制与逃逸防范
  • Pie Menu Editor V1.18.7.exe 怎么安装?详细安装教程(附安装包)​
  • [linux仓库]性能加速的隐形引擎:深度解析Linux文件IO中的缓冲区奥秘
  • Java并发锁相关
  • LeetCode - 202. 快乐数
  • 深度学习——数据增强(Data Augmentation)
  • HTML HTML基础(2)
  • 数控机床中,进行前瞻速度规划时,根据几何约束限制计算的拐角过渡速度
  • HTML基础(决定页面结构)
  • MQTT 与 Java 框架集成:Spring Boot 实战(一)
  • 【GEOS-Chem伴随模型第二期】GEOS-Chem Adjoint 安装与配置
  • 2025年互联网行业高含金量证书盘点!
  • leetcode 2749. 得到整数零需要执行的最少操作数 中等
  • 邪修实战系列(1)
  • 使用CI/CD部署项目(前端Nextjs)
  • SQL Server事务隔离级别
  • JavaScript 面向对象 原型和原型链 继承
  • 西嘎嘎学习-day 1
  • 栈:有效的括号
  • Dify-CHATflow案例
  • JS中的String的常用方法
  • Process Explorer 学习笔记(第三章3.2.3):工具栏与参考功能
  • 知微集:Python中的线程(三)
  • JavaScript 中的并发编程实践与误区:一次深入的探讨
  • 软考高级 — 系统规划与管理师考试知识点精要