基於動態時間規整(DTW)的孤立字語音識別


注意!這個博客里給出的文件是 在matlab2012版本里使用的,而我學習時使用的時matlab2018b,因此發現了很多新版本不兼容的問題,但我沒有給出修改后能直接在新版本中用的代碼。

另外,matlab要安裝voicebox才可以正常進行實驗!

還有一件事,錄音文件要修改好文件名和文件目錄,不然會檢測不到。

 
 

這個實驗在沒有同學和老師的指導和幫助下完成,一共花了大概兩周多(也沒有每天做啦),請珍惜勞動成果,不要直接拿去裝逼。

 
 

有個關鍵函數提前說一下:

DTW函數:myDTW

功能:利用DTW完成模式匹配

調用格式:

[cost] = myDTW(F,R)

參數說明:輸入參數F為模板的MFCC特征;R為當前待識別語音的MFCC。輸出參數cost為F和R之間的匹配距離。

 
 

看看目錄

 
 
 
 
 

下面是具體的程序清單:

 
function [cost] = myDTW(F,R)
[r1,c1] = size(F); %模板的維度
[r2,c2] = size(R); %當前待識別語音的維度
distance = zeros(r1,r2);
for n = 1:r1
for m=1:r2
FR = F(n,:)-R(m,:);
FR = FR.^2;
distance(n,m) = sqrt(sum(FR))/c1; %采用歐式距離度量F和R的相似程度
end
end
D=zeros(r1+1,r2+1);
D(1,:)=inf;
D(:,1)=inf;
D(1,1)=0;
D(2:(r1+1),2:(r2+1)) =distance;
%尋找整個過程的最短匹配距離
for i=1:r1;
for j= 1:r2;
[dmin] = min([D(i,j),D(i,j+1),D(i+1,j)]);
D(i+1,j+1) =D(i+1,j+1) +dmin;
end
end
cost =D(r1+1,r2+1); %返回最終距離,沒有反向回溯

  

 
 
 
 

這個基於動態時間規整(DTW)的孤立字語音識別的實驗,具體步驟有很多,都寫在下面了:

(1)訓練過程:運行setTemplates.m文件

1)實驗的語音來自三個人,每人說一遍0~9這十個數字,記錄下相應的語音,保存在 目錄“SpeechData”中。以第一個人的語音“1”為例,首先,調用[speechIn1,FS1] =audioread(q)讀入語音“1”,保存在speechIn1中,並獲得采樣率FS1。

2)對speechIn1進行預處理,首先調用my_vad函數進行端點檢測,提取出有效語音段,然后調用mfccf函數對有效語音段提取mfcc特征。

3)以同樣的方式處理其他訓練語音,把第一/二/三個說話人的十個數字0~9語音分別保存s1/s2/s3結構體中,並最終保存在Vectors1.mat/Vectors2.mat/Vectors3.mat文件中。

(2)識別過程:運行matchTemplates.m文件

1) 首先程序提示“Press any key to start 2 seconds of speech recording…”,在MATLAB主窗口按任意鍵開始錄入待識別的語音,程序將錄入的語音保存在speechIn變量中。

2) 對speechIn進行預處理,包括端點檢測和mfcc特征提取,這一過程和訓練中完全相同,得到(NP)矩陣。其中N為幀數,P為維度,接着調用CMN函數,對每一維度分量進行標准化,結果保存在rMatrix中。

3) 調用DTWScores函數,將當前待識別語音的rMatrix與訓練過程中保存的s1、s2、s3中 的0~9各數字匹配,此函數中調用myDTW的匹配過程,返回rMarix和每個說話人說的每一個數字的匹配分值,保存在Sco中((3*10)的矢量)。

4) 將Sco從大到小進行排序,首先得到每一個模板匹配的兩個最低分值對應的數字序號(有2*3=6個序號),而后返回出現頻率最高的序號作為判決結果,從而完成過程。

 
 
 
 

但是講這么多,還是很難把實驗完成對不對,現在我把全部用到的都放出來

 

