具有FFT问题的高斯模糊

rwb*_*rwb 5 c++ fft image-processing convolution kissfft

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

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

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

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

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

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

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

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

  4. 对于尺寸为512x512的图像,实部和虚部的大小也是如此.它们的长度是一样的吗?

再次感谢您的详细回复.

Jas*_*n B 13

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

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

总结一下(假设图像大小为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. 修剪边缘的垃圾

如果您在不同的图像上多次执行相同的过滤器,则不必每次都执行1-3次.

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

对编辑的回应

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

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

  3. 对于可视化,应使用复杂输出的大小.当较大的组件以线性标度淹没时,对数标度仅有助于可视化输出的较小组件.的分贝刻度经常使用,并通过任一给定的20*log10(abs(x))10*log10(x*x')哪些是等价的.(x是复杂的fft输出,x'是复杂的共轭x).

  4. FFT的输入和输出大小相同.此外,实部和虚部将具有相同的尺寸,因为一个实数和一个虚数值形成单个样本.


Pho*_*non 5

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

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

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

希望这可以帮助.