真实感图像渲染系列:颜色,向量与物体

这是真实感图像渲染系列的第二篇文章。

颜色

颜色,向量与物体是各种渲染算法的基础。

颜色定义为三个取值在0-1之间的浮点数,分别表示RGB分量。由于分量的值恰好为0-1之间的浮点数,颜色不仅可以理解为每种颜色的强度,还可以理解为每种颜色光线的穿透率。换句话来说,这样定义的颜色同时支持乘法和加法。

特别的,对于透明物体的折射,光线穿过物体,存在一定的吸收,该吸收率可以看做是距离的指数函数。即Color_{absorb} = e^{-dist \times Material_{absorb}},如在红光吸收率0.1的物体中穿过2单位距离,则射出的红光应该为原来射入红光强度的e^{-2*0.1}.

向量

向量的定义,也是三个0-1之间的浮点数,表示xyz坐标分量。向量同时具有表征方向和表征位置的功能,坐标本身在某种意义上也是坐标原点O加上某个表示方向的向量。

一个光线,我们就可以用两个向量rayOrayD分别表示他的起始位置以及方向,rayD是一个单位向量,某点P在光线上当且仅当P = rayO + k*rayD,且k>=0

 

物体

由于C++的多态机制,物体本身是一个抽象类,只需要支持:

  • 判断和某个光线的交点
  • 判断某个位置对应的贴图颜色
  • 提取出诸如漫反射率等表面材质的性质
  • 询问物体hash值(用于抗锯齿等)

碰撞

碰撞是渲染里面非常核心也很复杂的一个模块,我们使用一个专门的类记录碰撞的信息,实际上,对于碰撞,可以通过碰撞位置C,碰撞法线方向N和如何光线方向I完全表述。有了这三个量,我们可以简单的计算出这个碰撞的反射、折射方向(其中折射方向需要物体折射率)。这里讲一个小的注意点,就是反射,折射光线的起始点不应该都设置为C,反射需要保证光线的起始位置和入社光线位置同侧,而折射恰恰相反,由于精度误差,可能C实际在物体表面的某一侧而我们用C作为新光线的起点是不妥的。解决方案很简单,对于反射,我们使用C_{surface} = C+N*eps,折射则使用C_{backface}=C-N*eps作为光线的其实位置。

 

 

真实感图像渲染系列:工程概述

这是真实感图像渲染系列的第一篇文章。

这是图形学课程的一次大作业,基础要求是实现光线追踪算法(Ray Tracing),包括反射,折射以及漫反射,以及参数曲面求交(Bazier Curves),在此基础上,我增加了渐进式光子映射算法(Progressive Photon Mapping)以及景深效果(Depth of Focus). 不过由于本人毫无美术细胞,所以场景设计一团糟,只是粗略体现出了每一个算法的现象而已。结果如图

包含两个含贴图的球体,一个看起来像水果盘的贝塞尔曲线,一个透视的玻璃球,以及一个距离摄像头太近的,对焦失败的球。

工程源代码可以在 https://github.com/mhy12345/RayTracing-ProgressivePhotonMaping 找到。

其中,该工程依赖了glog(实际上master版本不依赖,但是需要他进行一些必要的调试信息输出),eigen(用于矩阵运算),opencv(用户实时显示渲染结果以及bmp格式保存)以及jsoncpp(用于解析json文件)。后面三个库都需要提前安装在电脑中。同时,工程需要你的编译器支持 -fopenmp 选项,例如,在我的笔记本上,编译器 clang-omp 支持该选项,编译命令即为

根目录下的 ./serial 和 ./main 分别是渲染程序的串行版本和并行版本。

python音频处理库librosa教程(2) hop_length的选取

教程中很坑的一点,是让我们自定义hop_length,然后……what is hop_length?

现在,我们需要重新思考一下特征提取的密度,对于一个22050HZ的采样数据,显然,最后提取出来的特征序列不能比22050HZ还要密集。当最后特征等于22050Hz时,提取出来的特征当然就是自身,当特征频率更小,比如2205Hz,那么我们就可以将连续的10个数据进行做平均值、方差等等操作,使得特征长度缩小至1/10。

这种分窗口压缩数据的方式确实很容易想到,通过将连续的若干个帧数据进行一些操作,合并成一个向量,即可表示这一段时间每一帧的特征。而这个帧数变少的序列就是frame。

因为音频采样数据本身就是在一个波形上的一些随机点,这些随机点进行平均值这类传统操作操作没有任何意义!怎么办呢,其实结果很简单,既然我们希望对于一个窗口提取音频信号,那么我们直接对于这个窗口进行一次傅里叶变换就好了。因此这也就是librosa所做的,对于大部分样本提取函数,你都需要传入一个hop_length,作为傅里叶变换的长度,显然,而这个值一般选择2的整数次幂(为了可以通过快速傅里叶算法优化性能)。

对于不想想太多的我们,完全可以直接使用每个函数的默认hop_length进行编写。而教程中人为设置hop_length确实是非常有误导性,让人以为这是一个必须自己设置的量。

python音频处理库librosa教程(1) 一个简单的音频特征提取程序

这是librosa教程的第一篇。主要通过解释官网的样例程序来解释如何使用librosa提取音频特征。librosa是一个音频特征提取库,在论文librosa: Audio and Music Signal Analysis in Python中提出的,如果在学术中使用的话,可以引用该论文。


本文的目标是在python中实现对于音频特征提取,提取出一个时序的特征向量。

librosa是一个专用于处理音频的库,其官方教程可以在 Librosa官方教程 中找到,这篇教程也是基于官方教程写出来的。

笔者学习一个库一般都是直接看一篇样例程序,一般来说,弄懂样例程序究竟讲了什么,也就弄懂了这个库了。作为一个目标为提取音频特征的代码库,官方索性就直接用一个简单的“音频特征提取”代码作为样例程序。这也给我们带来了很大的便利。

音频读入

首先,我们需要知道声音是如何在电脑中储存的,声音信号作为一个连续的函数,在计算机中,只能够用采样的形式储存。

具体来说,采样就是将原来的连续函数中,指定间隔求出一系列离散的时刻对应的信号大小,如librosa默认每秒钟采样22050个采样点,则原来的音频信号就变成了每秒钟22050个值得离散函数。显然,当采样点足够密集,那么我们得到的序列就能够足够逼近原函数。在波形图中,声波平均振幅可用来表示响度的大小,而每一个周期的时间(过零率)可以用来表示音高。

librosa.load 函数的具体实现细节如下

返回值的第一部分,就是将连续的音频信号按照给定的采样率(sample rate) 进行采样的结果,这个结果是一个一维的 numpy 数组,可用于后续的分析。

返回值的第二部分,就是采样率,也就是说完全是函数参数中sr的拷贝。在少数情况下,我们设置函数参数为 sr=None ,采样率就完全依照MP3默认的采样率返回,这个时候返回值 sr 就是我们评判数组 y 代表的音频究竟有多快的唯一方式了。

由简单数学推导可以得到音频时间T可以由ysr决定

    \[T = \frac{len(y)}{sr}\]

波形分割

接下来,y作为一个合成波形,可以分成两个分量,即谐波(harmonic)与冲击波(percussive)。由于笔者也不太清楚他们具体该怎么翻译,所以按照自己的理解自由发挥咯。

从粒度上来看,谐波相对为细粒度特征,而冲击波为粗粒度特征。诸如敲鼓类似的声音拥有可辨别的强弱变化,归为冲击波。而吹笛子这种人耳本身无法辨别的特征,是谐波。接下来,我们将分别对于冲击波以及谐波进行特征的提取。感性理解,冲击波表示声音节奏与情感,谐波表示声音的音色。

节拍提取

beat_frames指的就是每一拍(beat)所对应的帧(frame)位置。如果不明白beat,frame这些术语都是指的什么,那么可以参考官方文档.

这一句话真的是一个非常有误解性质的语句,对于hop_length的详细讲述可以在 python音频处理库librosa教程(2) hop_length的选取 里面看到。倘若无法理解的话,这里给出一个捷径:对于librosa里面的所有函数,都会有一个默认的 hop_length=512 ,也就是说,我们完全可以删掉这句话,对于之后的hop_length,我们都直接使用函数的默认值就好了。

节拍提取在音频特征提取中的意义在于,节拍是所有曲子共有的,不同节拍(beat)中,音频特征或多或少有些不同。在我们模型中,如果我们每隔1ms提取出了一个音频特征(如音高),我们不如将同一拍的所有音高取平均值作为这一拍的音频特征。这样表征效果会好很多。

音频特征提取

接下来,我们可以计算梅尔频率倒谱系数(MFCC),简单来说,MFCC可以用以表达曲目的音高和响度的关联。经典的MFCC的输出向量维数是13,也就是说在非必要的情况下,这个 n_mfcc 参数就不要改了(这是笔者投paper的时候用血换来的教训啊)

MFCC本身只反映了音频的静态特性,所以我们需要对他进行差分,以得到动态特性。即音频是“如何”变化的。

既然我们已经分别得到了mfcc以及mfcc_delta,那么我们可以将它们合称为一个特征,合并过程只是单纯的二维矩阵拼接,这里用了np.vstack函数。接着,librosa.util.sync函数用于以窗口形式合并多个连续的变量,比如这里,就以beat_frames为分界点,对每一个分界点中的序列用其平均值表示。到了这一步,我们得到的是,长度为音乐拍数,宽度为特征数的矩阵。

下面是对于谐波进行分析

由于这里出来的是一个类似于色度图的东西,所以笔者非常怀疑其在后续训练模型的意义是否足够。之后,用同样的方法,将色度图按照beat_frames进行简化。

附录

节拍提取算法详情:Beat Tracking by Dynamic Programming

基于Librosa的拓展:实时的音频特征分析库rcaudio