文章正文
使用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 分类:语音识别 阅读(3942) 评论(4)
评论: