国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

python利用不到一百行代碼實現一個小siri

2019-11-25 16:20:21
字體:
來源:轉載
供稿:網友

前言

如果想要容易理解核心的特征計算的話建議先去看看我之前的聽歌識曲的文章,傳送門://m.survivalescaperooms.com/article/97305.htm

本文主要是實現了一個簡單的命令詞識別程序,算法核心一是提取音頻特征,二是用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=utf8import osimport waveimport dtwimport numpy as npimport pyaudiodef 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能帶來一定的幫助,如果有疑問大家可以留言交流。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 海兴县| 嘉兴市| 七台河市| 宿州市| 吉林市| 秀山| 白水县| 光山县| 龙泉市| 德兴市| 满城县| 长岭县| 上饶市| 龙南县| 随州市| 青阳县| 获嘉县| 绍兴县| 屏东县| 九龙县| 武平县| 比如县| 罗源县| 万州区| 蓝田县| 辉南县| 平顺县| 瑞安市| 永兴县| 安岳县| 龙山县| 巩义市| 钟山县| 老河口市| 明溪县| 紫金县| 海口市| 弥勒县| 贺兰县| 古交市| 康平县|