欢迎光临散文网 会员登陆 & 注册

【第23节】OpenCV傅里叶变换

2022-11-30 14:21 作者:海鸥之道  | 我要投稿

目标

  • 使用OpenCV对图像进行傅里叶变换

  • 使用Numpy中FFT(快速傅里叶变换)函数

  • 傅里叶变换的一些用处

  • 我们将要学习的函数有:CV2.dft(),CV2.idft()等。

原理

        傅里叶变换经常被用来分析不同滤波器的频率特性。我们可以使用2D离散傅里叶变换(DFT)分析图像的频域特性。实现DFT的一个快速算法被称为快速傅里叶变换(FFT)。关于傅里叶变换的细节知识可以在任意一本图像处理或信号处理的书中找到。请查看本小节中更多资源部分。对于一个正弦信号: ax(t)= Asin (2πft),它的频率为f,如果把这个信号转到它的频域表示,我们会在频率f中看到一个峰值。如果我们的信号是由采样产生的离散信号好组成,我们会得到类似的频谱图,只不过前面是连续的,现在是离散。你可以把图像想象成沿着两个方向采集的信号。所以对图像同时进行X方向和Y方向的傅里叶变换,我们就会得到这幅图像的频域表示(频谱图)。.

        更直观一点,对于一个正弦信号,如果它的幅度变化非常快,我们可以说他是高频信号,如果变化非常慢,我们称之为低频信号。你可以把这种想法应用到图像中,图像那里的幅度变化非常大呢?边界点或者噪声。所以我们说边界和噪声是图像中的高频分量(注意这里的高频是指变化非常快,而非出现的次数多)。如果没有如此大的幅度变化我们称之为低频分量。

        现在我们看看怎样进行傅里叶变换。

1、Numpy中的傅里叶变换

        首先我们看看如何使用Numpy进行傅里叶变换。Numpy中的FFT包可以帮助我们实现快速傅里叶变换。函数np.fft.fft2()可以对信号进行频率转换,输出结果是一个复杂的数组。本函数的第--个参数是输入图像,要求是灰度格式。第二个参数是可选的,决定输出数组的大小。输出数组的大小和输入图像大小一样。如果输出结果比输入图像大,输入图像就需要在进行FFT前补0。如果输出结果比输入图像小的话,输入图像就会被切割。

        现在我们得到了结果,频率为0的部分(直流分量)在输出图像的左上角。如果想让它(直流分量)在输出图像的中心,我们还需要将结果沿两个方向平移。函数np.f.fftshift()可以帮助我们实现这一步。(这样更容易分析)。进行完频率变换之后,我们就可以构建振幅谱了。

结果如下:

        我们可以看到输出结果的中心部分更白(亮),这说明低频分量更多。现在我们可以进行频域变换了,我们就可以在频域对图像进行一些操作了,例如高通滤波和重建图像( DFT的逆变换)。比如我们可以使用一个60x60的矩形窗口对图像进行掩模操作从而去除低频分量。然后再使用函数np.fft.ifftshift()进行逆平移操作,所以现在直流分量又回到左上角了,左后使用函数np.ifft2()进行FFT逆变换。同样又得到一堆复杂的数字,我们可以对他们取绝对值:

结果如下:

        上图的结果显示高通滤波其实是一种边界检测操作。这就是我们在前面图像梯度那一章看到的。同时我们还发现图像中的大部分数据集中在频谱图的低频区域。我们现在已经知道如何使用Numpy进行DFT和IDFT了,接着我们来看看如何使用OpenCV进行这些操作。

        如果你观察仔细的话,尤其是最后一章JET颜色的图像,你会看到-些不自然的东西(如我用红色箭头标出的区域)。看上图那里有些条带装的结构,这被成为振铃效应。这是由于我们使用矩形窗口做掩模造成的。这个掩模被转换成正弦形状时就会出现这个问题。所以一般我们不适用矩形窗口滤波。最好的选择是高斯窗口。

2、OpenCV中的傅里叶变换

        OpenCV中相应的函数是CV2.dft()和CV2.idft()。和前面输出的结果一样,但是是双通道的。第一个通道是结果的实数部分,第二个通道是结果的虚数部分。输入图像要首先转换成np.float32格式。我们来看看如何操作。

注意:你可以使用函数CV2.cartToPolar(),它会同时返回幅度和相位。

        现在我们来做逆DFT。在前面的部分我们实现了一个HPF(高通滤波),现在我们来做LPF(低通滤波)将高频部分去除。其实就是对图像进行模糊操作。首先我们需要构建一个掩模,与低频区域对应的地方设置为1,与高频区域对应的地方设置为0。

结果如下:

注意: 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()使用。让我们一起使用IPython的魔法命令%timeit来测试一下吧。

看到了吧,数组的大小从( 342,548 )变成了( 360,576 )。现在我们为它补0,然后看看性能有没有提升。你可以创建一个大的0数组,然后把我们的数据拷贝过去,或者使用函数CV2.copyMakeBoder()。

或者:

现在我们看看 Numpy 的表现:

速度提高了 4 倍。我们再看看 OpenCV 的表现:

也提高了4倍,同时我们也会发现OpenCV的速度是Numpy的3倍。你也可以测试一下逆FFT的表现。


【第23节】OpenCV傅里叶变换的评论 (共 条)

分享到微博请遵守国家法律