人脸检测和对齐算法MTCNN

Posted zhiyong_will

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人脸检测和对齐算法MTCNN相关的知识,希望对你有一定的参考价值。

1. 概述

人脸识别在实际的生活中有着广泛的应用,得益于深度学习的发展,使得人脸识别的准确率得到大幅度提升。然而,为了做好人脸识别,第一步需要做的是对人脸检测,主要是通过对图片分析,定位出图片中的人脸。近年来,深度学习在人脸检测方面也得到了大力发展,在2016年Kaipeng Zhang, Zhanpeng Zhang等人提出了人脸检测算法MTCNN(Multi-task Cascaded Convolutional Networks)模型[1],MTCNN算法的效果也是得到了很多实际项目的验证,在工业界得到了广泛的应用,在我个人的实际项目中也得到了较多应用。在MTCNN算法中,主要有三点的创新:

  • MTCNN的整体框架是一个多任务的级联框架,同步对人脸检测和人脸对齐两个项目学习;
  • 在级联的框架中使用了三个卷积网络,并将这三个网络级联起来;
  • 在训练的过程中使用到了在线困难样本挖掘的方法;

这三个方面的设计都是为了能够提升最终的检测和对齐的效果。

2. 算法原理

2.1. MTCNN的基本原理

MTCNN是多任务级联CNN的人脸检测深度学习模型,在MTCNN中是通过三个卷积网络的级联:

  • 第一阶段的网络产出人脸的候选窗口
  • 第二阶段的第一阶段产出的候选串口修正,去除掉不符合要求的候选窗口
  • 第三阶段在第二阶段的基础上进一步修正,并给出最终的五个脸部的landmark

在网络的训练过程中综合考虑人脸边框回归和面部关键点检测。MTCNN的网络整体架构如下图所示:

由上图中可以看到,MTCNN主要由四个模块:

  • 图像金字塔(Image Pyramid):通过对原始图像进行不同尺度的变换,得到图像金字塔,以适应不同大小的人脸的进行检测,在MTCNN中,是将图像resize成了三种大小,分别为 12 × 12 × 3 12\\times 12\\times 3 12×12×3 24 × 24 × 3 24\\times 24\\times 3 24×24×3 48 × 48 × 3 48\\times 48\\times3 48×48×3,这三种大小分别对应了以下三个阶段模型的输入
  • 阶段1(Proposal Network): 对上述的图像金字塔中 12 × 12 × 3 12\\times 12\\times 3 12×12×3的图像提取Bounding-Box,并利用NMS过滤掉大部分的窗口
  • 阶段2(Refine Network): 对上述的图像金字塔中 24 × 24 × 3 24\\times 24\\times 3 24×24×3的图像,根据阶段1中提取出的Bounding-Box进一步修正,去除掉不符合要求的bounding box
  • 阶段3(Output Network): 对上述的图像金字塔中 48 × 48 × 3 48\\times 48\\times 3 48×48×3的图像,根据阶段2中提取出的Bounding-Box进行最终的分析,以得到最终的结果

2.2. 三个阶段的网络

2.2.1. 第一阶段P-Net

P-Net的网络结构如下图所示:

在P-Net中,包含了三个卷积+Max-Pooling操作,其中,卷积核的大小统一为 3 × 3 3\\times 3 3×3,对于上述的网络结果,具体的参数分析如下:

  • data:大小为 12 × 12 × 3 12\\times 12\\times 3 12×12×3
  • 第一组卷积(包括conv,PReLU,Max-Pooling)
    • conv:输入( 12 × 12 × 3 12\\times 12\\times 3 12×12×3),输出( 10 × 10 × 10 10\\times 10\\times 10 10×10×10,卷积核大小为 3 × 3 3\\times 3 3×3,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 10 10 10
    • PReLU:输入( 10 × 10 × 10 10\\times 10\\times 10 10×10×10),输出( 10 × 10 × 10 10\\times 10\\times 10 10×10×10
    • Max-Pooling:输入( 10 × 10 × 10 10\\times 10\\times 10 10×10×10),输出( 5 × 5 × 10 5\\times 5\\times 10 5×5×10,核的大小为 2 × 2 2\\times 2 2×2,padding为 0 0 0,步长为 2 2 2
  • 第二组卷积(包括conv,PReLU)
    • conv:输入( 5 × 5 × 10 5\\times 5\\times 10 5×5×10),输出( 3 × 3 × 16 3\\times 3\\times 16 3×3×16,卷积核大小为 3 × 3 3\\times 3 3×3,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 16 16 16
    • PReLU:输入( 3 × 3 × 16 3\\times 3\\times 16 3×3×16),输出( 3 × 3 × 16 3\\times 3\\times 16 3×3×16
  • 第三组卷积(包括conv,PReLU)
    • conv:输入( 3 × 3 × 16 3\\times 3\\times 16 3×3×16),输出( 1 × 1 × 32 1\\times 1\\times 32 1×1×32,卷积核大小为 3 × 3 3\\times 3 3×3,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 32 32 32
    • PReLU:输入( 1 × 1 × 32 1\\times 1\\times 32 1×1×32),输出( 1 × 1 × 32 1\\times 1\\times 32 1×1×32

最终得到 32 32 32个大小为 1 × 1 1\\times 1 1×1的特征图,下面分为三个任务分别描述:

  • face classification:输入( 1 × 1 × 32 1\\times 1\\times 32 1×1×32),输出( 1 × 1 × 2 1\\times 1\\times 2 1×1×2,卷积核大小为 1 × 1 1\\times 1 1×1,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 2 2 2
  • bounding box regression:输入( 1 × 1 × 32 1\\times 1\\times 32 1×1×32),输出( 1 × 1 × 4 1\\times 1\\times 4 1×1×4,卷积核大小为 1 × 1 1\\times 1 1×1,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 4 4 4
  • facial landmark localization:输入( 1 × 1 × 32 1\\times 1\\times 32 1×1×32),输出( 1 × 1 × 10 1\\times 1\\times 10 1×1×10,卷积核大小为 1 × 1 1\\times 1 1×1,padding为 0 0 0,步长为 1 1 1,卷积核的个数为 10 10 10

注:三个任务的输出都是直接在最后一层的特征图上使用卷积操作。

参考[2]的代码实现,P-Net的代码如下:

class PNet(NetWork):

    def setup(self, task='data', reuse=False):

        with tf.variable_scope('pnet', reuse=reuse):
            (
                self.feed(task) .conv( # 第一组卷积
                    3,
                    3,
                    10,
                    1,
                    1,
                    padding='VALID',
                    relu=False,
                    name='conv1') .prelu(
                    name='PReLU1') .max_pool(
                    2,
                    2,
                    2,
                    2,
                    name='pool1') .conv( # 第二组卷积
                    3,
                    3,
                    16,
                    1,
                    1,
                    padding='VALID',
                    relu=False,
                    name='conv2') .prelu(
                        name='PReLU2') .conv( # 第三组卷积
                            3,
                            3,
                            32,
                            1,
                            1,
                            task=task,
                            padding='VALID',
                            relu=False,
                            name='conv3',
                            wd=self.weight_decay_coeff) .prelu(
                                name='PReLU3'))

        if self.mode == 'train':
            if task == 'cls': # face classification
                (self.feed('PReLU3')
                     .conv(1, 1, 2, 1, 1, task=task, relu=False,
                           name='pnet/conv4-1', wd=self.weight_decay_coeff))
            elif task == 'bbx': # bounding box regression
                (self.feed('PReLU3')
                     .conv(1, 1, 4, 1, 1, task=task, relu=False,
                           name='pnet/conv4-2', wd=self.weight_decay_coeff))
            elif task == 'pts': # facial landmark localization
                (self.feed('PReLU3')
                     .conv(1, 1, 10, 1, 1, task=task, relu=False,
                           name='pnet/conv4-3', wd=self.weight_decay_coeff))
            self.out_put.append(self.get_output())
        else:
            (self.feed('PReLU3')
                 .conv(1, 1, 2, 1, 1, relu=False, name='pnet/conv4-1')
                 .softmax(name='softmax'))
            self.out_put.append(self.get_output())
            (self.feed('PReLU3')
                 .conv(1, 1, 4, 1, 1, relu=False, name='pnet/conv4-2'))
            self.out_put.append(self.get_output())

2.2.2. 第二阶段R-Net

R-Net的网络结构如下图所示:

第二阶段的模型与第一阶段基本一致,只是在最后一层的特征图后接上了一个全连接层,同时在连接三个不同任务时也是使用了全连接的操作,参考[2]的代码如下:

class RNet(NetWork):

    def setup(self, task='data', reuse=False):

        with tf.variable_scope('rnet', reuse=reuse):
            (
                self.feed(task) .conv( # 第一个卷积
                    3,
                    3,
                    28,
                    1,
                    1,
                    padding='VALID',
                    relu=False,
                    name='conv1') .prelu(
                    name='prelu1') .max_pool(
                    3,
                    3,
                    2,
                    2以上是关于人脸检测和对齐算法MTCNN的主要内容,如果未能解决你的问题,请参考以下文章

使用TensorRT对人脸检测网络MTCNN进行加速

mtcnn论文学习

MTCNN人脸识别

MTCNN实时人脸检测网络详解与opencv+tensorflow代码演示

Mtcnn进行人脸剪裁和对齐B

MTCNN 与 DLIB 相比如何进行人脸检测?