资讯动态

频率域滤波实战:从傅里叶变换到图像处理

发布时间:2026/10/13 19:42:46 来源:尧图企业网站定制
1. 频率域滤波到底在干嘛先别急着写代码说实话很多人在数字图像处理这门课里学到频率域这块最容易卡住的地方不是公式本身而是搞不清楚“我们明明已经会了空间域卷积为什么还要绕一大圈去搞傅里叶变换”我第一次接触这个内容的时候也是这样矩阵乘法、卷积核、边缘检测这些东西刚上手突然又来一个“频域”整个人都是懵的。后来在某个图像处理Demo里踩了不少坑才慢慢把这块理顺。频率域滤波说白了就是把图像从“像素坐标”的世界变换到“频率坐标”的世界在那边对图像做“按频率打分”式的处理再变回来。它解决的问题本质上和空间域滤波是一样的——模糊、锐化、去噪、提取边缘但换了一个视角很多在空间域里难以直观理解或者计算量很大的操作在频率域里会变得非常直观甚至几行代码就能搞定。这篇笔记适合谁看正在学数字图像处理课程的同学、准备做图像预处理相关项目的开发者、以及所有被“傅里叶变换到底有什么用”这个问题折磨过的人。我不打算把教科书上的公式重新抄一遍而是想用我实际调试项目时的经验把频率域滤波的来龙去脉、操作细节、还有那些容易踩的坑讲清楚。2. 设计思路为什么非要去频率域里折腾2.1 空间域卷积和频率域乘法的“等价关系”先摆一个核心结论空间域的卷积等于频率域的点乘。这个结论听起来很数学但它其实特别像我们日常生活中的“换一种语言表达同一个意思”。举个例子你想表达“这个苹果很甜”中文说一遍英文说一遍意思一样但不同场合下可能用某一种语言更方便。空间域滤波是在“像素值语言”里做加权平均而频率域滤波是在“频率分量语言”里做缩放。前者需要对整幅图像每个像素做一遍窗口运算后者只需要对整幅图像的频谱做一次逐点乘法然后再变换回来。用数学一点的话说如果图像是f(x, y)滤波器是h(x, y)空间域卷积是f * h频率域里就是它们的傅里叶变换相乘F(u, v) * H(u, v)。这个等价关系的意义在于可以把设计滤波器的思路从“我要用什么样的核去遍历图像”变成“我要让哪些频率通过哪些频率衰减”。2.2 频率域滤波的三板斧流程实际做频率域滤波的时候所有操作都围绕着下面这个固定套路展开我把叫做“三板斧”对输入图像做傅里叶变换得到频谱。在频谱上乘以一个滤波器通常是掩膜矩阵值域在0到1之间。对结果做逆傅里叶变换得到滤波后的图像。就这三步。所有花样无论是低通、高通、带通还是带阻变的都只是第二步里的那个掩膜长什么样。这里有个很容易被忽略的细节傅里叶变换后的频谱直流分量对应图像平均亮度通常在左上角而不是中心。如果不做中心化处理你设计的滤波器就算形状对了位置也会完全错位出来的结果一团糟。所以经典的操作是在变换前先把图像矩阵乘以 (-1)^(xy)让低频分量移到中心。当然很多库函数里直接提供了中心化的选项但理解这个步骤的原理比单纯调用API要重要得多。2.3 为什么说是“频率域”而不是“频域”可能有人会较真这个叫法。严格来说“频率域”和“频域”指的是同一个概念但中文教材里用“频率域滤波”这个词更常见因为它强调了对“频率成分”做处理这个动作。频率这个东西在图像里代表的是像素值变化的快慢程度低频对应平坦区域比如天空、墙面高频对应边缘、纹理、噪声这类像素值剧烈变化的地方。想通这一点很多滤波行为就很好解释了。低通滤波就是让图像变模糊因为把高频边缘信息削掉了高通滤波就是提取边缘因为把平坦区域低频压掉了带通滤波就是保留某一尺度下的纹理细节常用于指纹增强或者特定工业检测场景。2.4 一个关键的思想转变刚学空间域滤波的时候我们的经验法则是“核越大模糊越厉害”“核是拉普拉斯算子的离散近似就能锐化”。这些经验当然没错但没有回答一个根本问题为什么这个核能达到这种效果频率域给了答案。把任何一个卷积核做傅里叶变换观察它的频谱形状你立刻能看出它到底是一个低通还是高通滤波器。比如均值核的频谱在中心是高峰向外逐渐衰减所以它是低通拉普拉斯核的频谱在中心接近零向外围升高所以它是高通。这种视角一换你对滤波器的理解会一下子上升一个层次而且设计新滤波器的时候不再靠暴力试错而是直接在频率域里画一个想要的形状就行。3. 关键细节与实操要点滤波器怎么造、怎么用3.1 理想低通滤波器最直觉但最不实用的选择所谓理想低通滤波器ILPF就是在频率域里画一个圆圆心也就是中心化后的中心点周围半径D0以内的频率全部保留以外的全部砍掉。听起来很干净利落实际效果却很糟糕因为这种硬截断会在图像边缘产生明显的振铃现象就是那种一圈一圈的明暗波纹。为什么会有振铃因为频率域里的矩形截断对应空间域里的sinc函数卷积。而sinc函数有正有负的旁瓣跟图像卷积之后就会在边缘附近形成过冲和震荡。这个知识点在课本上就一句话但真自己跑一遍理想低通看到输出图像边缘出现诡异的波纹时才会真正理解什么叫做“理想的数学概念在现实中往往不理想”。实操建议是理想低通适合用来演示频率域的基本原理但不建议用在真实项目里。3.2 高斯低通滤波器平滑过渡才是王道高斯低通滤波器GLPF的传递函数长这样H(u, v) e^(-D²(u, v) / (2σ²))其中D(u, v)是点(u, v)到频谱中心的距离σ控制截止频率。它的特点是频率响应没有硬跳变而是平滑地衰减到0所以空间域里也对应高斯形的核不会产生振铃。实际项目中我绝大部分模糊需求都用它效果好、参数含义直观、几乎没有副作用。选择σ的时候我一般会先看图像尺寸。假设图像是M×N频谱中心到边界的最大距离大概是图像对角线的一半把σ设成这个距离的10%~20%通常是温和模糊5%以下是强模糊。比如512×512的图像频谱中心位置在(256, 256)边界距离大概256那么σ25~50是轻度模糊σ13左右就是比较激进的模糊。3.3 高通滤波器从低通转个身就来了高通滤波器和低通滤波器就是互补关系。最简单的高通就是1减去低通H_HP 1 - H_LP。实际做边缘提取的时候我通常用高斯高通因为它的响应平滑不会引入振铃。但有一个细节很多人会忽略直接用高通滤波后的图像往往亮度很低因为去掉了低频分量平均亮度没了图像看起来很暗甚至接近黑色。解决办法是在高通滤波器上加一个偏移量让一部分低频保留也就是所谓的“高频提升滤波”High-boost。公式是H_hb a b * H_HP其中a决定保留多少低频分量b决定高频放大的倍数。当a略小于1时图像动态范围得到压缩同时细节增强。这个技巧在医学图像增强、遥感图像细节强化里非常常见。3.4 拉普拉斯算子与高通滤波的关系空间域里的拉普拉斯算子我们可以直接当卷积核用但它也是一个不折不扣的高通滤波器。理论上拉普拉斯算子的频率响应是-4π²(u² v²)意味着离中心越远响应越大而且是平方级增长。这就导致拉普拉斯算子对噪声特别敏感因为噪声往往是高频中的高频。实操里如果你发现拉普拉斯锐化后的图像布满噪点不要急着调阈值先考虑在频率域里把最高频段稍微压一压比如做一个低通偏移这样既能保持边缘增强的效果又能抑制噪声放大。这种“高通低通的组合拳”在频率域里实现起来非常轻松但如果你一直死磕空间域卷积设计就得多绕很多弯路。3.5 滤波器构造时的坐标计算陷阱构造一个频率域滤波器掩膜时最基础的步骤就是计算每个像素点到中心的距离矩阵。很多人第一次写代码容易掉进一个坑直接用了原始像素坐标去算距离没有考虑中心化后的坐标偏移。正确做法是先得到图像尺寸M×N然后生成网格坐标import numpy as np M, N image.shape u np.arange(M) - M // 2 v np.arange(N) - N // 2 V, U np.meshgrid(v, u) D np.sqrt(U**2 V**2)这段代码生成的距离矩阵D中心点(M//2, N//2)处的值为0周围点的距离是相对于中心的欧氏距离。注意meshgrid的参数顺序我用的是np.meshgrid(v, u)而不是(u, v)因为图像数组的第一维是行方向对应u第二维是列方向对应v生成网格时两个维度也要对应好。顺序搞反了滤波器就会旋转90度输出结果完全不对。如果图像尺寸是偶数比如512×512那么中心点应该用(M//2, N//2)附近的四个点来定义严格来说真正的离散中心是(M/2, N/2)但因为像素是离散的我们实际操作时一般取M//2或(M-1)//2都行对最终结果影响很小。真正影响大的是你忘了中心化或者坐标换算了两次导致相位翻转。3.6 频谱中心化的两种实现前面提到中心化可以用(-1)^(xy)乘以图像实现或者更简单一点直接调用np.fft.fftshift把变换后的频谱搬移到中心。但我建议你在学习阶段两种方法都亲手实现一遍这样你对“频谱中心”这个概念会有更深刻的体感。# 方法一输入时乘(-1)^(xy) shifted_img image * ((-1) ** (np.indices(image.shape).sum(axis0))) F np.fft.fft2(shifted_img) # 方法二变换后fftshift F np.fft.fft2(image) F_shifted np.fft.fftshift(F)两种方法得到的结果在幅度谱上几乎一致但细微差别在于方法一处理后的结果在做逆变换时不需要再ifftshift因为(-1)^(xy)的平方等于1再乘一次就还原了。方法二则需要在逆变换前用np.fft.ifftshift把频谱移回去。我自己更喜欢方法二因为代码意图更清晰而且少一次对整个图像的逐元素乘法。注意np.fft.fft2默认沿最后两个轴变换如果你的图像是三维数组比如H×W×C记得先处理成灰度图或者对每个通道分别变换。别偷懒直接把三维数组丢进去在灰度图像上测试没问题但彩色图像上你会得到一堆奇怪的结果。4. 实操过程从灰度图到完整滤波流水线4.1 前置准备读图、灰度化、数据类型转换频率域滤波对输入数据格式比较敏感。用图像库读进来的图片通常是8位无符号整数0~255直接做傅里叶变换没问题但在构造滤波器、做乘法的时候容易因为溢出现象导致结果异常。我习惯先把图像转成浮点型并把像素值归一化到0~1之间这样频谱数值范围更可控逆变换后反归一化也方便。import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像并转为灰度 image cv2.imread(sample.jpg, cv2.IMREAD_GRAYSCALE) if image is None: raise ValueError(图片读取失败检查路径) # 转为float并归一化 image_f image.astype(np.float64) / 255.0有人会问为什么要归一化因为频率域滤波时滤波器的系数通常是0~1之间的小数如果图像是0~255的大数值乘以0.5后还是120左右表面看没问题但后续做逆变换、显示、对比时都容易混淆。统一到0~1各环节心智负担最小。4.2 第一步傅里叶变换与频谱可视化傅里叶变换这一步直接用np.fft.fft2就行。但注意变换结果是复数数组不能用imshow直接显示。可视化幅度谱时要取模再取对数否则动态范围太大肉眼只能看到中心一个亮点周围全是黑的。F np.fft.fft2(image_f) F_shifted np.fft.fftshift(F) # 幅度谱对数变换 magnitude np.log(np.abs(F_shifted) 1) plt.figure(figsize(12, 5)) plt.subplot(1, 2, 1) plt.imshow(image_f, cmapgray) plt.title(原图) plt.axis(off) plt.subplot(1, 2, 2) plt.imshow(magnitude, cmapgray) plt.title(频谱幅度对数显示) plt.axis(off) plt.show()1是为了避免log(0)出现负无穷对显示效果很有帮助。这是实操中几乎必备的处理教科书里很少强调但不加的话频谱图永远是一坨黑。4.3 第二步构造高斯低通滤波器并滤波构造高斯低通滤波器核心就是生成距离矩阵D然后代入高斯公式。下面这个函数我写了很多遍以后简化成了一个通用版本def gauss_lowpass(shape, sigma): M, N shape u np.arange(M) - M // 2 v np.arange(N) - N // 2 V, U np.meshgrid(v, u) D np.sqrt(U**2 V**2) return np.exp(-(D**2) / (2 * sigma**2))然后把它用在频域乘法上H gauss_lowpass(image_f.shape, sigma30) G_shifted F_shifted * H g np.real(np.fft.ifft2(np.fft.ifftshift(G_shifted)))这里g应该是一个接近0~1范围的实数数组但因为我们用了复数运算逆变换后可能会有很小的虚部残留浮点误差所以直接取实部即可。如果最后发现图像像素范围不在0~1内可以做一次np.clip(g, 0, 1)再显示避免异常值导致可视化发灰。4.4 第三步高通滤波和边缘提取高通滤波只要把低通函数取反就行。但如前面所说直接用1-H会丢掉全部直流分量输出图像几乎全黑。实际展示时我习惯做一个对比实验一组是纯高通一组是高频提升。H_hp 1 - gauss_lowpass(image_f.shape, sigma20) G_hp_shifted F_shifted * H_hp g_hp np.real(np.fft.ifft2(np.fft.ifftshift(G_hp_shifted))) # 高频提升 a 0.8 b 0.3 H_hb a b * H_hp G_hb_shifted F_shifted * H_hb g_hb np.real(np.fft.ifft2(np.fft.ifftshift(G_hb_shifted)))参数a0.8, b0.3是我在某次试验里调出来比较顺手的组合低频保留80%高频额外放大30%图像整体亮度变化不明显但边缘明显更清晰。大家可以根据图像特点调整如果原图本来就亮适当降低a如果细节弱增加b。4.5 第四步带通滤波器实现纹理增强带通滤波器的实现比想象中简单得多就是两个低通之差H_BP(u, v) H_LP1(u, v) - H_LP2(u, v)其中σ1 σ2。这样距离中心介于两个σ之间的频率被保留低频和高频都被压制。我在处理纹理增强类项目时经常用这个滤波器提取特定尺度的结构信息再叠加回原图效果类似空间域里的“非锐化掩膜”但频率域版本的可控性更强。H_bp gauss_lowpass(image_f.shape, sigma50) - gauss_lowpass(image_f.shape, sigma10) G_bp_shifted F_shifted * H_bp g_bp np.real(np.fft.ifft2(np.fft.ifftshift(G_bp_shifted)))注意两个低通相减后可能出现负值逆变换后的图像也会有负值这很正常。显示的时候我建议先做一次线性拉伸到0~1范围否则直接imshow会把负值当作黑色细节看不出来。4.6 一个完整的流水线示例把上面的内容整合成一个完整的处理流水线方便直接修改使用def freq_filter(image, filter_func): 通用的频率域滤波函数 image: 灰度图float类型值域0~1 filter_func: 接收shape返回与图像同尺寸的滤波器掩膜 F np.fft.fft2(image) F_shifted np.fft.fftshift(F) H filter_func(image.shape) G_shifted F_shifted * H G np.fft.ifftshift(G_shifted) return np.real(np.fft.ifft2(G)) # 使用例高斯低通滤波 result freq_filter(image_f, lambda shape: gauss_lowpass(shape, sigma40))用函数式封装的好处是后续想换任何滤波器只需要传入不同的掩膜生成函数不用把傅里叶变换代码复制一遍。这对于快速做实验对比特别方便。4.7 彩色图像怎么办频率域滤波完全可以用在彩色图像上但要注意通道处理。由于人眼对颜色感知有冗余简单地对每个通道分别做相同参数的滤波处理出来的颜色会偏暗、饱和度下降。更稳妥的做法是转换到亮度-色度分离的颜色空间比如YUV或Lab只对亮度通道做频率域滤波色度通道不动或者只做轻微处理然后再拼回去。# 伪代码YUV空间下的频域滤波 img_yuv cv2.cvtColor(img_bgr, cv2.COLOR_BGR2YUV) y, u, v cv2.split(img_yuv) y_f y.astype(np.float64) / 255.0 y_filtered freq_filter(y_f, lambda s: gauss_lowpass(s, sigma30)) y_out np.clip(y_filtered * 255, 0, 255).astype(np.uint8) img_out cv2.merge([y_out, u, v]) img_bgr_out cv2.cvtColor(img_out, cv2.COLOR_YUV2BGR)这样处理后的彩色图像边缘和细节增强会比较自然不会出现明显的色彩条纹。这个经验是我自己试出来的如果你发现某个通道滤波后颜色怪怪的八成是因为没有分隔色度通道。5. 常见问题与排查技巧实录5.1 逆变换后图像出现网格状伪影怎么回事如果你在逆变换后的图像上看到规律的细条纹或者格子大概率是频谱中心化没有处理好。特别是用fftshift做了正向中心化但逆变换前忘了ifftshift乘法结果的坐标就错位了导致频谱被旋转复制图像上表现为周期性的伪影。排查方法很简单在逆变换前检查一下频谱中心是不是在原点左上角如果不是就ifftshift一下。我一般会在调试代码里打印np.abs(G_shifted[0, 0])和np.sum(G_shifted)的值如果中心值异常小多半就是中心化问题。5.2 低通滤波后图像变暗了低通滤波本身只是滤除高频不会改变直流分量平均亮度按理说图像整体亮度应该基本保持不变。如果你发现图像明显变暗先检查滤波器的中心值是不是不为1。比如构造的掩膜是exp(-D^2/(2σ^2))中心处D0exp(0)1没问题。但如果你用了某些归一化操作不小心把中心值缩小了就会削弱直流分量图像自然变暗。同样高通滤波后图像变暗是正常的因为直流分量被滤掉了。如果你既想要高通效果又不想图像变暗就用高频提升把a调成0.95甚至1.0影像亮度基本不动细节照样增强。5.3 输出图像有负值直接显示一片噪点逆傅里叶变换的结果理论上应该是实数但由于浮点误差或者滤波器叠加了负值比如带通滤波输出数组里大概率会出现负值。直接用plt.imshow显示时负值会被裁成黑色图像看起来像黑白噪点。正确的处理是先归一化g_norm (g - g.min()) / (g.max() - g.min()) plt.imshow(g_norm, cmapgray)归一化之后再显示或者保存时用cv2.normalize拉伸到0~255。要记住你最终需要的是视觉效果不是中间结果的数学精确值。5.4 频谱图为什么黑乎乎一片频谱幅度谱的动态范围实在太大最大值可能是几万但平均值只有几十。如果直接线性显示整个图只有一个亮点。用对数变换log(1 abs(F))可以把窄区间放大、宽区间压缩让频谱细节显现出来。如果你发现对数变换后依然很黑多半是图像尺寸过大或者频率成分太集中在中心可以考虑对频谱做一次直方图均衡化再显示。5.5 振铃效应怎么避免振铃是理想边界型滤波器理想低通、理想高通的典型问题。在频率域里表现为陡峭的截止边对应空间域里振荡的sinc核。解决办法是使用平滑过渡的滤波器比如高斯型、巴特沃斯型但巴特沃斯的阶数不能太高否则也接近理想。实操中如果必须用带硬边界的滤波器可以把截止半径扩大一些同时增加过渡带宽度能显著减轻振铃。下面整理一个常见问题速查表方便你排查时对照现象可能原因解决办法输出图像有网格/条纹频谱中心化错位检查ifftshift是否使用低通后图像变暗滤波器中心值小于1检查掩膜构造直流分量应保留高通后图像全黑直流分量被完全滤除使用高频提升增加低频偏移量输出有负像素值带通/高通环节正常现象归一化后再显示边缘出现振铃滤波器截止过于陡峭换高斯/低阶巴特沃斯频谱图不可见动态范围太大使用对数变换显示彩色图像颜色失真直接对RGB三通道操作转换到亮度-色度空间只处理亮度结果有微弱虚部浮点计算误差逆变换后取实部5.6 关于参数选择的经验心得频率域滤波里最常调的就是σ。很多人问我有没有什么通用规律我只能说没有万能参数但有一个稳妥的调试路径先用较大的σ看全局效果再逐步减小每一次都观察输出图像有没有出现细节丢失或者噪声放大。同时记录σ和输出图像的均方误差或者拉普拉斯能量这几个指标能帮你科学选参而不是靠眼睛一遍遍人肉搜索。另一个习惯是把滤波器的频谱形状和图像频谱一起画出来看一眼二者重叠区域是否合理。如果滤波器覆盖了噪声集中的高频区域说明参数太激进了。这种可视化验证比不断猜参数高效得多。6. 延伸思考频率域滤波还能做什么频率域滤波不只是课本里的“低通高通”两个案例。我在实际项目里用过很多衍生玩法这里列几个我觉得特别有价值的6.1 图像去模糊与逆滤波如果已知图像退化过程的点扩散函数PSF理论上可以用逆滤波来恢复原图。在频率域里退化模型可以写成G(u, v) F(u, v) * H(u, v)那么恢复就是F G / H。但直接相除有个致命问题当H在某些频率上接近0时噪声会被无限放大。这时候就需要用维纳滤波最小均方误差滤波在信噪比和恢复清晰度之间做平衡。虽然这个话题可以单独写一篇长文但它的基础还是频率域滤波的基本流程——正向变换、乘滤波器、逆变换。6.2 周期性噪声的陷波滤波器现实中很多图像噪声不是随机的高斯噪声而是周期性的比如扫描图像的条纹、传感器串扰产生的规律纹理。周期性噪声在频谱上会表现为一对对称的亮点这时用陷波滤波器Notch Filter直接把这些亮点抑制掉效果立竿见影。这比任何空间域去噪都精确因为你是在“噪声的窝点”里定点清除。构造陷波滤波器的方式就是在距离矩阵上找出以这两个亮点为中心的圆形或方形区域把区域内的值设为0其余为1。做的时候注意亮点位置要从频谱幅度谱上找而不是在原始图上瞎猜。6.3 同态滤波压缩动态范围并增强对比度同态滤波是一种特殊的频率域滤波它先对图像取对数然后做傅里叶变换滤波后再取指数还原。它的理论基础是图像的亮度可以建模成照度分量和反射分量的乘积取对数后变成加法而照度分量主要落在低频反射分量主要落在高频。通过频率域滤波把照度分量压缩、反射分量增强就能同时改善光照不均和对比度不足的问题。这个方法在处理背光照片、夜间图像的时候非常实用。我试过把一张亮部过曝、暗部死黑的照片用同态滤波处理后细节层次明显提升。虽然现在有很多基于深度学习的图像增强方法但同态滤波不需要训练数据而且计算开销小在一些实时性要求高的场景下依然是优选方案。6.4 与其他图像处理环节的组合使用频率域滤波通常不是单独作为最终处理手段而是作为预处理或中间环节。比如在某个跨平台系统的图像配准模块里我用了频率域高通滤波来增强特征点周围的纹理结构显著提升了特征匹配的稳定性。在工业质检场景里用带通滤波提取特定缺陷尺度然后再做形态学分析比直接在原图上处理抗干扰性强很多。这些组合使用的核心逻辑是频率域滤波擅长的是“按频率选择性调整”而你真正要解决的问题往往是“把感兴趣的信息和干扰信息分离开”。想清楚这一点你就会发现频率域滤波其实是一座桥梁——把图像从像素连接到了语义。最后再分享一点个人体会第一次完整跑通频率域滤波流水线的时候我盯着输出的高斯低通模糊图像看了很久。当时心里想的不是“哇成功了”而是“原来空间域里那么大一个卷积核在这里就是一个简单的高斯掩膜”。那种两个世界观被打通的感觉比跑通代码本身更爽。后来在几次项目里被图像噪声、边缘模糊逼到焦头烂额我都会回到频率域来想问题先把图像变成频谱看一看通常三五分钟就能定位问题区域然后再设计对应的滤波器。这种工作方式比盲目调空间域参数要高效得多也让我真正理解了为什么教材要把频率域单独拎出来讲一整章。如果你正在被这块内容折磨我的建议是别停在理论上打开你的图像处理环境生成一张棋盘格图片、一张带周期性噪声的图片、一张光照不均的图片挨个跑一遍本文里的代码亲手看看频谱长什么样、滤波器怎么影响输出。跑完几个案例之后你回头看那些公式会发现它们变得亲切多了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