|
OpenCV Python 图像滤波完全指南:高斯、中值、双边、Gabor、傅里叶变换

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

理论

双边滤波是一种边缘保持的非线性滤波器。它同时考虑两个因素:

  1. 空间距离(与高斯滤波类似)——距离越近权重越大
  2. 颜色差异——颜色越接近权重越大

这意味着在平滑平坦区域的同时,双边滤波能很好地保留边缘信息,不会因为跨越边缘而混合不同区域的像素值。

语法

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()

Sobel 边缘检测结果

语法说明

cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
  • src:输入图像
  • ddepth:输出图像深度(推荐 cv2.CV_32Fcv2.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()

Gabor 滤波器结果

getGaborKernel 参数详解

cv2.getGaborKernel(ksize, sigma, theta, lambd, gamma, psi[, ktype])
参数说明
ksize内核大小,如 (21, 21)
sigma高斯函数的标准差
thetaGabor 函数的方向(弧度),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. 总结与最佳实践

核心要点

  1. 选择滤波器前,先分析噪声类型:高斯噪声用高斯滤波,椒盐噪声用中值滤波,需要保边用双边滤波。

  2. 参数调优是关键:核大小(ksize)越大,平滑效果越强,但也越模糊。建议从小核(3×3)开始逐步增大。

  3. 预处理组合使用:常见的流水线是先用中值/高斯去噪 → 再用 Sobel/Canny 检测边缘 → 最后阈值处理得到二值结果。

  4. 频域方法适合全局频率操作:如果你需要精确控制特定频率分量(如去除周期性噪声),傅里叶变换是最佳选择。

  5. Gabor 滤波器适合纹理任务:在需要检测特定方向纹理或边缘时,Gabor 滤波器提供了方向和频率的双重选择性。

  6. 自定义滤波器提供最大灵活性:通过 cv2.filter2D,你可以实现任何基于卷积的滤波操作,从简单的均值滤波到复杂的特征提取。

常用组合推荐

任务推荐组合
通用去噪GaussianBlurthreshold
椒盐噪声去除medianBlurCanny
边缘检测GaussianBlurSobel/Canny
纹理分析getGaborKernel + filter2D
周期性噪声去除dft → 频域掩码 → idft
图像锐化自定义锐化内核 + filter2D
美颜/磨皮bilateralFilter

参考文档