傅里叶变换方法的基本思想是图像可以看作二位函数f,这个函数可以表示成在二个维度上正弦和余弦(傅里叶基本函数)的加权和。
使用DFT可将(空间域/时域)图像中的一组灰度像素值转换为一组(频域)傅里叶系数,而且它是离散的(因为空间和变换变量只可以使用离散连续整数的值)。
类似地,频域中的傅里叶系数二维数组可以通过离散傅里叶逆变换(IDFT) 变换至空间域。这种变换也称为利用傅里叶系数重建图像。DFT和IDFT的数学定义在这里不作展开。
由于通常情况下图像是平滑的,大多数图像可以用少量的DFT系数表示,而其余所有较高的系数几乎可以被忽略或为0。因此,DFT对于图像压缩非常有用,尤其是对于稀疏傅里叶图像。只有少数的傅里叶系数用于重建图像,因此只有这些频率可以被存储,其他的可以丢弃,这可以用于高压缩,例如,在JPEG图像(即常见的.jpg格式图像)压缩算法中使用了类似于变换的离散余弦变换(DCT) 。
此外,在频域中使用DFT滤波比在空间域中进行滤波快得多。
快速傅里叶变换(FFT)运用了分治的思想,对于一幅n*n的图像,它递归计算的时间复杂度为O(NlogN),而离散傅里叶变换计算的速度慢得多(时间复杂度为O(N^2))。
在Python中,numpy和scipy库中都提供了使用FFT算法计算2D DFT/IDFT的函数。
利用scipy.fftpack模块的fft2()/ifft2()函数来计算DFT/IDFT,代码如下:
from PIL import Image
import numpy as np
import matplotlib.pylab as pylab
import scipy.fftpack as fp
def signaltonoise(a, axis, ddof=0): # scipy1.1中计算信噪比的函数signaltonoise已废除,故须自己实现,大家可自行保存,方便日后使用
a = np.asanyarray(a)
m = a.mean(axis)
sd = a.std(axis=axis, ddof=ddof)
return np.where(sd == 0, 0, m/sd)
im = np.array(Image.open(r'.../3.jpg').convert('L')) # 打开图像转换成灰度图像并将其转换为numpy数组。
snr = signaltonoise(im, axis=None)
print('SNR for the original image = ' + str(snr))
freq = fp.fft2(im)
im1 = fp.ifft2(freq).real
print('SNR for the image obtained after reconstruction = ' + str(snr))
assert(np.allclose(im, im1)) # 运用断言函数确保原始图像和重建图像彼此接近
pylab.figure(figsize=(20, 10))
pylab.subplot(121), pylab.imshow(im, cmap='gray'), pylab.axis('off')
pylab.title('Original Image', size=20)
pylab.subplot(122), pylab.imshow(im1, cmap='gray'), pylab.axis('off')
pylab.title('Image obtained after reconstruction', size=20)
pylab.show()
# calculate frequencies and then get 2D fourier-transformed image.
freq2 = fp.fftshift(freq)
pylab.figure(figsize=(10, 10)), pylab.imshow((20 * np.log10(0.1 + freq2)).astype(int)), pylab.show()
# 最后一行代码在运行时会收到警告“ComplexWarning: Casting complex values to real discards the imaginary part”
# 是因为我们将虚数转换为实数时丢失虚部,不用理会
结果如下:
SNR for the original image = 1.9233909074007256
SNR for the image obtained after reconstruction = 1.9233909074007256
由内联输出的信噪比和输入与重建图像的视觉差异可以看出,重建图像丢失了一些信息。如果把得到的所有系数都用在重建上,这些差异就可以忽略不计。
由于傅里叶系数是复数,因此我们可以直观地看出其幅度。显示傅里叶变换的幅度称为变换的频谱。DFT的F值[0,0]称为直流系数(DC coefficient)。DC系数对于其他系数值而言太大,因此看不到,多以我们需要通过显示变换的对数来增大变换值。此外为了显示方便,变换系数被移位(使用fftshift()),使直流分量在中间。
计算DFT的幅值和相位 利用fft2()得到傅里叶系数的实分量和虚分量;然后计算幅值、频谱和相位,最后用ifft2()重建图像,代码如下:
import numpy as np
from skimage.color import rgb2gray
from skimage.io import imread
import matplotlib.pylab as pylab
import numpy.fft as fp
im1 = rgb2gray(imread(r'.../4.jpg'))
pylab.figure(figsize=(12, 10))
freq1 = fp.fft2(im1)
im1_ = fp.ifft2(freq1).real
pylab.subplot(2, 2, 1), pylab.imshow(im1, cmap='gray'),
pylab.title('Original Image', size=20)
pylab.subplot(2, 2, 2), pylab.imshow(20*np.log10(0.01 + np.abs(fp.fftshift(freq1))), cmap='gray')
pylab.title('FFT Spectrum Magnitude', size=20)
pylab.subplot(2, 2, 3), pylab.imshow(np.angle(fp.fftshift(freq1)), cmap='gray')
pylab.title('FFT Phase', size=20)
pylab.subplot(2, 2, 4), pylab.imshow(np.clip(im1_, 0, 255), cmap='gray')
pylab.title('Reconstructed Image', size=20)
pylab.show()
输出结果如下:
虽然FFT相位的信息不像幅值那么丰富,但也很重要,如果相位不可用或使用不同的相位阵列,就无法重建图像