基于对数MMSE的语音增强算法

  常见的语音增强算法有谱减法,MMSE和维纳滤波等。谱减法虽然实现简单,运算量小,但效果比较差,容易出现刺耳的“音乐噪声”。MMSE和维纳滤波虽然原理较复杂,运算量也相对较大,但效果着实不错,甚至可以完全减除“音乐噪声”。由于实验室需要,对语音增强算法相对有点了解,但不够深入。实验室项目增强部分算法采用的是欧洲ETSI的ASR(自动语音识别)的前端去噪。采用的是两级维纳滤波,对于平稳的噪声语音去噪效果相当可观。官网也给出了C语言的实现和文档说明。虽然程序已移植到项目中,但由于文档主要是介绍了方法和一些经验参数,并没有具体说明为什么这样做,总体感觉就是只知道其中的算法流程。下图是ETSI的两级维纳滤波的程序框图。可以看出这个程序的维纳滤波是在梅尔频域上做的,估计是因为梅尔频率更符合人耳特性吧。就像语音识别中提取的特征参数是梅尔倒谱系数MFCC。

由于想进一步了解下语音增强算法的原理,这段时间着重看了MMSE的实现原理,尤其是对数的幅度谱误差平方MMSE-LSA的原理,并对这个算法进行C语言实现,最终结果与matlab代码一致。下面简要介绍下原理:

根据MMSE-LSA的开山始祖的文章Ephraim, Y. and Malah, D. (1985).《 Speech enhancement using a minimum 
mean-square error log-spectral amplitude estimator.》假设带噪信号y(n) = x(n) + d(n),其中x(n)为纯净信号,d(n)为噪声信号,转换为频域,Y(w) = X(w) + D(w)。根据论文推导出最优的对数MMSE估计器为:

  


其中就是对数MMSE估计器的增益函数。在这个函数中有两个未知量,分别是先验信噪比(可以被看作是第k个频谱分量的实际信噪比)和后验信噪比(可以看作是加入噪声后第k个频谱分量测得的信噪比)


其中 表示纯净信号谱的第k个频谱分量的方差。表示噪声频谱的第k个频谱分量的方差。从上面式子当中,只有Yk是已知的,即就是带噪信号的频域分量。如果假设噪声是平稳信号,噪声方差可以用VAD检测非语音段来不断更新。而纯净信号的方差则需要通过一定的方法估算出来,文章给出的是判决引导法,推导的结果是


其中a是平滑系数,一般取0.98。一些文章对上式提出改进方法,比如限定先验信噪比的最小值等。下面是MMSE-LSA的程序框图



MMSE-LSA算法实现步骤

1、对带噪信号分帧,加窗(这里是汉明窗)

2、对每帧带噪信号计算FFT ;  

3、估计后验信噪比 :    这里 是在非语音片段(语音开始之前几帧或语音间隙)估计的噪声能量谱;然后用判决引导法估计先验信噪比;

4、用最优的MMSE-LSA估计器的公式估计增强信号幅度;

5、重建增强信号谱,然后计算增强信号谱的IFFT,得到对应输入语音帧的增强的时域信号x(n)。


