参考:
1、http://docs.opencv.org/3.3.0/ 官方文档api
2、http://docs.opencv.org/3.3.0/d6/d00/tutorial_py_root.html 官方英文教程
3、https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
4、https://github.com/makelove/OpenCV-Python-Tutorial# 进阶教程
5、https://docs.opencv.org/3.3.0/index.html 官方英文教程
6、https://github.com/abidrahmank/OpenCV2-Python-Tutorials
7、https://www.learnopencv.com/
8、http://answers.opencv.org/questions/ OpenCV论坛
9、https://github.com/opencv/opencv 官方github
10、https://github.com/abidrahmank/OpenCV2-Python-Tutorials
注:安装的版本 opencv_python-3.3.0-cp36-cp36m-win_amd64.whl
参考:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
傅里叶变换
目标
-
In this section, we will learn
-
- To find the Fourier Transform of images using OpenCV
- To utilize the FFT functions available in Numpy
- Some applications of Fourier Transform
- We will see following functions : cv2.dft(), cv2.idft() etc
Numpy中的傅立叶变换
首先我们将看到如何使用Nump来找到傅里叶变换。 Numpy有一个 FFT包来做到这一点。 np.fft.fft2()为我们提供了一个复杂数组的频率变换。 它的第一个参数是 输入图像,这是 灰度。 第二个参数是可选的,它决定了输出数组的大小。 如果大于输入图像的大小,则输入图像在计算FFT之前用零填充。 如果小于输入图像,输入图像将被裁剪。 如果没有参数传递,输出数组大小将与输入相同。现在一旦得到结果,零频率分量(直流分量)将在左上角。 如果要将其带到中心位置,则需要将两个方向的
的结果相移。 这通过函数 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分量再次出现在左上角。 然后使用np.ifft2()函数找到逆FFT。 结果又是一个复杂的数字。 你可以把它的绝对价值。
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)) 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()
结果表明,高通滤波是一种边缘检测操作。 这是我们在“图像渐变”一章中看到的。 这也表明大部分图像数据存在于频谱的低频区域中。 无论如何,我们已经看到如何在Numpy找到DFT,IDFT等。 现在我们来看看OpenCV如何做到这一点。
如果您仔细观察结果,尤其是JET颜色的最后一张图像,您可以看到一些工件(一个例子是红色箭头标记)。 它显示了一些像这样的结构的波纹,它被称为振铃效应。 它是由我们用于掩蔽的矩形窗口引起的。 这个面具被转换成sinc形状,这导致这个问题。 所以矩形窗口不用于过滤。 更好的选择是高斯Windows。
OpenCV中的傅里叶变换
OpenCV为此提供了cv2.dft()和cv2.idft()函数。 它返回与以前相同的结果,但是具有两个通道。 第一个通道将具有结果的实际部分,第二个通道将具有结果的虚部。 输入图像应首先转换为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)的掩码,即我们通过LF内容,在HF区域通过0。
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])) 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()像往常一样,OpenCV函数cv2.dft()和cv2.idft()比Numpy对等体更快。 但是Numpy功能更加用户友好。 有关性能问题的更多详细信息,请参阅下文。
DFT的性能优化
对于某些阵列大小,DFT计算的性能更好。 当阵列大小为2时,速度最快。 尺寸为2,3和5的乘积的阵列也被非常有效地处理。 因此,如果您担心代码的性能,可以在找到DFT之前将数组的大小修改为任何最佳大小(通过填充零)。 对于OpenCV,您必须手动填零。 但是,对于Numpy,您可以指定FFT计算的新大小,并为您自动填充零。那么我们如何找到这个最佳尺寸? OpenCV为此提供了一个函数cv2.getOptimalDFTSize()。 它适用于cv2.dft()和np.fft.fft2()。 让我们用IPython magic命令 %timeit来检查他们的性能。
In [16]: img = cv2.imread('messi5.jpg',0) In [17]: rows,cols = img.shape In [18]: print rows,cols 342 548 In [19]: nrows = cv2.getOptimalDFTSize(rows) In [20]: ncols = cv2.getOptimalDFTSize(cols) In [21]: print nrows, ncols 360 576参见,尺寸(342,548)被修改为(360,576)。 现在让我们用零填充(对于OpenCV),并找到它们的DFT计算性能。 您可以通过创建一个新的大零数组并将数据复制到它,或使用cv2.copyMakeBorder()来实现。
nimg = np.zeros((nrows,ncols)) nimg[:rows,:cols] = imgOR:
right = ncols - cols bottom = nrows - rows bordertype = cv2.BORDER_CONSTANT #just to avoid line breakup in PDF file nimg = cv2.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)现在我们计算Numpy函数的DFT性能比较:
In [22]: %timeit fft1 = np.fft.fft2(img) 10 loops, best of 3: 40.9 ms per loop In [23]: %timeit fft2 = np.fft.fft2(img,[nrows,ncols]) 100 loops, best of 3: 10.4 ms per loop它显示了4倍的加速。 现在我们将尝试使用OpenCV功能
In [24]: %timeit dft1= cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT) 100 loops, best of 3: 13.5 ms per loop In [27]: %timeit dft2= cv2.dft(np.float32(nimg),flags=cv2.DFT_COMPLEX_OUTPUT) 100 loops, best of 3: 3.11 ms per loop
它也显示了4倍加速。 您还可以看到,OpenCV功能比Numpy功能快约3倍。 这也可以进行逆FFT测试,而作为一个练习。
为什么拉普拉斯算法是高通滤波器?
一个类似的问题在一个论坛上被问到。 问题是,为什么拉普拉斯算法是高通滤波器? 为什么Sobel是HPF? 而给出的第一个答案就是傅里叶变换。 只需要采用拉普拉斯算子的傅里叶变换来获得更大的FFT。 分析:
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()
Additional Resources
- An Intuitive Explanation of Fourier Theory by Steven Lehar
- Fourier Transform at HIPR
- What does frequency domain denote in case of images?