:::danger 首先声明:下面大部分内容参考于 https://www.zhihu.com/question/22611929 ::: lect2.pdf

一维傅里叶变换

:::info 一维傅里叶变换就是一个基变换

  • 在时域中,基是一族冲激信号激信号 傅里叶变换 - 图1
  • 在频域中,基是 傅里叶变换 - 图2 ,而且这组基是正交基。 :::

傅里叶变换 - 图3
傅里叶变换 - 图4,基变换示意图

二维傅里叶变换

:::info 二维FT将一个图像分解成若干个复平面波 傅里叶变换 - 图5 之和 :::

傅里叶变换 - 图6

基变换

傅里叶变换 - 图7
通过公式,我们可以计算出,每个平面波在图像中成分是多少。从公式也可以看到,二维傅里叶变换就是将图像与每个不同频率的不同方向的复平面波做内积(先点乘在求和),也就是一个求在基 傅里叶变换 - 图8 上的投影的过程。

傅里叶变换 - 图9

傅里叶变换 - 图10

旋转不变性

从平面波的角度很容易理解,旋转没有改变平面波的幅度相位,只是将所有的平面波都旋转了一个角度。下面这个图像显示了二维傅里叶变换中,实空间旋转多少,频率空间也会相应旋转多少。这其实是高维傅里叶变换缩放定理的一种特殊情况。(连续的是可以证明的,离散的涉及插值 ,不一定完全准确)
傅里叶变换 - 图11

参考链接

https://www.cs.unm.edu/~brayer/vision/fourier.html

实验

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import cv2
  4. %matplotlib inline
  5. %config InlineBackend.figure_formats=['svg']
  6. # Define gaussian, sobel, and laplacian (edge) filters
  7. gaussian = (1/9)*np.array([[1, 1, 1],
  8. [1, 1, 1],
  9. [1, 1, 1]])
  10. sobel_x= np.array([[-1, 0, 1],
  11. [-2, 0, 2],
  12. [-1, 0, 1]])
  13. sobel_y= np.array([[-1,-2,-1],
  14. [0, 0, 0],
  15. [1, 2, 1]])
  16. # laplacian, edge filter
  17. laplacian=np.array([[0, 1, 0],
  18. [1,-4, 1],
  19. [0, 1, 0]])
  20. filters = [gaussian, sobel_x, sobel_y, laplacian]
  21. filter_name = ['gaussian','sobel_x', \
  22. 'sobel_y', 'laplacian']
  23. # perform a fast fourier transform on each filter
  24. # and create a scaled, frequency transform image
  25. f_filters = [np.fft.fft2(x) for x in filters]
  26. fshift = [np.fft.fftshift(y) for y in f_filters]
  27. frequency_tx = [np.log(np.abs(z)+1) for z in fshift]
  28. # display 4 filters
  29. for i in range(len(filters)):
  30. plt.subplot(2,2,i+1),plt.imshow(frequency_tx[i],cmap = 'gray')
  31. plt.title(filter_name[i]), plt.xticks([]), plt.yticks([])
  32. plt.show()

f1.svg

  1. image_copy = cv2.imread('images/brain_MR.jpg')
  2. image = cv2.cvtColor(image_copy, cv2.COLOR_BGR2RGB)
  3. image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  4. gray = image_gray #/255.0
  5. gray=np.fft.fft2(gray)
  6. gray=np.fft.fftshift(gray)
  7. imgae_fft=np.log(np.abs(gray)+1)
  8. f, ((ax1,ax2),(ax3,ax4),(ax5,ax6)) = plt.subplots(3,2, figsize=(10,10))
  9. ax1.imshow(image)
  10. ax2.imshow(imgae_fft)
  11. filter_x=np.pad(sobel_x, ((254, 255),(254, 255)), 'constant', constant_values=(0, 0))
  12. filter_x_fft = np.fft.fft2(filter_x)
  13. filter_x_fft = np.fft.fftshift(filter_x_fft)
  14. filter_x_f = np.log(np.abs(filter_x_fft)+1)
  15. ax3.imshow(sobel_x)
  16. ax4.imshow(filter_x_f)
  17. # img_fft=gray
  18. img_fft=gray*filter_x_fft
  19. ax6.imshow(np.log(np.abs(img_fft)+1))
  20. img=np.fft.ifft2(img_fft)
  21. img=np.fft.ifftshift(img)
  22. img=np.abs(img)
  23. img=img/np.max(img)*255
  24. ax5.imshow(img)

f2.svg

  1. clc,clear,close all
  2. % im = double(imread('hand.jpg'))/255;
  3. im = double(imread('cameraman.tif'))/255;
  4. % im = rgb2gray(im); % im should be a gray-scale floating point image
  5. [imh, imw] = size(im);
  6. hs = 50; % filter half-size
  7. fil = fspecial('gaussian', hs*2+1, 10);
  8. fftsize = 1024; % should be order of 2 (for speed) and include padding
  9. im_fft = fft2(im, fftsize, fftsize); % 1) fft im with padding
  10. fil_fft = fft2(fil, fftsize, fftsize); % 2) fft fil, pad to same size as image
  11. im_fil_fft = im_fft .* fil_fft; % 3) multiply fft images
  12. im_fil = ifft2(im_fil_fft); % 4) inverse fft2
  13. im_fil = im_fil(1+hs:size(im,1)+hs, 1+hs:size(im, 2)+hs); % 5) remove padding
  14. %%
  15. subplot(2,3,4)
  16. imagesc(log(abs(fftshift(im_fft)))), colormap jet,axis off,axis equal
  17. subplot(2,3,2)
  18. imagesc(log(fil+1)), colormap jet,axis off,axis equal
  19. subplot(2,3,5)
  20. imagesc(log(abs(fftshift(fil_fft)))), colormap jet,axis off,axis equal
  21. subplot(2,3,6)
  22. imagesc(log(abs(fftshift(im_fil_fft)))), colormap jet,axis off,axis equal
  23. subplot(2,3,3)
  24. imshow(im_fil)
  25. subplot(2,3,1)
  26. imshow(im)

ft.png