OpenCV 滤波算法全面解析与代码实现
一、滤波算法概述
1.1 滤波的基本概念
滤波是图像处理中的基本操作,主要用于:
二、基本线性滤波算法
2.1 均值滤波(Average Filter)
原理:用邻域像素的平均值替换中心像素值
# 应用场景:去除随机噪声,简单平滑处理def mean_filter_example(img_rgb): # 使用3x3核 kernel_size = (3, 3) mean_filtered = cv2.blur(img_rgb, kernel_size) # 使用5x5核 mean_filtered_large = cv2.blur(img_rgb, (5, 5)) # 显示结果 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(mean_filtered) axes[1].set_title('3x3 Mean Filter') axes[1].axis('off') axes[2].imshow(mean_filtered_large) axes[2].set_title('5x5 Mean Filter') axes[2].axis('off') plt.tight_layout() plt.show() return mean_filtered
2.2 高斯滤波(Gaussian Filter)
原理:使用高斯函数作为权重核,距离中心越近权重越大
# 应用场景:去除高斯噪声,图像预处理,降采样前的平滑def gaussian_filter_example(img_rgb): # 不同标准差的高斯滤波 gaussian_small = cv2.GaussianBlur(img_rgb, (3, 3), 0) # sigma=0,自动计算 gaussian_medium = cv2.GaussianBlur(img_rgb, (5, 5), 1.0) gaussian_large = cv2.GaussianBlur(img_rgb, (9, 9), 2.0) # 显示结果 fig, axes = plt.subplots(2, 2, figsize=(12, 10)) axes[0, 0].imshow(img_rgb) axes[0, 0].set_title('Original Image') axes[0, 0].axis('off') axes[0, 1].imshow(gaussian_small) axes[0, 1].set_title('Gaussian 3x3, σ=auto') axes[0, 1].axis('off') axes[1, 0].imshow(gaussian_medium) axes[1, 0].set_title('Gaussian 5x5, σ=1.0') axes[1, 0].axis('off') axes[1, 1].imshow(gaussian_large) axes[1, 1].set_title('Gaussian 9x9, σ=2.0') axes[1, 1].axis('off') plt.tight_layout() plt.show() return gaussian_medium
2.3 方框滤波(Box Filter)
# 应用场景:快速平滑,积分图像计算def box_filter_example(img_rgb): # 归一化的方框滤波(等同于均值滤波) box_normalized = cv2.boxFilter(img_rgb, -1, (3, 3), normalize=True) # 非归一化的方框滤波(像素值会累积) box_non_normalized = cv2.boxFilter(img_rgb, -1, (3, 3), normalize=False) box_non_normalized = np.clip(box_non_normalized / 255, 0, 1) # 归一化显示 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(box_normalized) axes[1].set_title('Normalized Box Filter 3x3') axes[1].axis('off') axes[2].imshow(box_non_normalized) axes[2].set_title('Non-Normalized Box Filter 3x3') axes[2].axis('off') plt.tight_layout() plt.show() return box_normalized
三、非线性滤波算法
3.1 中值滤波(Median Filter)
原理:用邻域像素的中值替换中心像素
# 应用场景:去除椒盐噪声,保护边缘def median_filter_example(img_rgb): # 添加椒盐噪声 noisy_img = img_rgb.copy() noise_prob = 0.05 noise_mask = np.random.random(noisy_img.shape[:2]) < noise_prob # 添加椒盐噪声 noisy_img[noise_mask] = 0 # 黑点 noisy_img[np.random.random(noisy_img.shape[:2]) < noise_prob/2] = 255 # 白点 # 应用中值滤波 median_3 = cv2.medianBlur(noisy_img.astype(np.uint8), 3) median_5 = cv2.medianBlur(noisy_img.astype(np.uint8), 5) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(noisy_img) axes[0].set_title('Image with Salt & Pepper Noise') axes[0].axis('off') axes[1].imshow(median_3) axes[1].set_title('Median Filter 3x3') axes[1].axis('off') axes[2].imshow(median_5) axes[2].set_title('Median Filter 5x5') axes[2].axis('off') plt.tight_layout() plt.show() return median_3
3.2 双边滤波(Bilateral Filter)
原理:结合空间距离和像素值相似性的权重
# 应用场景:边缘保持平滑,美颜滤镜,去噪同时保留细节def bilateral_filter_example(img_rgb): # 双边滤波参数:d为邻域直径,sigmaColor为颜色空间标准差,sigmaSpace为坐标空间标准差 bilateral_small = cv2.bilateralFilter(img_rgb, d=9, sigmaColor=75, sigmaSpace=75) bilateral_large = cv2.bilateralFilter(img_rgb, d=15, sigmaColor=150, sigmaSpace=150) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(bilateral_small) axes[1].set_title('Bilateral Filter (d=9, σ=75)') axes[1].axis('off') axes[2].imshow(bilateral_large) axes[2].set_title('Bilateral Filter (d=15, σ=150)') axes[2].axis('off') plt.tight_layout() plt.show() return bilateral_small
四、边缘检测滤波器
4.1 Sobel算子
原理:计算图像梯度,检测水平或垂直边缘
# 应用场景:边缘检测,梯度计算def sobel_filter_example(img_rgb): # 转换为灰度图 gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # Sobel算子 sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) # 计算梯度幅值 sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2) sobel_magnitude = np.uint8(255 * sobel_magnitude / np.max(sobel_magnitude)) # 计算梯度方向 sobel_direction = np.arctan2(np.abs(sobel_y), np.abs(sobel_x)) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original Grayscale') axes[0, 0].axis('off') axes[0, 1].imshow(np.abs(sobel_x), cmap='gray') axes[0, 1].set_title('Sobel X') axes[0, 1].axis('off') axes[0, 2].imshow(np.abs(sobel_y), cmap='gray') axes[0, 2].set_title('Sobel Y') axes[0, 2].axis('off') axes[1, 0].imshow(sobel_magnitude, cmap='gray') axes[1, 0].set_title('Gradient Magnitude') axes[1, 0].axis('off') axes[1, 1].imshow(sobel_direction, cmap='hsv') axes[1, 1].set_title('Gradient Direction') axes[1, 1].axis('off') # 合并X和Y sobel_combined = cv2.addWeighted(np.abs(sobel_x), 0.5, np.abs(sobel_y), 0.5, 0) axes[1, 2].imshow(sobel_combined, cmap='gray') axes[1, 2].set_title('Combined Sobel') axes[1, 2].axis('off') plt.tight_layout() plt.show() return sobel_magnitude
4.2 Scharr算子
原理:Sobel的改进,对边缘响应更强
# 应用场景:更精确的边缘检测def scharr_filter_example(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) scharr_x = cv2.Scharr(gray, cv2.CV_64F, 1, 0) scharr_y = cv2.Scharr(gray, cv2.CV_64F, 0, 1) scharr_magnitude = np.sqrt(scharr_x**2 + scharr_y**2) scharr_magnitude = np.uint8(255 * scharr_magnitude / np.max(scharr_magnitude)) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(np.abs(scharr_x), cmap='gray') axes[1].set_title('Scharr X') axes[1].axis('off') axes[2].imshow(np.abs(scharr_y), cmap='gray') axes[2].set_title('Scharr Y') axes[2].axis('off') plt.tight_layout() plt.show() return scharr_magnitude
4.3 Laplacian算子
原理:二阶微分算子,检测图像强度的快速变化
# 应用场景:边缘检测,角点检测,图像锐化def laplacian_filter_example(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 应用高斯模糊后使用Laplacian blurred = cv2.GaussianBlur(gray, (3, 3), 0) # Laplacian算子 laplacian = cv2.Laplacian(blurred, cv2.CV_64F) laplacian_abs = np.uint8(np.absolute(laplacian)) # 零交叉检测 laplacian_zero_cross = np.zeros_like(laplacian_abs) laplacian_zero_cross[laplacian_abs > 30] = 255 fig, axes = plt.subplots(1, 4, figsize=(20, 5)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(laplacian_abs, cmap='gray') axes[1].set_title('Laplacian') axes[1].axis('off') axes[2].imshow(laplacian_zero_cross, cmap='gray') axes[2].set_title('Zero Crossing (Threshold=30)') axes[2].axis('off') # LoG (Laplacian of Gaussian) log_kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]], dtype=np.float32) log_filtered = cv2.filter2D(blurred, -1, log_kernel) axes[3].imshow(np.abs(log_filtered), cmap='gray') axes[3].set_title('LoG Filtered') axes[3].axis('off') plt.tight_layout() plt.show() return laplacian_abs
4.4 Canny边缘检测
原理:多阶段边缘检测算法
# 应用场景:精确边缘检测,计算机视觉预处理def canny_edge_detection(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 不同阈值的Canny检测 canny_low = cv2.Canny(gray, 50, 150) canny_medium = cv2.Canny(gray, 100, 200) canny_high = cv2.Canny(gray, 150, 250) # 自适应Canny median_intensity = np.median(gray) lower_thresh = int(max(0, 0.7 * median_intensity)) upper_thresh = int(min(255, 1.3 * median_intensity)) canny_adaptive = cv2.Canny(gray, lower_thresh, upper_thresh) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original Grayscale') axes[0, 0].axis('off') axes[0, 1].imshow(canny_low, cmap='gray') axes[0, 1].set_title('Canny (50, 150)') axes[0, 1].axis('off') axes[0, 2].imshow(canny_medium, cmap='gray') axes[0, 2].set_title('Canny (100, 200)') axes[0, 2].axis('off') axes[1, 0].imshow(canny_high, cmap='gray') axes[1, 0].set_title('Canny (150, 250)') axes[1, 0].axis('off') axes[1, 1].imshow(canny_adaptive, cmap='gray') axes[1, 1].set_title(f'Adaptive Canny ({lower_thresh}, {upper_thresh})') axes[1, 1].axis('off') axes[1, 2].axis('off') plt.tight_layout() plt.show() return canny_medium
五、高级滤波算法
5.1 导向滤波(Guided Filter)
# 应用场景:图像去噪,细节增强,HDR压缩def guided_filter_example(img_rgb): """导向滤波实现""" # 转换为浮点型 guide = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) / 255.0 src = guide.copy() # OpenCV中的导向滤波 guided = cv2.ximgproc.guidedFilter(guide=guide, src=src, radius=5, eps=0.01) # 与双边滤波比较 bilateral = cv2.bilateralFilter((guide * 255).astype(np.uint8), 9, 75, 75) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(guide, cmap='gray') axes[0].set_title('Original') axes[0].axis('off') axes[1].imshow(guided, cmap='gray') axes[1].set_title('Guided Filter') axes[1].axis('off') axes[2].imshow(bilateral, cmap='gray') axes[2].set_title('Bilateral Filter') axes[2].axis('off') plt.tight_layout() plt.show() return guided
5.2 非局部均值去噪(Non-Local Means Denoising)
# 应用场景:高质量图像去噪,医学图像处理def non_local_means_denoising(img_rgb): """非局部均值去噪""" # 添加高斯噪声 noisy = img_rgb.copy().astype(np.float32) / 255.0 noise = np.random.normal(0, 0.1, noisy.shape) noisy_with_gaussian = np.clip(noisy + noise, 0, 1) # 转换为uint8 noisy_uint8 = (noisy_with_gaussian * 255).astype(np.uint8) # 应用非局部均值去噪 denoised = cv2.fastNlMeansDenoisingColored(noisy_uint8, None, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original') axes[0].axis('off') axes[1].imshow(noisy_uint8) axes[1].set_title('With Gaussian Noise') axes[1].axis('off') axes[2].imshow(denoised) axes[2].set_title('Non-Local Means Denoised') axes[2].axis('off') plt.tight_layout() plt.show() return denoised
# 应用场景:二值图像处理,形状分析,噪声去除def morphological_filters(img_rgb): """形态学滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 创建二值图像用于演示 _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 结构元素 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 腐蚀 erosion = cv2.erode(binary, kernel, iterations=1) # 膨胀 dilation = cv2.dilate(binary, kernel, iterations=1) # 开运算(先腐蚀后膨胀) opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 闭运算(先膨胀后腐蚀) closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) # 形态学梯度 gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(binary, cmap='gray') axes[0, 0].set_title('Binary Image') axes[0, 0].axis('off') axes[0, 1].imshow(erosion, cmap='gray') axes[0, 1].set_title('Erosion') axes[0, 1].axis('off') axes[0, 2].imshow(dilation, cmap='gray') axes[0, 2].set_title('Dilation') axes[0, 2].axis('off') axes[1, 0].imshow(opening, cmap='gray') axes[1, 0].set_title('Opening') axes[1, 0].axis('off') axes[1, 1].imshow(closing, cmap='gray') axes[1, 1].set_title('Closing') axes[1, 1].axis('off') axes[1, 2].imshow(gradient, cmap='gray') axes[1, 2].set_title('Morphological Gradient') axes[1, 2].axis('off') plt.tight_layout() plt.show() return opening, closing
5.4 各向异性扩散滤波(Anisotropic Diffusion)
# 应用场景:医学图像处理,边缘保持平滑,纹理分析def anisotropic_diffusion(img_rgb): """各向异性扩散滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) # 简单的Perona-Malik各向异性扩散实现 def perona_malik_diffusion(img, iterations=10, kappa=50, gamma=0.1): img_diffused = img.copy() for _ in range(iterations): # 计算梯度 grad_north = np.roll(img_diffused, -1, axis=0) - img_diffused grad_south = np.roll(img_diffused, 1, axis=0) - img_diffused grad_east = np.roll(img_diffused, -1, axis=1) - img_diffused grad_west = np.roll(img_diffused, 1, axis=1) - img_diffused # 扩散系数 c_north = 1.0 / (1.0 + (grad_north / kappa)**2) c_south = 1.0 / (1.0 + (grad_south / kappa)**2) c_east = 1.0 / (1.0 + (grad_east / kappa)**2) c_west = 1.0 / (1.0 + (grad_west / kappa)**2) # 更新图像 img_diffused += gamma * (c_north * grad_north + c_south * grad_south + c_east * grad_east + c_west * grad_west) return img_diffused # 应用各向异性扩散 diffused = perona_malik_diffusion(gray, iterations=20, kappa=30, gamma=0.1) fig, axes = plt.subplots(1, 2, figsize=(12, 6)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(diffused, cmap='gray') axes[1].set_title('Anisotropic Diffusion') axes[1].axis('off') plt.tight_layout() plt.show() return diffused
# 应用场景:频域分析,周期性噪声去除,图像增强def frequency_domain_filtering(img_rgb): """频域滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 傅里叶变换 dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) # 创建滤波器 rows, cols = gray.shape crow, ccol = rows // 2, cols // 2 # 低通滤波器 mask_low = np.zeros((rows, cols, 2), np.uint8) r = 30 mask_low[crow-r:crow+r, ccol-r:ccol+r] = 1 # 高通滤波器 mask_high = np.ones((rows, cols, 2), np.uint8) r = 30 mask_high[crow-r:crow+r, ccol-r:ccol+r] = 0 # 带阻滤波器 mask_band = np.ones((rows, cols, 2), np.uint8) r_outer = 60 r_inner = 20 cv2.circle(mask_band, (ccol, crow), r_outer, 0, -1) cv2.circle(mask_band, (ccol, crow), r_inner, 1, -1) # 应用滤波 fshift_low = dft_shift * mask_low fshift_high = dft_shift * mask_high fshift_band = dft_shift * mask_band # 逆变换 def inverse_transform(fshift): f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1]) return cv2.normalize(img_back, None, 0, 255, cv2.NORM_MINMAX) result_low = inverse_transform(fshift_low) result_high = inverse_transform(fshift_high) result_band = inverse_transform(fshift_band) fig, axes = plt.subplots(2, 4, figsize=(16, 8)) # 显示原图和频谱 magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]) + 1) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original') axes[0, 0].axis('off') axes[0, 1].imshow(magnitude_spectrum, cmap='gray') axes[0, 1].set_title('Magnitude Spectrum') axes[0, 1].axis('off') axes[0, 2].imshow(mask_low[:,:,0], cmap='gray') axes[0, 2].set_title('Low Pass Filter') axes[0, 2].axis('off') axes[0, 3].imshow(result_low, cmap='gray') axes[0, 3].set_title('Low Pass Result') axes[0, 3].axis('off') axes[1, 0].imshow(mask_high[:,:,0], cmap='gray') axes[1, 0].set_title('High Pass Filter') axes[1, 0].axis('off') axes[1, 1].imshow(result_high, cmap='gray') axes[1, 1].set_title('High Pass Result') axes[1, 1].axis('off') axes[1, 2].imshow(mask_band[:,:,0], cmap='gray') axes[1, 2].set_title('Band Stop Filter') axes[1, 2].axis('off') axes[1, 3].imshow(result_band, cmap='gray') axes[1, 3].set_title('Band Stop Result') axes[1, 3].axis('off') plt.tight_layout() plt.show() return result_low, result_high
环境要求(代码亲测有效,放心使用):opencv-python、opencv-contrib-python、 numpy 、matplotlib
pip install opencv-python opencv-contrib-python numpy matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple/
import cv2import numpy as npimport matplotlib.pyplot as plt# 应用场景:去除随机噪声,简单平滑处理# 均值滤波def mean_filter_example(img_rgb): # 使用3x3核 kernel_size = (3, 3) mean_filtered = cv2.blur(img_rgb, kernel_size) # 使用5x5核 mean_filtered_large = cv2.blur(img_rgb, (5, 5)) # 显示结果 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(mean_filtered) axes[1].set_title('3x3 Mean Filter') axes[1].axis('off') axes[2].imshow(mean_filtered_large) axes[2].set_title('5x5 Mean Filter') axes[2].axis('off') plt.tight_layout() plt.show() return mean_filtered# 应用场景:去除高斯噪声,图像预处理,降采样前的平滑def gaussian_filter_example(img_rgb): # 不同标准差的高斯滤波 gaussian_small = cv2.GaussianBlur(img_rgb, (3, 3), 0) # sigma=0,自动计算 gaussian_medium = cv2.GaussianBlur(img_rgb, (5, 5), 1.0) gaussian_large = cv2.GaussianBlur(img_rgb, (9, 9), 2.0) # 显示结果 fig, axes = plt.subplots(2, 2, figsize=(12, 10)) axes[0, 0].imshow(img_rgb) axes[0, 0].set_title('Original Image') axes[0, 0].axis('off') axes[0, 1].imshow(gaussian_small) axes[0, 1].set_title('Gaussian 3x3, σ=auto') axes[0, 1].axis('off') axes[1, 0].imshow(gaussian_medium) axes[1, 0].set_title('Gaussian 5x5, σ=1.0') axes[1, 0].axis('off') axes[1, 1].imshow(gaussian_large) axes[1, 1].set_title('Gaussian 9x9, σ=2.0') axes[1, 1].axis('off') plt.tight_layout() plt.show() return gaussian_medium# 应用场景:快速平滑,积分图像计算def box_filter_example(img_rgb): # 归一化的方框滤波(等同于均值滤波) box_normalized = cv2.boxFilter(img_rgb, -1, (3, 3), normalize=True) # 非归一化的方框滤波(像素值会累积) box_non_normalized = cv2.boxFilter(img_rgb, -1, (3, 3), normalize=False) box_non_normalized = np.clip(box_non_normalized / 255, 0, 1) # 归一化显示 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(box_normalized) axes[1].set_title('Normalized Box Filter 3x3') axes[1].axis('off') axes[2].imshow(box_non_normalized) axes[2].set_title('Non-Normalized Box Filter 3x3') axes[2].axis('off') plt.tight_layout() plt.show() return box_normalized# 应用场景:去除椒盐噪声,保护边缘def median_filter_example(img_rgb): # 添加椒盐噪声 noisy_img = img_rgb.copy() noise_prob = 0.05 noise_mask = np.random.random(noisy_img.shape[:2]) < noise_prob # 添加椒盐噪声 noisy_img[noise_mask] = 0 # 黑点 noisy_img[np.random.random(noisy_img.shape[:2]) < noise_prob/2] = 255 # 白点 # 应用中值滤波 median_3 = cv2.medianBlur(noisy_img.astype(np.uint8), 3) median_5 = cv2.medianBlur(noisy_img.astype(np.uint8), 5) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(noisy_img) axes[0].set_title('Image with Salt & Pepper Noise') axes[0].axis('off') axes[1].imshow(median_3) axes[1].set_title('Median Filter 3x3') axes[1].axis('off') axes[2].imshow(median_5) axes[2].set_title('Median Filter 5x5') axes[2].axis('off') plt.tight_layout() plt.show() return median_3# 应用场景:边缘保持平滑,美颜滤镜,去噪同时保留细节def bilateral_filter_example(img_rgb): # 双边滤波参数:d为邻域直径,sigmaColor为颜色空间标准差,sigmaSpace为坐标空间标准差 bilateral_small = cv2.bilateralFilter(img_rgb, d=9, sigmaColor=75, sigmaSpace=75) bilateral_large = cv2.bilateralFilter(img_rgb, d=15, sigmaColor=150, sigmaSpace=150) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original Image') axes[0].axis('off') axes[1].imshow(bilateral_small) axes[1].set_title('Bilateral Filter (d=9, σ=75)') axes[1].axis('off') axes[2].imshow(bilateral_large) axes[2].set_title('Bilateral Filter (d=15, σ=150)') axes[2].axis('off') plt.tight_layout() plt.show() return bilateral_small# 应用场景:边缘检测,梯度计算def sobel_filter_example(img_rgb): # 转换为灰度图 gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # Sobel算子 sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) # 计算梯度幅值 sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2) sobel_magnitude = np.uint8(255 * sobel_magnitude / np.max(sobel_magnitude)) # 计算梯度方向 sobel_direction = np.arctan2(np.abs(sobel_y), np.abs(sobel_x)) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original Grayscale') axes[0, 0].axis('off') axes[0, 1].imshow(np.abs(sobel_x), cmap='gray') axes[0, 1].set_title('Sobel X') axes[0, 1].axis('off') axes[0, 2].imshow(np.abs(sobel_y), cmap='gray') axes[0, 2].set_title('Sobel Y') axes[0, 2].axis('off') axes[1, 0].imshow(sobel_magnitude, cmap='gray') axes[1, 0].set_title('Gradient Magnitude') axes[1, 0].axis('off') axes[1, 1].imshow(sobel_direction, cmap='hsv') axes[1, 1].set_title('Gradient Direction') axes[1, 1].axis('off') # 合并X和Y sobel_combined = cv2.addWeighted(np.abs(sobel_x), 0.5, np.abs(sobel_y), 0.5, 0) axes[1, 2].imshow(sobel_combined, cmap='gray') axes[1, 2].set_title('Combined Sobel') axes[1, 2].axis('off') plt.tight_layout() plt.show() return sobel_magnitude# 应用场景:更精确的边缘检测def scharr_filter_example(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) scharr_x = cv2.Scharr(gray, cv2.CV_64F, 1, 0) scharr_y = cv2.Scharr(gray, cv2.CV_64F, 0, 1) scharr_magnitude = np.sqrt(scharr_x**2 + scharr_y**2) scharr_magnitude = np.uint8(255 * scharr_magnitude / np.max(scharr_magnitude)) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(np.abs(scharr_x), cmap='gray') axes[1].set_title('Scharr X') axes[1].axis('off') axes[2].imshow(np.abs(scharr_y), cmap='gray') axes[2].set_title('Scharr Y') axes[2].axis('off') plt.tight_layout() plt.show() return scharr_magnitude# 应用场景:边缘检测,角点检测,图像锐化def laplacian_filter_example(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 应用高斯模糊后使用Laplacian blurred = cv2.GaussianBlur(gray, (3, 3), 0) # Laplacian算子 laplacian = cv2.Laplacian(blurred, cv2.CV_64F) laplacian_abs = np.uint8(np.absolute(laplacian)) # 零交叉检测 laplacian_zero_cross = np.zeros_like(laplacian_abs) laplacian_zero_cross[laplacian_abs > 30] = 255 fig, axes = plt.subplots(1, 4, figsize=(20, 5)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(laplacian_abs, cmap='gray') axes[1].set_title('Laplacian') axes[1].axis('off') axes[2].imshow(laplacian_zero_cross, cmap='gray') axes[2].set_title('Zero Crossing (Threshold=30)') axes[2].axis('off') # LoG (Laplacian of Gaussian) log_kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]], dtype=np.float32) log_filtered = cv2.filter2D(blurred, -1, log_kernel) axes[3].imshow(np.abs(log_filtered), cmap='gray') axes[3].set_title('LoG Filtered') axes[3].axis('off') plt.tight_layout() plt.show() return laplacian_abs# 应用场景:精确边缘检测,计算机视觉预处理def canny_edge_detection(img_rgb): gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 不同阈值的Canny检测 canny_low = cv2.Canny(gray, 50, 150) canny_medium = cv2.Canny(gray, 100, 200) canny_high = cv2.Canny(gray, 150, 250) # 自适应Canny median_intensity = np.median(gray) lower_thresh = int(max(0, 0.7 * median_intensity)) upper_thresh = int(min(255, 1.3 * median_intensity)) canny_adaptive = cv2.Canny(gray, lower_thresh, upper_thresh) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original Grayscale') axes[0, 0].axis('off') axes[0, 1].imshow(canny_low, cmap='gray') axes[0, 1].set_title('Canny (50, 150)') axes[0, 1].axis('off') axes[0, 2].imshow(canny_medium, cmap='gray') axes[0, 2].set_title('Canny (100, 200)') axes[0, 2].axis('off') axes[1, 0].imshow(canny_high, cmap='gray') axes[1, 0].set_title('Canny (150, 250)') axes[1, 0].axis('off') axes[1, 1].imshow(canny_adaptive, cmap='gray') axes[1, 1].set_title(f'Adaptive Canny ({lower_thresh}, {upper_thresh})') axes[1, 1].axis('off') axes[1, 2].axis('off') plt.tight_layout() plt.show() return canny_medium# 应用场景:图像去噪,细节增强,HDR压缩def guided_filter_example(img_rgb): """导向滤波实现""" # 转换为浮点型 guide = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) / 255.0 src = guide.copy() # OpenCV中的导向滤波 guided = cv2.ximgproc.guidedFilter(guide=guide, src=src, radius=5, eps=0.01) # 与双边滤波比较 bilateral = cv2.bilateralFilter((guide * 255).astype(np.uint8), 9, 75, 75) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(guide, cmap='gray') axes[0].set_title('Original') axes[0].axis('off') axes[1].imshow(guided, cmap='gray') axes[1].set_title('Guided Filter') axes[1].axis('off') axes[2].imshow(bilateral, cmap='gray') axes[2].set_title('Bilateral Filter') axes[2].axis('off') plt.tight_layout() plt.show() return guided# 应用场景:高质量图像去噪,医学图像处理def non_local_means_denoising(img_rgb): """非局部均值去噪""" # 添加高斯噪声 noisy = img_rgb.copy().astype(np.float32) / 255.0 noise = np.random.normal(0, 0.1, noisy.shape) noisy_with_gaussian = np.clip(noisy + noise, 0, 1) # 转换为uint8 noisy_uint8 = (noisy_with_gaussian * 255).astype(np.uint8) # 应用非局部均值去噪 denoised = cv2.fastNlMeansDenoisingColored(noisy_uint8, None, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_rgb) axes[0].set_title('Original') axes[0].axis('off') axes[1].imshow(noisy_uint8) axes[1].set_title('With Gaussian Noise') axes[1].axis('off') axes[2].imshow(denoised) axes[2].set_title('Non-Local Means Denoised') axes[2].axis('off') plt.tight_layout() plt.show() return denoised# 应用场景:二值图像处理,形状分析,噪声去除def morphological_filters(img_rgb): """形态学滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 创建二值图像用于演示 _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 结构元素 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 腐蚀 erosion = cv2.erode(binary, kernel, iterations=1) # 膨胀 dilation = cv2.dilate(binary, kernel, iterations=1) # 开运算(先腐蚀后膨胀) opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 闭运算(先膨胀后腐蚀) closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) # 形态学梯度 gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel) fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(binary, cmap='gray') axes[0, 0].set_title('Binary Image') axes[0, 0].axis('off') axes[0, 1].imshow(erosion, cmap='gray') axes[0, 1].set_title('Erosion') axes[0, 1].axis('off') axes[0, 2].imshow(dilation, cmap='gray') axes[0, 2].set_title('Dilation') axes[0, 2].axis('off') axes[1, 0].imshow(opening, cmap='gray') axes[1, 0].set_title('Opening') axes[1, 0].axis('off') axes[1, 1].imshow(closing, cmap='gray') axes[1, 1].set_title('Closing') axes[1, 1].axis('off') axes[1, 2].imshow(gradient, cmap='gray') axes[1, 2].set_title('Morphological Gradient') axes[1, 2].axis('off') plt.tight_layout() plt.show() return opening, closing# 应用场景:医学图像处理,边缘保持平滑,纹理分析def anisotropic_diffusion(img_rgb): """各向异性扩散滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) # 简单的Perona-Malik各向异性扩散实现 def perona_malik_diffusion(img, iterations=10, kappa=50, gamma=0.1): img_diffused = img.copy() for _ in range(iterations): # 计算梯度 grad_north = np.roll(img_diffused, -1, axis=0) - img_diffused grad_south = np.roll(img_diffused, 1, axis=0) - img_diffused grad_east = np.roll(img_diffused, -1, axis=1) - img_diffused grad_west = np.roll(img_diffused, 1, axis=1) - img_diffused # 扩散系数 c_north = 1.0 / (1.0 + (grad_north / kappa)**2) c_south = 1.0 / (1.0 + (grad_south / kappa)**2) c_east = 1.0 / (1.0 + (grad_east / kappa)**2) c_west = 1.0 / (1.0 + (grad_west / kappa)**2) # 更新图像 img_diffused += gamma * (c_north * grad_north + c_south * grad_south + c_east * grad_east + c_west * grad_west) return img_diffused # 应用各向异性扩散 diffused = perona_malik_diffusion(gray, iterations=20, kappa=30, gamma=0.1) fig, axes = plt.subplots(1, 2, figsize=(12, 6)) axes[0].imshow(gray, cmap='gray') axes[0].set_title('Original Grayscale') axes[0].axis('off') axes[1].imshow(diffused, cmap='gray') axes[1].set_title('Anisotropic Diffusion') axes[1].axis('off') plt.tight_layout() plt.show() return diffused# 应用场景:频域分析,周期性噪声去除,图像增强def frequency_domain_filtering(img_rgb): """频域滤波""" gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) # 傅里叶变换 dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) # 创建滤波器 rows, cols = gray.shape crow, ccol = rows // 2, cols // 2 # 低通滤波器 mask_low = np.zeros((rows, cols, 2), np.uint8) r = 30 mask_low[crow-r:crow+r, ccol-r:ccol+r] = 1 # 高通滤波器 mask_high = np.ones((rows, cols, 2), np.uint8) r = 30 mask_high[crow-r:crow+r, ccol-r:ccol+r] = 0 # 带阻滤波器 mask_band = np.ones((rows, cols, 2), np.uint8) r_outer = 60 r_inner = 20 cv2.circle(mask_band, (ccol, crow), r_outer, 0, -1) cv2.circle(mask_band, (ccol, crow), r_inner, 1, -1) # 应用滤波 fshift_low = dft_shift * mask_low fshift_high = dft_shift * mask_high fshift_band = dft_shift * mask_band # 逆变换 def inverse_transform(fshift): f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1]) return cv2.normalize(img_back, None, 0, 255, cv2.NORM_MINMAX) result_low = inverse_transform(fshift_low) result_high = inverse_transform(fshift_high) result_band = inverse_transform(fshift_band) fig, axes = plt.subplots(2, 4, figsize=(16, 8)) # 显示原图和频谱 magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]) + 1) axes[0, 0].imshow(gray, cmap='gray') axes[0, 0].set_title('Original') axes[0, 0].axis('off') axes[0, 1].imshow(magnitude_spectrum, cmap='gray') axes[0, 1].set_title('Magnitude Spectrum') axes[0, 1].axis('off') axes[0, 2].imshow(mask_low[:,:,0], cmap='gray') axes[0, 2].set_title('Low Pass Filter') axes[0, 2].axis('off') axes[0, 3].imshow(result_low, cmap='gray') axes[0, 3].set_title('Low Pass Result') axes[0, 3].axis('off') axes[1, 0].imshow(mask_high[:,:,0], cmap='gray') axes[1, 0].set_title('High Pass Filter') axes[1, 0].axis('off') axes[1, 1].imshow(result_high, cmap='gray') axes[1, 1].set_title('High Pass Result') axes[1, 1].axis('off') axes[1, 2].imshow(mask_band[:,:,0], cmap='gray') axes[1, 2].set_title('Band Stop Filter') axes[1, 2].axis('off') axes[1, 3].imshow(result_band, cmap='gray') axes[1, 3].set_title('Band Stop Result') axes[1, 3].axis('off') plt.tight_layout() plt.show() return result_low, result_highdef run_all_filters(img_rgb): """运行所有滤波示例""" print("开始执行所有滤波算法示例...") # 基本线性滤波 print("\n1. 均值滤波示例...") mean_result = mean_filter_example(img_rgb) print("\n2. 高斯滤波示例...") gaussian_result = gaussian_filter_example(img_rgb) print("\n3. 方框滤波示例...") box_result = box_filter_example(img_rgb) # 非线性滤波 print("\n4. 中值滤波示例...") median_result = median_filter_example(img_rgb) print("\n5. 双边滤波示例...") bilateral_result = bilateral_filter_example(img_rgb) # 边缘检测 print("\n6. Sobel算子示例...") sobel_result = sobel_filter_example(img_rgb) print("\n7. Laplacian算子示例...") laplacian_result = laplacian_filter_example(img_rgb) print("\n8. Canny边缘检测示例...") canny_result = canny_edge_detection(img_rgb) # 高级滤波 print("\n10. 导向滤波示例...") guided_result = guided_filter_example(img_rgb) print("\n11. 非局部均值去噪示例...") nlm_result = non_local_means_denoising(img_rgb) print("\n12. 形态学滤波示例...") morph_result = morphological_filters(img_rgb) print("\n13. 频域滤波示例...") freq_result = frequency_domain_filtering(img_rgb) return Trueif __name__ == "__main__": img_rgb = cv2.imread('data/datasets/JPEGImages/00F75605333_0730135838385.jpg') img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB) # 运行所有示例 run_all_filters(img_rgb)
八、滤波算法选择指南
8.1 根据应用场景选择滤波算法
| 应用场景 | 推荐算法 | 优点 | 缺点 |
| 去除高斯噪声 | | | |
| 去除椒盐噪声 | | | |
| 边缘保持去噪 | | | |
| 边缘检测 | | | |
| 图像锐化 | | | |
| 纹理分析 | | | |
| 医学图像处理 | | | |
| 实时处理 | | | |
8.2 参数调优建议
总结
OpenCV提供了丰富的滤波算法,从基本的线性滤波到高级的非线性滤波,每种算法都有其特定的应用场景。选择合适的滤波算法需要考虑: