Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型

1. 前言

最近,学了人脸关键点检测算法,发现一个比较好的人脸关键点检测模型,打算学一学,让我们来看看算法是如何实现的吧!

论文地址:https://arxiv.org/pdf/1902.10859.pdf

2. PFLD介绍

PFLD的全称是A Practical Facial Landmark Detector,论文提出在非限定条件下的具有理想检测精度的轻量级landmark检测模型,在移动设备上能达到超实时的性能。

PFLD采用辅助网络来估计人脸样本的集合信息。针对数据不平衡,设计新的损失函数,加大对难样本的惩罚力度。使用multi-scale fc层扩展感受野精确定位人脸的特征点。使用MobilenetV2 Block构建网络的Backbone提升模型的推理速度及减少模型的计算量。

2.1 PFLD网络结构

网络结构如下图所示。
Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第1张图片
其中,黄色圈起来的是PFLD是主干网络,也是主分支网络,用于预测关键的位置。主分支网络使用了普通卷积+MobilenetV2 Block+多尺度融合来增强网络的特征提取能力。

具体网络结构如下:
Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第2张图片

# ---------------------------- #
# 普通卷积
# ---------------------------- #
def conv_bn(filters, kernel_size, strides, padding='same'):
    def _conv_bn(x):
        x = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, use_bias=False)(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        return x
    
    return _conv_bn

# ---------------------------- #
# 倒残差网络块
# ---------------------------- #
def InvertedResidual(filters, strides, use_res_connect, expand_ratio=6,name=''):
    def _InvertedResidual(inputs):
        x = Conv2D(filters=filters*expand_ratio, kernel_size=1, strides=1, padding='valid', use_bias=False)(inputs)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = DepthwiseConv2D(kernel_size=3, strides=strides, padding='same', use_bias=False)(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Conv2D(filters=filters, kernel_size=1, strides=1, padding='valid', use_bias=False)(x)
        x = BatchNormalization()(x)

        if use_res_connect:
            if name:
                x = Add(name=name)([inputs, x])
                return x
            else:
                x = Add()([inputs, x])
                return x
        else:
            return x
    
    return _InvertedResidual


def PFLDInference(inputs, is_train=True, keypoints=196):
    inputs = Input(shape=inputs)
    x = Conv2D(filters=64, kernel_size=3, strides=2, padding='same', use_bias=False)(inputs)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    x = Conv2D(filters=64, kernel_size=3, strides=1, padding='same', use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    x = InvertedResidual(64, 2, False, 2)(x)

    x = InvertedResidual(64, 1, True, 2)(x)
    x = InvertedResidual(64, 1, True, 2)(x)
    x = InvertedResidual(64, 1, True, 2)(x)

    out1 = InvertedResidual(64, 1, True, 2)(x)
 
    x = InvertedResidual(128, 2, False, 2)(out1)
    
    x = InvertedResidual(128, 1, False, 4)(x)
    x = InvertedResidual(128, 1, True, 4)(x)
    x = InvertedResidual(128, 1, True, 4)(x)
    x = InvertedResidual(128, 1, True, 4)(x)
    x = InvertedResidual(128, 1, True, 4)(x)
    x = InvertedResidual(128, 1, True, 4)(x)
    
    x = InvertedResidual(16, 1, False, 2)(x)

    x1 = AvgPool2D(pool_size=(14, 14))(x)
    x1 = Reshape((x1.shape[1]*x1.shape[2]*x1.shape[3],))(x1)
    
    x = conv_bn(32, 3, 2, padding='same')(x)
    x2 = AvgPool2D(pool_size=(7, 7))(x)
    x2 = Reshape((x2.shape[1]*x2.shape[2]*x2.shape[3],))(x2)
    
    x3 = Conv2D(filters=128, kernel_size=7, strides=1, padding='valid')(x)
    x3 = Activation('relu')(x3)
    x3 = Reshape((x3.shape[1]*x3.shape[2]*x3.shape[3],))(x3)
    
    
    multi_scale = Concatenate()([x1, x2, x3])
    
    landmarks = Dense(keypoints, name='landmarks')(multi_scale)
    
    if is_train:
        out1 = AuxiliaryNet(out1)
        return Model(inputs, [Concatenate(name='train_out')([landmarks,out1]), landmarks])
    else:
        return Model(inputs, landmarks)

2.2 辅助网络

Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第3张图片
如上图,绿色圈起来的部分是辅助网络的结构,其内部是卷积层+全连接层,用于在训练阶段关键点回归的同时预测人脸姿态,使得模型更加关注那些稀有以及姿态角度过大的样本,从而提高预测的精度。

该子网络对每一个输入的人脸样本进行三维欧拉角估计,它的Ground Truth由训练数据中的关键点信息进行估计,虽然估计不太精确,但是作为区分数据分布的依据已经足够了,因为这个辅助网络的目的是监督和辅助关键点检测主分支。另外需要注意的一点是,这个辅助网络的输入不是训练数据,而是PFLD主分支网络的中间输出(第4个Block)。

具体网络结构如下:
Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第4张图片

# --------------------------------- #
# 辅助网络,辅助网络用以监督PFLD网络模型的训练
# 该子网络仅在训练的阶段起作用,在推理阶段不起作用。
# --------------------------------- #
def AuxiliaryNet(inputs):
    x = conv_bn(128, 3, 2)(inputs)
    x = conv_bn(128, 3, 1)(x)
    x = conv_bn(32, 3, 2)(x)
    x = conv_bn(128, 7, 1)(x)
    x = MaxPool2D(pool_size=(3, 3))(x)
    x = Flatten()(x)
    x = Dense(32)(x)
    x = Dense(3, name='out1')(x)
    return x

2.3 损失函数

RetinaNet中提出的Focal Loss可以较好的应对二分类中的数据不均衡情况,受到这一启发,作者设计了下面的损失函数来缓解数据不均衡的情况:
Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第5张图片
其中:

  • ∑ c = 1 C w n c ∑ k = 1 K ( 1 − cos ⁡ θ n k ) \sum^{C}_{c=1}w^{c}_{n}\sum^{K}_{k=1}(1-\cos\theta^k_n) c=1Cwnck=1K(1cosθnk)代表权重。
  • θ 1 , θ 2 , θ 3 ( K = 3 ) \theta^1,\theta^2,\theta^3(K=3) θ1,θ2,θ3(K=3)分别表示GT和Prediction在yaw、pitch、roll三种角度之间的偏差,其中pitch代表上下翻转,yaw代表水平翻转,roll代表平面内旋转,都表示人脸的一种姿态。
  • C C C表示不同的类别的人脸: 正脸、侧脸、抬头、低头、表情以及遮挡情况, w n c w^c_n wnc根据样本类别分数进行调整,论文中使用的分数样本数的导数计算的。
  • d n m d^m_n dnm由主分支网络计算得到, θ n k \theta^k_n θnk由辅助网络计算得到,然后由Loss来建立联系。
import tensorflow as tf
import tensorflow.keras.backend as K

def PFLDLoss():
    def _PFLDLoss(y_true, y_pred):
        """
        y_pred: N 199
        y_true: N 205
        """
        train_batchsize = tf.cast(K.shape(y_pred)[0], tf.float32)
        landmarks, angle = y_pred[:, :196], y_pred[:, 196:]
        # ----------------------- #
        # landmark_gt 标注点
        # attribute_gt 6个属性。
        # euler_angle_gt 三种角度 角度越大cos值越小,权重越大
        # ----------------------- #
        landmark_gt, attribute_gt, euler_angle_gt = tf.cast(y_true[:,:196], tf.float32),tf.cast(y_true[:,196:202], tf.float32),tf.cast(y_true[:,202:],tf.float32)
        weight_angle = K.sum(1 - tf.cos(angle - euler_angle_gt), axis=1) # [8,]
        """
        landmark_gt: N, 196
        landmarks: N, 196
        attribute_gt: N 6
        euler_angle_gt: N 3
        angle: N 3
        """
        attributes_w_n = tf.cast(attribute_gt[:, 1:6], tf.float32)
        mat_ratio = K.mean(attributes_w_n, axis=0)
        N = K.shape(mat_ratio)[0]
        mat_ratio = tf.where(mat_ratio>0, 1.0/mat_ratio, train_batchsize)
        weight_attribute = K.sum(tf.matmul(attributes_w_n, K.reshape(mat_ratio, (N,1))), axis=1) # [8,1]
        l2_distant = K.sum(
            (landmark_gt - landmarks) * (landmark_gt - landmarks), axis=1)
        return K.mean(weight_angle * weight_attribute *
                          l2_distant)
    return _PFLDLoss

3. 代码预测效果

Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第6张图片

4. 数据集

地址:https://wywu.github.io/projects/LAB/WFLW.html
或者百度网盘:
链接:https://pan.baidu.com/s/1CyMGCIX_3m2W1kdk7oOcFA
提取码:fh3e


每个的坐标点位置如下图:
Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型_第7张图片

5. 代码下载仓库

github地址:https://github.com/hao-ux/PFLD-tf2
gitee地址:https://gitee.com/Hao_gg/pfld-tf2

你可能感兴趣的:(人脸检测,人脸识别,算法,深度学习,计算机视觉,人脸关键点检测)