第二章:去噪的基石——空间域滤波与频域滤波
各位同学,欢迎来到实战手册的第二章。
上一章我们聊了噪声的本质,说白了就是搞清楚了「敌人长什么样」。这一章,我们就要亮出第一波武器了——滤波。我个人一直觉得,滤波是图像去噪的「内功心法」。你招式学得再多,内功不行,照片该糊还是糊,该有噪点还是有噪点。
这一章我会把空间域和频域这两大类方法掰开揉碎了讲。嗯,咱们先别急着上深度学习那些花里胡哨的东西,先把地基打牢。
核心观点:没有万能的滤波器,只有最合适的场景。理解每种滤波器的「脾气」,你才能在实战中选对工具。
2.1 空间域滤波:直接在像素上动手
空间域滤波,说白了就是直接拿像素周围的邻居来算。你想想看,一个噪点通常跟周围的像素长得不一样,那我们把它跟邻居「平均」一下,是不是就顺眼多了?
我最早接触图像处理时,第一个学的就是均值滤波。当时觉得这玩意儿太简单了,不就是算个平均值嘛。后来在项目里吃了亏才明白——简单的东西,往往坑最多。
2.1.1 均值滤波
均值滤波的原理,就是用一个窗口滑过图像,把窗口内所有像素的灰度值加起来,除以像素个数,用这个平均值代替中心像素的值。
// 均值滤波核心代码(3x3窗口)
void boxFilter(unsigned char* src, unsigned char* dst, int width, int height) {
int kernel[3][3] = {{1,1,1}, {1,1,1}, {1,1,1}};
int sum, ksum = 9;
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 / ksum;
}
}
}
我曾经踩过的坑:均值滤波会把边缘也「平均」掉。有一次做文档扫描件去噪,用了均值滤波,结果文字边缘全糊了,像泡过水一样。后来我才意识到——均值滤波只适合平坦区域,遇到边缘就得换招。
2.1.2 高斯滤波
高斯滤波跟均值滤波的区别在哪?说白了,均值滤波是「人人平等」,高斯滤波是「近亲远疏」。离中心像素越近的邻居,权重越大;越远的,权重越小。这个权重分布,就是高斯函数。
我个人习惯用高斯滤波做预处理。比如在手机夜景模式里,先来一轮轻度高斯滤波,把最刺眼的高频噪点压一压,后面再做细节增强就不会放大噪声了。
// 5x5高斯核生成(sigma=1.0)
float gaussianKernel[5][5];
float sigma = 1.0f, sum = 0.0f;
int half = 2;
for(int y=-half; y<=half; y++) {
for(int x=-half; x<=half; x++) {
float val = exp(-(x*x + y*y) / (2*sigma*sigma));
gaussianKernel[y+half][x+half] = val;
sum += val;
}
}
// 归一化
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
gaussianKernel[i][j] /= sum;
实战技巧:sigma值选1.0到1.5之间,对手机摄影来说比较安全。sigma太大,照片会像蒙了一层雾。我一般控制在1.2左右,既能去噪,又不损失太多细节。
2.1.3 中值滤波
中值滤波就更有意思了。它不取平均,而是取窗口内所有像素的「中位数」。你想想看,如果窗口里有个椒盐噪声(纯白或纯黑的点),取平均会被它带偏,但取中位数就完全不受影响。
我记得有一次做老照片修复,照片上有大量扫描产生的椒盐噪声。均值滤波和高斯滤波都试了,效果惨不忍睹。换成中值滤波后,那些白点黑点像变魔术一样消失了。嗯,中值滤波对付椒盐噪声,就是降维打击。
三种空间域滤波对比:
| 滤波器 | 去噪能力 | 边缘保留 | 计算速度 | 适用场景 |
|---|---|---|---|---|
| 均值滤波 | ★★☆☆☆ | ★☆☆☆☆ | ★★★★★ | 平坦区域、快速预览 |
| 高斯滤波 | ★★★☆☆ | ★★☆☆☆ | ★★★★☆ | 通用预处理、轻度去噪 |
| 中值滤波 | ★★★★☆ | ★★★★☆ | ★★★☆☆ | 椒盐噪声、文字边缘 |
2.2 频域滤波:换个角度看问题
空间域滤波是在像素层面操作,那频域滤波呢?说白了,就是把图像从「空间坐标」变换到「频率坐标」。你想想看,一张照片里,平坦区域对应低频,边缘和纹理对应高频,噪声通常也落在高频区域。
所以频域去噪的思路就很清晰了——把高频部分砍掉一部分,再变回空间域。这就是低通滤波的核心思想。
2.2.1 低通滤波
低通滤波在频域里做,其实就是一个「保留低频,衰减高频」的操作。最常用的就是理想低通滤波、巴特沃斯低通滤波和高斯低通滤波。
我个人在实际项目中很少用理想低通,因为它太「硬」了,会在图像边缘产生振铃效应——就是那种一圈一圈的伪影,看着特别假。高斯低通滤波就平滑得多,过渡自然。
// 频域低通滤波流程(伪代码)
1. 对图像做FFT(快速傅里叶变换),得到频域数据
2. 生成低通滤波器掩膜(比如高斯型)
3. 频域数据与掩膜逐元素相乘
4. 做IFFT(逆傅里叶变换),得到去噪后的图像
注意:频域滤波的边界处理很关键。如果图像边界不连续,FFT会产生高频泄漏。我建议在做FFT之前,先对图像做边缘延拓(比如镜像对称),能有效减少边界伪影。
2.2.2 小波变换
小波变换比傅里叶变换高明在哪?傅里叶变换只能告诉你「有哪些频率成分」,但不知道这些频率出现在图像的哪个位置。小波变换就不一样了,它能同时给出频率和位置信息——这就是所谓的「时频局部化」。
我记得在手机HDR+算法里,小波变换被用来做多帧融合时的细节分离。把图像分解成低频近似分量和高频细节分量,然后对不同分量做不同的处理。低频部分做去噪,高频部分做增强,互不干扰。
// 小波去噪的经典三步
1. 分解:对图像做小波变换,得到近似系数和细节系数
2. 阈值处理:对细节系数做软阈值或硬阈值处理
- 软阈值:系数小于阈值的置零,大于的向零收缩
- 硬阈值:系数小于阈值的置零,大于的保留原值
3. 重构:用处理后的系数做逆小波变换
我的经验:软阈值去噪效果更平滑,但会损失一些边缘锐度。硬阈值保留边缘更好,但可能残留一些噪声点。我一般先用软阈值做一轮粗去噪,再用硬阈值做一轮精修。两轮下来,效果就比较理想了。
2.3 经典去噪算法对比
好了,前面讲了这么多方法,到底该用哪个?我整理了一张对比表,方便你快速选型。
| 算法 | 噪声类型 | 边缘保留 | 计算开销 | 手机端可行性 |
|---|---|---|---|---|
| 均值滤波 | 高斯噪声 | 差 | 极低 | 实时 |
| 高斯滤波 | 高斯噪声 | 一般 | 低 | 实时 |
| 中值滤波 | 椒盐噪声 | 好 | 中等 | 实时 |
| 频域低通 | 高频噪声 | 一般 | 高(FFT) | 需优化 |
| 小波变换 | 多种噪声 | 好 | 高 | 需硬件加速 |
你可能会问:「那我到底该选哪个?」我的建议是——看场景。如果是实时预览,用高斯滤波就够了,速度快,效果也还行。如果是后期处理,小波变换是更好的选择,虽然慢一点,但质量高。如果是椒盐噪声,别犹豫,直接上中值滤波。
嗯,这一章的内容就到这儿。滤波是去噪的基石,把这些基础打牢了,后面学非局部均值、BM3D这些高级算法时,你才能理解它们到底「高级」在哪。
公众号:蓝海资料掘金营,微信deep3321