本文基于opencv官方文档,是本人的学习笔记。版本是在linux下的opencv4.2.0,全部程序调通可运行,无bug。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

# numpy中的傅里叶变换
# 首先,我们将看到如何使用Numpy查找傅立叶变换。
# Numpy具有FFT软件包来执行此操作。
# np.fft.fft2()为我们提供了频率转换,
# 它将是一个复杂的数组。它的第一个参数是输入图像,即灰度图像。
# 第二个参数是可选的,它决定输出数组的大小。
# 如果它大于输入图像的大小,则在计算FFT之前用零填充输入图像。
# 如果小于输入图像,将裁切输入图像。
# 如果未传递任何参数,则输出数组的大小将与输入的大小相同。
# 现在,一旦获得结果,零频率分量(DC分量)将位于左上角。
# 如果要使其居中,则需要在两个方向上将结果都移动$\frac{N}{2}$。
# 只需通过函数np.fft.fftshift()即可完成。
# (它更容易分析)。找到频率变换后,就可以找到幅度谱。

img = cv.imread('wq.jpg',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

rows, cols = img.shape
crow,ccol = rows//2 , cols//2
fshift[crow-30:crow+31, ccol-30:ccol+31] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)
plt.subplot(131),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(img_back, cmap = 'gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

# 结果表明高通滤波是边缘检测操作。这就是我们在“图像渐变”一章中看到的。
# 这也表明大多数图像数据都存在于频谱的低频区域。
# 无论如何,我们已经看到了如何在Numpy中找到DFT,IDFT等。
# 现在,让我们看看如何在OpenCV中进行操作。
# 如果仔细观察结果,尤其是最后一张JET颜色的图像,会看到一些伪像。
# 它在那里显示出一些波纹状结构,称为振铃效应。
# 这是由我们用于遮罩的矩形窗口引起的。此掩码转换为正弦形状,
# 从而导致此问题。因此,矩形窗口不用于过滤。更好的选择是高斯窗口


# opencv中的傅里叶变换
# OpenCV为此提供了cv.dft()和cv.idft()函数。
# 它返回与前一个相同的结果,但是有两个通道。
# 第一个通道是结果的实部,第二个通道是结果的虚部。
# 输入图像首先应转换为np.float32。我们来看看怎么做。

img = cv.imread('wq.png',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

# 现在我们要做DFT的逆变换。在上一节中,我们创建了一个HPF,
# 这次我们将看到如何删除图像中的高频内容,即我们将LPF应用到图像中。
# 它实际上模糊了图像。为此,我们首先创建一个高值(1)在低频部分,即我们过滤低频内容,0在高频区。
rows, cols = img.shape
crow,ccol = int(rows/2) , int(cols/2)
# 首先创建一个掩码,中心正方形为1,其余全为零
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# 应用掩码和逆DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()
# OpenCV函数cv.dft()和cv.idft()比Numpy函数更快。但是Numpy函数更容易使用


# DFT的性能优化
#
# 对于某些数组尺寸,DFT的计算性能较好。当数组大小为2的幂时,速度最快。
# 对于大小为2、3和5的乘积的数组,也可以非常有效地进行处理。
# 因此,如果您担心代码的性能,可以在找到DFT之前将数组的大小修改为任何最佳大小(通过填充零)。
# 对于OpenCV,您必须手动填充零。但是对于Numpy,您指定FFT计算的新大小,它将自动为您填充零。
# 那么如何找到最优的大小呢?
# OpenCV为此提供了一个函数,cv.getOptimalDFTSize()。它同时适用于cv.dft()和np.fft.fft2()。