基于mtcnn和facenet的实时人脸检测与识别系统开发 - 知乎

标签: | 发表时间:2018-11-03 17:24 | 作者:
出处:https://zhuanlan.zhihu.com
简介:本文主要介绍了实时人脸检测与识别系统的详细方法。该系统基于python/opencv2/tensorflow环境,实现了从摄像头读取视频,检测人脸,识别人脸的功能。本系统代码地址: real time face detection and recognition

1,前言

人脸识别是计算机视觉研究领域的一个热点。目前,在实验室环境下,许多人脸识别已经赶上(超过)人工识别精度(准确率:0.9427~0.9920),比如face++,DeepID3,FaceNet等(详情可以参考: 基于深度学习的人脸识别技术综述)。但是,由于光线,角度,表情,年龄等多种因素,导致人脸识别技术无法在现实生活中广泛应用。本文基于python/opencv/tensorflow环境,采用 FaceNet(LFW:0.9963 )为基础来构建实时人脸检测与识别系统,探索人脸识别系统在现实应用中的难点。下文主要内容如下 :

2,采用opencv2实现从摄像头读取视频帧; 
3,对读取的视频帧采用mtcnn方法,检测人脸;
4,采用预训练的facenet对检测的人脸进行embedding,embedding成128维度的特征;
5,对人脸embedding特征采用knn进行分类,实现人脸识别;
6,结果与改进;
7,总结。

图1-1 人脸检测与识别系统概况


2,opencv2从摄像头读取视频帧

采用opencv2,可以很方便的实现从摄像头读取视频帧。下文代码实现了从摄像头读取视频帧,并转化为灰度图像的功能,更多内容可以参考: Python-OpenCV 图像与视频处理

    import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    # Our operations on the frame come here
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Display the resulting frame
    cv2.imshow('frame',gray)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

3,人脸检测

人脸检测方法有许多,比如opencv自带的人脸Haar特征分类器和dlib人脸检测方法等。

对于opencv的人脸检测方法,有点是简单,快速;存在的问题是人脸检测效果不好。如图3-1所示,正面/垂直/光线较好的人脸,该方法可以检测出来,而侧面/歪斜/光线不好的人脸,无法检测。因此,该方法不适合现场应用。对于dlib人脸检测方法 ,效果好于opencv的方法,但是检测力度也难以达到现场应用标准。

本文中,我们采用了基于深度学习方法的mtcnn人脸检测系统( mtcnn: Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks)。mtcnn人脸检测方法对自然环境中光线,角度和人脸表情变化更具有鲁棒性,人脸检测效果更好;同时,内存消耗不大,可以实现实时人脸检测。本文中采用mtcnn是基于python和tensorflow的实现(代码来自于 davidsandberg,caffe实现代码参见: kpzhang93)。mtcnn检测出人脸后,对人脸进行剪切并resize为(96,96,3)作为facenet输入,如图3-3所示。

如图3-2所示,mtcnn方法成功检测出所有人脸。

下文代码采用opencv2的haarcascade_frontalface_alt2实现人脸检测。

    %%time
#图片人脸检测
import cv2
import sys
# Get user supplied values
imagePath = "./multi_faces.jpg"
# Create the haar cascade
faceCascade = cv2.CascadeClassifier("./haarcascade_frontalface_alt2.xml") 
# Read the image
image = cv2.imread(imagePath)#2
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)#3
# Detect faces in the image
faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.15,
    minNeighbors=5,
    minSize=(5,5),
    flags = cv2.cv.CV_HAAR_SCALE_IMAGE
) #4
print "Found {0} faces!".format(len(faces))#5
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2) #6
cv2.imshow("Faces found", image)#7
cv2.waitKey(0) #8

图3-1 opencv2人脸检测方法结果

图3-2 mtcnn人脸检测方法结果


mtcnn人脸检测部分代码摘录:

    #建立mtcnn人脸检测模型,加载参数
print('Creating networks and loading parameters')
gpu_memory_fraction=1.0
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 = detect_face.create_mtcnn(sess, './davidsandberg_facenet-master/data/')

bounding_boxes, _ = detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor)
nrof_faces = bounding_boxes.shape[0]#人脸数目
print('找到人脸数目为:{}'.format(nrof_faces))

