Python語音基礎操作--5.1自適應濾波

《語音信號處理試驗教程》(梁瑞宇等)的代碼主要是Matlab實現的,現在Python比較熱門,所以把這個項目大部分內容寫成了Python實現,大部分是手動寫的。使用CSDN博客查看幫助文件:

Python語音基礎操作–2.1語音錄製,播放,讀取
Python語音基礎操作–2.2語音編輯
Python語音基礎操作–2.3聲強與響度
Python語音基礎操作–2.4語音信號生成
Python語音基礎操作–3.1語音分幀與加窗
Python語音基礎操作–3.2短時時域分析
Python語音基礎操作–3.3短時頻域分析
Python語音基礎操作–3.4倒譜分析與MFCC係數
Python語音基礎操作–4.1語音端點檢測
Python語音基礎操作–4.2基音週期檢測
Python語音基礎操作–4.3共振峯估計
Python語音基礎操作–5.1自適應濾波
Python語音基礎操作–5.2譜減法
Python語音基礎操作–5.4小波分解
Python語音基礎操作–6.1PCM編碼
Python語音基礎操作–6.2LPC編碼
Python語音基礎操作–6.3ADPCM編碼
Python語音基礎操作–7.1幀合併
Python語音基礎操作–7.2LPC的語音合成
Python語音基礎操作–10.1基於動態時間規整(DTW)的孤立字語音識別試驗
Python語音基礎操作–10.2隱馬爾科夫模型的孤立字識別
Python語音基礎操作–11.1矢量量化(VQ)的說話人情感識別
Python語音基礎操作–11.2基於GMM的說話人識別模型
Python語音基礎操作–12.1基於KNN的情感識別
Python語音基礎操作–12.2基於神經網絡的情感識別
Python語音基礎操作–12.3基於支持向量機SVM的語音情感識別
Python語音基礎操作–12.4基於LDA,PCA的語音情感識別

代碼可在Github上下載busyyang/python_sound_open

語音降噪主要研究如何利用信號處理技術消除信號中的強噪聲干擾,從而提高輸出信噪比以提取出有用信號的技術。消除信號中噪聲污染的通常方法是讓受污染的信號通過二個能抑制噪聲而讓信號相對不變的濾波器,此濾波器從信號不可檢測的噪聲場中取得輸入,將此輸入加以濾波,抵消其中的原始噪聲,從而達到提高信噪比的目的。

然而,由於干擾通常都是隨機的,從帶噪語音巾提取完全純淨的語音幾乎不可能。在這種情況下,語音增強的目的主要有兩個:一是改進語音質量,消除背景噪聲,使昕者樂於接受,不感覺疲勞,這是一種主觀度量;二是提高語音可懂度,這是一種客觀度量。這兩個目的往往不能兼得,所以實際應用中總是視具體情況而有所側重的。

根據語音和噪聲的特點,出現了很多種語音增強算法。比較常用的有譜減法、維納濾波法、卡爾曼濾波法、自適應濾波法等。此外,隨着科學技術的發展又出現了一些新的增強技術,如基於神經網絡的語音增強、基於HMM 的語音增強、基於昕覺感知的語音增強、基於多分辨率分析的語音增強、基於語音產生模型的線性濾波法、基於小波變換的語音增強方法、梳狀潔、波法、自相關法、基於語音模型的語音增強方法等。

帶噪語音模型

而通常所說噪聲是局部平穩的,是指一段帶噪語音中的噪聲,具有和語音段開始前那段噪聲相同的統計特性,且在整個語音段中保持不變。也就是說,可以根據語音開始前那段噪聲來估計語音中所疊加的噪聲統計特性。

LMS自適應濾波器

所謂自適應濾波器就是利用前一時刻已獲得的濾波器參數等結果,自動地調節現時刻的濾波器參數,以適應信號和|噪聲未知的隨機變化的統計特性,從而實現最優濾波。

最小方均(LMS) 白適應算法就是以已知期望響應和濾波器輸出信號之間誤差的方均值最小爲準的,依據輸入信號在迭代過程中估計梯度矢量,並更新權係數以達到最優的白適應迭代算法。LMS 算法是一種梯度最速下降方法,其顯著的優點是它的簡單性,這種算法不需要計算相應的相關函數,也不需要進行矩陣運算。

