利用Python通过频谱分析和KNN完成iphone拨号的语音识别

最近这段时间,学校里的事情实在太多了,从七月下旬一直到八月底实验室里基本天天十二点或者通宵,实在是没有精力和时间来写博客。这周老师出国开会,也算有了一个短暂的休息机会,刚好写点有意思的东西。

上周在天津的会议上碰到一个北交的姐们儿,她想利用小波变换来处理失超信号,刚好之前自己就有这个想法,所以回来后就想着把相关的内容好好复习复习,最相关的就是傅里叶分析和小波变换了。数学推导固然重要,但写那个实在是太乏味了,然后想到之前网上一个新闻,说一个同学通过新闻里记者拨号的声音反推出了周鸿祎的手机号码,就想着能不能自己也做一个这样的号码识别程序呢?

说做就做,首先整理一下思路,我觉得大概的流程应该包括一下几步:

1 单键声音的采集与分析

这是后面号码识别的基础,针对每个按键音分析其在频域上的分布规律进而得到一个基准,后面再采集到的声音可据此进行判定。

2 声音的降噪

自己录的这些声音总不会太完美,直接进行频谱分析,会得到一个非常杂乱的结果,所以有效的声音降噪可以帮助我们更加精确地进行判断。

3 号码识别思路

我想的方案主要是两种,刚好也符合我最近复习的这两种变换

A 对声音文件进行有效区域划分,然后对每个区域单独进行频谱分析,然后比对训练数据,推断每一处对应的号码,最后输出。

B 采用小波分析,输出对应声音文件的小波时频图,观察其在不同时刻频域各处强度的变化,进而确定所拨的号码。

两种方案我感觉应该都可以,不过第一种感觉相对要简单一点,所以我们就先来试试第一个。

Ok,那第一步我们得先找到合适的单个按键音数据,这个上网一搜iphone按键音就找到了,下载下来发现刚好又是wav文件,可以用Python自带的wave库直接处理,简直美滋滋。

简单说一下wave库,用它读取一个wave文件后,我们可以获取四个参数,包括通道数目,样本宽度,采样率以及采样数目。根据通道数目,你可以确定是单通道和双通道,如果是单通道你直接读取采样文件就行,但如果是双通道,采样是一左一右轮着来的,所以到时候你还得把它分成两列,然后选择其中一列来读。从网上下载的这些音源刚好是单声道的,所以直接读取就行了,下面以0为例,读取它的波形并画出来。

import numpy as np
import wave
from matplotlib import pyplot as plt
file_path='C:\Users\**\Desktop\iphone\dtmf-0.wav'
f=wave.open(file_path,'rb')
num=file_path[-5] 
params=f.getparams()
nchannels,samplewidth,framerate,nframes=params[:4]
str_data=f.readframes(nframes)
f.close()
wave_data=np.fromstring(str_data,dtype=np.short)
wave_data.shape=-1,1
if nchannels==2:
    wave_data.shape=-1,2
else:
    pass
wave_data=wave_data.T
time=np.arange(0,nframes)*(1.0/framerate)
plt.subplot(211)
plt.plot(time,wave_data[0],'r-')
plt.xlabel('Time/s')
plt.ylabel('Ampltitude')
plt.title('Num '+num+' time/ampltitude')
plt.show()

结果如下


其实波形看起来还是比较规律的,但明显是有不同频率的波叠加在一起,所以下一步我们采用傅里叶分析,看看它在频域上是什么样的。Python中进行傅里叶分析是十分方便的,直接利用numpy中的fft就行,分解完成后,考虑到高频的成分不需要,所以选择4000做个阈值,超出这个区域的都舍掉。另外,因为目前我并不考虑信号的重构,所以我直接把对应频率的幅值去了绝对值,这样方便我下一步寻找波峰。完成这些操作后,画个频谱图看看有什么规律。

df=framerate/(nframes-1)
freq=[df*n for n in range(0,nframes)]
transformed=np.fft.fft(wave_data[0])
d=int(len(transformed)/2)
while freq[d]>4000:
    d-=10
freq=freq[:d]
transformed=transformed[:d]
for i,data in enumerate(transformed):
    transformed[i]=abs(data)
plt.subplot(212)
plt.plot(freq,transformed,'b-')
plt.xlabel('Freq/Hz')
plt.ylabel('Ampltitude')
plt.title('Num '+num+' freq/ampltitude')
plt.show()

结果如下


哎呀我去,这也太完美了吧……我都怀疑下的这些波形文件是电脑写的,也太规整了,仅在两个频率处出现了峰值,其余处为0,感觉事情并不简单,上网查了一圈发现Iphone的按键音采用的是DTMF,双音多频,每个按键音都是一个高频加一个低频信号的叠加。原来如此,那我就放心继续做下去了。那么下一步是提取这两个频率值,上面我已经说了,为了方便我寻找这两个波峰,我已经将平率对应幅度全部取绝对值,现在其实就是找到两个局部极值对应的频率就好。那首先找到两个极大值,然后确定它们的位置再对应到频域上就OK,代码如下

local_max=[]
for i in np.arange(1,len(transformed)-1):
    if transformed[i]>transformed[i-1] and transformed[i]>transformed[i+1]:
        local_max.append(transformed[i])
local_max=sorted(local_max)
loc1=np.where(transformed==local_max[-1])
max_freq=freq[loc1[0][0]]
loc1=np.where(transformed==local_max[-2])
min_freq=freq[loc1[0][0]]
print 'Two freq ',max_freq,min_freq

结果如下

Two freq  1278 900

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值