【问题标题】:Matlab - FFT of Gaussian - EquivalencyMatlab - 高斯的 FFT - 等价
【发布时间】:2023-03-14 19:09:01
【问题描述】:

简单的问题:

我在 Matlab 中绘制了一个具有一定分辨率的二维高斯函数。我用方差或 sigma = 1.0 进行测试。我想将它与 FFT(Gaussian) 的结果进行比较,这应该会产生另一个方差为 (1./sigma) 的高斯。由于我使用 sigma = 1.0 进行测试,我认为我应该获得两个等效的 2D 内核。

g1FFT = buildKernel(rows, cols, mu, sigma)    % uses normpdf over arbitrary resolution (rows, cols, 3) with the peak in the center

构建内核:

function result = buildKernel(rows, cols, mu, sigma)  

result = zeros(rows, cols, 3);

center_w = floor(cols / 2);
center_h = floor(rows / 2);

for i = 1:rows
    for j = 1:cols
        distance = sqrt((center_w - j).^2 + (center_h - i).^2);
        g_val = normpdf(distance, mu, sigma);
        result(i, j, :) = g_val;
    end
end

% normalize so that kernel sums to 1
sumKernel = sum(result(:));
result = result ./ sumKernel;    

end

我正在使用 mu = 0.0(始终)和方差或 sigma = 1.0 进行测试。我想将它与 FFT(Gaussian) 的结果进行比较,这应该会产生另一个方差为 (1./sigma) 的高斯。

g1FFT = circshift(g1FFT, [rows/2, cols/2, 0]);       % fft2 expects center to be in corners 
freq_G1 = fft2(g1FFT);
freq_G1 = circshift(freq_G1, [-rows/2, -cols/2, 0]); % shift back to center, for comparison's sake

由于我使用 sigma = 1.0 进行测试,我认为我应该得到两个等效的 2D 内核,因为如果 sigma = 1.0,那么 1.0/sigma = 1.0。因此,g1FFT 将等于 freq_G1。

但是,我没有。即使在归一化之后,它们也有不同的大小。有什么我遗漏的吗?

【问题讨论】:

  • 请提供您的所有代码以重现您的错误。您是正确的,因为高斯的 FFT 是另一个高斯,但是没有代码可以向我们展示您所做的事情,我们无法判断您是否正确执行。
  • 你的buildKernel函数是什么样的?
  • 真的很简单,但添加了代码。
  • 为什么你的内核是 3D 矩阵?你说它是一个二维高斯函数,但是有三个通道。
  • 您可能已经发现了这一点,但您不能直接将连续时间傅里叶变换对应用于离散时间/DFT。 users.ece.gatech.edu/mrichard/… 显示离散情况的等价性取决于标准偏差,即 samples

标签: matlab fft gaussian


【解决方案1】:

为简单起见,我将首先介绍一维信号的情况。对于多维情况,也可以进行类似的观察。

连续时间高斯信号的傅立叶变换本身就是一个高斯函数,如this table 所示。可以注意到,时域中的高斯越宽,频域中变换后的高斯越窄,对于 mu=0 和 sigma=1/sqrt(2π)(对应于 a=1/(2*sigma^ 2)=上面变换表中的π),连续时间函数的傅里叶变换

将是类似的功能(仅发生变量变化):

这很好,但这是针对连续时间信号的,我们对离散时间信号非常感兴趣。 不幸的是,正如wikipedia 中所指出的,通过对连续时间高斯函数进行采样而获得的核的离散傅里叶变换本身并不是采样的高斯函数。 幸运的是,这种关系通常仍然是近似正确的(无需过多详细说明,它需要时域内核足够宽但又不能太宽,以至于频域近似也足够宽以使关系也近似逆变换为真)。在这种情况下,离散时间信号的周期扩展(周期为N)的离散傅里叶变换

其中 mu=0 和 sigma=sqrt(N/2π) 可以用类似的函数来近似(直到一个比例因子和变量的变化):

然后您可以修改 buildKernel 以分别支持沿行和列的不同标准差 sqrt(rows/2π) 和 sqrt(cols/2π):