濾波器的輸出y(n)y(n)表示爲:
y(n)=WT(n)X(n)=i=0N1wi(n)x(ni)y(n)=\bold{W^T}(n)\bold{X}(n)=\sum_{i=0}^{N-1}w_i(n)\bold{x}(n-i)

對於LMS濾波器的結構,誤差爲:e(n)=d(n)y(n)\bold{e}(n)=\bold{d}(n)-\bold{y}(n)。方均誤差爲:
ϵ=E[e2(n)]=E[d(n)y(n)]=E[d2(n)]+WT(n)RW(n)2PW(n)\epsilon=\bold{E}[\bold{e^2}(n)]=\bold{E}[\bold{d}(n)-\bold{y}(n)]=\bold{E}[\bold{d^2}(n)]+\bold{W^T}(n)R\bold{W}(n)-2\bold{PW}(n)

其中R=E[XXT]\bold{R}=\bold{E}[\bold{X}\bold{X^T}],是N×NN\times N的自相關矩陣,P=E[d(n)XT(n)]\bold{P}=\bold{E}[\bold{d}(n)\bold{X^T}(n)]爲互相關矩陣,代表理想信號d(n)\bold{d}(n)與輸入矢量X(n)\bold{X}(n)的相關性。

在達到誤差ϵ\epsilon最小時,有:
ϵW(n)W(n)=W=0\frac{\partial \epsilon}{\partial\bold{W}(n)}|_{\bold{W}(n)=W^*}=0

有:
RWP=0W=R1P\bold{RW^*-P}=0\rightarrow\bold{W^*=\bold{R^{-1}P}}

LMS算法使用梯度下降來解,即W:=WμΔW(n)\bold{W:=W-\mu \Delta W(n)}

ΔW(n)=E[e2(n)]W(n)=2E[e(n)]E[e(n)]W(n)=2E[e(n)]E[d(n)y(n)]W(n)=2E[e(n)x(n)]\Delta W(n)=\frac{\partial E[e^2(n)]}{\partial W(n)}=2E[e(n)]\frac{\partial E[e(n)]}{\partial W(n)}=2E[e(n)]\frac{\partial E[\bold{d}(n)-\bold{y}(n)]}{\partial W(n)}=-2E[e(n)x(n)]

那麼:
W(n+1)=W(n)+2μΔe(n)x(n)W_{(n+1)}=W_{(n)}+2\mu \Delta e(n)x(n)

語音質量性能指標

  • 信噪比
    SNR=10lgn=1Ns2(n)n=1Nd2(n)SNR=10\lg \frac{\sum\limits_{n=1}^Ns^2(n)}{\sum\limits_{n=1}^Nd^2(n)}
    ss表示信號,dd表示噪聲。
  • PESQ(Perceptual Evaluation of Speech Quality)
    PESQ 算法需要帶噪的衰減信號和一個原始的參考信號。開始時將兩個待比較的語音信號經過電平調整、輸入濾波器濾波、時間對準和補償、昕覺變換之後,分別提取兩路信號的參數,綜合其時頻特性,得到PESQ分數,最終將這個分數映射到主觀平均意見分(MOS)。PESQ得分範圍在-0.5到4.5之間。得分越高表示語音質量越好。
from chapter2_基礎.soundBase import *
from chapter5_語音降噪.自適應濾波 import *


def awgn(x, snr):
    snr = 10 ** (snr / 10.0)
    xpower = np.sum(x ** 2) / len(x)
    npower = xpower / snr
    return x + np.random.randn(len(x)) * np.sqrt(npower)


