2. 图像滤波与增强
图像滤波和增强,说白了就是给图像「做美容」。但美容手法不对,反而会毁掉原始信息。我在嵌入式视觉项目里踩过不少坑,今天就把这些经验掰开揉碎讲给你听。
核心认知:滤波是「减法」——去除噪声;增强是「加法」——突出特征。两者顺序不能乱,先滤波再增强是铁律。
2.1 均值滤波:最简单的平滑,但代价不小
均值滤波的原理很简单:用邻域内所有像素的平均值代替中心像素。你想想看,这就像把一块布在脏玻璃上抹过去——整体干净了,但细节也糊了。
// C语言实现3x3均值滤波(针对8位灰度图)
void boxFilter_3x3(uint8_t *src, uint8_t *dst, int width, int height) {
int sum;
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
sum = 0;
for (int ky = -1; ky <= 1; ky++)
for (int kx = -1; kx <= 1; kx++)
sum += src[(y + ky) * width + (x + kx)];
dst[y * width + x] = sum / 9;
}
}
}
我曾经在安防摄像头项目里直接用均值滤波去噪,结果运动目标的边缘全糊了,导致后续检测算法误报率飙升。记住:均值滤波会破坏边缘,不适合需要保留细节的场景。
| 核大小 | 平滑程度 | 边缘保留 | 计算量 |
|---|---|---|---|
| 3×3 | 低 | 一般 | 9次加法+1次除法 |
| 5×5 | 中 | 较差 | 25次加法+1次除法 |
| 7×7 | 高 | 差 | 49次加法+1次除法 |
2.2 高斯滤波:更自然的平滑方式
高斯滤波和均值滤波的区别在哪?均值滤波「一视同仁」,高斯滤波「远近亲疏」——离中心越近的像素权重越大。这更符合自然图像的统计特性。
我个人习惯用 σ(标准差)来控制平滑强度。σ 越大,核中心的权重越分散,平滑效果越强。
// 生成5x5高斯核(σ=1.0)
float gaussianKernel_5x5[5][5] = {
{1, 4, 7, 4, 1},
{4, 16, 26, 16, 4},
{7, 26, 41, 26, 7},
{4, 16, 26, 16, 4},
{1, 4, 7, 4, 1}
};
// 归一化因子:273
实战技巧:在嵌入式平台上,我建议用整型高斯核(如上所示)代替浮点运算。把归一化因子做成移位操作,能省掉除法。比如上面这个核,273 ≈ 256,右移8位即可。
2.3 中值滤波:椒盐噪声的克星
中值滤波不取平均,取中位数。这招对付椒盐噪声特别管用。为什么?因为噪声点通常是极值(0或255),取中位数时会被直接跳过。
我记得有一次做工业质检,摄像头采集的图像上全是黑白噪点。均值滤波搞不定,高斯滤波也糊。换成3×3中值滤波,一次通过。
// 3x3中值滤波核心(快速选择算法)
uint8_t median_3x3(uint8_t window[9]) {
// 使用冒泡排序只排前5个
for (int i = 0; i < 5; i++) {
for (int j = i + 1; j < 9; j++) {
if (window[i] > window[j]) {
uint8_t tmp = window[i];
window[i] = window[j];
window[j] = tmp;
}
}
}
return window[4]; // 第5个就是中位数
}
注意:中值滤波的计算量比均值滤波大得多。3×3核需要排序9个数,5×5核要排25个数。在嵌入式平台上,我通常只在检测到椒盐噪声时才启用中值滤波,平时用高斯滤波保底。
2.4 直方图均衡化:让暗部亮起来
直方图均衡化(HE)的原理很简单:把像素值的分布「拉平」。原本集中在暗区的像素会被拉伸到整个0-255范围。
但这里有个坑——HE是全局操作。如果图像本身有亮区和暗区,均衡化后亮区会过曝,暗区会过暗。我曾在车载夜视项目里吃过这个亏。
// 直方图均衡化核心步骤
void histogramEqualize(uint8_t *img, int size) {
int hist[256] = {0};
int cdf[256] = {0};
// 1. 统计直方图
for (int i = 0; i < size; i++)
hist[img[i]]++;
// 2. 计算累积分布函数
cdf[0] = hist[0];
for (int i = 1; i < 256; i++)
cdf[i] = cdf[i-1] + hist[i];
// 3. 映射像素值
int cdf_min = cdf[0];
for (int i = 0; i < size; i++)
img[i] = (uint8_t)((cdf[img[i]] - cdf_min) * 255.0f / (size - cdf_min));
}
2.5 自适应直方图均衡化:局部才是王道
为了解决全局HE的问题,自适应直方图均衡化(AHE)出现了。它把图像分成小块,每块单独做均衡化。
但AHE有个新问题:噪声会被放大。尤其是在平坦区域,一点点噪声经过局部均衡化后,会变成明显的颗粒感。
解决方案是限制对比度——CLAHE(Contrast Limited AHE)。我建议把裁剪阈值设在2-4之间,效果最好。
CLAHE参数推荐(嵌入式场景):
- 块大小:8×8(速度快)或 16×16(效果好)
- 裁剪阈值:3.0(平衡噪声和增强)
- 插值方式:双线性插值(避免块效应)
2.6 实战选择指南
说了这么多,到底该用哪个?我整理了一个决策流程,你在项目里可以直接套用:
- 先判断噪声类型:椒盐噪声→中值滤波;高斯噪声→高斯滤波;混合噪声→先中值后高斯
- 再评估计算资源:MCU平台→均值滤波+查表法HE;ARM Linux→高斯滤波+CLAHE
- 最后看应用场景:人脸检测→CLAHE(增强局部特征);文字识别→高斯滤波(平滑背景)
我的经验:在摄像头调优时,我通常把滤波和增强做成可配置的参数链。ISP管线里预留3个滤波级和2个增强级,现场根据场景切换。这样一套代码能适配室内、室外、夜视多种模式。