文章正文
使用Python进行语音识别(一)
基于能量与过零率的端点检测
一直想尝试着做语音识别的东西,就是简单的识别几个特定的声音也好,但是感觉网上的东西要么太水,要么太难,不过这几天突然有了一点灵感,借用机器学习进行识别不知道可不可行,于是就有了现在这篇博客了。这篇博客算是语音识别的第一步,对语音进行端点检测。
写代码之前首先确保安装了必要的环境,这里说一下我的环境,window7,python2.7,numpy,scipy,pyaduio, Matplotlib。安装了这几个库就可以进行这里的实验了。下面正式进入实验。
第一步,录音。这里使用pyaudio,首先将声音保存为文件。
import pyaudio import wave p=pyaudio.PyAudio() stream=p.open(format=pyaudio.paInt16,#格式 channels=2,#通道 rate=44100,#采样率 input=True, frames_per_buffer=1024) print "recording" frames=[] for i in range(0,int(44100/1024*10)):#录音秒数 data=stream.read(1024) frames.append(data) print "recording ok" stream.stop_stream() stream.close() p.terminate() wf=wave.open("recording.wav",'wb') wf.setnchannels(2) wf.setsampwidth(p.get_sample_size(pyaudio.paInt16)) wf.setframerate(44100) wf.writeframes(b''.join(frames)) wf.close()
第二步可以看看这个录音文件的波形,由于录音的时候使用的是双通道,所以这里将两个通道的波形全部显示出来。
# -*- coding: utf-8 -*- import wave import pylab as pl import numpy as np # 打开WAV文档 f = wave.open(r"recording.wav", "rb") # 读取格式信息 # (nchannels, sampwidth, framerate, nframes, comptype, compname) params = f.getparams() nchannels, sampwidth, framerate, nframes = params[:4] # 读取波形数据 str_data = f.readframes(nframes) f.close() #将波形数据转换为数组 wave_data = np.fromstring(str_data, dtype=np.short) wave_data.shape = -1, 2 wave_data = wave_data.T time = np.arange(0, nframes) * (1.0 / framerate) print len(time) # 绘制波形 pl.subplot(211) pl.plot(time, wave_data[0]) pl.subplot(212) pl.plot(time, wave_data[1], c="g") pl.xlabel("time (seconds)") pl.show()
下面是1,2,3,4,5,6,7,8,9,10,ok的波形
第三步,进入我们的重点,端点识别,由于从上图看到两通道的波形基本相同,所以我们只要分析其中的一个通道就可以了。这里我们使用能量和过零法进行检测.
#encoding=utf-8 import wave import pylab as pl import numpy as np wf=wave.open("recording.wav",'rb') params=wf.getparams() nchannels,sampwidth,framerate,nframes=params[:4] str_data=wf.readframes(nframes) wf.close() #change to matrix wave_data=np.fromstring(str_data,dtype=np.short) wave_data.shape=-1,2 wave_data=wave_data.T time=np.arange(0,nframes)*(1.0/framerate) data=[] maxvalue=max(wave_data[0]) print maxvalue #归一化 for i in xrange(len(wave_data[0])): #滤波器,滤去环境噪音 tt=(wave_data[0][i]*1.0)/maxvalue if tt>0.1: data.append(tt) else: data.append(0) #windows size windows=framerate/80#窗大小(1000/80)ms #energy energy=[] #过零法 zero=[] #采用矩形窗,帧移为0,为使效果变好可以使用汉明窗或是其他窗 for i in xrange(int(len(time)/windows)): #energy tmp=data[windows*i:windows*(i+1)] vec=np.array(tmp,dtype=np.float) en=np.dot(vec,vec.T) if en<0: print vec exit() energy.append(en) #zero count=0 base=i*windows for j in xrange(windows): if (data[base+j]>=0 and data[base+j+1]<0)or(data[base+j]<=0 and data[base+j+1]>0): count=count+1 zero.append(count) print nframes print len(energy) print len(time[::windows][:-1]) pl.subplot(211) pl.plot(time[::windows][:-1],energy) pl.subplot(212) print len(zero) pl.plot(time[::windows][:-1],zero) pl.show()
这里得到的图就是能量和过零率的图形
然后,我们将过零率与能量合成,由于能量事先经过归一化,过零率与能量差别有点大,所以将过零率归一化后合成,然后找到边界。
#过零归一 maxzero=max(zero) zerodata=[] for i in xrange(len(zero)): zerodata.append(zero[i]*1.0/maxzero) sum=[] for i in xrange(len(zerodata)): sum.append(zerodata[i]+energy[i]) pl.plot(time[::windows][:-1],sum) #得出语音端点 word=[] flag=0 left=0 right=0 for i in xrange(len(sum)-1): if sum[i]==0 and sum[i+1]>0: if flag==1: nextleft=i if (nextleft-right)*1000*1.0/80>0.05:#小于0.05s 为同一个发音 flag=0#一个独立字识别出来 print time[right*windows]-time[left*windows] if time[right*windows]-time[left*windows]>0.08:#最小发音长度 word.append((left,right)) if flag==0: left=i elif sum[i]>0 and sum[i+1]==0: right=i+1 flag=1 print word
最后将得到的边界作图,并将音频分割出去。
def toarr(time): l=[] for i in range(100): l.append(time) return l jj=0 for i in word: left=i[0] right=i[1] print left,right pl.plot(toarr(time[left*windows]),range(0,100)) pl.plot(toarr(time[right*windows]),range(0,100)) tmpwave=wave_data[0][left*windows:right*windows] ff=wave.open(str(jj)+".wav",'wb') ff.setnchannels(1) ff.setsampwidth(sampwidth) ff.setframerate(framerate) ff.writeframes(tmpwave.tostring()) ff.close() jj=jj+1 pl.show()
得到的分割图形如下:
经过试听分割得到的语音,这个方法在低噪音的环境能很好的检测出声音的端点。下一步就是语音的简单的识别了。
2015年4月5日晚
于浙大西溪校区
April 6, 2015, 10:47 a.m. 作者:zachary 分类:语音识别 阅读(3771) 评论(4)
评论: