基于TensorFlow的MTCNN人脸检测算法 学习记录

什么是MTCNN

MTCNN,Multi-task convolutional neural network(多任务卷积神经网络),将人脸区域检测与人脸关键点检测放在了一起,基于TensorFlow框架。总体可分为PNet、RNet、和ONet三层网络结构。

PNet

全称为Proposal Network,其基本的构造是一个全连接网络。对上一步构建完成的图像金字塔,通过一个FCN进行初步特征提取与标定边框,并进行Bounding-Box Regression调整窗口与NMS进行大部分窗口的过滤。
首先,PNet为全卷积网络,该网络的input size为12123,而在输入PNet网络之前,由于Test_image大小不一,所以先用一个循环对原始图片进行缩放,再输入PNet;当宽高小于12时候这张图片对应的循环结束,12是PNet的最小图片输入尺寸。下图表示的是PNet的结构:
基于TensorFlow的MTCNN人脸检测算法 学习记录
由上图知:一张输入12123的图片。历经Conv1、MP1、Conv2、Conv3后,输出结果为1132(Conv和FC层有权重参数,而Pool层没有),而后转化为三分支,分别用于人脸分类、边框回归、人脸特征点定位。

其中,各自的损失函数见下表:

分支 损失函数
人脸分类 交叉熵
边框回归 边框回归平方和损失函数
人脸特征点定位 5个特征点与标定好的数据的平方和损失
总损失 三个损失乘上各自的权重比之和

各损失函数的表达式可以参见论文;在PNet里面三种损失的权重是1:0.5:0.5。
将图片输入PNet后,得到了cls_cls_map, reg这两个数组,其中cls_cls_map是(H,W,2)的二维数组,就是非人脸和人脸的概率PNet直接输出的边界框并不是传统回归中的边界坐标,而是预测人脸位置相对于输入图片的位置差,即为reg

将cls_cls_map里面人脸的概率和一个事先设定的阈值相比较,如果大于这个阈值,就将这张图片对应的reg数组里面的预测值提取出来,通过逆运算得到原始像素坐标。对于 x * y 的输入,将产生大小为[(x−12)/2+1]∗[(y−12)2+1]的输出。因为池化层的步长是2,所以上述式子的分母为2。将reg的坐标通过此方法可还原预测边框值在原始图片的像素坐标。最后返回的数组是(x1,y1,x2,y2,score,reg),其中(x1,y1,x2,y2)是bbox在原始图片中的像素坐标。score是cls_cls_map对应的人脸概率

完成这一步后,将使用非极大值抑制法(NMS)去掉一些重复框,这个算法的原理是将上一步返回的数组的score值最大的那一行元素提取出来,将剩下的所有的元素的score和一个设定好的阈值相比较,将score值大于阈值(0.5)的元素抛弃,再将剩下的元素重复之前的提取最大值并进行比较的操作。直到最后,这样就初步抛弃了那些重合度较高的人脸框

因为**(x1,y1,x2,y2)是在原图像中的像素坐标**,reg是候选框区域相对于像素坐标的偏差,这样讲将原像素坐标加上偏差值,即可得到候选框的坐标。将初步筛选后的的bbox按照上面的方法refine,到此为止,PNet这一部分就结束了。输出的是候选框的4个坐标加上对应的score值

RNet

全称为Refine Network,其基本的构造是一个卷积神经网络,相对于第一层的PNet来说,增加了一个全连接层,因此对于输入数据的筛选会更加严格。在图片经过PNet后,会留下许多预测窗口,我们将所有的预测窗口送入RNet,这个网络会滤除大量效果比较差的候选框最后对选定的候选框进行Bounding-Box Regression(边界回归)和NMS(非最大值抑制)进一步优化预测结果

先将PNet的输出resize成正方形,(主要基于人脸一般都是正方形的)。
再将PNet生成的bbox里的元素调整一下,生成(dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph)这样的数组;生成的元素的意义如下:

dx,dy bbox的相对本身起点坐标(0,0)
edx,edy bbox的相对本身终点坐标(tmpw-1, tmph-1)
x,y 原始图片的bbox起点
ex,ey 原始图片的bbox结束点
tmpw, tmph 原始图片中bbox的宽高

在生成的过程还会有检测,避免bbox的坐标超出原始图片或者为负值;接下来遍历这个数组,将里面的bbox从原始图片里面抠出来,resize成24x24同时进行归一化

完成了前面的操作后,接下来就是将resize后的24x24图片送入RNet,下图表示的是RNet的结构:

基于TensorFlow的MTCNN人脸检测算法 学习记录
可以看出RNet最大的不同之处在于最后一步是采用的FC层(这也是为什么喂入的图片统一成24x24大小,必须统一)。

由上面这张图可以得到,一张24x24x3的图片最终的输出的结果是3x3x64的特征图,再经历FC层后分成三条分支,用于人脸分类、边框回归、人脸特征点定位。这三条支路的损失函数和PNet的一样,各损失的权重比也为1:0.5:0.5。

将图片输入PNet后,得到了cls_scores, reg这两个数组,cls_scores表示非人脸和人脸的概率,reg表示bbox的回归信息。同样将cls_scores中人脸的概率与设定的阈值比较,将大于阈值的图片对应的bbox提取出来,过滤掉一部分非人脸的bbox

接着再次调用NMS,抛弃掉大量的重叠率高的人脸框,经过两次的筛选,剩下的bbox的数量就少了很多。最后进行RNet的最后一步操作,就是回归信息reg来调整bbox的坐标,大致就是将bbox的4个坐标乘上bbox的宽或者高,其中x和宽相乘,y和高相乘。最后就是返回调整后的四个坐标。

ONet

全称为Output Network,基本结构是一个较为复杂的卷积神经网络,相对于RNet来说多了一个卷积层。ONet的效果与RNet的区别在于这一层结构会通过更多的监督来识别面部的区域而且会对人的面部特征点进行回归,最终输出五个人脸面部特征点

先将RNet的输出resize成正方形,接下来的操作和对应的RNet部分相似,只是再送入ONet的图片大小是resize形成的48483。

下图表示的是ONet的结构:
基于TensorFlow的MTCNN人脸检测算法 学习记录
将48x48x3的图片送入ONet后输出的是3x3x128的特征图,经过FC层后同样是有着三条分支。三条分支的损失函数与PNet、RNet一样,但是三个损失函数的权重比为1:0.5:1

这次从ONet的输出cls_scores, reg, landmark这三个数组,同样先根据cls_scores的人脸概率是否大于设定的阈值来抛弃一部分非人脸框接下来就是确定landmark的值,因为前面直接得到的关键点的x、y坐标相关信息并不是x、y的值,而是一个相对于宽高的偏置值,最终的关键点的x、y值可以通过这个偏置值和bbox的宽或者高(x与宽,y与高)相乘再与bbox的坐标相加得到。

接下来就是回归信息reg来调整bbox的坐标,与RNet输出前的操作一样。完成之后经历两次的NMS操作,但是这次的NMS操作与之前的略有不用,大家可以看详细的代码解释。最后就可以输出bbox和landmark了,至此算法就结束了。

附录(代码详解)

这里(生成PNet的人脸数据样本、生成PNet人脸关键点的训练数据、训练数据进行合并、为PNet生成tfrecord文件、PNet的训练、生成RNet训练数据、RNet的训练、生成ONet训练数据、ONet的训练)是一些训练过程中的代码的解释。