crop_faces=[]
for face_position in bounding_boxes:
    face_position=face_position.astype(int)
    print(face_position[0:4])
    cv2.rectangle(img_color, (face_position[0], face_position[1]), (face_position[2], face_position[3]), (0, 255, 0), 2)
    crop=img_color[face_position[1]:face_position[3],
             face_position[0]:face_position[2],]
    
    crop = cv2.resize(crop, (96, 96), interpolation=cv2.INTER_CUBIC )

    
    print(crop.shape)
    crop_faces.append(crop)
    plt.imshow(crop)
    plt.show()
    
plt.imshow(img_color)
plt.show()

图3-3 剪切人脸区域

4,facenet embedding

Facenet是谷歌研发的人脸识别系统,该系统是基于百万级人脸数据训练的深度卷积神经网络,可以将人脸图像embedding(映射)成128维度的特征向量。以该向量为特征,采用knn或者svm等机器学习方法实现人脸识别。Facenet在LFW数据集上识别准确率为0.9963,详情可以参见: 谷歌人脸识别系统FaceNet解析。本文采用的是 davidsandberg基于FaceScrub and CASIA-WebFace数据集预训练的Facenet模型,LFW测试集准确率为0.98。下文代码实现了恢复预训练facenet模型和使用模型进行embedding,应用代码参考了 yobiface实现的代码。

    #建立facenet embedding模型
print('建立facenet embedding模型')
tf.Graph().as_default()
sess = tf.Session()
images_placeholder = tf.placeholder(tf.float32, shape=(batch_size, 
                                                       image_size, 
                                                       image_size, 3), name='input')

phase_train_placeholder = tf.placeholder(tf.bool, name='phase_train')



embeddings = network.inference(images_placeholder, pool_type, 
                               use_lrn, 
                               1.0, 
                               phase_train=phase_train_placeholder)



ema = tf.train.ExponentialMovingAverage(1.0)
saver = tf.train.Saver(ema.variables_to_restore())
#ckpt = tf.train.get_checkpoint_state(os.path.expanduser(model_dir))
#saver.restore(sess, ckpt.model_checkpoint_path)

model_checkpoint_path='./model-20160506.ckpt-500000'
#ckpt = tf.train.get_checkpoint_state(os.path.expanduser(model_dir))
#model_checkpoint_path='model-20160506.ckpt-500000'


#saver.restore(sess, ckpt.model_checkpoint_path)
saver.restore(sess, model_checkpoint_path)
print('facenet embedding模型建立完毕')

######省略部分代码
emb_data = sess.run([embeddings], 
                            feed_dict={images_placeholder: face_data, phase_train_placeholder: False })[0]
        

5,人脸识别

对人脸进行embedding后,得到128维度的特征向量 。以该特征向量为基础,可以采用任何机器学习的方法进行分类和识别。本文中,选取了knn(k-NearestNeighbor)方法(你可以换其它任何分类方法,比如svm或者神经网络方法等)。本文中,采用了sklearn库实现knn模型的训练和预测。

首先,需要训练分类器。训练数据为:类别1:目标人脸1;类别2:目标人脸2...,类别n:其他人脸。本代码中,类别1:我的人脸数据(经过人脸检测和embedding,共98个样本);类别2:其它人脸(采用lfw数据集随机选取的人脸数据,共69个样本)。训练代码如下:

    #训练KNN分类

from sklearn import metrics  
from sklearn.externals import joblib

X_train, X_test, y_train, y_test = train_test_split(X, train_y, test_size=.3, random_state=42)

# KNN Classifier  
def knn_classifier(train_x, train_y):  
    from sklearn.neighbors import KNeighborsClassifier  
    model = KNeighborsClassifier()  
    model.fit(train_x, train_y)  
    return model  

classifiers = knn_classifier 

model = classifiers(X_train,y_train)  
predict = model.predict(X_test)  

accuracy = metrics.accuracy_score(y_test, predict)  
print ('accuracy: %.2f%%' % (100 * accuracy)  ) 
  
    
#保存模型
joblib.dump(model, 'knn.model')

训练后的分类器即可对人脸进行识别,代码如下:

    model = joblib.load('knn.model')
predict = model.predict(X_test) 
print ('识别结果为:{}'.format(predict)) 

6,结果与改进

6.1 运行结果

如图6-1所示,系统可以从摄像头获取视频,实时检测出人脸,并识别。系统不管对正面人脸,还是歪斜的人脸,以及不同表情的人脸均能有效的检测并识别,具有一定的鲁棒性。






图6-1 成功检测和识别出我和其他人

6.2 改进

本系统虽然采用了高准确率的人脸检测(mtcnn)与识别方法(facenet),可以实现实时人脸检测与识别,具有一定的鲁棒性。但是,如果要在现实生活中应用本系统,还需要做许多改进。

6.2.1 人脸检测

mtcnn人脸检测方法精确度很高,但是依然存在一些问题。

一,该方法无法识别倾斜度大于45度的人脸和侧面的人脸。如图6-2所示,当倾斜度大于45度后,系统无法检测出人脸。该问题原因可能是mtcnn网络训练数据中没有大倾斜度的人脸照片。解决的方法可以有两种:(1)对训练照片进行旋转,在但前mtcnn网络的基础上进行finetune;(2)在视频帧读取后,旋转不同角度后,分别传入mtcnn进行人脸检测。前一种方法需要处理大量的人脸数据;后一种方法会影响实时检测的速度。

图6-2 难以检测出倾斜度过大的人脸
二,mtcnn人脸检测方法还存在另外的小问题:有时可能会被汪星人欺骗(好吧,汪星人的脸也是脸,该方法很强大,哦也!请忽略)。该问题不影响实际应用。

图6-3 检测出潜伏在人类星球的汪星人脸

6.2.2 人脸识别

目前,基于深度神经网络方法的人脸识别系统在实验环境下准确率很高,已经达到或者超越人类的准确率,但是在现实环境中的准确率仅在60%-70%左右。现实环境中,光线/表情/角度/化妆等多种因素均会影响识别效果,有待研究新方法(参见: 基于深度学习的人脸识别技术综述)。

本系统采用的是 davidsandberg预训练的Facenet网络模型,如果需要更高准确率的facenet模型,可以考虑 openface

同时,当前系统的识别分类器是基于仅仅167个正负样本训练的knn分类器,测试准确率仅为94%左右。进一步改进可以考虑:(1)采集更多的类别与数量的训练样本训练分类器,实现多个类别的人脸识别; (2)选择其它分类方法(svm/神经网络等)。(3)将分类器训练部分集成,实现实时训练与识别。

6.2.3 其它改进

本系统的核心方法是基于mtcnn和facenet,均是基于深度学习的方法。虽然神经网络模型不算大,但是相对于opencv内置的人脸检测和识别方法来讲内存消耗还是比较大的。本文运行环境为Ubuntu 16.04.1,8G内存,仅能设置为每5帧进行一次检测和识别(不影响实际效果)。可以考虑在不影响深度神经网络方法精确度的情况下,减少内存消耗,实现在移动设备(手机/平板等)运行。

同时,本系统代码采用的是ipython notebook(为了代码结构更加清晰,易读),运行一段时间后会导致内存满,kernel die的问题。

7,总结

本文主要介绍了实时人脸检测与识别系统的详细方法,该系统基于python/opencv2/tensorflow环境,实现了从摄像头读取视频,检测人脸,识别人脸的功能。实现类似功能的代码有 openface,但是,openface核心是基于torch和lua。本人更喜欢python和tensorflow,因此,才有本文的产生。

本系统相对于opencv自带的人脸检测和识别的准确率更高,鲁棒性更好;但距离实际应用,还需要许多改进。同时,实时人脸检测与识别技术还需要许多改进和研究,期待和大家一起探索,谢谢。

相关 [mtcnn facenet 实时] 推荐:

基于mtcnn和facenet的实时人脸检测与识别系统开发 - 知乎

- -
简介:本文主要介绍了实时人脸检测与识别系统的详细方法. 该系统基于python/opencv2/tensorflow环境,实现了从摄像头读取视频,检测人脸,识别人脸的功能. 本系统代码地址: real time face detection and recognition. 人脸识别是计算机视觉研究领域的一个热点.

Facebook的实时Hadoop系统

