概述
MTCNN,Multi-task convolutional neural network(多任务卷积神经网络)。该模型利用级联思想,化繁为简,通过三个级联的网络:P-Net(Proposal Network)、R-Net(Refine Network)和O-Net(Output Network),逐层筛选。在每个网络都非常简单,易于训练的同时,实现了高精度的人脸检测。
思路
人脸检测属于单类多目标检测,相对于单类单目标,实现中有以下问题:
- 人脸数目
因为不知道1张图中需要检测多少人脸,也就不能通过网络直接输出预测值。
解决方法:像卷积操作中卷积核的滑动一样,以一定的大小和步长扫描整张图片,将扫描到的小图输入网络做检测。 - 人脸大小
紧接着的问题是:如何确定“卷积核”大小?
因为需要检测的人脸大小也不确定,所以考虑应该用不同大小的“卷积核”去扫描。
反之,也可以保持“卷积核”不变,通过数次缩小原图(图像金字塔)来实现。 - 精度不高
如果像普通单类单目标任务一样,直接回归真实人脸框,那么检测中一旦错过了合适的框,就错过该目标了。
因此MTCNN中一个精妙的点在于:不直接学习真实框,而是学习偏移框,由偏移量反算回真实框。
这样做的好处是:在人脸目标周围,会检测到多个偏移框,从而反算回多个预测框,大大增加了容错率。 - 一大堆框
这样就可以将不同大小的扫描图片传入P-Net检测,在一大堆重叠的预测框中,我们需要的,只应该是其中最确定的那一个,这就依赖于NMS操作了 。
数据处理
选择CelebA人脸数据集,标签为人脸框左上角坐标:x1、y1,宽高:w、h,及5个人脸特征点坐标。
- 对真实框中心坐标做随机偏移,最大边长+随机量作为新边长,生成正方形框。可以选择将1张图片增样成数张。
- 将正方形框和原真实框做 IOU,通过设定 IOU 的阈值,将生成框划分为正样本、负样本、部分样本。
- 裁下正方形框,resize成尺寸12、24、48,分别用于P、R、O网络训练。
- 计算生成框坐标、5个人脸特征点坐标的偏移量,负样本偏移量均设为0,因其不参与坐标回归训练。
计算偏移量作用相当于归一化,加快收敛。 - 将置信度、2个生成框坐标偏移量、5个人脸特征点坐标偏移量,共15个值写入txt文件作为训练标签。
生成框坐标偏移量计算如下图,其余特征点坐标偏移量计算方法同 (_x1,_y1),对左上角点做偏移即可,也可以自己找一个点做偏移,如右下角点或矩形中点,测试时按对应点反算坐标。

我处理后的3种样本数据集,每种尺寸20万张,共60万张。
正样本:

部分样本:

负样本:

工具
非极大值抑制
交并比(Intersection-over-Union,IOU),两个图形交集与并集的比值,表示重叠程度。


需要注意的是:在最后的O网络输出时,将把 IOU 计算公式中分母改为A和B中的面积较小值,从而去除候选框大框套小框的情况。

非极大值抑制(Non-Maximum Suppression,NMS)
作用:在同一个人脸的一堆候选框中,选出最可信的一个。
步骤1:将候选框按置信度降序排序
步骤2:第一个候选框依次与之后的框做 IOU, 大于设定阈值的框(认为同一个人脸)舍弃
步骤3:保留第一个候选框,剩余的候选框重复步骤1~步骤3,直至剩下一个框,保留。
最后保留下来的候选框就是 NMS 处理后的结果。
Soft-NMS 是对 NMS 的一种改进。
伪代码如图:

B: 初始检测框集合,S:对应检测框的分数, Nt :IOU的阈值,M :得分最高的检测框
NMS 的处理是将大于 IOU 阈值的框直接舍弃(分数置0),这样容易错过一些重合度较大的框。

Soft-NMS 的思路:根据 IOU 的值降低框的得分,最后根据分数阈值统一删除。
降低置信度的方法有两种:
- 线性加权(不连续)

- 高斯加权(连续)

这两种方法在实验中效果差别不大,因此代码中我选择第一种更简洁的形式。
def nms(boxes, thresh=0.3, is_min=False, softnms=False):
if boxes.shape[0] == 0:
return np.array([])
_boxes = boxes[(-boxes[:, 14]).argsort()] # 按置信度排序
r_boxes = []
while _boxes.shape[0] > 1:
a_box = _boxes[0]
b_boxes = _boxes[1:]
score = b_boxes[:, 14]
r_boxes.append(a_box)
if softnms:
score_thresh = 0.5
# IOU>阈值的框 置信度衰减
t_idx = np.where(iou(a_box, b_boxes, is_min) > thresh)
score[t_idx] *= (1 - iou(a_box, b_boxes, is_min))[t_idx]
# 删除分数<阈值的框
_boxes = np.delete(b_boxes, np.where(score < score_thresh), axis=0)
else:
# 筛选IOU<阈值的框
index = np.where(iou(a_box, b_boxes, is_min) < thresh)
_boxes = b_boxes[index]
# 剩余最后1个框 保留
if _boxes.shape[0] > 0:
r_boxes.append(_boxes[0])
# 把list组装成矩阵
return np.stack(r_boxes<


4849

被折叠的 条评论
为什么被折叠?



