python提取語音信號MFCC

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

在這裏插入圖片描述

二、預加重

預加重的目的是爲了補償高頻分量的損失,提升高頻分量,預加重的濾波器常設爲
H(z)=1az1 H(z)=1-az^{-1}
變換後:
y(t)=x(t)ax(t1) y(t)=x(t)-ax(t-1)

signal_add=np.append(signal[0],signal[1:]-0.97*signal[:-1])   #預加重

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pfA54HWO-1576392559693)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1576388987768.png)]

三、分幀、加窗

分幀處理:由於語音信號是一個準穩態的信號,把它分成較短的幀,在每幀中可將其看做穩態信號,可用處理穩態信號的方法來處理。同時,爲了使一幀與另一幀之間的參數能較平穩地過渡,在相鄰兩幀之間互相有部分重疊。

加窗函數:加窗函數的目的是減少頻域中的泄漏,將對每一幀語音乘以漢明窗或海寧窗。語音信號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)

四、快速傅里葉變換

對每一幀信號進行快速傅里葉變換。
X(i,k)=FFT(xi(m)) X(i,k)=FFT(x_{i}(m))

for i in range(nf): #幀數
    x=frames[i:i+1]
    y=win*x[0]
    a=np.fft.fft(y) #快速傅里葉變換

五、計算能量譜線

對每一幀數據計算能量譜線。
E(i,k)=[X(i,k)]2 E(i,k)=[X(i,k)]^{2}

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(f)=2595log10(1+f700) mel(f)=2595*log10(1+\frac{f}{700})
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BJXSCmBI-1576392559694)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1576392519332.png)]

將mel頻率轉化爲普通頻率公式爲:
f=700(10mel(f)/15951) f=700(10^{mel(f)/1595}-1)
梅爾濾波器組,每個濾波器的傳遞函數爲:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RT9e3Hab-1576392559694)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1576390569317.png)]

每個濾波器在mel頻率上是等帶寬的。

中心頻率f(m)可以表示爲:
f(m)=(Nfs)Fmel1(Fmel(fl+mFmel(fh)Fmel(fi)M+1) f(m)=(\frac{N}{f_{s}})F_{mel}^{-1}(F_{mel}(f_{l}+m\frac{F_{mel}(f_{h})-F_{mel}(f_{i})}{M+1})
這裏我採用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()

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ZkVrw9aq-1576392559695)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1576391540230.png)]

具體過程可參考:

[梅爾濾波器組的分析與設計思路]: 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提取流程圖:

img

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,:);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章