人脸对齐 3000fps

1 介绍

          3000fps 相关概念主要是在实现jda方法的时候仔细了解过,基本原理总体说来很复杂,但是从实现的角度来看并不难。该方法的速度还是不错的,能够达到论文中提到的数据,效果嘛就有点差强人意了,主要还是由样本决定。

2 方法

       总体上,人脸对齐的主要理论依据是如下方程:

                                    F * W = R

其中F是特征,W是转换系数,R是偏移量。

2.1 基础知识

       本节主要介绍两个知识:相似变换(similiarity transform)和均值人脸(meanshape)。

2.1.1 相似变换

        相似变换的目的是求解两个形状之间的尺度和角度关系,和普氏分析的作用相同,不同点是不需要迭代求解,普氏分析理论上更准确,实际上在这里没什么特殊效果。下面的代码(取自:https://github.com/yulequan/face-alignment-in-3000fps/blob/master/Utils.cpp)就是相似变换的过程:

void SimilarityTransform(const Mat_<double>& shape1, const Mat_<double>& shape2, 
                         Mat_<double>& rotation,double& scale){
    rotation = Mat::zeros(2,2,CV_64FC1);
    scale = 0;
    
    // center the data
    double center_x_1 = 0;
    double center_y_1 = 0;
    double center_x_2 = 0;
    double center_y_2 = 0;
    for(int i = 0;i < shape1.rows;i++){
        center_x_1 += shape1(i,0);
        center_y_1 += shape1(i,1);
        center_x_2 += shape2(i,0);
        center_y_2 += shape2(i,1); 
    }
    center_x_1 /= shape1.rows;
    center_y_1 /= shape1.rows;
    center_x_2 /= shape2.rows;
    center_y_2 /= shape2.rows;
    
    Mat_<double> temp1 = shape1.clone();
    Mat_<double> temp2 = shape2.clone();
    for(int i = 0;i < shape1.rows;i++){
        temp1(i,0) -= center_x_1;
        temp1(i,1) -= center_y_1;
        temp2(i,0) -= center_x_2;
        temp2(i,1) -= center_y_2;
    }

     
    Mat_<double> covariance1, covariance2;
    Mat_<double> mean1,mean2;
    // calculate covariance matrix
    calcCovarMatrix(temp1,covariance1,mean1,CV_COVAR_COLS);
    calcCovarMatrix(temp2,covariance2,mean2,CV_COVAR_COLS);

    double s1 = sqrt(norm(covariance1));
    double s2 = sqrt(norm(covariance2));
    scale = s1 / s2; 
    temp1 = 1.0 / s1 * temp1;
    temp2 = 1.0 / s2 * temp2;

    double num = 0;
    double den = 0;
    for(int i = 0;i < shape1.rows;i++){
        num = num + temp1(i,1) * temp2(i,0) - temp1(i,0) * temp2(i,1);
        den = den + temp1(i,0) * temp2(i,0) + temp1(i,1) * temp2(i,1);      
    }
    
    double norm = sqrt(num*num + den*den);    
    double sin_theta = num / norm;
    double cos_theta = den / norm;
    rotation(0,0) = cos_theta;
    rotation(0,1) = -sin_theta;
    rotation(1,0) = sin_theta;
    rotation(1,1) = cos_theta;
}


文字描述是这样的:

       1)中心化两个形状;

       2)以质心为原点,计算其到没一个形状点之间的距离,并求和

       3)两个形状的尺度系数为2)中计算得到值的比值

       4)以质心为原点,计算其到每一个形状点之间的向量,并求解两个形状对应向量之间的角度

       5)两个形状之间的角度即4)得到角度的平均值

2.1.2 均值人脸

          对齐的过程是迭代进行基于估计人脸提取特征,然后预测偏移量,最后对估计人脸变形得到新的估计人脸,估计人脸的初值即均值人脸,均值人脸需要根据样本统计生成。有两种生成方法:

         1 平均形状:输入为大量人脸形状数据,人脸标准大小

         1)对每一一张人脸形状,计算外接矩形,以最长边和人脸标准大小之间的比例缩放人脸形状

         2)对1)中生成的人脸求平均值,即得到均值人脸

         2 迭代求解:输入为大量的人脸形状,人脸标准大:

         1)对每一张人脸形状,计算外接矩形,以最长边和人脸标准大小之间的比例缩放人脸形状

         2)对1)中生成的人脸求平均值,得到均值人脸

         3)将输入人脸通过相似变换使得其和均值人脸角度和尺度统一

         4)将3)中的到的形状求平均得到新的均值人脸

         5)重复3),4)使得前后的均值人脸误差小于指定值(L2范数),即收敛状态

2.2  特征提取

       3000fps 中用于回归的特征设计的非常巧妙,使得对齐的速度非常的快。特征提取是基于样本偏移量聚类方式计算得来的。聚类方式主要是通过决策树(cart),决策树分叉方法是基于shape index feature(形状索引特征)分类。

2.2.1 特征模板

         特征模板包含的元素:

         1)形状点ID1,

         2)基于ID1的偏移量dx1,dy1,

         3)形状点ID2,

         4)基于ID2的便宜量dx2,dy2。

        对于每一棵决策数的没一个结点都要随机生成指定数量的特征模板,然后从中根据指定原则选出最佳的那一个。偏移量的范围是以形状点为中心,指定半径内点与中心的插值。论文中叙述每一层的半径相对于前一层的半径要减小。
 

