具有FFT问题的高斯模糊

时间:2011-08-16 13:29:05

标签: c++ image-processing fft convolution kissfft

我使用常规卷积来实现高斯模糊的当前实现。它对于小内核来说足够有效,但是一旦内核大小变大,性能就会受到影响。所以,我正在考虑使用FFT实现卷积。我从未有过与FFT相关的图像处理经验,所以我有几个问题。

  1. 基于2D FFT的卷积是否也可分为两个1D卷积?

    • 如果是,那么它是这样的 - 每行1DFFT,然后每列一次FFT,然后乘以2D内核,然后每列的逆变换和每行的逆变换?或者,在每次1D FFT变换后,我是否必须乘以1D内核?
  2. 现在我知道内核大小应该与图像大小相同(如果是1D则为行)。但它会如何影响边缘?我是否必须用零填充图像边缘?如果是这样,内核大小应该等于填充之前或之后的图像大小?

  3. 此外,这是一个C ++项目,我打算使用kissFFT,因为这是一个商业项目。欢迎您提出更好的选择。谢谢。

    编辑:感谢您的回复,但我还有一些问题。

    1. 我看到输入图像的虚部将全部为零。但输出虚部也会是零吗?我是否必须将高斯核与实部和虚部相乘?

    2. 我有相同图像的实例在不同比例下被模糊,即相同的图像被缩放到不同的尺寸并且在不同的内核尺寸下被模糊。每次缩放图像时是否必须执行FFT,还是可以使用相同的FFT?

    3. 最后,如果我想要显示FFT,我知道必须将一个对数滤波器应用于FFT。但我真的迷失在哪个部分应该用于可视化FFT?实部或虚部。

    4. 同样对于尺寸为512x512的图像,实部和虚部的大小是多少。它们的长度是否相同?

    5. 再次感谢您的详细回复。

2 个答案:

答案 0 :(得分:12)

  1. 2-D FFT是可分离的,除了必须乘以2D内核的2-D FFT之外,你是如何执行它的。如果您使用的是kissfft,则执行二维FFT的更简单方法是在Kissfft包的tools目录中使用kiss_fftnd。这将进行多维FFT。

  2. 内核大小不必是任何特定大小。如果内核小于图像,则只需在执行二维FFT之前将零填充到图像大小。你也应该对图像边缘进行零填充,因为在频域中通过乘法执行的计算实际上是循环卷积,结果在边缘处环绕。

  3. 总结(假设图像大小为M x N):

    1. 提出任意大小的二维内核(U x V)
    2. 将内核零填充到(M + U-1)x(N + V-1)
    3. 取内核的2-D fft
    4. 将图像零填充到(M + U-1)x(N + V-1)
    5. 拍摄图像的二维FFT
    6. 通过图像的FFT乘以内核的FFT
    7. 对结果进行逆二维FFT
    8. 修剪边缘的垃圾
    9. 如果您在不同的图像上多次执行相同的过滤器,则不必每次都执行1-3次。

      注意:内核大小必须相当大才能比直接计算卷积更快。另外,你是否实现了直接卷积,利用了二维高斯滤波器可分离的事实(see this几段到“力学”部分)?也就是说,您可以在行和列上执行二维卷积作为一维卷积。我发现这比大多数基于FFT的方法要快,除非内核非常大。

      对编辑的回应

      1. 如果输入是真实的,除极少数情况外,输出仍然很复杂。高斯内核的FFT也很复杂,因此乘法必须是复数乘法。当您执行逆FFT时,输出应该是真实的,因为您的输入图像和内核是真实的。输出将以复数数组的形式返回,但虚数组件应为零或非常小(浮点错误),并且可以丢弃。

      2. 如果您使用相同的图像,则可以重复使用图像FFT,但您需要根据最大内核大小进行零填充。您将不得不计算所有不同内核的FFT。

      3. 对于可视化,应使用复杂输出的大小。当较大的组件以线性标度淹没时,对数标度仅有助于可视化输出的较小组件。 Decibel比例经常被使用,由20*log10(abs(x))10*log10(x*x')给出,它们是等价的。 (x是复数fft输出,x'x)的复共轭。

      4. FFT的输入和输出大小相同。此外,实部和虚部的大小相同,因为一个实数和一个虚数值构成一个样本。

答案 1 :(得分:5)

请记住,空间中的卷积等效于频域中的乘法。这意味着一旦你执行了图像和掩码(内核)的FFT,你只需要进行逐点乘法,然后进行结果的IFFT。话虽如此,这里有几句谨慎的话。

您可能知道在数字信号处理中,我们经常使用circular convolution,而不是linear convolution。这是因为好奇的periodicity。简单来说,这意味着DFT(和FFT是计算效率高的变体)假设您的信号是周期性的,当您以这种方式过滤信号时 - 假设您的图像是 N x M 像素 - 它在( N m < N 处的邻居或像素处获取像素(1, m ) / em>)对于某些 m &lt; M 。你几乎把自己的信号包裹起来。这意味着你的高斯蒙版将最右边的像素与最左边的像素进行平均,同样适用于顶部和底部。这可能是也可能不是,但总的来说,无论如何都必须处理边缘伪影。然而,在处理FFT乘法时更容易忘记这个问题,因为问题不再明显。有很多方法可以解决这个问题。最好的方法是简单地用零填充图像,然后再删除多余的像素。

在频域中使用高斯滤波器的一个非常巧妙的事情是你永远不必真正采用它的FFT。众所周知,高斯的傅里叶变换是高斯(一些技术细节here)。您只需要用零填充图像(顶部和底部),在频域中生成高斯,将它们相乘并采用IFFT。然后你就完成了。

希望这有帮助。