- wangjia - Solrex Shuffling
Facebook 在今年六月 SIGMOD 2011 上发表了一篇名为“Apache Hadoop Goes Realtime at Facebook”的会议论文 (pdf),介绍了 Facebook 为了打造一个实时的 HBase 系统使用到的独门秘技. 由于该论文提到的应用场景与小弟负责的系统要解决的问题域有相似之处,因而抽时间仔细阅读了这篇论文.

Storm 实时性分析

- - CSDN博客架构设计推荐文章
都说Storm是一个实时流处理系统,但Storm的实时性体现在什么方面呢. 首先有一个前提:这里的实时性和我们通常所说的实时系统(芯片+汇编或C编写的实时处理软件)的实时性肯定是没法比的,也不是同一个概念. 这里的实时性应该是一个相对的实时性(相对于Hadoop之类 ). 总结一下,Storm的实时性可能主要体现在:.

storm准实时应用

- - CSDN博客推荐文章
1 应用背景: 需要实时统计用户的登陆数,在线人数,活跃时间,下载等指标的数据,或者清洗后移到hdfs上.         1) 客户端产生数据---.         2) kafka-生产者实时采集数据(保留7天)-----.         3) storm实时消费数据,处理数据.         4)把实时数据统计结果缓存到memcached 中.

实时传输协议(RTP)和实时控制协议(RTCP)

- - CSDN博客推荐文章
RTP是一种提供端对端传输服务的实时传输. 协议,用来支持在单目标广播和多目标广播网络服务中传输实时数据,而实时数据的传输则由RTCP. 使用RTP协议的应用程序运行在RTP之上,而执行RTP的程序运行在UDP的上层,目的是为了使用UDP的端口号 和检查和. 如图16-12所示,RTP可以看成是传输层的子层.

实时流计算、Spark Streaming、Kafka、Redis、Exactly-once、实时去重

- - lxw的大数据田地
本文想记录和表达的东西挺多的,一时想不到什么好的标题,所以就用上面的关键字作为标题了. 在实时流式计算中,最重要的是在任何情况下,消息不重复、不丢失,即Exactly-once. 本文以Kafka–>Spark Streaming–>Redis为例,一方面说明一下如何做到Exactly-once,另一方面说明一下我是如何计算实时去重指标的.

快速构建实时抓取集群

- 狗尾草 - 搜索技术博客-淘宝
首先,我们定义一下定向抓取,定向抓取是一种特定的抓取需求,目标站点是已知的,站点的页面是已知的. 本文的介绍里面,主要是侧重于如何快速构建一个实时的抓取系统,并不包含通用意义上的比如链接分析,站点发现等等特性. 在本文提到的实例系统里面,主要用到linux+mysql+redis+django+scrapy+webkit,其中scrapy+webkit作为抓取端,redis作为链接库存储,mysql作为网页信息存储,django作为爬虫管理界面,快速实现分布式抓取系统的原型.

论文:Hadoop在Facebook的实时应用

- Adam - NoSQLFan
Facebook在其最新的消息系统中使用了HBase,这已经不是什么新闻了,而HBase与其基础设施HDFS也因此越来越受追捧,下面是Facebook在SIGMOD 2011大会上发表的论文,描述了Hadoop系列工具在Facebook中的应用情况. Facebook为何选择了Hadoop和HBase.

快速构建实时抓取集群

- Wang Dong - NoSQLFan
本文转载自搜淘宝索技术博客,描述了实时抓取集群的架构. 其架构中使用Redis作为核心的LinkBase存储,包括了使用List结构来存储抓取队列,使用Hash结构来存储链接表及使用Sorted Sets结构来存储已抓取集合. 注:原文中描述的已抓取集合是使用Set结构来存储,应该是不正确的,NoSQLFan进行了修改,如果修改有问题,欢迎指正.

inotify-rsync实时同步脚本

- lostsnow - 无网不剩
rsync是linux下一款非常强大的同步工具,采用差异同步的方法,只上传文件/文件夹的不同部分,同时可以对上传部分先进行压缩,所以rsync的传输效率是很高的. 但rsync也有缺点,最大的问题就是每次执行rsync命令都会遍历目标目录,当文件不多时,这没什么问题,一旦文件数到了一定规模,那么每次遍历都会消耗很多资源.