OpenCV OpenCV Python 图像滤波完全指南:高斯、中值、双边、Gabor、傅里叶变换
1. 简介
在实际的图像中都是有噪声(噪点)的,噪声不仅会破坏图像的清晰度,还会使我们的算法更难将其作为输入进行处理。图像滤波是计算机视觉中最基础也最重要的预处理技术之一,它可以去除噪声、增强边缘、平滑纹理,甚至可以实现各种艺术滤镜效果。
本指南将全面介绍 OpenCV Python 中的各类图像滤波技术,涵盖空间域滤波(高斯滤波、中值滤波、双边滤波)、自定义滤波器、Sobel 边缘检测、阈值处理、Gabor 滤波器,以及频域滤波(离散傅里叶变换 DFT)。每个部分都包含理论说明、完整代码示例和实际应用场景分析,帮助你在实际项目中选择最合适的滤波方法。
本示例使用的 OpenCV 版本是:OpenCV 4.1.1
运行 Python 的编辑器:Jupyter notebook 6.0.0
2. 环境准备
首先加载必要的库并准备示例图像。后续所有示例代码都基于此环境。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 加载图像并归一化到 0-1 范围
img = cv2.imread('dog.png').astype(np.float32) / 255
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.title('原始图像')
plt.axis('off')
plt.show()

为了演示滤波效果,我们为原图添加随机噪声:
noised = (img + 0.2 * np.random.rand(*img.shape).astype(np.float32))
noised = noised.clip(0, 1)
plt.imshow(noised[:, :, [0, 1, 2]])
plt.title('添加噪声后的图像')
plt.axis('off')
plt.show()

3. 高斯滤波 cv2.GaussianBlur
理论
高斯滤波是最常用的线性平滑滤波器。它的核心思想是用一个高斯核(二维高斯函数的离散化)与图像做卷积,每个像素的新值是其邻域像素的加权平均值,权重由高斯分布决定。距离中心越近的像素权重越大,越远的权重越小。
高斯滤波能有效去除高斯噪声,但会模糊边缘和细节。
语法
cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])
参数说明:
src:输入图像ksize:高斯核大小,格式为(width, height),必须是正奇数sigmaX:X 方向的高斯标准差sigmaY:Y 方向的高斯标准差;如果设为 0,则自动从sigmaX计算
代码示例
gauss_blur = cv2.GaussianBlur(noised, (7, 7), 0)
plt.imshow(gauss_blur[:, :, [0, 1, 2]])
plt.title('高斯滤波结果')
plt.axis('off')
plt.show()

适用场景
- 去除高斯噪声
- 图像预处理中的平滑步骤
- 在边缘检测之前降低图像噪声
- 对图像质量要求不高,只需快速平滑的场景
4. 中值滤波 cv2.medianBlur
理论
中值滤波是一种非线性滤波器。它将每个像素的值替换为其邻域内所有像素值的中值。中值滤波对椒盐噪声(salt-and-pepper noise)特别有效,因为椒盐噪声表现为极端值(纯黑或纯白),而中值运算天然能排除极端值。
与高斯滤波不同,中值滤波在去噪的同时能较好地保持边缘锐利度。
语法
cv2.medianBlur(src, ksize[, dst])
参数说明:
src:输入图像(注意:对于某些数据类型,需要转换为uint8)ksize:核大小,必须是正奇数(如 3、5、7、9…)
代码示例
median_blur = cv2.medianBlur((noised * 255).astype(np.uint8), 7)
plt.imshow(median_blur[:, :, [0, 1, 2]])
plt.title('中值滤波结果')
plt.axis('off')
plt.show()

适用场景
- 去除椒盐噪声(效果最佳)
- 需要保持边缘锐利的去噪场景
- 医学图像处理
- 嵌入式系统中简单的去噪预处理
5. 双边滤波 cv2.bilateralFilter
理论
双边滤波是一种边缘保持的非线性滤波器。它同时考虑两个因素:
- 空间距离(与高斯滤波类似)——距离越近权重越大
- 颜色差异——颜色越接近权重越大
这意味着在平滑平坦区域的同时,双边滤波能很好地保留边缘信息,不会因为跨越边缘而混合不同区域的像素值。
语法
cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])
参数说明:
src:输入图像d:过滤过程中每个像素邻域的直径。如果为负值,则从sigmaSpace计算sigmaColor:颜色空间的标准差。值越大,越多的颜色会被认为是相似的sigmaSpace:坐标空间的标准差。值越大,越远的像素只要颜色接近就会被纳入考虑
代码示例
bilat = cv2.bilateralFilter(noised, -1, 0.3, 10)
plt.imshow(bilat[:, :, [0, 1, 2]])
plt.title('双边滤波结果')
plt.axis('off')
plt.show()

适用场景与权衡
- 需要保留边缘的去噪(如人像美颜)
- 去除纹理同时保留结构信息
- 权衡:计算速度比高斯和中值滤波慢,不适合实时处理大图像
- 参数调优较复杂,需要反复实验
6. 三种滤波对比
| 对比维度 | 高斯滤波 | 中值滤波 | 双边滤波 |
|---|---|---|---|
| 滤波类型 | 线性 | 非线性 | 非线性 |
| 边缘保持 | 差(会模糊边缘) | 好 | 最好 |
| 去高斯噪声 | 好 | 一般 | 好 |
| 去椒盐噪声 | 差 | 最佳 | 一般 |
| 计算速度 | 快 | 快 | 慢 |
| 参数复杂度 | 低 | 低 | 高 |
| 推荐场景 | 快速平滑、预处理 | 椒盐噪声去除 | 保边去噪、美颜 |
如何选择?
- 如果你只是需要快速平滑图像 → 用高斯滤波
- 如果图像有椒盐噪声 → 用中值滤波
- 如果需要去噪但保留清晰边缘 → 用双边滤波
7. Sobel 边缘检测
理论
Sobel 算子是一种离散微分算子,用于计算图像亮度的梯度近似值。它结合了高斯平滑和微分运算,对噪声有一定的抑制作用。
Sobel 算子分别计算水平方向(x)和垂直方向(y)的梯度:
- dx:检测垂直方向上的边缘(水平梯度)
- dy:检测水平方向上的边缘(垂直梯度)
代码示例
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 以灰度模式加载图片
img = cv2.imread('dog.png', 0)
# 使用 Sobel 算子计算梯度近似值
dx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
dy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
# 显示结果
plt.figure(figsize=(10, 10))
plt.subplot(131)
plt.axis('off')
plt.title('原始图像')
plt.imshow(img, cmap='gray')
plt.subplot(132)
plt.axis('off')
plt.imshow(dx, cmap='gray')
plt.title(r'$\frac{dI}{dx}$')
plt.subplot(133)
plt.axis('off')
plt.title(r'$\frac{dI}{dy}$')
plt.imshow(dy, cmap='gray')
plt.tight_layout()
plt.show()

语法说明
cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
src:输入图像ddepth:输出图像深度(推荐cv2.CV_32F或cv2.CV_64F)dx:x 方向的导数阶数(1 表示一阶导数)dy:y 方向的导数阶数ksize:核大小(1、3、5、7),默认为 3
与阈值结合使用
你可以将 Sobel 结果与阈值处理结合,得到二值化的边缘图:
# 计算梯度幅值
magnitude = np.sqrt(dx**2 + dy**2)
# 归一化
magnitude = magnitude / magnitude.max()
# 阈值化
_, edges = cv2.threshold((magnitude * 255).astype(np.uint8), 50, 255, cv2.THRESH_BINARY)
8. 阈值处理 cv2.threshold
理论
阈值处理是最基本的图像分割方法。它将每个像素与一个阈值比较,根据比较结果决定像素的新值。OpenCV 提供了多种阈值类型,可以分为两组:
- 全局阈值:对所有像素使用相同的阈值
- 自适应阈值:每个像素的阈值取决于其周围像素值
全局阈值类型
ret, dst = cv2.threshold(src, thresh, maxval, type)
| 类型 | 说明 |
|---|---|
cv2.THRESH_BINARY | 像素值 > thresh → maxval;否则 → 0 |
cv2.THRESH_BINARY_INV | 像素值 > thresh → 0;否则 → maxval |
cv2.THRESH_TRUNC | 像素值 > thresh → thresh;否则不变 |
cv2.THRESH_TOZERO | 像素值 > thresh → 不变;否则 → 0 |
cv2.THRESH_TOZERO_INV | 像素值 > thresh → 0;否则不变 |
cv2.THRESH_OTSU | 使用 Otsu 算法自动计算最优阈值(需与上述类型组合使用) |
代码示例
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('dog.png', 0)
# 应用简单的二进制阈值
thr, mask = cv2.threshold(img, 200, 1, cv2.THRESH_BINARY)
print('Threshold used:', thr)
# 应用自适应阈值
adapt_mask = cv2.adaptiveThreshold(
img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 10
)
# 显示对比
plt.figure(figsize=(10, 10))
plt.subplot(131)
plt.axis('off')
plt.title('原始图像')
plt.imshow(img, cmap='gray')
plt.subplot(132)
plt.axis('off')
plt.title('二进制阈值')
plt.imshow(mask, cmap='gray')
plt.subplot(133)
plt.axis('off')
plt.title('自适应阈值')
plt.imshow(adapt_mask, cmap='gray')
plt.tight_layout()
plt.show()

