Opencv(十七) : 绘制图像轮廓

文章目录

一、轮廓的核心概念与基础定义

1. 轮廓的本质的

轮廓是目标物体或区域在图像中的封闭边界集合,由一系列连续相连的像素点构成,能够完整勾勒出物体的外部形态和内部结构。与单纯的边缘检测不同,轮廓强调像素的连通性和边界的封闭性,是对物体形状的完整描述,而非孤立的边缘片段。

在机器视觉任务中,轮廓的核心价值在于将图像的像素信息转化为可量化的几何特征(如周长、面积、中心坐标等),为后续的形状分析、目标识别和图像分割提供基础数据支撑。

2. 轮廓与边缘的核心区别

很多初学者容易混淆轮廓和边缘,二者虽有重叠但本质不同,具体区别如下:

对比维度 轮廓 边缘
像素特性 连续连通的封闭集合 可孤立、不连续的像素点
核心用途 物体形态分析(周长、面积等) 图像特征提取(区分不同区域)
存在前提 必须形成封闭结构 只要灰度值突变即可存在
数据形式 完整的边界序列 离散的边缘点或边缘线

举个直观例子:一张手写数字"8"的二值图,边缘是数字笔画与背景交界处的所有像素点(可能存在断点),而轮廓是能够完整包围"8"外部和内部孔洞的两条封闭边界,通过这两条轮廓可直接计算数字的总面积、外周长、内孔洞面积等关键参数。

3. 轮廓绘制的基础术语定义

在进行轮廓绘制前,需明确以下核心术语,避免后续算法理解出现偏差:

(1)图像与像素相关
  • frame(图像框架):一张图像的最上行、最下行、最左列、最右列构成的边界,是图像的天然边界。
  • 0-pixel(0像素):灰度值为0的像素,默认作为图像背景填充像素。
  • 1-pixel(1像素):灰度值为1的像素,默认作为目标物体的构成像素。
  • 像素坐标与灰度值:用(i, j)表示图像中第i行、第j列的像素点,(f_{i,j})表示该像素点的灰度值,整幅图像可表示为像素灰度值的集合(F={f_{i,j}})。
(2)连通域相关
  • 连通域(component) :由相邻(通常指8连通或4连通)的同一灰度值像素构成的区域。
    • 1-component(1连通域):由1像素组成的连通域,即目标物体所在区域。
    • 0-component(0连通域):由0像素组成的连通域,可能是背景或物体内部的孔洞。
  • background(背景):包含frame(图像框架)的0连通域,是图像中目标物体的外部环境区域。
  • 孔洞(hole):不包含frame的0连通域,是目标物体内部被1像素包围的空白区域(如数字"8"中间的两个空白区域)。
(3)边界相关
  • borderpoint(边界点):若一个1像素的8连通区域内存在0像素,则该1像素为边界点。轮廓正是由一系列连续的边界点构成。
  • 环绕连通域:在二值图中,若连通域S1中任意像素点沿4方向到达frame的路径上均存在S2的像素,则称S2环绕S1;若S2环绕S1且两者之间存在边界点,则称S2直接环绕S1。
  • 外边界与孔边界
    • 外边界:若0连通域S2直接环绕1连通域S1,则S2与S1之间的边界为外边界(包围目标物体的外部边界)。
    • 孔边界:若1连通域S1直接环绕0连通域S2,则S2与S1之间的边界为孔边界(包围物体内部孔洞的边界)。
  • 父边界:若0连通域S2直接环绕1连通域S1,1连通域S3直接环绕S2,S1与S2的边界为B1,S2与S3的边界为B2,则B2为B1的父边界;若S2是背景(background),则B1的父边界为frame。

二、轮廓绘制的实操流程与注意事项

1. 完整实操流程(以Python+OpenCV为例)

OpenCV是机器视觉领域常用的开源库,提供了成熟的轮廓检测函数cv2.findContours(),其底层实现基于上述边界跟踪算法。以下是完整的实操流程,从图像预处理到轮廓可视化,一步一步实现轮廓绘制:

(1)环境准备

安装必要的库:

python 复制代码
pip install opencv-python numpy matplotlib
(2)图像预处理

图像预处理是轮廓绘制的关键前提,直接影响轮廓检测的准确性。常见的预处理步骤包括:

  • 读取图像:读取原始图像,若为彩色图需转为灰度图。
  • 二值化:将灰度图转为二值图,分离目标与背景。
  • 噪声去除:通过形态学操作(如腐蚀、膨胀)去除图像噪声,避免噪声形成虚假轮廓。

代码实现:

python 复制代码
#导入opencv库
import cv2
#1.读取图片
image_np = cv2.imread('./picture.png')
#复制一份原始图像,用来绘制轮廓
image_contours = image_np.copy()
#2.灰度化
image_gray = cv2.cvtColor(image_np,cv2.COLOR_BGR2GRAY)
#3.二值化
ret,image_binary = cv2.threshold(image_gray,127,255,cv2.THRESH_OTSU+cv2.THRESH_BINARY_INV)
#4.查找轮廓
#contours:存储了所有轮廓点的坐标,比如术后contours[0]就存储了第0个轮廓的点的坐标,contours[1]就存储了第一个轮廓的层级关系
#hierarchy: 存储了对应轮廓的层级关系,hierarchy[0]就存储了第0个轮廓的层级关系
contours,hierarchy = cv2.findContours(image_binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#5.绘制轮廓
cv2.drawContours(image_contours,contours,-1,(0,0,255) )
#6.结果显示
cv2.imshow('image_np',image_np)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)```

#### (3)轮廓检测
使用`cv2.findContours()`函数检测轮廓,该函数返回轮廓列表、轮廓层级关系和边界矩形:
```python
# 轮廓检测(cv2.RETR_CCOMP:检测所有轮廓并建立层级关系;cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点)
contours, hierarchy = cv2.findContours(opening, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# 打印轮廓信息
print(f"检测到的轮廓数量:{len(contours)}")
print(f"轮廓层级关系:{hierarchy}")  # hierarchy格式:[Next, Previous, First_Child, Parent]

输出结果为:
注意:图片路径需要你自己导入你设备上的路径

(3) 轮廓点压缩的合理使用

cv2.findContours()函数的method参数用于轮廓点的压缩:

  • cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点,仅保留轮廓的关键顶点(如矩形轮廓仅保留 4 个顶点),减少数据量,提高后续处理效率。
  • cv2.CHAIN_APPROX_NONE:保留轮廓上的所有像素点,数据量较大,但适合需要高精度轮廓的场景(如微小零件的尺寸测量)。
(4)轮廓特征计算

根据检测到的轮廓,计算关键几何特征:

python 复制代码
# 遍历所有轮廓,计算特征
contour_features = []
for i, cnt in enumerate(contours):
    # 1. 面积
    area = cv2.contourArea(cnt)
    # 2. 周长(True表示闭合轮廓)
    perimeter = cv2.arcLength(cnt, True)
    # 3. 外接矩形
    x, y, w, h = cv2.boundingRect(cnt)
    # 4. 中心坐标(矩)
    M = cv2.moments(cnt)
    cx = int(M["m10"] / M["m00"]) if M["m00"] != 0 else 0
    cy = int(M["m01"] / M["m00"]) if M["m00"] != 0 else 0
    # 5. 圆形度
    circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter != 0 else 0
    
    contour_features.append({
        "轮廓编号": i,
        "面积": area,
        "周长": perimeter,
        "外接矩形(x,y,w,h)": (x, y, w, h),
        "中心坐标(cx,cy)": (cx, cy),
        "圆形度": circularity
    })

# 打印特征结果
for feature in contour_features:
    print(f"\n轮廓{feature['轮廓编号']}特征:")
    for key, value in feature.items():
        print(f"{key}:{value}")
(5)轮廓可视化

将检测到的轮廓绘制在原始图像上,直观展示检测结果:

python 复制代码
# 绘制轮廓(-1表示填充轮廓内部,0表示仅绘制边界)
img_contour = img.copy()
cv2.drawContours(img_contour, contours, -1, (0, 255, 0), 2)  # 绿色绘制轮廓,线宽2

# 绘制中心坐标和轮廓编号
for i, cnt in enumerate(contours):
    M = cv2.moments(cnt)
    cx = int(M["m10"] / M["m00"]) if M["m00"] != 0 else 0
    cy = int(M["m01"] / M["m00"]) if M["m00"] != 0 else 0
    cv2.circle(img_contour, (cx, cy), 3, (255, 0, 0), -1)  # 蓝色绘制中心点
    cv2.putText(img_contour, str(i), (cx+5, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)  # 红色标注轮廓编号

# 显示可视化结果
plt.figure(figsize=(8, 6))
plt.imshow(cv2.cvtColor(img_contour, cv2.COLOR_BGR2RGB))
plt.title("轮廓检测结果")
plt.axis("off")
plt.show()

2. 实操注意事项(避坑指南)

(1)图像预处理是关键
  • 二值化阈值选择:若阈值过高,会导致目标物体像素丢失,轮廓不完整;若阈值过低,会引入过多背景噪声,形成虚假轮廓。建议使用自适应阈值分割(cv2.adaptiveThreshold())或大津阈值分割(cv2.THRESH_OTSU),避免手动阈值的主观性。
  • 噪声去除:工业图像或手写图像中常存在椒盐噪声,需通过形态学操作(开运算、闭运算)或高斯滤波(cv2.GaussianBlur())去除噪声,否则噪声会被误检测为小轮廓,影响后续分析。
(2)轮廓层级关系的正确理解

cv2.findContours()函数的mode参数决定了轮廓的层级检测方式,常用的两种模式:

  • cv2.RETR_EXTERNAL:仅检测最外层轮廓,忽略内部孔边界,适合只需目标物体外轮廓的场景。
  • cv2.RETR_CCOMP:检测所有轮廓并建立两层层级关系(外边界为第一层,孔边界为第二层),适合需要分析物体内部孔洞的场景。

若不理解层级关系,可能会误将孔边界当作独立的目标轮廓,导致特征计算错误。

(3)轮廓点压缩的合理使用

cv2.findContours()函数的method参数用于轮廓点的压缩:

  • cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点,仅保留轮廓的关键顶点(如矩形轮廓仅保留4个顶点),减少数据量,提高后续处理效率。
  • cv2.CHAIN_APPROX_NONE:保留轮廓上的所有像素点,数据量较大,但适合需要高精度轮廓的场景(如微小零件的尺寸测量)。

根据实际需求选择压缩方式,避免不必要的计算开销。

(4)特征计算的异常处理

在计算轮廓特征时,需避免分母为0的情况:

  • 周长为0:可能是孤立的单个像素点,需过滤此类无效轮廓。
  • 矩的m00为0:表示轮廓面积为0,是无效轮廓,需跳过中心坐标计算。
(5)多目标轮廓的筛选

若图像中存在多个轮廓,需根据实际需求筛选目标轮廓:

  • 按面积筛选:设置面积阈值,过滤掉面积过小的噪声轮廓和面积过大的背景轮廓。
  • 按宽高比筛选:根据目标物体的宽高比范围,筛选出符合要求的轮廓(如矩形零件的宽高比通常在1-3之间)。

代码示例(按面积筛选):

python 复制代码
# 筛选面积在100-10000之间的轮廓
valid_contours = [cnt for cnt in contours if 100 < cv2.contourArea(cnt) < 10000]
print(f"筛选后的有效轮廓数量:{len(valid_contours)}")

三、总结与拓展

图像轮廓绘制是机器视觉的核心技术之一,其本质是通过边界跟踪算法提取目标物体的封闭边界,为形状分析、目标识别和图像分割提供基础。本文从核心概念、作用场景、算法原理、实操流程四个维度,详细解析了轮廓绘制的全流程,关键要点总结如下:

  1. 轮廓与边缘的核心区别在于连通性和封闭性,轮廓是完整的封闭边界,而边缘可孤立不连续。
  2. 边界跟踪算法是轮廓绘制的核心,通过光栅扫描检测起始点,结合顺时针/逆时针邻域搜索实现轮廓跟踪。
  3. 实操中需重视图像预处理,合理选择二值化方法和形态学操作,避免噪声和阈值不当导致的检测误差。
  4. 轮廓特征计算和可视化是后续应用的基础,需根据实际需求筛选有效轮廓并计算关键参数。
相关推荐
子午2 分钟前
【鸟类识别系统】Python+TensorFlow+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
qq_160144873 分钟前
AI爱好者入门:2025年CAIE报考指南与学习路径解析
人工智能·学习
WebGIS开发4 分钟前
东北黑土地保护|智慧城市地图可视化智能监测、管理系统
人工智能·gis·智慧城市·gis开发·webgis·地理信息科学
某林2125 分钟前
在slam建图中为何坐标base_link,laser,imu_link是始终在一起的,但是odom 会与这位三个坐标在运行中产生偏差
人工智能·算法
Keep__Fighting7 分钟前
【机器学习:逻辑回归】
人工智能·python·算法·机器学习·逻辑回归·scikit-learn·matplotlib
23遇见8 分钟前
AI情绪识别技术:价值与局限并存的智能革新
人工智能
科技与数码8 分钟前
国产MATLAB替代软件的关键能力与生态发展现状
大数据·人工智能·matlab
数据的世界0112 分钟前
重构智慧书-第6条:在趋近圆满中践行成长
人工智能
阿杰学AI14 分钟前
AI核心知识29——大语言模型之Multimodality(简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·aigc·多模态·多模态大模型
极市平台17 分钟前
骁龙大赛技术分享第4期来了
人工智能·经验分享·笔记·后端·个人开发