識別過程matchTemplates.m程序清單:

 
clear all; 
close all;
ncoeff = 12; %MFCC參數階數
N = 10; %10個數字
fs=16000; %采樣頻率 
duration2 = 2; %錄音時長
k = 3; %訓練樣本的人數
speech = audiorecorder(fs,16,1);
disp('Press any key to start 2 seconds of speech recording...'); 
pause
disp('Recording speech...'); 
recordblocking(speech,duration2) % duration*fs 為采樣點數 
speechIn=getaudiodata(speech);
disp('Finished recording.');
disp('System is trying to recognize what you have spoken...');
speechIn = my_vad(speechIn); %端點檢測 
rMatrix1 = mfccf(ncoeff,speechIn,fs); %采用MFCC系數作為特征矢量
rMatrix = CMN(rMatrix1); %歸一化處理 
Sco = DTWScores(rMatrix,N); %計算DTW值
[SortedScores,EIndex] = sort(Sco,2); %按行遞增排序,並返回對應的原始次序
Nbr = EIndex(:,1:2) %得到每個模板匹配的2個最低值對應的次序
[Modal,Freq] = mode(Nbr(:)); %返回出現頻率最高的數Modal及其出現頻率Freq
Word = char('zero','One','Two','Three','Four','Five','Six','Seven','Eight','Nine'); 
if mean(abs(speechIn)) < 0.01
 fprintf('No microphone connected or you have not said anything.\n');
else if (Freq <2) %頻率太低不確定
 fprintf('The word you have said could not be properly recognised.\n');
else
 fprintf('You have just said %s.\n',Word(Modal,:)); 
end

  

 

 

訓練過程setTemplates----程序清單:

 
%設置模板
ncoeff=12; %mfcc系數的個數
fMatrix1 = cell(1,10);
fMatrix2 = cell(1,10);
fMatrix3 = cell(1,10); 
for i = 1:10
 q = ['F:\ SpeechData\p1\' num2str(i-1) '.wav'];
 [speechIn1,FS1] = audioread(q);
 speechIn1 = my_vad(speechIn1); 
 fMatrix1(1,i) = {mfccf(ncoeff,speechIn1,FS1)}; 
  
end
for j = 1:10
 q = ['F: \SpeechData\p2\' num2str(j-1) '.wav'];
 [speechIn2,FS2] = audioread(q);
 speechIn2 = my_vad(speechIn2); 
 fMatrix2(1,j) = {mfccf(ncoeff,speechIn2,FS2)}; 
end
for k = 1:10
 q = ['F: \SpeechData\p2\' num2str(k-1) '.wav'];
 [speechIn3,FS3] = audioread(q);
 speechIn3 = my_vad(speechIn3); 
 fMatrix3(1,k) = {mfccf(ncoeff,speechIn3,FS3)};
end
%將數據保存為mat文件
fields = {'zero','One','Two','Three','Four','Five','Six','Seven','Eight','nine'};
s1 = cell2struct(fMatrix1, fields, 2); %fields項作為行
save Vectors1.mat -struct s1;
s2 = cell2struct(fMatrix2, fields, 2);
save Vectors2.mat -struct s2;
s3 = cell2struct(fMatrix3, fields, 2);
save Vectors3.mat -struct s3;

  

 

 

端點檢測my_vad()函數----程序清單:

 
function trimmed_X = my_vad(x)
%端點檢測;輸入為錄入語音,輸出為有用信號
Ini = 0.1; %初始靜默時間
Ts = 0.01; %窗的時長
Tsh = 0.005; %幀移時長
Fs = 16000; %采樣頻率
counter1 = 0; %以下四個參數用來尋找起始點和結束點
counter2 = 0;
counter3 = 0;
counter4 = 0;
ZCRCountf = 0; %用於存儲過零率檢測結果
ZCRCountb = 0; 
ZTh = 40; %過零閾值
w_sam = fix(Ts*Fs); %窗口長度
o_sam = fix(Tsh*Fs); %幀移長度
lengthX = length(x);
segs = fix((lengthX-w_sam)/o_sam)+1; %分幀數
sil = fix((Ini-Ts)/Tsh)+1; %靜默時間幀數
win = hamming(w_sam);
Limit = o_sam*(segs-1)+1; %最后一幀的起始位置
FrmIndex = 1:o_sam:Limit; %每一幀的起始位置
ZCR_Vector = zeros(1,segs); %記錄每一幀的過零點數 
%短時過零點
for t = 1:segs
 ZCRCounter = 0; 
 nextIndex = (t-1)*o_sam+1;
 for r = nextIndex+1:(nextIndex+w_sam-1)
 if (x(r) >= 0) && (x(r-1) >= 0)
 
 elseif (x(r) > 0) && (x(r-1) < 0)
 ZCRCounter = ZCRCounter + 1;
 elseif (x(r) < 0) && (x(r-1) < 0)
 
 elseif (x(r) < 0) && (x(r-1) > 0)
 ZCRCounter = ZCRCounter + 1;
 end
 end
 ZCR_Vector(t) = ZCRCounter;
end
%短時平均幅度
Erg_Vector = zeros(1,segs);
for u = 1:segs
 nextIndex = (u-1)*o_sam+1;
 Energy = x(nextIndex:nextIndex+w_sam-1).*win;
 Erg_Vector(u) = sum(abs(Energy));
end
IMN = mean(Erg_Vector(1:sil));  %靜默能量均值(噪聲均值)
IMX = max(Erg_Vector); %短時平均幅度的最大值
I1 = 0.03 * (IMX-IMN) + IMN;  %I1,I2為初始能量閾值
I2 = 4 * IMN;
ITL = 100*min(I1,I2); %能量閾值下限,前面系數根據實際情況更改得到合適結果
ITU = 10* ITL;  %能量閾值上限
IZC = mean(ZCR_Vector(1:sil)); 
stdev = std(ZCR_Vector(1:sil));  %靜默階段過零率標准差
IZCT = min(ZTh,IZC+2*stdev);  %過零率閾值
indexi = zeros(1,lengthX); 
indexj = indexi; 
indexk = indexi;
indexl = indexi;
%搜尋超過能量閾值上限的部分
for i = 1:length(Erg_Vector)
 if (Erg_Vector(i) > ITU)
 counter1 = counter1 + 1;
 indexi(counter1) = i;
 end
end
ITUs = indexi(1); %第一個能量超過閾值上限的幀
%搜尋能量超過能量下限的部分
for j = ITUs:-1:1
 if (Erg_Vector(j) < ITL)
 counter2 = counter2 + 1;
 indexj(counter2) = j;
 end
end
start = indexj(1)+1; %第一級判決起始幀
 
Erg_Vectorf = fliplr(Erg_Vector);%將能量矩陣關於中心左右對稱,若是一行向量相當於逆序 
%重復上面過程相當於找結束幀
for k = 1:length(Erg_Vectorf)
 if (Erg_Vectorf(k) > ITU)
 counter3 = counter3 + 1;
 indexk(counter3) = k;
 end
end
ITUf = indexk(1);
for l = ITUf:-1:1
 if (Erg_Vectorf(l) < ITL)
 counter4 = counter4 + 1;
 indexl(counter4) = l;
 end
end
finish = length(Erg_Vector)-indexl(1)+1;%第一級判決結束幀 
%從第一級判決起始幀開始進行第二判決(過零率)端點檢測
BackSearch = min(start,25);
for m = start:-1:start-BackSearch+1
 rate = ZCR_Vector(m);
 if rate > IZCT
 ZCRCountb = ZCRCountb + 1;
 realstart = m;
 end
end
if ZCRCountb > 3
 start = realstart; 
end
FwdSearch = min(length(Erg_Vector)-finish,25);
for n = finish+1:finish+FwdSearch
 rate = ZCR_Vector(n);
 if rate > IZCT
 ZCRCountf = ZCRCountf + 1;
 realfinish = n;
 end
end
if ZCRCountf > 3
 finish = realfinish; 
end
x_start = FrmIndex(start); %最終的起始位置
x_finish = FrmIndex(finish-1); %最終的結束位置
trimmed_X = x(x_start:x_finish);

  

 

 

特征提取mfccf()函數----程序清單

 
 
function FMatrix=mfccf(num,s,Fs)
%計算並返回信號s的mfcc參數及其一階和二階差分參數
%參數說明: num 是mfcc系數階數,Fs為采樣頻率
N=512; % FFT 數
Tf=0.02;  %窗口的時長
n=Fs*Tf;  %每個窗口的長度
M=24; %M為濾波器組數
l=length(s); 
Ts=0.01; %幀移時長
FrameStep=Fs*Ts; %幀移
a=1;
b=[1, -0.97];   %預加重處理的一階FIR高通濾波器系數
noFrames=fix((l-n)/FrameStep)+1; %幀數
FMatrix=zeros(noFrames-2, num); %倒譜系數初始矩陣,一階差分的首尾2幀為0,
lifter=1:num; %倒譜加權初始矩陣
lifter=1+floor(num/2)*(sin(lifter*pi/num));%倒譜加權函數
if mean(abs(s)) > 0.01
 s=s/max(s); %初始化
end

 
%計算 MFCC 系數

for i=1:noFrames
 frame=s((i-1)*FrameStep+1:(i-1)*FrameStep+n); %frame用於存儲每一幀數據
 framef=filter(b,a,frame); %預加重濾波器
 F=framef.*hamming(n); %加漢明窗
 FFTo=fft(F,n); %計算FFT
 melf=melbankm(M,N,Fs); %計算mel濾波器組系數 
 halfn=1+floor(N/2); 
 spectr=log(melf*(abs(FFTo(1:halfn)).^2)+1e-22);%三角濾波器進行濾波加上1e-22防止進行對數運算后變成無窮

 
%進行DCT變換

c=zeros(1,num);
 for p=1:num
 for m=1:M
 c(p)=c(p)+spectr(m)*cos(p*(m-0.5)*pi/M);
 end
 end
 ncoeffs=c.*lifter; %對MFCC參數進行倒譜加權
 FMatrix(i, :)=ncoeffs; 
end
 
%調用deltacoeff函數計算MFCC差分系數

d=deltacoeff(FMatrix); %計算一階差分系數
d1=deltacoeff(d); %計算二階差分系數
FMatrix=[FMatrix,d,d1]; %將三組數據作為特征向量

  

 
 

 

melbankm()函數---程序清單:

 
function H=melbankm(M,N,fs,fl,fh)
%Mel濾波器組
% 輸入參數: M 濾波器組數量
%  N FFT長度
%  fs 采樣頻率
%  fl -fh 為線性功率譜的有用頻帶(默認為0-0.5*fs)
%  
%輸出參數: H返回濾波器組,每一行為一個濾波器
if nargin<4
 fl=0*fs;
 fh=0.5*fs;
end
%計算每個濾波器的中心頻率
f=zeros(1,M+1);
for m=1:M+2
 f(m)=floor((N/fs)*mel2freq(freq2mel(fl)...
 +(m-1)*(freq2mel(fh)-freq2mel(fl))/(M+1)));
end
%求濾波器組H
c=floor(N/2)+1;
y=zeros(1,c);
H=zeros(M,c);
for m=2:M+1
 for k=1:c %由於fh最高為fs/2,那么最多需c位就能存儲H
 if f(m-1)<=k&&k<=f(m)
 y(k)=(k-f(m-1))/(f(m)-f(m-1));
 elseif f(m)<=k&&k<=f(m+1)
 y(k)=(f(m+1)-k)/(f(m+1)-f(m));
 else
 y(k)=0;
 end
 end
 H(m,:)=y;
end

  

 

 

freq2mel()函數----程序清單:

 
function b=freq2mel(f)
%%實現線性頻域到mel頻域的轉換
%輸入參數:f為線性頻域頻率
%輸出參數:b為mel頻域頻率
b=2595*log10(1+f/700);

  

 

 

mel2freq()函數----程序清單:

 
function f=mel2freq(b)
%%實現mel頻域轉換到線性頻域
%輸入參數:b為mel頻域內頻率
%輸出參數:f為線性頻域內頻率
f=700*(10^(b/2595)-1);
 
deltacoeff()函數----程序清單:
function diff = deltacoeff(x)
%計算MFCC差分系數
[nr,nc]=size(x);
N=2;
diff=zeros(nr,nc);
for t=3:nr-2
 for n=1:N
 diff(t,:)=diff(t,:)+n*(x(t+n,:)-x(t-n,:));
 end
 diff(t,:)=diff(t,:)/10; %10=2*(1^2+2^2)
end

  

 

 

CMN()函數----程序清單

 
function NormMatrix = CMN(Matrix)
%歸一化處理
[r,c]=size(Matrix);
NormMatrix=zeros(r,c);
for i=1:c
 MatMean=mean(Matrix(:,i)); 
 NormMatrix(:,i)=Matrix(:,i)-MatMean;
end

  

 

 

DTWScores()函數----程序清單

 
function AllScores = DTWScores(rMatrix,N)
%動態時間規整(DTW)尋找最小失真
%輸入參數:rMatrix為當前讀入語音的MFCC參數矩陣,N為每個模板數量詞匯數
%輸出參數:AllScores
%初始化DTW判別矩陣
Scores1 = zeros(1,N); 
Scores2 = zeros(1,N);
Scores3 = zeros(1,N);
%加載模板數據
s1 = load('Vectors1.mat');
fMatrixall1 = struct2cell(s1);
s2 = load('Vectors2.mat');
fMatrixall2 = struct2cell(s2);
s3 = load('Vectors3.mat');
fMatrixall3 = struct2cell(s3);
%計算DTW
for i = 1:N
 fMatrix1 = fMatrixall1{i,1};
 fMatrix1 = CMN(fMatrix1);
 Scores1(i) = myDTW(fMatrix1,rMatrix);
end
for j = 1:N
 fMatrix2 = fMatrixall2{j,1};
 fMatrix2 = CMN(fMatrix2);
 Scores2(j) = myDTW(fMatrix2,rMatrix);
end
for k= 1:N
 fMatrix3 = fMatrixall3{k,1};
 fMatrix3 = CMN(fMatrix3);
 Scores3(k) = myDTW(fMatrix3,rMatrix);
end
AllScores = [Scores1;Scores2;Scores3];

  

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM