音频指纹技术

in #cn7 years ago

音频指纹技术(audio fingerprinting technology)是指通过特定的算法将一段音频中独一无二的数字特征以标识符的形式提取出来,用于识别海量的声音样本或跟踪定位样本在数据库中的位置。音频指纹作为内容自动识别技术的核心算法,已广泛应用于音乐识别,版权内容监控,内容库去重和电视第二屏互动等领域。

技术属性
音频指纹技术通过提取声音中的数据特征将需要被识别的内容与建立的音频指纹数据库进行比对完成的。识别过程不受音频本身的储存格式,编码方式,码率和压缩技术影响。音频指纹的匹配是高度精确的匹配,不依赖于文件meta信息,加水印和文件哈希值。
技术应用
音乐识别、电视第二屏互动、音乐广告监督、文件识别(版权),收视调查

python的开元音频指纹项目Dejavu Project,https://github.com/worldveil/dejavu

dejavu 再从磁盘读取位置波形文件或听取记录至少5秒时展现100%回忆。

快速傅里叶变换(FFT)做信号处理。
音乐是数字编码的,只是很长一段数字而已。在一个为压缩的wav文件中,有很多这样的数字 - 每个频道每秒44100个,例如一首三分钟的歌曲有近1600万个样本

采样
奈奎斯特-香农抽样理论,每秒44100个样本,记录时我们可以准确不获得最大频率存在理论上的限制,这个最大频率取决于我们采样信号的速度。

考虑观看风扇叶片,以每秒钟一次的速度(1 Hz)完成一次完整的旋转。现在想象一下,闭上眼睛,但每秒打开一次。如果风扇仍然每隔1秒完成一次完整的旋转,那么风扇叶片看起来好像还没有移动!每次你睁开眼睛,刀片恰好在同一个位置。但是有一个问题。事实上,据你所知,风扇叶片每秒钟可以产生0,1,2,3,10,100甚至1百万次自旋,你永远不会知道 - 它仍然会呈现静止状态!因此,为了确保您正确采样(或“看见”)更高的频率(或“旋转”),您需要更频繁地采样(或“打开你的眼睛”)

在录制音频的情况下,我们接受的规则是我们可以接受22050Hz以上的频率,因为人们听不到20000Hz以上的频率,因此对于斯奎奈特来说,我们要采样两次
每秒需要的采样=最高频率 * 2 = 22050 * 2 = 44100
MP3格式压缩了这一点为了:1)节省硬盘空间;2)刺激发烧友,但是计算机上纯粹的.wav只是一个16位整数(带有小标题)的列表。

频谱图
由于这些样本是各种信号,我们可以在歌曲样本的小窗口中重复使用FFT来创建歌曲的图谱。Thicke的‘模糊线条’的前几秒钟的光谱图。

正如你所看到的,它是一个二维数组,其振幅随时间和频率的变化而变化。FFT显示了该特定频率夏新好的强度(幅度),给了我们一个列。如果我们用FFT的滑动窗口做足够多次,我们将它们放在一起并得到二维阵列谱图。

峰值查找
我们已经获得了音频信号的谱图,我们可以从振幅中找到‘峰值’开始,我们将一个峰值定义为一个(时间,频率)对,它对应于在其周围的一个局部‘邻域’中最大的幅度值。其它(时间,频率)对围绕他的幅度较低,因此不太可能存在噪声。
把光谱图视为图像,使用图像处理工具包和技术scipy来查找峰值。高通滤波器(强调高振幅)和scipy局部最大值结构的组合完成了这个诀窍。一旦我们提取了这些抗噪声峰值,我们就找到了能识别他的歌曲中的兴趣点,一旦我们找到了峰值,我们就有效地降低了图谱。振幅已经达到了他们的目的,并且不再需要。

我们找到了峰值,并且离散成了(时间,频率)对,但是不同的歌曲可能有相同的峰值???

指纹哈希
我们将峰组合成指纹,通过使用散列函数来做到这点。散列函数需要一个整数输入,返回另一个整数输出。一个好的散列函数不仅会在每次输入相同时返回相同的输出整数,而且很少有不同的输入将具有相同的输出。
通过查看我们的谱图峰值,并结合峰值频率和他们之间的时间差,我们可以创建一个散列,表示这首歌的唯一指纹。
hash(frequencies of peaks, time difference between peaks) = fingerprint hash value

有很多不同的方法可以做到这一点,Shazam拥有自己的SoundHound,等等。重点是通过考虑多个单峰值的值,可以创建熵指纹,可以包含更多的信息,他们是强大的歌曲标示符,他们的冲突较少。
可以直观的看到缩放的带注释的光谱图在下面剪切的情况下发生了什么:

