深入理解均值滤波的原理、实现与应用
均值滤波是一种线性滤波技术,通过计算像素邻域内的平均值来平滑图像,常用于去除噪声和平滑图像。
均值滤波是一种简单的线性滤波器,它将每个像素的值替换为其邻域内所有像素值的平均值。这种操作可以有效地减少图像中的噪声,但同时也会降低图像的锐度。
| 核大小 | 去噪效果 | 模糊程度 | 适用场景 |
|---|---|---|---|
| 3×3 | ★☆☆ | 低 | 轻微噪声 |
| 5×5 | ★★☆ | 中 | 一般噪声 |
| 7×7 | ★★★ | 高 | 严重噪声 |
对于一个大小为M×N的滤波窗口,输出像素值的计算公式为:
g(i, j) = (1/(M×N)) × Σ Σ f(i+m, j+n)
其中求和范围是 m ∈ [-M/2, M/2], n ∈ [-N/2, N/2]
最常用的均值滤波器是3×3的模板,其权重矩阵为:
1/9 × [1 1 1]
[1 1 1]
[1 1 1]
import cv2
import numpy as np
import matplotlib.pyplot as plt
def mean_filter_builtin(image_path, kernel_size=3):
"""
使用OpenCV内置函数实现均值滤波
:param image_path: 输入图像路径
:param kernel_size: 滤波器大小,必须为奇数
:return: 原图和滤波后的图像
"""
# 读取图像
img = cv2.imread(image_path)
# 应用均值滤波
filtered = cv2.blur(img, (kernel_size, kernel_size))
return img, filtered
def manual_mean_filter(image_path, kernel_size=3):
"""
手动实现均值滤波
:param image_path: 输入图像路径
:param kernel_size: 滤波器大小,必须为奇数
:return: 均值滤波后的图像
"""
# 读取图像
img = cv2.imread(image_path)
img_float = img.astype(np.float32)
height, width, channels = img.shape
# 计算边界填充大小
pad_size = kernel_size // 2
# 对图像进行边界填充
padded_img = np.pad(img_float, ((pad_size, pad_size), (pad_size, pad_size), (0, 0)), mode='edge')
# 创建输出图像
filtered = np.zeros_like(img_float)
# 应用均值滤波
for i in range(height):
for j in range(width):
for c in range(channels):
# 提取当前窗口
window = padded_img[i:i+kernel_size, j:j+kernel_size, c]
# 计算平均值
filtered[i, j, c] = np.mean(window)
return img, filtered.astype(np.uint8)
def separable_mean_filter(image_path, kernel_size=3):
"""
使用可分离滤波器实现均值滤波(提高效率)
:param image_path: 输入图像路径
:param kernel_size: 滤波器大小,必须为奇数
:return: 原图和滤波后的图像
"""
# 读取图像
img = cv2.imread(image_path)
# 创建一维均值滤波器
kernel_1d = np.ones(kernel_size) / kernel_size
# 分别在水平和垂直方向应用滤波器
temp = cv2.filter2D(img, -1, kernel_1d.reshape(1, -1)) # 水平方向
filtered = cv2.filter2D(temp, -1, kernel_1d.reshape(-1, 1)) # 垂直方向
return img, filtered
def adaptive_mean_filter(image_path, noise_variance, kernel_size=3):
"""
自适应均值滤波
:param image_path: 输入图像路径
:param noise_variance: 噪声方差估计
:param kernel_size: 滤波器大小
:return: 原图和自适应滤波后的图像
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE).astype(np.float32)
height, width = img.shape
# 计算边界填充大小
pad_size = kernel_size // 2
# 对图像进行边界填充
padded_img = np.pad(img, pad_size, mode='edge')
# 创建输出图像
filtered = np.zeros_like(img)
# 计算局部统计信息
for i in range(height):
for j in range(width):
# 提取当前窗口
window = padded_img[i:i+kernel_size, j:j+kernel_size]
# 计算局部均值和方差
local_mean = np.mean(window)
local_variance = np.var(window)
# 如果局部方差小于噪声方差,则认为该区域是平坦区域
# 否则应用自适应滤波
if local_variance == 0:
filtered[i, j] = img[i, j]
else:
weight = min(noise_variance / local_variance, 1)
filtered[i, j] = img[i, j] - weight * (img[i, j] - local_mean)
return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_GRAY2BGR), cv2.cvtColor(filtered.astype(np.uint8), cv2.COLOR_GRAY2BGR)
def weighted_mean_filter(image_path, weights=None):
"""
加权均值滤波
:param image_path: 输入图像路径
:param weights: 自定义权重矩阵
:return: 原图和加权滤波后的图像
"""
if weights is None:
# 默认使用高斯权重
weights = np.array([
[1, 2, 1],
[2, 4, 2],
[1, 2, 1]
], dtype=np.float32)
weights /= np.sum(weights)
# 读取图像
img = cv2.imread(image_path)
# 应用加权均值滤波
filtered = cv2.filter2D(img, -1, weights)
return img, filtered
# 使用示例
if __name__ == "__main__":
# 注意:需要提供实际的图像路径
# img, result = mean_filter_builtin('image.jpg', 5)
# manual_img, manual_result = manual_mean_filter('image.jpg', 5)
# sep_img, sep_result = separable_mean_filter('image.jpg', 5)
# adapt_img, adapt_result = adaptive_mean_filter('image.jpg', 10, 5)
# weight_img, weight_result = weighted_mean_filter('image.jpg')
pass
| 核大小 | 去噪效果 | 模糊程度 | 适用场景 |
|---|---|---|---|
| 3×3 | 弱 | 低 | 轻微噪声 |
| 5×5 | 中 | 中 | 一般噪声 |
| 7×7 | 强 | 高 | 严重噪声 |