321
Python
python音頻庫dejavu原理淺析
dejavu庫是利用python開發的音頻指紋提取與識別庫,用來實現聽歌識曲的功能。
由於近一段時間正在學習音頻分析方麵的知識。
古人有雲:要想快速掌握知識,就要學會站在巨人的肩膀上。
因此特意研究了一下dejavu的源碼。
這篇文章主要是記錄學習的過程以及庫的核心方法,權當做讀書筆記。
關於庫的使用方法,本篇不做進一步說明,作者已進行了詳細介紹,需要說明的是庫的開發者目前已不再維護該庫。
1
【前言】
在音頻分析中,最簡單的是時域分析,音頻信號的時域分析是指對聲音信號幅值隨時間變化曲線進行分析。利用pydub讀取“我.mp3”的文件(音頻的內容隻有一個字為“我”),將其波形畫出如下圖,橫軸為時間,縱軸為音頻的幅值。從圖中可以看出,在時域內隻能分析聲音信號的強弱,從波形中很難看出頻率的變化,無法對不同的音頻信號做出有效的區分。
音頻信號分析的另外一種方法是頻域分析,具體來說就是借助傅裏葉變化將原始信號從時域空間轉換至頻域空間,揭示出構成音頻信號的不同頻率成分。下圖為“我.mp3”文件的頻譜圖,從中可以看出音頻信號的頻率分布,這種分析方法可以有效的區分不同的音頻文件。但是頻譜分析無法反映音頻信號的時間信息,隻能提供全局的頻率信息,不能提供某一時刻的頻率信息,隻能用於穩態信號的分析,不能用來進行時變信號的分析,單純的利用頻譜分析無法達到聽歌識曲的目的。
下麵介紹聲音的時頻分析,獲取時頻信息最常用的方法是短時傅裏葉變換,也是dejavu庫所采用的方法。短時傅裏葉變換的原理如下,在計算音頻文件的頻率信息時,不同於頻域分析計算整段音頻文件的頻率信息,短時傅裏葉變換方法會對音頻文件進行加窗操作,選擇一個較短的窗函數對音頻信號進行截斷,利用快速傅裏葉變換計算該窗口內的信號的頻率信息,然後移動窗函數,以得到音頻文件不同時刻內的頻率信息。這樣就得到了聲音信號不同時刻的頻率分布。
dejavu庫會讀取音頻文件,利用時頻分析的方法得到不同時刻的頻率分布,然後按照一定的算法將音頻的指紋信息從音頻文件的時頻信息中提取出來。通過指紋信息來識別和區分聲音文件,每個音頻文件都有其單獨的指紋庫,比對指紋庫可以根據聲音片段以識別出整個音頻文件,以達到聽歌識曲的目的。
2
【音頻內容的讀取】
通過fingerprint_file()方法進行音頻的讀入,該方法返回音頻的各個通道的raw_data、frame_rate以及文件的unique_hash(該方法通過獲取文件MD5來對文件進行標記,主要是為了後續的文件去重)。在read()方法內引入了兩個外部庫來進行音頻文件的讀取,一個是pydub(基於ffmpeg),一個是wavio,這兩個庫都是用來進行對音頻的讀取,作者在源碼內注釋
Reads any file supportedby pydub (ffmpeg) and returns the data contained within.If filereading fails due to input being a 24-bit wav file, wavio is used asa backup.
pydub在讀取24位wav文件時可能會出錯,因此使用wavio來進行wav文件處理。另外在讀取文件時還提供另外一種方法fingerprint_directory()以支持文件的批量讀取。
3
【音頻指紋提取】
首先介紹一下本庫所利用的指紋提取的方法,在讀取完音頻文件之後,會利用短時傅裏葉轉化對音頻文件的各個通道的raw_data進行轉換,以獲取時頻信息,通過指定的算子進行過濾,獲取時頻信息內突出的點,時頻圖如下(獲取顏色相較周圍重的幾個點)(該圖片取自github)。通過比較獲取同一時間上突出點頻率最大的峰值點,然後求出每個峰值點及其後麵相鄰的15個峰值點的時間差,並將相應的峰值點和時間差哈希化,這樣就完成音頻文件的指紋提取。
指紋提取的方法在fingerprint內的fingerprint方法內,利用matplotlib內的specgram方法獲取頻率的信息,之前對於音頻分析方麵並不太熟悉,通過對這個庫的學習算是初識音頻分析。下麵做一個簡要的說明,在第一步內獲取了音頻的raw_data,這是音頻的時間與音頻幅度(也就是聲音大小)的數值列表,區分聲音不同的指標主要是聲音的頻率,因此為了進行音頻指紋的提取,需要獲取音頻的時間與頻率的對應值。使用specgram方法獲得的頻率是由一個二維列表組成的列表,列表的長度代表著音頻的時間長度單位為ms,二維列表內單個列表是各個時間點的頻率構成,每個時間點的聲音是由多個不同頻率的波組合而成。
獲取時頻信息之後,在fingerprint內的get_2D_peak方法就是對其進行過濾並提取特征值,使用了scipy內的max_filter方法進行頻率峰值的篩選,在進行過濾時選擇的原始算子如下,中間一行和中間一列全部為true,然後逐次上下遞減,呈對稱金子塔狀。
[0,1, 0]
[1,1, 1]
[0,1, 0]
該庫采用的算子長度為41*41,代碼如下:
獲取到所有的突出點之後,通過比較獲取同一時間上突出點頻率最大的峰值點,下圖為“我.mp3”所提取出來的突出點以及峰值點,其中紅點為按照上述算子所過濾出來的突出點,藍點為各個時間點內所提取出來的峰值點,圖示如下。
在dejavu庫的源碼當中,圖標顯示的方法有一些錯誤,目前已經pullrequest了,但是項目已經沒人維護了,修複見這裏(https://github.com/worldveil/dejavu/pull/107)
在提取出所有的峰值點後(圖中藍點),計算出和後續相鄰的的十五個峰值點的時間差(峰值點的數目可以可以根據自己的需求進行更改),然後利用generate_hashes對相應的峰值點和時間差進行hash化處理。generate_hashes函數如下:
至此,整個音頻文件的指紋信息已經提取出來了,在整個提取過程中,最重要的是特征點的提取。在不同的指紋提取方法中,特征點的提取方法也不相同,文章末尾所推薦的parper中有介紹另外的方法,有興趣的同學可以看看。
4
【存儲與比對】
指紋提取完畢後,會將提取到的指紋存儲至數據庫內,默認存儲為Mysql,database類為數據庫的抽象類,裏麵定義了必須重寫的一些方法,database_sql為具體的實現。這方麵不做過多描述。
通過麥克風或者硬盤讀取完一段音頻文件後,會提取出這段音頻文件的所有指紋,然後與數據庫內的指紋庫進行比對,將匹配成功的音頻信息返回。
5
【總結】
音頻識別最主要的是對於音頻特征值的提取分析,難點也在這裏。由於本人是音頻分析的新手,通過閱讀這些源碼似的我對音頻方麵的分析方法和流程的學習更加深入,因此寫下這篇文章以示記錄。下麵是我在進行學習過程中讀到的幾篇parper,很不錯,同樣推薦給大家。
Shazam
(https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf)
基於哈希的音頻指紋提取算法的研究
最後更新:2017-10-09 02:46:13