Shazam白皮书将这些峰值组比作一种用于识别歌曲的峰值‘星座’。实际上,他们使用双峰值和两者之间的时间增量,质问中更多的峰值意味着更难以识别歌曲的罕见指纹,但更多的峰值也意味着在噪音面前不好。

学习一首歌
我们研究这样的系统是如何工作的,音频指纹识别系统有两个任务:

  1. 通过指纹识别新歌
    2.通过在学习歌曲的数据库中搜索位置歌曲来识别未知歌曲
    为此,我们将使用迄今为止的只是以及MySql数据库功能。数据库模式包含两个表格:指纹和歌曲

指纹表
指纹表具有以下字段
create table fingerprints (
hash binaru(10) not null,
song_id mediumint unsigned not null,
offset int unsigned not null,
index(hash),
unique(song_id,offset,hash)
)

我们不仅有散列和歌曲ID,还有一个偏移量。这对应于哈希源自的光谱图的时间窗口,在我们进行匹配散列进行筛选时会发挥作用。只有‘对齐’的散列来自我们想要识别的真实信号(参照指纹对齐部分)。我们做了一个index哈希,查询的时候会有非常快速的检索。
unique只是保证我们没有重复。
使用binary(10)哈希域,是因为我们有很多这样的哈希,减少空间是非常重要的。以下是每首歌曲的指纹数量图表:

图的前面是justin timberlake的‘镜子’,超过240k指纹,后面是robin thicke的‘blurred lines’,180k。地卜师Acapella的‘杯子’,这是一个系数的乐器配乐。图形中,听‘镜子’你会注意到显而易见的噪音强仪器,并安排将频谱从高到低填充,这意味着频谱在高频和低频都有峰值,该数据集平均每首歌曲的指纹数量超过10万。
有了这么多指纹,我们需要从散列值级别减少不必要的磁盘存储空间。对于我们的指纹散列,我们将使用SHA-1散列开始,然后将其缩小到其大小的一半(仅前20个字符),这将每个散列的字节使用量减半。

char(40) =>char(20)从40字节到20字节

接下来我们将采用这种16进制编码并将其转换为二进制,再次大幅减少空间:

char(20) => binary(10) 从20个字节到10个字节
好多了,我们将320位下降到80位hash,减少了75%。
第一次尝试系统,我char(40)为每一个哈希使用了一个字段,这导致超过1GB的空间禁用于志文。在binary(10)现场,我们将表格大小缩减为仅有377MB的520万个指纹。
我们确实对事一些信息,统计上来说,我们的哈希现在会更频繁的发生碰撞,我们大大减少了散列的‘熵’。然而重要的是记住,我们的熵(或信息)还包括offset4字节的字段,这将我们没个指纹的总熵带到:
10字节(散列) + 4字节(偏移量) = 14字节 = 112位 = 2^112 ==5.2+e33可能的指纹
我们已经为自己节省了75%的空间,并且仍然拥有难以想象的大型指纹空间,大型钥匙的分配是一个很难说的问题,但我们确实有足够的熵来解决问题。

歌曲表

歌曲表格相对简单,基本上我们只是用它来保存关于歌曲的信息,我们需要将它song_id与歌曲的字符串名称配对。
create table songs (
song_id mediumint unsigned not null auto_increment,
song_name varchar(250) not null,
fingerprinted tinyint default 0,
primary key(song_id),
unique key song_id(song_id)
)
fingerprinted Dejavu在内部使用该标志来决定是否对文件执行打印。我们将该位设置为初始位置0,并且在指纹处理(通常是两个通道)完成后将其设置为1。

指纹对齐
现在我们已经听到了一段音轨,在歌曲长度上的重叠窗口中执行FFT,提取峰值并形成指纹。假设我们已经在已知的轨道上执行了此指纹,即我们已经蒋志文插入表有歌曲ID的数据库中,我们可以简单地匹配
伪代码:
channels = capture_audio()
fingerprints_matching = []
for channel_samples in channels
hashes = process_audio(channel_samples)
fingerprints_matching +=find_database_matches(hashes)
predicted_song = align_matches(fingerprints_matching)

对于哈希值来说,这意味着什么?让我们将我们正在听的样本作为原始音轨的一部分进行思考。一旦我们做到这一点,我们从提取样品的散列将有一个offset是相对于样品的开始。

当然问题在于,当我们最初获得指纹时,我们记录了散列的绝对偏移量。除非我们从歌曲的开始处开始录制样本,否则样本中的相对哈希值和数据库中的绝对哈希值将永远不匹配,相当不可能。