下面是matlab代码的程序(来自《语音增强-理论与实践》光盘,这本书是语音大牛Philipos C.Loizou的大作。这本书对语音增强方面的主流算法原理逐一介绍和推导,更难能可贵的是针对每一个算法给出了Matlab实例,是本难得好书)

  1. function logmmse(filename,outfile)  
  2.   
  3. %  
  4. %  Implements the logMMSE algorithm [1].  
  5. %   
  6. %  Usage:  logmmse(noisyFile, outputFile)  
  7. %             
  8. %         infile - noisy speech file in .wav format  
  9. %         outputFile - enhanced output file in .wav format  
  10. %    
  11. %  
  12. %  Example call:  logmmse(‘sp04_babble_sn10.wav’,’out_log.wav’);  
  13. %  
  14. %  References:  
  15. %   [1] Ephraim, Y. and Malah, D. (1985). Speech enhancement using a minimum   
  16. %       mean-square error log-spectral amplitude estimator. IEEE Trans. Acoust.,   
  17. %       Speech, Signal Process., ASSP-23(2), 443-445.  
  18. %     
  19. % Authors: Philipos C. Loizou  
  20. %  
  21. % Copyright (c) 2006 by Philipos C. Loizou  
  22. Revision: 0.0     Date: 10/09/2006    
  23. %————————————————————————-  
  24.   
  25. if nargin<2  
  26.    fprintf(‘Usage: logmmse(noisyfile.wav,outFile.wav) \n\n’);  
  27.    return;  
  28. end  
  29.   
  30. [x, Srate, bits]= wavread( filename);   %nsdata is a column vector  
  31.   
  32. % =============== Initialize variables ===============  
  33.   
  34. len=floor(20*Srate/1000); % Frame size in samples  
  35. if rem(len,2)==1, len=len+1; end;  
  36. PERC=50; % window overlap in percent of frame size  
  37. len1=floor(len*PERC/100);  
  38. len2=len-len1;  
  39.   
  40.   
  41. win=hamming(len);  % define window  
  42.   
  43.   
  44. % Noise magnitude calculations - assuming that the first 6 frames is  
  45. % noise/silence   
  46.   
  47. nFFT=2*len;  
  48. noise_mean=zeros(nFFT,1);  
  49. j=1;  
  50. for m=1:2  
  51.     noise_mean=noise_mean+abs(fft(win.*x(j:j+len-1),nFFT));  
  52.     j=j+len;  
  53. end  
  54. noise_mu=noise_mean/6;  
  55. noise_mu2=noise_mu.^2;  
  56.   
  57. %— allocate memory and initialize various variables  
  58.   
  59.   
  60.   
  61. x_old=zeros(len1,1);  
  62. Nframes=floor(length(x)/len2)-floor(len/len2);  
  63. xfinal=zeros(Nframes*len2,1);  
  64.   
  65.   
  66. %===============================  Start Processing =======================================================  
  67. %  
  68. k=1;  
  69. aa=0.98;  
  70. mu=0.98;  
  71. eta=0.15;   
  72.   
  73. ksi_min=10^(-25/10);  
  74.   
  75. for n=1:Nframes  
  76.   
  77.     insign=win.*x(k:k+len-1);  
  78.   
  79.     spec=fft(insign,nFFT);  
  80.     sig=abs(spec); % compute the magnitude  
  81.     sig2=sig.^2;  
  82.   
  83.     gammak=min(sig2./noise_mu2,40);  % limit post SNR to avoid overflows  
  84.     if n==1  
  85.         ksi=aa+(1-aa)*max(gammak-1,0);  
  86.     else  
  87.         ksi=aa*Xk_prev./noise_mu2 + (1-aa)*max(gammak-1,0);     % a priori SNR  
  88.         ksi=max(ksi_min,ksi);  % limit ksi to -25 dB  
  89.     end  
  90.   
  91.     log_sigma_k= gammak.* ksi./ (1+ ksi)- log(1+ ksi);      
  92.     vad_decision= sum(log_sigma_k)/ len;      
  93.     if (vad_decision< eta)   
  94.         % noise only frame found  
  95.         noise_mu2= mu* noise_mu2+ (1- mu)* sig2;  
  96.     end  
  97.     % ===end of vad===  
  98.   
  99.     A=ksi./(1+ksi);  % Log-MMSE estimator  
  100.     vk=A.*gammak;  
  101.     ei_vk=0.5*expint(vk);  
  102.     hw=A.*exp(ei_vk);  
  103.   
  104.     sig=sig.*hw;  
  105.     Xk_prev=sig.^2;  
  106.   
  107.     xi_w= ifft( hw .* spec,nFFT);  
  108.     xi_w= real( xi_w);  
  109.   
  110.     xfinal(k:k+ len2-1)= x_old+ xi_w(1:len1);  
  111.     x_old= xi_w(len1+ 1: len);  
  112.   
  113.     k=k+len2;  
  114.       
  115. end  
  116.   
  117. wavwrite(xfinal,Srate,16,outfile);  
function logmmse(filename,outfile)

%
%  Implements the logMMSE algorithm [1].
% 
%  Usage:  logmmse(noisyFile, outputFile)
%           
%         infile - noisy speech file in .wav format
%         outputFile - enhanced output file in .wav format
%  
%
%  Example call:  logmmse('sp04_babble_sn10.wav','out_log.wav');
%
%  References:
%   [1] Ephraim, Y. and Malah, D. (1985). Speech enhancement using a minimum 
%       mean-square error log-spectral amplitude estimator. IEEE Trans. Acoust., 
%       Speech, Signal Process., ASSP-23(2), 443-445.
%   
% Authors: Philipos C. Loizou
%
% Copyright (c) 2006 by Philipos C. Loizou
% $Revision: 0.0 $  $Date: 10/09/2006 $
%-------------------------------------------------------------------------

if nargin<2
   fprintf('Usage: logmmse(noisyfile.wav,outFile.wav) \n\n');
   return;
end

[x, Srate, bits]= wavread( filename);   %nsdata is a column vector

% =============== Initialize variables ===============

len=floor(20*Srate/1000); % Frame size in samples
if rem(len,2)==1, len=len+1; end;
PERC=50; % window overlap in percent of frame size
len1=floor(len*PERC/100);
len2=len-len1;


win=hamming(len);  % define window


% Noise magnitude calculations - assuming that the first 6 frames is
% noise/silence 

nFFT=2*len;
noise_mean=zeros(nFFT,1);
j=1;
for m=1:2
    noise_mean=noise_mean+abs(fft(win.*x(j:j+len-1),nFFT));
    j=j+len;
end
noise_mu=noise_mean/6;
noise_mu2=noise_mu.^2;

%--- allocate memory and initialize various variables



x_old=zeros(len1,1);
Nframes=floor(length(x)/len2)-floor(len/len2);
xfinal=zeros(Nframes*len2,1);


%===============================  Start Processing =======================================================
%
k=1;
aa=0.98;
mu=0.98;
eta=0.15; 

ksi_min=10^(-25/10);

for n=1:Nframes

    insign=win.*x(k:k+len-1);

    spec=fft(insign,nFFT);
    sig=abs(spec); % compute the magnitude
    sig2=sig.^2;

    gammak=min(sig2./noise_mu2,40);  % limit post SNR to avoid overflows
    if n==1
        ksi=aa+(1-aa)*max(gammak-1,0);
    else
        ksi=aa*Xk_prev./noise_mu2 + (1-aa)*max(gammak-1,0);     % a priori SNR
        ksi=max(ksi_min,ksi);  % limit ksi to -25 dB
    end

    log_sigma_k= gammak.* ksi./ (1+ ksi)- log(1+ ksi);    
    vad_decision= sum(log_sigma_k)/ len;    
    if (vad_decision< eta) 
        % noise only frame found
        noise_mu2= mu* noise_mu2+ (1- mu)* sig2;
    end
    % ===end of vad===

    A=ksi./(1+ksi);  % Log-MMSE estimator
    vk=A.*gammak;
    ei_vk=0.5*expint(vk);
    hw=A.*exp(ei_vk);

    sig=sig.*hw;
    Xk_prev=sig.^2;

    xi_w= ifft( hw .* spec,nFFT);
    xi_w= real( xi_w);

    xfinal(k:k+ len2-1)= x_old+ xi_w(1:len1);
    x_old= xi_w(len1+ 1: len);

    k=k+len2;

end

wavwrite(xfinal,Srate,16,outfile);


下面对带有0dB的白噪声语音进行实验。第一张图是带噪信号,第二张图是matlab代码的去噪信号,第三张图是C代码实现的去噪信号,第四张图是ETSI的维纳滤波(只使用了一级去噪)





C语言实现代码github地址   https://github.com/willhope/Noise-reduction ‘

