C++实现和解读Face Alignment at 3000fps via Local Binary Feature
C++实现代码的error和论文一致(最近又新添加了一个example,重写了一些代码),详见我的github链接
以下是论文解读:效果见最后
1.Framework
整个流程基于Cascade Pose Regression(CVPR 2010),分为T个stage,在训练时步骤如下(testing也类似)
- 每个stage先抽取local binary features,
- 然后根据真实的ΔˆSi 用linear regression训练一个regressor,
- 最后用训练出来的regressor得到ΔSi(是ΔˆSi的近似)去更新前一个stage的shape,得到更加精确的shape
1.1 Training Phase:
Input: Image set {I}(N samples), ground truth shapes {ˆS}, initial shapes set {S0}
For t=1:T do
featuresi=ϕt(Ii,St−1i)
Δ^Si=^Si−Si−1
E=∑‖^Si−Rt(featuresi)‖2
ΔSi=Rt(featuresi)
Sti=St−1i+ΔSi
End For
1.2 Testing phase
Input: Image I, initial shape S0
Output: refined shape S
For t=1:T do #总共有T个stage
features=ϕt(I,St−1)
ΔS=Rt(features)
St=St−1+ΔS
End For
2. 抽取Local Binary Features
- 先random产生500个pixel difference features
- 选取最具有分辨力的pixel difference features作为Random Forest中每棵树中的非叶子结点
- 输入图片得到Local Binary Features
2.1 Pixel Difference Features
Pixel Difference Features源自CVPR 2012的一篇叫face alignment by explicity shape regression的文章。
- Pixel Indexed Features
如上图,作者认为对于特定的一个landmrk,它周围的有些点是几何不变的,比如我们有一个landmark是左眼的右眼角P(x,y),在它的上方某个地方ΔP(Δx,Δy)具有不变的性质,比如附近的眉毛某点 P(x+Δx,y+Δy) 就是黑色的,这个对每个人来说都是一样,当然由于光照等其他因素的原因,这个颜色还是有很大差异的,那么作者就提出了 Pixel Difference Features - Pixel Difference Features
其实很简单,就是将附近某两个点的值相减得到一个差值,而这个值很大程度上在一个阈值内浮动,而且还可以剔除光照等因素的影响,即
feature=I(x+Δx1,y+Δy1)−I(x+Δx2,y+Δy2)
2.2 创建Random Forest
Random Forest维基链接。Random Forest由很多tree组成,相比于单棵tree能够防止模型的over fitting。Random Forest能用于regression(本文用到的功能)和classification。
那么如何建立Random Forest,主要是如何选择split node,下面以如何构建一颗regression tree为例。
- 首先我们确定一个landmark l,随机产生在l附近的500个pixel difference features的位置,然后对training中的所有images抽取这500个features,
- 确定要构建l的第几个棵树(其他树一样,只是训练数据不一样而已)
- 从树根节点开始
var = variance of landmark l of traing images,
var_red = -INFINITY, fea = -1, left_child = NULL, right_child = NULL
For each feature f:
threshold = random choose from all images’s feature f
// 比如现在所有图的f的值是2,2,2,4,5,3(假设有5张图),那么随机选择threshold可能是3
tmp_left_child = images with f < threshold
// 左子节点为所有f小于threshold的图片
tmp_right_child = images with f >= threshold
tmp_var_red = var - |left_child|/|root|*var_tmp_left_child - |right_child|/|root|*var_tmp_right_child
// var_tmp_left_child是左子节点landmark l的variance
if ( tmp_var_red > var_red) {
mvar_red = tmp_var_red
fea = f
left_child = tmp_left_child
right_child = tmp_right_child
}
End For
实际上var是固定的,所以不用算,|left_child|是当前left_child所包含的图片数,|root|表示root包含的图片数,实际计算的时候可以省去,因为是定的。
fea就是最后选择的feature - 对子节点left_child和right_child做跟3一样的操作,直到达到tree的最大depth,或者对于某一个根节点根据maximum variance reduction找到的feature是恰好一个child包含了所有的图,而另一个child没有图(事实上这个情况基本上不太可能出现),所以训练的时候基本上能够达到定义的max_depth,经我个人验证max_depth=5,6就可以了,在深很容易出现overfitting的问题
- landmark l的其他树也一样如上,对其他landmark和对l的操作一样
2.3 Local Binary Features
那么对于每一张图的每一个landmark的每一棵树最后都会输出一个值,如上图下方,第一棵树遍历后来到了最左边的子节点所以记为[1, 0, 0, 0],对于每一棵树访问到的叶子节点记为1,其他的记为0,然后一个landmark拥有一个forest即有多棵树,那么把所有的结果连起来就是ϕtl=[1,0,0,0,0,1,0,0,0,0,1,0,…],真正的Local Binary Features是将所有的landmark的这些feature都连起来。
ϕt=[ϕt1,ϕt2,…,ϕtL]
所以我们可以看出这是一个很稀疏的向量,中间为1的个数是所有landmark中tree的总个数,其余的为0。然后就是要训练一个global linear regression了
3. Learning Global Linear Regression
minWt∑Ni=1‖Δ^Sti−Wtϕt(Ii,St−1i)‖22+λ‖Wt‖22
对每一个landmark训练时用的都是同一个local binary features.
- 比如对于第一个landmark的Δx坐标进行regression,输入就是所有图片的local binary feature矩阵,所有Δx坐标组成的vector作为regression target,最后可以得到一个权重向量w,然后有了新的图片,抽取它的local binary feature后,乘以w就可以得到预测的Δx值,最后加到上一个stage的x上面得到新的x
- 对于第一个landmark的Δy也是一样,这里的local binary feature矩阵和上面Δx矩阵的一样,只是要regression的target Δy不一样,
- 其他的landmark同上
- 上面的公式,是将所有的landmark的w都合在一起了,求一个整体的W