人脸匹配(face matching)

以下是人脸匹配(face matching)的实验报告,实验中采用ldlib,facenet,arcface三种算法,简单对比了三种算法的效果,源代码见文章末尾

一.问题分析

1.问题描述

人脸识别是基于人的脸部特征信息进行身份识别的一种生物识别技术。人脸匹配就是给定任意两张人脸图像,判断这两张人脸图像中的人脸是否属于同一个人。

在人脸匹配中存在着诸多难点,比如,人脸表情复杂;人脸随年龄而改变;人脸有易变化的附加物;人脸特征遮掩;人脸图像畸变。

 

2.数据集分析

实验中给出的数据集是收集全班人的人脸照片,每个人两张照片,一共有80张照片。对这80张照片进行随机分配为40组图片,每组图片包含任意两个人的人脸照片。

用实验中设计的人脸匹配算法来判断这40组图片对是否属于同一张人脸。以此来判断算法的优劣性。

3.现有方法

现有的进行人脸匹配的方式有很多种,大概都可以分为三个步骤,首先就是识别出每张图像中的人脸,找到人脸所在的位置,接着对人脸提取特征,最后对比不同人脸的特征之间的距离,如果距离小于某个阈值则认为是同一张人脸,否则就认为是不同的人脸。

实验中我们尝试了dlib,facenet,arcface三种方式,经过测试发现arcface的效果最好,所以实验中最后选择了arcface作为人脸匹配的算法。

 

二.方法总结

2.1 dlib

Dlib是一个机器学习库,里面集成了许多优质的算法,其中就包括人脸识别,特征提取等算法。利用dlib进行人脸匹配的算法流程图如下图2.1所示。

                                                                       图2.1 dlib人脸匹配流程图

从图2.1可以看出整个步骤也是分为三步:

(1)识别人脸,主要原理就是提取图片的HOG特征,SVM用于对特征进行分类找到最后的人脸位置。

                                                                                      图2.2 dlib检测人脸图

(2)提取人脸特征点。在检测出人脸之后,在人脸的区域中提取68个关键点作为特征点。

                                                                               图2.3 dlib提取人脸关键点图

(3)提取特征向量。在得到每张人脸的特征点之后提取相应的特征向量,这里得到的一个128维的特征向量。

(4)计算特征向量之间的距离。在得到了每张图片人脸的特征向量之后,计算特征向量之间的欧式距离,如果该距离小于指定的阈值则认为是同一张人脸,否则就认为是不同的人脸。经过测试实验中设置的阈值为0.42。

 

2.2 facenet

Facenet的基本思想就是利用相同人脸在不同角度等姿态的照片下有高内聚性,不同人脸有低耦合性,提出使用 cnn + triplet mining 方法。

利用facenet进行人脸匹配的步骤如下:

(1)用MTCCN进行人脸检测。MTCNN利用三个CNN级联的方式对人脸识别任务实现了从粗到细的处理。主要结构图如下图2.4所示。

                                                                                  图2.4 MTCNN结构图

主要是先通过P-Net来生成若干候选框,接着用bounding box regression来矫正,用NMS来抑制重叠框。用R-Net进一步筛选掉那些错误的box。O-Net用来输出最终的人脸框还有特征点的位置。

(2)利用facenet输出人脸的特征向量。Facenet通过将人脸映射到一个多维空间,然后用triplets的loss做到将相同人脸的距离拉到尽可能小,不同人脸之间的空间距离尽可能的大。主要流程图如下图2.5所示。

                                                                            图2.5 facenet算法流程图

图2.5中的Batch是MTCNN检测出来的人脸图片。接着经过facenet的网络结构,一般采用Googlenet,将网络输出用L2进行归一化,得到人脸的特征表示,最后用三元组即Triplet Loss计算损失函数。

                                                                                    图2.6 Triplet原理图

如图2.6所示,facenet就是将人脸先embedding到一个d维度的欧几里得空间 ,在该向量空间中,希望将单个图像的和该个体的其他图像距离近,和其他个体的图像的距离远。即最小化如下公式的损失函数

  

(3)计算特征向量之间的距离。在用facenet得到人脸的特征向量之后,用欧式距离计算出特征向量之间的距离,如果距离小于阈值则认为值属于同一张人脸,否则就不是。

2.3 arcface

Arcface的主要思想就是使用角边距最大化类间距离、最小化类内距离。使用arc-cosine函数计算特征与目标权重间的角度,然后,为目标角度添加一个角余量惩罚m,通过cosine函数重新获得目标logit最后,使用固定的特征范数重新确定所有的逻辑,随后完全按照softmax loss处理。主要流程如图2.7所示。

 

                                                                                图2.7 archface原理图

Arcface主要是提出一种新的用于人脸识别的损失函数:additive angular margin loss,直接在角度空间(angular space)中最大化分类界限。

L1为基于softmax的损失函数。

进行权重归一化后,使L1 loss只跟特征向量和权重之间的角度有关。


最后添加角余量惩罚m,获得ArcFace损失。


Arcface用来做人脸匹配的步骤基本和上述的facenet类似,步骤如下:

(1)利用MTCNN检测出人脸

(2)利用Arcface输出人脸特征向量

(3)比较特征向量之间的距离,小于指定阈值则认为是同一张人脸。否则就不是。

 

三.实验

由于在做实验的时候,测试图片没有公开,我们仅仅收集了本小组的图片,小组一共六个人,每个人两张图片,一共12张图片。

我们将这12张图片两两进行人脸匹配,从而分析算法的效果。下图是使用三种算法得到的实验结果图。其中dlib,facenet,arcface设置的距离阈值分别是0.4,0.75,1.1,阈值的设置为经验值。

                                                                                     图3.1 实验结果图

图3.1的每个矩阵是12*12,第i行j列的值表示第i张图像与第j张图片进行人俩匹配的结果,如果值为1表示算法认为是同一张人脸,值为0表示不是同一张人脸。从图3.1可以看出在dlib算法检测结果中用红圈框处的区域位置,即(11,12),(12,11)应该是同一张人脸,但是dlib却误检测为不同的人脸。在facenet的实验结果中,同样可以看到在红圈框处的位置(1,5),(1,6),(2,5),(5,1),(5,2),(6,1)处,误将不同的人脸认为是同一张人脸。只有arcface做到了100%的准确率,证明了arcface的优越性。

 

四.总结

在这次人脸匹配的实验中,我们一共尝试了三种不同的人脸匹配的算法,进一步对比了不同算法的效果,从而选出效果最好的算法用于最终的人脸匹配。实验中也发现对于最后人脸相似性的阈值设置十分依赖于经验,在不同的数据集上面的阈值都不尽相同。

在计算人脸特征向量之间的距离的时候,实验中都是采用的欧式距离,没有深究其他距离的计算方式,可能其他的方式会带来更好的效果。另外实验中采用的测试数据集非常少,这样导致最后得到的实验结果可能不具有很强的说服力,后续需要进一步进行测试。

 

源代码

dlib:

要求的文件结构

#dlib人脸匹配源码
import sys
import dlib
import numpy as np
import cv2
import os
import glob

def compare_feature(a, b):
    diff = 0
    for i in range(len(a)):
        diff += (a[i] - b[i])**2
    diff = np.sqrt(diff)
   # print(diff)
    # return float(diff)
    if (diff < 0.42): #阈值设置为0.42
        return True
    else:
        return False
    #     print("It's the same person")
    # else:
    #     print("It's not the same person")

def extract_feature(img_path):
    img = cv2.imread(img_path, cv2.IMREAD_COLOR)
    b, g, r = cv2.split(img)
    img2 = cv2.merge([r, g, b])
    dets = detector(img, 1)  # 人脸标定
    for index, face in enumerate(dets): #一张图片出现多张人脸?
        # face = face.rect
        # print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(), face.bottom()))
        cv2.rectangle(img, (face.left(), face.top()), (face.right(), face.bottom()), (0, 255, 0), 3)
        shape = shape_predictor(img2, face)   # 提取68个特征点
        for i, pt in enumerate(shape.parts()):
            pt_pos = (pt.x, pt.y)
            cv2.circle(img, pt_pos, 5, (255, 0, 0), 1)

        # cv2.imwrite(img_path+str(index)+'.jpg', img)
        # save_path = os.path.join(img_path[:img_path.rfind('/')], 'result/')
        save_path = os.path.join('./result/', img_path[2:img_path.rfind('/')])
        if not os.path.exists(save_path):
           os.makedirs(save_path)
        cv2.imwrite(os.path.join(save_path,img_path[img_path.rfind('/')+1:]), img)
        face_descriptor = face_rec_model.compute_face_descriptor(img2, shape)   # 计算人脸的128维的向量

    return face_descriptor

predictor_path = "shape_predictor_68_face_landmarks.dat"
face_rec_model_path = "dlib_face_recognition_resnet_model_v1.dat"
detector = dlib.get_frontal_face_detector() #检测人脸
# detector = dlib.cnn_face_detection_model_v1('mmod_human_face_detector.dat') #CNN方式,用的时候需要加face = face.rect
shape_predictor = dlib.shape_predictor(predictor_path) #提取关键点
face_rec_model = dlib.face_recognition_model_v1(face_rec_model_path) #根据关键点输出人脸特征向量

root_path = './6th'
root_path_dir = sorted(list(map(int, os.listdir(root_path))))
root_path_dir = list(map(str, root_path_dir))
i = 1
result = open('result.txt', 'w')
for imgs_path in root_path_dir:
    img_pair_path = sorted(glob.glob(os.path.join(root_path,imgs_path, "*")))
    img1_vector = extract_feature(img_pair_path[0])
    img2_vector = extract_feature(img_pair_path[1])
    print(imgs_path, end=':')
    r = compare_feature(img1_vector,img2_vector)
    print(r)
    if r:
        result.write(str(1)+'\n') #将结果输入到文件中
    else:
        result.write('0'+'\n')

facenet:https://github.com/davidsandberg/facenet 直接运行其中的https://github.com/davidsandberg/facenet/blob/master/src/compare.py即可

arcface:https://github.com/deepinsight/insightface

参考:

https://blog.csdn.net/tinyzhao/article/details/53236191

https://zhuanlan.zhihu.com/p/76541084

猜你喜欢

转载自blog.csdn.net/breeze_blows/article/details/103669084