語音信號預處理——數字濾波器


濾波器的技術指標

$w_p$:通帶截止頻率

$w_s$:阻帶截止頻率

$\delta_p$:通帶波動

$\delta_s$:阻帶波動

衰減單位是db

濾波器系數、脈沖響應、頻率響應的關系:濾波器變換,時域卷積等於頻域乘積,濾波操作在時域表現為輸入信號與濾波器脈沖響應的卷積;從頻域上看濾波器操作表現為,輸入信號的傅立葉變換和脈沖響應的傅立葉變換做乘積。

低通濾波器:只允許某一頻率以下的信號無衰減地通過濾波器,其分界處的頻率稱為截止頻率。<p id="lowpass" style="#lowpass:hover{}">圖片</p>

高通濾波器:規定高於設定臨界值頻率(fc)的信號能正常通過,而低於設定臨界值頻率(fc)的信號則被阻隔和衰減。

帶通濾波器:允許特定頻率信號通過的濾波器,阻隔和衰減該頻帶上下頻率的信號。帶通濾波器可以看做是由高通和低通濾波器協同作用的結果,高通和低通濾波器的截止頻率可作為通帶的下限頻率和上限頻率(圖3中的L和U點)。其主要參數是中心頻率和帶寬,上限頻率和下限頻率之差就是濾波器的帶寬。

帶阻濾波器:能通過大多數頻率分量,但將某些范圍的頻率分量衰減到極低水平的濾波器,與帶通濾波器的概念相對。其中陷波濾波器(notch filter)是一種特殊的帶阻濾波器,它的阻帶范圍極小。

陷波濾波器:可以在某一個頻率點迅速衰減輸入信號,以達到阻礙此頻率信號通過的濾波效果,主要用在電路上濾除不需要的頻率的信號。

  對於FIR濾波器,濾波器系數即為脈沖響應,因此,對於FIR濾波器,系數的FFT變換即為濾波器的頻率響應曲線

巴特沃斯濾波器

butterworth低通濾波器的頻域特性

$$|H(jw)|^2=\frac{1}{1+(\frac{\omega }{\omega _c})^{2N}}$$

N:濾波器的階數

$\omega _c$:3dB截頻

           典型BW低通濾波器的幅度響應

特點:在通帶的頻率響應曲線最平滑

Python實現

scipy.signal.butter(N, Wn, btype='low', analog=False, output='ba')

輸入

  • N:濾波器的階數
  • Wn:截止頻率,對於巴特沃斯濾波器,這是增益下降到通帶$\frac{1}{\sqrt 2}$的點(即“-3db點”)。
    • 對於數字濾波器:Wn與fs的單位相同Hz(fs是采樣率),在MATLAB中Wn是歸一化截止頻率(0,1),Wn=截止頻率/信號頻率,(信號頻率=采樣率的一半,奈奎斯特采樣定理)。歸一化頻率=1,截止頻率$w_c$代表采樣頻率,0.5代表奈奎斯特頻率
    • 對於模擬濾波器:Wn是角頻率(弧度/秒,radians/second)
  • btype:濾波器的類型{'lowpass','highpass','bandpass','bandstop'}
  • analog:如果為True,則返回模擬濾波器,否則返回數字濾波器。

輸出

  • b,a:濾波器系數, a為分母,b為分子。

scipy.signal.freqs(b, a, worN=200, plot=None)  

計算模擬濾波器的頻率響應H(w)。

$$H(w)=\frac{b[0]*(jw)**M+b[1]*(jw)**(M-1)+...+b[M]}{ a[0]*(jw)**N + a[1]*(jw)**(N-1) + ... + a[N]}$$

參數

  • b,a:線性濾波器的分子和分母,
  • worN:可選,如果為None,則計算響應曲線的有趣部分周圍的200個頻率。如果是一個整數,則計算那么多頻率。

返回

  • w:計算h角頻率
  • h:頻率響應(在該頻率值得振幅)

scipy.signal.freqz(b, a=1, worN=512, whole=False, plot=None)

計算數字濾波器的頻率響應。

$$H(e^{jw})=\frac{B(e^{jw})}{A(e^{jw})}=\frac{b(1)+b(2)e^{-jw}+...+b(n+1)e^{-jwn}}{a(1)+a(2)e^{-jw}+...+a(m+1)e^{-jwm}}$$