鉴于很多人需要光盘,我在这里分享下 http://download.csdn.net/detail/zwhlxl/8789731 设置了2个积分,实在没积分的可以留下QQ邮箱,我发给你链接,因为资料很大,我放在了腾讯的云盘里。

                </div>

  常见的语音增强算法有谱减法,MMSE和维纳滤波等。谱减法虽然实现简单,运算量小,但效果比较差,容易出现刺耳的“音乐噪声”。MMSE和维纳滤波虽然原理较复杂,运算量也相对较大,但效果着实不错,甚至可以完全减除“音乐噪声”。由于实验室需要,对语音增强算法相对有点了解,但不够深入。实验室项目增强部分算法采用的是欧洲ETSI的ASR(自动语音识别)的前端去噪。采用的是两级维纳滤波,对于平稳的噪声语音去噪效果相当可观。官网也给出了C语言的实现和文档说明。虽然程序已移植到项目中,但由于文档主要是介绍了方法和一些经验参数,并没有具体说明为什么这样做,总体感觉就是只知道其中的算法流程。下图是ETSI的两级维纳滤波的程序框图。可以看出这个程序的维纳滤波是在梅尔频域上做的,估计是因为梅尔频率更符合人耳特性吧。就像语音识别中提取的特征参数是梅尔倒谱系数MFCC。

由于想进一步了解下语音增强算法的原理,这段时间着重看了MMSE的实现原理,尤其是对数的幅度谱误差平方MMSE-LSA的原理,并对这个算法进行C语言实现,最终结果与matlab代码一致。下面简要介绍下原理:

根据MMSE-LSA的开山始祖的文章Ephraim, Y. and Malah, D. (1985).《 Speech enhancement using a minimum 
mean-square error log-spectral amplitude estimator.》假设带噪信号y(n) = x(n) + d(n),其中x(n)为纯净信号,d(n)为噪声信号,转换为频域,Y(w) = X(w) + D(w)。根据论文推导出最优的对数MMSE估计器为:

  


其中就是对数MMSE估计器的增益函数。在这个函数中有两个未知量,分别是先验信噪比(可以被看作是第k个频谱分量的实际信噪比)和后验信噪比(可以看作是加入噪声后第k个频谱分量测得的信噪比)


其中 表示纯净信号谱的第k个频谱分量的方差。表示噪声频谱的第k个频谱分量的方差。从上面式子当中,只有Yk是已知的,即就是带噪信号的频域分量。如果假设噪声是平稳信号,噪声方差可以用VAD检测非语音段来不断更新。而纯净信号的方差则需要通过一定的方法估算出来,文章给出的是判决引导法,推导的结果是


其中a是平滑系数,一般取0.98。一些文章对上式提出改进方法,比如限定先验信噪比的最小值等。下面是MMSE-LSA的程序框图



MMSE-LSA算法实现步骤

1、对带噪信号分帧,加窗(这里是汉明窗)

2、对每帧带噪信号计算FFT ;  

3、估计后验信噪比 :    这里 是在非语音片段(语音开始之前几帧或语音间隙)估计的噪声能量谱;然后用判决引导法估计先验信噪比;

4、用最优的MMSE-LSA估计器的公式估计增强信号幅度;

5、重建增强信号谱,然后计算增强信号谱的IFFT,得到对应输入语音帧的增强的时域信号x(n)。


下面是matlab代码的程序(来自《语音增强-理论与实践》光盘,这本书是语音大牛Philipos C.Loizou的大作。这本书对语音增强方面的主流算法原理逐一介绍和推导,更难能可贵的是针对每一个算法给出了Matlab实例,是本难得好书)

  1. function logmmse(filename,outfile)  
  2.   
  3. %  
  4. %  Implements the logMMSE algorithm [1].  
  5. %   
  6. %  Usage:  logmmse(noisyFile, outputFile)  
  7. %             
  8. %         infile - noisy speech file in .wav format  
  9. %         outputFile - enhanced output file in .wav format  
  10. %    
  11. %  
  12. %  Example call:  logmmse(‘sp04_babble_sn10.wav’,’out_log.wav’);  
  13. %  
  14. %  References:  
  15. %   [1] Ephraim, Y. and Malah, D. (1985). Speech enhancement using a minimum   
  16. %       mean-square error log-spectral amplitude estimator. IEEE Trans. Acoust.,   
  17. %       Speech, Signal Process., ASSP-23(2), 443-445.  
  18. %     
  19. % Authors: Philipos C. Loizou  
  20. %  
  21. % Copyright (c) 2006 by Philipos C. Loizou  
  22. Revision:&nbsp;0.0&nbsp;    Date:&nbsp;10/09/2006&nbsp;   
  23. %————————————————————————-  
  24.   
  25. if nargin<2  
  26.    fprintf(‘Usage: logmmse(noisyfile.wav,outFile.wav) \n\n’);  
  27.    return;  
  28. end  
  29.   
  30. [x, Srate, bits]= wavread( filename);   %nsdata is a column vector  
  31.   
  32. % =============== Initialize variables ===============  
  33.   
  34. len=floor(20*Srate/1000); % Frame size in samples  
  35. if rem(len,2)==1, len=len+1; end;  
  36. PERC=50; % window overlap in percent of frame size  
  37. len1=floor(len*PERC/100);  
  38. len2=len-len1;  
  39.   
  40.   
  41. win=hamming(len);  % define window  
  42.   
  43.   
  44. % Noise magnitude calculations - assuming that the first 6 frames is  
  45. % noise/silence   
  46.   
  47. nFFT=2*len;  
  48. noise_mean=zeros(nFFT,1);  
  49. j=1;  
  50. for m=1:2  
  51.     noise_mean=noise_mean+abs(fft(win.*x(j:j+len-1),nFFT));  
  52.     j=j+len;  
  53. end  
  54. noise_mu=noise_mean/6;  
  55. noise_mu2=noise_mu.^2;  
  56.   
  57. %— allocate memory and initialize various variables  
  58.   
  59.   
  60.   
  61. x_old=zeros(len1,1);  
  62. Nframes=floor(length(x)/len2)-floor(len/len2);  
  63. xfinal=zeros(Nframes*len2,1);  
  64.   
  65.   
  66. %===============================  Start Processing =======================================================  
  67. %  
  68. k=1;  
  69. aa=0.98;  
  70. mu=0.98;  
  71. eta=0.15;   
  72.   
  73. ksi_min=10^(-25/10);  
  74.   
  75. for n=1:Nframes  
  76.   
  77.     insign=win.*x(k:k+len-1);  
  78.   
  79.     spec=fft(insign,nFFT);  
  80.     sig=abs(spec); % compute the magnitude  
  81.     sig2=sig.^2;  
  82.   
  83.     gammak=min(sig2./noise_mu2,40);  % limit post SNR to avoid overflows  
  84.     if n==1  
  85.         ksi=aa+(1-aa)*max(gammak-1,0);  
  86.     else  
  87.         ksi=aa*Xk_prev./noise_mu2 + (1-aa)*max(gammak-1,0);     % a priori SNR  
  88.         ksi=max(ksi_min,ksi);  % limit ksi to -25 dB  
  89.     end  
  90.   
  91.     log_sigma_k= gammak.* ksi./ (1+ ksi)- log(1+ ksi);      
  92.     vad_decision= sum(log_sigma_k)/ len;      
  93.     if (vad_decision< eta)   
  94.         % noise only frame found  
  95.         noise_mu2= mu* noise_mu2+ (1- mu)* sig2;  
  96.     end  
  97.     % ===end of vad===  
  98.   
  99.     A=ksi./(1+ksi);  % Log-MMSE estimator  
  100.     vk=A.*gammak;  
  101.     ei_vk=0.5*expint(vk);  
  102.     hw=A.*exp(ei_vk);  
  103.   
  104.     sig=sig.*hw;  
  105.     Xk_prev=sig.^2;  
  106.   
  107.     xi_w= ifft( hw .* spec,nFFT);  
  108.     xi_w= real( xi_w);  
  109.   
  110.     xfinal(k:k+ len2-1)= x_old+ xi_w(1:len1);  
  111.     x_old= xi_w(len1+ 1: len);  
  112.   
  113.     k=k+len2;  
  114.       
  115. end  
  116.   
  117. wavwrite(xfinal,Srate,16,outfile);  
function logmmse(filename,outfile)

%
%  Implements the logMMSE algorithm [1].
% 
%  Usage:  logmmse(noisyFile, outputFile)
%           
%         infile - noisy speech file in .wav format
%         outputFile - enhanced output file in .wav format
%  
%
%  Example call:  logmmse('sp04_babble_sn10.wav','out_log.wav');
%
%  References:
%   [1] Ephraim, Y. and Malah, D. (1985). Speech enhancement using a minimum 
%       mean-square error log-spectral amplitude estimator. IEEE Trans. Acoust., 
%       Speech, Signal Process., ASSP-23(2), 443-445.
%   
% Authors: Philipos C. Loizou
%
% Copyright (c) 2006 by Philipos C. Loizou
% $Revision: 0.0 $  $Date: 10/09/2006 $
%-------------------------------------------------------------------------

if nargin<2
   fprintf('Usage: logmmse(noisyfile.wav,outFile.wav) \n\n');
   return;
end

[x, Srate, bits]= wavread( filename);   %nsdata is a column vector

% =============== Initialize variables ===============

len=floor(20*Srate/1000); % Frame size in samples
if rem(len,2)==1, len=len+1; end;
PERC=50; % window overlap in percent of frame size
len1=floor(len*PERC/100);
len2=len-len1;


win=hamming(len);  % define window


% Noise magnitude calculations - assuming that the first 6 frames is
% noise/silence 

nFFT=2*len;
noise_mean=zeros(nFFT,1);
j=1;
for m=1:2
    noise_mean=noise_mean+abs(fft(win.*x(j:j+len-1),nFFT));
    j=j+len;
end
noise_mu=noise_mean/6;
noise_mu2=noise_mu.^2;

%--- allocate memory and initialize various variables



x_old=zeros(len1,1);
Nframes=floor(length(x)/len2)-floor(len/len2);
xfinal=zeros(Nframes*len2,1);


%===============================  Start Processing =======================================================
%
k=1;
aa=0.98;
mu=0.98;
eta=0.15; 

ksi_min=10^(-25/10);

for n=1:Nframes

    insign=win.*x(k:k+len-1);

    spec=fft(insign,nFFT);
    sig=abs(spec); % compute the magnitude
    sig2=sig.^2;

    gammak=min(sig2./noise_mu2,40);  % limit post SNR to avoid overflows
    if n==1
        ksi=aa+(1-aa)*max(gammak-1,0);
    else
        ksi=aa*Xk_prev./noise_mu2 + (1-aa)*max(gammak-1,0);     % a priori SNR
        ksi=max(ksi_min,ksi);  % limit ksi to -25 dB
    end

    log_sigma_k= gammak.* ksi./ (1+ ksi)- log(1+ ksi);    
    vad_decision= sum(log_sigma_k)/ len;    
    if (vad_decision< eta) 
        % noise only frame found
        noise_mu2= mu* noise_mu2+ (1- mu)* sig2;
    end
    % ===end of vad===

    A=ksi./(1+ksi);  % Log-MMSE estimator
    vk=A.*gammak;
    ei_vk=0.5*expint(vk);
    hw=A.*exp(ei_vk);

    sig=sig.*hw;
    Xk_prev=sig.^2;

    xi_w= ifft( hw .* spec,nFFT);
    xi_w= real( xi_w);

    xfinal(k:k+ len2-1)= x_old+ xi_w(1:len1);
    x_old= xi_w(len1+ 1: len);

    k=k+len2;

end

wavwrite(xfinal,Srate,16,outfile);


下面对带有0dB的白噪声语音进行实验。第一张图是带噪信号,第二张图是matlab代码的去噪信号,第三张图是C代码实现的去噪信号,第四张图是ETSI的维纳滤波(只使用了一级去噪)





C语言实现代码github地址   https://github.com/willhope/Noise-reduction ‘

鉴于很多人需要光盘,我在这里分享下 http://download.csdn.net/detail/zwhlxl/8789731 设置了2个积分,实在没积分的可以留下QQ邮箱,我发给你链接,因为资料很大,我放在了腾讯的云盘里。

                </div>

猜你喜欢

转载自blog.csdn.net/fangbingxiao/article/details/80037662
今日推荐