歌曲識別-dejavu-python

周小董發表於2019-02-01

Dejavu 是音訊指紋和識別的演算法的一個 Python實現。Dejavu可以通過聽一次,就記住音訊然後指紋識別它。通過播放歌曲和錄音麥克風輸入,dejavu會嘗試匹配儲存在資料庫中的指紋音訊,識別歌曲。

安裝軟體包

ffmpeg用來把.mp3轉換為.wav格式

$  sudo apt install ffmpeg

pydub是ffmpeg的Python封裝

$  sudo pip install pydub

portaudio是一個免費、跨平臺、開源的音訊I/O庫。PortAudio操作的I/O不是檔案,而是音訊裝置。pyaudio需要用到這個庫

$  sudo apt install portaudio19-dev

pyaudio用來從麥克風捕獲音訊

$  sudo pip install pyaudio

numpy負責對音訊訊號執行快速傅氏變換

$  sudo pip install numpy

scipy用在執行尋峰演算法(peak finding)

$  sudo pip install scipy

matplotlib用來繪圖

$  sudo apt install python-matplotlib

MySQLdb操作MySQL資料庫

$  sudo apt install python-mysqldb mysql-server

在安裝MySQL-server時需要設定MySQL root使用者密碼。

安裝PyDejavu

$  sudo pip install PyDejavu

PyDejavu這個包好像沒有python3版本的,安裝之後裡面的內容,需要修改,import fingerprint會找不到,改成這個import dejavu.fingerprint as fingerprint


配置資料庫

建立一個資料庫用來儲存音訊指紋:

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.12-0ubuntu1.1 (Ubuntu)
 
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
mysql> CREATE DATABASE IF NOT EXISTS dejavu;
Query OK, 1 row affected (0.00 sec)
mysql> \q

從一堆mp3檔案中生成對應的指紋,然後儲存到資料庫:

genFinger.py

from dejavu import Dejavu
 
config = {
    "database": {
        "host": "127.0.0.1",
        "user": "root",
        "passwd": "test1234", 
        "db": "dejavu",
    },
    "database_type" : "mysql",
}
 
djv = Dejavu(config)
 
djv.fingerprint_directory("/home/snail/Music", [".mp3"])
print(djv.db.get_num_fingerprints())

搭建歌曲識別系統-dejavu-python

識別歌曲:pgetSongName.py

from dejavu import Dejavu
from dejavu.recognize import FileRecognizer, MicrophoneRecognizer
config = {
    "database": {
        "host": "127.0.0.1",
        "user": "root",
        "passwd": "test1234", 
        "db": "dejavu",
    },
     "database_type" : "mysql"
}
djv = Dejavu(config)
 
# 識別檔案
song = djv.recognize(FileRecognizer, "/home/snail/Music/Crystals.mp3")
print(song)
 
# 識別從mic輸入的音訊
secs = 5
song = djv.recognize(MicrophoneRecognizer, seconds=secs)
if song is None:
    print "No Match"
else:
    print(song)

搭建歌曲識別系統-dejavu-python

搭建歌曲識別系統-dejavu-python


音訊庫dejavu原理淺析

關於庫的使用方法,本篇不做進一步說明,作者已進行了詳細介紹,需要說明的是庫的開發者目前已不再維護該庫。

前言

在音訊分析中,最簡單的是時域分析,音訊訊號的時域分析是指對聲音訊號幅值隨時間變化曲線進行分析。利用pydub讀取“我.mp3”的檔案(音訊的內容只有一個字為“我”),將其波形畫出如下圖,橫軸為時間,縱軸為音訊的幅值。從圖中可以看出,在時域內只能分析聲音訊號的強弱,從波形中很難看出頻率的變化,無法對不同的音訊訊號做出有效的區分。

image

音訊訊號分析的另外一種方法是頻域分析,具體來說就是藉助傅立葉變化將原始訊號從時域空間轉換至頻域空間,揭示出構成音訊訊號的不同頻率成分。下圖為“我.mp3”檔案的頻譜圖,從中可以看出音訊訊號的頻率分佈,這種分析方法可以有效的區分不同的音訊檔案。但是頻譜分析無法反映音訊訊號的時間資訊,只能提供全域性的頻率資訊,不能提供某一時刻的頻率資訊,只能用於穩態訊號的分析,不能用來進行時變訊號的分析,單純的利用頻譜分析無法達到聽歌識曲的目的。

image

下面介紹聲音的時頻分析,獲取時頻資訊最常用的方法是短時傅立葉變換,也是dejavu庫所採用的方法。短時傅立葉變換的原理如下,**在計算音訊檔案的頻率資訊時,不同於頻域分析計算整段音訊檔案的頻率資訊,短時傅立葉變換方法會對音訊檔案進行加窗操作,選擇一個較短的窗函式對音訊訊號進行截斷,利用快速傅立葉變換計算該視窗內的訊號的頻率資訊,然後移動窗函式,以得到音訊檔案不同時刻內的頻率資訊。**這樣就得到了聲音訊號不同時刻的頻率分佈。

dejavu庫會讀取音訊檔案,利用時頻分析的方法得到不同時刻的頻率分佈,然後按照一定的演算法將音訊的指紋資訊從音訊檔案的時頻資訊中提取出來。通過指紋資訊來識別和區分聲音檔案,每個音訊檔案都有其單獨的指紋庫,比對指紋庫可以根據聲音片段以識別出整個音訊檔案,以達到聽歌識曲的目的。

音訊內容的讀取

通過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()以支援檔案的批量讀取。

音訊指紋提取

首先介紹一下本庫所利用的指紋提取的方法,在讀取完音訊檔案之後,會利用短時傅立葉轉化對音訊檔案的各個通道的raw_data進行轉換,以獲取時頻資訊,通過指定的運算元進行過濾,獲取時頻資訊內突出的點,時頻圖如下(獲取顏色相較周圍重的幾個點)(該圖片取自github)。通過比較獲取同一時間上突出點頻率最大的峰值點,然後求出每個峰值點及其後面相鄰的15個峰值點的時間差,並將相應的峰值點和時間差雜湊化,這樣就完成音訊檔案的指紋提取。

image

指紋提取的方法在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,程式碼如下:

struct= generate_binary_structure( 2, 1)neighborhood= iterate_structure(struct, 20)

獲取到所有的突出點之後,通過比較獲取同一時間上突出點頻率最大的峰值點,下圖為“我.mp3”所提取出來的突出點以及峰值點,其中紅點為按照上述運算元所過濾出來的突出點,藍點為各個時間點內所提取出來的峰值點,圖示如下。

在dejavu庫的原始碼當中,圖示顯示的方法有一些錯誤,目前已經pullrequest了,但是專案已經沒人維護了,修復見這裡(https://github.com/worldveil/dejavu/pull/107)

image

在提取出所有的峰值點後(圖中藍點),計算出和後續相鄰的的十五個峰值點的時間差(峰值點的數目可以可以根據自己的需求進行更改),然後利用generate_hashes對相應的峰值點和時間差進行hash化處理。

至此,整個音訊檔案的指紋資訊已經提取出來了,在整個提取過程中,最重要的是特徵點的提取。在不同的指紋提取方法中,特徵點的提取方法也不相同,文章末尾所推薦的parper中有介紹另外的方法,有興趣的同學可以看看。

儲存與比對

指紋提取完畢後,會將提取到的指紋儲存至資料庫內,預設儲存為Mysql,database類為資料庫的抽象類,裡面定義了必須重寫的一些方法,database_sql為具體的實現。這方面不做過多描述。

通過麥克風或者硬碟讀取完一段音訊檔案後,會提取出這段音訊檔案的所有指紋,然後與資料庫內的指紋庫進行比對,將匹配成功的音訊資訊返回。

總結

音訊識別最主要的是對於音訊特徵值的提取分析,難點也在這裡。

Shazam:http://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf

基於雜湊的音訊指紋提取演算法的研究:http://gb.oversea.cnki.net/KCMS/detail/detail.aspx?filename=1016092927.nh&dbcode=CMFD&dbname=CMFD2017

來源:http://blog.topspeedsnail.com/archives/6530
https://github.com/worldveil/dejavu
https://www.sohu.com/a/196804134_575744

相關文章