參數

  • b,a:線性濾波器的分子和分母
  • worN:如果為None(默認值),則計算在單位圓周圍等間隔的512個頻率。如果是一個整數,則計算那么多頻率。如果是array_like,則計算給定頻率的響應(以弧度/樣本為單位)。

返回

  • w:計算h的頻率,單位與fs相同。默認情況下,w被歸一化為$[0,1)*\pi$范圍(radians/sample),例如對於一個采樣頻率為1000Hz的系統,300Hz則對應300/500=0.6。若要將歸一化頻率轉換為單位圓上的弧度,則將歸一化值乘以π即可。圖中的0.5表示的是0.5π(rad),即為w = 0.5π, 由 w = 2 * pi * f / fs 得到 f = w * fs / 2pi,即 f = 0.5 * fs / 2 ,因為 fs = 122.88MHz,所以截止頻率 f = 30.72MHz。
  • h:頻率響應,以復數表示

scipy.signal.lfilter(b, a, x, axis=-1, zi=None)

使用IIR或FIR濾波器沿一維過濾數據。使用數字濾波器過濾數據序列x。

輸入

  • b,a:分子和分母,即濾波器系數
  • x:輸入數據

返回:數字濾波器的輸出

from scipy.signal import butter, lfilter
from scipy import signal
import numpy as np 
import matplotlib.pyplot as plt

b, a = signal.butter(4, 100, 'low', analog=True)    # 設計N階數字或模擬Butterworth濾波器並返回濾波器系數
w, h = signal.freqs(b, a)            # 根據系數計算濾波器的頻率響應,w是角頻率,h是頻率響應
plt.semilogx(w, 20 * np.log10(abs(h)))
plt.title('Butterworth filter frequency response')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.show()

提取窄帶語音信號

對采樣率為16000Hz,奈奎斯特頻率為8000Hz的語音,通過巴特沃斯低通濾波器,濾除高於4000Hz頻率的語音,提取低頻語音。過濾出的信號,在采樣率相同的情況下,頻率只有原來的一半。

import librosa 
import numpy as np
from scipy.signal import butter, lfilter, freqz
import matplotlib.pyplot as plt


def butter_lowpass(cutoff, fs, order=5):
    # cutoff:截止頻率
    # fs 采樣率
    nyq = 0.5 * fs                     # 信號頻率
    normal_cutoff = cutoff / nyq    # 正常截止頻率=截止頻率/信號頻率
    b, a = butter(order, normal_cutoff, btype='lowpass', analog=False)
    return b, a


def butter_lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y  # Filter requirements.


order = 10
fs = 16000                                        # 采樣率, Hz
cutoff = 4000                          # 濾波器的期望截止頻率,Hz # 得到濾波器系數,這樣我們就可以檢查它的頻率響應。
b, a = butter_lowpass(cutoff, fs, order)         # 繪制頻率響應
w, h = freqz(b, a)
plt.subplot(3, 1, 1)
plt.plot(0.5*fs*w/np.pi, np.abs(h), 'b')
plt.axvline(cutoff, color='k')
plt.xlim(0, 0.5*fs)
plt.title("Lowpass Filter Frequency Response")
plt.xlabel('Frequency [Hz]')

data, wav_fs = librosa.load("./48k/p225_001.wav", sr=16000, mono=True)        # 48000--->16000
y = butter_lowpass_filter(data, cutoff, fs, order)

plt.subplot(3, 1, 2)
plt.specgram(data, Fs=16000, scale_by_freq=True, sides='default')
plt.xlabel('Time [sec]')

plt.subplot(3, 1, 3)
plt.specgram(y, Fs=16000, scale_by_freq=True, sides='default')
plt.xlabel('Time [sec]')

plt.show()

切比雪夫I形狀濾波器

CB I型低通濾波器的頻域特性

$$|H(jw)|^2=\frac{1}{1+\varepsilon ^2C_N^2(\frac{w}{w_c})}$$

N:濾波器的階數

$\varepsilon$:通帶波紋

$\omega _c$:通帶截頻

圖  CB I型低通濾波器的幅度響應

特點:通帶是等波動的,阻帶是單調的

scipy.signal.cheby1(N, rp, Wn, btype='low', analog=False, output='ba')

Chebyshev I型數字和模擬濾波器,設計N階數字或模擬Chebyshev I型濾波器並返回濾波器系數。

參數:

  • N:濾波器的階數
  • rp:通帶中允許的最大紋波低於單位增益,以分貝為單位,正數
  • Wn:對於數字濾波器:Wn應該歸一化為(0,1),Wn=截止頻率/信號頻率,(信號頻率=采樣率的一半,奈奎斯特采樣定理)對於模擬濾波器:Wn是角頻率,弧度/樣本,rad/s
  • btype:濾波器的類型{'lowpass','highpass','bandpass','bandstop'}
  • analog:如果為True,則返回模擬濾波器,否則返回數字濾波器。
  • output:默認“ba”,輸出分子和分母

返回

  • b,a:濾波器系數, a為分母,b為分子。
import numpy as np 
from scipy import signal
import matplotlib.pyplot as plt

b, a = signal.cheby1(4, 5, 100, 'low', analog=True)
w, h = signal.freqs(b, a)
plt.semilogx(w, 20 * np.log10(abs(h)))
plt.title('Chebyshev Type I frequency response (rp=5)')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.axhline(-5, color='green') # rp
plt.show()

切比雪夫II形狀濾波器

CB II型低通濾波器的頻域特性

 $$|H(jw)|^2=1-\frac{1}{1+\varepsilon ^2C_N^2(\frac{w}{w_c})}$$

N:濾波器的階數

$\varepsilon$:阻帶波紋

$\omega _c$:阻帶截頻

圖  CB II型低通濾波器的幅度響應

特點:通帶是單調的,阻帶是等波動的

scipy.signal.cheby2(N, rs, Wn, btype='low', analog=False, output='ba')

Chebyshev II型數字和模擬濾波器,設計N階數字或模擬Chebyshev II型濾波器並返回濾波器系數。

參數:

  • N:濾波器的階數
  • rs:阻帶所需最小衰減,以分貝為單位,正數
  • Wn:對於數字濾波器:Wn應該歸一化為(0,1),Wn=截止頻率/信號頻率,(信號頻率=采樣率的一半,奈奎斯特采樣定理)對於模擬濾波器:Wn是角頻率,弧度/樣本,rad/s
  • btype:濾波器的類型{'lowpass','highpass','bandpass','bandstop'}
  • analog:如果為True,則返回模擬濾波器,否則返回數字濾波器。
  • output:默認“ba”,輸出分子和分母

返回

  • b,a:濾波器系數, a為分母,b為分子。
from scipy import signal
import numpy as np 
import matplotlib.pyplot as plt

b, a = signal.cheby2(4, 40, 100, 'low', analog=True)
w, h = signal.freqs(b, a)
plt.semilogx(w, 20 * np.log10(abs(h)))
plt.title('Chebyshev Type II frequency response (rs=40)')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.axhline(-40, color='green') # rs
plt.show()

橢圓低通濾波器

橢圓模擬低通濾波器的頻域特性

圖  橢圓低通濾波器的幅度相應

特點:通帶和阻帶都等波動

scipy.signal.ellip(N, rp, rs, Wn, btype='low', analog=False, output='ba')

橢圓數字和模擬濾波器,設計N階數字或模擬橢圓濾波器並返回濾波器系數。

參數:

  • N:濾波器的階數
  • rp:通帶中允許的最大紋波低於單位增益,以分貝為單位,正數
  • rs:阻帶所需最小衰減,以分貝為單位,正數
  • Wn:對於數字濾波器:Wn應該歸一化為(0,1),Wn=截止頻率/信號頻率,(信號頻率=采樣率的一半,奈奎斯特采樣定理)對於模擬濾波器:Wn是角頻率,弧度/樣本,rad/s
  • btype:濾波器的類型{'lowpass','highpass','bandpass','bandstop'}
  • analog:如果為True,則返回模擬濾波器,否則返回數字濾波器。
  • output:默認“ba”,輸出分子和分母

返回

  • b,a:濾波器系數, a為分母,b為分子。
import numpy as np 
from scipy import signal
import matplotlib.pyplot as plt

b, a = signal.ellip(4, 5, 40, 100, 'low', analog=True)
w, h = signal.freqs(b, a)
plt.semilogx(w, 20 * np.log10(abs(h)))
plt.title('Elliptic filter frequency response (rp=5, rs=40)')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.axhline(-40, color='green') # rs
plt.axhline(-5, color='green') # rp
plt.show()

下采樣方法

插值方法進行下采樣

Volodymyr Kuleshov的論文中使用抗混疊濾波器對語音信號進行下采樣,再通過三次樣條插值把下采樣信號上采樣到相同的長度。

from scipy.signal import decimate
import librosa 
import numpy as np 
import matplotlib.pyplot as plt 
from scipy import interpolate

def upsample(x_lr, r):
    """
    上采樣,每隔一步去掉語音波形的r個點,然后用三次樣條插值的方法把去掉的點補回來,有機會可以畫圖看看
    :param x_lr:    音頻數據
    :param r:       樣條插值前個數
    :return:        樣條插值后的音頻信號
    """
    x_lr = x_lr.flatten()                   # 把x_lr數組折疊成一維的數組
    x_hr_len = len(x_lr) * r
    i_lr = np.arange(x_hr_len, step=r)
    i_hr = np.arange(x_hr_len)

    f = interpolate.splrep(i_lr, x_lr)      # 樣條曲線插值系數
    x_sp = interpolate.splev(i_hr, f)       # 給定樣條表示的節點和系數,返回在節點處的樣條值

    return x_sp


yt, wav_fs = librosa.load("./48k/p225_001.wav", sr=16000, mono=True)
x_lr = decimate(yt, 2)          # 應用抗混疊濾波器后對信號進行下采樣,獲得低分辨率音頻,下采樣因子scale=2

print(len(yt))
print(len(x_lr))

plt.subplot(2, 1, 1)
plt.specgram(yt, Fs=16000, scale_by_freq=True, sides='default')

x_lr = upsample(x_lr, 2)       # 上采樣
plt.subplot(2, 1, 2)
plt.specgram(x_lr, Fs=16000, scale_by_freq=True, sides='default')

plt.show()

重采樣(signal.resample)——下采樣

利用重新采樣的方法對語音進行下采樣

scipy.signal.resample(x, num, t=None, axis=0, window=None)

沿給定軸使用傅立葉方法重新采樣x到num個樣本。因為使用傅立葉方法,所以假設信號是周期性的。

參數:

  • x:要重采樣的數組
  • num:重采樣信號的樣本數

返回:

  • resample_x:重新采樣返回的數組
import librosa 
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt

y, wav_fs = librosa.load("./48k/p225_001.wav", sr=16000, mono=True) 
f = signal.resample(y, len(y)//2)
f = signal.resample(f, len(y))

plt.subplot(2,1,1)
plt.specgram(y, Fs=16000, scale_by_freq=True, sides='default')

plt.subplot(2,1,2)
plt.specgram(f, Fs=16000, scale_by_freq=True, sides='default')

plt.show()

librosa.core.resample重采樣(下采樣)

凌振華老師的下采樣方法和上面的一樣

import librosa 
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt

y, wav_fs = librosa.load("./48k/p225_001.wav", sr=16000, mono=True) 
audio8k = librosa.core.resample(y, wav_fs, wav_fs/2)            # 下采樣率 16000-->8000
audio8k = librosa.core.resample(audio8k, wav_fs/2, wav_fs)    # 上采樣率 8000-->16000,並不恢復高頻部分

plt.subplot(2,1,1)
plt.specgram(y, Fs=16000, scale_by_freq=True, sides='default')

plt.subplot(2,1,2)
plt.specgram(audio8k, Fs=16000, scale_by_freq=True, sides='default')

plt.show()

librosa.load下采樣

用librosa.load想下采樣,再不恢復頻率的情況下上采樣。

import librosa 
import matplotlib.pyplot as plt

y_16k, fs_16k = librosa.load("./48k/p225_001.wav", sr=16000, mono=True) 
y_8k, fs_8k = librosa.load("./48k/p225_001.wav", sr=8000, mono=True) 
librosa.output.write_wav('./8k_sample.wav', y_8k, sr=8000)    # 把下采樣的寫好
y_8k, fs_8k = librosa.load("./8k_sample.wav", sr=16000, mono=True)     # 失去的就補不回來了


plt.subplot(2, 1, 1)
plt.specgram(y_16k, Fs=16000, scale_by_freq=True, sides='default')
plt.xlabel('16k')

plt.subplot(2, 1, 2)
plt.specgram(y_8k, Fs=16000, scale_by_freq=True, sides='default')
plt.xlabel('8k')
plt.show()

 

參考文獻

北京交通大學(數字信號處理)陳后金教授

信號處理(scipy.signal

scipy.signal.butter

scipy.signal.freqs

scipy.signal.freqz

scipy.signal.cheby1

scipy.signal.ellip

scipy.signal.decimate

scipy.signal.resample

Normalized Frequency(*π rad/sample)含義

高通濾波、低通濾波、帶通濾波和陷波器的區別


免責聲明!

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



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