python提取語音信號MFCC
Mel頻率倒譜系數(MFCC)的分析是基於人的聽覺機理,即依據人的聽覺實驗結果來分析語音的頻譜,期望獲得好的語音特性。
一、讀取語音信號
import numpy as np
import wave
import matplotlib.pyplot as plt
from scipy.fftpack import dct
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
signal = np.fromstring(str_data, dtype=np.short)
signal=signal*1.0/(max(abs(signal))) #歸一化
python讀取語音調用wave模塊
nchannels: 聲道數 1
sampwidth:量化位數 2
framerate:採樣頻率 8000
nframes:採樣點數 19000
二、預加重
預加重的目的是爲了補償高頻分量的損失,提升高頻分量,預加重的濾波器常設爲
變換後:
signal_add=np.append(signal[0],signal[1:]-0.97*signal[:-1]) #預加重
三、分幀、加窗
分幀處理:由於語音信號是一個準穩態的信號,把它分成較短的幀,在每幀中可將其看做穩態信號,可用處理穩態信號的方法來處理。同時,爲了使一幀與另一幀之間的參數能較平穩地過渡,在相鄰兩幀之間互相有部分重疊。
加窗函數:加窗函數的目的是減少頻域中的泄漏,將對每一幀語音乘以漢明窗或海寧窗。語音信號x(n)經預處理後爲xi(m),其中下標i表示分幀後的第i幀。
wlen=512
inc=128
N=512
if signal_len<wlen:
nf=1
else:
nf = int(np.ceil((1.0 * signal_len - wlen + inc) / inc))
pad_len=int((nf-1)*inc+wlen)
zeros=np.zeros(pad_len-signal_len)
pad_signal=np.concatenate((signal,zeros))
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T
indices=np.array(indices,dtype=np.int32)
frames=pad_signal[indices]
win=np.hanning(wlen)
四、快速傅里葉變換
對每一幀信號進行快速傅里葉變換。
for i in range(nf): #幀數
x=frames[i:i+1]
y=win*x[0]
a=np.fft.fft(y) #快速傅里葉變換
五、計算能量譜線
對每一幀數據計算能量譜線。
for i in range(nf):
x=frames[i:i+1]
y=win*x[0]
a=np.fft.fft(y)
b=np.square(abs(a)) #求FFT變換結果的模的平方
六、梅爾濾波器
人的聽覺系統是一個特殊的非線性系統,它響應不同頻率信號的靈敏度是不同的。在語音特徵的提取上,人類聽覺系統做得非常好,它不僅能提取出語義信息, 而且能提取出說話人的個人特徵,這些都是現有的語音識別系統所望塵莫及的。如果在語音識別系統中能模擬人類聽覺感知處理特點,就有可能提高語音的識別率。
梅爾頻率倒譜系數(Mel Frequency Cepstrum Coefficient, MFCC)考慮到了人類的聽覺特徵,先將線性頻譜映射到基於聽覺感知的Mel非線性頻譜中,然後轉換到倒譜上。
將普通頻率轉化爲mel頻率的公式爲:
將mel頻率轉化爲普通頻率公式爲:
梅爾濾波器組,每個濾波器的傳遞函數爲:
每個濾波器在mel頻率上是等帶寬的。
中心頻率f(m)可以表示爲:
這裏我採用m=24個濾波器
mel_high=1125*np.log(1+(framerate/2)/700) #mel最高頻率
mel_point=np.linspace(0,mel_high,m+2) #將mel頻率等距離分成m+2個點
Fp=700 * (np.exp(mel_point / 1125) - 1) #將等距分好的mel頻率轉換爲實際頻率
w=int(N/2+1)
df=framerate/N
fr=[]
for n in range(w): #mel濾波器的橫座標
frs=int(n*df)
fr.append(frs)
melbank=np.zeros((m,w))
for k in range(m+1): #畫mel濾波器
f1=Fp[k-1] #三角形左邊點的橫座標
f2=Fp[k+1] #三角形右邊點的橫座標
f0=Fp[k] #三角形中心點點的橫座標
n1=np.floor(f1/df)
n2=np.floor(f2/df)
n0=np.floor(f0/df)
for j in range(w):
if j>= n1 and j<= n0:
melbank[k-1,j]=(j-n1)/(n0-n1)
if j>= n0 and j<= n2:
melbank[k-1,j]=(n2-j)/(n2-n0)
for c in range(w):
s[i,k-1]=s[i,k-1]+b[c:c+1]*melbank[k-1,c]
plt.plot(fr, melbank[k - 1,])
plt.show()
具體過程可參考:
[梅爾濾波器組的分析與設計思路]: https://blog.csdn.net/tengfei0973/article/details/103182621
七、離散餘弦變換(DCT)
logs=np.log(s) #取對數
num_ceps=12
D = dct(logs,type = 2,axis = 0,norm = 'ortho')[:,1 : (num_ceps + 1)]
因爲語音信號特徵主要集中在低頻部分,所以一般選用每一幀信號的前12個濾波器作爲MFCC參數。
八、總結
MFCC參數主要用來做語音識別和端點檢測。
總代碼:
import numpy as np
import wave
import matplotlib.pyplot as plt
from scipy.fftpack import dct
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
signal = np.fromstring(str_data, dtype=np.short)
signal=signal*1.0/(max(abs(signal)))
signal_len=len(signal)
#預加重
signal_add=np.append(signal[0],signal[1:]-0.97*signal[:-1]) #預加重
time=np.arange(0,nframes)/1.0*framerate
#plt.figure(figsize=(20,10))
#plt.subplot(2,1,1)
#plt.plot(time,signal)
#plt.subplot(2,1,2)
#plt.plot(time,signal_add)
#plt.show()
#分幀
wlen=512
inc=128
N=512
if signal_len<wlen:
nf=1
else:
nf = int(np.ceil((1.0 * signal_len - wlen + inc) / inc))
pad_len=int((nf-1)*inc+wlen)
zeros=np.zeros(pad_len-signal_len)
pad_signal=np.concatenate((signal,zeros))
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T
indices=np.array(indices,dtype=np.int32)
frames=pad_signal[indices]
win=np.hanning(wlen)
m=24
s=np.zeros((nf,m))
for i in range(nf):
x=frames[i:i+1]
y=win*x[0]
a=np.fft.fft(y)
b=np.square(abs(a))
mel_high=1125*np.log(1+(framerate/2)/700)
mel_point=np.linspace(0,mel_high,m+2)
Fp=700 * (np.exp(mel_point / 1125) - 1)
w=int(N/2+1)
df=framerate/N
fr=[]
for n in range(w):
frs=int(n*df)
fr.append(frs)
melbank=np.zeros((m,w))
for k in range(m+1):
f1=Fp[k-1]
f2=Fp[k+1]
f0=Fp[k]
n1=np.floor(f1/df)
n2=np.floor(f2/df)
n0=np.floor(f0/df)
for j in range(w):
if j>= n1 and j<= n0:
melbank[k-1,j]=(j-n1)/(n0-n1)
if j>= n0 and j<= n2:
melbank[k-1,j]=(n2-j)/(n2-n0)
for c in range(w):
s[i,k-1]=s[i,k-1]+b[c:c+1]*melbank[k-1,c]
plt.plot(fr, melbank[k - 1,])
plt.show()
logs=np.log(s)
num_ceps=12
D = dct(logs,type = 2,axis = 0,norm = 'ortho')[:,1 : (num_ceps + 1)]
print(D)
print(np.shape(D))
輸出結果:一個146×12的矩陣 146代表幀數,12代表每幀的MFCC係數
[[ 4.84318860e+01 5.43872867e+01 2.97738841e+01 … -1.80842897e+01
-2.56759247e+01 -3.18041757e+01]
[-7.09811701e+00 -5.86839796e+00 -4.37673606e+00 … 3.47982198e+00
4.99207591e+00 6.14210310e+00]
[-1.46795072e+01 -1.88020165e+01 -9.72372794e+00 … -8.57395667e+00
-1.24359229e+01 -1.45616032e+01]
…
[-2.27406690e-01 -1.80257328e-01 -5.82389817e-01 … -1.26641927e-01
-2.98866486e-02 -6.72742599e-02]
[-1.74213838e-01 -2.16393861e-01 -1.68467011e-01 … 4.40449351e-02
-1.80765461e-02 2.14003905e-02]
[-1.06398728e-01 -6.37571905e-02 -1.43970643e-01 … 1.37202632e-01
-7.46614028e-02 8.66251398e-02]]
MFCC提取流程圖:
matlab代碼(來自書本)
clear all;
clc;
clear all;
[x,fs]=audioread('H:\語音信號處理\speech_signal\bluesky3.wav');
p=24;
frameSize=512;
inc=128;
bank=melbankm(p,frameSize,fs,0,0.5,'m');
% 歸一化Mel濾波器組係數
bank=full(bank);
bank=bank/max(bank(:));
p2=p/2;
% DCT係數,p2*p
for k=1:p2
n=0:p-1;
dctcoef(k,:)=cos((2*n+1)*k*pi/(2*p));
end
% 歸一化倒譜提升窗口
w = 1 + 6 * sin(pi * [1:p2] ./ p2);
w = w/max(w);
% 預加重濾波器
xx=double(x);
xx=filter([1 -0.9375],1,xx);
% 語音信號分幀
xx=enframe(xx,frameSize,inc);
n2=fix(frameSize/2)+1;
% 計算每幀的MFCC參數
for i=1:size(xx,1)
y = xx(i,:);
s = y' .* hamming(frameSize);
t = abs(fft(s));
t = t.^2;
c1=dctcoef * log(bank * t(1:n2));
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,:);