Face Detection PCN 论文理解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wwwhp/article/details/84076952

paper:: Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks(基于渐进校准网络的实时旋转不变人脸检测)
linkPCN paper
codehttps://github.com/Jack-CV/PCN-FaceDetection

摘要

  • 旋转不变人脸检测当前面临的问题:
    • 检测在平面具有任意旋转角度的人脸在飞限制场景中应用广泛;
    • 为了处理RIP(平面旋转)差异,当前许多检测方法都在速度和精度上受到限制;
  • 论文提出Progressive Calibration Network对旋转不变人脸进行由粗到细的检测:
    • PCN是三阶段级联网络,每阶段不仅区分是否存在人脸,同时逐渐对候选人脸的旋转角度进行垂直方向上的校准;
    • PCN可以实时检测具有 [ 0 o , 36 0 o ] [0^o,360^o] 之间任意旋转角度的人脸;

引言

  • 本文的研究重点是face detection in full RIP,提出三种策略来处理人脸旋转所带来的差异:
  • 数据增强 data augmentation:将垂直人脸均匀地在full RIP角度进行旋转,训练一个包含多种人脸角度的人脸检测器需要一个大型的网络同时耗时也比较高;
  • 分治 divide and conquer:将人脸的角度分为up、down、left、right四个主要的朝向,然后训练多个人脸检测器,多个检测器使得时间成本增加同时可能引入其他问题(不太理解论文里面提到的false alarms);
  • 旋转路由器 rotation router:使用router network来评估候选人脸的旋转角度,然后在垂直方向上对候选人脸进行校准,然后就可以应用垂直人脸检测器对候选人脸区域进行校准了,但是精准地评估人脸的角度需要大型网络来作为router network耗时成本也增加了;
  • 为了解决上述三种策略所面临的问题,PCN采用逐步校准旋转角度的方式来处理候选人脸:
    • stage1:提取出候选人脸区域并进行角度校准,将RIP角度的范围缩小 [ 18 0 o , 18 0 o ] [-180^o,180^o] to [ 9 0 o , 9 0 o ] [-90^o,90^o]
    • stage2:第一阶段校准后的人脸将进行进一步的人脸鉴别以及角度校准 [ 9 0 o , 9 0 o ] [-90^o,90^o] to [ 4 5 o , 4 5 o ] [-45^o,45^o] ,再次将RIP角度缩小一半;
    • stage3:对第二阶段的结果进行进一步精准预测,判断人脸候选区是否是正例,同时预测精准的RIP角度;
  • PCN的创新点和优势:
    • 将角度校准的任务分为几个渐进的阶段,每个阶段只是一个简单的任务,在精确校准的同时耗时也低,RIP角度的逐渐减小有助于人脸/非人脸的鉴别;
    • 前两个阶段只进行粗略的校准,通过联合学习校准任务、分类任务、bbox回归任务,可以在不增加时间成本的情况下完成对粗略校准的具有鲁棒性准确的预测;校准方法可以利用图像的翻转来实现;
    • 在multi-oriented FDDB和a subset of WIDER FACE dataset上以非常快的速度到达了优秀的性能;

Progressive Calibration Networks

PCN检测流程:利用滑动窗口和图像金字塔原理得到候选人脸,然后经过多阶段检测器处理;在PCN的每个阶段,低置信度的候选区域会被舍弃,剩余的候选区域会进行bbox的回归以及RIP角度的校准;每个阶段完成后进行NMS操作合并overlap高的候选区域;

  • PCN-1:
    • 针对每一个输入窗口,PCN-1有三个学习目标: [ f , t , g ] = F 1 ( x ) [f,t,g]=F_1(x)
      • 用softmax loss鉴别人脸/非人脸: L c l s = y l o g f + ( 1 y ) l o g ( 1 f ) L_{cls}=ylogf+(1-y)log(1-f) ;
      • 用smooth L1 loss进行bbox回归: L ( t , t ) = S ( t t ) L(t,t^*)=S(t-t^*) ,其中 t t 表示预测值, t t^* 表示gt值,回归目标由三部分组成,其中a,b,w分别表示预测bbox的top-left坐标和宽度:
        (1) t w = w / w t_w=w^*/w
        (2) t a = ( a + 0.5 w a 0.5 w ) / w t_a=(a^*+0.5w^*-a-0.5w)/w^*
        (3) t b = ( b + 0.5 w b 0.5 w ) / w t_b=(b^*+0.5w^*-b-0.5w)/w^*
      • 使用二值分类器对候选区域的粗略朝向进行预测: L c a l = y l o g ( g ) + ( 1 y ) l o g ( 1 g ) L_{cal}=ylog(g)+(1-y)log(1-g) ,当候选区域是facing up时 y = 1 y=1
    • PCN-1的学习目标损失函数是 m i n L = L c l s + λ r e g L r e g + λ c a l L c a l min L = L_{cls}+\lambda_{reg} \cdot L_{reg}+\lambda_{cal} \cdot L_{cal} ,其中 λ r e g λ c a l \lambda_{reg}和\lambda_{cal} 是平衡loss的参数;
    • 经过PCN-1阶段的处理之后,候选人脸框根据PCN-1的回归值更新bbox,然后通过PCN-1预测的粗略的RIP角度进行旋转,假设预测角度为 θ 1 \theta_1 ,当 ( g > = 0.5 , θ 1 = 0 o ) , ( g < 0.5 , θ 1 = 18 0 o ) (g>=0.5, \theta_1=0^o),(g<0.5,\theta_1=180^o) ,当 θ 1 = 0 o \theta_1=0^o 时代表候选区人脸的正的没有旋转的必要,相反 θ 1 = 18 0 o \theta_1=180^o 时候选区人脸要旋转 18 0 o 180^o 将人脸摆正,将RIP角度从 [ 18 0 o , 18 0 o ] [-180^o,180^o] 减少到 [ 9 0 o , 9 0 o ] [-90^o,90^o] ;
    • 为了训练旋转不变人脸检测器,将正脸训练数据集中的图像旋转任意的角度,组成一个新的包括 36 0 o 360^o RIP角度人脸图像的训练集,在训练期间用到positive、negative、suspected三种samples:
      • positive samples:与人脸区域IoU大于0.7的窗口;
      • negative samples:IoU小于0.3的窗口;
      • suspected samples:IoU介于 [ 0.4 , 0.7 ] [0.4,0.7] 之间的窗口;
    • positive和negative samples 作用于人脸\非人脸的分类训练;positive和suspected samples作用于bbox回归和校正的训练,其中RIP在 [ 6 5 o , 6 5 o ] [-65^o,65^o] 范围内samples被定义为facing up即正脸,在 [ 18 0 o , 11 5 o ] a n d [ 11 5 o , 18 0 o ] [-180^o,-115^o] and [115^o,180^o] 范围内被定义为facing down,RIP角度在其他范围的样本不作用于校准训练;
  • PCN-2:
    • 这个阶段的主要流程和stage1类似,PCN-2进一步对人脸/非人脸鉴别,同时对bbox进行回归并校准候选区域人脸;但是在PCN-2的粗略朝向定位是一个根据RIP角度范围设定的三元分类 [ 9 0 o , 4 5 o ] , [ 4 5 o , 4 5 o ] , [ 4 5 o , 9 0 o ] [-90^o,-45^o],[-45^o,45^o],[45^o,90^o] ,候选区域旋转角度通过PCN-2对应的RIP角度的预测值进行相应的校准:
      • i d = a r g m a x ( g i ) id=argmax(g_i)
      • θ 2 = 9 0 o 0 o 9 0 o ( i d = 0 1 2 ) \theta_2=-90^o,0^o,90^o (id=0,1,2)
    • 其中 g 1 , g 2 , g 3 g_1,g_2,g_3 是对应预测得到的三元朝向分类分数,根据不同的id值,候选区域要分别旋转 9 0 o , 0 o , 9 0 o -90^o,0^o,90^o ,经过这个阶段RIP角度减小到 [ 4 5 o , 4 5 o ] [-45^o,45^o] ;在这个阶段的训练期间,将训练集的图像在 [ 9 0 o , 9 0 o ] [-90^o,90^o] 范围内均匀地旋转,并过滤掉第一阶段训练中的困难负样本;在PCN-2中positive和suspected样本的RIP角度范围在 [ 9 0 o , 6 0 o ] , [ 3 0 o , 3 0 o ] , [ 6 0 o , 9 0 o ] [-90^o,-60^o],[-30^o,30^o],[60^o,90^o] 分别对应 g i g_i 中label的 0 , 1 , 2 0,1,2 ;
  • PCN-3:
  • PCN-3阶段直接准确回归人脸候选区域的RIP角度,最后每个人脸候选区域的的 θ R I P \theta_{RIP} 值是由所有阶段 θ \theta 预测值累加而成: θ R I P = θ 1 + θ 2 + θ 3 \theta_{RIP}=\theta_1+\theta_2+\theta_3 ,上图内容为RIP角度的计算过程,RIP角度的回归是一个由粗到细的级联过程;在stage3的训练过程中,将训练集图像旋转到 [ 4 5 o , 4 5 o ] [-45^o,45^o] ,过滤掉PCN-2训练过程中的困难负样本,校准回归branch使用smooth L1 loss进行训练;
  • 关于上图内容的解析: 对第一行的PCN流程分析,首先原图的RIP角度介于 [ 18 0 o , 9 0 o ] [-180^o,-90^o] 之间,经过PCN-1的校准 θ 1 = 18 0 o \theta_1=180^o 之后RIP角度介于 [ 4 5 o , 9 0 o ] [45^o,90^o] ,然后经过PCN-2校准 θ 2 = 9 0 o \theta_2=90^o 之后RIP角度再次旋转 9 0 o 90^o ,最后经过PCN-3的连续值准确校准 θ 3 = 3 0 o \theta_3=-30^o 之后得到人脸的RIP角度 θ R I P = 12 0 o \theta_{RIP}=-120^o
  • 快速精确的校准:基于级联方式的渐进式校准
    • 早期阶段PCN只预测粗略的RIP方向,对大多数多样的样本都具有较强的鲁棒性,有利于后面连续阶段的预测;
    • 基于粗略RIP预测的校准可以通过翻转原始图像实现不会增加额外的时间消耗,通过将原图旋转 9 0 o , 9 0 o , 18 0 o -90^o,90^o,180^o 可以从原图中截出四种角度的windows 0 o , 9 0 o , 9 0 o , 18 0 o 0^o,-90^o,90^o,180^o ,如下图所示;搭配着快速精确的校准,候选人脸会被逐步校准到正直方向从而更容易检测;

实现细节

  • PCN网络结构图:
  • PCN有由小到大的三个部分组成,使用WIDER FACE的训练集进行训练,将人脸的标注区域调整为正方形,训练时候每个mini-batch中positive、negative、suspected samples的比例为2:2:1;
  • PCN的速度和准确度表现:

总结

  • 本文提出了一个旋转不变人脸检测器PCN,将检测阶段分为几个渐进的阶段,逐步校准候选人脸的RIP方向,使用翻转原始图像的方式实现人脸的校准,能达到实时的旋转不变人脸检测;
  • 通过对人脸与非人脸进行二值分类并逐渐减小RIP范围,PCN可以精确检测任意RIP角度的人脸;

源码学习

github PCN源码简单分析和理解

  • 主要数据结构:
/*定义了一个PCN类,用于构造人脸检测器*/
class PCN
{
public:
    PCN(std::string model, std::string net1, std::string net2, std::string net3); // 创建一个PCN的对象加载PCN caffemodel
    void SetMinFaceSize(int minFace); //设置检测人脸的最小尺寸
    void SetScoreThresh(float thresh1, float thresh2, float thresh3); //设置PCN三个阶段人脸置信度的阈值
    void SetImagePyramidScaleFactor(float factor); //设置图像金字塔的比例因子
    void SetVideoSmooth(bool smooth); //设置True使得检测视频流的时候人脸box更加平滑稳定
    std::vector<Window> DetectFace(cv::Mat img); //PCN对图像进行检测

private:
    void* impl_; // void指针构造PCN对象的时候用Impl类指针初始化
};

/*定义类implementation,用于构造检测器中实际检测的部分*/
class Impl
{
public:
    void LoadModel(std::string model, std::string net1, std::string net2, std::string net3); //加载caffemodel,构造caffeNet对象
    cv::Mat ResizeImg(cv::Mat img, float scale);
    static bool CompareWin(const Window2 &w1, const Window2 &w2);
    bool Legal(int x, int y, cv::Mat img); //判断候选区域的坐标是否合理
    bool Inside(int x, int y, Window2 rect);
    int SmoothAngle(int a, int b);
    float IoU(Window2 &w1, Window2 &w2);
    std::vector<Window2> NMS(std::vector<Window2> &winList, bool local, float threshold);
    std::vector<Window2> DeleteFP(std::vector<Window2> &winList);
    cv::Mat PreProcessImg(cv::Mat img);
    cv::Mat PreProcessImg(cv::Mat img,  int dim);
    void SetInput(cv::Mat input, caffe::shared_ptr<caffe::Net<float> > &net);
    void SetInput(std::vector<cv::Mat> &input, caffe::shared_ptr<caffe::Net<float> > &net);
    cv::Mat PadImg(cv::Mat img); //在原始图像四周加上padding为了检测在边缘的人脸
    std::vector<Window> TransWindow(cv::Mat img, cv::Mat imgPad, std::vector<Window2> &winList);
    std::vector<Window2> Stage1(cv::Mat img, cv::Mat imgPad, caffe::shared_ptr<caffe::Net<float> > &net, float thres);
    std::vector<Window2> Stage2(cv::Mat img, cv::Mat img180,
                                caffe::shared_ptr<caffe::Net<float> > &net, float thres, int dim, std::vector<Window2> &winList);
    std::vector<Window2> Stage3(cv::Mat img, cv::Mat img180, cv::Mat img90, cv::Mat imgNeg90,
                                caffe::shared_ptr<caffe::Net<float> > &net, float thres, int dim, std::vector<Window2> &winList);


    caffe::shared_ptr<caffe::Net<float> > net_[3];
    int minFace_;
    float scale_;
    int stride_;
    float classThreshold_[3];
    float nmsThreshold_[3];
    float angleRange_;
    bool stable_;
};
  • 主要检测流程
std::vector<Window> PCN::DetectFace(cv::Mat img)
{
    Impl *p = (Impl *)impl_;
    cv::Mat imgPad = p->PadImg(img); //对原始进行padding,为了检测图像边缘的box
    cv::Mat img180, img90, imgNeg90;
    cv::flip(imgPad, img180, 0); //图像垂直翻转,得到image_down
    cv::transpose(imgPad, img90); //对图像进行转置,得到image_left
    cv::flip(img90, imgNeg90, 0); //对image_left进行垂直翻转->image_right?

    std::vector<Window2> winList = p->Stage1(img, imgPad, p->net_[0], p->classThreshold_[0]); //PCN-1:通过输入图像金字塔得到人脸候选框的集合
    winList = p->NMS(winList, true, p->nmsThreshold_[0]);

    winList = p->Stage2(imgPad, img180, p->net_[1], p->classThreshold_[1], 24, winList); //把候选区域从原图中crop出来,经过PCN-2滤掉prob小于阈值的候选人脸区域,进一步对bbox进行回归,再次对angle校准
    winList = p->NMS(winList, true, p->nmsThreshold_[1]);

    winList = p->Stage3(imgPad, img180, img90, imgNeg90, p->net_[2], p->classThreshold_[2], 48, winList); //从原图像根据PCN-2的输出结果crop出候选人脸区域,对候选人脸bbox进行精确地回归,对angle校准度也进行精确回归
    winList = p->NMS(winList, false, p->nmsThreshold_[2]);
    winList = p->DeleteFP(winList); // 删除FP 具体实现没太理解,待改进补充

    static std::vector<Window2> preList;
    if (p->stable_) //针对视频检测的平滑操作
    {
        for (int i = 0; i < winList.size(); i++)
        {
            for (int j = 0; j < preList.size(); j++)
            {
                if (p->IoU(winList[i], preList[j]) > 0.9)
                    winList[i] = preList[j];
                else if (p->IoU(winList[i], preList[j]) > 0.6)
                {
                    winList[i].x = (winList[i].x + preList[j].x) / 2;
                    winList[i].y = (winList[i].y + preList[j].y) / 2;
                    winList[i].w = (winList[i].w + preList[j].w) / 2;
                    winList[i].h = (winList[i].h + preList[j].h) / 2;
                    winList[i].angle = p->SmoothAngle(winList[i].angle, preList[j].angle);
                }
            }
        }
        preList = winList;
    }
    return p->TransWindow(img, imgPad, winList); //由于之前加了padding,x、y的坐标都需要转换一下
}

猜你喜欢

转载自blog.csdn.net/wwwhp/article/details/84076952