0、序
0.1、FaceNet是啥?
由Google工程师Florian Schroff、Dmitry Kalenichenko、 James Philbin在2015年提出的人脸识别的模型,从现在来看的话有点上古时期的东西的感觉。但是FaceNet的首先改变了人脸验证和人脸识别系统需要分开训练两个神经网络的状况,将两者统一到一个框架之中。FaceNet主要用于验证人脸是否为同一个人,以及通过人脸识别得出这个人是谁等方面的应用。与之前人脸识别的方法大相庭径,FaceNet也是将人脸的特征数据映射到一个多维的空间,通过空间的欧式距离来获取人脸数据的相似度。欧式距离越小,两张脸的相似度就越大。FaceNet采用基于深度神经网络的图像映射方法和基于Triplets的Loss函数训练神经网络,网络直接输出128维的向量空间。如图1所示,深度学习框架在这里就像一个黑箱一样,我们只是需要给其输入Batch,这里的Batch指的是人脸图像的样本。Deep Architecture指的是采用一种深度学习架构,是Google在2014年提出的GoogLeNet网络,是一个22层的深度网络。
L2是指特征归一化,这样所有的图像的特征都会被映射到一个超球面上。Enbedding是经由前面所说的GoogLeNet深度学习网络输出的并经由L2归一化后生成的特征向量。在FaceNet的末端是其提出的新的损失函数Triplet Loss。FaceNet学习的过程就是使三元组中尽可能多的Anchor到Positive的距离小于Anchor到Negative的距离,即属于同一个人的尽可能近,不属于同一个人的尽可能远。
(i)Negatives: Regions that the Intersec-tion-over-Union (IoU) ratio less than 0.3 to any ground-truth faces;
(ii) Positives: IoU above 0.65 to a ground truth face;
(iii) Part faces: IoU between 0.4 and 0.65 to a ground truth face;
如上图所示,末端的Triplet Loss损失函数的目的是直接学习特征间的可分性。传统的Loss函数通常都是Single or Double Loss, 趋向于将有一类特征的人脸图像映射到同一个空间中,而Triplet Loss则是尝试将一个个体的人脸图像和其他人的图像进行分开。
即在每次训练中取3张人脸的图像,分别记为 x i a x_i^a xia、 x i p x_i^p xip、 x i n x_i^n xin,其中a和p为同一人的人脸图像,n为另一个人的人脸图像,三元组损失是直接对距离进行优化,可以解决人脸特征的表示问题但是需要非常大的数据量才能有很好的效果。
0.2、FaceNet的优点
FaceNet中没有限制需要人脸对齐。该模型的优点是只需要对图片进行很少量的处理(只需要裁剪脸部区域,而不需要额外预处理,比如3d对齐等),即可作为模型输入。同时,该模型在数据集上准确率非常高。FaceNet并没有像DeepFace和DeepID那样需要对齐。FaceNet得到最终表示后不用像DeepID那样需要再训练模型进行分类,直接计算距离就好了,简单而有效。
论文下载传送门:https://arxiv.org/pdf/1503.03832.pdf
1、环境准备
1.1、依赖库安装
sudo apt-get install libopenblas-dev gfortrandir
pip3 install scipy
pip3 install scikit-learn
pip3 install Pillow
在上述的相关依赖库中因为网络问题,scipy下载会出现Read timed out.等错误,强烈建议直接下载源文件包,进行本地安装。scipy源码传送门:https://github.com/scipy/scipy/releases,下载完成之后进行本地安装。
pip3 install scipy-1.2.3.tar.gz
1.2、clone FaceNet项目源码
git clone https://github.com/davidsandberg/facenet ./
进入FaceNet src目录,打开python环境,试着导入facenet运行库,查看是否能列出相关的内置函数。
1.3、下载预训练模型
资源传送门:https://github.com/davidsandberg/facenet
此处使用的预训练模型是:20180402-114759.zip,解压后放置到工作路径。
2、Coding
2.1、新建FaceNet model类,编写相关构造函数,推理函数,析构函数
import tensorflow.compat.v1 as tf
from scipy import misc
import facenet
import numpy as np
#facenet network class
class FaceNetModel():
def __init__(self, model_file):
tf.Graph().as_default()
facenet.load_model('20180402-114759.pb')
self.image_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
self.phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
self.embeddings_op = tf.get_default_graph().get_tensor_by_name("embeddings:0")
self.sess = tf.Session()
def get_descriptor(self, image):
image = misc.imresize(image, (160,160), interp = "bilinear")
image = facenet.prewhiten(image)
images = np.stack([image])
feed_dict = {
self.image_placeholder:images, self.phase_train_placeholder:False}
emb = self.sess.run(self.embeddings_op, feed_dict = feed_dict)
return emb[0,:]
def __del__(self):
self.sess.close()
def get_FaceNetModel(model_file):
return FaceNetModel(model_file)
2.2、Face register
import dlib
import cv2
import os
import facenet_model
import pickle
font = cv2.FONT_HERSHEY_SIMPLEX
detector = dlib.get_frontal_face_detector()
face_net = facenet_model.get_FaceNetModel('20180402-114759.pb')
imagePATH = '/home/colin/works/face_recognition_facenet/dataset/processed/Colin/'
def create_known(path):
global font
person_names = []
face_features = []
print("creating known face lib...")
for file_name in os.listdir(path):
if '.jpg' in file_name or '.png' in file_name:
#read imege and change to RGB
image = cv2.imread(path + file_name)
rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#detect face
dets = detector(rgb_img)
if(len(dets) == 0):
continue
det = dets[0]
face_img = rgb_img[det.top():det.bottom(), det.left():det.right()]
descriptor = face_net.get_descriptor(face_img)
person_name = file_name[:file_name.rfind('_')]
person_names.append(person_name)
face_features.append(descriptor)
print('Appending + '+person_name+'...')
with open('train.pkl', 'wb') as f:
pickle.dump(person_names, f)
pickle.dump(face_features, f)
print('Face Library Created!')
#return person_names, face_features
if __name__ == '__main__':
create_known(imagePATH)
2.3、Face recognition
import cv2
import os
import time
import numpy as np
import facenet_model
import dlib
import pickle
import image_shop
################################ Global variable ######################################
person_names = []
face_features = []
imagePATH = '/home/colin/works/face_recognition_facenet/dataset/processed/Colin/'
detector = dlib.get_frontal_face_detector()
face_net = facenet_model.get_FaceNetModel('20180402-114759.pb')
########################################################################################
# 640 480 320 240
def gstreamer_pipeline(
capture_width=320,
capture_height=240,
display_width=320,
display_height=240,
framerate=30,
flip_method=0,
):
return (
"nvarguscamerasrc ! "
"video/x-raw(memory:NVMM), "
"width=(int)%d, height=(int)%d, "
"format=(string)NV12, framerate=(fraction)%d/1 ! "
"nvvidconv flip-method=%d ! "
"video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! "
"videoconvert ! "
"video/x-raw, format=(string)BGR ! appsink"
% (
capture_width,
capture_height,
framerate,
flip_method,
display_width,
display_height,
)
)
def train_data_load():
global person_names, face_features
with open('train.pkl','rb') as f:
person_names=pickle.load(f)
face_features=pickle.load(f)
def facenet_recognition(image):
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
dets = detector(rgb_image)
#dets = detector(gray_image, 2)
for det in dets:
if(det.top() < 0) or (det.bottom() < 0) or (det.left() < 0) or (det.right() < 0):
continue
#get the face area
face_img = rgb_image[det.top():det.bottom(), det.left():det.right()]
#get the face descriptor
descriptor = face_net.get_descriptor(face_img)
min_dist = 0.7 #1
person_name = 'unknown'
for i in range(len(face_features)):
dist = np.linalg.norm(descriptor-face_features[i])
print('dist:', dist)
if dist < min_dist:
min_dist = dist
person_name = person_names[i]
cv2.rectangle(image, (det.left(), det.top()+10), (det.right(), det.bottom()), (0, 255, 0), 2)
cv2.putText(image, person_name , (det.left(),det.top()), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,0,0), 2, cv2.LINE_AA)
image = image_shop.mark_add(det.left(), det.right(), det.top(), det.bottom(), image)
return image
def face_recognition_livevideo(window_name, camera_idx):
cv2.namedWindow(window_name)
#CSI Camera for get pipeline
cap = cv2.VideoCapture(gstreamer_pipeline(flip_method=camera_idx), cv2.CAP_GSTREAMER)
while cap.isOpened():
ok, frame = cap.read() #read 1 frame
if not ok:
break
resImage = facenet_recognition(frame)
#display
cv2.imshow(window_name, resImage)
c = cv2.waitKey(1)
if c & 0xFF == ord('q'):
break
#close
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
train_data_load()
face_recognition_livevideo('Find Face', 0)
3、Demo效果
参考附录
1)《基于NVIDIA Jetson平台的人工智能实例开发入门》