自适应阈值说明
cv2.adaptiveThreshold 为每个像素计算独立的阈值。以 ADAPTIVE_THRESH_MEAN_C 为例,它计算周围像素的平均值,然后减去用户指定的偏差(上例中为 10),作为该像素的阈值。这在光照不均匀的图像中特别有用。
9. 自定义滤波器
理论
除了使用 OpenCV 内置的滤波函数,你还可以自己设计滤波器内核(kernel),然后使用 cv2.filter2D 将其应用到图像上。自定义滤波器的核心是构建一个二维矩阵(内核),矩阵中的值决定了卷积运算时每个邻域像素的权重。
代码示例:创建锐化滤波器
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('dog.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
KSIZE = 11
ALPHA = 2
# 使用 cv2.getGaussianKernel 创建高斯内核
kernel = cv2.getGaussianKernel(KSIZE, 0)
# 构造锐化核:用负的高斯核加上偏移
kernel = -ALPHA * kernel @ kernel.T
kernel += 1 + ALPHA
# 应用自定义滤波器
filtered = cv2.filter2D(img, -1, kernel)
# 显示对比
plt.figure(figsize=(10, 10))
plt.subplot(121)
plt.axis('off')
plt.title('原始图像')
plt.imshow(img[:, :, [0, 1, 2]])
plt.subplot(122)
plt.axis('off')
plt.title('锐化滤波')
plt.imshow(filtered[:, :, [0, 1, 2]])
plt.tight_layout(True)
plt.show()

其他常用自定义内核
# 锐化内核 (3x3)
sharpen_kernel = np.array([[ 0, -1, 0],
[-1, 5, -1],
[ 0, -1, 0]])
# 边缘检测内核 (Laplacian)
laplacian_kernel = np.array([[0, 1, 0],
[1, -4, 1],
[0, 1, 0]])
# 浮雕内核
emboss_kernel = np.array([[-2, -1, 0],
[-1, 1, 1],
[ 0, 1, 2]])
# 应用
filtered = cv2.filter2D(img, -1, sharpen_kernel)
filter2D 语法
cv2.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])
src:输入图像ddepth:输出图像深度(-1 表示与输入相同)kernel:卷积核(单通道浮点矩阵)
10. Gabor 滤波器
理论
Gabor 滤波器是一种线性滤波器,其核心是二维高斯函数与余弦函数的乘积(实数 Gabor)或复指数函数的乘积(复数 Gabor)。Gabor 滤波器能同时获取频率和方向信息,非常适合用于:
- 纹理分析:检测特定方向的纹理模式
- 边缘检测:检测已知方向的边缘
- 特征提取:在人脸识别、指纹识别中广泛使用
代码示例
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
# 以灰度模式加载图片并归一化
img = cv2.imread('dog.png', 0).astype(np.float32) / 255
# 创建实数 Gabor 滤波器内核
kernel = cv2.getGaborKernel((21, 21), 5, 1, 10, 1, 0, cv2.CV_32F)
# 归一化内核
kernel /= math.sqrt((kernel * kernel).sum())
# 应用滤波器
filtered = cv2.filter2D(img, -1, kernel)
# 显示结果
plt.figure(figsize=(10, 10))
plt.subplot(131)
plt.axis('off')
plt.title('原始图像')
plt.imshow(img, cmap='gray')
plt.subplot(132)
plt.title('Gabor 内核')
plt.imshow(kernel, cmap='gray')
plt.subplot(133)
plt.axis('off')
plt.title('滤波结果')
plt.imshow(filtered, cmap='gray')
plt.tight_layout()
plt.show()

getGaborKernel 参数详解
cv2.getGaborKernel(ksize, sigma, theta, lambd, gamma, psi[, ktype])
| 参数 | 说明 |
|---|---|
ksize | 内核大小,如 (21, 21) |
sigma | 高斯函数的标准差 |
theta | Gabor 函数的方向(弧度),0 表示垂直方向 |
lambd | 正弦因子的波长 |
gamma | 空间纵横比(椭圆的长宽比) |
psi | 相位偏移 |
ktype | 内核数据类型 |
通过调整 theta 参数,你可以检测不同方向的边缘。例如,设置 theta = np.pi/4 检测 45° 方向的边缘。
11. 傅里叶变换 cv2.dft
理论
傅里叶变换将图像从空间域转换到频域。在频域中:
- 低频分量:对应图像中缓慢变化的区域(大面积的平坦区域)
- 高频分量:对应图像中快速变化的区域(边缘、纹理、噪声)
通过在频域中操作频率分量,我们可以实现强大的滤波效果:
- 低通滤波:保留低频、去除高频 → 平滑/模糊
- 高通滤波:保留高频、去除低频 → 锐化/边缘增强
步骤 1:从空间域到频域
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('dog.png', 0).astype(np.float32) / 255
# 应用离散傅里叶变换
fft = cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT)
# 将低频率移到数组中心
shifted = np.fft.fftshift(fft, axes=[0, 1])
# 计算幅度谱并取对数以便可视化
magnitude = cv2.magnitude(shifted[:, :, 0], shifted[:, :, 1])
magnitude = np.log(magnitude)
plt.axis('off')
plt.imshow(magnitude, cmap='gray')
plt.title('频率幅度谱')
plt.tight_layout()
plt.show()

