利用MTCNN和facenet实现人脸检测和人脸识别
人脸检测和人脸识别技术算是目前人工智能方面应用最成熟的技术了。本博客将利用mtcnn和faceNet搭建一个实现人脸检测和人脸识别的系统。基本思路也很简单,先利用mtcnn的进行人脸检测,当然也可以使用其他的人脸检测方法,如Dilb,OpenCV,OpenFace人脸检测等等,然后再利用faceNet进行人脸识别,faceNet可简单看成是提取人脸特征的CNN网络,这个特征就是embadding了,有了人脸特征embadding,最后一步,就只需要与数据库人脸特征进行相似性比较,即可完成人脸识别的任务。
老规矩,先上Github源码:记得给个“Star”哦,不然,对不起我的苦劳!!!!
本博客Github源码: https://github.com/PanJinquan/Face_Detection_Recognition/tree/master/faceRecognition
目录
一、项目结构:
打开FaceNet Github地址: https://github.com/davidsandberg/facenet,把我们需要的文件拷贝到自己独立的工程中,(1)align文件夹,(2)facenet.py文件:
align:这个文件夹是从facenet中拷贝的,https://github.com/davidsandberg/facenet/tree/master/src/align,主要是MTCNN人脸检测的相关文件
facenet.py:这个Python文件也是从facenet中拷贝的,https://github.com/davidsandberg/facenet/blob/master/src/facenet.py
其他文件介绍
dataset:这个文件夹主要存放数据,如人脸数据库
utils:这个文件是工具类文件,用于文件读写,图像相关操作的函数方法等
models:存放facenet预训练模型,下载地址:
Pre-trained models:
Model name LFW accuracy Training dataset Architecture 20180408-102900 0.9905 CASIA-WebFace Inception ResNet v1 20180402-114759 0.9965 VGGFace2 Inception ResNet v1 NOTE: If you use any of the models, please do not forget to give proper credit to those providing the training dataset as well.
二、实现流程
1.通过MTCNN人脸检测模型,从照片中提取人脸图像。
2.把人脸图像输入到FaceNet,计算Embedding的特征向量。
3.比较特征向量间的欧式距离,判断是否为同一人,例如当特征距离小于1的时候认为是同一个人,特征距离大于1的时候认为是不同人。
三、MTCNN人脸检测
人脸检测方法很多,如Dilb,OpenCV,OpenFace人脸检测等等,这里使用MTCNN进行人脸检测,一方面是因为其检测精度确实不错,另一方面facenet工程中,已经提供了用于人脸检测的mtcnn接口。 MTCNN是多任务级联CNN的人脸检测深度学习模型,该模型中综合考虑了人脸边框回归和面部关键点检测。在facenet工程中的位置是align/detect_face.py
,它的参数模型也保存在align文件夹下,分别是det1.npy,det2.npy,det3.npy
。
这里提供一个使用MTCNN进行人脸检测的方法:
def detection_face(img):
minsize = 20 # minimum size of face
threshold = [0.6, 0.7, 0.7] # three steps's threshold
factor = 0.709 # scale factor
print('Creating networks and loading parameters')
with tf.Graph().as_default():
# gpu_memory_fraction = 1.0
# 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))
sess = tf.Session()
with sess.as_default():
pnet, rnet, onet = detect_face.create_mtcnn(sess, None)
bounding_boxes, points = detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor)
return bounding_boxes,points
当然,实际应用中,建议还是封装成一个类吧,方面初始化和单独调用:
class Facedetection:
def __init__(self):
self.minsize = 20 # minimum size of face
self.threshold = [0.6, 0.7, 0.7] # three steps's threshold
self.factor = 0.709 # scale factor
print('Creating networks and loading parameters')
with tf.Graph().as_default():
# gpu_memory_fraction = 1.0
# 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))
sess = tf.Session()
with sess.as_default():
self.pnet, self.rnet, self.onet = detect_face.create_mtcnn(sess, None)
def detect_face(self,image):
bounding_boxes, points = detect_face.detect_face(image, self.minsize, self.pnet, self.rnet, self.onet, self.threshold, self.factor)
return bounding_boxes, points
四、faceNet人脸识别
FaceNet Github地址: https://github.com/davidsandberg/facenet
Google工程师Florian Schroff,Dmitry Kalenichenko,James Philbin提出了人脸识别FaceNet模型,该模型没有用传统的softmax的方式去进行分类学习,而是抽取其中某一层作为特征,学习一个从图像到欧式空间的编码方法,然后基于这个编码再做人脸识别、人脸验证和人脸聚类等。
FaceNet主要用于验证人脸是否为同一个人,通过人脸识别这个人是谁。FaceNet的主要思想是把人脸图像映射到一个多维空间,通过空间距离表示人脸的相似度。同个人脸图像的空间距离比较小,不同人脸图像的空间距离比较大。这样通过人脸图像的空间映射就可以实现人脸识别,FaceNet中采用基于深度神经网络的图像映射方法和基于triplets(三联子)的loss函数训练神经网络,网络直接输出为128维度的向量空间。
FaceNet的网络结构如下图所示,其中Batch表示人脸的训练数据,接下来是深度卷积神经网络,然后采用L2归一化操作,得到人脸图像的特征表示,最后为三元组(Triplet Loss)的损失函数。
下面是鄙人已经封装好的facenetEmbedding类,其中类函数get_embedding(self,images)方法用于提取facenet的人脸特征embadding,有了人脸embadding特征,就可以比较人脸相似性啦!
class facenetEmbedding:
def __init__(self,model_path):
self.sess = tf.InteractiveSession()
self.sess.run(tf.global_variables_initializer())
# Load the model
facenet.load_model(model_path)
# Get input and output tensors
self.images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
self.tf_embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
self.phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
def get_embedding(self,images):
feed_dict = {self.images_placeholder: images, self.phase_train_placeholder: False}
embedding = self.sess.run(self.tf_embeddings, feed_dict=feed_dict)
return embedding
def free(self):
self.sess.close()
五、产生数据库
既然是人脸识别,数据库肯定要有已知人脸的数据库,不然怎么知道被检测的人脸是哪位大神,所以先制作人脸数据库。
(1)制作人脸数据图库:
把相关大神的人像收集放在dataset/images文件夹下:
特别说明:
- 这里只收集了4张胡歌和4张周杰伦的单人照片,
- 注意制作人脸数据图库时,所使用的照片必须是单人照片!!!
- 若需要新增图库,只需在dataset/images下,新建一个文件夹,如,新增“xietingfeng”(谢霆锋)的文件夹,然后把谢霆锋的单人照片放在里面即可,图片名称可以是任意
- 函数image_list,names_list=file_processing.gen_files_labels(images_dir,postfix='jpg')可以获取目录images_dir下所有文件,包括子目录下的所有文件路径(image_list),其中names_list就是子目录的文件名,一般子目录作为样本的标签。
然后使用下面的代码,进行人脸检测,把人脸都crop下来,并保存在项目dataset\emb_face中,这些emb_face人脸数据图库将用于生成embedding数据库
def create_face(images_dir, out_face_dir):
'''
生成人脸数据图库,保存在out_face_dir中,这些数据库将用于生成embedding数据库
:param images_dir:
:param out_face_dir:
:return:
'''
# image_list=file_processing.get_files_list(images_dir, postfix='jpg')
image_list,names_list=file_processing.gen_files_labels(images_dir,postfix='jpg')
face_detect=face_recognition.Facedetection()
for image_path ,name in zip(image_list,names_list):
image=image_processing.read_image(image_path, resize_height=0, resize_width=0, normalization=False)
# 获取 判断标识 bounding_box crop_image
bounding_box, points = face_detect.detect_face(image)
bounding_box = bounding_box[:,0:4].astype(int)
bounding_box=bounding_box[0,:]
print("face box:{}".format(bounding_box))
face_image = image_processing.crop_image(image,bounding_box)
# image_processing.show_image("face", face_image)
# image_processing.show_image_box("face",image,bounding_box)
out_path=os.path.join(out_face_dir,name)
face_image=image_processing.resize_image(face_image, resize_height, resize_width)
if not os.path.exists(out_path):
os.mkdir(out_path)
basename=os.path.basename(image_path)
out_path=os.path.join(out_path,basename)
image_processing.save_image(out_path,face_image)
# cv2.waitKey(0)
(2)生成embedding数据库
有了人脸数据图库,就可以生成embedding数据库(人脸特征),后面待检测识别的人脸,只需要与这些embedding数据库(人脸特征)进行相似性比较,就可以识别人脸啦!!!!
def create_embedding(model_path, emb_face_dir, out_emb_path, out_filename):
'''
产生embedding数据库,保存在out_data_path中,这些embedding其实就是人脸的特征
:param model_path:
:param emb_face_dir:
:param out_emb_path:
:param out_filename:
:return:
'''
face_net = face_recognition.facenetEmbedding(model_path)
# image_list=file_processing.get_files_list(emb_face_dir,postfix='jpg')
image_list,names_list=file_processing.gen_files_labels(emb_face_dir,postfix='jpg')
images= image_processing.get_images(image_list,resize_height,resize_width,whiten=True)
compare_emb = face_net.get_embedding(images)
np.save(out_emb_path, compare_emb)
# 可以选择保存image_list或者names_list作为人脸的标签
# 测试时建议保存image_list,这样方便知道被检测人脸与哪一张图片相似
file_processing.write_data(out_filename, image_list, model='w')
六、人脸识别过程
(1)加载人脸数据库
把上面制作的,已知的人脸数据库加载进来:
def load_dataset(dataset_path,filename):
'''
加载人脸数据库
:param dataset_path: embedding.npy文件(faceEmbedding.npy)
:param filename: labels文件路径路径(name.txt)
:return:
'''
compare_emb=np.load(dataset_path)
names_list=file_processing.read_data(filename)
return compare_emb,names_list
(2)进行人脸检测
def face_recognition_image(model_path,dataset_path, filename,image_path):
# 加载数据库的数据
dataset_emb,names_list=load_dataset(dataset_path, filename)
# 初始化mtcnn人脸检测
face_detect=face_recognition.Facedetection()
# 初始化facenet
face_net=face_recognition.facenetEmbedding(model_path)
image=image_processing.read_image(image_path)
# 进行人脸检测,获得bounding_box
bounding_box, points = face_detect.detect_face(image)
bounding_box = bounding_box[:,0:4].astype(int)
# 获得人脸区域
face_images = image_processing.get_crop_images(image,bounding_box,resize_height,resize_width,whiten=True)
# image_processing.show_image("face", face_images[0,:,:,:])
pred_emb=face_net.get_embedding(face_images)
pred_name=compare_embadding(pred_emb, dataset_emb, names_list)
# 在图像上绘制人脸边框和识别的结果
bgr_image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
image_processing.cv_show_image_text("face_recognition", bgr_image,bounding_box,pred_name)
cv2.waitKey(0)
(3)人脸识别(比较相似性)
比较特征向量间的欧式距离
def compare_embadding(pred_emb, dataset_emb, names_list):
# 为bounding_box 匹配标签
pred_num = len(pred_emb)
dataset_num = len(dataset_emb)
pred_name = []
for i in range(pred_num):
dist_list = []
for j in range(dataset_num):
dist = np.sqrt(np.sum(np.square(np.subtract(pred_emb[i, :], dataset_emb[j, :]))))
dist_list.append(dist)
min_value = min(dist_list)
if (min_value > 0.65):
pred_name.append('unknow')
else:
pred_name.append(names_list[dist_list.index(min_value)])
return pred_name
(4)人脸识别效果
一切准备好了,开始run:
if __name__=='__main__':
model_path='models/20180408-102900'
dataset_path='dataset/emb/faceEmbedding.npy'
filename='dataset/emb/name.txt'
image_path='dataset/test_images/1.jpg'
face_recognition_image(model_path, dataset_path, filename,image_path)
说明:
为了方便测试, 这里以文件的路径作为人脸label,这样方便知道被检测人脸与哪一张图片最相似
./dataset/emb_face\huge\huge_1.jpg
./dataset/emb_face\huge\huge_2.jpg
./dataset/emb_face\huge\huge_3.jpg
./dataset/emb_face\huge\huge_4.jpg
./dataset/emb_face\zhoujielun\zhoujielun_1.jpg
./dataset/emb_face\zhoujielun\zhoujielun_2.jpg
./dataset/emb_face\zhoujielun\zhoujielun_3.jpg
./dataset/emb_face\zhoujielun\zhoujielun_4.jpg对应的label是:
huge
huge
huge
huge
zhoujielun
zhoujielun
zhoujielun
zhoujielun
七、参考资料:
【1】《如何应用MTCNN和FaceNet模型实现人脸检测及识别》http://www.uml.org.cn/ai/201806124.asp