语音识别之MFCC

在进行端点处理之后,就可以得到需要处理的信号。但是要进行语音识别就必须进行一个处理:特征提取。进行特征提取我们这里采用的就是FMCC。

具体的流程是怎么样的呢?

那就是:


概述:


MFCC:Mel频率倒谱系数的缩写。Mel频率是基于人耳听觉特性提出来的,它与Hz频率成非线性对应关系。Mel频率倒谱系数(MFCC)则是利用它们之间的这种关系,计算得到的Hz频谱特征。


应用:


MFCC已经广泛地应用在语音识别领域。由于Mel频率与Hz频率之间非线性的对应关系,使得MFCC随着频率的提高,其计算精度随之下降。因此,在应用中常常只使用低频MFCC,而丢弃中高频MFCC。


提取流程 :


MFCC参数的提取包括以下几个步骤:

1. 预滤波:CODEC前端带宽为300-3400Hz的抗混叠滤波器。

2. A/D变换:8kHz的采样频率,12bit的线性量化精度。

3. 预加重:通过一个一阶有限激励响应高通滤波器,使信号的频谱变得平坦,不易受到有限字长效应的影响。


4. 分帧:根据语音的短时平稳特性,语音可以以帧为单位进行处理,实验中选取的语音帧长为32ms,帧叠为16ms。

5. 加窗:采用哈明窗对一帧语音加窗,以减小吉布斯效应的影响。

参考:

以上matlab部分来自于: http://www.ee.columbia.edu/~dpwe/resources/matlab/rastamat/

以及截图来自于JIE的李明老师的ppt。


6. 快速傅立叶变换(Fast Fourier Transformation,FFT):将时域信号变换成为信号的功率谱。

FFT可以用IPP实现,但是用起来好复杂。我找到更简单粗暴的方法:

下面给的C++代码的fft其实是有错的,需要细心的读者发现问题,参考下面两篇博客你就可以知道下面的c++代码错在哪里了。

http://blog.csdn.net/hippig/article/details/8778753

http://bbs.csdn.net/topics/300168593

7.三角窗滤波:用一组Mel频标上线性分布的三角窗滤波器(共24个三角窗滤波器),对信号的功率谱滤波,每一个三角窗滤波器覆盖的范围都近似于人耳的一个临界带宽,以此来模拟人耳的掩蔽效应。

8. 求对数:三角窗滤波器组的输出求取对数,可以得到近似于同态变换的结果。

9. 离散余弦变换(Discrete Cosine Transformation,DCT):去除各维信号之间的相关性,将信号映射到低维空间。

        把信号转换到频域之后就更好处理了。在这里我们对于DFT做一些简单的介绍:离散傅立叶变化不能对非周期信号进行处理。我们把这个信号进行处理的时候看作是无限长的周期信号的一个周期。我们可以把我们要做的那部分信号进行扩展成周期性无限的信号进行处理。

可以转换成:

计算公式如下:


10.谱加权:由于倒谱的低阶参数易受说话人特性、信道特性等的影响,而高阶参数的分辨能力比较低,所以需要进行谱加权,抑制其低阶和高阶参数。

11. 倒谱均值减(Cepstrum Mean Subtraction,CMS):CMS可以有效地减小语音输入信道对特征参数的影响。 
12.差分参数:大量实验表明,在语音特征中加入表征语音动态特性的差分参数,能够提高系统的识别性能。在本系统中,我们也用到了MFCC参数的一阶差分参数和二阶差分参数。 
13. 短时能量:语音的短时能量也是重要的特征参数,本系统中我们采用了语音的短时归一化对数能量及其一阶差分、二阶差分参数。


流程图:



代码:


//ASR.h
#include 
#include 

#define MAXDATA (256*400)  //一般采样数据大小,语音文件的数据不能大于该数据
#define SFREMQ (8000)   //采样数据的采样频率8khz

typedef struct WaveStruck//wav数据结构
{
 //data head
 struct HEAD{
  char cRiffFlag[4];
  int nFileLen;
  char cWaveFlag[4];//WAV文件标志
  char cFmtFlag[4];
  int cTransition;
  short nFormatTag;
  short nChannels;
  int nSamplesPerSec;//采样频率,mfcc为8khz
  int nAvgBytesperSec;
  short nBlockAlign;
  short nBitNumPerSample;//样本数据位数,mfcc为12bit
 } head;

 //data block
 struct BLOCK{
  char cDataFlag[4];//数据标志符(data)
  int nAudioLength;//采样数据总数
 } block;
} WAVE;


typedef struct //定义实数结构
{
  double real;
  double image;
}complex;

//获取wav数据样本数据,sample数组的长度至少为MAXDATA,函数返回读入的数据的长度
int getWaveData(char* file,double *sample);

int copyData(double *src,double *dest,int num);//实数数据复制

//所有mfcc操作都是基于每帧操作的,len为总长度,unitsize为采样点组成的观察单位,256||512
//emp_v为欲加重因子,hanming_v为汉明窗因子,fft_v为傅里叶变化级数
//filter_v为mel滤波器个数,dct_v为dct变换因子
//函数返回mfcc识别后特征参数数组,数组长度存储在第0个元素中
double* MFCC(double *sample,int len ,int unitsize=256,double emp_v=0.97,double hanming_v=0.46,int filter_v=20,int dct=12);

//MFCC欲加重,len为数组sample的长度,factor为加重因子
bool _mfcc_preEmphasize(double *sample,int len,double factor=0.9);

//框化数据即分帧,同事在原始采样数据中插入重叠区数据
//函数返回总的帧数,和分帧后的数据sample
int _mfcc_Frame(double *sample,int unitsize,int len);

//加汉明窗,factor为汉明窗因子
bool _mfcc_HanmingWindow(double *sample,int unitsize,int frames,double hanming_factor=0.46);

//快速傅里叶变换,输入参数sample为实数,无虚数部分,len为sample的总大小,也为傅里叶变换点数
//函数返回时complex为fft变换后的频率能量复数数值,总长度为len,频率从0-SFREMQ(8khz)
complex* _mfcc_FFT(double *sample,int len);

//功率计算,返回计算后的功率实数powersample,总长度为len,频率从0-SFREMQ(8khz)
bool  _mfcc_Power(complex* fftdata,double *powersample,int len);

//三角滤波,len为powersample数字长度,num为mei滤波个数
//函数返回mel频率下的滤波器内的对数能量double*(长度为num)
double* _mfcc_filter(double *powersample,int len,int num);

//离散余弦变换,lnpower(长度为len)为滤波器内积对数能量,num与mel滤波器一致,dctnum为dct变换级数
//函数返回时double*的长度为dctnum,代表dct变换后的倒频参数
double* _mfcc_DCT(double *lnpower,int len,int dctnum);

//求音框1的能量+音框2的能量+....+音框frames的能量+dct参数
//函数返回double *,其长度为(dctlen+1)*frames
double* _mfc_Logenergy(double *powersample,int unitsize,int frames,double * dctdata,int dctlen);

//差量倒频谱参数,函数返回double*,数组长度为len*2
double* _mfcc_differential(double *logenergy,int len);

 

//asp.cpp
#include "stdafx.h"
#include "ASR.h"

//获取wav数据样本数据
int getWaveData(char* file,double *sample)
{
 WAVE wave[1];
 FILE * f;
 f = fopen(file,"rb");
 if(!f)
 {
  printf("Cannot open %s for reading\n",file);
  return -1;
 }
 
 //读取wav文件头并且分析
 fread(wave,1,sizeof(wave),f);
 
 if(wave[0].head.cWaveFlag[0]=='W'&&wave[0].head.cWaveFlag[1]=='A'
  &&wave[0].head.cWaveFlag[2]=='V'&&wave[0].head.cWaveFlag[3]=='E')//判断是否是wav文件
 {
  printf("It's not .wav file\n" );
  return -1;
 }
 if(wave[0].head.nSamplesPerSec!=SFREMQ&&wave[1].head.nBitNumPerSample!=12)//判断是否采样频率是8khz,12bit量化
 {
  printf("It's not 8khz and 12 bit\n" );
  return -1;
 }
 
 if(wave[0].block.nAudioLength>MAXDATA/2)//wav文件不能太大,为sample长度的一半
 {
  printf("wav file is to long\n" );
  return -1;
 }
 
 //读取采样数据 
 fread(sample,sizeof(char),wave[0].block.nAudioLength,f);
 fclose(f);
 return wave[0].block.nAudioLength;
}

int copyData(double *src,double *dest,int num)
{
 if(src==NULL||dest==NULL)
 {
  printf("src and dest are NULL\n");
  return -1;
 }
 
 for(int i=0;i512)
 {
  printf("unitsize of mfcc is biger than 512");
  return false;
 }
 
 //因为要操作附加的重叠区域,故分成三部分,第一部分数据不变
 //开始和结尾部分不包含附加重叠区的操作
 
 //中间部分,包括附件重叠区域变换
 
 int m=unitsize/2;//重叠区域的单位大小为正常区的一半
 int frames=2;//加上重叠区后的帧数
 int index=unitsize;//当前添加后的数据的排列序号
 
 for(int i=unitsize;i=1||hanming_factor<=0)
 {
  printf("hanming hanming_factor 0-1\n");
  return false; 
 }
 
 if(unitsize>512)
 {
  printf("unitsize of mfcc is biger than 512");
  return false;
 }
 
 
 int windowsize;//框化后的窗口大小
 
 double HanmingWindow[512];//汉明窗口乘法因子
 double fDataTemp;
 
 for(int i=0;i>1;//变换级数
 
 W = new complex[count / 2];
 X1 = new complex[count];
 X2 = new complex[count]; // 分配运算所需存储器
 
 // 计算加权系数(旋转因子w的i次幂表)
 for(i = 0; i < count / 2; i++)
 {
  angle = -i * 3.141592653589793 * 2 / count;
  W[ i ].real=cos(angle);
  W[ i ].image=sin(angle);
 }
 // 将时域点写入X1
 memcpy(X1, TD, sizeof(complex) * count);
 // 采用蝶形算法进行快速Fourier变换
 for(k = 0; k < r; k++ )
 {
  for(j = 0; j < (1 << k); j++ )
  {
   bfsize = 1 << (r-k);
   for(i = 0; i < bfsize / 2; i++ )
   {
    p = j * bfsize;
    X2[i + p].real = X1[i + p].real + X1[i + p + bfsize / 2].real * W[i * (1<mel频率
 double* lnpower=new double(filterNum);//对数能量
 memset(lnpower,0,filterNum);
 
 double f=0;
 for(int i=0;ifilterNum)//跳过最右边半个滤波器数据
   break;
  filterpara=1+(melf[i]-j*filterFW)/filterFW; //斜率为1,上升 
  powersample[i]=filterpara*powersample[i]; 
  lnpower[j-1]+=powersample[i];//累计一个滤波器内的能量
 }
 
 for(i=0;i

以上代码转载自:http://blog.chinaunix.net/uid-26715658-id-3256938.html


以下是matlab代码:

以下的代码用的是matlab的工具箱voice box里面的代码,可以直接使用,也可以添加到matlab的工具箱!安装做法如下:

http://zhidao.baidu.com/link?url=NsLV97zR0LEyK4Cyn1rTuGpVd4qFpqJYVyaUtV53L9JJM1O7-kT-ErVWJQFXu9EQiRSVo7KWioYym3MpDLHFFa

参考代码:

http://blog.sina.com.cn/s/blog_4e0987310102v3sg.html

http://zhidao.baidu.com/link?url=vCqDKDZRsgZlva_gSZbXFfjyak0O2sxSe_822X5qwXNgeF4YlWzRMNBP-9dhF5agrvgKIQ3dZc3xy9DhV51TRa

http://www.verysource.com/code/7160767_1/mfcc.txt.html


%function ccc=mfcc(x)   

%归一化mel滤波器组系数   

filename=input('input filename:','s');

[x,fs,bits]=wavread(filename);

bank=melbankm(24,256,fs,0,0.5,'m');   

bank=full(bank);   

bank=bank/max(bank(:));   

%T系数,12*24   

for k=1:12   

    n=0:23;   

    dctcoef(k,:)=cos((2*n+1)*k*pi/(2*24));   

end   

%归一化倒谱提升窗口   

w=1+6*sin(pi*[1:12] ./12);   

w=w/max(w);   

%预加重滤波器   

xx=double(x);   

xx=filter([1 -0.9375],1,xx);   

%语音信号分帧   

xx=enframe(xx,256,80);   

%计算每帧的MFCC参数   

for i=1:size(xx,1)   

    y=xx(i,:)   

    s=y' .*hamming(256);   

    t=abs(fft(s));   

    t=t.^2;   

    c1=dctcoef*log(bank*t(1:129));   

    c2=c1.*w';   

    m(i,:)=c2';   

end   

%差分参数   

dtm=zeros(size(m));   

for i=3:size(m,1)-2   

    dtm(i,:)=-2*m(i-2,:)-m(i-1,:)+m(i+1,:)+2*m(i+2,:);   

end   

dtm=dtm/3;   

%合并mfcc参数和一阶差分mfcc参数   

ccc=[m dtm];   

%去除首位两帧,因为这两帧的一阶差分参数为0   

ccc=ccc(3:size(m,1)-2,:);

subplot(211)

ccc_1=ccc(:,1);

plot(ccc_1);title('MFCC');

% ylabel('幅值');

% title('一维数组及其幅值的关系')

% [h,w]=size(ccc);

% A=size(ccc);

% subplot(212)

% plot([1,w],A);

% xlabel('维数');

% ylabel('幅值');

% title('维数于幅值的关系')

在最后再给出一份代码,这个是测试过很ok的:

MFCC.h

#ifndef _MFCC_H_

#define FRAMES_PER_BUFFER (400)
#define NOT_OVERLAP (200)
#define NUM_FILTER (40)
void MFCC(const short* waveData, int numSamples, int sampleRate);
void FFT_Power(float *in, float *energySpectrum);
void computeMel(float *mel, int sampleRate, const float *energySpectrum);
void DCT(const float *mel, float *c);


#endif


MFCC.c

#include "MFCC.h"
#include 
#include 
#include 
#include "fftw3.h"
using namespace std;


void MFCC(const short* waveData, int numSamples, int sampleRate)
{
	float *preemp = new float[numSamples];
	preemp[0] = waveData[0];
	for(int i = 1; i < numSamples; i++)
	{
		preemp[i] = waveData[i] - 0.95*waveData[i-1];
		//printf("%f ", preemp[i]);
	}
	//printf("\n");
	//calculate the total frames when overlaps exist
	int numFrames =  ceil((numSamples-FRAMES_PER_BUFFER)/NOT_OVERLAP)+1;
	//printf("%d\n", numFrames);
	float hammingWindow[FRAMES_PER_BUFFER];
	float afterWin[512] = {0.0};
	float energySpectrum[512] = {0.0};
	float mel[NUM_FILTER] = {0};
	float c[13] = {0};
	for(int i = 0; i< FRAMES_PER_BUFFER; i++)
	{
		hammingWindow[i] = 0.54 - 0.46*cos(2*3.14*i/(FRAMES_PER_BUFFER-1));
	}
	//handle all frames one by one
	for(int i = 0; i < numFrames; i++)
	{
		int j;
		//windowing
		for(j=0; j=melFilters[i][0] && jmelFilters[i][1] && j<=melFilters[i][2])
			{
				mel[i] += ((j-melFilters[i][2])/(melFilters[i][1]-melFilters[i][2])) * energySpectrum[j];
			}
		}
	}
}

void DCT(const float *mel, float *c)
{
	for(int i = 0; i < 13; i++)
	{
		for(int j = 0; j < NUM_FILTER; j++)
		{
			if(mel[j] <= -0.0001 || mel[j] >= 0.0001)
				c[i] += log(mel[j])*cos(3.14*i/(2*NUM_FILTER)*(2*j+1));
		}
	}
}

要下载那个库fftw3:

http://download.csdn.net/detail/c602273091/8790239

你不会呀,我教你呀~

你可能感兴趣的:(CC,&,Speech,Recognition)