單通道噪聲抑制算法總結

單通道噪聲抑制算法主要分爲三個部分,噪聲估計,信噪比估計,增益計算。這三個部分的重要性依次遞減。

噪聲估計

根據統計的觀點,認爲噪聲成分會比語音成分更加的平穩。依此來區分噪聲和語音。噪聲估計有以下三大類的方法,最小值跟蹤,遞歸平滑,直方圖和分位數法。最基礎的方法是最小值跟蹤,該方法認爲在一段時間內,需要包括純噪聲段,各個頻點的最小幅值可以認爲是該頻點在這段時間內的噪聲估計。這種觀點很容易理解。但是有兩個缺點。

  1. 延遲比較大。如果噪聲變化了,需要到純噪聲段才能被更新。如果每D幀更新一次噪聲,對噪聲變化的響應會延遲2*D幀。
  2. 有偏估計。即估計出來的噪聲值會偏小。噪聲應該是略大於最小值的。這需要一個補償因子。

我一般認爲大部分噪聲估計方法都是在對這兩個缺點進行改進。這裏介紹三種方法,分別是Speex的遞歸平滑,Cohen提出的改進遞歸平滑(IMCRA),WebRTC的基於直方圖和分位數。其中Speex的遞歸平滑最簡單,效果一般。Cohen的複雜度中等,IMCRA降噪效果中等。WebRTC的複雜度最高,效果也最好,簡直是神一般的存在。

Speex噪聲估計

  1. 最小值強制更新的長度是變化的,這樣做的好處是起始時更新快,迅速更新到當前噪聲,因爲大部分語音默認一開始是噪聲。缺點是有可能出現語音失真。並且後面更新時間太長,300幀,就是3秒。噪聲一旦變得不平穩就會帶來大量的殘留噪聲和失真。而IMCRA是每0.2秒更新一次,更新的最小值是1.2秒的。保證了統計特性,和更新速度。計算開銷和存儲開銷都會更大一些。
    a. 0 ~ 100幀時, 每15幀更新一次
    b. 100 ~ 1000幀時, 每50幀更新一次
    c. 1000 ~ 10000幀時, 每150幀更新一次
    d. 10000幀 每300幀更新一次
  2. 一旦當前的輸入功率(未平滑)小於已估計的噪聲功率,或者輸入功率(平滑)大於最小值(Smin)的2.5倍,則認爲需要更新噪聲。平滑係數也是慢慢變大的,即隨着時間,更新越來越慢。這個策略還是比較粗,並未區分不同頻點,不同的信噪比。代碼如下。
static void update_noise_prob(SpeexPreprocessState *st)
{
   int i;
   int min_range;
   int N = st->ps_size;
//0. 帶噪語音的頻點間和幀間平滑
   for (i=1;i<N-1;i++)
      st->S[i] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i-1]) 
                      + MULT16_32_Q15(QCONST16(.1f,15),st->ps[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i+1]);
   st->S[0] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[0]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[0]);
   st->S[N-1] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[N-1]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[N-1]);
   
//1. 強制更新的時間長度
if (st->nb_adapt < 100)
      min_range = 15;
else if (st->nb_adapt < 1000)
      min_range = 50;
else if (st->nb_adapt < 10000)
      min_range = 150;
else
      min_range = 300;
//2. 強制更新最小值
   if (st->min_count > min_range)
   {
      st->min_count = 0;
      for (i=0;i<N;i++)
      {
         st->Smin[i] = MIN32(st->Stmp[i], st->S[i]);// min_range之後強制更新Smin。
         st->Stmp[i] = st->S[i];
      }
   } else {
      for (i=0;i<N;i++)
      {
         st->Smin[i] = MIN32(st->Smin[i], st->S[i]); //求Smin與S之間的最小值
         st->Stmp[i] = MIN32(st->Stmp[i], st->S[i]); //求Stmp與S之間的最小值,Stmp相當於min_range範圍內的局部最小值      
      }
   }
   //3. 噪聲更新概率,如果當前信號功率S乘以0.4 大於 Smin,則表示需要更新噪聲。
   for (i=0;i<N;i++)
   {
      if (MULT16_32_Q15(QCONST16(.4f,15),st->S[i]) > st->Smin[i])
         st->update_prob[i] = 1;
      else
         st->update_prob[i] = 0;
   }
}
   update_noise_prob(st);

   /* Update the noise estimate for the frequencies where it can be */
   for (i=0;i<N;i++)
   {
      if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i], NOISE_SHIFT))
         st->noise[i] = MAX32(EXTEND32(0),MULT16_32_Q15(beta_1,st->noise[i]) + MULT16_32_Q15(beta,SHL32(st->ps[i],NOISE_SHIFT)));
   }

IMCRA噪聲估計

  1. 對輸入的信號功率做平滑,然後跟蹤最小值Smin。這一步相當於做了一個VAD,判斷噪聲與帶噪語音。
    1.1. 需要做兩次平滑,先是頻點間的平滑,然後是幀間的平滑。頻點間平滑用的是三個點,而幀間平滑的係數選擇很重要。
    1.2. 如果不平滑,則最小值會是一些奇異值,特別小。平滑係數選擇在0.85~0.95之間。平滑係數越大,則跟蹤出來的最小值Smin會越大。Smin太小則欠估計,太大則過估計。這是第一次平滑,記爲Smin1。
    1.3. 使用了兩個比值作爲粗略判斷帶噪語音段和純噪聲段的判決依據,即VAD。一個是S1/Smin1,一個是Ya2/Smin1。前者是平滑後的輸入功率和Smin1的比值,後者是未平滑功率與Smin1的比值。兩個條件是與關係,能夠較準確的分辨出是否爲純噪聲段。這兩個比例的閾值分別是1.66×1.67 和1.66 × 4.6.
    1.4. 這兩個比值的閾值是通過統計得來的。如下圖所示,作者分別統計了三種環境下的實時功率和最小功率的比值的概率分佈。其中Sr是S1/Smin,純噪聲時,這個比值大部分分佈在1~2之間,佔了50%。而如果是帶噪語音時,這個比值分佈比較均勻,從很小到很大時都有,表示帶噪語音段的幅度變化較大。所以可以認爲這兩個比值小於某個常數時是噪聲段。圖中H0表示純噪聲,H1表示帶噪語音。
    在這裏插入圖片描述
  2. 根據第一次平滑後的VAD結果對功率重新平滑。
    2.1. 同樣有頻點間平滑和幀間平滑。頻點間平滑時只對被判爲噪聲(I=1)的頻點平滑。而有語音部分不平滑。
    2.2. 幀間平滑也是如此,只對噪聲部分平滑,帶噪語音不變。這樣做的好處是,在時間軸上,有語音部分的頻點功率不變,即頻點上的功率信息一直是最後一幀純噪聲的功率信息。而噪聲部分的頻點功率一直在變化。好處是提高了噪聲更新的速度。因爲帶噪語音段的功率不會因爲語音功率而增加,造成過估計或者延遲。之前需要2D才能更新的噪聲,現在只需要D+V幀就可以了。這時較爲準確的最小值就跟蹤出來。優化了過估計欠估計和延遲的問題。
  3. Smin的估計是分爲兩個步驟的。有一個buffer,Sw,兩個變量,Smin和Smact。Smin存放的是全局最小值,Smact存放的是局部最小值。每一段時間,比如1.2S,將這這段時間分爲若干個小時間比如0.2S。每隔0.2S,把這個時間內的最小值Smact存入Sw,然後在Sw中求出1.2S的最小值更新Smin。因爲如果讓Smin一直更新,而不是每0.2S強制更新一次。只要出現了一個零,Smin就等於零,然後無法恢復。
  4. 我們不能把跟蹤的最小值都當成是噪聲,這時需要計算語音存在概率。利用貝葉斯公式,推算話音存在概率。這裏約定兩個狀態H0,純噪聲段,H1帶噪語音段。
    先驗概率,通過統計特性來判斷的。即通過對帶噪語音做統計分析,得到幅度的比值的特性,是先驗特性,然後做粗略的判斷。
    似然概率,是假設噪聲和帶噪語音分別滿足高斯分佈,然後根據當前幀已經收到的信號來預估這個分佈的參數。
    後驗概率,利用貝葉斯公式,推斷當前信號語音存在的後驗概率。
  5. 估計出來的最小值Smin需要進行平滑,而平滑係數則有剛剛計算得到的話音存在概率計算得到。基本方法是,話音存在概率越高,則平滑係數越大,即噪聲更新變慢。相反則平滑係數越大,噪聲更新快。平滑之後的噪聲,需要再做一次補償。

WebRTC噪聲估計

  1. 首先對輸入信號功率(未平滑)估計四分位數。因爲未平滑,如果跟蹤最小值,則結果會特別小,所以這裏跟蹤四分位數。認爲四分位數和平滑後功率的最小值差不多
    a. 對於不同的幀長,不用調整平滑因子。
    b. 不用buffer,節省內存
    c. 壞處是四分位數的數值計算我不懂。。。。。。
  2. 跟IMCRA類似的,估計出來的四分位數被當成粗略噪聲估計。利用這個噪聲估計計算似然比。同時用輸入功率(未平滑)計算譜平度,譜差(與一個保守估計的噪聲譜之間的差)。
    有一個直方圖統計一直在後臺。每200幀會根據統計的似然比,譜平度,譜差的特性來決定噪聲更新的係數。其中似然比的權值最大。如果譜平度和譜差的直方圖比較均勻,即沒有明顯的峯值,則不會使用這兩個特性。反之則使用。這三個特性通過tanh函數映射到[0~1]。相加成爲平滑因子。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章