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×3核就够了。核越大,计算量成倍增加。我有个项目为了追求“干净”,用了9×9核,结果边缘全糊了,缺陷根本检测不出来。后来换回5×5,效果反而更好。

3.2 中值滤波:对付椒盐噪声的利器

中值滤波的原理更直接——把窗口内的像素排序,取中间值替代中心像素。

它最擅长处理椒盐噪声(黑白点噪声)。 为什么?因为椒盐噪声的像素值要么极亮要么极暗,排序后它们会被挤到两端,中间值大概率是正常像素。

我曾经在一个PCB焊点检测项目里遇到过这种情况:图像上有大量随机出现的白点和黑点,高斯滤波怎么调都去不干净。换成中值滤波,3×3核,一遍就干净了。

# 中值滤波
median_blur = cv2.medianBlur(img, 3)

# 对比一下效果
cv2.imshow('Median Blur', median_blur)
滤波器 适用场景 缺点
高斯滤波 高斯噪声、一般噪声 对椒盐噪声效果一般
中值滤波 椒盐噪声、脉冲噪声 计算较慢,核太大图像会“油画化”
注意: 中值滤波的核大小不要超过7×7。我见过有人用15×15,结果图像变得像水彩画,细节全丢了。核大小3或5基本够用。

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)
我的习惯: 只要做均衡化,我默认用CLAHE。clipLimit从2.0开始调,tileGridSize用(8,8)或(16,16)。效果比全局均衡化好太多了。

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 实战建议:预处理流程怎么搭

我一般按这个顺序来搭预处理流水线:

  1. 先看图像——用imshow看一眼,判断噪声类型和对比度情况
  2. 去噪声——椒盐噪声用中值滤波,高斯噪声用高斯滤波
  3. 增强对比度——用CLAHE,别用全局均衡化
  4. 二值化——用Otsu或自适应阈值
  5. 形态学修复——开运算去噪点,闭运算填孔洞
记住: 预处理没有“标准答案”,只有“最适合你的图像的答案”。多试几个参数组合,用imshow实时看效果,比死记硬背参数值管用得多。