我正在尝试使用FFT属性来获得2D函数的第i个导数 - 特别是2D高斯函数。我也想在MATLAB中用数字做这个。
实际上,我对我正在做的事情一无所知,但我在互联网上阅读了很多内容并且所有MATLAB都有帮助,似乎没什么可以帮助我的,所以我会在这里问一下
这是我到目前为止所拥有的:
h = fspecial('gaussian', size(imr), 10);
imshow(h, []);
G = fft2(h);
这是imr
大小的高斯的0阶导数,或512 x 512.我知道我必须做反转并乘以某些东西,但我不知道是什么。
任何人都可以给我一个关于使用FFT属性获得2D高斯函数的第i个导数的线索吗?
提前致谢
答案 0 :(得分:15)
由于我对频域衍生操作的理解存在一个小但基本的缺陷,这篇文章发生了重大变化。自上一次迭代以来,很多帖子都发生了变化。
我要感谢Luis Mendo帮助我调试为什么这个除了在帖子的原始版本中提供的其他图像之外没有其他图像。
当涉及离散时间数据时,没有衍生物这样的东西。衍生工具是一种分析工具,仅存在于连续时间中。在离散时间内,我们只能使用差异进行近似。因此,我将假设您的意思是差异操作而不是衍生操作。导数的最常见近似之一是使用前向差分运算。具体而言,对于1D离散序列x[n]
,应用前向差分运算的输出序列y[n]
定义为:
y[n] = x[n+1] - x[n], for n = 1, 2, ..., M-1
M
是离散序列中的样本总数。当然,由于前向差异操作的性质,价值会减少一个。
要在频域中完成相同的操作,必须使用离散傅立叶变换属性中的shift theorem。具体而言,给定信号x[n]
的DFTed版本X(k)
,以下属性将时域和频域关联起来:
此处,i
表示复数,或-1的平方根,k
是频域中的角频率。 F^{-1}
是信号X(k)
的逆傅里叶变换,它是时域信号x[n]
的傅里叶变换。 M
也是信号x[n]
的长度,m
是您想要移动信号的移位量。因此,这就是说如果你想通过移位m
位置来计算移位操作,你只需要逐个元素进行傅立叶变换,将每个分量乘以exp(-i*2*pi*m*k/M)
其中k
1}}是傅立叶变换中一个点的索引,并采用该中间结果的逆傅立叶变换。
请特别注意傅里叶变换的最后一个元素是没有意义的,因为差分运算会产生一个元素少一个的信号,因此从该结果中删除最后一个元素非常重要。
如上所述,为了计算导数的近似值,我们需要找到y[n]
或Y(k)
的傅立叶变换,并且可以这样计算:
请注意,班次为-1
,x[n+1] = x[n-(-1)]
。因此,您可以计算傅立叶变换,将每个对应的值乘以exp(i*2*pi*k/M) - 1
并取相反值。
进入2D并不是一件容易的事。在这里,我将假设我们在两个方向上做差异操作 - 水平和垂直。请记住,我们有两个表示水平和垂直空间坐标的变量。我们还将其称为空间域,而不是时域,因为变量符合我们在2D空间中的位置。按照上面的表述,进入2D非常简单:
此处,u
和v
表示2D离散差分运算y[u,v]
的空间坐标。 U
和V
是2D频率成分,M
和N
是2D信号的列数和行数。特别注意您实际上正在进行水平差异操作,然后进行垂直差分操作。具体来说,您将独立地为每一行执行水平差异操作,然后分别对每列执行垂直差异操作。事实上,订单并不重要。你可以按照你选择的顺序做一个跟随另一个。需要注意的重要一点是,信号的二维傅里叶变换保持不变,并且您只是将每个值乘以一些复数常数。但是,您需要确保删除结果的最后一行和最后一列,因为每次操作都会产生比每行和每列的原始长度小一个点的信号,这是由于我们所讨论的差异操作观察。
假设您已经拥有该函数的FFT,您只需要获取每个2D空间坐标并乘以(exp(i*2*pi*U/M) - 1)*(exp(i*2*pi*V/N) - 1)
。对于存储在(U,V)
的FFT的每个2D分量,我们使用这些相同的频率坐标,并将该位置乘以前两个事物的乘积。之后,您将采用逆FFT,我们将其与实际的空间域导数进行比较。我们应该看到他们是一样的。
请注意,当您执行2D FFT时,频率归一化,使得它们在两个维度中均在[-1,1]
之间。在显示两个域中衍生操作的等效性时,我们需要考虑到这一点。此外,为了使事情变得更简单,如果您移动频率成分使它们出现在图像的中心中,那么也是谨慎的做法。当执行2D FFT时,组件被定位成使得DC分量或原点位于图像的左上角。让我们使用fftshift
将所有内容转移到中心。
现在,让我们从一些小事做起。为了这个过程,我们假设每个维度的大小是奇数,以允许通过fftshift
移动频率更容易和明确。假设我们有一个101 x 101高斯滤波器,标准差为10,我们采用FFT,然后fftshift
。所以:
h = fspecial('gaussian', [101 101], 10);
hf = fftshift(fft2(h));
如果我们想找到导数,我们定义一个频域点网格,它跨越频域中图像的大小,从0
开始,现在是中心。我们可以通过meshgrid
完成此操作,因此:
[U,V] = meshgrid(-50:50, -50:50);
通常,此水平和垂直坐标的水平范围为[-floor(cols/2),floor(cols/2)]
,垂直方向的[-floor(rows/2),floor(rows/2)]
范围为(0,0)
。请注意,上面的网格与图像具有相同的尺寸,但中心位于x
,并且我们在两个维度上都从-50到50。
现在,在两个方向上进行频域差分运算。我们为 {/ strong> y
和hf2 = (exp(1i*2*pi*U/101) - 1).*(exp(1i*2*pi*V/101) - 1).*hf;
执行此操作,因此两个方向的一阶导数:
real
请注意,由于FFT的频率被归一化,我们必须通过除以101来归一化频率。 101 x 101是由于图像的大小。如果我们想要回到时域,只需采用逆FFT。但是,在进行逆FFT之前,我们需要撤消我们对ifftshift
所做的转换。我们还使用out_freq = real(ifft2(ifftshift(hf2)));
来消除由于数值不准确而导致的任何残余复值分量。您会看到输出是复数值,但虚数组件的大小非常小,我们可以忽略它们。就这样:
out_freq = out_freq(1:end-1,1:end-1);
请记住同时删除最后一行和一列:
h
如果您想将此输出与过滤器out_spatial = diff(diff(h, 1, 1), 1, 2);
的时域差异操作进行比较,我们可以通过在行上使用diff
来完全执行此操作,然后使用此结果,我们将结束列。就这样:
figure;
subplot(1,2,1);
imshow(out_spatial, []);
title('Derivative from spatial domain');
subplot(1,2,2);
imshow(out_freq, []);
title('Derivative from frequency domain');
第二个参数表示差异操作的顺序。这是一阶差异,因此水平和垂直差异操作都设置为1.第三个参数告诉您正在进行差分的维度。我们首先在列上执行此操作,然后在行上应用中间结果。然后我们可以展示它们的样子:
Z
我们得到:
酷!这与我所知的高斯导数一致。
作为一点奖励,我想向您指出康奈尔大学的一些精彩幻灯片:http://www.cs.cornell.edu/courses/cs6670/2011sp/lectures/lec02_filter.pdf
具体来说,如果您在3D中查看它,高斯看起来就是这样,我们有水平和垂直坐标,m
值是3D中高斯的高度:
所以,如果我们独立地采用两个方向的导数就会发生这种情况。顶部显示了空间发生的情况,底部显示了如果我们直接在鸟瞰图上看到这一点会发生什么:
表面的最负面部分用黑色绘制,表面最正面的部分用白色绘制,其他所有部分都按照强度进行缩放......所以,如果我们采用空间导数在一个方向,然后使用这个中间结果并采取另一个方向的空间导数,你会看到我们会得到我们在上面看到的。使用不同的n
和{{1}}值进行游戏,看看你得到了什么。我在实践中经常使用的这个属性,但它确实很有用。