2017-08-04 105 views
1

注意:這個問題是建立在我的另一個問題: Two dimensional FFT using python results in slightly shifted frequency二維FFT限制

我有一些數據,基本上是一個函數E(X,Y)與(X,Y)作爲R^2的(離散)子集映射到實數。對於(x,y)平面,我有x-和y-方向(0,2)中的數據點之間的固定距離。我想使用python使用二維快速傅里葉變換(FFT)分析我的E(x,y)信號的頻譜。

據我所知,無論我的信號中實際包含哪些頻率,使用FFT,我只能看到低於Nyquisit極限Ny的信號,即Ny =採樣頻率/ 2。在我的情況下我有一個0.2的實際間距,導致採樣頻率爲1/0,2 = 5,因此我的Nyquisit限制是Ny = 5/2 = 2,5。

如果我的信號的頻率高於Nyquisit限制,它們將被「摺疊」回Nyquisit域,導致錯誤結果(混疊)。但即使我可能採樣頻率太低,理論上不可能看到任何高於Niquisit限制的頻率,是正確的嗎?

所以這裏是我的問題:分析我的信號應該只會導致頻率最高2.5,但我cleary得到更高的頻率。鑑於我對這裏的理論非常肯定,我的代碼中必然存在一些錯誤。我公司將提供縮短代碼版本,只爲這個問題提供必要的信息:

simulationArea =... # length of simulation area in x and y direction 
x = np.linspace(0, simulationArea, numberOfGridPointsInX, endpoint=False) 
y = x 
xx, yy = np.meshgrid(x, y) 
Ex = np.genfromtxt('E_field_x100.txt') # this is the actual signal to be analyzed, which may have arbitrary frequencies 
FTEx = np.fft.fft2(Ex) # calculating fft coefficients of signal 
dx = x[1] - x[0] # calculating spacing of signals in real space. 'print(dx)' results in '0.2' 

sampleFrequency = 1.0/dx 
nyquisitFrequency = sampleFrequency/2.0 
half = len(FTEx)/2 

fig, axarr = plt.subplots(2, 1) 

im1 = axarr[0, 0].imshow(Ex, 
         origin='lower', 
         cmap='jet', 
         extent=(0, simulationArea, 0, simulationArea)) 
axarr[0, 0].set_xlabel('X', fontsize=14) 
axarr[0, 0].set_ylabel('Y', fontsize=14) 
axarr[0, 0].set_title('$E_x$', fontsize=14) 
fig.colorbar(im1, ax=axarr[0, 0]) 

im2 = axarr[1, 0].matshow(2 * abs(FTEx[:half, :half])/half, 
          aspect='equal', 
          origin='lower', 
          interpolation='nearest') 
axarr[1, 0].set_xlabel('Frequency wx') 
axarr[1, 0].set_ylabel('Frequency wy') 
axarr[1, 0].xaxis.set_ticks_position('bottom') 
axarr[1, 0].set_title('$FFT(E_x)$', fontsize=14) 
fig.colorbar(im2, ax=axarr[1, 0]) 

這樣做的結果是:

enter image description here

這怎麼可能?當我對相當簡單的信號使用相同的代碼時,它工作得很好(例如,在特定頻率的x或y方向上的正弦波)。

+0

底部情節的軸線只是像素,而不是頻率!!!還有一些關於使用2D FFT需要了解的約定,比如如何構建X和Y頻率向量等,但在這個答案中,我給出了一個非常簡單的例子,它使用了復指數和二維FFT,但是在Matlab:https://stackoverflow.com/a/39774496/500207看看你是否可以適應Python,如果沒有,讓我知道,我會移植它。 –

+0

在Python中它更容易一些,因爲Numpy提供了'fftfreq'函數。如果你可以上傳一些(真實的或假的)Ex'數據和一組完整的'simulationArea'值,那麼向你展示這應該是什麼樣子會很容易和令人信服。 –

+0

謝謝你的回答!在談到stackoverflow.com/a/39774496/500207你的答案,我如何在Python正確使用「fftreq」,以獲得x和y的適當頻率的空間?我想它可以用來轉換'Nfft = 4 * 2。^ nextpow2(size(im)); imF = fftshift(fft2(im,Nfft(1),Nfft(2)))/ numel(im);'到Python代碼。 – Alienbash

回答

1

好的,我們開始吧!這裏有幾個簡單的功能和完整的例子,你可以用:它有相關的繪圖和數據生成,但第一功能額外的克魯夫特的一點點,makeSpectrum展示瞭如何使用fftfreqfftshiftfft2達到什麼樣的你想。如果您有任何問題,請告訴我。

import numpy as np 
import numpy.fft as fft 
import matplotlib.pylab as plt 


