用Python实现的音频指纹和识别算法,请参见此处的说明: How it works

Dejavu可以通过听一次并提取指纹来记忆音频。然后,通过播放歌曲并录制麦克风输入或从磁盘读取的内容,Dejavu尝试将音频与数据库中保存的指纹进行匹配,返回正在播放的歌曲。

注意:对于语音识别,*Dejavu不是正确的工具!*Dejavu擅长识别具有合理噪声量的精确信号。

Quickstart with Docker

First, install Docker.

# 构建并运行我们的容器
$ docker-compose build
$ docker-compose up -d

# 在容器内获取一个外壳
$ docker-compose run python /bin/bash
Starting dejavu_db_1 ... done
root@f9ea95ce5cea:/code# python example_docker_postgres.py 
Fingerprinting channel 1/2 for test/woodward_43s.wav
Fingerprinting channel 1/2 for test/sean_secs.wav
...

# 连接到数据库并四处查看
root@f9ea95ce5cea:/code# psql -h db -U postgres dejavu
Password for user postgres:  # type "password", as specified in the docker-compose.yml !
psql (11.7 (Debian 11.7-0+deb10u1), server 10.7)
Type "help" for help.

dejavu=# \dt
            List of relations
 Schema |     Name     | Type  |  Owner   
--------+--------------+-------+----------
 public | fingerprints | table | postgres
 public | songs        | table | postgres
(2 rows)

dejavu=# select * from fingerprints limit 5;
          hash          | song_id | offset |        date_created        |       date_modified        
------------------------+---------+--------+----------------------------+----------------------------
 \x71ffcb900d06fe642a18 |       1 |    137 | 2020-06-03 05:14:19.400153 | 2020-06-03 05:14:19.400153
 \xf731d792977330e6cc9f |       1 |    148 | 2020-06-03 05:14:19.400153 | 2020-06-03 05:14:19.400153
 \x71ff24aaeeb55d7b60c4 |       1 |    146 | 2020-06-03 05:14:19.400153 | 2020-06-03 05:14:19.400153
 \x29349c79b317d45a45a8 |       1 |    101 | 2020-06-03 05:14:19.400153 | 2020-06-03 05:14:19.400153
 \x5a052144e67d2248ccf4 |       1 |    123 | 2020-06-03 05:14:19.400153 | 2020-06-03 05:14:19.400153
(10 rows)

# then to shut it all down...
$ docker-compose down

如果您希望能够将麦克风与Docker容器一起使用,则需要执行以下操作:little extra work. 我还没有时间写这篇文章,但是如果有人想做公关,我很乐意合并。

本地机器上的Docker备选方案

Follow instructions in INSTALLATION.md

接下来,您需要创建一个MySQL数据库,Dejavu可以在其中存储指纹。例如,在本地设置上:

$ mysql -u root -p
Enter password: **********
mysql> CREATE DATABASE IF NOT EXISTS dejavu;

现在,您可以开始为音频采集提取指纹了!

当然,你也可以使用Postgres。同样的方法也适用。

指纹

比如说,我们想找出2013年7月弗吉尼亚州全美40大热门歌曲的指纹。

首先使用您的配置设置创建一个Dejavu对象(Dejavu使用一个普通的Python字典进行设置)。

>>> from dejavu import Dejavu
>>> config = {
...     "database": {
...         "host": "127.0.0.1",
...         "user": "root",
...         "password": <password above>, 
...         "database": <name of the database you created above>,
...     }
... }
>>> djv = Dejavu(config)

接下来,为fingerprint_directory方法提供三个参数:

  • 输入目录以查找音频文件
  • 要在输入目录中查找的音频扩展
  • 进程数(可选)
>>> djv.fingerprint_directory("va_us_top_40/mp3", [".mp3"], 3)

对于大量文件,这将需要一段时间。然而,Dejavu足够强大,您可以在不影响进度的情况下杀死并重新启动:Dejavu会记住哪些歌曲被提取指纹并转换,哪些没有,因此不会重复。

完成一个大文件夹的MP3后,您会有很多指纹:

>>> print djv.db.get_num_fingerprints()
5442376

此外,对fingerprint_filefingerprint_directory的任何后续调用都将对这些歌曲进行指纹识别并将其添加到数据库中。这是为了模拟一个系统,在这个系统中,当新歌发布时,它们会被提取指纹并毫无表情地添加到数据库中,而不会停止系统。

配置选项

Dejavu构造函数的配置对象必须是字典。

以下密钥是必需的:

以下键是可选的:

  • fingerprint_limit: 允许您控制每个音频文件的指纹识别时间。省略此密钥,或者使用-1None将导致Dejavu对整个音频文件进行指纹识别。默认值为None
  • database_typemysql(默认值)和postgres。如果您想为BaseDatabase添加另一个子类并实现一种新的数据库类型,请fork并发送pull请求!

