单通道噪声抑制算法总结

单通道噪声抑制算法主要分为三个部分,噪声估计,信噪比估计,增益计算。这三个部分的重要性依次递减。

噪声估计

根据统计的观点,认为噪声成分会比语音成分更加的平稳。依此来区分噪声和语音。噪声估计有以下三大类的方法,最小值跟踪,递归平滑,直方图和分位数法。最基础的方法是最小值跟踪,该方法认为在一段时间内,需要包括纯噪声段,各个频点的最小幅值可以认为是该频点在这段时间内的噪声估计。这种观点很容易理解。但是有两个缺点。

  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]。相加成为平滑因子。

猜你喜欢

转载自blog.csdn.net/s09094031/article/details/84673428