Jetson nano实战系列:基于dlib+FaceNet实现人脸识别

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平台的人工智能实例开发入门》

猜你喜欢

转载自blog.csdn.net/qq_33475105/article/details/111994319
今日推荐