学习目标
- 使用OpenCV计算傅里叶变换
- 使用Numpy中的傅里叶变换(FFT)
- 傅里叶变换的应用
- 学习函数如下:
cv2.dft()
,cv2.idft()
理论
傅里叶变换用来分析不同滤波器的频率特性。对于图像而言,2D离散傅里叶变换(DFT)用于寻找频率域。傅里叶变换的快速算法,FFT,常用于计算DFT。
对于正弦信号,,我们称f
为频率信号,如果频率域确定,那么我们可以看到f
的具体形状(spike)。如果一个信号是离散采样的,我们得到相同的频率域,周期的范围,。可以将图像看成从两个方向采样的信号。那么从X和Y方向采样可以得到图像的表达。
更为直观的说,对于正弦信号,如果短时间内振幅变化很快,那么可以称为高频信号。如果变化很慢,则称之为低频信号。如果将该思想扩展到图像中,那么图像的什么区域振幅较大,通常是边界点或者噪声。所以,我们可以说,边界和噪声是图像中的高频信号。反之则为低频区域。
Numpy中的傅里叶变换
首先我们学习如何利用Numpy计算傅里叶变换。Numpy有相应的FFT包。np.fft.fft2()
提供了频率变换,它是复数组。第一个输入参数是灰度图像。第二个参数是可选的,决定了输出数组的大小。如果输出数组大于输入图像,那么输入图像会被填充零,然后计算FFT
。如果小于输入图像,那么输入图像会被裁剪。如果没有传入参数,输出数组大小与输入相同。
计算得到结果,零频域部分(DC component)在左上角。如果需要将其移入中心,那么需要偏移N/2。可以使用函数计算:np.fft.fftshift()
. 得到频率变换后,也可以得到振幅谱。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('messi5.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()
,那么DC区域会变换到左上角。并计算FFT的逆操作,np.ifft2()
。得到的结果是复数,取绝对值:
rows, cols = img.shape
crow,ccol = rows/2 , cols/2
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0
f_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()
运行结果如下:
从结果可以看出,高通滤波器是边界检测器。在图像梯度的章节我们也看到类似的效果。同样可以得到,图像大部分是低频区域。
OpenCV中傅里叶变换
OpenCV提供的函数:cv2.dft()
,cv2.idft()
. 返回结果与上面的一样,但是通道为2. 第一个通道是结果的实部,第二个通道是结果的虚部。输入图像必须转为np.float32,下面是代码示例:
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',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]))
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()
运行结果如下:
cv2.cartToPolar()可以同时返回幅度和相位
现在需要进行DFT
的逆操作。在上面的叙述中,我们创建了HPF,现在我们学习去除图像的高频部分,即对图像使用LPF。实际上会模糊图像。那么,我们对于低频部分置为1,高频为0.
rows, cols = img.shape
crow,ccol = rows/2 , cols/2
# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# apply mask and inverse DFT
fshift = dft_shift*mask
f_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()
运行结果如下:
优化DFT
某些大小的数组进行DFT
会非常快,比如数组大小时. 如果需要提速,可以对数组进行零填充,然后进行DFT
。对于OpenCV,需要人工填充零,而Numpy中,需要指定FFT的大小参数即可,然后会自动填充零。
所以,最佳的数组大小是?OpenCV提供了函数:cv2.getOptimalDFTSize()
。该函数可以应用于cv2.dft()
,cv2.idft()
,下面来验证:
为什么Laplacian是高通滤波器?
import cv2
import numpy as np
from matplotlib import pyplot as plt
# simple averaging filter without scaling parameter
mean_filter = np.ones((3, 3))
# creating a guassian filter
x = cv2.getGaussianKernel(5, 10)
gaussian = x * x.T
# different edge detecting filters scharr in x-direction
scharr = np.array([[-3, 0, 3],
[-10, 0, 10],
[-3, 0, 3]])
# sobel in x direction
sobel_x = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
# sobel in y direction
sobel_y = np.array([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]])
# laplacian
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 range(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()
运行结果: