python利用不到一百行代码实现一个小siri


Posted in Python onMarch 02, 2017

前言

如果想要容易理解核心的特征计算的话建议先去看看我之前的听歌识曲的文章,传送门:https://3water.com/article/97305.htm

本文主要是实现了一个简单的命令词识别程序,算法核心一是提取音频特征,二是用DTW算法进行匹配。当然,这样的代码肯定不能用于商业化,大家做出来玩玩娱乐一下还是不错的。

设计思路

就算是个小东西,我们也要先明确思路再做。音频识别,困难不小,其中提取特征的难度在我听歌识曲那篇文章里能看得出来。而语音识别难度更大,因为音乐总是固定的,而人类说话常常是变化的。比如说一个“芝麻开门”,有的人就会说成“芝麻开门”,有的人会说成“芝麻开门”。而且在录音时说话的时间也不一样,可能很紧迫的一开始录音就说话了,也可能不紧不慢的快要录音结束了才把这四个字说出来。这样难度就大了。

算法流程:

python利用不到一百行代码实现一个小siri

特征提取

和之前的听歌识曲一样,同样是将一秒钟分成40块,对每一块进行傅里叶变换,然后取模长。只是这不像之前听歌识曲中进一步进行提取峰值,而是直接当做特征值。

看不懂我在说什么的朋友可以看看下面的源代码,或者看听歌识曲那篇文章。

DTW算法

DTW,Dynamic Time Warping,动态时间归整。算法解决的问题是将不同发音长短和位置进行最适合的匹配。

算法输入两组音频的特征向量: A:[fp1,fp2,fp3,......,fpM1] B:[fp1,fp2,fp3,fp4,.....fpM2]
A组共有M1个特征,B组共有M2个音频。每个特征向量中的元素就是之前我们将每秒切成40块之后FFT求模长的向量。计算每对fp之间的代价采用的是欧氏距离。

设D(fpa,fpb)为两个特征的距离代价。

那么我们可以画出下面这样的图

python利用不到一百行代码实现一个小siri 

我们需要从(1,1)点走到(M1,M2)点,这会有很多种走法,而每种走法就是一种两个音频位置匹配的方式。但我们的目标是走的总过程中代价最小,这样可以保证这种对齐方式是使我们得到最接近的对齐方式。

我们这样走:首先两个坐标轴上的各个点都是可以直接计算累加代价和求出的。然后对于中间的点来说D(i,j) = Min{D(i-1,j)+D(fpi,fpj) , D(i,j-1)+D(fpi,fpj) , D(i-1,j-1) + 2 * D(fpi,fpj)}

为什么由(i-1,j-1)直接走到(i,j)这个点需要加上两倍的代价呢?因为别人走正方形的两个直角边,它走的是正方形的对角线啊

按照这个原理选择,一直算到D(M1,M2),这就是两个音频的距离。

python利用不到一百行代码实现一个小siri 

python利用不到一百行代码实现一个小siri 

python利用不到一百行代码实现一个小siri 

源代码和注释

# coding=utf8
import os
import wave
import dtw
import numpy as np
import pyaudio

def compute_distance_vec(vec1, vec2):
 return np.linalg.norm(vec1 - vec2) #计算两个特征之间的欧氏距离

class record():
 def record(self, CHUNK=44100, FORMAT=pyaudio.paInt16, CHANNELS=2, RATE=44100, RECORD_SECONDS=200,
    WAVE_OUTPUT_FILENAME="record.wav"):
  #录歌方法
  p = pyaudio.PyAudio()
  stream = p.open(format=FORMAT,
      channels=CHANNELS,
      rate=RATE,
      input=True,
      frames_per_buffer=CHUNK)
  frames = []
  for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
   data = stream.read(CHUNK)
   frames.append(data)
  stream.stop_stream()
  stream.close()
  p.terminate()
  wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
  wf.setnchannels(CHANNELS)
  wf.setsampwidth(p.get_sample_size(FORMAT))
  wf.setframerate(RATE)
  wf.writeframes(''.join(frames))
  wf.close()

class voice():
 def loaddata(self, filepath):
  try:
   f = wave.open(filepath, 'rb')
   params = f.getparams()
   self.nchannels, self.sampwidth, self.framerate, self.nframes = params[:4]
   str_data = f.readframes(self.nframes)
   self.wave_data = np.fromstring(str_data, dtype=np.short)
   self.wave_data.shape = -1, self.sampwidth
   self.wave_data = self.wave_data.T #存储歌曲原始数组
   f.close()
   self.name = os.path.basename(filepath) # 记录下文件名
   return True
  except:
   raise IOError, 'File Error'

 def fft(self, frames=40):
  self.fft_blocks = [] #将音频每秒分成40块,再对每块做傅里叶变换
  blocks_size = self.framerate / frames
  for i in xrange(0, len(self.wave_data[0]) - blocks_size, blocks_size):
   self.fft_blocks.append(np.abs(np.fft.fft(self.wave_data[0][i:i + blocks_size])))
 @staticmethod
 def play(filepath):
  chunk = 1024
  wf = wave.open(filepath, 'rb')
  p = pyaudio.PyAudio()
  # 播放音乐方法
  stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
      channels=wf.getnchannels(),
      rate=wf.getframerate(),
      output=True)
  while True:
   data = wf.readframes(chunk)
   if data == "": break
   stream.write(data)
  stream.close()
  p.terminate()
