梅爾頻率倒譜系數(shù)(mfcc)及Python實(shí)現(xiàn)
語(yǔ)音識(shí)別系統(tǒng)的第一步是進(jìn)行特征提取,mfcc是描述短時(shí)功率譜包絡(luò)的一種特征,在語(yǔ)音識(shí)別系統(tǒng)中被廣泛應(yīng)用。
一、mel濾波器
每一段語(yǔ)音信號(hào)被分為多幀,每幀信號(hào)都對(duì)應(yīng)一個(gè)頻譜(通過FFT變換實(shí)現(xiàn)),頻譜表示頻率與信號(hào)能量之間的關(guān)系。mel濾波器是指多個(gè)帶通濾波器,在mel頻率中帶通濾波器的通帶是等寬的,但在赫茲(Hertz)頻譜內(nèi)mel濾波器在低頻處較密集切通帶較窄,高頻處較稀疏且通帶較寬,旨在通過在較低頻率處更具辨別性并且在較高頻率處較少辨別性來模擬非線性人類耳朵對(duì)聲音的感知。
赫茲頻率和梅爾頻率之間的關(guān)系為:

假設(shè)在梅爾頻譜內(nèi),有M 個(gè)帶通濾波器Hm (k),0≤m<M,每個(gè)帶通濾波器的中心頻率為F(m) F(m)F(m)每個(gè)帶通濾波器的傳遞函數(shù)為:

下圖為赫茲頻率內(nèi)的mel濾波器,帶通濾波器個(gè)數(shù)為24:

二、mfcc特征
MFCC系數(shù)提取步驟:
(1)語(yǔ)音信號(hào)分幀處理
(2)每一幀傅里葉變換---->功率譜
(3)將短時(shí)功率譜通過mel濾波器
(4)濾波器組系數(shù)取對(duì)數(shù)
(5)將濾波器組系數(shù)的對(duì)數(shù)進(jìn)行離散余弦變換(DCT)
(6)一般將第2到底13個(gè)倒譜系數(shù)保留作為短時(shí)語(yǔ)音信號(hào)的特征
Python實(shí)現(xiàn)
import wave
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.fftpack import dct
def read(data_path):
'''讀取語(yǔ)音信號(hào)
'''
wavepath = data_path
f = wave.open(wavepath,'rb')
params = f.getparams()
nchannels,sampwidth,framerate,nframes = params[:4] #聲道數(shù)、量化位數(shù)、采樣頻率、采樣點(diǎn)數(shù)
str_data = f.readframes(nframes) #讀取音頻,字符串格式
f.close()
wavedata = np.fromstring(str_data,dtype = np.short) #將字符串轉(zhuǎn)化為浮點(diǎn)型數(shù)據(jù)
wavedata = wavedata * 1.0 / (max(abs(wavedata))) #wave幅值歸一化
return wavedata,nframes,framerate
def enframe(data,win,inc):
'''對(duì)語(yǔ)音數(shù)據(jù)進(jìn)行分幀處理
input:data(一維array):語(yǔ)音信號(hào)
wlen(int):滑動(dòng)窗長(zhǎng)
inc(int):窗口每次移動(dòng)的長(zhǎng)度
output:f(二維array)每次滑動(dòng)窗內(nèi)的數(shù)據(jù)組成的二維array
'''
nx = len(data) #語(yǔ)音信號(hào)的長(zhǎng)度
try:
nwin = len(win)
except Exception as err:
nwin = 1
if nwin == 1:
wlen = win
else:
wlen = nwin
nf = int(np.fix((nx - wlen) / inc) + 1) #窗口移動(dòng)的次數(shù)
f = np.zeros((nf,wlen)) #初始化二維數(shù)組
indf = [inc * j for j in range(nf)]
indf = (np.mat(indf)).T
inds = np.mat(range(wlen))
indf_tile = np.tile(indf,wlen)
inds_tile = np.tile(inds,(nf,1))
mix_tile = indf_tile + inds_tile
f = np.zeros((nf,wlen))
for i in range(nf):
for j in range(wlen):
f[i,j] = data[mix_tile[i,j]]
return f
def point_check(wavedata,win,inc):
'''語(yǔ)音信號(hào)端點(diǎn)檢測(cè)
input:wavedata(一維array):原始語(yǔ)音信號(hào)
output:StartPoint(int):起始端點(diǎn)
EndPoint(int):終止端點(diǎn)
'''
#1.計(jì)算短時(shí)過零率
FrameTemp1 = enframe(wavedata[0:-1],win,inc)
FrameTemp2 = enframe(wavedata[1:],win,inc)
signs = np.sign(np.multiply(FrameTemp1,FrameTemp2)) # 計(jì)算每一位與其相鄰的數(shù)據(jù)是否異號(hào),異號(hào)則過零
signs = list(map(lambda x:[[i,0] [i>0] for i in x],signs))
signs = list(map(lambda x:[[i,1] [i<0] for i in x], signs))
diffs = np.sign(abs(FrameTemp1 - FrameTemp2)-0.01)
diffs = list(map(lambda x:[[i,0] [i<0] for i in x], diffs))
zcr = list((np.multiply(signs, diffs)).sum(axis = 1))
#2.計(jì)算短時(shí)能量
amp = list((abs(enframe(wavedata,win,inc))).sum(axis = 1))
# # 設(shè)置門限
# print('設(shè)置門限')
ZcrLow = max([round(np.mean(zcr)*0.1),3])#過零率低門限
ZcrHigh = max([round(max(zcr)*0.1),5])#過零率高門限
AmpLow = min([min(amp)*10,np.mean(amp)*0.2,max(amp)*0.1])#能量低門限
AmpHigh = max([min(amp)*10,np.mean(amp)*0.2,max(amp)*0.1])#能量高門限
# 端點(diǎn)檢測(cè)
MaxSilence = 8 #最長(zhǎng)語(yǔ)音間隙時(shí)間
MinAudio = 16 #最短語(yǔ)音時(shí)間
Status = 0 #狀態(tài)0:靜音段,1:過渡段,2:語(yǔ)音段,3:結(jié)束段
HoldTime = 0 #語(yǔ)音持續(xù)時(shí)間
SilenceTime = 0 #語(yǔ)音間隙時(shí)間
print('開始端點(diǎn)檢測(cè)')
StartPoint = 0
for n in range(len(zcr)):
if Status ==0 or Status == 1:
if amp[n] > AmpHigh or zcr[n] > ZcrHigh:
StartPoint = n - HoldTime
Status = 2
HoldTime = HoldTime + 1
SilenceTime = 0
elif amp[n] > AmpLow or zcr[n] > ZcrLow:
Status = 1
HoldTime = HoldTime + 1
else:
Status = 0
HoldTime = 0
elif Status == 2:
if amp[n] > AmpLow or zcr[n] > ZcrLow:
HoldTime = HoldTime + 1
else:
SilenceTime = SilenceTime + 1
if SilenceTime < MaxSilence:
HoldTime = HoldTime + 1
elif (HoldTime - SilenceTime) < MinAudio:
Status = 0
HoldTime = 0
SilenceTime = 0
else:
Status = 3
elif Status == 3:
break
if Status == 3:
break
HoldTime = HoldTime - SilenceTime
EndPoint = StartPoint + HoldTime
return FrameTemp1[StartPoint:EndPoint]
def mfcc(FrameK,framerate,win):
'''提取mfcc參數(shù)
input:FrameK(二維array):二維分幀語(yǔ)音信號(hào)
framerate:語(yǔ)音采樣頻率
win:分幀窗長(zhǎng)(FFT點(diǎn)數(shù))
output:
'''
#mel濾波器
mel_bank,w2 = mel_filter(24,win,framerate,0,0.5)
FrameK = FrameK.T
#計(jì)算功率譜
S = abs(np.fft.fft(FrameK,axis = 0)) ** 2
#將功率譜通過濾波器
P = np.dot(mel_bank,S[0:w2,:])
#取對(duì)數(shù)
logP = np.log(P)
#計(jì)算DCT系數(shù)
# rDCT = 12
# cDCT = 24
# dctcoef = []
# for i in range(1,rDCT+1):
# tmp = [np.cos((2*j+1)*i*math.pi*1.0/(2.0*cDCT)) for j in range(cDCT)]
# dctcoef.append(tmp)
# #取對(duì)數(shù)后做余弦變換
# D = np.dot(dctcoef,logP)
num_ceps = 12
D = dct(logP,type = 2,axis = 0,norm = 'ortho')[1:(num_ceps+1),:]
return S,mel_bank,P,logP,D
def mel_filter(M,N,fs,l,h):
'''mel濾波器
input:M(int):濾波器個(gè)數(shù)
N(int):FFT點(diǎn)數(shù)
fs(int):采樣頻率
l(float):低頻系數(shù)
h(float):高頻系數(shù)
output:melbank(二維array):mel濾波器
'''
fl = fs * l #濾波器范圍的最低頻率
fh = fs * h #濾波器范圍的最高頻率
bl = 1125 * np.log(1 + fl / 700) #將頻率轉(zhuǎn)換為mel頻率
bh = 1125 * np.log(1 + fh /700)
B = bh - bl #頻帶寬度
y = np.linspace(0,B,M+2) #將mel刻度等間距
print('mel間隔',y)
Fb = 700 * (np.exp(y / 1125) - 1) #將mel變?yōu)镠Z
print(Fb)
w2 = int(N / 2 + 1)
df = fs / N
freq = [] #采樣頻率值
for n in range(0,w2):
freqs = int(n * df)
freq.append(freqs)
melbank = np.zeros((M,w2))
print(freq)
for k in range(1,M+1):
f1 = Fb[k - 1]
f2 = Fb[k + 1]
f0 = Fb[k]
n1 = np.floor(f1/df)
n2 = np.floor(f2/df)
n0 = np.floor(f0/df)
for i in range(1,w2):
if i >= n1 and i <= n0:
melbank[k-1,i] = (i-n1)/(n0-n1)
if i >= n0 and i <= n2:
melbank[k-1,i] = (n2-i)/(n2-n0)
plt.plot(freq,melbank[k-1,:])
plt.show()
return melbank,w2
if __name__ == '__main__':
data_path = 'audio_data.wav'
win = 256
inc = 80
wavedata,nframes,framerate = read(data_path)
FrameK = point_check(wavedata,win,inc)
S,mel_bank,P,logP,D = mfcc(FrameK,framerate,win)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python 單元測(cè)試(unittest)的使用小結(jié)
Python中有一個(gè)自帶的單元測(cè)試框架是unittest模塊,用它來做單元測(cè)試,本篇文章主要介紹了Python 單元測(cè)試(unittest)的使用小結(jié),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
pycharm 將django中多個(gè)app放到同個(gè)文件夾apps的處理方法
今天小編就為大家分享一篇pycharm 將django中多個(gè)app放到同個(gè)文件夾apps的處理方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-05-05
Python實(shí)現(xiàn)將PowerPoint轉(zhuǎn)為HTML格式
有時(shí)我們需要將精心設(shè)計(jì)的PPT發(fā)布到網(wǎng)絡(luò)上以便于更廣泛的訪問和分享,本文將介紹如何使用Python將PowerPoint轉(zhuǎn)換為HTML格式,需要的可以參考下2024-04-04
Python使用Socket(Https)Post登錄百度的實(shí)現(xiàn)代碼
以前都是用一些高級(jí)模塊,封裝的比較好,今天嘗試使用socket模塊登錄百度,弄了半天才弄好,主要由于百度在登陸頁(yè)使用了https,我們需要對(duì)socket進(jìn)行一定處理2012-05-05
python編輯用戶登入界面的實(shí)現(xiàn)代碼
這篇文章主要介紹了python編輯用戶登入界面的實(shí)現(xiàn)代碼,非常不錯(cuò),代碼簡(jiǎn)單易懂,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07
python使用pyaudio錄音和格式轉(zhuǎn)化方式
這篇文章主要介紹了python使用pyaudio錄音和格式轉(zhuǎn)化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
Python使用Matplotlib進(jìn)行圖案填充和邊緣顏色分離的三種方法
Matplotlib是Python中功能強(qiáng)大的繪圖庫(kù),允許廣泛的自定義選項(xiàng),一個(gè)常見的要求是分離出圖中的圖案填充和邊緣顏色,默認(rèn)情況下,Matplotlib中的填充顏色與邊緣顏色相關(guān)聯(lián),但有一些方法可以獨(dú)立自定義這些顏色,本文將深入研究如何實(shí)現(xiàn)這一點(diǎn)的技術(shù)細(xì)節(jié),并提供分步說明和示例2025-01-01
Python實(shí)現(xiàn)多線程HTTP下載器示例
本篇文章主要介紹了Python實(shí)現(xiàn)多線程HTTP下載器示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02

