1. OpenCV4 图像形态学操作实战指南
作为一名计算机视觉方向的开发者,我在实际项目中经常需要处理各种图像预处理任务。OpenCV 作为最常用的计算机视觉库,其形态学操作功能在图像处理中扮演着重要角色。本章将详细解析 OpenCV4 中的图像形态学操作,包含距离变换、连通域分析、腐蚀膨胀等核心操作,这些都是我在实际项目中的经验总结。
形态学操作看似简单,但在实际应用中却有许多需要注意的细节。比如在工业检测项目中,我曾因为结构元素选择不当导致特征提取失败;在医疗图像处理时,也遇到过连通域分析参数设置不合理造成区域分割错误的情况。通过本文,我将分享这些实战经验,帮助大家避开这些"坑"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 像素距离与连通域分析
2.1 图像像素距离变换
距离变换是计算图像中每个像素到最近背景像素的距离,在图像分割、形状分析等领域有广泛应用。OpenCV 提供了 distanceTransform() 函数来实现这一功能。
2.1.1 距离类型详解
OpenCV 支持三种距离计算方式:
-
欧氏距离 (DIST_L2):最直观的直线距离,计算两个像素点之间的几何距离。公式为 √[(x2-x1)² + (y2-y1)²]。这种距离最精确但计算量较大。
-
街区距离 (DIST_L1):也称为曼哈顿距离,只能沿坐标轴方向移动的距离总和。公式为 |x2-x1| + |y2-y1|。计算速度快,适合对精度要求不高的场景。
-
棋盘距离 (DIST_C):取两个方向距离的最大值,公式为 max(|x2-x1|, |y2-y1|)。计算最简单,但精度最低。
以下是三种距离在 5×5 矩阵中的计算示例:
cpp复制// 欧氏距离矩阵
[2.8, 2.2, 2, 2.2, 2.8
2.2, 1.4, 1, 1.4, 2.2
2, 1, 0, 1, 2
2.2, 1.4, 1, 1.4, 2.2
2.8, 2.2, 2, 2.2, 2.8]
// 街区距离矩阵
[4, 3, 2, 3, 4
3, 2, 1, 2, 3
2, 1, 0, 1, 2
3, 2, 1, 2, 3
4, 3, 2, 3, 4]
// 棋盘距离矩阵
[2, 2, 2, 2, 2
2, 1, 1, 1, 2
2, 1, 0, 1, 2
2, 1, 1, 1, 2
2, 2, 2, 2, 2]
2.1.2 distanceTransform() 函数实战
distanceTransform() 函数原型:
cpp复制void distanceTransform(
InputArray src,
OutputArray dst,
OutputArray labels,
int distanceType,
int maskSize,
int labelType = DIST_LABEL_CCOMP
);
参数说明:
- src:输入图像(8位单通道二值图)
- dst:输出距离图(32位浮点单通道图)
- labels:可选的2维标签数组(离散维诺图)
- distanceType:距离类型(DIST_L1, DIST_L2, DIST_C)
- maskSize:距离变换掩码大小(3, 5 或 DIST_MASK_PRECISE)
- labelType:标签数组类型
实际应用示例(大米颗粒分析):
cpp复制Mat rice = imread("rice.png", IMREAD_GRAYSCALE);
threshold(rice, riceBW, 50, 255, THRESH_BINARY);
Mat dist;
distanceTransform(riceBW, dist, DIST_L2, 5);
// 显示结果
normalize(dist, dist, 0, 1.0, NORM_MINMAX); // 归一化便于显示
imshow("Distance Transform", dist);
注意事项:
- 输入图像必须是单通道8位二值图
- 对于大图像,DIST_L2 计算较慢,可考虑使用 DIST_L1
- 结果需要归一化后才能正确显示
- 实际项目中,常将距离变换与阈值处理结合使用
2.2 图像连通域分析
连通域分析用于识别图像中相互连接的像素区域,是许多计算机视觉任务的基础。
2.2.1 连通域算法比较
-
两遍扫描法:
- 第一遍:从左到右、从上到下扫描,给每个前景像素分配临时标签
- 第二遍:合并等价标签,生成最终结果
- 优点:内存效率高
- 缺点:需要处理等价标签
-
种子填充法:
- 随机选择种子点,通过区域生长填充连通区域
- 优点:实现简单
- 缺点:递归实现可能导致栈溢出
2.2.2 connectedComponents() 函数详解
基础连通域分析函数:
cpp复制int connectedComponents(
InputArray image,
OutputArray labels,
int connectivity = 8,
int ltype = CV_32S
);
增强版函数(带统计信息):
cpp复制int connectedComponentsWithStats(
InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
int connectivity = 8,
int ltype = CV_32S
);
stats 输出矩阵包含以下信息(每行对应一个连通域):
