在前几天的分享中,我们从图像平滑、边缘检测到轮廓处理,始终在「空间域」操作——直接对图像的像素点进行处理,聚焦“像素在哪里、灰度值是多少”。而今天要讲的傅里叶变换,将带我们进入「频域」视角,从“频率”维度分析图像,解锁图像处理的新思路。
傅里叶变换是信号处理、图像处理的核心数学工具,1822年法国工程师傅里叶提出:任意周期函数都可以分解为无穷多个不同频率的正弦和/或余弦的和,这一理论延伸到图像处理中,便可以将空间域图像的灰度分布函数,转换为频域的频率分布函数。它的核心作用的是“将图像的灰度变化,分解为不同频率的信号”,比如图像的平滑、降噪、边缘增强,都可以在频域高效完成,且效果往往优于空间域处理。
很多新手觉得傅里叶变换“晦涩难懂”,其实无需死记复杂公式,抓住核心逻辑即可。今天这篇实操教程,全程聚焦OpenCV-Python落地,从傅里叶变换的核心原理(通俗解读)、二维离散傅里叶变换、频谱图解读,到频域滤波(低通/高通)实战,附完整可复制代码、效果对比和避坑技巧,新手也能一键跑通,衔接此前空间域处理内容,形成“空间域+频域”完整的图像处理知识体系。
一、前置基础(新手必看,衔接前文)
1. 核心前提:环境配置
所有操作均基于OpenCV-Python,若未配置环境,执行以下命令一键安装/升级,适配Python 3.7-3.12,Windows/Mac/Linux全兼容(与前几篇保持一致,降低新手学习成本):
python# 安装/升级OpenCV-Python(核心依赖)pip install opencv-python -U# 安装辅助库(图像显示、傅里叶变换计算)pip install numpy matplotlib |
2. 核心概念:空间域 vs 频域(通俗解读)
新手最容易混淆的就是“空间域”和“频域”,用两句话讲清,无需复杂公式:
•空间域:我们平时看到的图像,聚焦“像素的位置和灰度值”,比如某个像素在(100,200)位置,灰度值为128,是“直观可见”的视角,前几篇的平滑、边缘检测都在这个域操作;
•频域:聚焦“图像灰度变化的频率”,灰度变化越剧烈(比如边缘、噪声),频率越高;灰度变化越平缓(比如图像背景),频率越低,是“抽象的频率分布”视角。
傅里叶变换的核心作用:将空间域图像→ 频域图像,在频域对不同频率的信号进行处理(比如过滤高频噪声),再通过傅里叶逆变换,将频域图像转换回空间域,得到处理后的图像,本质是“换个角度做图像处理”。
举个通俗例子:一张有噪声的图像,空间域处理是“逐个像素去噪”,而频域处理是“直接过滤掉高频的噪声信号”,效率更高、效果更均匀,这也是傅里叶变换的核心优势。
3. 测试图像准备(统一对比,保持连贯)
为了让傅里叶变换的效果更直观,同时与前几篇文章保持连贯,我们统一使用“带高斯噪声的灰度图像”(与边缘检测、轮廓处理篇测试图一致),代码如下,可直接复用:
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 1. 读取图像(灰度图)img = cv2.imread("test.jpg", 0) # 0表示灰度图,替换为自己的图像路径# 2. 添加高斯噪声(模拟真实拍摄场景,用于后续频域降噪测试)def add_gaussian_noise(img, mean=0, var=0.001):img = np.array(img/255, dtype=np.float32)noise = np.random.normal(mean, var**0.5, img.shape)noise_img = img + noisenoise_img = np.clip(noise_img, 0, 1)noise_img = np.uint8(noise_img*255)return noise_img# 生成带噪灰度图img_noise = add_gaussian_noise(img)# 显示原始图与带噪图plt.subplot(1, 2, 1)plt.imshow(img, cmap="gray")plt.title("原始灰度图像")plt.axis("off")plt.subplot(1, 2, 2)plt.imshow(img_noise, cmap="gray")plt.title("带高斯噪声的灰度图像")plt.axis("off")plt.show() |
二、核心原理:傅里叶变换(通俗解读,拒绝公式堆砌)
傅里叶变换的数学公式复杂(比如二维离散傅里叶变换的正变换和逆变换公式),但新手无需掌握公式,只需理解3个核心逻辑,就能轻松上手实操,结合OpenCV封装好的函数,无需手动推导计算。
1. 核心逻辑1:图像 = 不同频率的信号叠加
任何一张图像,都可以拆解成无数个“不同频率的正弦/余弦波”叠加而成——低频波对应图像的“背景、整体轮廓”(灰度变化平缓),高频波对应图像的“边缘、噪声”(灰度变化剧烈),这是傅里叶变换的核心理论基础,也是傅里叶级数的延伸应用。
2. 核心逻辑2:傅里叶变换的两步操作
•正变换(DFT):将空间域图像转换为频域图像,得到“频率分布”(即频谱),能清晰看到图像中不同频率的信号分布;
•逆变换(IDFT):将处理后的频域图像转换回空间域图像,得到最终的处理效果(比如降噪、增强后的图像)。
简单说:正变换“拆信号”,逆变换“拼信号”,中间的频域处理“筛选信号”(留有用的、删无用的)。
3. 核心逻辑3:频谱图的解读(关键!)
傅里叶正变换后得到的“频域图像”,我们通常用“频谱图”来直观展示,频谱图的解读是傅里叶变换实操的核心,记住2个关键点即可:
•频谱图的亮度:代表对应频率的信号强度,亮度越高,该频率的信号越强;
•频谱图的位置:默认情况下,低频信号集中在频谱图的4个角落,高频信号集中在中间;通过“频谱平移”(将低频信号移到中心),能更清晰地观察频率分布——中心亮斑是低频信号(背景),周围的亮斑是高频信号(边缘/噪声)。
三、OpenCV实战:傅里叶变换全流程(代码+效果+解读)
OpenCV和NumPy都封装了傅里叶变换的核心函数,无需手动实现复杂计算,我们重点掌握“正变换→频谱处理→逆变换”的全流程,以及频谱平移、频谱图显示的关键操作,实操性拉满。
1. 核心函数(必记)
实操中常用4个核心函数,结合NumPy和OpenCV,效率更高,参数简单易记:
•NumPy正变换:np.fft.fft2(img),将灰度图像转换为频域复数数组(核心正变换);
•频谱平移:np.fft.fftshift(fft_result),将低频信号移到频谱图中心,方便观察和处理;
•频谱图显示:通过20*np.log(np.abs(fft_shift))转换,将复数频谱转换为可显示的灰度值(解决频谱亮度差异过大的问题);
•逆变换:np.fft.ifft2(ifft_shift),将处理后的频域图像转换回空间域,得到最终效果;
•OpenCV正变换:cv2.dft(),与NumPy功能一致,可搭配cv2.idft()实现逆变换,适配OpenCV图像格式处理。
2. 基础实操:傅里叶正变换+频谱图显示
先实现“空间域图像→频域频谱图”的转换,掌握频谱平移和显示的关键步骤,代码可直接复制运行:
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 读取带噪灰度图(沿用前面的测试图)img_noise = add_gaussian_noise(cv2.imread("test.jpg", 0))# 1. 傅里叶正变换(NumPy实现,简单高效)fft_result = np.fft.fft2(img_noise) # 得到频域复数数组# 2. 频谱平移:将低频信号移到中心fft_shift = np.fft.fftshift(fft_result)# 3. 转换为可显示的频谱图(灰度值)spectrum = 20 * np.log(np.abs(fft_shift)) # 20*log缩放,避免亮度差异过大# 显示效果(原始带噪图 vs 频谱图)plt.subplot(1, 2, 1)plt.imshow(img_noise, cmap="gray")plt.title("带噪灰度图像(空间域)")plt.axis("off")plt.subplot(1, 2, 2)plt.imshow(spectrum, cmap="gray")plt.title("傅里叶频谱图(频域,低频在中心)")plt.axis("off")plt.show() |
关键说明:频谱图中,中心的亮斑是低频信号(图像背景),周围的亮斑是高频信号(噪声和边缘),噪声对应的高频信号比较分散,这也是我们后续频域降噪的核心目标——过滤掉这些分散的高频噪声信号。
3. 核心实战:频域滤波(低通+高通,最常用场景)
傅里叶变换的核心应用的是“频域滤波”——通过在频域“保留有用频率、过滤无用频率”,实现图像降噪、边缘增强等效果,最常用的两种滤波方式:低通滤波、高通滤波。
(1)低通滤波(LPF):降噪神器
核心逻辑:保留低频信号(背景、整体轮廓),过滤高频信号(噪声、细边缘),实现图像降噪,效果比空间域的高斯平滑更均匀、更自然,尤其适合处理复杂噪声场景。
实操思路:在频谱图中,用“掩码”挡住高频区域(中心以外的区域),只保留中心的低频区域,再进行逆变换,得到降噪后的图像。
代码实现:
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 读取带噪灰度图,执行正变换和频谱平移(沿用前面的代码)img = cv2.imread("test.jpg", 0)img_noise = add_gaussian_noise(img)fft_result = np.fft.fft2(img_noise)fft_shift = np.fft.fftshift(fft_result)# 1. 创建低通滤波掩码(保留低频,过滤高频)rows, cols = img_noise.shapecrow, ccol = rows // 2, cols // 2 # 频谱中心坐标mask = np.zeros((rows, cols), np.uint8)radius = 30 # 低通半径(越大,保留的低频越多,降噪越明显,可调整)mask[crow-radius:crow+radius, ccol-radius:ccol+radius] = 1 # 中心区域保留(低频)# 2. 应用掩码:频域处理(过滤高频)fft_filtered = fft_shift * mask# 3. 频谱逆平移:将低频移回原始位置ifft_shift = np.fft.ifftshift(fft_filtered)# 4. 傅里叶逆变换:转换回空间域img_filtered = np.fft.ifft2(ifft_shift)# 5. 转换为可显示的灰度值(逆变换结果是复数,取绝对值)img_filtered = np.abs(img_filtered)img_filtered = np.uint8(img_filtered) # 转换为8位灰度图# 对比显示(原始图 vs 带噪图 vs 低通滤波降噪图)plt.subplot(1, 3, 1)plt.imshow(img, cmap="gray")plt.title("原始灰度图像")plt.axis("off")plt.subplot(1, 3, 2)plt.imshow(img_noise, cmap="gray")plt.title("带高斯噪声图像")plt.axis("off")plt.subplot(1, 3, 3)plt.imshow(img_filtered, cmap="gray")plt.title("低通滤波降噪图(频域)")plt.axis("off")plt.show() |
调优技巧:低通半径radius越大,保留的低频信号越多,降噪效果越明显,但图像会越模糊;radius越小,降噪效果越弱,但能保留更多细节,建议取值20-50(根据图像大小调整)。
(2)高通滤波(HPF):边缘增强神器
核心逻辑:保留高频信号(边缘、细节),过滤低频信号(背景),实现图像边缘增强,让轮廓更清晰,可衔接此前的边缘检测、轮廓处理内容,提升边缘提取效果。
实操思路:与低通滤波相反,用掩码挡住中心的低频区域,保留周围的高频区域,再进行逆变换,得到边缘增强后的图像。
代码实现(复用正变换结果,修改掩码即可):
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 读取原始灰度图,执行正变换和频谱平移img = cv2.imread("test.jpg", 0)fft_result = np.fft.fft2(img)fft_shift = np.fft.fftshift(fft_result)# 1. 创建高通滤波掩码(保留高频,过滤低频)rows, cols = img.shapecrow, ccol = rows // 2, cols // 2mask = np.ones((rows, cols), np.uint8)radius = 30 # 高通半径(越大,过滤的低频越多,边缘增强越明显)mask[crow-radius:crow+radius, ccol-radius:ccol+radius] = 0 # 中心区域过滤(低频)# 2. 应用掩码:频域处理(保留高频)fft_filtered = fft_shift * mask# 3. 频谱逆平移 + 逆变换ifft_shift = np.fft.ifftshift(fft_filtered)img_hpf = np.fft.ifft2(ifft_shift)img_hpf = np.abs(img_hpf)img_hpf = np.uint8(img_hpf)# 对比显示(原始图 vs 高通滤波边缘增强图)plt.subplot(1, 2, 1)plt.imshow(img, cmap="gray")plt.title("原始灰度图像")plt.axis("off")plt.subplot(1, 2, 2)plt.imshow(img_hpf, cmap="gray")plt.title("高通滤波边缘增强图(频域)")plt.axis("off")plt.show() |
调优技巧:高通半径radius越大,过滤的低频信号越多,边缘增强效果越明显,但图像背景会越暗;radius越小,边缘增强效果越弱,背景保留越多,建议取值20-40。
4. OpenCV函数实现(补充,适配OpenCV原生操作)
除了NumPy,OpenCV的cv2.dft()和cv2.idft()也能实现傅里叶变换,适配OpenCV的图像格式(Mat),代码如下,可根据需求选择:
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 读取灰度图img = cv2.imread("test.jpg", 0)# 转换为float32格式(cv2.dft()要求输入为float32)img_float = np.float32(img)# 1. 傅里叶正变换(OpenCV实现)dft = cv2.dft(img_float, flags=cv2.DFT_COMPLEX_OUTPUT)# 2. 频谱平移dft_shift = np.fft.fftshift(dft)# 3. 转换为频谱图(分离实部和虚部,计算幅度)mag, ang = cv2.cartToPolar(dft_shift[:, :, 0], dft_shift[:, :, 1])spectrum = 20 * np.log(mag)# 4. 低通滤波(复用前面的掩码)rows, cols = img.shapecrow, ccol = rows//2, cols//2mask = np.zeros((rows, cols, 2), np.uint8)mask[crow-30:crow+30, ccol-30:ccol+30] = 1dft_filtered = dft_shift * mask# 5. 逆变换(OpenCV实现)ifft_shift = np.fft.ifftshift(dft_filtered)idft = cv2.idft(ifft_shift)# 转换为可显示的灰度图img_filtered = cv2.magnitude(idft[:, :, 0], idft[:, :, 1])img_filtered = np.uint8(img_filtered)# 显示效果plt.subplot(1, 2, 1)plt.imshow(img, cmap="gray")plt.title("原始图像")plt.axis("off")plt.subplot(1, 2, 2)plt.imshow(img_filtered, cmap="gray")plt.title("OpenCV DFT 低通滤波降噪")plt.axis("off")plt.show() |
说明:NumPy实现更简洁,适合新手快速上手;OpenCV实现更适配复杂的OpenCV图像处理流程,两者效果一致,可灵活选择。
四、实操避坑指南(新手必看)
•傅里叶变换优先使用灰度图像,若用彩色图,需先转为灰度图(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)),否则会对三个通道分别处理,导致频谱图杂乱、效果异常;
•频谱图显示必须用20*np.log(np.abs(fft_shift))缩放,否则频谱图亮度差异过大,只能看到少数亮斑,无法正常解读;
•频域滤波的核心是“掩码设计”,低通掩码是“中心为1、周围为0”,高通掩码是“中心为0、周围为1”,掩码的半径的是关键,需根据图像大小调整;
•逆变换后得到的是复数数组,必须取绝对值(np.abs()),再转换为uint8格式(np.uint8()),否则无法正常显示图像;
•傅里叶变换的计算量较大,若图像尺寸过大,可先缩放图像(cv2.resize()),再进行变换,提升运行速度;也可使用OpenCV的getOptimalDFTSize()函数获取最优尺寸,优化计算效率。
五、完整实战代码(一键复制运行)
整合以上所有操作,包含“图像预处理+傅里叶正变换+频谱图显示+低通滤波降噪+高通滤波边缘增强+OpenCV实现”,替换自己的图像路径,即可一键跑通,适合新手直接实操,巩固所有知识点:
pythonimport cv2import numpy as npimport matplotlib.pyplot as plt# 1. 环境验证(可选)print("OpenCV版本:", cv2.__version__)# 2. 图像预处理(读取+加噪)def add_gaussian_noise(img, mean=0, var=0.001):img = np.array(img/255, dtype=np.float32)noise = np.random.normal(mean, var**0.5, img.shape)noise_img = img + noisenoise_img = np.clip(noise_img, 0, 1)noise_img = np.uint8(noise_img*255)return noise_imgimg = cv2.imread("test.jpg", 0) # 替换为自己的图像路径img_noise = add_gaussian_noise(img)# 3. NumPy实现傅里叶变换(正变换+频谱显示)fft_result = np.fft.fft2(img_noise)fft_shift = np.fft.fftshift(fft_result)spectrum = 20 * np.log(np.abs(fft_shift))# 4. 低通滤波(降噪)rows, cols = img_noise.shapecrow, ccol = rows//2, cols//2# 低通掩码mask_lpf = np.zeros((rows, cols), np.uint8)mask_lpf[crow-30:crow+30, ccol-30:ccol+30] = 1# 频域处理+逆变换fft_lpf = fft_shift * mask_lpfifft_shift_lpf = np.fft.ifftshift(fft_lpf)img_lpf = np.fft.ifft2(ifft_shift_lpf)img_lpf = np.uint8(np.abs(img_lpf))# 5. 高通滤波(边缘增强)fft_result_hpf = np.fft.fft2(img)fft_shift_hpf = np.fft.fftshift(fft_result_hpf)# 高通掩码mask_hpf = np.ones((rows, cols), np.uint8)mask_hpf[crow-30:crow+30, ccol-30:ccol+30] = 0# 频域处理+逆变换fft_hpf = fft_shift_hpf * mask_hpfifft_shift_hpf = np.fft.ifftshift(fft_hpf)img_hpf = np.fft.ifft2(ifft_shift_hpf)img_hpf = np.uint8(np.abs(img_hpf))# 6. OpenCV实现傅里叶变换(补充)img_float = np.float32(img_noise)dft = cv2.dft(img_float, flags=cv2.DFT_COMPLEX_OUTPUT)dft_shift_cv = np.fft.fftshift(dft)# 低通滤波mask_cv = np.zeros((rows, cols, 2), np.uint8)mask_cv[crow-30:crow+30, ccol-30:ccol+30] = 1dft_filtered_cv = dft_shift_cv * mask_cvifft_shift_cv = np.fft.ifftshift(dft_filtered_cv)idft_cv = cv2.idft(ifft_shift_cv)img_cv = cv2.magnitude(idft_cv[:, :, 0], idft_cv[:, :, 1])img_cv = np.uint8(img_cv)# 7. 统一显示所有效果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(img_noise, cmap="gray")plt.title("带高斯噪声图像")plt.axis("off")plt.subplot(2, 4, 3)plt.imshow(spectrum, cmap="gray")plt.title("傅里叶频谱图(低频在中心)")plt.axis("off")# 第二行:低通滤波、高通滤波、OpenCV实现plt.subplot(2, 4, 4)plt.imshow(img_lpf, cmap="gray")plt.title("低通滤波(降噪)")plt.axis("off")plt.subplot(2, 4, 5)plt.imshow(img_hpf, cmap="gray")plt.title("高通滤波(边缘增强)")plt.axis("off")plt.subplot(2, 4, 6)plt.imshow(img_cv, cmap="gray")plt.title("OpenCV DFT 降噪")plt.axis("off")plt.tight_layout()plt.show()# 8. 保存结果(可选)cv2.imwrite("fourier_spectrum.jpg", spectrum)cv2.imwrite("fourier_lpf.jpg", img_lpf)cv2.imwrite("fourier_hpf.jpg", img_hpf) |
最后总结(衔接系列,强化记忆)
从空间域的平滑、边缘检测、轮廓处理,到今天的频域傅里叶变换,我们已经完整掌握了OpenCV图像处理的两大核心视角——空间域(操作像素)+ 频域(操作频率),两者相辅相成,能解决不同场景的图像处理需求。
傅里叶变换的核心不是复杂公式,而是“将图像拆解为不同频率的信号”,通过频域滤波,高效实现降噪、边缘增强等效果——低通滤波过滤高频噪声,高通滤波保留高频边缘,比空间域处理更高效、更均匀,是工业质检、图像增强等项目的常用技巧。
记住核心要点:傅里叶变换的全流程是“正变换→频谱处理(掩码)→逆变换”,频谱图的解读是关键(中心低频、周围高频),新手先掌握NumPy实现,再尝试OpenCV实现,多调整掩码半径,就能快速上手。
辛苦大家看到这里啦,如果你觉得这篇OpenCV实操教程对你有帮助,麻烦动动小手,点赞+在看,让更多学习计算机视觉、OpenCV的小伙伴看到,一起交流学习、共同进步~
关注【AI与计算机视觉】,后台回复「傅里叶变换」,即可免费获取本文完整代码、测试素材,还有更多OpenCV实战教程,助力大家快速上手,搞定频域图像处理全流程!
评论区留言「傅里叶实操」,我们一起打卡练习,互相交流遇到的问题,深耕计算机视觉,解锁更多实战技巧!后续我们将讲解频域进阶应用(带通/带阻滤波),衔接今天的傅里叶变换,记得持续关注哦~