2.2.2 形状索引特征

       在对齐的过程中特征是基于当前形状提取的,为了能够提取特征统一,所以有了形状索引特征,主要是保证,假如特征模板指的是在人眼上方某处的点,对于不同角度不同尺度的人脸形状应该也是从同样的位置上获取像素。形状索引特征的提取方式如下:

       1)计算当前形状与均值形状之间的角度和尺度

       2)根据1)中的值使当前形状和均值形状角度和尺度统一

       3)2)中得到的形状加上对应偏移量

       4) 3)中得到的形状根据1)中的角度和尺度进行恢复

       5)根据4)中得到的形状提取制定像素值

       根据几何关系可以将偏移量根据1)中得到的角度和尺度进行变化,然后加到当前形状上,这样做更省时。

       shape index feature,就是基于人脸形状的几何关系求解两个像素点之差。

2.2.2 决策树

       决策数是cart树,是一个完全二叉树,深度至少为4,越高的树需要的样本量越大。

       决策树的节点保存有回归器,回归器是一种在形状索引特征的基础上保证样本指定形状点的偏移量(真实形状和估计形状之间的差值,形状要归一化到均值形状计算)方差最小,用于将偏移量相近的样本分为同一类。下面就是寻找最佳特征的代码,不过总觉得哪里写的有点问题,但是在我的框架内比开源的方法效果好:

static void binary_classify_loss(float *posFeats, float *offsets, int size, float &bestThresh, double &minLoss){
    float maxFeatValue, minFeatValue;
    float featStep, rfeatStep;

    double v2[LENGTH];
    double v[LENGTH];

    int count[LENGTH];

    maxFeatValue = -FLT_MAX, minFeatValue = FLT_MAX;

    for(int i = 0; i < size; i++){
        maxFeatValue = HU_MAX(maxFeatValue, posFeats[i]);
        minFeatValue = HU_MIN(minFeatValue, posFeats[i]);
    }

    featStep = (maxFeatValue - minFeatValue) / (LENGTH - 1);
    assert(featStep > 0);

    memset(v2, 0, sizeof(double) * LENGTH);
    memset(v, 0, sizeof(double) * LENGTH);
    memset(count, 0, sizeof(int) * LENGTH);

    rfeatStep = 1.0f / featStep;

    for(int i = 0; i < size; i++){
        int idx = (posFeats[i] -  minFeatValue) * rfeatStep;

        float off = offsets[i];

        v2[idx] += off * off;
        v[idx] += off;

        count[idx] ++ ;
    }

    for(int i = 1; i < LENGTH; i++){
        v2[i] += v2[i - 1];
        v[i] += v[i - 1];

        count[i] += count[i - 1];
    }

    double cumv2 = v2[LENGTH - 1];
    double cumv = v[LENGTH - 1];
    double cumc = size;

    for(int i = 0; i < LENGTH; i++){
        double lv2 = v2[i];
        double lv = v[i];
        int lc = count[i];

        double rv2 = cumv2 - lv2;
        double rv = cumv - lv;

        int rc = cumc - lc;

        double t, lvar, rvar, var;

        t = lv / lc;
        lvar = lv2 / lc - t * t;

        t = rv / rc;
        rvar = rv2 / rc - t * t;

        var = HU_MAX(lvar, rvar);

        if(var < minLoss){
            minLoss = var;
            bestThresh = i * featStep + minFeatValue;
        }
    }
}

       决策树的叶节点保存有偏移量,样本在决策树森林中通过的叶节点的偏移量的和为样本形状的偏移量。在多级森林的情况下,形状的变化是一级森林结束后,而不是每一棵决策树。

2.2.3 全局二值特征

        对于一个样本,如果经过决策树,落在决策树地一个叶节点,那么形成特征10000000 (八个叶节点,需保证每棵决策树叶节点一致),如果落在第二个叶节点那么形成特征01000000。将一级中的决策树的特征按次序组合,就形成用于回归的全局二值特征。

        在训练的过程中,我们得到N个特征Fi,组合形成F,设样本的形状点数量为68,那么对应有136个偏移量,N个Rij,形成Rj,通过解2中的方程形成136个W。假如有136棵决策数,那么Fi的维度是136x8=1088, Wi的维度是1088x1,那么W的维度是1088x136。

        从这些数据可以看出,W可以分散到各个叶节点,那么就形成每个叶节点包含136个偏移量,这样在预测的时候就不用带入2章中的方程了,直接累加即可。这就是3000fps快的原因之一。

     

3 效果

         测试机的配置:Intel Xeon E3-1231,测试时使用单核。

       训练相关参数:人脸窗口大小96x96,单级决策数数量544,共5级,采用特殊压缩算法模型大小3.5M

       测试性能:一张人脸的对齐平均速度为1ms。估计在作者论文上提到的机器3000fps无压力。

       测试效果见下图:

4 总结

      本文是基于一段时间在论文复现工作的基础上,对一些方法进行记录,目前有很多关于3000fps无法复现的传言,从目前我的结果来看,应该是没有问题的。由于对3000fps模型压缩的工作做的比较出色,我认为用在手机上并没有太大的压力。

      后来,转战深度学习,效果和速度均比3000fps要好很多,这是android app:

, 支持三张人脸追踪,log中可以查看运行速度。

      邮箱:[email protected]

      QQ: 136816548

参考

[1]  Face Alignment at 3000 FPS via Regressing Local Binary Features

[2] X. Cao, Y. Wei, F. Wen, and J. Sun. Face alignment by explicit shape regression. In Computer Vision and Pattern Recognition (CVPR), 2012 IEEE Conference on. IEEE,2012.

[3] http://blog.csdn.net/huneng1991/article/details/51606140

猜你喜欢

转载自blog.csdn.net/huneng1991/article/details/71305340
今日推荐