示例配置如下所示:

>>> from dejavu import Dejavu
>>> config = {
...     "database": {
...         "host": "127.0.0.1",
...         "user": "root",
...         "password": "Password123", 
...         "database": "dejavu_db",
...     },
...     "database_type" : "mysql",
...     "fingerprint_limit" : 10
... }
>>> djv = Dejavu(config)

配置

config/settings.py中,您可能需要调整以下参数(下面给出了一些值)。

FINGERPRINT_REDUCTION = 30
PEAK_SORT = False
DEFAULT_OVERLAP_RATIO = 0.4
DEFAULT_FAN_VALUE = 5
DEFAULT_AMP_MIN = 10
PEAK_NEIGHBORHOOD_SIZE = 10

文件中详细描述了这些参数。阅读此文以了解更改这些值的影响。

识别

使用Dejavu识别音频有两种方法。您可以通过读取和处理磁盘上的文件或通过计算机的麦克风进行识别。

识别:在磁盘上

通过终端:

$ python dejavu.py --recognize file sometrack.wav 
{'total_time': 2.863781690597534, 'fingerprint_time': 2.4306554794311523, 'query_time': 0.4067542552947998, 'align_time': 0.007731199264526367, 'results': [{'song_id': 1, 'song_name': 'Taylor Swift - Shake It Off', 'input_total_hashes': 76168, 'fingerprinted_hashes_in_db': 4919, 'hashes_matched_in_input': 794, 'input_confidence': 0.01, 'fingerprinted_confidence': 0.16, 'offset': -924, 'offset_seconds': -30.00018, 'file_sha1': b'3DC269DF7B8DB9B30D2604DA80783155912593E8'}, {...}, ...]}

或者在脚本中,假设您已经实例化了Dejavu对象:

>>> from dejavu.logic.recognizer.file_recognizer import FileRecognizer
>>> song = djv.recognize(FileRecognizer, "va_us_top_40/wav/Mirrors - Justin Timberlake.wav")

识别:通过麦克风

使用脚本:

>>> from dejavu.logic.recognizer.microphone_recognizer import MicrophoneRecognizer
>>> song = djv.recognize(MicrophoneRecognizer, seconds=10) # Defaults to 10 seconds.

使用脚本:和命令行脚本,可以指定要侦听的秒数:

$ python dejavu.py --recognize mic 10

Testing

随着语料库越来越大,不可避免地在速度和准确性之间进行权衡,测试指纹算法的不同参数化通常是有用的。

 

在音频文件的语料库上测试Dejavu设置,测试指标有很多:

  • 匹配的置信度(对齐的指纹数)
  • 偏移匹配精度
  • 歌曲匹配精度
  • 比赛时间到了

 

test_dejavu.sh中给出了一个示例脚本,如下所示:

#####################################
### Dejavu示例测试脚本 ###
#####################################

###########
# 清除以前的结果
rm -rf ./results ./temp_audio

###########
# ./mp3文件夹中mp3扩展名的指纹文件
python dejavu.py --fingerprint ./mp3/ mp3

##########
# 通过提取1、2、3、4和5,在./mp3文件夹上运行测试套件
# 第二个剪辑从每首歌曲中随机抽取8秒
# 远离起点或终点,随机种子的采样偏移=42,最后,
# 将结果存储在./results中并记录到./results/dejavu-test.log
python run_tests.py \
    --secs 5 \
    --temp ./temp_audio \
    --log-file ./results/dejavu-test.log \
    --padding 8 \
    --seed 42 \
    --results ./results \
    ./mp3

