[置顶] 基于对数MMSE的语音增强算法

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

[置顶] 基于对数MMSE的语音增强算法_第1张图片

由于想进一步了解下语音增强算法的原理,这段时间着重看了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的语音增强算法_第2张图片  


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


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


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

[置顶] 基于对数MMSE的语音增强算法_第3张图片


MMSE-LSA算法实现步骤

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

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

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

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

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


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

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的维纳滤波(只使用了一级去噪)

[置顶] 基于对数MMSE的语音增强算法_第4张图片

[置顶] 基于对数MMSE的语音增强算法_第5张图片

[置顶] 基于对数MMSE的语音增强算法_第6张图片

[置顶] 基于对数MMSE的语音增强算法_第7张图片

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

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

你可能感兴趣的:(算法,MMSE,语音增强)