如何从二维FFT计算波数域坐标
我有一个二维数组,里面存的是复杂数字,这些数字代表了在真实空间中沿着一个平面测量的潜在场。假设这个数组有128行和128列,总面积是500米乘500米。数组中的每个单元格代表空间中的一个点,坐标用x和y表示。
当我使用scipy.fftpack里的二维快速傅里叶变换(2d FFT)对这个二维数组进行处理时,我得到了同样的信息,只不过是以波的形式表现出来。那么,我该如何计算输出数组中点的波域坐标kx和ky呢?
3 个回答
0
我在下面的FORTRAN代码中找到了一个子程序,然后尝试把它实现成MATLAB的函数。请给我一些意见,看看我的翻译是否正确。
接下来,这是FORTRAN的子程序:
subroutine kvalue(i,j,nx,ny,dkx,dky,kx,ky)
c Subroutine KVALUE finds the wavenumber coordinates of one
c element of a rectangular grid from subroutine FOURN.
c
c Input parameters:
c i - index in the ky direction,
c j - index in the kx direction.
c nx - dimension of grid in ky direction (a power of two).
c ny - dimension of grid in kx direction (a power of two).
c dkx - sample interval in the kx direction,
c dky - sample interval in the ky direction,
c
c Output parameters:
c kx - the wavenumber coordinate in the kx direction,
c ky - the wavenumber coordinate in the ky direction,
c
real kx,ky
nyqx=nx/2+l
nyqy=ny/2+l
if(j.le.nyqx)then
kx=(j-l)*dkx
else
kx=(j-nx-l)*dkx
end if
if(i.le.nyqy)then
ky=(i-l)*dky
else
ky=(i-ny-l)*dky
end if
return
end
我把它翻译成了MATLAB函数:
function [kx,ky]=kvalue(gz,nx,ny,dkx,dky)
nyq_x=nx/2+1;
nyq_y=ny/2+1;
for i=1:length(gz)
for j=1:length(gz)
if j <= nyq_x
kx(j)=(j-1)*dkx;
else
kx(j)=(j-nx-1)*dkx;
end
if i <= nyq_y
ky(i)=(i-1)*dky;
else
ky(i)=(i-ny-1)*dky;
end
end
end
谢谢你。
2
嗯,看来如果我使用FFT函数,直流成分(DC)会出现在第一个元素。而在你的情况下,频率之间的间隔是1/500米。所以下面这个(虽然不太简洁)的小代码片段可以帮你得到频率轴:
meter = 1.0
L = 500.0 * meter
N = 128
dF = 1.0 / L
freqs = arange(0, N/L, dF) # array of spatial frequencies.
当然,这些频率是以每米多少个周期来表示的,而不是每米多少弧度。如果我想要kx和ky是以每米多少弧度的空间频率数组,我只需要这样写:
kx = 2*pi*freqs
ky = 2*pi*freqs
(假设我已经导入了像arange和pi这样的东西)。
编辑
Stu提到的关于奈奎斯特频率以上的频率是个好点子,你可能更愿意把它们看作是负频率(我通常是这样想的,但在代码里不是)。你总是可以这样做:
freqs[freqs > 0.5*N/(2*L)] -= N/L
不过如果你真的想要负频率,你可能还想试试fftshift,这又是另一个复杂的问题。
10
这里有一些代码,可以完整地展示我遇到的问题和我找到的解决办法。
from numpy import linspace , arange , reshape ,zeros
from scipy.fftpack import fft2 , fftfreq
from cmath import pi
# create some arbitrary data
some_data = arange(0.0 , 16384.0 , dtype = complex)
# reshape it to be a 128x128 2d grid
some_data_grid = reshape(some_data , (128 , 128) )
# assign some real spatial co-ordinates to the grid points
# first define the edge values
x_min = -250.0
x_max = 250.0
y_min = -250.0
y_max = 250
# then create some empty 2d arrays to hold the individual cell values
x_array = zeros( (128,128) , dtype = float )
y_array = zeros( (128,128) , dtype = float )
# now fill the arrays with the associated values
for row , y_value in enumerate(linspace (y_min , y_max , num = 128) ):
for column , x_value in enumerate(linspace (x_min , x_max , num = 128) ):
x_array[row][column] = x_value
y_array[row][column] = y_value
# now for any row,column pair the x_array and y_array hold the spatial domain
# co-ordinates of the associated point in some_data_grid
# now use the fft to transform the data to the wavenumber domain
some_data_wavedomain = fft2(some_data_grid)
# now we can use fftfreq to give us a base for the wavenumber co-ords
# this returns [0.0 , 1.0 , 2.0 , ... , 62.0 , 63.0 , -64.0 , -63.0 , ... , -2.0 , -1.0 ]
n_value = fftfreq( 128 , (1.0 / 128.0 ) )
# now we can initialize some arrays to hold the wavenumber co-ordinates of each cell
kx_array = zeros( (128,128) , dtype = float )
ky_array = zeros( (128,128) , dtype = float )
# before we can calculate the wavenumbers we need to know the total length of the spatial
# domain data in x and y. This assumes that the spatial domain units are metres and
# will result in wavenumber domain units of radians / metre.
x_length = x_max - x_min
y_length = y_max - y_min
# now the loops to calculate the wavenumbers
for row in xrange(128):
for column in xrange(128):
kx_array[row][column] = ( 2.0 * pi * n_value[column] ) / x_length
ky_array[row][column] = ( 2.0 * pi * n_value[row] ) / y_length
# now for any row,column pair kx_array , and ky_array will hold the wavedomain coordinates
# of the correspoing point in some_data_wavedomain
我知道这可能不是最有效的方法,但希望大家能容易理解。我希望这能帮助到某些人,避免一些小烦恼。