def makeSpectrum(E, dx, dy, upsample=10): 
    """ 
    Convert a time-domain array `E` to the frequency domain via 2D FFT. `dx` and 
    `dy` are sample spacing in x (left-right, 1st axis) and y (up-down, 0th 
    axis) directions. An optional `upsample > 1` will zero-pad `E` to obtain an 
    upsampled spectrum. 

    Returns `(spectrum, xf, yf)` where `spectrum` contains the 2D FFT of `E`. If 
    `Ny, Nx = spectrum.shape`, `xf` and `yf` will be vectors of length `Nx` and 
    `Ny` respectively, containing the frequencies corresponding to each pixel of 
    `spectrum`. 

    The returned spectrum is zero-centered (via `fftshift`). The 2D FFT, and 
    this function, assume your input `E` has its origin at the top-left of the 
    array. If this is not the case, i.e., your input `E`'s origin is translated 
    away from the first pixel, the returned `spectrum`'s phase will *not* match 
    what you expect, since a translation in the time domain is a modulation of 
    the frequency domain. (If you don't care about the spectrum's phase, i.e., 
    only magnitude, then you can ignore all these origin issues.) 
    """ 
    zeropadded = np.array(E.shape) * upsample 
    F = fft.fftshift(fft.fft2(E, zeropadded))/E.size 
    xf = fft.fftshift(fft.fftfreq(zeropadded[1], d=dx)) 
    yf = fft.fftshift(fft.fftfreq(zeropadded[0], d=dy)) 
    return (F, xf, yf) 


def extents(f): 
    "Convert a vector into the 2-element extents vector imshow needs" 
    delta = f[1] - f[0] 
    return [f[0] - delta/2, f[-1] + delta/2] 


def plotSpectrum(F, xf, yf): 
    "Plot a spectrum array and vectors of x and y frequency spacings" 
    plt.figure() 
    plt.imshow(abs(F), 
       aspect="equal", 
       interpolation="none", 
       origin="lower", 
       extent=extents(xf) + extents(yf)) 
    plt.colorbar() 
    plt.xlabel('f_x (Hz)') 
    plt.ylabel('f_y (Hz)') 
    plt.title('|Spectrum|') 
    plt.show() 


if __name__ == '__main__': 
    # In seconds 
    x = np.linspace(0, 4, 20) 
    y = np.linspace(0, 4, 30) 
    # Uncomment the next two lines and notice that the spectral peak is no 
    # longer equal to 1.0! That's because `makeSpectrum` expects its input's 
    # origin to be at the top-left pixel, which isn't the case for the following 
    # two lines. 
    # x = np.linspace(.123 + 0, .123 + 4, 20) 
    # y = np.linspace(.123 + 0, .123 + 4, 30) 

    # Sinusoid frequency, in Hz 
    x0 = 1.9 
    y0 = -2.9 

    # Generate data 
    im = np.exp(2j * np.pi * (y[:, np.newaxis] * y0 + x[np.newaxis, :] * x0)) 

    # Generate spectrum and plot 
    spectrum, xf, yf = makeSpectrum(im, x[1] - x[0], y[1] - y[0]) 
    plotSpectrum(spectrum, xf, yf) 

    # Report peak 
    peak = spectrum[:, np.isclose(xf, x0)][np.isclose(yf, y0)] 
    peak = peak[0, 0] 
    print('spectral peak={}'.format(peak)) 

結果如下圖中,和打印出,spectral peak=(1+7.660797103157986e-16j),這正是用於在純復指數的頻率頻譜中的正確的值。

Result

+0

非常好,非常感謝;-)最後一個問題:在最後一步中,我使用了上面的代碼示例,並替換了「x = np.linspace(0,4,20); y = np。 (0,simulationArea,numberOfGridPointsInX,endpoint = False); y = x「,當然exp()和我的真實數據E(真實物理數據)之間的」linspace(0,4,30)「。結果看起來不錯,因爲它不再顯示Nyquisit限制以上的頻率。但它也顯示負頻率,我(與你的例子相反)是一個純真實的信號。那麼我如何擺脫那些冗餘的負頻率? – Alienbash

+0

所以我試圖通過以純真實信號爲例來解決負頻問題:「np.sin(2 * np.pi *(y [:, np.newaxis] * y0))* np.sin (2 * np.pi * x [:, np.newaxis] * x0)「作爲時域函數,然後在」plotSpectrum()「內部使用:」imshow( 2 * abs(F [:half,:half] )...「,然後是」plt.xlim(0,max(extents(xf)))「和」plt.xlim(0,max(extents(xf)))「,但不幸的是, (只是非常微弱的頻率,這是週期性地出現,所以絕對不是我想要的) – Alienbash

+0

也知道高斯的FT導致另一個高斯函數,我用「im = np.exp( - (np.power(x [:,np.newaxis] - mu,2)+ np.power(y [:, np.newaxis] - mu,2))/(2 * np.power(sig,2)))「,用「mu = 0.5」和「sig = 1」,從中可以看出,它不會導致高斯。對於真實信號,這裏需要做些什麼調整? – Alienbash