Python機器學習(五十六)SciPy fftpack(傅里葉變換)


SciPy提供了fftpack模塊,包含了傅里葉變換的算法實現。

傅里葉變換把信號從時域變換到頻域,以便對信號進行處理。傅里葉變換在信號與噪聲處理、圖像處理、音頻信號處理等領域得到了廣泛應用。

如需進一步了解傅里葉變換原理,可以參考相關資料。

快速傅里葉變換

計算機只能處理離散信號,使用離散傅里葉變換(DFT) 是計算機分析信號的基本方法。但是離散傅里葉變換的缺點是:計算量大,時間復雜度太高,當采樣點數太高的時候,計算緩慢,由此出現了DFT的快速實現,即快速傅里葉變換FFT。

快速傅里葉變換(FFT)是計算量更小的離散傅里葉變換的一種實現方法,其逆變換被稱為快速傅里葉逆變換(IFFT)。

示例

先對數據進行fft變換,然后再ifft逆變換。

import numpy as np
#從fftpack中導入fft(快速傅里葉變化)和ifft(快速傅里葉逆變換)函數
from scipy.fftpack import fft,ifft

#創建一個隨機值數組
x = np.array([1.0, 2.0, 1.0, -1.0, 1.5])

#對數組數據進行傅里葉變換
y = fft(x)
print('fft: ')
print(y)
print('\n')

#快速傅里葉逆變換
yinv = ifft(y)
print('ifft: ')
print(yinv)
print('\n')

輸出

fft:
[ 4.5       +0.j          2.08155948-1.65109876j -1.83155948+1.60822041j
 -1.83155948-1.60822041j  2.08155948+1.65109876j]


ifft:
[ 1. +0.j  2. +0.j  1. +0.j -1. +0.j  1.5+0.j]

可以看到fft,ifft返回的都是復數。ifft返回的結果中,復數的虛部都是0,實部與原始數據x一致。

這些點的頻率無法計算,因為沒有設置這N個點的時間長度。如不理解,不必深究,后面會介紹。

理解fft變換結果

我們知道,傅里葉變換把時域信號變為頻域信號。在離散傅里葉變換中,頻域信號由一系列不同頻率的諧波(頻率成倍數)組成。fft返回值是一個復數數組,每個復數表示一個正弦波。通常一個波形由振幅,相位,頻率三個變量確定,可以從fft的返回值里,獲取這些信息。

假設a是時域中的周期信號,采樣頻率為Fs,采樣點數為N。如果A[N] = fft(a[N]),返回值A[N]是一個復數數組,其中:

  • A[0]表示頻率為0hz的信號,即直流分量。
  • A[1:N/2]包含正頻率項,A[N/2:]包含負頻率項。正頻率項就是轉化后的頻域信號,通常我們只需要正頻率項,即前面的n/2項,負頻率項是計算的中間結果(正頻率項的鏡像值)。
  • 每一項的頻率計算:假設A[i]為數組中的元素,表示一個波形,該波形的頻率 = i * Fs / N
  • A[i] = real + j * imag,是一個復數,相位就是復數的輻角,相位 = arg(real/imag)
  • 類似的,振幅就是復數的模,振幅 = sqrt(real^2+imag^2)。但是fft的返回值的模是放大值,直流分量的振幅放大了N倍,弦波分量的振幅放大了N/2倍。

頻率分辨率
頻率分辨率是離散傅里葉變換(DFT)頻域相鄰刻度之間的實際頻率之差。采樣時,數據采樣了T秒(T = 采樣點數N / 采樣頻率Fs),信號的成分中周期最大也就是T秒,最低頻率即“基頻”就等於1 / T,也就是Fs / N,這就是頻率分辨率。基頻 = Fs / N,各個諧波的頻率就是 i * Fs / N,這個公式用於計算各個波形的頻率。

示例

import numpy as np
from scipy.fftpack import fft

# 采樣點數
N = 4000

# 采樣頻率 (根據采樣定理,采樣頻率必須大於信號最高頻率的2倍,信號才不會失真)
Fs = 8000
x = np.linspace(0.0, N/Fs, N)

# 時域信號,包含:直流分量振幅1.0,正弦波分量頻率100hz/振幅2.0, 正弦波分量頻率150Hz/振幅0.5/相位np.pi
y = 1.0 + 2.0 * np.sin(100.0 * 2.0*np.pi*x) + 0.5*np.sin(150.0 * 2.0*np.pi*x + np.pi)

# 進行fft變換
yf = fft(y)

# 獲取振幅,取復數的絕對值,即復數的模
abs_yf = np.abs(yf)

# 獲取相位,取復數的角度
angle_y=np.angle(yf)

# 直流信號
print('\n直流信號')
print('振幅:', abs_yf[0]/N) # 直流分量的振幅放大了N倍

# 100hz信號
index_100hz = 100 * N // Fs # 波形的頻率 = i * Fs / N,倒推計算索引:i = 波形頻率 * N / Fs
print('\n100hz波形')
print('振幅:', abs_yf[index_100hz] * 2.0/N) # 弦波分量的振幅放大了N/2倍
print('相位:', angle_y[index_100hz])

# 150hz信號
index_150hz = 150 * N // Fs # 波形的頻率 = i * Fs / N,倒推計算索引:i = 波形頻率 * N / Fs
print('\n150hz波形')
print('振幅:', abs_yf[index_150hz] * 2.0/N) # 弦波分量的振幅放大了N/2倍
print('相位:', angle_y[index_150hz])
print('100hz與150hz相位差:',  angle_y[index_150hz] - angle_y[index_100hz])
print('\n')

輸出

直流信號
振幅: 1.0

100hz波形
振幅: 1.9989359813189005
相位: -1.5315264186250062

150hz波形
振幅: 0.5008489983048182
相位: 1.6297011890497097
100hz與150hz相位差: 3.161227607674716

可以看到,正弦波的相位不一定從0開始,但波形之間的相位差確實s約等於一個pi(值跟采樣頻率與采樣點數有關系)。

離散余弦變換(DCT)

由於許多要處理的信號都是實信號,在使用FFT時,對於實信號,傅立葉變換的共軛對稱性導致在頻域中有一半的數據冗余。

離散余弦變換(DCT)是對實信號定義的一種變換,變換后在頻域中得到的也是一個實信號,相比離散傅里葉變換DFT而言, DCT可以減少一半以上的計算。DCT還有一個很重要的性質(能量集中特性):大多書自然信號(聲音、圖像)的能量都集中在離散余弦變換后的低頻部分,因而DCT在(聲音、圖像)數據壓縮中得到了廣泛的使用。由於DCT是從DFT推導出來的另一種變換,因此許多DFT的屬性在DCT中仍然是保留下來的。

SciPy.fftpack中,提供了離散余弦變換(DCT)與離散余弦逆變換(IDCT)的實現。

示例

import numpy as np
from scipy.fftpack import dct,idct
y = dct(np.array([4., 3., 5., 10., 5., 3.]))
print(y)

輸出

[ 60.          -3.48476592 -13.85640646  11.3137085    6.
  -6.31319305]

離散余弦逆變換(idct),是離散余弦變換(DCT)的反變換。

示例

import numpy as np
from scipy.fftpack import dct,idct
y = idct(np.array([4., 3., 5., 10., 5., 3.]))
print(y)

輸出

[ 39.15085889 -20.14213562  -6.45392043   7.13341236   8.14213562
  -3.83035081]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM