OpenCV傅里叶变换

傅立叶变换用于分析各种滤波器的频率特性。图像中的振幅在边缘点或噪声急剧变化。因此,可以说边缘和噪声是图像中的高频内容。如果幅度没有太大变化,则它是低频分量。

1.Numpy中的傅里叶变换

主要使用np.fft.fft2()、np.fft.fftshift()以及20*np.log(np.abs(fshift))函数来实现。

np.fft.fft2()

numpy.fft.fft2(img, s=None, axes=(-2, -1), norm=None)

参数如下:

  • img:输入图像(二维数组),即灰度图像
  • s:可选参数,它决定输出数组的大小。如果它大于输入图像的大小,则在计算FFT之前用零填充输入图像。如果小于输入图像,将裁切输入图像。如果未传递任何参数,则输出数组的大小将与输入的大小相同
  • axes:计算FFT的轴。如果未给出,则使用最后两个轴。轴上重复的索引表示该轴上的变换执行了多次
  • norm:可选参数
  • 返回值是一个复数数组
  • 对于np.fft.fft()以及np.fft.fftn()函数,第一个参数分别是一维数组和n维数组,返回值均为复数数组,第二个参数同上。

np.fft.fftshift()

函数参数主要是np.fft.fft2()函数的输出,即复数数组。此函数可以将零频率分量移到频谱中心。变换之后的频谱显示范围从[0, N]变为:[-N/2,N/2-1](N为偶数)或[-(N-1)/2,(N-1)/2](N为奇数)。

20*np.log(np.abs(fshift))

函数参数主要是np.fft.fftshift()函数的输出,傅里叶变换得到的结果是一个复数数组,不能直接用于显示图像,要想得到频谱灰度图像,需要一个映射,把复数映射[0, 255]之间。

代码示例:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv
img = cv.imread('C:\\Users\\dell\\Desktop\\prac files\\prac1.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
f = np.fft.fft2(img_gray)
f_shift = np.fft.fftshift(f)
f_shift_log = 20*np.log(np.abs(f_shift))
plt.subplot(1, 2, 1)
plt.imshow(img_gray, cmap = "gray")
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 2, 2)
plt.imshow(f_shift_log, cmap = "gray")
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

逆傅里叶变换

利用频率变换可以在频域中进行一些操作,例如高通滤波和重建图像,即找到逆DFT。需用尺寸为60x60的矩形窗口遮罩即可消除低频。然后,使用np.fft.ifftshift()应用反向移位,以使DC分量再次出现在左上角。然后使用np.ifft2()函数找到逆FFT。同样,结果将是一个复数,可以采用其实部或绝对值。

代码示例:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv
img = cv.imread('C:\\Users\\dell\\Desktop\\prac files\\prac1.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#傅里叶
f = np.fft.fft2(img_gray)
f_shift = np.fft.fftshift(f)
f_shift_log = 20*np.log(np.abs(f_shift))
#逆傅里叶
rows, cols = img_gray.shape
crow, ccol = rows//2 , cols//2
f_shift[crow-30:crow+31, ccol-30:ccol+31] = 0
f_ishift = np.fft.ifftshift(f_shift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)
#显示
plt.subplot(1, 3, 1)
plt.imshow(img_gray, cmap = "gray")
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2)
plt.imshow(f_shift_log, cmap = "gray")
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3)
plt.imshow(img_back, cmap = "gray")
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

2.OpenCV中的傅里叶变换

主要使用cv.dft()函数来实现。

cv.dft(src, dst, flags, nonzeroRows)

参数如下:

  • src:输入矩阵,可以为实数或者虚数,输入图像应该先转换为np.float32
  • dst:函数调用后的运算结果存在这里,其尺寸和类型取决于标识符,也就是第三个参数flags
  • flags:转换的标识符,有默认值0,取值如下:
标识符名称 意义
DFT_INVERSE 用一维或二维逆变换代替默认的正向变换
DFT_SCALE 缩放比例标识符,输出的结果都会以 1/N进行缩放,通常会结合DFT_INVERSE一起使用
DFT_ROWS 对输入矩阵的每行进行正向或反向的变换,此标识符可以在处理多种矢量的时候用于减小资源开销,这些处理常常是三维或高维变换等复杂操作
DFT_COMPLEX_OUTPUT 进行一维或二维实数数组正变换。这样的结果虽然是复数阵列,但拥有复数的共扼对称性(CCS),所以可以被写成一个拥有同样尺寸的实数阵列
DFT_REAL_OUTPUT 进行一维或二维复数数组反变换。这样的结果通常是一个大小相同的复矩阵。如果输入的矩阵有复数的共轭对称性(比如是一个带有DFT_COMPLEX_OUTPUT标识符的正变换结果),便会输出实矩阵
  • nonzeroRows:默认值为0。当此参数设为非零时,函数会假设只有输入矩阵的第一个非零行包含非零元素,或只有输出矩阵的一个非零行包含非零元素
  • 返回值有两个通道,第一个通道是结果的实部,第二个通道是结果的虚部。

代码示例:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv
img = cv.imread("C:\\Users\\dell\\Desktop\\prac files\\prac1.jpg", 0)
f = cv.dft(np.float32(img), flags = cv.DFT_COMPLEX_OUTPUT)
f_shift = np.fft.fftshift(f)
f_shift_log = 20*np.log(cv.magnitude(f_shift[:,:,0], f_shift[:,:,1]))
plt.subplot(1, 2, 1)
plt.imshow(img, cmap="gray")
plt.title("Input Image"), plt.xticks([]), plt.yticks([])
plt.subplot(1, 2, 2)
plt.imshow(f_shift_log, cmap="gray")
plt.title("Magnitude Spectrum"), plt.xticks([]), plt.yticks([])
plt.show()

其中cv.magnitude()函数用来计算二维矢量的幅值。

cv.magnitude(x, y, magnitude)

参数如下:

  • x:表示矢量的浮点型X坐标值,也就是实部
  • y:表示矢量的浮点型Y坐标值,也就是虚部
  • magnitude:输出的幅值,它和第一个参数X有着同样的尺寸和类型

也可以使用cv.cartToPolar()函数,参数同上,此函数在单个镜头中同时返回幅值和相位。

逆傅里叶变换

删除图像中的高频内容,即将LPF应用到图像中。它实际上模糊了图像。首先创建一个高值(1)在低频部分,即我们过滤低频内容,0在高频区。

代码示例:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv
img = cv.imread("C:\\Users\\dell\\Desktop\\prac files\\prac1.jpg", 0)
#傅里叶
f = cv.dft(np.float32(img), flags = cv.DFT_COMPLEX_OUTPUT)
f_shift = np.fft.fftshift(f)
f_shift_log = 20*np.log(cv.magnitude(f_shift[:,:,0], f_shift[:,:,1]))
#逆傅里叶
rows, cols = img.shape
crow,ccol = rows//2 , cols//2
# 首先创建一个掩码,中心正方形为1,其余全为零
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# 应用掩码和逆DFT
fshift = f_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(1, 3, 1)
plt.imshow(img, cmap="gray")
plt.title("Input Image"), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2)
plt.imshow(f_shift_log, cmap="gray")
plt.title("Magnitude Spectrum"), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3)
plt.imshow(img_back, cmap="gray")
plt.title("Result in JET"), plt.xticks([]), plt.yticks([])
plt.show()

3.DFT的性能优化

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

代码示例:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv
img = cv.imread("C:\\Users\\dell\\Desktop\\prac files\\prac1.jpg", 0)
rows, cols = img.shape
print(rows, cols)
nrows = cv.getOptimalDFTSize(rows)
ncols = cv.getOptimalDFTSize(cols)
print(nrows, ncols)

之后我们使用OpenCV对图像进行填充。

代码示例:

#填充图像方法一
nimg = np.zeros((nrows,ncols))
nimg[:rows,:cols] = img

或者

#填充图像方法二
right = ncols - cols
bottom = nrows - rows
bordertype = cv.BORDER_CONSTANT #只是为了避免PDF文件中的行中断
nimg = cv.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)

之后再对图像’nimg’进行傅里叶变换等操作即可。

4.HPF、LPF解释

通过傅里叶变化得到的频谱图,(零频率分量即DC分量已经平移到了频谱图的中心位置)中心为低频区域,从中心往外缘扩散,越往外频率越大。HPF为高通滤波器,可以滤掉低频信号,LPF为低通滤波器,可以滤掉高频信号。以下为拉普拉斯变换的结果,可以很好的解释为什么拉普拉斯变换是高通滤波器。其余的同理。
OpenCV傅里叶变换_第1张图片

你可能感兴趣的:(opencv,python)