人脸检测(detection)在opencv中早就有直接能拿来用的haar分类器,基于Viola-Jones算法。但是毕竟是老掉牙的技术,Precision/Recall曲线渣到不行,在实际工程中根本没法给boss看,作为MSRA脑残粉,这里介绍一种MSRA在14年的最新技术:下点击打开链接载。这篇文章直接在30ms的时间里把detection和alignment都给做了,PR曲线彪到很高,时效性高,内存占用却非常低,在一些库上虐了Face++和Google
Picasa,正好契合这篇想讲的东西。可以作为本节的主线。
人脸校准(alignment)是给你一张脸,你给我找出我需要的特征点的位置,比如鼻子左侧,鼻孔下侧,瞳孔位置,上嘴唇下侧等等点的位置。如果觉得还是不明白,看下图:
图中黄色框框就是在做detection,白色点点就是在做alignment。如果知道了点的位置做一下位置驱动的变形,脸就成正的了,如何驱动变形不是本节的重点,在此省略。首先介绍一下下面正文要写的东西,文中根据“boosted
cascade structure+ simple features”的原则由于干货非常多所以可能会看着看着就乱了,所以给出框架图:
作者用了一个叫post classifier的分类器,具体操作方法如下:
1.首先作者调用opencv的Viola-Jones分类器,将recal阀值设到99%,这样能够尽可能地检测出所有的脸,但是同时也会有非常多的不是脸的东东被检测出来。于是,检测出来的框框们被分成了两类:是脸和不是脸。这些图片被resize到96*96。
2.特征提取:接下来是特征提取,怎么提取呢?作者采用了三种方法,有和没有校准的:
1. we divide the window into 6*6 non-overlapping cells and extract a SIFT descriptor in each cell.
2. we use a fixed mean face shape with 27 facial points and extract a SIFT descriptor centered on each point.
3. we align the 27 facial points using the alignment algorithm in [21] and extract a SIFT descriptor centered on each point.
第一种:把window划分成6*6个小windows,分别提取SIFT特征,然后连接着36个sift特征向量成为图像的特征。
第二种:先求出一个固定的脸的平均shape(27个特征点的位置,比如眼睛左边,嘴唇右边等等),然后以这27个特征点为中心提取sift特征,然后连接后作为特征。
第三种:用他们组去年的另一个成果Face Alignment at 3000 FPS via Regressing Local Binary Features (CVPR14) ,也就是图中的3000FPS方法,回归出每张脸的shape,然后再以每张脸自己的27个shape
points为中心做sift,然后连接得到特征。3.分类:将上述的三种特征分别扔到线性SVM中做分类,训练出一个能分辨一张图是不是脸的SVM模型。
但是问题来了:如果把所有的windows都做一下alignment,即使是3000 faces per second的速度一张图可能也要处理上1秒,这无法满足一般一秒30帧的实时需求。作者也说,用opencv分类器,参数设成99%的recall率将会带来很严重的效率灾难——一张图能找出来3000个框,处理一张图都要好几秒。
这么渣的效率可咋办呢?以上内容已经证明了alignment确实对detection的preciseness有帮助,这就够啦,对下面的工作也是个启发——能不能在做detection的同时把alignment做了呢?alignment的中间结果是否能给detection带来一些帮助呢?后面慢慢讲。先说两个通用的面部检测和矫正的模型,翻译自论文:
如何实现回归校准(Cascade Alignment)呐?
这里介绍的是一个人在10年发的文章:Cascaded Pose Regression? (CVPR10),给图像一个初始shape(通常采用平均shape),然后通过一次一次的回归把shape回归到正确的地方。算法结构很简单,但是效果确实非常好:
回归过程如下:首先提取特征,原作者采用的是Pose-Indexed point features,然后根据特征训练回归函数(可以用线性回归,CART,随机森林等等),原作者采用了一个叫Random Fern Regressor的东西,回归出这一阶段的偏移量,然后shape加上这个偏移量,反复这一过程,直到迭代上限或者shape错误率不再下降。
随机蕨的算法过程和随机森林类似,他是一个半朴素贝叶斯模型。更为详细的介绍,可以参考这个哥们的博客点击打开链接.首先选取M组每组K个特征建立M个蕨(弱分类器),然后假设蕨内特征是相关的,蕨间特征是独立的,这样从统计学上随机蕨是一个完整的把朴素贝叶斯分类器,让计算变得简单:
式中C代表分类,ci代表第I类,M代表蕨数量。
综上偏置就是上述回归方法之一搞定的。比如随机森林或者随机蕨,或者线性回归。
现在再说说怎么训练得到这个回归Rt。
有两种思路:一种是像刚才随机蕨那样,每个每个蕨的叶子节点存储一个偏移量,计算训练的时候落入这个叶子节点的样本偏移之平均,然后作为最终的叶子节点偏移量。其实就是在优化一个如下目标函数:
然而MSRA组在3000fps中采用的是另一种方法,形状的偏移量ΔδS为:
目标函数是:
其实也是同样的思路,Φ代表特征提取函数,论文中称Φ的输出为局部二值特征(LBF),W为线性回归参数矩阵,其实就是把提取出来的特征映射到一个二维的偏移量上,是一个2*lenth(特征空间维数)的变换矩阵。
首先讲Φ是怎么训练的:Φ其实就是一个随机森林。输入像素差特征(pixel-difference features),输出一个offest。训练的时候随机给每个根节点像素差特征中的一部分。非叶节点的分裂依据是从输入的pixel-difference features中找出能够做到最大的方差衰减的feature。在最后的叶子节点上写上落在叶子节点上的样本偏移量,如果有多个样本都落在这里,则求平均。这样训练出来的东西就是下面这个公式所表达的东西:
但是我只想要其中的Φ,于是这里给出了LBF(local binary feature)的定义,直接简单粗暴地统计所有树叶节点是否被该样本落入,如果落入了就记为1否则记为0,然后把所有的01串连起来就是LBF了。还是看图说话:
先看b,随机森林的三棵树,样本经过三棵树后分别落在了第1,2,3个叶子节点上,于是三棵树的LBF就是1000,0100,0010.连接起来就是100001000010.然后看a,把27个特征点的lbf都连接起来形成总的LBF就是Φ了。
接下来是训练w:之前已经得到了wΦ(I,S)以及Φ(I,S),现在想求w,这还不容易吗,直接算呀。不过作者又调皮了,他说他不想求w,而是想求一个总的大W=[w1,w2,w3,…,w27].怎么求呢?得做二次回归。至于为什么要这么做下面会介绍。目标函数:
后面加了个L2项,因为W是炒鸡sparse的,防止过拟合。做线性回归即可得到W。
现在解释一下为啥不直接用w1w2w3…而是要再回归出来一个W:原因有两个:
1. 再次回归W可以去除原先小wi叶子节点上的噪声,因为随机森林里的决策树都是弱分类器噪声多多滴;
2.大W是全局回归(之前的一个一个小w也就是一个一个特征点单独的回归是local回归),全局回归可以有效地实施一个全局形状约束以减少局部误差以及模糊不清的局部表现。
这样一来,测试的时候每输入一张图片I,先用随机森林Φ求出它的LBF,然后在用W乘一下就得到了下一个stage的shape,然后迭代几次就得到了最终的shape。所以效率十分的快。
刚才讲的是两个uniform的model来做detection和shape regression的。接下来该讲作者是怎么边detection边regression shape的了!
作者建立了一个分类回归树,就叫CRT好了。这个CRT在距离根节点比较近的几层偏重于分类,在接近叶子节点的几层偏重于回归,具体实现上,每个节点究竟用于回归还是分类呢?用一个概率p表示用于分类的概率,自然回归就是1-p了。而这个p随着深数的深度减小,作者采用了一个经验公式:
知道了CRT怎么建立,那就直接就看算法细节吧!边测试是不是脸边做特征点回归的算法如下:
这个模型的训练方法如下: