opencv 图像傅里叶变换

来源:互联网 发布:淘宝爱奇艺会员没了 编辑:程序博客网 时间:2024/05/18 01:31

1、 傅里叶变换

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

2、Numpy 中的傅里叶变换

  首先我们看看如何使用 Numpy 进行傅里叶变换。 Numpy 中的 FFT 包可以帮助我们实现快速傅里叶变换。函数 np.fft.fft2() 可以对信号进行频率转换,输出结果是一个复杂的数组。本函数的第一个参数是输入图像,要求是灰度格式,第二个参数是可选的, 决定输出数组的大小。输出数组的大小和输入图
像大小一样。如果输出结果比输入图像大,输入图像就需要在进行 FFT 前补0;如果输出结果比输入图像小的话,输入图像就会被切割。
  现在我们得到了结果, 频率为 0 的部分(直流分量)在输出图像的左上角。如果想让它(直流分量)在输出图像的中心,我们还需要将结果沿两个方向平移 N/2 。 函数 np.fft.fftshift() 可以帮助我们实现这一步。(这样更容易分析), 进行完频率变换之后,我们就可以构建振幅谱了。

import cv2import numpy as np from matplotlib import pyplot as plt img = cv2.imread('image/lufei.jpeg',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 逆变换。同样又得到一堆复杂的数字,我们可以对他们取绝对值:

#上接前段代码rows,cols = img.shapecrow,ccol = rows/2,cols/2fshift[crow-30:crow+30,ccol-30:ccol+30] = 0f_ishift = np.fft.ifftshift(fshift)img_back = np.fft.ifft2(f_ishift)img_back = np.abs(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 进行这些操作。
  但是图像中有一些不自然的东西,看上图头发那里有些条带装的结构,这被成为振铃效应。这是由于我们使用矩形窗口做掩模造成的,这个掩模被转换成正弦形状时就会出现这个问题。所以一般我们不适用矩形窗口滤波,最好的选择是高斯窗口。

2、 OpenCV 中的傅里叶变换

  OpenCV 中相应的函数是 cv2.dft() 和 cv2.idft()。和前面输出的结果一样,但是是双通道的。第一个通道是结果的实数部分,第二个通道是结果的虚数部分。输入图像要首先转换成 np.float32 格式。我们来看看如何操作。
  在前面的部分我们实现了一个 HPF(高通滤波),后面做 LPF(低通滤波)将高频部分去除。其实就是对图像进行模糊操作。首先我们需要构建一个掩模,与低频区域对应的地方设置为 1, 与高频区域对应的地方设置为 0。

import cv2import numpy as np from matplotlib import pyplot as plt img = cv2.imread('image/lufei.jpeg',0)dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)dft_shift = np.fft.fftshift(dft)magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))rows,cols = img.shapecrow,ccol = rows/2,cols/2mask = np.zeros((rows,cols,2),np.uint8)mask[crow-30:crow+30,ccol-30:ccol+30] = 1fshift = dft_shift*maskf_ishift = np.fft.ifftshift(fshift)img_back = cv2.idft(f_ishift)img_back = cv2.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 中的函数 cv2.dft() 和 cv2.idft() 要比 Numpy 快。但是Numpy 函数更加用户友好。

3 、DFT 的性能优化

  当数组的大小为某些值时 DFT 的性能会更好,当数组的大小是 2 的指数时 DFT 效率最高。当数组的大小是 2, 3, 5 的倍数时效率也会很高。所以如果你想提高代码的运行效率时,你可以修改输入图像的大小(补 0)。对于OpenCV 你必须自己手动补 0,但是 Numpy,你只需要指定 FFT 运算的大小,它会自动补 0。那我们怎样确定最佳大小呢?OpenCV 提供了一个函数:cv2.getOptimalDFTSize(),可以同时被 cv2.dft() 和 np.fft.fft2() 使用。

4 、为什么拉普拉斯算子是高通滤波器

为什么拉普拉斯算子是高通滤波器?为什么 Sobel 是 HPF?等等。对于第一个问题的答案我们以傅里叶变换的形式给出。我们一起来对不同的算子进行傅里叶变换并分析它们

import cv2import numpy as np from matplotlib import pyplot as plt mean_filter = np.ones((3,3))x = cv2.getGaussianKernel(5,10)gaussian = x*x.Tscharr = np.array([[-3,0,3],[-10,0,10],[-3,0,3]])sobel_x = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])sobel_y = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])laplacian = np.array([[0,1,0],[1,-4,1],[0,1,0]])filters = [mean_filter,gaussian,laplacian,sobel_x,sobel_y,scharr]filter_name = ['mean_filter','gaussian','laplacian','sobel_x','sobel_y','scharr_x']fft_filters = [np.fft.fft2(x) for x in filters]fft_shift = [np.fft.fftshift(y) for y in fft_filters]mag_spectrum = [np.log(np.abs(z)+1) for z in fft_shift]for i in xrange(6):    plt.subplot(2,3,i+1),plt.imshow(mag_spectrum[i],cmap = 'gray')    plt.title(filter_name[i]),plt.xticks([]),plt.yticks([])plt.show()

结果图:
          这里写图片描述
  从图像中我们就可以看出每一个算子允许通过那些信号。从这些信息中我
们就可以知道那些是 HPF 那是 LPF.
参考:Opencv官方教程中文版(For Python)

0 0