第四章 频域图像处理

1. 傅里叶级数和傅里叶变换
前言
1.为什么图像处理需要傅里叶变换?
当我们处理数字图像时,常常需要分析图像中的各种频率成分。傅里叶变换就像是一副"数学眼镜",能让我们看到图像中不同频率的成分分布。例如:
高频部分:代表图像的边缘、细节和噪声
低频部分:代表图像的整体结构和大致轮廓
这样,我们就可以根据图像的不同频率成分,对图像进行增强、锐化、模糊等处理,从而实现图像的增强、锐化、模糊等效果。
比如,我们可以通过高通滤波器来增强图像的边缘和细节,通过低通滤波器来模糊图像的边缘和细节。
那怎样才能获取这些频率成分呢?这就需要用到傅里叶变换 。
在讲解傅里叶变换之前,我们先来了解一下傅里叶级数 。 👈 👈
2. 从周期现象到傅里叶级数
在讲解知识之前,我们先介绍下一个人,傅里叶 。

Fourier出生于1768年,是法国数学家、物理学家。他发明了傅里叶级数。后人在他研究成果的基础上,又对傅里叶级数进行了改进,提出了傅里叶变换。
傅里叶变换是现代工程中最重要的数学理论之一
好了,接下来我们就来讲解傅里叶级数。👇 👇
1. 基本概念
想象一个复杂的音乐和弦,它其实是由多个简单的正弦波组成的。
傅里叶级数的核心思想就是:任何周期信号都可以表示为不同频率的正弦和余弦函数的和。 👈 👈
如果不理解,可以看下图:

如果还不理解,可以通过可视化的方式展示:https://www.jezzamon.com/fourier/zh-cn.html 👈 👈
2. 傅里叶级数公式
对于周期为T的函数f(t),可以表示为:
f(t) = a₀/2 + Σ[aₙcos(nω₀t) + bₙsin(nω₀t)]
其中:
ω₀=2π/T 是基频
aₙ和bₙ是各频率成分的系数
n=1,2,3,...表示谐波次数
复数形式的傅里叶级数公式为:
f(t) = Σ cₙe^(jnω₀t)
其中cₙ是复数系数,包含了幅度和相位信息。
相位指的是正弦函数的初始相位,幅度指的是正弦函数的振幅。<----- 记住这个词汇,后面会用到相位谱,是图像的结构蓝图,表示了图像中不同频率成分的结构分布
幅度指的是正弦函数的振幅。<----- 是图像的"能量地图",表示了图像中不同频率成分的能量分布结合上述的可视化页面,我们对傅里叶级数有了初步的了解,发现傅里叶级数就像一台"周期信号分析仪" :
- 只能处理严格周期性的信号(如完美的心电图、交流电)
- 要求信号必须无限重复(周期T固定)
而实际问题:
- 现实中大多数信号是非周期的 (如语音信号、图像、突发脉冲)
- 即使近似周期信号,也常有微小变化 (如实际心电图每次心跳略有不同)如下图👇


而傅里叶级数无法处理这种情况,😢 😢 所以我们需要用到傅里叶变换 。:
3. 从傅里叶级数到傅里叶变换
3. 1. 非周期信号的扩展
当周期T→∞ 扩展无穷时,我们可以认为信号是无限重复的,这样就可以用傅里叶级数来分析信号了,离散的频率nω₀变为连续的频率ω,求和变为积分,这就是傅里叶变换:

F(ω) = ∫ f(t)e^(-jωt) dt /
f(t)是时域信号,F(ω)是频域信号
上式是从时域到频域的变换,称为傅里叶变换3.2. 直观理解
傅里叶变换可以看作是一个"信号成分分析器":
输入:时域信号(如随时间变化的电压)
输出:频域表示(各频率成分的强度和相位)

那对于数字图像的增强,我们可以采用傅里叶变换将图像转为 频域图像,然后对频域图像进行处理,最后将处理后的频域图像转为时域图像。
F(ω) = ∫ f(t)e^(-jωt) dt /
f(t)是时域信号,F(ω)是频域信号
上式是从时域到频域的变换,称为傅里叶变换
逆变换:
f(t) = (1/2π) ∫ F(ω)e^(jωt) dω
上式是从频域到时域的变换,称为傅里叶逆变换
2. 离散傅里叶变换
前言
1. 为什么数字图像处理要用二维离散傅里叶变换尼?
傅里叶变换有多种不同的变体形式,如连续时间傅里叶变换、离散时间傅里叶变换等,在数字信号处理中,处理的信号是随着时间变化的时域信号,其自变量为t;而图像中是二维随空间坐标变化的灰度值,属于空域信号,其自变量为x和y。因此图像处理通常使用的是二维离散傅里叶变换(DFT),信号在空间域和频率域上都呈现离散形式。
数据结构维度: 👇
- 一维信号
- 时域:单一时间轴上的幅度变化(如音频波形)
- 频域:仅反映时间方向的频率成分


二维图像
- 空间域:像素在x和y两个方向上的强度分布
- 频域:需要同时捕获水平和垂直方向的空间频率

中心点:坐标原点(0,0),代表零频率(直流分量)
水平轴(u):对应图像水平方向的空间频率
垂直轴(v):对应图像垂直方向的空间频率
白色/亮区域:该频率成分强度高
黑色/暗区域:该频率成分强度低
灰度渐变:表示强度的连续变化
案例展示:👇
import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy import misc
# 尝试读取图像(添加错误处理)
img = cv2.imread('./img/cameraman.bmp', 0)
# 检查图像尺寸
if img.size == 0:
raise ValueError("加载的图像为空,请检查文件路径")
# 计算DFT
dft = np.fft.fft2(img)
dft_shift = np.fft.fftshift(dft) # 将低频移到中心
a
# 计算幅度谱(加1避免log(0))
magnitude_spectrum = 20 * np.log(np.abs(dft_shift) + 1e-6)
# 使用matplotlib显示(比cv2.imshow更稳定)
plt.figure(figsize=(12, 6))
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('原始图像'), plt.axis('off')
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('幅度谱'), plt.axis('off')
plt.tight_layout()
plt.show()
2. 二维傅里叶变换与图像处理
2.1 二维DFT公式:

其中:
- F(u,v) 是频域图像
- f(x,y) 是空域图像
- M和N是图像的宽度和高度
- u和v是频域坐标
2.2 频域滤波的本质原理:
频域滤波的本质是通过修改图像的频率成分来实现特定处理效果。当图像转换到频域后,我们实际上获得了一幅"频谱地图",其中:

- 坐标位置:每个点(u,v)代表特定的空间频率(u是水平方向,v是垂直方向)
- 靠近中心:低频成分(图像整体明暗)
- 远离中心:高频成分(边缘和细节)
- 点的亮度:表示该频率成分的强度
- 点的相位:记录频率成分的空间位置信息
2.3 设计频域滤波器
- 低通滤波器:
- 保留中心区域(低频)
- 衰减外围区域(高频)
- 效果:图像平滑、去噪 👈 👈
- 高通滤波器:
- 保留外围区域(高频)
- 衰减中心区域(低频)
- 效果:边缘增强 👈 👈
- 带通/带阻滤波器:
- 选择特定环形区域
- 效果:纹理分析、周期性噪声去除 👈 👈
我们来通过案例观察一下不同图像的频谱图:👇
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负
# 设置图像大小
plt.rcParams['figure.figsize'] = [10, 8]
# 创建子图布局
fig, axs = plt.subplots(4, 2, figsize=(12, 16))
# 1. 纯色图像
gray_img = np.ones((256, 256)) * 128 # 创建256x256的全灰色图像(灰度值128)
gray_fft = np.fft.fft2(gray_img)
gray_fft_shift = np.fft.fftshift(gray_fft)
axs[0, 0].imshow(gray_img, cmap='gray')
axs[0, 0].set_title('纯色图像 (128灰度)')
axs[0, 1].imshow(np.log(1 + np.abs(gray_fft_shift)), cmap='gray')
axs[0, 1].set_title('频谱 (仅直流分量)')
# 2. 垂直黑白条纹
stripe_img = np.zeros((256, 256))
stripe_img[:, ::20] = 255 # 每20像素一条垂直线
stripe_fft = np.fft.fft2(stripe_img)
stripe_fft_shift = np.fft.fftshift(stripe_fft)
axs[1, 0].imshow(stripe_img, cmap='gray')
axs[1, 0].set_title('垂直黑白条纹')
axs[1, 1].imshow(np.log(1 + np.abs(stripe_fft_shift)), cmap='gray')
axs[1, 1].set_title('频谱 (水平对称亮斑)')
# 3. 棋盘格图像
checker_img = np.zeros((256, 256))
checker_img[::20, ::20] = 255 # 20x20的棋盘格
checker_img[10::20, 10::20] = 255
checker_fft = np.fft.fft2(checker_img)
checker_fft_shift = np.fft.fftshift(checker_fft)
axs[2, 0].imshow(checker_img, cmap='gray')
axs[2, 0].set_title('棋盘格图像')
axs[2, 1].imshow(np.log(1 + np.abs(checker_fft_shift)), cmap='gray')
axs[2, 1].set_title('频谱 (十字+对角亮斑)')
# 4. 自然图像,导入本地图像 cameraman.bmp
# 尝试读取图像(添加错误处理)
cameraman_img = cv2.imread('./img/cameraman.bmp', 0) # 获取灰度图像
cameraman_fft = np.fft.fft2(cameraman_img)
cameraman_fft_shift = np.fft.fftshift(cameraman_fft)
axs[3, 0].imshow(cameraman_img, cmap='gray')
axs[3, 0].set_title('自然图像 (摄影师)')
axs[3, 1].imshow(np.log(1 + np.abs(cameraman_fft_shift)), cmap='gray')
axs[3, 1].set_title('频谱 (复杂亮斑)')
# 关闭所有的坐标显示
for ax in axs.flat:
ax.set_xticks([])
ax.set_yticks([])
# # 调整布局
plt.tight_layout()
plt.show()
- 纯色图像
- 图像:全灰色图 :全灰色图
- 频谱:仅中心一个亮点(只有直流分量)
- 黑白条纹图
- 图像:垂直黑白相间条纹
- 频谱:水平方向的两个对称亮斑(不含垂直频率)
- 棋盘格图像
- 图像:黑白棋盘
- 频谱:呈现十字形+对角线的亮斑阵列
- 自然图像
- 图像:普通照片
- 频谱:中心亮斑+向外辐射的星形图案(高频成分)
3. OpenCV中的傅里叶变换
OpenCV中的傅里叶变换

