不到一百行实现一个小siri
想要容易理解核心的特征计算的话建议先去看看我之前的听歌识曲的文章,传送门:http://www.cnblogs.com/chuxiuhong/p/6063602.html
本文主要是实现了一个简单的命令词识别程序,算法核心一是提取音频特征,二是用DTW算法进行匹配。当然,这样的代码肯定不能用于商业化,大家做出来玩玩娱乐一下还是不错的。
转载请保留本文链接,谢谢。
设计思路
就算是个小东西,我们也要先明确思路再做。音频识别,困难不小,其中提取特征的难度在我听歌识曲那篇文章里能看得出来。而语音识别难度更大,因为音乐总是固定的,而人类说话常常是变化的。比如说一个“芝麻开门”,有的人就会说成“芝麻~开门”,有的人会说成“芝麻开门~”。而且在录音时说话的时间也不一样,可能很紧迫的一开始录音就说话了,也可能不紧不慢的快要录音结束了才把这四个字说出来。这样难度就大了。
算法流程:

特征提取
和之前的听歌识曲一样,同样是将一秒钟分成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)为两个特征的距离代价。
那么我们可以画出下面这样的图

我们需要从(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),这就是两个音频的距离。



源代码和注释
# 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
欢迎大家提建议
不到一百行实现一个小siri的更多相关文章
- Redola.Rpc 的一个小目标
Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...
- UniversalImageLoader的一个小问题
最近在使用UniversalImageLoader时遇到了一个小问题,多个地方同时通过ImageLoader.getInstance().loadImage(url, new ImageSize(dp ...
- 用android去写一个小程序
前言: 软工的一个小作业:实现"黄金分割小游戏", 需要结对编程,队友:陈乐云 共用时两天. 早期思路设计: 采用键值对的形式,以Map作为存储结构.优点:能够将数据与用户对 ...
- 230行实现一个简单的MVVM
作者:mirone链接:https://zhuanlan.zhihu.com/p/24451202来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. MVVM这两年在前端届 ...
- 通过一个小问题来学习SQL关联查询
原话题: 是关于一个left join的,没有技术难度,但不想清楚不一定能回答出正确答案来: TabA表有三个字段Id,Col1,Col2 且里面有一条数据1,1,2 TabB表有两个字段Id,Col ...
- 定一个小目标:明年1024能成功转行web前端,光荣地成为一个程序员!
第一次在博客园写博,我为什么要选择这里吗? 据说博客园这里的IT大牛如云,作为一个求知若渴的小白,我屁颠屁颠的跟着过来了. 于是今天早上兴高采烈的注册了账号,迫不及待的打开我的博客,呃!注册账号成功了 ...
- 开源自己的一个小android项目(美女撕衣服游戏)
这是自己的一个开源自己的一个小android项目(美女撕衣服游戏),也是前6个月开发的,有部分的资源来自网络上的,现在开源出来给大家吧,由于源码比较大,不上传了,我已经上传到源码天堂那个网站那里了,大 ...
- FMX相当于在界面上自己又做了一个小操作系统
FMX的自画界面我也不看好,比如复制粘贴,太丑了,系统做得很好很精细的复制粘贴界面,就是无法调出,比如MIUI,复制粘贴还能有个放大镜,可以选择到屏幕边缘的文字,可以选择剪贴板内多个可粘贴的文字:还有 ...
- 关于SVN配置文件的一个小例子
1 背景假设 厦门央瞬公司是一家电子元器件设备供应商,其中有个ARM部门,专门负责ARM芯片的方案设计.销售,并在北京.上海各设立了一个办事处.对于工作日志,原先采用邮件方式发给经理,但是这种方式 ...
随机推荐
- x:bind不支持样式文件 或 此Xaml文件必须又代码隐藏类才能使用{x:Bind} 解决办法
这两天学习UWP开发,发现一个很有趣的问题,就是我题目中的描述的. 我习惯了在ResourceDictionary中写样式文件,但是发现用x:Bind时会有问题 如果是写在Style里,则提示 “x: ...
- Hawk 4.6 并行化
并行化 Hawk支持单机并行化,也就是使用多线程获取数据.它可以控制目前所有任务的数量,为了不给网站造成过大的压力,仅当任务池中的任务数量小于一定值后,才会插入新的任务. 你可以在数据清洗的 执行面板 ...
- JSP 标准标签库(JSTL)
JSP 标准标签库(JSTL) JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签, ...
- 学习C的笔记
[unsigned] 16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535.由于在计算机中,整数是以补码形式存放的.根据最高位的不同 ...
- MySQL常用命令
数据库登陆命令: mysql -uroot -p 2.提示输入密码: 3.登陆成功: 4.数据库修改相关命令: 修改数据库的编码格式: 语法格式为:ALTER {DATABASE|SCHEMA} [ ...
- Zookeeper常用命令
http://www.cnblogs.com/chengxin1982/p/3997706.html
- Linux1 在Linux(CentOS)上安装MySql详细记录
前记: 毕业两年了,前两天换了份工作,由以前的传统行业跳到了互联网行业.之前的公司一直在用WinServer2003+Tomcat+SqlServer/Oracle这套部署环境.对于Linux+To ...
- 虚拟机体验之 VirtualBox 篇 —— 性能强大的经典架构
前两篇体验了 QEMU 和经过 KVM 加速的 QEMU,并体验了第三方虚拟机管理工具 virt-manager,让我们见识了开源社区的强大和开源虚拟机软件的高质量和高性能.这一篇,我来剖析一下 Vi ...
- 详解this
this 虐我千百遍,看完此文效立见!不得不说,这篇文章的总结很地道很全面,适合收藏之用. 原文:all this 习惯了高级语言的你或许觉得JavaScript中的this跟Java这些面向对象语言 ...
- Entity Framework 6 Recipes 2nd Edition(13-10)译 -> 显式创建代理
问题 你有一个POCO实体,原本在执行一个查询时动态创建代理,现在你不想EF延迟创建代理带来的代价. 解决方案 假设你有一个如图Figure13-15的模型 Figure 13-15. A model ...