本文仅聚焦于如下几个问题:

  • 从三角级数展开,过渡到欧拉形式的傅里叶变换
  • 什么是离散傅里叶变换
  • 什么是2D傅里叶变换
  • 从2D傅里叶变换,解释到2D离散傅里叶变换
  • 如何对图像进行2D傅里叶变换
  • 2D傅里叶变换的周期性问题
  • 从2D傅里叶变换的周期性理解傅里叶2D频谱图的中心化

傅里叶级数

傅里叶级数是一种数学工具,它表示一个周期函数为一组正弦和余弦函数的无穷和。

三角级数展开

傅里叶变换最初是基于三角级数展开,即周期函数可以表示为一系列正弦和余弦函数的线性组合:

$$ f(t) = a_0 + \sum_{n=1}^{\infty} a_n \cos(n\omega_0 t) + b_n \sin(n\omega_0 t) $$

其中 $\omega_0 = \frac{2\pi}{T}$ 是基本频率。

复指数形式

利用欧拉公式:

$$ \cos(x) = \frac{e^{ix} + e^{-ix}}{2}, \quad \sin(x) = \frac{e^{ix} - e^{-ix}}{2i} $$

可以将傅里叶级数改写为复指数形式:

$$ f(t) = \sum_{n=-\infty}^{\infty} c_n e^{in\omega_0 t} $$

其中 $c_n$ 是复数系数,封装了正弦和余弦部分的信息。欧拉形式的傅里叶表示更对称、更易于处理,也为后续推广到连续和多维情况提供了基础。

在这里,$n$的取值范围变成了$-\infty$到$\infty$。

离散傅里叶变换 DFT

离散傅里叶变换(Discrete Fourier Transform,简称 DFT)是傅里叶分析的一个重要分支,它的作用是将离散的有限长度信号(通常是数字信号)从时间域/空间域转换到频率域,表示成一组复数频率分量的叠加。

实际中我们处理的是有限、离散的数据。离散傅里叶变换(DFT)定义如下:

$$ X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-i \frac{2\pi}{N}kn}, \quad k = 0, 1, \dots, N-1 $$

对应的逆变换为:

$$ x[n] = \frac{1}{N} \sum_{k=0}^{N-1} X[k] \cdot e^{i \frac{2\pi}{N}kn} $$

DFT 把时间域信号 $x[n]$ 映射到频域 $X[k]$,每个 $X[k]$ 对应一个频率分量的幅度和相位。

负频率、周期重排

离散傅里叶变换中,可以有负频率。

虽然 DFT 的索引 $k$ 是非负整数,但傅里叶频率具有模 $N$ 的周期性。

也就是说:

$$ e^{-j \frac{2\pi}{N} k n} = e^{-j \frac{2\pi}{N} (k + mN) n}, \quad \forall m \in \mathbb{Z} $$

因此,频率 $k = N - 1$ 实际上等价于频率 $-1$,频率 $k = N - 2$ 等价于 $-2$,以此类推。

我们可以将频率索引重新解释为从负频率到正频率

$$ k = -\frac{N}{2}, \dots, -1, 0, 1, \dots, \frac{N}{2} - 1 \quad (\text{当 } N \text{ 为偶数时}) $$

这种方式可以通过频谱的中心化重排,使得结果更符合频率对称直觉。

在后面的对图像的2D傅里叶变换后,频谱图通过中心化重排后,将低频区域全部变换到中央。

2D傅里叶变换

傅里叶变换可以从将原本的1D形式,扩展到2D形式。

什么是2D傅里叶变换

连续2D傅里叶变换的定义如下,对于一个连续函数 $f(x, y)$:

$$ F(u, v) = \iint_{-\infty}^{\infty} f(x, y) \cdot e^{-i2\pi (ux + vy)} , dx,dy $$

其逆变换为:

$$ f(x, y) = \iint_{-\infty}^{\infty} F(u, v) \cdot e^{i2\pi (ux + vy)} , du,dv $$

其中 $(x, y)$ 是空间域坐标,$(u, v)$ 是频率域坐标。变换结果 $F(u, v)$ 描述了信号中每个频率分量的幅值和相位。

2D 离散傅里叶变换

离散情况下,对于一个 $M \times N$ 的二维离散函数 $f[m, n]$,其二维离散傅里叶变换(2D DFT)定义为:

$$ F[k, l] = \sum_{m=0}^{M-1} \sum_{n=0}^{N-1} f[m, n] \cdot e^{-i 2\pi \left( \frac{km}{M} + \frac{ln}{N} \right)} $$

其逆变换为:

$$ f[m, n] = \frac{1}{MN} \sum_{k=0}^{M-1} \sum_{l=0}^{N-1} F[k, l] \cdot e^{i 2\pi \left( \frac{km}{M} + \frac{ln}{N} \right)} $$

2D傅里叶变换的周期性

类似于1D DFT,2D DFT 也具有周期性:

$$ F[k + M, l] = F[k, l], \quad F[k, l + N] = F[k, l] $$

这意味着频谱在两个方向(水平和垂直)上都是周期性的。

DFT 中的频率索引 $k$ 和 $l$ 取值范围是 $0$ 到 $M-1$ 和 $N-1$,但这些索引并不是单纯的“正频率”,它们是模周期的。

因为周期是 $M$ 和 $N$,所以后半部分可以看作对应的负频率分量。具体来说,当索引 $k > \frac{M}{2}$ 时,实际频率等价于 $k - M$ 对应的负频率;同理,当索引 $l > \frac{N}{2}$ 时,频率等价于 $l - N$ 对应的负频率。这样,频率索引的范围在 $[0, M-1]$ 和 $[0, N-1]$ 上循环,形成正负频率对称的结构。

如何对图像进行2D傅里叶变换

Python 中可以使用 numpyopencv 等库方便地进行 2D 傅里叶变换。例如,使用 NumPy:

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

# 加载图像并转换为灰度
img = Image.open('doge.jpg').convert('L')
f = np.array(img)

# 计算2D傅里叶变换
F = np.fft.fft2(f)

# 幅度谱与相位谱
magnitude_spectrum = np.abs(F)
phase_spectrum = np.angle(F)
log_magnitude = np.log(1 + magnitude_spectrum)

# 创建子图
fig, axs = plt.subplots(1, 3, figsize=(15, 5))

# 原始图像
axs[0].imshow(f, cmap='gray')
axs[0].set_title('Original Image')
axs[0].axis('off')

# 幅度谱
axs[1].imshow(log_magnitude, cmap='gray')
axs[1].set_title('Magnitude Spectrum (log)')
axs[1].axis('off')

# 相位谱
im = axs[2].imshow(phase_spectrum, cmap='gray')
axs[2].set_title('Phase Spectrum')
axs[2].axis('off')

# 添加 colorbar 到相位谱
fig.colorbar(im, ax=axs[2], shrink=0.7)

plt.tight_layout()
plt.show()

运行后可以看到幅度谱和相位谱,这里需要注意的一点在于,

频谱图

逆变换还原图像(频率+相位)

傅里叶变换的结果是复数,包含了幅度(Magnitude)和相位(Phase)信息。只保留其中之一就不能完整还原图像。

还原图像的方式如下:

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

# 加载图像并转换为灰度
img = Image.open('doge.jpg').convert('L')
f = np.array(img)

# 计算2D傅里叶变换
F = np.fft.fft2(f)

# 进行逆傅里叶变换
recovered = np.fft.ifft2(F)
recovered_real = np.real(recovered)

plt.imshow(recovered_real, cmap='gray')
plt.title('Recovered Image from IFFT')
plt.axis('off')
plt.show()

逆变换后应该和原图一样。

rfft

频谱图中心化

为了更清晰地观察频率分布,我们通常对频谱进行中心化,使低频位于中心,高频分布在周围。

中心化操作可以使用 np.fft.fftshift

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('doge.jpg', cv2.IMREAD_GRAYSCALE)

f = np.fft.fft2(img)

fshift = np.fft.fftshift(f)

magnitude_spectrum = 20 * np.log(np.abs(fshift) + 1)

plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(img, cmap='gray')

plt.subplot(1, 2, 2)
plt.title('Magnitude Spectrum')
plt.imshow(magnitude_spectrum, cmap='gray')

plt.show()

此时频谱图将低频成分移到了中心,更易于观察纹理和方向性等信息。

fftshift

中心大致可以表示轮廓信息,而细节纹理是高频信息位于边缘。