function result = buildKernel(rows, cols, mu, sigma)  

  if (length(mu)>1)
    mu_h = mu(1);
    mu_w = mu(2);
  else
    mu_h = mu;
    mu_w = mu;
  endif
  if (length(sigma)>1)
    sigma_h = sigma(1);
    sigma_w = sigma(2);
  else
    sigma_h = sigma;
    sigma_w = sigma;
  endif

  center_w = mu_w + floor(cols / 2);
  center_h = mu_h + floor(rows / 2);

  r = transpose(normpdf([0:rows-1],center_h,sigma_h));
  c = normpdf([0:cols-1],center_w,sigma_w);
  result = repmat(r * c, [1 1 3]);

  % normalize so that kernel sums to 1
  sumKernel = sum(result(:));
  result = result ./ sumKernel;    

end

您可以使用它来获取其 FFT 是其自身的缩放版本的内核。换句话说,使用获得的内核

g1FFTin = buildKernel(rows, cols, mu, [sqrt(rows/2/pi) sqrt(cols/2/pi)]);

这样freq_G1(在您发布的代码中计算得出)几乎等于g1FFTin * sqrt(rows*cols)

最后考虑到您的意图实际上只是为了测试内核的 FFT 也是(近似)高斯分布,您可能希望将具有标准偏差 sigma 的任意内核的 FFT 与 另一个 进行比较> 直接在频域中计算的适当缩放的高斯核。换句话说,假设通过以下方式获得空间域内核:

g1FFTin = buildKernel(rows, cols, mu, sigma);

用相应的频域表示获得:

g1FFT   = circshift(g1FFTin, [rows/2, cols/2, 0]);
freq_G1 = fft2(g1FFT);
freq_G1 = circshift(freq_G1, [-rows/2, -cols/2, 0]);

然后freq_G1 可以与另一个在频域中直接计算的适当缩放的高斯核进行比较:

freq_G1_approx = (rows*cols/(2*pi*sigma^2))*buildKernel(rows, cols, mu, [rows cols]/(2*pi*sigma));

【讨论】:

  • 感谢您的详细解释@SleuthEye。我想我大部分都明白。我尝试了您的第一个示例(sigma 为[sqrt(rows/2/pi) sqrt(cols/2/pi)]),发现通过sqrt(rows*cols) 缩放此内核大约等于使用fft 计算的结果。但是,第二个示例对我不起作用。通过(rows*cols/(2*pi*sigma.^2)) 缩放下一个内核并没有(到目前为止)等于使用 fft 计算的结果。有什么建议?正如你所说,我希望这适用于任意 sigma。
  • @kdottiemo 只是为了澄清它应该适用于大于 ~0.5 且小于 ~min(rows,cols)/3 的任意标准偏差。此外,如果我的帖子中不清楚,freq_G1_approx 应该与buildKernel(rows,cols,mu,sigma) 上的 fft 的结果进行比较(后面是circshift)。因此,第二个示例将使用 两个buildKernel 的不同调用:一个在空间域中构造,另一个直接在频域中构造。
  • 再次感谢。但我想我正在做你所说的,如果我仍然误解,请告诉我......%First kernelsigma = 2.0;g1 = buildKernel(rows, cols, 0.0, [rows, cols]/(2*pi*sigma));g1 = buildKernel(rows, cols, 0.0, [rows, cols]/(2*pi*sigma));scale = ones(size(g1)).*scale;scale = ones(size(g1)).*scale;new_G1 = g1.*scale;%Next kernelg1FFT = circshift(g1, [rows/2, cols/2, 0]);@ 987654353@freq_G1 = circshift(freq_G1, [-rows/2, -cols/2, 0]);%So, in this case, freq_G1 != new_G1
  • 查看更新。空间和频率域中的内核宽度仅与第一个示例的非常具体的情况相匹配。对于第二个例子,宽度不匹配(所以内核不是它自己的变换)。根据您的评论,保留第一个内核,但更改 %Next kernel g1prime = buildKernel(rows,cols,mu,sigma); g1primeFFT = circshift(g1prime,...; freq_G1 = fft2(g1primeFFT); freq_G1 = circshift.... 然后 freq_G1 == new_G1
  • 谢谢;清除它。这对我来说太违反直觉了——所以要实现输入 sigma 所期望的形状,sigma 本身实际上变成了 [rows cols]/2*pi*(sigma)。我这么说是不是过于简单化了?并且在给定相同 sigma 的情况下与其频率空间表示的关系是 ((rows * cols)/2*pi*(sigma^2)) 的缩放比例?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 2019-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
相关资源
最近更新 更多