3. 空间域滤波基础:均值滤波、高斯滤波与中值滤波
各位同学,今天我们正式进入去噪算法的核心地带——空间域滤波。说白了,就是直接在图像像素上做文章,用邻域内的像素值来“修复”当前像素。我做了这么多年安防监控,处理过无数个雨夜、雾霾天的监控画面,可以说空间域滤波是咱们这一行的基本功。今天咱们就把均值、高斯、中值这三种最经典的滤波器讲透。
3.1 均值滤波:最简单的平滑方式
均值滤波的原理,说白了就是“平均主义”。用一个窗口(比如3x3)滑过图像,把窗口内所有像素的灰度值加起来求个平均值,然后把这个平均值赋给窗口中心的像素。
数学表达式:
g(x,y) = (1/(m*n)) * Σ f(i,j) 其中 (i,j) ∈ 窗口
嗯,这里要注意:窗口越大,平滑效果越强,但图像也会越模糊。我在项目中遇到过这种情况——为了去掉一个老旧摄像头的雪花噪点,把窗口开到7x7,结果人脸轮廓都糊了。后来我学乖了,一般先用3x3试试。
核心特点:
- 计算简单,速度快
- 对高斯噪声有一定抑制效果
- 会模糊边缘,细节丢失严重
- 对椒盐噪声几乎无效
Python实现:
import cv2
import numpy as np
def mean_filter(image, kernel_size=3):
# 直接用OpenCV的blur函数
result = cv2.blur(image, (kernel_size, kernel_size))
return result
# 或者自己手写一个
def mean_filter_manual(image, kernel_size=3):
h, w = image.shape[:2]
pad = kernel_size // 2
# 边界填充,我习惯用复制填充
padded = cv2.copyMakeBorder(image, pad, pad, pad, pad, cv2.BORDER_REPLICATE)
result = np.zeros_like(image)
for i in range(h):
for j in range(w):
# 提取窗口区域
window = padded[i:i+kernel_size, j:j+kernel_size]
result[i, j] = np.mean(window)
return result
我的小技巧:在安防监控中,如果画面噪点比较均匀(比如老旧CCD传感器),均值滤波3x3就够了。千万别贪大,否则监控画面里人的五官都看不清,那就失去监控的意义了。
3.2 高斯滤波:更自然的平滑
高斯滤波和均值滤波最大的区别在于——它不搞“平均主义”,而是根据距离给每个像素分配不同的权重。离中心越近的像素,权重越大;越远的,权重越小。这个权重分布服从高斯函数(也就是正态分布)。
为什么这样更好? 你想想看,自然图像中,相邻像素的相关性本来就高。离得近的像素,颜色和亮度更接近。高斯滤波正好模拟了这种自然规律,所以平滑效果更自然,边缘保留得也更好。
高斯核的计算:
G(x,y) = (1/(2πσ²)) * exp(-(x²+y²)/(2σ²))
其中σ(标准差)控制着权重的分布。σ越大,权重分布越平缓,平滑效果越强。我个人习惯在安防监控中,σ取1.0到1.5之间,效果比较适中。
Python实现:
def gaussian_filter(image, kernel_size=3, sigma=1.0):
# OpenCV一行搞定
result = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma)
return result
# 手写高斯核
def create_gaussian_kernel(kernel_size=3, sigma=1.0):
k = kernel_size // 2
kernel = np.zeros((kernel_size, kernel_size), dtype=np.float32)
for x in range(-k, k+1):
for y in range(-k, k+1):
kernel[x+k, y+k] = np.exp(-(x**2 + y**2) / (2*sigma**2))
# 归一化,让所有权重加起来等于1
kernel /= np.sum(kernel)
return kernel
我曾经踩过的坑:有一次处理夜间监控画面,噪点特别多,我把σ设到了3.0,结果画面是平滑了,但车牌上的数字全糊成了一片。后来我意识到,高斯滤波的σ不是越大越好,要根据实际噪点水平来调。对于安防监控,我建议σ不要超过2.0。
3.3 中值滤波:椒盐噪声的克星
中值滤波的思路完全不同——它不计算平均值,而是把窗口内的像素值排序,取中间那个值作为结果。这招对付椒盐噪声(就是那种黑白点噪声)特别有效。
为什么中值滤波对椒盐噪声有效? 椒盐噪声的像素值要么是0(黑点),要么是255(白点),都是极端值。取中值的时候,这些极端值会被排到最前面或最后面,根本选不到它。而正常的像素值因为数量多,中值大概率落在正常范围内。
Python实现:
def median_filter(image, kernel_size=3):
# OpenCV直接调用
result = cv2.medianBlur(image, kernel_size)
return result
# 手写实现,加深理解
def median_filter_manual(image, kernel_size=3):
h, w = image.shape[:2]
pad = kernel_size // 2
padded = cv2.copyMakeBorder(image, pad, pad, pad, pad, cv2.BORDER_REPLICATE)
result = np.zeros_like(image)
for i in range(h):
for j in range(w):
window = padded[i:i+kernel_size, j:j+kernel_size]
# 排序取中值
result[i, j] = np.median(window)
return result
三种滤波器的对比:
| 滤波器 | 适用噪声 | 边缘保留 | 计算速度 | 安防场景推荐 |
|---|---|---|---|---|
| 均值滤波 | 高斯噪声(轻度) | 差 | 快 | 老旧传感器、均匀噪点 |
| 高斯滤波 | 高斯噪声(中重度) | 中等 | 中等 | 低光照、夜间监控 |
| 中值滤波 | 椒盐噪声 | 好 | 较慢 | 传输干扰、摄像头故障 |
3.4 本章知识体系
下面这张图总结了三种空间域滤波的核心逻辑,我画出来方便大家对照理解:
实战建议:在安防监控项目中,我通常的做法是——先观察噪点类型。如果是雪花点(均匀分布),用高斯滤波;如果是黑白点(椒盐噪声),用中值滤波。如果实在分不清,那就用中值滤波,它最保险,边缘保留也最好。
好了,今天的内容就到这里。这三种滤波器是空间域去噪的基石,大家一定要亲手跑一遍代码,看看不同参数下的效果差异。下一章我们会聊更高级的滤波方法,到时候你们会发现,今天学的这些基础有多重要。
公众号:蓝海资料掘金营,微信deep3321