3. 图像预处理实战:高斯滤波、中值滤波、直方图均衡化、形态学操作
图像预处理,说白了就是给原始图像“洗个澡”。
我做了这么多年视觉项目,发现一个规律:预处理做得好,后续算法事半功倍;预处理糊弄过去,后面调参调到怀疑人生。 尤其是做缺陷检测,光照不稳定、噪声干扰、对比度不足,这些问题不解决,边缘检测和Blob分析根本没法用。
这一章,咱们就把预处理这四板斧练扎实:高斯滤波、中值滤波、直方图均衡化、形态学操作。每个我都会结合项目里的实际坑点来讲。
3.1 高斯滤波:平滑噪声,保留边缘
高斯滤波是视觉工程师最常用的滤波器。它的原理很简单——用一个高斯核跟图像做卷积,每个像素的值被它周围像素的加权平均替代,权重由高斯分布决定。
为什么用高斯核而不是均值核?
因为高斯核给中心像素更高的权重,边缘像素权重低。这样平滑噪声的同时,能尽量保留边缘细节。均值核是“一刀切”,容易把边缘也抹糊了。
| 参数 | 说明 | 我的经验值 |
|---|---|---|
ksize |
核大小,必须为奇数 | 3×3 或 5×5 最常用,太大图像会变模糊 |
sigmaX |
X方向标准差 | 设为0,OpenCV会自动根据核大小计算 |
sigmaY |
Y方向标准差 | 默认与sigmaX相同 |
import cv2
import numpy as np
# 读取图像
img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)
# 高斯滤波
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 0)
# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.2 中值滤波:对付椒盐噪声的利器
中值滤波的原理更直接——把窗口内的像素排序,取中间值替代中心像素。
它最擅长处理椒盐噪声(黑白点噪声)。 为什么?因为椒盐噪声的像素值要么极亮要么极暗,排序后它们会被挤到两端,中间值大概率是正常像素。
我曾经在一个PCB焊点检测项目里遇到过这种情况:图像上有大量随机出现的白点和黑点,高斯滤波怎么调都去不干净。换成中值滤波,3×3核,一遍就干净了。
# 中值滤波
median_blur = cv2.medianBlur(img, 3)
# 对比一下效果
cv2.imshow('Median Blur', median_blur)
| 滤波器 | 适用场景 | 缺点 |
|---|---|---|
| 高斯滤波 | 高斯噪声、一般噪声 | 对椒盐噪声效果一般 |
| 中值滤波 | 椒盐噪声、脉冲噪声 | 计算较慢,核太大图像会“油画化” |
3.3 直方图均衡化:让暗部细节“浮”出来
直方图均衡化,说白了就是把图像的灰度分布“拉平”。
想象一下:一张图像如果整体偏暗,它的直方图会集中在低灰度区域。均衡化就是把这些像素“摊开”到整个0-255范围,让暗部细节变得可见。
什么时候用?
- 光照不均匀的图像
- 对比度太低的图像
- 缺陷和背景灰度接近时
# 全局直方图均衡化
equ = cv2.equalizeHist(img)
# 对比显示
cv2.imshow('Equalized', equ)
不过,全局均衡化有个问题——它会放大噪声。我有个项目是做金属表面划痕检测,原图噪声不大,均衡化之后噪声全出来了,划痕反而被噪声淹没了。
解决方案:CLAHE(限制对比度自适应直方图均衡化)
# CLAHE 均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_img = clahe.apply(img)
cv2.imshow('CLAHE', clahe_img)
| 方法 | 优点 | 缺点 |
|---|---|---|
| 全局均衡化 | 简单、速度快 | 放大噪声、过度增强 |
| CLAHE | 抑制噪声、局部自适应 | 参数需要调(clipLimit和tileGridSize) |
3.4 形态学操作:腐蚀、膨胀、开闭运算
形态学操作是基于图像形状的“数学游戏”。它主要用在二值图像上,但灰度图也能用。
四个基本操作:
| 操作 | 效果 | 应用场景 |
|---|---|---|
| 腐蚀 | 白色区域缩小,消除小凸起 | 去除噪点、分离粘连目标 |
| 膨胀 | 白色区域扩大,填充小孔洞 | 连接断裂区域、填补空洞 |
| 开运算 | 先腐蚀后膨胀 | 去除小噪点,保持目标大小 |
| 闭运算 | 先膨胀后腐蚀 | 填充小孔洞,保持目标大小 |
# 定义结构元素(核)
kernel = np.ones((3, 3), np.uint8)
# 腐蚀
erosion = cv2.erode(img, kernel, iterations=1)
# 膨胀
dilation = cv2.dilate(img, kernel, iterations=1)
# 开运算
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
3.5 本章知识体系
下面这张图把本章的核心逻辑串起来了。你看一遍就能明白:预处理不是乱做,而是有章可循的。
3.6 实战建议:预处理流程怎么搭
我一般按这个顺序来搭预处理流水线:
- 先看图像——用imshow看一眼,判断噪声类型和对比度情况
- 去噪声——椒盐噪声用中值滤波,高斯噪声用高斯滤波
- 增强对比度——用CLAHE,别用全局均衡化
- 二值化——用Otsu或自适应阈值
- 形态学修复——开运算去噪点,闭运算填孔洞