前言
频域滤波是数字图像处理的核心技术之一,其核心思想是将图像从空间域转换到频率域,通过修改频率分量实现图像增强、去噪、锐化等操作。本文将按照《数字图像处理》第 4 章的完整目录,用通俗易懂的语言讲解频域滤波的全知识点,并配套可直接运行的 Python 代码、效果对比图,帮你彻底吃透频域滤波!
4.1 背景知识
4.1.1 傅里叶级数与傅里叶变换的发展简史
傅里叶变换的核心是“任何周期函数都可以分解为正弦 / 余弦函数的叠加”,其发展脉络可总结为:
- 1807 年,傅里叶提出 “任何周期函数都可展开为三角函数级数”(傅里叶级数);
- 1822 年,《热的解析理论》正式发表,奠定傅里叶分析基础;
- 后续经柯西、狄利克雷等数学家完善,形成现代傅里叶变换体系;
- 数字时代,离散傅里叶变换(DFT)和快速傅里叶变换(FFT)让傅里叶分析落地到计算机图像处理。
4.1.2 本章示例说明
本章所有示例基于 Python 实现,依赖库包括:numpy(数值计算)、cv2(图像读取 / 处理)、matplotlib(可视化)、scipy(信号处理)。环境准备:
pip install numpy opencv-python matplotlib scipy4.2 预备知识
4.2.1 复数
代码示例:复数基本运算
import numpy as np # 定义复数(三种正确方式,任选其一即可) # 方式1:直接用Python原生复数(推荐,最简单) z1 = 3 + 4j # 3+4j z2 = 1 + 2j # 1+2j # 方式2:np.complex_ 传入字符串(兼容旧版本NumPy) # z1 = np.complex_("3+4j") # z2 = np.complex_("1+2j") # 方式3:np.array 构造复数数组(适合批量定义) # z1 = np.array([3, 4], dtype=np.complex128) # z2 = np.array([1, 2], dtype=np.complex128) # 基本运算 z_add = z1 + z2 # 加法 z_mul = z1 * z2 # 乘法 z_abs = np.abs(z1) # 模长 z_angle = np.angle(z1)# 辐角(弧度) # 输出结果 print(f"z1 = {z1}, z2 = {z2}") print(f"z1 + z2 = {z_add}") print(f"z1 * z2 = {z_mul}") print(f"|z1| = {z_abs}, 辐角 = {z_angle:.2f} rad")输出:
z1 = (3+4j), z2 = (1+2j) z1 + z2 = (4+6j) z1 * z2 = (-5+10j) |z1| = 5.0, 辐角 = 0.93 rad4.2.2 傅里叶级数
代码示例:方波的傅里叶级数逼近
import numpy as np import matplotlib.pyplot as plt # 设置中文字体(解决matplotlib中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 定义方波函数 def square_wave(t, T=2*np.pi): return np.where(np.mod(t, T) < T/2, 1, -1) # 傅里叶级数逼近 def fourier_series(t, n_max, T=2*np.pi): omega0 = 2*np.pi / T result = 0 for n in range(1, n_max+1, 2): # 只保留奇次谐波 an = 0 bn = 4/(n*np.pi) result += an*np.cos(n*omega0*t) + bn*np.sin(n*omega0*t) return result # 生成数据 t = np.linspace(-2*np.pi, 2*np.pi, 1000) y_original = square_wave(t) # 不同项数的逼近 y_1 = fourier_series(t, 1) y_5 = fourier_series(t, 5) y_20 = fourier_series(t, 20) # 可视化对比 plt.figure(figsize=(12, 8)) plt.subplot(2, 2, 1) plt.plot(t, y_original, label='原始方波') plt.title('原始方波') plt.grid(True) plt.subplot(2, 2, 2) plt.plot(t, y_1, label='1项逼近', color='orange') plt.title('1项傅里叶级数逼近') plt.grid(True) plt.subplot(2, 2, 3) plt.plot(t, y_5, label='5项逼近', color='red') plt.title('5项傅里叶级数逼近') plt.grid(True) plt.subplot(2, 2, 4) plt.plot(t, y_20, label='20项逼近', color='green') plt.title('20项傅里叶级数逼近') plt.grid(True) plt.tight_layout() plt.show()效果对比图:
- 1 项逼近:仅一条正弦曲线,与方波差距大;
- 5 项逼近:开始接近方波轮廓;
- 20 项逼近:几乎与原始方波重合,验证了 “傅里叶级数可逼近周期函数”。
4.2.3 冲激函数及其筛分性质
代码示例:冲激函数可视化与筛分性质验证
import numpy as np import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 模拟冲激函数(离散近似) def delta_func(t, t0=0, eps=0.01): return np.where(np.abs(t - t0) < eps, 1/eps, 0) # 定义测试函数 def f(t): return np.sin(t) + 0.5*t # 生成数据 t = np.linspace(-5, 5, 1000) delta = delta_func(t, t0=1) # t0=1处的冲激 f_t = f(t) # 验证筛分性质:积分f(t)*delta(t-1) ≈ f(1) integral = np.trapz(f_t * delta, t) f_1 = f(1) # 可视化 plt.figure(figsize=(10, 6)) plt.plot(t, f_t, label='f(t) = sin(t) + 0.5t', color='blue') plt.plot(t, delta, label='δ(t-1)', color='red', alpha=0.7) plt.scatter(1, f_1, color='green', s=100, label=f'f(1) = {f_1:.2f}') plt.title(f'冲激函数的筛分性质:积分结果 = {integral:.2f} ≈ f(1)') plt.xlabel('t') plt.ylabel('幅值') plt.legend() plt.grid(True) plt.show()4.2.4 单变量连续函数的傅里叶变换
4.2.5 卷积
代码示例:卷积可视化
import numpy as np import matplotlib.pyplot as plt from scipy.signal import convolve plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 定义两个函数 t = np.linspace(-5, 5, 1000) f = np.exp(-t**2) # 高斯函数 g = np.where((t >= -1) & (t <= 1), 1, 0) # 矩形窗 # 计算卷积 conv = convolve(f, g, mode='same') / len(t) # 归一化 # 可视化 plt.figure(figsize=(12, 4)) plt.subplot(1, 3, 1) plt.plot(t, f, label='f(t) = e^(-t²)') plt.title('函数f(t)') plt.grid(True) plt.subplot(1, 3, 2) plt.plot(t, g, label='g(t) 矩形窗') plt.title('函数g(t)') plt.grid(True) plt.subplot(1, 3, 3) plt.plot(t, conv, label='f*g', color='red') plt.title('卷积结果 (f*g)(t)') plt.grid(True) plt.tight_layout() plt.show()4.3 采样与采样函数的傅里叶变换
4.3.1 采样原理
4.3.2 采样函数的傅里叶变换
4.3.3 采样定理
4.3.4 混叠现象
当采样频率不满足奈奎斯特定理时,高频分量会 “折叠” 到低频区域,导致信号失真,称为混叠。
代码示例:采样与混叠对比
import numpy as np import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 定义原始信号:10Hz + 20Hz 正弦波 def signal(t): return np.sin(2*np.pi*10*t) + 0.5*np.sin(2*np.pi*20*t) # 生成连续信号 t_continuous = np.linspace(0, 1, 1000) y_continuous = signal(t_continuous) # 采样1:满足奈奎斯特(采样频率50Hz > 2*20Hz) fs1 = 50 t1 = np.linspace(0, 1, fs1) y1 = signal(t1) # 采样2:不满足奈奎斯特(采样频率30Hz < 2*20Hz) fs2 = 30 t2 = np.linspace(0, 1, fs2) y2 = signal(t2) # 可视化 plt.figure(figsize=(12, 8)) plt.subplot(2, 1, 1) plt.plot(t_continuous, y_continuous, label='原始连续信号', color='blue') plt.scatter(t1, y1, color='red', label=f'采样频率{fs1}Hz(无混叠)') plt.title('满足奈奎斯特定理:无混叠') plt.xlabel('时间 (s)') plt.ylabel('幅值') plt.legend() plt.grid(True) plt.subplot(2, 1, 2) plt.plot(t_continuous, y_continuous, label='原始连续信号', color='blue') plt.scatter(t2, y2, color='orange', label=f'采样频率{fs2}Hz(混叠)') plt.title('不满足奈奎斯特定理:混叠失真') plt.xlabel('时间 (s)') plt.ylabel('幅值') plt.legend() plt.grid(True) plt.tight_layout() plt.show()效果对比:
- 50Hz 采样:离散点能准确还原原始信号;
- 30Hz 采样:离散点明显偏离原始信号,出现混叠失真。
4.3.5 基于采样数据的函数重建(恢复)
4.4 单变量离散傅里叶变换(DFT)
4.4.1 由采样函数的连续傅里叶变换推导离散傅里叶变换
4.4.2 采样间隔与频率间隔的关系
代码示例:单变量 DFT 计算
import numpy as np import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 生成离散信号:5Hz正弦波,采样频率50Hz,长度100 fs = 50 N = 100 t = np.arange(N) / fs f_n = np.sin(2*np.pi*5*t) # 计算DFT F_k = np.fft.fft(f_n) # 频率轴 freq = np.fft.fftfreq(N, 1/fs) # 幅值谱(归一化) amp = np.abs(F_k) / N # 可视化 plt.figure(figsize=(10, 6)) plt.subplot(2, 1, 1) plt.plot(t, f_n) plt.title('原始离散信号(5Hz正弦波)') plt.xlabel('时间 (s)') plt.ylabel('幅值') plt.grid(True) plt.subplot(2, 1, 2) plt.stem(freq, amp, basefmt='b-') plt.title('DFT幅值谱') plt.xlabel('频率 (Hz)') plt.ylabel('幅值') plt.xlim(0, fs/2) # 只显示正频率 plt.grid(True) plt.tight_layout() plt.show()4.5 扩展至双变量函数
4.5.1 二维冲激函数及其筛分性质
4.5.2 二维连续傅里叶变换对
4.5.3 二维采样与二维采样定理
4.5.4 图像中的混叠现象
图像采样时,若分辨率不足(采样频率低),会出现边缘模糊、摩尔纹等混叠现象(如低分辨率图片放大后的锯齿)。
4.5.5 二维离散傅里叶变换及其逆变换
代码示例:图像的二维 DFT 与逆变换
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 读取图像(灰度图) img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE) if img is None: # 若读取失败,生成测试图像 img = np.zeros((256, 256), dtype=np.uint8) img[100:150, 100:150] = 255 # 白色方块 # 计算二维DFT dft = np.fft.fft2(img) # 将低频移到中心 dft_shift = np.fft.fftshift(dft) # 幅值谱(对数缩放,便于可视化) magnitude_spectrum = 20 * np.log(np.abs(dft_shift)) # 逆DFT dft_shift_back = np.fft.ifftshift(dft_shift) img_back = np.fft.ifft2(dft_shift_back) img_back = np.abs(img_back).astype(np.uint8) # 可视化 plt.figure(figsize=(12, 6)) plt.subplot(1, 3, 1) plt.imshow(img, cmap='gray') plt.title('原始图像') plt.axis('off') plt.subplot(1, 3, 2) plt.imshow(magnitude_spectrum, cmap='gray') plt.title('DFT幅值谱(中心对齐)') plt.axis('off') plt.subplot(1, 3, 3) plt.imshow(img_back, cmap='gray') plt.title('逆DFT恢复图像') plt.axis('off') plt.tight_layout() plt.show()效果说明:
- 原始图像:灰度图(或测试方块图);
- 幅值谱:中心亮斑为低频分量(对应图像整体亮度),边缘为高频分量(对应边缘 / 细节);
- 恢复图像:与原始图像几乎一致,验证 IDFT 的正确性。
4.6 二维离散傅里叶变换的性质
4.6.1 空间域间隔与频域间隔的关系
4.6.2 平移性与旋转性
代码示例:图像平移的 DFT 变化
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 读取图像 img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE) if img is None: img = np.zeros((256, 256), dtype=np.uint8) img[100:150, 100:150] = 255 # 图像平移(右移50,下移50) M, N = img.shape dx, dy = 50, 50 img_shift = np.roll(np.roll(img, dx, axis=1), dy, axis=0) # 计算DFT dft_original = np.fft.fftshift(np.fft.fft2(img)) dft_shift = np.fft.fftshift(np.fft.fft2(img_shift)) # 幅值谱 mag_original = 20 * np.log(np.abs(dft_original)) mag_shift = 20 * np.log(np.abs(dft_shift)) # 可视化 plt.figure(figsize=(12, 6)) plt.subplot(2, 2, 1) plt.imshow(img, cmap='gray') plt.title('原始图像') plt.axis('off') plt.subplot(2, 2, 2) plt.imshow(img_shift, cmap='gray') plt.title('平移后图像') plt.axis('off') plt.subplot(2, 2, 3) plt.imshow(mag_original, cmap='gray') plt.title('原始图像DFT幅值谱') plt.axis('off') plt.subplot(2, 2, 4) plt.imshow(mag_shift, cmap='gray') plt.title('平移图像DFT幅值谱') plt.axis('off') plt.tight_layout() plt.show()效果说明:平移后图像的 DFT 幅值谱与原始一致(仅相位变化),验证平移性。
4.6.3 周期性
4.6.4 对称性
4.6.5 傅里叶谱与相位角
代码示例:幅值谱与相位谱可视化
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE) if img is None: img = np.zeros((256, 256), dtype=np.uint8) img[100:150, 100:150] = 255 # 计算DFT并移到中心 dft = np.fft.fft2(img) dft_shift = np.fft.fftshift(dft) # 幅值谱和相位谱 magnitude = 20 * np.log(np.abs(dft_shift)) phase = np.angle(dft_shift) # 可视化 plt.figure(figsize=(12, 6)) plt.subplot(1, 3, 1) plt.imshow(img, cmap='gray') plt.title('原始图像') plt.axis('off') plt.subplot(1, 3, 2) plt.imshow(magnitude, cmap='gray') plt.title('幅值谱') plt.axis('off') plt.subplot(1, 3, 3) plt.imshow(phase, cmap='gray') plt.title('相位谱') plt.axis('off') plt.tight_layout() plt.show()4.6.6 二维卷积定理
4.6.7 二维离散傅里叶变换性质总结
4.7 频域滤波基础
4.7.1 频域的其他特性
- 低频分量:对应图像的整体亮度、大结构;
- 高频分量:对应图像的边缘、细节、噪声;
- 滤波核心:修改频域分量(保留 / 增强 / 抑制),再逆变换回空间域。
4.7.2 频域滤波基本原理
4.7.3 频域滤波步骤总结
- 图像预处理:灰度化、补零(可选);
- 计算二维 DFT 并中心对齐;
- 设计频域滤波器(低通 / 高通 / 带通等);
- 频域相乘(滤波);
- 逆 DFT 转换回空间域;
- 后处理:归一化、取实部。
4.7.4 空间域滤波与频域滤波的对应关系
| 空间域操作 | 频域操作 |
|---|---|
| 平滑(均值 / 高斯滤波) | 低通滤波 |
| 锐化(拉普拉斯 / Sobel) | 高通滤波 |
| 边缘检测 | 高通滤波 |
| 噪声去除 | 低通滤波 |
4.8 基于频域滤波器的图像平滑
图像平滑的核心是抑制高频分量(噪声 / 细节),保留低频分量。
4.8.1 理想低通滤波器(ILPF)
4.8.2 巴特沃斯低通滤波器(BLPF)
4.8.3 高斯低通滤波器(GLPF)
4.8.4 低通滤波的扩展示例
完整代码:三种低通滤波器对比
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 1. 读取并预处理图像 img = cv2.imread('../picture/AALi.jpg', cv2.IMREAD_GRAYSCALE) if img is None: # 生成测试图像(添加噪声) img = np.zeros((256, 256), dtype=np.uint8) img[80:180, 80:180] = 255 # 添加高斯噪声 noise = np.random.normal(0, 30, img.shape).astype(np.float32) img = np.clip(img + noise, 0, 255).astype(np.uint8) M, N = img.shape # M=高度(行), N=宽度(列) print(f"图像形状:M={M}, N={N}") # 打印维度,方便排查 # 2. 计算DFT并中心对齐 dft = np.fft.fft2(img) dft_shift = np.fft.fftshift(dft) # 3. 生成频域网格(关键修正:先v后u,匹配图像行列) # 注意:meshgrid第一个参数对应列(v),第二个对应行(u) v = np.arange(N) # 列方向(宽度) u = np.arange(M) # 行方向(高度) u_grid, v_grid = np.meshgrid(u, v, indexing='ij') # indexing='ij' 确保(u,v)对应(行,列) # 中心坐标 u0, v0 = M // 2, N // 2 # 距离D(u,v):计算每个频域点到中心的欧式距离 D = np.sqrt((u_grid - u0) ** 2 + (v_grid - v0) ** 2) print(f"距离矩阵形状:{D.shape},DFT移位后形状:{dft_shift.shape}") # 验证维度匹配 # 4. 设计滤波器(截止频率D0=30) D0 = 30 n = 2 # 巴特沃斯阶数 # 理想低通 H_ilpf = np.where(D <= D0, 1, 0) # 巴特沃斯低通 H_blpf = 1 / (1 + (D / D0) ** (2 * n)) # 高斯低通 H_glpf = np.exp(-(D ** 2) / (2 * D0 ** 2)) # 5. 频域滤波(现在维度匹配,可正常相乘) g_shift_ilpf = dft_shift * H_ilpf g_shift_blpf = dft_shift * H_blpf g_shift_glpf = dft_shift * H_glpf # 6. 逆DFT(添加小技巧:取实部后归一化,避免数值误差) # 理想低通 g_ilpf = np.fft.ifftshift(g_shift_ilpf) g_ilpf = np.fft.ifft2(g_ilpf) g_ilpf = np.real(g_ilpf) # 优先取实部,避免虚部残留 g_ilpf = np.clip(g_ilpf, 0, 255).astype(np.uint8) # 归一化到0-255 # 巴特沃斯低通 g_blpf = np.fft.ifftshift(g_shift_blpf) g_blpf = np.fft.ifft2(g_blpf) g_blpf = np.real(g_blpf) g_blpf = np.clip(g_blpf, 0, 255).astype(np.uint8) # 高斯低通 g_glpf = np.fft.ifftshift(g_shift_glpf) g_glpf = np.fft.ifft2(g_glpf) g_glpf = np.real(g_glpf) g_glpf = np.clip(g_glpf, 0, 255).astype(np.uint8) # 7. 可视化(补充第8个子图,避免布局警告) plt.figure(figsize=(15, 10)) # 原始图像 plt.subplot(2, 4, 1) plt.imshow(img, cmap='gray') plt.title('原始图像(含噪声)') plt.axis('off') # 理想低通 plt.subplot(2, 4, 2) plt.imshow(g_ilpf, cmap='gray') plt.title('理想低通滤波(振铃效应明显)') plt.axis('off') # 巴特沃斯低通 plt.subplot(2, 4, 3) plt.imshow(g_blpf, cmap='gray') plt.title('巴特沃斯低通(轻微振铃)') plt.axis('off') # 高斯低通 plt.subplot(2, 4, 4) plt.imshow(g_glpf, cmap='gray') plt.title('高斯低通(无振铃)') plt.axis('off') # 滤波器可视化 plt.subplot(2, 4, 5) plt.imshow(H_ilpf, cmap='gray') plt.title('理想低通滤波器') plt.axis('off') plt.subplot(2, 4, 6) plt.imshow(H_blpf, cmap='gray') plt.title('巴特沃斯低通滤波器') plt.axis('off') plt.subplot(2, 4, 7) plt.imshow(H_glpf, cmap='gray') plt.title('高斯低通滤波器') plt.axis('off') # 补充第8个子图(避免布局警告) plt.subplot(2, 4, 8) plt.text(0.5, 0.5, '低通滤波对比', ha='center', va='center', fontsize=12) plt.axis('off') plt.tight_layout() plt.show()效果对比:
- 理想低通:噪声去除明显,但边缘出现振铃效应(吉布斯现象);
- 巴特沃斯低通:振铃效应减轻,平滑效果适中;
- 高斯低通:无振铃效应,平滑效果最自然。
4.9 基于频域滤波器的图像锐化
图像锐化的核心是增强高频分量(边缘 / 细节),抑制低频分量。
4.9.1 理想高通滤波器(IHPF)
4.9.2 巴特沃斯高通滤波器(BHPF)
4.9.3 高斯高通滤波器(GHPF)
4.9.4 频域中的拉普拉斯算子
4.9.5 非锐化掩模、高提升滤波与高频强调滤波
4.9.6 同态滤波
同时增强对比度和抑制噪声,核心是将图像分解为照度(低频)和反射(高频)分量:
完整代码:高通滤波 + 同态滤波对比
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 1. 读取图像 img = cv2.imread('../picture/TianHuoSanXuanBian.jpg', cv2.IMREAD_GRAYSCALE) if img is None: # 生成测试图像(低对比度) img = np.zeros((256, 256), dtype=np.uint8) img[80:180, 80:180] = 255 # 降低对比度 img = (img * 0.5).astype(np.uint8) M, N = img.shape # M=高度(行), N=宽度(列) print(f"图像形状:M={M}, N={N}") # 打印维度,方便排查 # 2. 计算DFT并中心对齐 dft = np.fft.fft2(img.astype(np.float32)) dft_shift = np.fft.fftshift(dft) # 3. 生成频域网格(关键修正:匹配图像行列维度) # 注意:先定义列(v)、后定义行(u),并使用indexing='ij'确保维度匹配 v = np.arange(N) # 列方向(宽度) u = np.arange(M) # 行方向(高度) u_grid, v_grid = np.meshgrid(u, v, indexing='ij') # 生成(M,N)形状的网格 # 中心坐标 u0, v0 = M // 2, N // 2 # 距离D(u,v):添加极小值eps避免除零 eps = 1e-8 # 防止D=0导致除零 D = np.sqrt((u_grid - u0) ** 2 + (v_grid - v0) ** 2) + eps print(f"距离矩阵形状:{D.shape},DFT移位后形状:{dft_shift.shape}") # 验证维度匹配 # 4. 设计高通滤波器(截止频率D0=30) D0 = 30 n = 2 # 巴特沃斯阶数 # 理想高通 H_ihpf = np.where(D <= D0, 0, 1) # 巴特沃斯高通(修正除零问题) H_bhpf = 1 / (1 + (D0 / D) ** (2 * n)) # 高斯高通 H_ghpf = 1 - np.exp(-(D ** 2) / (2 * D0 ** 2)) # 高频强调滤波(加入直流分量,提升亮度) H_hfe = 0.5 + 0.7 * H_ghpf # 5. 同态滤波(增强对比度) gamma_h = 2.0 # 高频增益(增强细节) gamma_l = 0.5 # 低频增益(抑制背景) c = 1 D1 = 30 H_homomorphic = (gamma_h - gamma_l) * (1 - np.exp(-c * (D ** 2) / (D1 ** 2))) + gamma_l # 6. 滤波计算(统一优化:取实部+归一化,避免虚部误差) # 理想高通 g_ihpf = np.fft.ifftshift(dft_shift * H_ihpf) g_ihpf = np.fft.ifft2(g_ihpf) g_ihpf = np.real(g_ihpf) # 优先取实部,避免虚部残留 g_ihpf = np.clip(g_ihpf, 0, 255).astype(np.uint8) # 巴特沃斯高通 g_bhpf = np.fft.ifftshift(dft_shift * H_bhpf) g_bhpf = np.fft.ifft2(g_bhpf) g_bhpf = np.real(g_bhpf) g_bhpf = np.clip(g_bhpf, 0, 255).astype(np.uint8) # 高频强调 g_hfe = np.fft.ifftshift(dft_shift * H_hfe) g_hfe = np.fft.ifft2(g_hfe) g_hfe = np.real(g_hfe) g_hfe = np.clip(g_hfe, 0, 255).astype(np.uint8) # 同态滤波(优化数值稳定性) img_log = np.log1p(img.astype(np.float32)) # log(1+x)避免0值 dft_log = np.fft.fft2(img_log) dft_log_shift = np.fft.fftshift(dft_log) g_log_shift = dft_log_shift * H_homomorphic g_log = np.fft.ifftshift(g_log_shift) g_log = np.fft.ifft2(g_log) g_homomorphic = np.expm1(np.real(g_log)) # exp(x)-1还原 g_homomorphic = np.clip(g_homomorphic, 0, 255).astype(np.uint8) # 7. 可视化(补充第6个子图,避免布局警告) plt.figure(figsize=(15, 10)) plt.subplot(2, 3, 1) plt.imshow(img, cmap='gray') plt.title('原始图像(低对比度)') plt.axis('off') plt.subplot(2, 3, 2) plt.imshow(g_ihpf, cmap='gray') plt.title('理想高通滤波(振铃+过暗)') plt.axis('off') plt.subplot(2, 3, 3) plt.imshow(g_bhpf, cmap='gray') plt.title('巴特沃斯高通滤波') plt.axis('off') plt.subplot(2, 3, 4) plt.imshow(g_hfe, cmap='gray') plt.title('高频强调滤波(亮度提升)') plt.axis('off') plt.subplot(2, 3, 5) plt.imshow(g_homomorphic, cmap='gray') plt.title('同态滤波(对比度增强)') plt.axis('off') # 补充第6个子图(显示滤波器示意图,提升实用性) plt.subplot(2, 3, 6) plt.imshow(H_hfe, cmap='gray') plt.title('高频强调滤波器') plt.axis('off') plt.tight_layout() plt.show()效果对比:
- 理想高通:边缘增强但有振铃,图像整体过暗;
- 巴特沃斯高通:边缘增强,振铃减轻;
- 高频强调:在增强边缘的同时提升整体亮度;
- 同态滤波:同时增强对比度和细节,效果最优。
4.10 选择性滤波
4.10.1 带阻滤波器与带通滤波器
- 带阻滤波器:抑制某一频段的分量(如去除特定频率的噪声);
- 带通滤波器:保留某一频段的分量(如提取特定频率的纹理)。
4.10.2 陷波滤波器
代码示例:陷波滤波器去除周期性噪声
import numpy as np import cv2 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 1. 读取/生成图像 img = cv2.imread('../picture/GaoDa.png', cv2.IMREAD_GRAYSCALE) if img is None: # 备用测试图像(确保维度是行×列) img = np.ones((256, 256), dtype=np.uint8) * 128 img[80:180, 80:180] = 255 # 核心:明确图像的行(M)和列(N) M, N = img.shape # M=高度(行), N=宽度(列) print(f"✅ 图像形状(行×列):{img.shape}") # 2. 生成与图像完全匹配的网格(行×列 = M×N) # 正确逻辑:先列(N)、后行(M),indexing='xy' → 最终网格形状M×N x = np.arange(N) # 列坐标(0~N-1) y = np.arange(M) # 行坐标(0~M-1) x_grid, y_grid = np.meshgrid(x, y, indexing='xy') # 结果:x_grid(M×N), y_grid(M×N) print(f"✅ 网格形状(行×列):x_grid={x_grid.shape}, y_grid={y_grid.shape}") # 3. 添加周期性噪声(维度100%匹配) freq = 8 # 噪声频率(越大条纹越密) noise_amp = 30 # 噪声幅值(越大条纹越明显) # 生成与图像同维度的噪声 noise = noise_amp * ( np.sin(2 * np.pi * freq * x_grid / N) + # 水平条纹(沿列方向) np.sin(2 * np.pi * freq * y_grid / M) # 垂直条纹(沿行方向) ) print(f"✅ 噪声形状(行×列):{noise.shape}") # 叠加噪声并归一化 img_noisy = np.clip(img + noise, 0, 255).astype(np.uint8) # 4. 计算DFT并中心化 dft = np.fft.fft2(img_noisy.astype(np.float32)) dft_shift = np.fft.fftshift(dft) # 幅值谱(避免log(0)) magnitude = 20 * np.log(np.abs(dft_shift) + 1e-8) # 5. 设计陷波滤波器(精准抑制噪声频率) # 频域中心 cy, cx = M // 2, N // 2 # 噪声频率对应的频域偏移(根据实际效果调整) offset = freq # 陷波中心(4个方向的噪声频率点) notch_centers = [ (cy, cx + offset), (cy, cx - offset), (cy + offset, cx), (cy - offset, cx) ] print(f"✅ 陷波中心坐标:{notch_centers}") # 巴特沃斯陷波滤波器参数 D0 = 15 # 陷波半径(大图像适当增大) n = 4 # 阶数 eps = 1e-8 # 防除零 # 初始化全通滤波器 H_notch = np.ones((M, N), dtype=np.float32) for (yc, xc) in notch_centers: # 计算每个点到陷波中心的距离 D = np.sqrt((y_grid - yc)**2 + (x_grid - xc)**2) + eps # 陷波阻带(抑制该中心周围的频率) H_notch *= 1 / (1 + (D0 / D)**(2*n)) # 6. 频域滤波 + 逆变换 g_shift = dft_shift * H_notch g = np.fft.ifftshift(g_shift) g = np.fft.ifft2(g) # 取实部并归一化(避免虚部残留) g_filtered = np.clip(np.real(g), 0, 255).astype(np.uint8) # 7. 可视化(清晰对比) plt.figure(figsize=(18, 10)) # 原始图像 plt.subplot(2, 3, 1) plt.imshow(img, cmap='gray') plt.title('1. 原始无噪声图像', fontsize=12) plt.axis('off') # 含噪声图像 plt.subplot(2, 3, 2) plt.imshow(img_noisy, cmap='gray') plt.title('2. 含周期性条纹噪声的图像', fontsize=12) plt.axis('off') # DFT幅值谱 plt.subplot(2, 3, 3) plt.imshow(magnitude, cmap='gray') plt.title('3. 噪声图像DFT幅值谱\n(亮点=噪声频率)', fontsize=12) plt.axis('off') # 陷波滤波器 plt.subplot(2, 3, 4) plt.imshow(H_notch, cmap='gray') plt.title('4. 陷波滤波器\n(黑色=噪声抑制区)', fontsize=12) plt.axis('off') # 滤波后图像 plt.subplot(2, 3, 5) plt.imshow(g_filtered, cmap='gray') plt.title('5. 陷波滤波后图像\n(噪声已去除)', fontsize=12) plt.axis('off') # 噪声残留 plt.subplot(2, 3, 6) residual = np.abs(img_noisy - g_filtered).astype(np.uint8) plt.imshow(residual, cmap='gray') plt.title('6. 噪声残留(越黑残留越少)', fontsize=12) plt.axis('off') plt.tight_layout() plt.show()4.11 实现方法
4.11.1 二维离散傅里叶变换的可分离性
4.11.2 利用离散傅里叶变换算法计算逆变换
4.11.3 快速傅里叶变换(FFT)
4.11.4 滤波器设计相关说明
- 滤波器需中心对齐(与 DFT 移位匹配);
- 截止频率需根据图像尺寸归一化;
- 补零扩展可减少滤波后的边缘效应;
- 实值图像滤波后需取实部,避免虚部误差。
小结
核心知识点总结
- 频域滤波核心:将图像从空间域转换到频率域,通过修改频率分量实现增强 / 去噪,再逆变换回空间域;
- 滤波器分类:
- 低通滤波(平滑):理想低通(振铃)、巴特沃斯低通(适中)、高斯低通(无振铃);
- 高通滤波(锐化):理想高通、巴特沃斯高通、高斯高通,高频强调滤波可提升亮度;
- 选择性滤波:陷波滤波器可去除周期性噪声;
- 关键实现技巧:
- 用
np.fft.fft2/np.fft.ifft2实现 DFT/IDFT; np.fft.fftshift将低频移到中心,便于滤波器设计;- 滤波后需取实部并归一化到 0-255。
- 用
实践建议
- 所有代码可直接运行,只需替换
lena.jpg为自己的图像路径; - 调整截止频率
D0、阶数n等参数,观察滤波效果变化; - 频域滤波的核心是理解 “低频对应整体、高频对应细节”,根据需求选择合适的滤波器。