傅里葉變換是一種數學變換,它可以將一個函數或信號轉換為另一個函數或信號,它可以將時域信號轉換為頻域信號,也可以將頻域信號轉換為時域信號。在很多的領域都有廣泛的應用,例如信號處理、通信、圖像處理、電腦科學、物理學、生物學等。 它最大的功能是能夠分析和提取信號的特征,將複雜的信號分解為簡單的信號。有人 ...
傅里葉變換是一種數學變換,它可以將一個函數或信號轉換為另一個函數或信號,它可以將時域信號轉換為頻域信號,也可以將頻域信號轉換為時域信號。
在很多的領域都有廣泛的應用,例如信號處理、通信、圖像處理、電腦科學、物理學、生物學等。
它最大的功能是能夠分析和提取信號的特征,將複雜的信號分解為簡單的信號。
有人甚至說傅里葉變換是一種可以讓我們看透世界本質的變換,將紛繁世界的表象中的本質顯現出來。
1. 簡介
從數學的角度來推導傅里葉變換的話,會涉及到很多的基本的數學概念,比如正交基,級數,正弦餘弦函數等等。
這裡主要是為了看懂和使用傅里葉變換,瞭解傅里葉變換中的主要概念即可,Scipy
庫會幫助我們處理其中的細節。
舉例來說,對於現實中遇到的如下一個原始信號(一般都是非周期性且無規律的):
這樣的信號,可以是聲音,可以是氣溫變化,可以是心電圖,甚至可以是股票漲跌情況等等。
原始的信號變化複雜,而且大部分情況都看不出什麼規律。
這時,傅里葉變換就能發揮作用了,它能夠將複雜無序的信號轉換為一系列簡單規則的信號。
比如上面的信號轉換之後變成下麵6個簡單的信號:
另外,傅里葉變換是可逆的,它也能夠將變換後的多個簡單的信號還原成原始的複雜信號。
2. fft 模塊
Scipy
中處理傅里葉變換有2個子模塊:fft
和fftpack
。fftpack
即將被淘汰,所以儘量使用fft
模塊。
fft
模塊中,傅里葉變換的主要函數有:
函數名 | 說明 |
---|---|
fft | 計算一維離散傅里葉變換 |
ifft | 計算一維離散傅里葉逆變換 |
fft2 | 計算二維離散傅里葉變換 |
ifft2 | 計算二維離散傅里葉逆變換 |
fftn | 計算N維離散傅里葉變換 |
ifftn | 計算N維離散傅里葉逆變換 |
2.1. 變換示例
創建一個複合的信號:
import numpy as np
import matplotlib.pyplot as plt
# 生成 0~8pi 之間100個點
x = np.linspace(0, 8*np.pi, 100)
# 隨便生成6個不同的正弦信號
y1 = np.sin(x)
y2 = 4*np.sin(2*x)
y3 = 2*np.sin(4*x)
y4 = 8*np.sin(0.3*x)
y5 = 6*np.sin(0.8*x)
y6 = 0.5*np.sin(5*x)
y = y1 + y2 + y3 + y4 + y5 + y6
plt.plot(x, y)
plt.show()
下麵通過一維傅里葉變換,看看得到的結果:
from scipy import fft as spfft
fft_result = spfft.fft(y)
print(fft_result.shape)
# 運行結果
(100,)
fft_result[1:11]
# 運行結果(顯示前10個,總共100個)
array([ 99.548317 -0.j , 273.43743482-274.69892934j,
-10.91586207 +66.24943608j, 167.24328363-151.29814008j,
-57.93543536 +49.80593353j, -30.50244642 +56.21303872j,
-19.99077861 +37.24642604j, -13.54997057 +21.30562857j,
35.119202 -161.91923934j, -17.17382544 +41.98143084j])
fft_result[-10:]
# 運行結果(顯示後10個,總共100個)
array([-13.61273919 -29.66475047j, -17.17382544 -41.98143084j,
35.119202 +161.91923934j, -13.54997057 -21.30562857j,
-19.99077861 -37.24642604j, -30.50244642 -56.21303872j,
-57.93543536 -49.80593353j, 167.24328363+151.29814008j,
-10.91586207 -66.24943608j, 273.43743482+274.69892934j])
觀察快速傅里葉轉換之後得到的結果:
- 轉換的結果中,每個元素都是複數
- 前10個元素中,除去第一個,剩下的元素和後10個元素相比,實部相同,虛部相反
傅里葉變換之後,可以得到兩個描述信號的圖形:
- 頻譜圖:各個頻率的波的振幅信息
- 相點陣圖:各個頻率的波的相位信息,相位就是波在特定時間所處的位置
頻譜信息可以通過 np.abs
方法計算得出:
fig = plt.figure(figsize=[8,4])
ax1 = fig.add_subplot(121)
data = np.abs(fft_result)
ax1.plot(data)
ax1.set_title("雙邊頻譜圖")
ax2 = fig.add_subplot(122)
data = np.abs(fft_result[:50])
ax2.plot(data)
ax2.set_title("單邊頻譜圖")
plt.show()
相位信息可以通過 np.angle
方法計算得出:
fig = plt.figure(figsize=[8,4])
ax1 = fig.add_subplot(121)
data = np.angle(fft_result)
ax1.plot(data)
ax1.set_title("雙邊相點陣圖")
ax2 = fig.add_subplot(122)
data = np.angle(fft_result[:50])
ax2.plot(data)
ax2.set_title("單邊相點陣圖")
plt.show()
從這兩個圖中就能得變換後各個頻率的信號的頻率,振幅,相位信息。
2.2. 逆變換示例
逆變換就是將變換後的信號還原為原始信號。
因為傅里葉變換並沒有任何信息的損失,所以逆變換之後可以看出信號的波形沒有任何改變。
data = spfft.ifft(fft_result)
# 逆變換之後,忽略虛部的數字
plt.plot(x, np.real(data))
plt.show()
3. 應用
最後,利用傅里葉變換,我們試著做一個改變聲音效果的小例子。
首先,讀取一段音頻,Scipy
就可以直接讀取wav
文件。
import scipy.io.wavfile as wav
# 讀取音頻,返回采樣率和採樣的數值
rate, all_samples = wav.read("/path/to/fft-test.wav")
print(rate)
print(all_samples[1000:1010])
# 運行結果
16000
[122 133 149 151 165 151 160 159 155 151]
plt.plot(all_samples)
plt.show()
音頻文件是網上隨便找的一段英語對話。
接著,對讀取的信號做傅里葉變換,觀察變換後的結果:
dd = spfft.rfft(all_samples)
plt.plot(np.abs(dd))
plt.show()
註意,這裡用了 rfft
函數,不是之前的fft
函數,
兩者的區別在於fft
的結果是複數,會形成對稱的雙邊圖,就像上一節介紹的那樣。
而rfft
的結果是實數,且是單邊的,如上圖所示。
這兩個函數根據實際情況選擇使用,都可以對信號進行傅里葉變換。
因為我後面的處理不需要雙邊的信息,所以這裡用 rfft
函數來做傅里葉變換。
3.1. 處理一
第一種處理,我嘗試把頻率20000HZ
以上的信號都去掉,看看音頻的效果有什麼變化。
new_data = dd.copy()
# 20000HZ以上頻率的數據設為0
new_data[20000:] = 0
fig = plt.figure(figsize=[8,4])
ax1 = fig.add_subplot(121)
ax1.plot(np.abs(new_data))
ax2 = fig.add_subplot(122)
ax2.plot(np.abs(dd))
plt.show()
處理之後的信號逆變換為原始信號,再保存為wav音頻文件,看音頻的變化效果。
new_data = spfft.irfft(new_data)
new_data = np.rint(new_data)
new_data = new_data.astype("int16")
wav.write("/path/to/fft-test-1.wav", rate, new_data)
轉換後的音頻和原音頻比,聽起來聲音更加悶一些,模糊一些。
3.2. 處理二
這次與處理一相反,把20000HZ以下的信號去掉。
new_data = dd.copy()
# new_data[20000:] = 0
new_data[:20000] = 0
fig = plt.figure(figsize=[8,4])
ax1 = fig.add_subplot(121)
ax1.plot(np.abs(new_data))
ax2 = fig.add_subplot(122)
ax2.plot(np.abs(dd))
plt.show()
然後同樣的把信號逆變換回去,並保存為wav
文件。
new_data = spfft.irfft(new_data)
new_data = np.rint(new_data)
new_data = new_data.astype("int16")
wav.write("/path/to/fft-test-2.wav", rate, new_data)
這次的聲音聽起來很遙遠,像是長途電話的感覺。
4. 總結
本篇主要介紹了傅里葉變換是什麼,以及如何使用Scipy
庫來進行傅里葉變換。
目的是瞭解和使用傅里葉變換,而不是從數學角度去推導傅里葉變換。
最後的小例子雖然簡單,但見微知著,僅僅刪除了一些頻率的信號,聲音效果就隨之變化。
如果把不同聲音的文件中,影響音調,音色的頻率分析出來,就可以製作一個變聲器,
把自己的聲音變成男聲女聲,兒童老人等等。
進一步,用二維傅里葉變換的話,可以分析圖像,把圖像中的主要頻率找出來,
深入下去既可以做圖像修複,也可以做圖像識別等等。
Scipy
庫中還有多維傅里葉變換,可以分析現實情況下更加複雜的信號。
5. 附錄
文中用到的完整代碼(ipynb格式)和一個音頻文件,可以從下麵的地址下載:
scipy-fft-sample.zip:
https://url11.ctfile.com/f/45455611-910542660-6d7e0f?p=6872
(訪問密碼: 6872)