目前的测试脚本有点粗糙,如果你对提交PR感兴趣的话,当然需要一些关爱和关注!例如,当前音频文件名中的下划线[中断](https://github.com/worldveil/dejavu/issues/63)测试脚本。

它是如何工作的?

该算法适用于基于指纹的系统,非常类似于:

T“指纹”是根据音频的频谱图计算出的对位置敏感的散列。这是通过在歌曲重叠窗口上对信号进行FFT并识别峰值来实现的。需要一个非常健壮的峰值查找算法,否则会产生糟糕的信噪比。 在这里,我拍摄了“模糊线”前几秒钟的光谱图。频谱图是一个二维图,显示振幅随时间(实际上是一个特定的窗口)和频率的函数,按逻辑组合,就像人耳所感知的那样。在下图中,您可以看到振幅空间中出现局部最大值的位置:  找到这些局部最大值是高通滤波器(振幅空间中的阈值)和一些图像处理技术的组合,以找到最大值。需要一个“邻里关系”的概念——只有直接相邻像素的局部最大值是一个很差的峰值——一个无法经受来自扬声器和麦克风的噪音的峰值。 如果我们放大得更近,我们可以开始想象如何对这些峰值进行分类和离散化。找到峰值本身是计算最密集的部分,但它不是终点。峰值使用离散的时间和频率容器组合,为歌曲中的特定时刻创建一个唯一的散列-创建一个指纹。  要更详细地了解Dejavu的制作过程,请参阅我的博客here.

 

 

效果如何

要真正获得音频指纹识别系统的好处,指纹识别不会花费很长时间。这是一种糟糕的用户体验,而且,用户可能只会决定在广播电台进入广告时段之前,尝试将歌曲与仅剩的几秒钟宝贵音频进行匹配。 为了测试Dejavu的速度和准确性,我提取了2013年7月美国弗吉尼亚州前40名中的45首歌曲的指纹(我知道,它们的计数在某处不准确)。我以三种方式进行测试:

1.从磁盘读取原始mp3->wav数据,以及 1.通过扬声器播放歌曲,Dejavu使用笔记本电脑麦克风收听。 1.在我的iPhone上播放的压缩流音乐

以下是结果。

1. 从磁盘读取

从磁盘上读是一个压倒性的100%的回忆-没有错误的45首歌曲我指纹。由于Dejavu从歌曲中获得了所有的样本(没有噪音),如果每次都无法从磁盘读取相同的文件,那将是一个令人讨厌的惊喜!

2. Audio over laptop microphone

在这里,我写了一个脚本,从原始mp3文件中随机选择n秒的音频播放,让Dejavu通过麦克风收听。公平地说,我只允许从音轨的开始/结束超过10秒的音频片段,以避免听到沉默。

此外,我的朋友甚至在说话,我在整个过程中哼唱了一些,只是为了制造一些噪音。

以下是不同收听时间值的结果(n):

 

这真是太棒了。有关百分比:

秒数数字正确准确率百分比
127 / 4560.0%
243 / 4595.6%
344 / 4597.8%
444 / 4597.8%
545 / 45100.0%
645 / 45100.0%

即使只有一秒钟,从歌曲的任何地方随机选择,Dejavu也能得到60%!多出一秒到两秒,我们就可以达到96%左右,而达到完美只需要5秒或更长时间。老实说,当我自己测试这首歌的时候,我发现Dejavu打败了我——断章取义地只听1-2秒的歌是很难识别的。我甚至在调试时连续两天都在听这些歌。。。

总之,Dejavu工作得非常好,即使几乎没有什么东西可以使用。

3. 在我的iPhone上播放的压缩流音乐

为了尝试一下,我试着通过iPhone的扬声器播放Spotify帐户中的音乐(160 kbit/s压缩),Dejavu再次在我的MacBook麦克风上收听。我没有看到性能下降;1-2秒足以识别任何歌曲。

性能

速度

在我的MacBook Pro上,匹配是以3倍的收听速度完成的,而且开销很小。为了测试,我尝试了不同的录制时间,并绘制了录制时间加上匹配时间。由于特定歌曲的速度基本上是不变的,并且更多地取决于所创建的谱图的长度,因此我测试了一首由愚蠢朋克创作的歌曲“Get Lucky”:

 

正如你所看到的,这种关系是线性的。您看到的直线是与数据拟合的最小二乘线性回归,具有相应的直线方程:

1.364757 * record_time - 0.034373 = time_to_match

当然要注意,因为匹配本身是单线程的,所以匹配时间包括录制时间。这对于纯粹匹配的3倍速度是有意义的,因为:

1 (recording) + 1/3 (matching) = 4/3 ~= 1.364757

如果我们忽略微小的常数项。

峰值查找的开销是一个瓶颈——我尝试了多线程和实时匹配,唉,这本不应该出现在Python中。一个等价的Java或C/C++实现很可能不会有什么问题,可以实时应用FFT和peakfinding。

当然,一个重要的警告是进行比赛的往返时间(RTT)。因为我的MySQL实例是本地的,所以我不必处理通过无线传输指纹匹配的延迟惩罚。这会将RTT添加到整体计算中的常数项中,但不会影响匹配过程。

存储

对于我提取指纹的45首歌曲,数据库使用了377 MB的空间来存储540万个指纹。相比之下,磁盘使用情况如下所示:

音频信息类型存储容量(MB)
mp3339
wav1885
fingerprints377

在必要的记录时间和所需的存储量之间有一个相当直接的权衡。调整峰值的振幅阈值和指纹的扇形值将添加更多指纹,并以牺牲更多空间为代价提高准确性。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