因为这两篇论文感觉内容较短,故而合并到一个博文中。

Multi-view face detection

本文来自《Multi-view Face Detection Using Deep Convolutional Neural Networks》的解读。时间线是2015年4月。

本文考虑的是多角度的人脸检测问题。在当前已经有很多这方面的工作,而当前最好的方法都需要对人脸关键点进行标注,如TSM,或者需要对人脸姿态进行标注,同时还需要训练十几个模型,从而能够在所有方向上抓取所有的人脸,例如HeadHunter方法中需要22个模型。而本文提出深度密度人脸检测(deep dense face detector,DDFD),不需要姿态或者关键点标注,而且能够用一个单一的模型区抓取各个方向上的人脸。而且不需要额外的组件,比如分割,候选框回归,或者SVM分类器等等。此外,还分析了本文的方法:发现

后者表明,通过使用更好的采样策略和更复杂的数据增强技术,可以进一步提高该模型的性能。

0.引言

2001年,Viola和Jones当初发明的级联人脸检测器,将人脸检测的速度大大提升,算是人脸发展史的一个里程碑。然而他们的方法却只能处理差不多是正脸且正向的人脸,而对不同方向不同姿态的人脸就没办法了。

face detection[Multi-view face detection&& MTCNN]-LMLPHP

对于这二十几年在多角度人脸检测上的工作,大致可以分成以下三个类别:

  • 基于神经网络的:使用神经网络来做人脸检测的历史也是挺长的。

多角度人脸检测中的难点是传统模型使用的特征不够鲁棒,不足以表征不同姿态的人脸,因此导致分类器也无法正确分类。然而随着DL的发展,这个问题被不断的逼近解决。本文提出的单一神经网络模型,就是不需要额外的关键点和姿态标注数据,且不需要引入额外的如SVM的分类器。

1. 本文方法

首先准备数据对AlexNet网络进行微调,然后通过对正样本进行裁剪然后使用IOU超过50%的作为正样本填充,并通过随机翻转等打到了一共200K个正样本和20百万个负样本。然后统一缩放到227x227,并用来微调一个经过预训练的Alexnet。然后作者采用了划窗的方法,不断的从输入图片中提取图片块,先经过Alexnet做特征映射,然后将全连接层reshape成二维的,再通过一个人脸分类器。该人脸分类器是由一个5层CNN+3层全连接层组成的网络。

ps:个人认为,就是不断的划窗,然后对一堆窗口进行分类是否是人脸,然后再进行NMS


MTCNN

0 引言

MTCNN的灵感来自《A convolutional neural network cascade for face detection》,凯鹏认为,

所以凯鹏减少了滤波器的个数,并将5x5的大小变成3x3的大小,然后加深了网络的通道数量。

face detection[Multi-view face detection&& MTCNN]-LMLPHP

上图就是MTCNN的网络层与级联CNN的网络层的对比。

MTCNN主要贡献就是一个框架可以同时预测人脸区域并且同时预测人脸关键点,且能够在线进行硬样本的挖掘,从而提升性能。通过采用多任务学习将级联CNN进行统一起来,该提出的CNN框架中包含三个阶段:

1 结构

这里我们先给出MTCNN的操作流程图和对应的网络结构图

face detection[Multi-view face detection&& MTCNN]-LMLPHP

图1.1 MTCNN操作流程图

face detection[Multi-view face detection&& MTCNN]-LMLPHP

图1.2 MTCNN的网络结构图

如图1.1所示,给定一张图片,先对图片进行金字塔构建,保证整个网络结构的尺度不变性。

2 训练过程

MTCNN用了三个任务去训练整个CNN检测器:

2.1 人脸分类

这是一个二分类问题,那么采用交叉熵loss:

face detection[Multi-view face detection&& MTCNN]-LMLPHP

这里\(p_i\)是网络预测\(x_i\)是人脸的概率,其中\(y_j^{det}\in {0,1}\)是ground-truth

2.2 边界框回归

对每个候选框,都预测基于最近的ground truth的偏移量(边界框的【左上角的坐标,宽,高】四个量),这是一个回归问题,使用欧式距离:

face detection[Multi-view face detection&& MTCNN]-LMLPHP

其中\(\hat y_j^{box}\)是网络预测的结果;\(y_i^{box}\)是ground-truth;

2.3 人脸关键点定位

和边界框回归任务一样,采用欧式距离进行回归

face detection[Multi-view face detection&& MTCNN]-LMLPHP

这里\(\hat y_i^{landmark}\)是人脸关键点的预测值;\(y_i^{landmark}\)是ground truth。

2.4 多源训练

因为在每个CNN中有不同的任务存在,所以这里在学习过程中也有不同类型的训练样本,比如人脸,非人脸,半对齐的人脸等等。这种情况下,上面三个公式在某些情况下就不能完全使用,比如对背景区域采用上,就只启动\(L_i^{det}\),并直接将其他两个loss置0。这是通过采用类型指示器完成的,如果将上述三个loss函数统一起来,就瑞下图:

face detection[Multi-view face detection&& MTCNN]-LMLPHP

这里\(N\)是样本个数,\(\alpha_j\)表示任务重要程度,这里使用的值是:

其中\(\beta_j^j\in {0,1}\)是采样类型指示器。

2.5 在线硬样本挖掘

不同于传统的,基于原始分类器训练后的硬样本挖掘,这里使用的是在线硬样本挖掘。在每个mini-batch中,先基于所有样本前向一次,并计算loss值,然后进行排序,选择前70%的作为硬样本。这样只需要计算这部分硬样本的BP,不需要计算所有样本的BP。其内在含义就是,如果loss值小意味着当前样本拟合的不错了,就不需要训练了,主要就是关注分类严重错误的那些样本

我们这里比较关心,通过图像金字塔对原图进行缩放之后,是如何经过整个Pnet,Rnet和Onet的,有两种方法:

这里我们调试了下pangyupo/mxnet_mtcnn_face_detection的代码。

face detection[Multi-view face detection&& MTCNN]-LMLPHP

face detection[Multi-view face detection&& MTCNN]-LMLPHP

face detection[Multi-view face detection&& MTCNN]-LMLPHP

face detection[Multi-view face detection&& MTCNN]-LMLPHP

face detection[Multi-view face detection&& MTCNN]-LMLPHP

从上图可以知道,首先计算多个缩放因子,然后一个缩放因子对应一个进程进行处理,最后将结果通过除以缩放因子,将结果还原到scale=1的原图空间中。即图像金字塔只存在于Pnet过程中。

# https://github.com/pangyupo/mxnet_mtcnn_face_detection/blob/master/helper.py
def generate_bbox(map, reg, scale, threshold):
"""
generate bbox from feature map
Parameters:
----------
map: numpy array , n x m x 1
detect score for each position
reg: numpy array , n x m x 4
bbox
scale: float number
scale of this detection
threshold: float number
detect threshold
Returns:
-------
bbox array
"""
stride = 2
cellsize = 12 t_index = np.where(map>threshold) # find nothing
if t_index[0].size == 0:
return np.array([]) dx1, dy1, dx2, dy2 = [reg[0, i, t_index[0], t_index[1]] for i in range(4)] reg = np.array([dx1, dy1, dx2, dy2])
score = map[t_index[0], t_index[1]]
# 获取当前结果之后,通过下面的除以scale,将结果映射回scale=1的原图中。
boundingbox = np.vstack([np.round((stride*t_index[1]+1)/scale),
np.round((stride*t_index[0]+1)/scale),
np.round((stride*t_index[1]+1+cellsize)/scale),
np.round((stride*t_index[0]+1+cellsize)/scale),
score,
reg]) return boundingbox.T def detect_first_stage(img, net, scale, threshold):
"""
run PNet for first stage Parameters:
----------
img: numpy array, bgr order
input image
scale: float number
how much should the input image scale
net: PNet
worker
Returns:
-------
total_boxes : bboxes
"""
height, width, _ = img.shape
hs = int(math.ceil(height * scale))
ws = int(math.ceil(width * scale)) im_data = cv2.resize(img, (ws,hs)) # 基于缩放因子对图片进行缩放 # adjust for the network input
input_buf = adjust_input(im_data)
output = net.predict(input_buf) # 获取PNet网络的输出
'''添加如下代码
print(f'len(output):{len(output)} output[0].shape:{output[0].shape} output[1].shape:{output[1].shape}')
输出结果为(下面为4个进程的输出结果,对应4个不同的缩放因子): # 第一个结果中的32 61 表示的是对应的划框map中的结果,即此缩放因子下一共有31x61个划框
len(output):2 output[0].shape:(1, 4, 32, 61) output[1].shape:(1, 2, 32, 61)
len(output):2 output[0].shape:(1, 4, 48, 88) output[1].shape:(1, 2, 48, 88)
len(output):2 output[0].shape:(1, 4, 69, 126) output[1].shape:(1, 2, 69, 126)
len(output):2 output[0].shape:(1, 4, 99, 179) output[1].shape:(1, 2, 99, 179)
通过shape的数量可以判定,P-Net只输出【边界框的四个预测值;是否有人脸的两个预测值】,并不输出对应的人脸关键点位置
然后通过下面的generate_bbox函数,带上缩放因子统一的将每个缩放因子结果再映射回原图中,从而完成图像金字塔的结果融合
'''
boxes = generate_bbox(output[1][0,1,:,:], output[0], scale, threshold) if boxes.size == 0:
return None
# nms
pick = nms(boxes[:,0:5], 0.5, mode='Union')
boxes = boxes[pick]
return boxes def detect_first_stage_warpper( args ):
return detect_first_stage(*args)
05-11 17:30