python-opencv Tutorials 一码人翻译(27)图像处理---- --傅里叶变换

目标
本小节我们将要学习:
  • 使用 OpenCV 对图像进行傅里叶变换
  • 使用 Numpy 中 FFT(快速傅里叶变换)函数
  • 傅里叶变换的一些用处
  • 我们将要学习的函数有:cv2.dft(),cv2.idft() 等
原理
  傅里叶变换经常被用来分析不同滤波器的频率特性。我们可以使用 2D 离散傅里叶变换 (DFT) 分析图像的频域特性。实现 DFT 的一个快速算法被称为快速傅里叶变换(FFT)。关于傅里叶变换的细节知识可以在任意一本图像处理或信号处理的书中找到。请查看本小节中更多资源部分。
对于一个正弦信号:x(t) = Asin(2πft), 它的频率为 f,如果把这个信号转到它的频域表示,我们会在频率 f 中看到一个峰值。如果我们的信号是由采样产生的离散信号好组成,我们会得到类似的频谱图,只不过前面是连续的,现在是离散。你可以把图像想象成沿着两个方向采集的信号。所以对图像同时进行 X 方向和 Y 方向的傅里叶变换,我们就会得到这幅图像的频域表示(频谱图)。
更直观一点,对于一个正弦信号,如果它的幅度变化非常快,我们可以说他是高频信号,如果变化非常慢,我们称之为低频信号。你可以把这种想法应用到图像中,图像那里的幅度变化非常大呢?边界点或者噪声。所以我们说边界和噪声是图像中的高频分量(注意这里的高频是指变化非常快,而非出现的次数多)。如果没有如此大的幅度变化我们称之为低频分量。
现在我们看看怎样进行傅里叶变换。


23.1.1 Numpy 中的傅里叶变换
  首先我们看看如何使用 Numpy 进行傅里叶变换。Numpy 中的 FFT 包可以帮助我们实现快速傅里叶变换。函数 np.fft.fft2() 可以对信号进行频率转换,输出结果是一个复杂的数组。本函数的第一个参数是输入图像,要求是灰度格式。第二个参数是可选的, 决定输出数组的大小。输出数组的大小和输入图像大小一样。如果输出结果比输入图像大,输入图像就需要在进行 FFT 前补0。如果输出结果比输入图像小的话,输入图像就会被切割。

现在我们得到了结果,频率为 0 的部分(直流分量)在输出图像的左上角。如果想让它(直流分量)在输出图像的中心,我们还需要将结果沿两个方向平移
N
2
。函数 np.fft.fftshift() 可以帮助我们实现这一步。(这样更容易分析)。
进行完频率变换之后,我们就可以构建振幅谱了。、

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('lenna.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()

我们可以看到输出结果的中心部分更白(亮),这说明低频分量更多。
现在我们可以进行频域变换了,我们就可以在频域对图像进行一些操作了,例如高通滤波和重建图像(DFT 的逆变换)。比如我们可以使用一个60x60 的矩形窗口对图像进行掩模操作从而去除低频分量。然后再使用函数np.fft.ifftshift() 进行逆平移操作,所以现在直流分量又回到左上角了,左后使用函数 np.ifft2() 进行 FFT 逆变换。同样又得到一堆复杂的数字,我们可以对他们取绝对值:

结果表明,高通滤波是一种边缘检测操作。这是我们在图像梯度章节中看到的。这也表明大多数图像数据都存在于频谱的低频区域中。不管怎样,我们已经看到了如何在Numpy中找到DFT,IDFT等。现在让我们看看如何在OpenCV中完成它。

如果您仔细观察结果,特别是JET颜色的最后一个图像,您可以看到一些工件(我用红色箭头标记的一个实例)。它显示了一些波纹状结构,叫做振铃效应。它是由我们用来掩蔽的矩形窗引起的。这个掩模被转换成sinc函数,从而导致这个问题。所以矩形窗口不用于过滤。更好的选择是高斯窗口。

傅里叶变换OpenCV

OpenCV为这一功能提供了cvdft()和cv.idft()函数。它返回的结果与前面相同,但是有两个通道。第一个通道将有结果的实部,第二个通道将有结果的虚部。输入图像应该转换为np。float32第一。我们将看到如何做到这一点。


import cv2

import numpy as np

import matplotlib.pyplot as plt

 

image = cv2.imread('lenna.jpg', cv2.IMREAD_GRAYSCALE)

 

dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)

dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))



image_back = np.fft.ifftshift(dft_shift)

image_back = cv2.idft(image_back)

image_back = cv2.magnitude(image_back[:, :, 0], image_back[:, :, 1])




rows, cols = image.shape

center_x, center_y = int(rows/2), int(cols/2)

dft_shift[center_x-30:center_x+30, center_y-30:center_y+30] = 0

filter_back = np.fft.ifftshift(dft_shift)

filter_back = cv2.idft(filter_back)

filter_back = cv2.magnitude(filter_back[:, :, 0], filter_back[:, :, 1])




plt.subplot(221), plt.imshow(image, 'gray'), plt.title('Origin')

plt.subplot(222), plt.imshow(magnitude_spectrum, 'gray'), plt.title('Fourier TransForm')

plt.subplot(223), plt.imshow(image_back, 'gray'), plt.title('Origin Back')

plt.subplot(224), plt.imshow(filter_back, 'gray'), plt.title('Transform Back')

plt.show()

猜你喜欢

转载自blog.csdn.net/qq_41905045/article/details/81586575