numpy.fft——numpy中的离散傅里叶变换模块
这篇博客将简要介绍numpy.fft中对图像进行处理的函数以及简单的滤波方法
1.numpy.fft.fft2
这个函数用于计算二维的离散傅里叶变换。
fft.fft2(a,s=None,axes=(-2,-1),norm=None)
从函数定义中可以看出,fft2接收4个参数:
a: 要进行傅里叶变换的数据,需要是一个数组,可以是复数形式的;
s:int型的序列(list,tuple都可以),描述输出的大小,即输出的分辨率。非必填参数。s中的各个元素用于设置输出在各个维度上的长度,即每个维度上的像素个数,s[0]代表第一个轴,s[1]代表第二个轴,以此类推。 如果设定的大小小于输入大小,则将输入裁剪成设定大小;如果设定大小大于输入大小,则将输入用0填充成设定大小。
axes:指定要在哪两个维度上进行傅里叶变换。可选参数,默认是最后两个维度。
norm: 归一化的方式,可选'backward','ortho','forward',默认是backward。
例:
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
root = r'lena.png'
img = Image.open(root).convert('L') #转成灰度图像
fft = np.fft.fft2(img,axes=(0,1)) # 进行傅里叶变换,axes不需要自定义
plt.subplot(121),plt.imshow(img,cmap='gray') # 显示原图
plt.subplot(122)
plt.imshow(np.log(np.abs(fft)),cmap='gray') # 显示傅里叶变换的结果
# 因为傅里叶变换的结果是复数,所以取模;
# 傅里叶变换后的值过大,所以取对数以便判读
plt.show()
显示结果:
2.傅里叶变换的图像怎么判读?
对于傅里叶变换的结果,每个轴的最前端代表频率为0的项,前半部分代表正频率项,中间部分代表奈奎斯特频率项(我也不懂这是什么),后半部分代表负频率项,即:
从0升高到最大频率(正值)|中间| 从最小频率(负值)升高到负数中的最大值(负值) 就像:[ 0. 1. 2. 3. 4. -5. -4. -3. -2. -1.]
那么对于图像,左上角就是频率为0的项,左上1/4就是正频率项(在两个轴都是正),右下1/4是负频率项(在两个轴都是负)。
numpy.fft.fftshift
前面通过fft2生成的频谱图不方便理解和处理(零频率在左(上)侧,而正频率和负频率都在零频率的右(下)侧),fftshift函数通过将频谱沿着每个维度移动一半的长度,将零频率分量移到频谱的中心位置,从而使频谱更易于理解和处理。
fft.fftshift(x, axes=None)
此函数接收2个参数:
x:输入的频谱图,需要是array;
axes: 指示在哪个轴上进行移动。非必填,默认为None,即在每个轴上都进行移动。
fft = np.fft.fft2(img,axes=(0,1))
fft_shift = np.fft.fftshift(fft) # shift
plt.subplot(131),plt.imshow(img,cmap='gray') # 显示原图
plt.title('Original Image'), plt.axis('off')
plt.subplot(132)
plt.title('FFT'), plt.axis('off')
plt.imshow(np.log(np.abs(fft)),cmap='gray')
plt.subplot(133),plt.imshow(np.log(np.abs(fft_shift)),cmap='gray')
plt.title('FFT Shifted'), plt.axis('off')
plt.show()
显示结果:
这样频谱图的中心是0频率部分,左上1/4为负频率,右下1/4为正频率。
numpy.fft.ifftshift
把fftshift转换过的频谱图再转换回去,是fftshift的逆函数。Shift过的频谱图不能直接用于反傅里叶变换,需要使用这个函数转换回原来的形式才能进行反傅里叶变换。
3.numpy.fft.ifft2
用于计算离散傅里叶逆变换
fft.ifft2(a, s=None, axes=(-2, -1), norm=None)
该函数接收4个参数,其中输入参数a是与fft2的输出有着相同排序方式的频谱图,其他参数与fft2相同,不再赘述。
ifft = np.fft.ifft2(fft)
plt.subplot(121),plt.imshow(img,cmap='gray')
plt.title('Original Image'), plt.axis('off')
plt.subplot(122),plt.imshow(np.abs(ifft),cmap='gray')
plt.title('iFFT Image'), plt.axis('off')
plt.show()
与原图一致。
4.简单的滤波
1)用傅里叶变换实现低通和高通滤波
根据前文,频谱图(shifted)中的中间是低频部分,让中间部分通过即为低通滤波,中间部分不通过,其他部分通过则为高通滤波。
rows, cols = fft.shape[0], fft.shape[1]
mid_row, mid_col = int(rows/2), int(cols/2) # 中间的行号、列号
low_pass_fftshift = np.zeros_like(fft_shift)
high_pass_fftshift = fft_shift.copy()
low_pass_fftshift[mid_row-20:mid_row+20,mid_col-20:mid_col+20] = fft_shift[mid_row-20:mid_row+20,mid_col-20:mid_col+20] # 低频通过
high_pass_fftshift[mid_row-20:mid_row+20,mid_col-20:mid_col+20] = 0 # 低频设为0,则为高通
low = np.fft.ifft2(np.fft.ifftshift(np.fft.ifftshift(low_pass_fftshift,axes=(0,1))))
high = np.fft.ifft2(np.fft.ifftshift(np.fft.ifftshift(high_pass_fftshift,axes=(0,1))))
plt.subplot(221),plt.imshow(np.log(np.abs(low_pass_fftshift)),cmap='gray')
plt.title('Low Pass Filter'), plt.axis('off')
plt.subplot(222),plt.imshow(np.abs(low),cmap='gray')
plt.title('Low Pass Image'), plt.axis('off')
plt.subplot(223),plt.imshow(np.log(np.abs(high_pass_fftshift)),cmap='gray')
plt.title('High Pass Filter'), plt.axis('off')
plt.subplot(224),plt.imshow(np.abs(high),cmap='gray')
plt.title('High Pass Image'), plt.axis('off')
plt.show()
效果:
低通滤波即为图像模糊,高通滤波则得到图像边缘。
2)去横、竖线条
在频域图像中可以很容易地注意到两条白色的直线,一条水平,一条竖直,在图像中央交叉。其中,水平线条代表原图中沿着水平方向变化快的部分,即为竖线条;同理,竖直线条代表原图中的横线部分。我们可以试着分别将这两部分去除。
以上面的网格图为例。
root = r'grid.png'
img = Image.open(root).convert('L')
fft = np.fft.fft2(img)
fft_shift = np.fft.fftshift(fft)
rows, cols = fft.shape[0],fft.shape[1]
mid_row, mid_col = int(rows/2), int(cols/2)
horizen_shift, vertical_shift = np.copy(fft_shift), np.copy(fft_shift)
horizen_shift[mid_row-10:mid_row+10,:mid_col-10] = 0
horizen_shift[mid_row-10:mid_row+10,mid_col+10:] = 0
vertical_shift[:mid_row-10,mid_col-10:mid_col+10] = 0
vertical_shift[mid_row+10:,mid_col-10:mid_col+10] = 0
plt.subplot(221),plt.imshow(np.log(np.abs(horizen_shift)),cmap='gray')
plt.title('Horizontal Shift'), plt.axis('off')
plt.subplot(222),plt.imshow(np.log(np.abs(vertical_shift)),cmap='gray')
plt.title('Vertical Shift'), plt.axis('off')
ver_ishift = np.fft.ifftshift(horizen_shift,axes=(0,1))
hor_ishift = np.fft.ifftshift(vertical_shift,axes=(0,1))
horizental_remove = np.fft.ifft2(horizen_shift)
vertical_remove = np.fft.ifft2(vertical_shift)
plt.subplot(223),plt.imshow(np.abs(horizental_remove),cmap='gray')
plt.title('Horizontal Removed'), plt.axis('off')
plt.subplot(224),plt.imshow(np.abs(vertical_remove),cmap='gray')
plt.title('Vertical Removed'), plt.axis('off')
plt.show()
去除频域图中的水平线,则原图中竖线消失;去除频域中的竖直线,则原图中横线消失。