Old Chinese version
在前一節中,我們可以使用「離散時間傅立葉轉換」(簡稱 DTFT)來將一段數位訊號轉換成各個頻譜的分量,但這是一個連續的函數,並不適合在電腦中處理,因此本節將介紹「離散傅立葉轉換」(Discrete Fourier Transform),簡稱 DFT,其功能是將一段數位訊號轉換成其各個離散頻率的弦波分量,以便後續使用電腦進行各種處理。
如果我們的訊號可以表示成 x[n], n = 0~N-1,那麼 DFT 的公式如下:
X[k]=(1/N)*Sn=0N-1
x[n]*exp(-j*2p*n*k/N), k=0, ..., N-1
這些傅立葉係數 X[k] 所代表的資訊是 k 的函數,而 k 直接和頻率有正比關係,因此這些係數 X[k] 通稱為「頻譜」(Spectrum),而對於 X[k] 的分析,我們通稱為「頻譜分析」(Spectral Analysis)。我們也可以由這些傅立葉係數 X[k],來反推原始訊號 x[n],如下:
x[n]=Sk=0N-1
X[k]*exp(j*2p*n*k/N), n=0, ..., N-1
這邊有幾點要說明:
- 如果原始訊號 x[n] 有 N 點,那麼轉換出來的訊號 X[k] 也會有 N 點。
- 一般而言,X[k] 是一個複數,其大小是 |(X[k])| (abs(X[k]) in MATLAB),相位是 ∠X[k] (angle(X[k]) or atan(imag(X[k])/real(X[k]) in MATLAB)。
- 如果原始訊號 x[n] 都是實數,那麼 X[k] 和 X[N-k] 會是共軛複數,滿足 |(X[k])| = |(X[N-k])| 以及 ∠X[k] = -∠X[N-k]。
如果 x[n] 是實數,我們可以將 x[n] 表示如下:
x[n] = X[0]
+ X[1]*exp(j*2p*n*1/N) + X[N-1]*exp(j*2p*n*(N-1)/N)
+ X[2]*exp(j*2p*n*2/N) + X[N-2]*exp(j*2p*n*(N-2)/N)
+ X[3]*exp(j*2p*n*3/N) + X[N-3]*exp(j*2p*n*(N-3)/N)
+ ...
對上述第 k 項而言,我們有
X[k]*exp(j*2p*n*k/N) + X[N-k]*exp(j*2p*n*(N-k)/N)
= X[k]*exp(j*2p*n*k/N) + X[N-k]*exp(j*2p*n)*exp(-j*2p*n*k/N)
= X[k]*exp(j*2p*n*k/N) + X[N-k]*exp(-j*2p*n*k/N)
= 2*Re(X[k]*exp(j*2p*n*k/N))
如果我們以 mk 表示 X[k] 的大小,以 pk 表示 X[k] 的相位,那麼上式可以化簡如下:
2*Re(mk*exp(j*pk)*exp(j*2p*n*k/N))
= 2*Re(mk*exp(j*(2p*n*k/N + pk))
= 2*mk*cos(2p*n*k/N + pk)
一般而言,N 是 2 的倍數,因此 x[n] 可以表示成
x[n] = X[0]
+ 2*Sk=1N/2-1mk*cos(2p*n*k/N + pk) + mN/2*cos(p*n + pN/2)
換句話說,我們可以將原始訊號拆解成一個直流訊號 X[0] 再加上 N/2 個弦波的組合,這些弦波的震幅就是 X[k] 的大小,而相位則是 X[k] 的相位。因此對於實數的 x[n] 而言,我們只需要看單邊的 X[k],k = 0 ~ N/2,此種可稱為「單邊頻譜」。組成這些單邊頻譜的弦波共有 1+N/2 個,頻率由小到大分別是 fs/N*(0:N/2),這些弦波稱為「基本弦波」。
若是套用上述公式來計算 DFT,所需要的複雜度是 O(n2),但在 1965 年,有兩位學者提出來一套更精簡的演算法,所需的複雜度只有 O(n log n),這一套演算法稱為「快速傅立葉轉換」(Fast Fourier Transform,簡稱 FFT),換句話說,FFT 是用來計算 DFT 的快速方法。若使用 MATLAB,相關的指令也是 fft。
首先,我們使用 MATLAB 的 fft 指令來驗證實數訊號之 DFT 係數的共軛性,如下:
由上圖可以看出,DFT 係數會對稱於複數平面的實數軸,代表這些係數都會以共軛複數的方式成對出現。
如果 x[n] 恰巧是這些基本弦波中的其中一個,那麼計算出來的雙邊頻譜,應該只有兩個係數不為零,我們可用 MATLAB 驗證如下:
由上圖可以看出:
- 能量頻譜只有在兩點不為零,其他各點理論上應該是零,但由於計算精度之故,實際值很接近於零,但不一定是零。
- 相位頻譜沒有一定規則,像是亂數。這是由於大部分的能量頻譜很小,因此實際的相位頻譜並沒有任何意義。
如果 x[n] 的頻率不是這些弦波中的其中一個,那麼 DFT 還是會將此訊號拆解成基本弦波的組合,因此能量頻譜就會分散在各個基本弦波,如下所示:
在上述範例中,我們使用 fftTwoSide.m 函數(位於 SAP Toolbox)來進行雙邊頻譜的計算,並將同時畫出時域訊號、頻譜能量、頻譜相位三個圖。
由於對於實數的 x[n] 而言,雙邊頻譜能量圖會左右對稱,而雙邊頻譜相位圖則是左右反對稱,因此我們可以只看單邊頻譜即可,範例如下:
在上述範例中,我們使用 fftOneSide.m 函數(位於 SAP Toolbox)來進行單邊頻譜的計算,並將同時畫出時域訊號、頻譜能量、頻譜相位三個圖。
下面這個範例,顯示一個語音音框的單邊頻譜:
在上述範例中,我們可以看到很明顯的諧波出現在頻譜能量,這是由於訊號的週期性所造成的結果。(為了凸顯諧波現象,我們的音框剛好包含了三個完整的基本週期。)
當 k 越大時,代表對應的弦波是高頻訊號,因此我們可以捨棄高頻訊號,只用幾個低頻弦波,來合成原始訊號,達到下列兩項目的:
以下這個範例,顯示如何以弦波來合成一個方波:
由此可見當我們所用的 DFT 係數越多,所合成的波形就會越接近原來的波形。
下面這個範例,則是以弦波來合成一個實際說話聲音的波形:
由此可見在實際的聲音波形中,我們只要少數幾個低頻的係數,就可以合成類似原來的訊號,由此也可以知道很多高頻訊號是沒有作用的。
對於週期性的波形,DFT 會插入零點,如下:
為什麼 DFT 會插入零點呢?若不使用詳細的數學分析,各位同學是否可以以直覺的方式來推導出這個結果呢?
若在時域波形加上零點,則在頻域所產生的效果是內差以增加點數,如下所示:
換句話說,加上零點,在實質上並沒有增加訊號的資訊,但是 DFT 的點數必須隨之增加,因此在頻譜的效應則是進行內差以增加點數。
若在時域進行 Downsample,則在頻域所產生的效果如下:
當我們 Downsample 進行越多次,曲線會越平滑,代表高頻部分已經慢慢不見了!
此外,我們可以將原先的時域訊號表示成數個弦波函數的線性組合,並使用最小平方法(lease-squares estimate)來進行曲線擬合(Curve fitting)以找出最佳的線性係數,這些線性係數和 fft 所得到的結果是一樣的,範例如下:
Audio Signal Processing and Recognition (音訊處理與辨識)