步骤 2:验证逆变换
# 逆变换恢复图像
restored = cv2.idft(fft, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
plt.imshow(restored, cmap='gray')
plt.title('逆变换恢复的图像')
plt.axis('off')
plt.show()
步骤 3:频域低通滤波
# 重新执行 DFT
fft = cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT)
fft_shift = np.fft.fftshift(fft, axes=[0, 1])
# 创建低通滤波掩码:只保留中心矩形区域(低频分量)
sz = 25
mask = np.zeros(fft_shift.shape, np.uint8)
mask[mask.shape[0]//2-sz:mask.shape[0]//2+sz,
mask.shape[1]//2-sz:mask.shape[1]//2+sz, :] = 1
fft_shift *= mask
# 将结果移回
fft = np.fft.ifftshift(fft_shift, axes=[0, 1])
# 逆 DFT 转换回空间域
filtered = cv2.idft(fft, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
# 显示对比
plt.figure(figsize=(10, 10))
plt.subplot(121)
plt.axis('off')
plt.title('原始图像')
plt.imshow(img, cmap='gray')
plt.subplot(122)
plt.axis('off')
plt.title('去除高频后的图像')
plt.imshow(filtered, cmap='gray')
plt.tight_layout()
plt.show()

高通滤波(保留边缘)
只需将掩码取反即可实现高通滤波:
# 高通掩码:去除中心低频,保留外围高频
mask_hp = 1 - mask
fft_shift_hp = np.fft.fftshift(fft, axes=[0, 1])
fft_shift_hp *= mask_hp
fft_hp = np.fft.ifftshift(fft_shift_hp, axes=[0, 1])
filtered_hp = cv2.idft(fft_hp, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
12. 综合对比表
| 方法 | 函数 | 域 | 类型 | 主要用途 | 边缘保持 | 速度 |
|---|---|---|---|---|---|---|
| 高斯滤波 | cv2.GaussianBlur | 空间域 | 线性 | 去噪、平滑 | 差 | 快 |
| 中值滤波 | cv2.medianBlur | 空间域 | 非线性 | 去椒盐噪声 | 好 | 快 |
| 双边滤波 | cv2.bilateralFilter | 空间域 | 非线性 | 保边去噪 | 最好 | 慢 |
| Sobel 算子 | cv2.Sobel | 空间域 | 微分 | 边缘检测 | — | 快 |
| 阈值处理 | cv2.threshold | 空间域 | 分割 | 图像二值化 | — | 快 |
| 自定义滤波 | cv2.filter2D | 空间域 | 自定义 | 锐化、浮雕等 | 取决于核 | 中 |
| Gabor 滤波 | cv2.getGaborKernel + filter2D | 空间域 | 线性 | 纹理分析、方向边缘 | 好 | 中 |
| 傅里叶变换 | cv2.dft / cv2.idft | 频域 | 线性 | 频域滤波 | 取决于设计 | 中 |
13. 总结与最佳实践
核心要点
-
选择滤波器前,先分析噪声类型:高斯噪声用高斯滤波,椒盐噪声用中值滤波,需要保边用双边滤波。
-
参数调优是关键:核大小(ksize)越大,平滑效果越强,但也越模糊。建议从小核(3×3)开始逐步增大。
-
预处理组合使用:常见的流水线是先用中值/高斯去噪 → 再用 Sobel/Canny 检测边缘 → 最后阈值处理得到二值结果。
-
频域方法适合全局频率操作:如果你需要精确控制特定频率分量(如去除周期性噪声),傅里叶变换是最佳选择。
-
Gabor 滤波器适合纹理任务:在需要检测特定方向纹理或边缘时,Gabor 滤波器提供了方向和频率的双重选择性。
-
自定义滤波器提供最大灵活性:通过
cv2.filter2D,你可以实现任何基于卷积的滤波操作,从简单的均值滤波到复杂的特征提取。
常用组合推荐
| 任务 | 推荐组合 |
|---|---|
| 通用去噪 | GaussianBlur → threshold |
| 椒盐噪声去除 | medianBlur → Canny |
| 边缘检测 | GaussianBlur → Sobel/Canny |
| 纹理分析 | getGaborKernel + filter2D |
| 周期性噪声去除 | dft → 频域掩码 → idft |
| 图像锐化 | 自定义锐化内核 + filter2D |
| 美颜/磨皮 | bilateralFilter |