多任务卷积神经网络(MTCNN)实现人脸检测与对齐是在一个网络里实现了人脸检测与五点标定的模型,主要是经过CNN模型级联实现了多任务学习网络。整个模型分为三个阶段,第一阶段经过一个浅层的CNN网络快速产生一系列的候选窗口;第二阶段经过一个能力更强的CNN网络过滤掉绝大部分非人脸候选窗口;第三阶段经过一个能力更增强的网络找到人脸上面的五个标记点;完整的MTCNN模型级联以下:html
该模型的特征跟HAAR级联检测在某些程度上有必定的相通之处,都是采用了级联方式,都是在初期就拒绝了绝大多数的图像区域,有效的下降了后期CNN网络的计算量与计算时间。MTCNN模型主要贡献在于:网络
1.提供一种基于CNN方式的级联检测方法,基于轻量级的CNN模型就实现了人 脸检测与点位标定,并且性能实时。 2.实现了对难样本挖掘在线训练提高性能 3.一次能够完成多个任务。
第一阶段
网络是全卷积神经网络是一个推荐网络简称 P-Net, 主要功能是得到脸部区域的窗口与边界Box回归,得到的脸部区域窗口会经过BB回归的结果进行校订,而后使用非最大压制(NMS)合并重叠窗口。
第二阶段
网络模型称为优化网络R-Net,大量过滤非人脸区域候选窗口,而后继续校订BB回归的结果,使用NMS进行合并。
第三阶段
网络模型称为O-Net,输入第二阶段数据进行更进一步的提取,最终输出人脸标定的5个点位置。架构
网络架构与训练
对CNN网络架构,论文做者发现影响网络性能的因素主要缘由有两个:ide
1.样本的多样性缺少会影响网络的鉴别能力 2.相比其它的多类别的分类与检测任务来讲, 人脸检测是一个二分类,每一层不须要太多filters, 也就是说每层网络的feature maps个数不须要太多
根据上述两个因素,做者设计网络每层的filter个数有限,可是它增长了整个网络的深度,这样作的好处是能够显著减小计算量,提高整个网络性能,同时所有改用3x3的filter更进一步下降计算量,在卷积层与全链接层使用PReLU做为非线性激活函数(输出层除外)整个网络架构以下:函数
训练这个网络须要以下三任务获得收敛性能
1.人脸二元分类 2.BB回归(bounding box regression) 3.标记定位(Landmark localization)
训练时候对于人脸采用交叉熵损失:
BB回归损失:
对每一个候选窗口,计算它与标注框之间的offset,目标是进行位置回归,计算其平方差损失以下学习
脸部landmark位置损失:
总计有五个点位坐标分别为左眼、右眼、鼻子、左嘴角、右嘴角
由于每一个CNN网络完成不一样的训练任务,因此在网络学习/训练阶段须要不一样类型的训练数据。因此在计算损失的时候须要区别对待,对待背景区域,在R-Net与O-Net中的训练损失为0,由于它没有包含人脸区域,经过参数beta=0来表示这种类型。总的训练损失能够表示以下:
在P-Net中对人脸进行二元分类时候就能够在线进行难样本挖掘,在网络前向传播时候对每一个样本计算获得的损失进行排序(从高到低)而后选择70%进行反向传播,缘由在于好的样本对网络的性能提高有限,只有那些难样本才能更加有效训练,进行反向传播以后才会更好的提高整个网络的人脸检测准确率。做者的对比实验数据代表这样作能够有效提高准确率。在训练阶段数据被分为四种类型:测试
负样本:并交比小于0.3 正样本:并交比大于0.65 部分脸:并交比在0.4~0.65之间 Landmark脸:可以找到五个landmark位置的
其中在负样本与部分脸之间并无明显的差别鸿沟,做者选择0.3与0.4做为区间。优化
正负样本被用来实现人脸分类任务训练
正样本与部分脸样本训练BB回归
Landmark脸用来训练人脸五个点位置定位设计
整个训练数的比例以下:
负样本:正样本:部分脸:landmark脸=3:1:1:2
加载网络
print('Creating networks and loading parameters') with tf.Graph().as_default(): gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False)) with sess.as_default(): pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
人脸检测
def detection(image): minsize = 20 # minimum size of face threshold = [0.6, 0.7, 0.7] # three steps's threshold factor = 0.709 # scale factor # detect with RGB image h, w = image.shape[:2] bounding_boxes, _ = align.detect_face.detect_face(image, minsize, pnet, rnet, onet, threshold, factor) if len(bounding_boxes) < 1: print("can't detect face in the frame") return None print("num %d faces detected"% len(bounding_boxes)) bgr = cv.cvtColor(image, cv.COLOR_RGB2BGR) for i in range(len(bounding_boxes)): det = np.squeeze(bounding_boxes[i, 0:4]) bb = np.zeros(4, dtype=np.int32) # x1, y1, x2, y2 bb[0] = np.maximum(det[0] - margin / 2, 0) bb[1] = np.maximum(det[1] - margin / 2, 0) bb[2] = np.minimum(det[2] + margin / 2, w) bb[3] = np.minimum(det[3] + margin / 2, h) cv.rectangle(bgr, (bb[0], bb[1]), (bb[2], bb[3]), (0, 0, 255), 2, 8, 0) cv.imshow("detected faces", bgr) return bgr
实时摄像头检测
capture = cv.VideoCapture(0) height = capture.get(cv.CAP_PROP_FRAME_HEIGHT) width = capture.get(cv.CAP_PROP_FRAME_WIDTH) out = cv.VideoWriter("D:/mtcnn_demo.mp4", cv.VideoWriter_fourcc('D', 'I', 'V', 'X'), 15, (np.int(width), np.int(height)), True) while True: ret, frame = capture.read() if ret is True: frame = cv.flip(frame, 1) cv.imshow("frame", frame) rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB) result = detection(rgb) out.write(result) c = cv.waitKey(10) if c == 27: break else: break cv.destroyAllWindows()
运行演示:
原本想上传视频的发现上传不了了,因此就把视频写成多张连续的图像,截屏显示各类效果,其实视频十分流畅,效果也很是的好。
有遮挡、部分脸检测
侧脸检测
角度俯仰脸检测
总结一下
整个模型的运行速度极快,即便在CPU上也能够彻底达到实时性能,关键是其检测准确率与稳定性跟HAAR/LBP的方式相比,你就会感受HAAR/LBP的方式就是渣,彻底凉啦!