Aynakeya's Blog

Kill My Emotion

when i was playing with mplayer in my ras pi. I found that it always appear warning "Your system is too slow to play this". And voice often play faster than the video.

after some modifying with config and option, i found nothing was helpful.

(Btw, you should tune the speed of lcd screen at least 48000000, but you may find some strange colour behaviour appear lol)

So, finally, i tried to re encode my video.

result turns out that we can simply solve this problem by encode the video into the older encoding format like mpg or ogg.

don't use format like mp4 or flv

Result:

mplayer -nolirc -vo fbdev2:/dev/fb1 -fs -zoom -x 480 -y 320 ~/Videos/BadApple.mpg

ogg format:
ogg.JPG

mpg format:
mpg.JPG

flv format:
flv.JPG

Look at the a-v,
You can see a significant difference between the mpg and flv.

Which means flv always face sync problem while mpg not.

Update:

add -lavdopts skipframe=nonref:skiploopfilter=all may helo

Last night, when i was using vnc to log in my ras pi, i found that i stuck in the login menu.

which means even i type the correct password, after several sec, it going back to the login menu.

So, it turns out vnc may have some authoritive problem. WE can simply solve this problem by log in the ssh use:

rm -rf /home/pi/.Xauthority

第三部份
在开始之前,我们首先要认真阅读一下论文《An Industrial-Strength Audio Search Algorithm》。

paper下载链接:https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf
中文翻译:https://blog.csdn.net/yutianzuijin/article/details/49787551
建议读英文的鸭。

为获得良好的阅读体验,你可能先需要了解 mysql数据库的基础知识,python基础语法,一定的代码阅读能力,一定的语文理解能力(up写的很乱)。

好的,那么今天我们进入最后一部分,搜索与匹配。

Up在这里使用了orm框架SQLalchemy

首先,我们要建立起一个数据库来储存上一步中获取的指纹信息。

其中一个表来储存歌曲信息,一个表来储存歌曲信息对应的指纹,两个表用歌曲的id作为外键相连。

sqlalchemy的格式

注意,fingerprint 这个column 必须要加上index,这将会大大减少查询速度,千真万确。

songs表
songs 表为歌曲信息表:

id 用来储存歌曲id,

name用来存储歌曲名字

filehash用来储存该歌曲文件的哈希

fingerprinted用来判断该歌曲是否已经进行了fingerprint

fingerprints表

Fingerprints 表为储存指纹的表:

id为指纹id,没什么用

song_id 是外键,记录对应的歌曲信息

fingerprint就是获取到的指纹信息

offset就是该指纹的offset位置

建完这个表,然后你就可以把指纹信息全都放进去了。

然后,我们来讲讲搜索与识别的基本原理:

1.首先输入一段声音,然后和之间一样,获取这一段声音的指纹。

2.对获取到的每一个指纹,在数据库中搜索相同的指纹,并将指纹对应的歌曲信息,以及offset偏移值保存。

3.有相同offset差值越多的歌曲就是识别出的歌曲。

tk’=tk+offset,

这样子说可能有点抽象,我们来举个栗子:

假如在某个音频中,你提取到了1个指纹,这个指纹是 c19dde0ecb8fca81b6c98d5ee3775c26cbb32a610c82a5ecda515b0beb86d357, 且它在那段音频中的offset 是 30。

通过这个指纹,你在数据库中取到了如下的数据

取得的数据

然后我们可以计算得到 offset 差值分别为 70, 70 ,170, 20, 93, 160, 90, 70

计算后

在这里,我们可以看到,offset差值是70 且 id 为 1 的有3 个,offset差值是170 且 id 为 1的有一个,offset差值是20 且 id 为 2 的有1 个,offset差值是933 且 id 为 3 的有一个……

所以最多就是 有3个的那个值。也就是id为1的歌曲。

有人会问,为什么不一首歌一首歌的检索呢,因为慢。

接下来代码。
获取所有匹配的指纹,并将歌曲信息与offset差值保存下来。

匹配

对数据进行处理,得出相同offset差值最多的歌曲,也就是识别出来的歌曲。

处理

结束。

转载请先获得许可