if __name__ == '__main__':
 r = record()
 r.record(RECORD_SECONDS=3, WAVE_OUTPUT_FILENAME='record.wav')
 v = voice()
 v.loaddata('record.wav')
 v.fft()
 file_list = os.listdir(os.getcwd())
 res = []
 for i in file_list:
  if i.split('.')[1] == 'wav' and i.split('.')[0] != 'record':
   temp = voice()
   temp.loaddata(i)
   temp.fft()
   res.append((dtw.dtw(v.fft_blocks, temp.fft_blocks, compute_distance_vec)[0],i))
 res.sort()
 print res
 if res[0][1].find('open_qq') != -1:
  os.system('C:\program\Tencent\QQ\Bin\QQScLauncher.exe') #我的QQ路径
 elif res[0][1].find('zhimakaimen') != -1:
  os.system('chrome.exe')#浏览器的路径,之前已经被添加到了Path中了
 elif res[0][1].find('play_music') != -1:
  voice.play('C:\data\music\\audio\\audio\\ (9).wav') #播放一段音乐
 # r = record()
 # r.record(RECORD_SECONDS=3,WAVE_OUTPUT_FILENAME='zhimakaimen_09.wav')

事先可以先用这里的record方法录制几段命令词,尝试用不同语气说,不同节奏说,这样可以提高准确度。然后设计好文件名,根据匹配到的最接近音频的文件名就可以知道是哪种命令,进而自定义执行不同的任务

这是一段演示视频:http://www.iqiyi.com/w_19ruisynsd.html

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者使用python能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
Python md5与sha1加密算法用法分析
Jul 14 Python
利用Python批量压缩png方法实例(支持过滤个别文件与文件夹)
Jul 30 Python
mac系统安装Python3初体验
Jan 02 Python
致Python初学者 Anaconda入门使用指南完整版
Apr 05 Python
Pycharm2017版本设置启动时默认自动打开项目的方法
Oct 29 Python
对Django项目中的ORM映射与模糊查询的使用详解
Jul 18 Python
Python单元测试模块doctest的具体使用
Feb 10 Python
在python中logger setlevel没有生效的解决
Feb 21 Python
python目标检测给图画框,bbox画到图上并保存案例
Mar 10 Python
Pandas将列表(List)转换为数据框(Dataframe)
Apr 24 Python
Opencv图像处理:如何判断图片里某个颜色值占的比例
Jun 03 Python
Python趣味挑战之实现简易版音乐播放器
May 28 Python
Python正则表达式教程之三:贪婪/非贪婪特性
Mar 02 #Python
Python正则表达式教程之二:捕获篇
Mar 02 #Python
Python正则表达式教程之一:基础篇
Mar 02 #Python
Python单例模式实例详解
Mar 01 #Python
python实现字典(dict)和字符串(string)的相互转换方法
Mar 01 #Python
python 截取 取出一部分的字符串方法
Mar 01 #Python
详解Python中的静态方法与类成员方法
Feb 28 #Python
You might like
PHP MYSQL乱码问题,使用SET NAMES utf8校正
2009/11/30 PHP
JavaScript Konami Code 实现代码
2009/07/29 Javascript
csdn 博客中实现运行代码功能实现
2009/08/29 Javascript
jquery 查找新建元素代码
2010/07/06 Javascript
javascript中length属性的探索
2011/07/31 Javascript
Javascript base64编码实现代码
2011/12/02 Javascript
无缝滚动改进版支持上下左右滚动(封装成函数)
2012/12/04 Javascript
JavaScript使用HTML5的window.postMessage实现跨域通信例子
2014/04/11 Javascript
js函数模拟显示桌面.scf程序示例
2014/04/20 Javascript
jquery分页插件jpaginate在IE中不兼容问题
2014/04/22 Javascript
javascript正则表达式基础知识入门
2015/04/20 Javascript
JS+DIV+CSS实现仿表单下拉列表效果
2015/08/18 Javascript
18个非常棒的jQuery代码片段
2015/11/02 Javascript
深入理解Java线程编程中的阻塞队列容器
2015/12/07 Javascript
javascript瀑布流式图片懒加载实例解析与优化
2016/02/23 Javascript
详解各版本React路由的跳转的方法
2018/05/10 Javascript
详解使用VUE搭建后台管理系统(vue-cli更新至3.0)
2018/08/22 Javascript
小程序实现展开/收起的效果示例
2018/09/22 Javascript
video.js 一个页面同时播放多个视频的实例代码
2018/11/27 Javascript
openlayers实现地图弹窗
2020/09/25 Javascript
Python类方法__init__和__del__构造、析构过程分析
2015/03/06 Python
PyTorch上实现卷积神经网络CNN的方法
2018/04/28 Python
python使用tkinter库实现五子棋游戏
2019/06/18 Python
浅谈pytorch中的BN层的注意事项
2020/06/23 Python
python 如何对logging日志封装
2020/12/02 Python
Farfetch香港官网:汇集全球时尚奢侈品购物平台
2017/11/26 全球购物
39美元购买一副眼镜或太阳镜:39DollarGlasses.com
2018/06/17 全球购物
李开复演讲稿
2014/05/24 职场文书
12.4法制宣传日标语
2014/10/08 职场文书
入党积极分子对十八届四中全会期盼的思想汇报
2014/10/17 职场文书
2015年街道除四害工作总结
2015/05/15 职场文书
2015年小学实验室工作总结
2015/07/28 职场文书
考试后的感想
2015/08/07 职场文书
python xlwt模块的使用解析
2021/04/13 Python
了解Kubernetes中的Service和Endpoint
2022/04/01 Servers
Mysql 文件配置解析介绍
2022/05/06 MySQL