OpenCV基于Python傅里叶变换及高通/低通滤波

傅里叶变换及高通/低通滤波

  • 目标
  • 前言
  • 1. Numpy中的傅里叶变换
  • 2. OpenCV中的傅里叶变换
  • 3. 为什么拉普拉斯算子是高通滤波器?
  • 4. 总结
  • 附加资源

目标

通过本篇文章的学习,你将学习到以下内容:

  • 使用OpenCV查找图像的傅立叶变换
  • 利用Numpy中可用的FFT函数
  • 傅立叶变换的某些应用
  • 学习到函数:cv.dft()cv.idft()

前言

傅立叶变换用于分析各种滤波器的频率特性。对于图像,使用2D离散傅里叶变换(DFT)查找频域。一种称为快速傅立叶变换(FFT)的快速算法用于DFT的计算。

对于正弦信号 x ( t ) = A sin ⁡ ( 2 π f t ) x(t) = A \sin(2 \pi ft) x(t)=Asin(2πft),我们可以说 f f f是信号的频率,如果采用其频域,则可以看到 f f f的尖峰。如果对信号进行采样以形成离散信号,我们将获相同的频域,但是在[-π,π]或[0,2π]范围内(对于N点DFT为[0,N])是周期性的,你可以将图像视为在两个方向上采样的信号。因此,在X 和Y方向都进行傅立叶变换,可以得到图像的频率表示。

更直观地说,对于正弦信号,如果幅度在短时间内变化如此之快,则可以说它是高频信号。如果变化缓慢,则为低频信号。你可以将相同的方式扩展到图像。图像中的振幅在哪里急剧变化?在边缘点或噪声。因此,可以说边缘和噪声是图像中的高频内容。如果幅度没有太大变化,则它是低频分量。(一些链接已添加到“其他资源”,其中通过示例直观地说明了频率变换)。

接下来,我们将学习如何找到傅立叶变换。

1. Numpy中的傅里叶变换

首先,我们将看到如何使用Numpy查找傅立叶变换。Numpy具有FFT软件包来执行此操作。np.fft.fft2()为我们提供了频率转换,它将是一个复杂的数组。它的第一个参数是输入图像,即灰度图像。第二个参数是可选的,它决定输出数组的大小。如果它大于输入图像的大小,则在计算FFT之前用零填充输入图像。如果小于输入图像,将裁切输入图像。如果未传递任何参数,则输出数组的大小将与输入的大小相同。

现在,一旦获得结果,零频率分量(DC分量)将位于左上角。如果要使其居中,则需要在两个方向上将结果都移动 N 2 \frac{N}{2} 2N。只需通过函数np.fft.fftshift()即可完成。(它更容易分析)。找到频率变换后,就可以找到幅度谱。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('./data/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, 'gray'), plt.title('Input image')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, 'gray'), plt.title('Magnitude spectrum')
plt.xticks([]), plt.yticks([])
plt.show()


从图中你可以在中心处看到更多白色区域,这表明低频内容更多

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

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('./data/messi5.jpg', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)

rows, cols = img.shape
crow, ccol = rows//2, cols//2
fshift[crow-30:crow+31, ccol-30:ccol+31] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)

plt.subplot(131), plt.imshow(img, 'gray')
plt.title('Input image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_back, '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基于Python傅里叶变换及高通/低通滤波_第1张图片

结果表明高通滤波是边缘检测操作。这就是我们在“图像渐变”一章中看到的。这也表明大多数图像数据都存在于频谱的低频区域。无论如何,我们已经看到了如何在Numpy中找到DFT,IDFT等。现在,让我们看看如何在OpenCV中进行操作。 如果您仔细观察结果,尤其是最后一张JET颜色的图像,您会看到一些伪像(我用红色箭头标记的一个实例)。它在那里显示出一些波纹状结构,称为振铃效应。这是由我们用于遮罩的矩形窗口引起的。此掩码转换为正弦形状,从而导致此问题。因此,矩形窗口不用于过滤。更好的选择是高斯窗口。

2. OpenCV中的傅里叶变换

OpenCV为此提供了cv.dft()cv.idft()函数。它返回与前一个相同的结果,但是有两个通道。第一个通道是结果的实部,第二个通道是结果的虚部。输入图像首先应转换为np.float32 。我们来看看怎么做。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('./data/messi5.jpg',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv.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()

注意: 您还可以使用cv.cartToPolar(),它在单个镜头中同时返回幅值和相位

现在我们要做DFT的逆变换。在上一节中,我们创建了一个HPF,这次我们将看到如何删除图像中的高频内容,即我们将LPF应用到图像中。它实际上模糊了图像。为此,我们首先创建一个高值1在低频部分,即我们过滤低频内容,0在高频区。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('./data/messi5.jpg',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

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 = dft_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(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基于Python傅里叶变换及高通/低通滤波_第2张图片

注意: 通常,OpenCV函数cv.dft()和cv.idft()比Numpy函数更快。但是Numpy函数更容易使用。

3. 为什么拉普拉斯算子是高通滤波器?

在一个论坛上也有人提出了类似的问题。问题是,为什么拉普拉斯变换是高通滤波器?为什么Sobel是HPF?等。第一个答案是关于傅里叶变换的。对于更大的FFT只需要拉普拉斯变换。分析下面的代码:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 没有缩放参数的简单均值滤波器
mean_filter = np.ones((3,3))
# 创建高斯滤波器
x = cv.getGaussianKernel(5,10)
gaussian = x*x.T
# 不同的边缘检测滤波器
# x方向上的scharr
scharr = np.array([[-3, 0, 3],
 [-10,0,10],
 [-3, 0, 3]])
# x方向上的sobel
sobel_x= np.array([[-1, 0, 1],
 [-2, 0, 2],
 [-1, 0, 1]])
# y方向上的sobel
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 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()

OpenCV基于Python傅里叶变换及高通/低通滤波_第3张图片
从图像中,你可以看到每种内核阻止的频率区域以及它允许经过的区域。从这些信息中,我们可以说出为什么每个内核都是HPF或LPF。

4. 总结

本篇文章介绍了图像处理中的傅里叶变换,主要介绍了Numpy中的傅里叶变换及其逆变换,OpenCV中的傅里叶变换及其逆变换,还解释了为什么拉普拉斯算子是高通滤波器的原因。

附加资源

  1. 傅里叶变换的直观解释:http://cns-alumni.bu.edu/~slehar/fourier/fourier.html by Steven Lehar
  2. 傅里叶变换:添加链接描述 at HIPR
  3. 图像中的频率域指什么?http://dsp.stackexchange.com/q/1637/818

如果文章对你有帮助,欢迎一键三连哦~~, 谢谢~~

你可能感兴趣的:(opencv,python,计算机视觉)