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

凌逆戰發表於2019-05-29

濾波器的技術指標

$\omega _p$:通帶截止頻率

$\omega _s$:阻帶截止頻率

$\delta_p$:通帶波動

$\delta_s$:阻帶波動

衰減單位是db

巴特沃斯濾波器

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:對於數字濾波器:Wn應該歸一化為(0,1),Wn=截止頻率/訊號頻率,(訊號頻率=取樣率的一半,奈奎斯特取樣定理)對於模擬濾波器:Wn是角頻率,弧度/樣本,rad/s
  • btype:濾波器的型別{'lowpass','highpass','bandpass','bandstop'}
  • analog:如果為True,則返回模擬濾波器,否則返回數字濾波器。

輸出

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

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

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

引數

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

返回

  • w:計算h角頻率
  • h:頻率響應

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

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

引數

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

返回

  • w:計算h的歸一化頻率,以弧度/樣本計算。
  • 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

 

相關文章