data, fs = soundBase('C5_1_y.wav').audioread()
data -= np.mean(data)
data /= np.max(np.abs(data))
IS = 0.25  # 設置前導無話段長度
wlen = 200  # 設置幀長爲25ms
inc = 80  # 設置幀移爲10ms
SNR = 5  # 設置信噪比SNR
N = len(data)  # 信號長度
time = [i / fs for i in range(N)]  # 設置時間
r1 = awgn(data, SNR)
NIS = int((IS * fs - wlen) // inc + 1)
# 5.2.1
snr1 = SNR_Calc(r1, r1 - data)
a, b = 4, 0.001
output = SpectralSub(r1, wlen, inc, NIS, a, b)
if len(output) < len(r1):
    filted = np.zeros(len(r1))
    filted[:len(output)] = output
elif len(output) > len(r1):
    filted = output[:len(r1)]
else:
    filted = output

plt.subplot(4, 1, 1)
plt.plot(time, data)
plt.ylabel('原始信號')
plt.subplot(4, 1, 2)
plt.plot(time, r1)
plt.ylabel('加噪聲信號')
plt.subplot(4, 1, 3)
plt.ylabel('濾波信號')
plt.plot(time, filted)
plt.show()

#自適應濾波.py
import numpy as np
from chapter3_分析實驗.C3_1_y_1 import enframe


def SNR_Calc(s, r):
    """
    計算信號的信噪比
    :param s: 信號
    :param r1: 噪聲
    :return:
    """
    Ps = np.sum(np.power(s - np.mean(s), 2))
    Pr = np.sum(np.power(r - np.mean(r), 2))
    return 10 * np.log10(Ps / Pr)


def LMS(xn, dn, M, mu, itr):
    """
    使用LMS自適應濾波
    :param xn:輸入的信號序列
    :param dn:所期望的響應序列
    :param M:濾波器的階數
    :param mu:收斂因子(步長)
    :param itr:迭代次數
    :return:
    """
    en = np.zeros(itr)  # 誤差序列,en(k)表示第k次迭代時預期輸出與實際輸入的誤差
    W = np.zeros((M, itr))  # 每一行代表一個加權參量,每一列代表-次迭代,初始爲0
    # 迭代計算
    for k in range(M, itr):
        x = xn[k:k - M:-1]
        y = np.matmul(W[:, k - 1], x)
        en[k] = dn[k] - y
        W[:, k] = W[:, k - 1] + 2 * mu * en[k] * x
    # 求最優輸出序列
    yn = np.inf * np.ones(len(xn))
    for k in range(M, len(xn)):
        x = xn[k:k - M:-1]
        yn[k] = np.matmul(W[:, -1], x)
    return yn, W, en


def NLMS(xn, dn, M, mu, itr):
    """
    使用Normal LMS自適應濾波
    :param xn:輸入的信號序列
    :param dn:所期望的響應序列
    :param M:濾波器的階數
    :param mu:收斂因子(步長)
    :param itr:迭代次數
    :return:
    """
    en = np.zeros(itr)  # 誤差序列,en(k)表示第k次迭代時預期輸出與實際輸入的誤差
    W = np.zeros((M, itr))  # 每一行代表一個加權參量,每一列代表-次迭代,初始爲0
    # 迭代計算
    for k in range(M, itr):
        x = xn[k:k - M:-1]
        y = np.matmul(W[:, k - 1], x)
        en[k] = dn[k] - y
        W[:, k] = W[:, k - 1] + 2 * mu * en[k] * x / (np.sum(np.multiply(x, x)) + 1e-10)
    # 求最優輸出序列
    yn = np.inf * np.ones(len(xn))
    for k in range(M, len(xn)):
        x = xn[k:k - M:-1]
        yn[k] = np.matmul(W[:, -1], x)
    return yn, W, en


def SpectralSub(signal, wlen, inc, NIS, a, b):
    """
    譜減法濾波
    :param signal:
    :param wlen:
    :param inc:
    :param NIS:
    :param a:
    :param b:
    :return:
    """
    wnd = np.hamming(wlen)
    y = enframe(signal, wnd, inc)
    fn, flen = y.shape
    y_a = np.abs(np.fft.fft(y, axis=1))
    y_a2 = np.power(y_a, 2)
    y_angle = np.angle(np.fft.fft(y, axis=1))
    Nt = np.mean(y_a2[:NIS, ], axis=0)

    y_a2 = np.where(y_a2 >= a * Nt, y_a2 - a * Nt, b * Nt)

    X = y_a2 * np.cos(y_angle) + 1j * y_a2 * np.sin(y_angle)
    hatx = np.real(np.fft.ifft(X, axis=1))

    sig = np.zeros(int((fn - 1) * inc + wlen))

    for i in range(fn):
        start = i * inc
        sig[start:start + flen] += hatx[i, :]
    return sig


def SpectralSubIm(signal, wind, inc, NIS, Gamma, Beta):
    pass

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章