3. 图像预处理:灰度化、直方图均衡化、滤波与增强
图像预处理,说白了就是给原始图像“洗把脸”。
相机采集到的数据,往往带着噪声、光照不均、对比度差等问题。如果直接扔给后面的检测算法,结果大概率会翻车。我做了这么多年视觉项目,可以负责任地告诉你:预处理做得好,后续算法事半功倍;预处理糊弄事,后面调参调到怀疑人生。
3.1 图像灰度化:降维打击
彩色图像每个像素有 R、G、B 三个通道,处理起来计算量太大。灰度化就是把三通道压缩成单通道,保留亮度信息,丢掉颜色信息。
常用的方法有三种:
| 方法 | 公式 | 特点 | 适用场景 |
|---|---|---|---|
| 加权平均法 | Gray = 0.299R + 0.587G + 0.114B | 符合人眼视觉特性 | 通用场景,最推荐 |
| 平均值法 | Gray = (R + G + B) / 3 | 计算简单,但丢失细节 | 对精度要求不高的场合 |
| 最大值法 | Gray = max(R, G, B) | 保留高亮区域 | 高光检测场景 |
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 底层就是用的这个权重,稳定可靠。
import cv2
import numpy as np
# 读取彩色图像
img = cv2.imread('input.jpg')
# 方法1:OpenCV 内置函数(推荐)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 方法2:手动实现加权平均
b, g, r = cv2.split(img)
gray_manual = (0.299 * r + 0.587 * g + 0.114 * b).astype(np.uint8)
cv2.imwrite('gray.jpg', gray)
3.2 直方图均衡化:让对比度“支棱”起来
你想想看,一张图像如果整体偏暗或者偏亮,细节根本看不清楚。直方图均衡化就是通过拉伸像素值的分布范围,让暗的地方变亮,亮的地方变暗,整体对比度拉满。
我遇到过最典型的案例:一个工厂的零件检测项目,车间灯光不均匀,图像半边亮半边暗。直接做阈值分割根本没法用。后来用 CLAHE(限制对比度自适应直方图均衡化)处理了一下,效果立竿见影。
两种均衡化的对比:
| 类型 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 全局均衡化 (HE) | 基于整张图像的直方图 | 实现简单,效果明显 | 噪声放大,过度增强 |
| CLAHE | 分块均衡 + 对比度限制 | 抑制噪声,局部细节好 | 参数需要调(clip limit、网格大小) |
import cv2
img = cv2.imread('dark.jpg', 0) # 以灰度图读取
# 全局直方图均衡化
equ = cv2.equalizeHist(img)
# CLAHE(推荐)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(img)
cv2.imwrite('clahe_result.jpg', clahe_img)
clipLimit 一般设在 1.0~3.0 之间,tileGridSize 用 8x8 起步。如果图像细节多,可以适当减小网格尺寸。
3.3 图像滤波:去噪与保边的博弈
滤波的本质是“卷积”。用一个小的窗口(核)在图像上滑动,对窗口内的像素做加权平均。不同的核,干不同的事。
三种最常用的滤波,我帮你拆开讲:
3.3.1 均值滤波
最简单的线性滤波。核内所有像素取平均。优点是快,缺点是——它不分青红皂白,边缘和噪声一起模糊。
blur = cv2.blur(img, (5, 5)) # 5x5 的核
3.3.2 高斯滤波
比均值滤波聪明一点。它根据像素离中心点的距离分配权重——越近权重越大,越远权重越小。这样能更好地保留边缘。
gaussian = cv2.GaussianBlur(img, (5, 5), sigmaX=1.0)
sigmaX 控制高斯函数的“胖瘦”。sigma 越大,图像越模糊。我一般从 1.0 开始试,不够再加。
3.3.3 中值滤波
非线性的。把窗口内的像素排序,取中间值代替中心像素。它对椒盐噪声(黑白点噪声)有奇效。
我记得有一次做 PCB 焊点检测,图像上全是随机出现的白点噪声。均值滤波和高斯滤波都试了,效果很差。换成中值滤波,核大小设成 3,噪声直接消失,焊点边缘还清清楚楚。
median = cv2.medianBlur(img, 3) # 核大小必须是奇数
三种滤波的对比总结:
| 滤波类型 | 噪声抑制 | 边缘保留 | 计算速度 | 最佳应用 |
|---|---|---|---|---|
| 均值滤波 | 一般 | 差 | 快 | 轻度噪声,实时性要求高 |
| 高斯滤波 | 较好 | 中等 | 中等 | 通用去噪,预处理标准步骤 |
| 中值滤波 | 优秀(椒盐噪声) | 好 | 较慢 | 椒盐噪声、需要保边的场景 |
3.4 图像增强技术:让特征“跳”出来
增强不是去噪,而是主动提升图像的某些特征,让后续算法更容易抓到关键信息。
常用的增强手段:
- 伽马校正: 调整图像的亮度曲线。gamma < 1 提亮暗部,gamma > 1 压暗亮部。我常用 gamma=0.5 来处理过曝的图像。
- 拉普拉斯锐化: 增强边缘和细节。说白了就是让图像更“锋利”。
- USM 锐化(Unsharp Masking): 先模糊,再与原图做差,把差值加回去。效果比直接拉普拉斯更自然。
import cv2
import numpy as np
img = cv2.imread('blurry.jpg')
# 伽马校正
gamma = 0.5
gamma_corrected = np.power(img / 255.0, gamma) * 255.0
gamma_corrected = gamma_corrected.astype(np.uint8)
# 拉普拉斯锐化
laplacian = cv2.Laplacian(img, cv2.CV_64F)
sharpened = cv2.addWeighted(img, 1.5, laplacian, -0.5, 0)
# USM 锐化
blurred = cv2.GaussianBlur(img, (0, 0), sigmaX=3.0)
usm = cv2.addWeighted(img, 1.5, blurred, -0.5, 0)
cv2.imwrite('enhanced.jpg', usm)
3.5 预处理流程设计思路
实际项目中,预处理不是把上面所有方法都跑一遍。而是根据问题来选。我一般这样思考:
- 先看噪声类型: 椒盐噪声?用中值滤波。高斯噪声?用高斯滤波。
- 再看光照条件: 不均匀?用 CLAHE。整体偏暗?用伽马校正。
- 最后看检测目标: 要边缘?做锐化。要区域?做平滑。