赞
踩
听歌识曲是如何工作的,看完这个你就懂啦!
使用的算法是《An Industrial-Strength Audio Search Algorithm》,部分算法借鉴了Github的dejavu(https://github.com/worldveil/dejavu)项目。
上一篇:
为获得良好的阅读体验,你可能先需要了解 散列(哈希),python基础语法,一定的代码阅读能力,一定的语文理解能力(up写的很乱)。
在开始阅读本篇文章之前,最好认真阅读一下论文《An Industrial-Strength Audio Search Algorithm》,最好是一个字一个字,一个图一个图的把paper看下来(然后你就不需要来看这篇文章了,笑)
paper下载链接:https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf
中文翻译:https://blog.csdn.net/yutianzuijin/article/details/49787551
建议读英文的鸭。
好的,那么正式开始。
基本原理:对于每个音频,我们都要给他生成一些特殊的指纹,然后用这些指纹和要识别的歌曲进行比较,最后取匹配率最高的那个,就是识别出来的歌曲啦~
听起来是不是非常简单鸭~~~~
那么怎么生成这个指纹呢,好问题!我们要分为三个步骤:生成频谱图(频域图像)。
在频谱图的基础上计算出Constellation Map(星状图)
对星状图进行处理,生成指纹哈希。
首先是生成频谱图。
为了生成频谱图,我们就得把歌曲最开始拿出来的数据进行一波傅里叶变换,把时域信号转变为频域信号。什么?你不知道傅里叶变换(Fourier Transformation)?不如看看这个:https://zhuanlan.zhihu.com/p/19759362
但是,傅里叶变换有一个缺陷,那就是它在变换过程中把时间信息丢失了,也就是说傅里叶变换不能反映时间维度局部区域上的特征, 人们虽然从傅立叶变换能清楚地看到一整段信号包含的每一个频率的分量值,但很难看出对应于频率域成分的不同时间信号的持续时间和发射的持续时间,缺少时间信息使得傅立叶分析再更精密的分析中失去作用 [1]。
在听歌识曲中,时间是一个非常重要的因素。如果我们没有了时间,我就不知道什么频率在什么时候出现,强度又是多少。所以,我们不能抛弃时间这个变量。因此,我们可以利用短时傅里叶变换(STFT,short time Fourier transformation)来对音频数据进行分析和计算。
短时傅里叶变换通过加窗操作,把信号分成一段一段来计算,这样子在将时域型号转变为频域信号的同时,保留住了时间。
这看起来非常困难,但是!python中大量的第三方库给予了我们极大的便利。这里我们用到了 matplotlib 中的 mlab.specgram 来对音频数据进行分析。
生成频谱图
如图所示,只要我们把上一步中取到的音频数据(sample 每一个channel中的数据),放到里面,然后加上一些参数就好啦。
对于剩下的参数,我们可以取默认值,也可以自己修改。有兴趣可以去官方文档里看。
UP的取值是 nfft = 4096, nooverlap = 2048, window(加窗函数)用的是Hamming窗函数,fs是音频的采样速率。
接着,我们还可以对数据进行一些操作,比如:对取出来的值进行log运算,将其放到对数空间中,便于数据之间的比较与操作(因为不放入对数空间的话,数值可能会很大)。放入对数空间中
像这样,通过log10 之后的强度大小(或者说能量大小)便会控制在0-70这个范围之内啦。
频谱图的样子:在au中生成的频谱图
论文中给出的频谱图
好的,生成了频谱图,接下来我们要做的就是第二步,生成 Constellation Map!
首先我们来看看星状图到底长什么样(如果你读过paper,或许可能已经知道了)论文中给出的图片
根据peper中所说的,能量越大的点抗噪性就越强,所以我们将能量大的点作为判断依据。
在频谱图中,颜色越亮代表该点的能量越大。
小小的解释一下,这个频谱图看起来是二维的,实际上是三维的,包含时间,频率,以及能量大小(强度)
仔细观察一下,每个点(也就是星星,或者说峰值)的位置是不是都在都在颜色比较亮的地方。通过matplotlib生成的
这是一整首歌所产生的星状图
那么,我们要如何找到这些能量较大的点(峰值)呢?(由于up能力有限,只能复制粘贴别人的方法了)。
worldveil 大神巧妙的使用scipy,利用图像分析法来找到这些点。
好的,我们请看 worldveil 大神在项目dejavu [2] 中带来的算法(鼓掌!!!!!)。寻找峰值
minimun_peak_amplitude 是可以认为是峰值的最小能量值(在log空间中它 的取值范围是0-70),取得越高,产生的能量较大点的数量就越少,准确率就越低,但也不要取的太小。我在这里取了minimun_peak_amplitude=10
peak_neighborhood_size 是一个点想要成为能量较大点,也就是local maximum 所处的领域的大小。越小的话产生的点会越多,不建议太大,也不建议太小。我在这里取了peak_neighborhood_size = 20
经过一顿操作,峰值就找出来了。
其中 frequency_idx 和 time_idx 就是能量较大的点(峰值)所处的频率坐标和时间坐标了。
我们可以用zip函数把他们组成坐标的形式[(t1,f1),(t2,f2),(t3,f3)……]组合为坐标
----
其实到这里,我们已经可以通过这个星状图来对歌曲进行识别了。只要看某个片段是否能够与整首歌中某一块区域的对上就可以判断了。
论文的作者是这样子描述的:If you put the constellation map of a database song on a strip chart, and the constellation map of a short matching audio sample of a few seconds length on a transparent piece of plastic, then slide the latter over the former, at some point a significant number of points will coincide when the proper time offset is located and the two constellation maps are aligned in register. [4]翻译:如果你把数据库中某首歌的星状图散乱在一个条形图上,然后将几秒样本的星状图放在一个透明的塑料板上。在条形图上滑行塑料板(有点像游标卡尺),到某个时刻的时候就会出现一件神奇的事情:当样本和数据库歌曲的正确位置对齐时,重叠的极大值就会格外多,这样就意味着样本和数据库中正确音乐的正确位置匹配上了![3]
但是,直接对比需要耗费大量的时间。那么如何才能加快呢?我们可以对这些坐标进行快速组合哈希来获取最终的指纹并减少对比所需要的时间。
所以第三步!对星状图进行处理,生成指纹哈希。
生成指纹哈希的基本原理:将两个峰值点组合在一起,生成一个指纹,包含由两者的频率和时间差组成的哈希,以及前一个点的时间位置。指纹的生成方式
为此,我们首先要选择一个锚点(anchor point),然后每个锚点都对应一个目标区域(target zone)。(ps: 这个target zone 的选择极为讲究,我踩了一堆坑。)指纹生成图解
target zone 与 anchor point 的距离不宜靠的太近,也不宜离得太远。
前者会导致选择的两个点没有独特性,从而使生成 的哈希没有什么卵用;后者会导致两个点的时间跨度太大,也会使生成哈希没什么鸡儿用。所以,这个时候我们要认真看一下论文中的图(上图)。
在图中可以发现,anchor point 和 target zone 之间至少隔了5个时间单位,小于5个时间单位的都没选。
所以,我们在选择target zone的时候,时间差最好要超过5个单位。如果你们不信的话可以自己试一试,up被这个坑了好久。
接下来上代码
首先,我们要对上一步中取得的那些峰值以时间顺序进行排序,方便之后选择 anchor point 与 target zone。峰值排序
然后,我们将每一个能量较大点后的n个点作为target zone的范围。这个n称为fan-out。Fan-out用来决定每一个 anchor point 最多可以与多少个点进行匹配。
同时,我们通过判断两个点之间的时间差是否在我们需要的范围内(是否在 target zone之中),来最终决定是否将这两个点作为歌曲的指纹放入数据库中。选择生成指纹的峰值
time_constraint_condition 是一个 元组,包含可以作为指纹的两个点的最小时间差和最大时间差,up的取值是 (9,200)
fanout_factor 就是fanout常数。该值越大,生成的指纹就越多。在提高成功率的同时大大增大了所需要的储存空间以及牺牲了一点点的查询速度。up 的取值是 fanout_facotr = 20 (论文中说大于10就可以了)
然后,我们只需要把两个点的频率以及时间差组合起来,生成一个哈希,在加上 anchor point 的时间位置,一个指纹就生成好了。生成指纹
嘛,这样子生成指纹的部分就完成啦。接下来就只有搜索与识别的部分了。
结束。
Reference:
[1]: https://blog.csdn.net/lvsehaiyang1993/article/details/80521538
[2]: https://github.com/worldveil/dejavu
[3]: https://blog.csdn.net/yutianzuijin/article/details/49787551
[4]: Wang, Avery. "An Industrial Strength Audio Search Algorithm." Ismir. Vol. 2003. 2003.
转载请先获得许可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。