但虽然他们不一样,但我们从噪音背后的真是信号知道一些关于匹配的信息,我们知道所有的相对偏移将相距相同的距离。这需要假设曲目正在以录制和演播室中录制和播放的相同速度进行播放和采样,实际上,在播放速度不同的情况下,我们会很失败,因为这会影响播放的频率,从而影响频谱图总的峰值。无论如何,播放速度假设是一个好的假设。

在这个假设下,对于每次测试,我们计算偏移之间的差异:
差异 = 原始轨道的数据库偏移量 - 记录的样本偏移量

这将始终产生一个后整数,因为数据库轨道总是至少是样本的长度,所有真正的检测都有同样的不同。因此我们将数据库中的匹配变为如下所示:
(song_id,区别)

现在我们只是查看所有的测试,并预测差异最大的歌曲ID,如果将其视为直方图,这很容易想象。这就是他的全部。

工作效果
要真正获得音频指纹识别系统的好处,指纹不需要很长时间,这是一种糟糕的用户体验,此外,用户可能只决定尝试该歌曲与电台在商业休息之前留下的几个宝贵的音频时间相匹配
为了测试Dejavu的速度和准确性,选取了45首歌曲列表,通过三种方式进行测试:
1.从磁盘读取原始的mp3 ->wav数据
2.用Dejavu在笔记本电脑麦克风聆听,通过养生区播放歌曲。
3.在iphone上播放压缩流媒体音乐
一、从磁盘读取
从磁盘读取是百分之百回忆,对不同指纹的45手歌曲没有犯错。由于Dejavu从歌曲中获取所有的样本(没有噪音),如果从磁盘读取相同的文件不会每次都工作,那将是令人讨厌的惊喜!
二、通过笔记本电脑麦克
在这里,写了一个脚本,n从原始的mp3文件中随机选择几秒音频播放,并让Dejavu监听麦克风。公平的说,我只允许音轨的开始/结束超过10秒的音频段,以避免听到沉默。
此外,测试过程有人说话,有人哼哼,有点噪音。
以下是不同听音时间值(n)的结果:

百分比:
秒数 数量正确 百分比准确度
1 27/45 60%
2 43/45 95.6%
3 44/45 97.8%
4 44/45 97.8%
5 45/45 100%
6 45/45 100%
即使只有1秒钟,从歌曲的任何地方随机选择,Dejavu正在获得60%!一秒钟到两秒,我们可以达到96%左右,而完美只需要5秒钟或更长时间。说实话,当我自己测试这个时,我发现Dejavu只要1-2秒就能辨别出一首非常难听出来的歌曲。效率非常高。
3.在我的iPhone上播放压缩的流媒体音乐
试试看,我尝试通过我的iPhone扬声器播放我的Spotify帐户中的音乐(压缩为160 kbit / s),Dejavu再次听我的MacBook麦克风。我没有看到表演中的降级; 1-2秒足以识别任何歌曲。

性能:速度
在macbook pro上,匹配是在3倍的聆听速度下完成的,并且消耗很小。为了测试,尝试了不同的录制时间,并绘制了录制时间加上那个匹配的时间。歌曲的播放素的是不变的,依赖于创建频谱图的长度,使用单曲‘get lucky’进行测试。

正如你看到的,这种关系非常线性的,您看到的是这条线是对数据进行拟合的最小二乘线性回归,并带有相应的线方程。
1.364757 * 记录时间 - 0.034373 = 匹配时间
当然,因为匹配本身是单线程的,所以匹配时间包括记录时间,这与纯粹匹配的3倍速度有关,如下所示:
1(记录)+1/3(匹配)=4/3 = 1.364757

性能:存储
对于我指纹的45首歌曲,数据库使用了377 MB的空间用于540万个指纹。相比之下,磁盘使用情况如下:
音频信息类型 以MB为单位存储
MP3 339
WAV 1885
指纹 377
在必要的记录时间和所需的存储量之间有一个相当直接的折衷。调整峰值的振幅阈值和指纹识别的风扇值将增加更多的指纹并以更多空间为代价来提高精度。
确实,这些指纹占据了惊人的空间(比原始MP3文件略微多一些)。这似乎令人震惊,直到你认为每​​首歌曲有数十次,有时数十万次散列。我们将波形文件中的整个音频信号的纯信息换算成指纹中存储的大约20%。我们还在五秒内非常可靠地启用了匹配的歌曲,所以我们的空间/速度折衷似乎已经取得成功。

Sort:  

Congratulations @wangjihui! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!