第二部份
在开始之前,我们首先要认真阅读一下论文《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
建议读英文的鸭。

好的,那么正式开始。

基本原理:对于每个音频,我们都要给他生成一些特殊的指纹,然后用这些指纹和要识别的歌曲进行比较,最后取匹配率最高的那个,就是识别出来的歌曲啦~

听起来是不是非常简单鸭~~~~

那么怎么生成这个指纹呢,好问题!我们要分为三个步骤:

1、生成频谱图(频域图像)。

2、在频谱图的基础上计算出Constellation Map(星状图)

3、对星状图进行处理,生成指纹哈希。

首先是生成频谱图。

为了生成频谱图,我们就得把歌曲最开始拿出来的数据进行一波傅里叶变换,把时域信号转变为频域信号。

什么?你不知道傅里叶变换(Fourier Transformation)?不如看看这个
https://zhuanlan.zhihu.com/p/19759362

但是,傅里叶变换有一个缺陷,那就是他在变换过程中把时间信息丢失了,也就是说傅里叶变换“不能反映时间维度局部区域上的特征, 人们虽然从傅立叶变换能清楚地看到一整段信号包含的每一个频率的分量值,但很难看出对应于频率域成分的不同时间信号的持续时间和发射的持续时间,缺少时间信息使得傅立叶分析再更精密的分析中失去作用[1]。“

在听歌识曲中,时间是一个非常重要的因素。如果我们没有了时间,我就不知道什么频率在什么时候出现,强度又是多少。

所以,我们不能抛弃时间这个变量。因此,我们可以利用短时傅里叶变换(STFT,short time Fourier transformation)来对音频数据进行分析和计算。
短时傅里叶变换通过加窗操作,把信号分成一段一段来计算,这样子在将时域型号转变为频域信号的同时,保留住时间了。

这看起来非常困难,但是!python中大量的第三方库给予了我们极大的便利。

这里我们用到了matplotlib 中的 mlab.specgram 来对音频数据进行分析。

生成频谱图

如图所示,只要我们把上一步中取到的音频数据(sample 每一个channel中的数据),放到里面,然后加上一些参数就好啦。

对于这些参数,我们可以取默认值,也可以自己修改。有兴趣可以去官方文档里看。

我的取值是 nfft = 4096, nooverlap = 2048, window(加窗函数)用的是Hamming窗函数,fs是音频的采样速率。

接着,我们还可以对数据进行一些操作,比如:对取出来的值进行log运算,将其放到对数空间中,便于数据之间的比较与操作(因为不放入对数空间的话,数值可能会很大)。

放入对数空间中

像这样,通过log10 之后的强度大小(或者说能量大小)范围便会控制在0-70这个范围之内啦。

频谱图的样子:

au中的

论文中的

好的,生成了频谱图,接下来我们要做的就是第二步,生成Constellation Map!

首先我们来看看星状图到底长什么样(如果你读过paper,或许可能已经知道了)

论文中下星状图

根据paper中所说的,能量越大的点抗噪性就越强,所以我们将能量大的点作为判断依据。

在频谱图中,颜色越亮代表该点的能量越大。仔细观察一下,每个点(也就是星星)的位置是不是都在都在颜色比较亮的地方。

myplot生成的

 小小的解释一下,这个频谱图看起来是二维的,实际上是三维的,包含时间,频率,以及能量大小(强度)

那么,我们要如何找到这些能量较大的点呢?(由于up能力有限,只能复制粘贴别人的方法了)。

我们可以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)……]

zip函数

其实到这里,我们已经可以通过这个星状图来对歌曲进行识别了,只要看某个片段是否能够与整首歌中某一块区域的对上就可以判断了。

论文的作者是这样子描述的: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被这个坑了好久。

接下来上代码

捕获排序

首先,我们要对上一步中取得的那些能量较大点以时间顺序进行排序。

操作一波

然后,我们将每一个能量较大点后的n个点作为target zone的范围。这个n称为fan-out。Fan-out用来决定每一个anchor point 最多可以与多少个点进行匹配。

同时,我们通过判断两个点之间的时间差是否在我们需要的范围内,来最终决定是否将这两个点作为歌曲的指纹放入数据库中。

time_constraint_condition 是一个 元组,包含可以作为指纹的两个点的最小时间差和最大时间差,up的取值是 (9,200)
fanout_factor 越大,生成的指纹就越多,在提高成功率的同时增大了所需要的储存空间以及牺牲了一点点的查询速度。up 的取值是 fanout_facotr = 20 (论文中说大于10就可以了)

然后,我们只需要把两个点的频率以及时间差组合起来,生成一个哈希,在加上anchor point的时间位置,一个指纹就生成好了。

生成指纹

嘛,生成指纹的部分就完成啦。接下来就只有搜索与识别的部分了。

结束。

转载请先获得许可。

[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.

###shazam听歌识曲算法的解析与听歌识曲python实现的分析-1 读取歌曲


开新坑,歌曲识别计划。

使用的算法是《An Industrial-Strength Audio Search Algorithm》,其中部分代码借鉴了Github的dejavu项目。


关于读取歌曲,首先需要安装一个python库. pydub。你可以使用pip3 install pydub来安装。

audiofile = AudioSegment.from_file(filepath)
 # data = audiofile.get_array_of_samples()
 # Stereo audio array is in form of [sample_1_L, sample_1_R, sample_2_L, sample_2_R, …]
 data = np.fromstring(audiofile.raw_data, np.int16)
channels = []
 # Get data for different channel
for channel in range(audiofile.channels):
    channels.append(data[channel::audiofile.channels])

`AudioSegment.from_file(filepath)`可以读取大部分音频,wav除外。在项目中,我们需要直接对音频信息进行操作,我们可以使用raw_data 来获取最原始的数据。另外我们需要知道音频的采样速率,用frame_rate 来获取。

由于现在大部分音频都是立体声的(有多个通道),所以,对于每一个通道我们都需要分开处理。raw_data 返回的格式是这样子的[sample_1_L, sample_1_R, sample_2_L, sample_2_R, …] (在这里有左声道和右声道),分开不同的通道通道可以用切片来做`data[channel::audiofile.channels]`。

这样子,读取音频的工作就完成了。

另外,读取音频的时候可以计算一下文件的sha256值来作为这一首歌的唯一识别码。
def generateFilehash(filepath, blocksize=2 ** 16):
    sha = sha256()
    with open(filepath, "rb") as f:
        while True:
            buf = f.read(blocksize)
            if not buf:
                break
            sha.update(buf)
    return sha.hexdigest().upper()

其中,sha256可以替换为其他,如md5,sha1,sha128之类的,效果都一样。

本页面以加载以下代码,切换标签页看看吧.

var orig_title = document.title;
document.addEventListener('visibilitychange',function(){
    //浏览器切换事件
    if(document.visibilityState=='hidden') { //状态判断
        orig_title = document.title;
        document.title = '嘤嘤嘤~~~'; 
    }else {
        document.title = orig_title;
        myWaifu.showMessage("欢迎回来,Rua!",null)
    }
});

在automator里使用python3

使用/bin/bash,然后看下面代码。

export PATH+=:/usr/local/bin:/usr/bin:/bin
/usr/local/bin/python3 << "EOF" - "$@"
      //python codes
EOF

e.g. 实现FFmpeg提取音频。
export PATH+=:/usr/local/bin:/usr/bin:/bin
/usr/local/bin/python3 << "EOF" - "$@"
import sys,os
import subprocess

files = []
for f in sys.argv[1:]:
files.append(f)
for file in files:
videoPath = file
audioPath = os.path.splitext(file)[0]+".m4a"
status = subprocess.call(["ffmpeg", "-y", "-i", videoPath, "-acodec", "copy", "-vn", audioPath], shell=False)
if status == 0:
pass
else:
pass
EOF

今天遇见了一个问题,垃圾百度上什么都系都没找到,最后在stackoverflow上找到了解决方法。

https://stackoverflow.com/questions/12050590/redirect-non-www-to-www-in-htaccess

# Redirect to www
RewriteCond %{HTTP_HOST} ^[^.]+\.[^.]+$
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Redirect to https
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]
0%