import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# 读取图像并转为浮点型
img = cv2.imread(r'./img/peppers.bmp', 0).astype(np.float32) # 关键修改1:转为float32
if img is None:
raise FileNotFoundError("无法加载图像,请检查路径")
# 傅里叶变换
dft = np.fft.fft2(img)
dft_shift = np.fft.fftshift(dft) # 低频移到中心
# 计算幅值谱和相位谱
magnitude_spectrum = 20 * np.log(1 + np.abs(dft_shift))
phase_spectrum = np.angle(dft_shift) # 获取相位信息
# 提取幅度谱和相位谱
magnitude = np.abs(dft_shift)
phase = np.angle(dft_shift)
## 注意啦,这里有2种重构方式!!!! 可以自行尝试
# 重构图像(从中心化频谱)
# reconstructed = np.fft.ifft2(np.fft.ifftshift(dft_shift)).real
# 幅值谱和相位谱重构图像
reconstructed = np.fft.ifft2(np.fft.ifftshift(magnitude * np.exp(1j*phase))).real
# 可视化
plt.figure(figsize=(15, 10))
plt.subplot(231), plt.imshow(img, cmap='gray')
plt.title('原始图像'), plt.axis('off')
plt.subplot(232), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('幅值谱'), plt.axis('off')
plt.subplot(233), plt.imshow(phase_spectrum, cmap='gray')
plt.title('相位谱'), plt.axis('off')
plt.subplot(234), plt.imshow(reconstructed, cmap='gray')
plt.title('重构图像'), plt.axis('off')
# 幅值谱和相位谱互换实验
mag_only = np.abs(dft_shift) * np.exp(1j * 0) # 仅保留幅值
phase_only = 1 * np.exp(1j * np.angle(dft_shift)) # 仅保留相位
recon_mag = np.fft.ifft2(np.fft.ifftshift(mag_only)).real
recon_phase = np.fft.ifft2(np.fft.ifftshift(phase_only)).real
plt.subplot(235), plt.imshow(recon_mag, cmap='gray')
plt.title('仅用幅值重构'), plt.axis('off')
plt.subplot(236), plt.imshow(recon_phase, cmap='gray')
plt.title('仅用相位重构'), plt.axis('off')
plt.tight_layout()
plt.show()
总结记忆要点:👇
- 幅度谱是图像的"能量地图"(回答"有多少")
- 相位谱是图像的"结构蓝图"(回答"在哪里")
- 只有幅度 → 模糊的色块--失去所有结构信息,呈现无意义的能量分布
- 只有相位 → 可识别的轮廓--保留边缘和结构特征,但失去明暗对比
重要发现:
- 仅用相位重构的图像仍能保留主要边缘特征
- 仅用幅值重构的图像几乎丢失所有结构信息
- 这证明相位谱在图像重建中比幅值谱更重要
4. 傅里叶变换的应用---提前了解下,后期会用
傅里叶变换的应用
高通滤波器Vs低通滤波器
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 读取图像并转为灰度
img = cv2.imread('./img/moon.jpg', 0).astype(np.float32)
if img is None:
# 如果找不到图像,创建测试图
img = np.zeros((256,256))
img[100:156, 100:156] = 255 # 白色方块
print("使用模拟图像替代")
# 傅里叶变换
dft = np.fft.fft2(img) # 获取频域表示
dft_shift = np.fft.fftshift(dft) # 低频移到中心
def create_filter(shape, center, radius, filter_type='lowpass'):
"""创建圆形滤波器"""
rows, cols = shape # 获取图像尺寸
crow, ccol = center # 获取中心坐标
mask = np.zeros((rows, cols), np.float32) # 创建掩码
y, x = np.ogrid[:rows, :cols] # 创建网格
# 计算距离矩阵
dist_from_center = np.sqrt((x - ccol)**2 + (y - crow)**2) #
if filter_type == 'lowpass':# 低通滤波器
mask[dist_from_center <= radius] = 1 # 在半径内设为1
elif filter_type == 'highpass':# 高通滤波器
mask[dist_from_center > radius] = 1 # 在半径外设为1
elif filter_type == 'bandpass':# 带通滤波器
mask[np.logical_and(dist_from_center >= radius[0],
dist_from_center <= radius[1])] = 1
return mask
# 参数设置
rows, cols = img.shape # 获取图像尺寸
center = (rows//2, cols//2) # 设置中心坐标
radius = 10 # 截止频率
# 创建低通滤波器
lowpass_mask = create_filter(img.shape, center, radius, 'lowpass')
# 应用滤波
filtered_dft = dft_shift * lowpass_mask #
lowpass_img = np.fft.ifft2(np.fft.ifftshift(filtered_dft)).real
# 创建高通滤波器
highpass_mask = create_filter(img.shape, center, radius, 'highpass')
# 应用滤波
filtered_dft = dft_shift * highpass_mask
highpass_img = np.fft.ifft2(np.fft.ifftshift(filtered_dft)).real
plt.figure(figsize=(15,10))
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负
# 原始图像和频谱
plt.subplot(241), plt.imshow(img, cmap='gray')
plt.title('原始图像'), plt.axis('off')
magnitude_spectrum = 20*np.log(1+np.abs(dft_shift))
plt.subplot(242), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('幅度谱'), plt.axis('off')
# 低通滤波结果
plt.subplot(243), plt.imshow(lowpass_mask, cmap='gray')
plt.title('低通滤波器'), plt.axis('off')
plt.subplot(244), plt.imshow(lowpass_img, cmap='gray')
plt.title('低通滤波结果'), plt.axis('off')
# 高通滤波结果
plt.subplot(247), plt.imshow(highpass_mask, cmap='gray')
plt.title('高通滤波器'), plt.axis('off')
plt.subplot(248), plt.imshow(highpass_img, cmap='gray')
plt.title('高通滤波结果'), plt.axis('off')
plt.tight_layout()
plt.show()