1. 程式人生 > >利用MTCNN和facenet實現人臉檢測和人臉識別

利用MTCNN和facenet實現人臉檢測和人臉識別

利用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


目錄

利用MTCNN和faceNet實現人臉檢測和人臉識別

一、專案結構:

二、實現流程

三、MTCNN人臉檢測

四、faceNet人臉識別

五、產生資料庫

(1)製作人臉資料相簿:

(2)生成embedding資料庫

六、人臉識別過程

(1)載入人臉資料庫

(2)進行人臉檢測

(3)人臉識別(比較相似性)

(4)人臉識別效果

七、參考資料:


一、專案結構:

開啟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資料夾下:

 

特別說明:

  1. 這裡只收集了4張胡歌和4張周杰倫的單人照片,
  2. 注意製作人臉資料相簿時,所使用的照片必須是單人照片!!!
  3. 若需要新增相簿,只需在dataset/images下,新建一個資料夾,如,新增“xietingfeng”(謝霆鋒)的資料夾,然後把謝霆鋒的單人照片放在裡面即可,圖片名稱可以是任意
  4. 函式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