使用 dlib 进行人脸识别

「这是我参与2022首次更文挑战的第32天,活动详情查看:2022首次更文挑战

使用 dlib 进行人脸识别

Dlib 提供了基于深度学习的高性能人脸识别算法,该模型对户外数据集中标记人脸的识别准确率可以达到 99.38%。该算法基于 ResNet-34 网络实现,使用 300 万张人脸进行训练,预训练的模型文件 dlib_face_recognition_resnet_model_v1.dat 下载后就可以直接用于前向计算。

网络以生成 128 维 (128D) 描述符的方式进行训练,用于量化人脸。训练使用三元组执行,单个三元组训练数据由三个图像组成,其中两个对应于同一个人。网络为每张图像生成 128D 描述符,三元组损失函数对此进行了量化,尝试将同一个人的两个图像的 128D 描述符相距更近,同时将不同人的两个图像的 128D 描述符相距更远。

这个过程对数千个不同人的数百万张图像重复数百万次,最后,它能够为每个人生成一个 128D 描述符,最终的 128D 描述符是能够很好的对人脸进行编码:

  • 同一个人的两幅图像生成的 128D 描述符彼此非常相似
  • 不同人的两张图像生成的 128D 描述符差别很大

因此,利用 dlib 函数,我们可以使用预训练模型将人脸映射到 128D 描述符。然后使用这些特征向量来进行人脸识别。

计算 128D 描述符用于量化人脸的过程很简单:

# 使用 dlib 库加载特征点预测器、人脸编码和人脸检测器
pose_predictor_5_point = dlib.shape_predictor("shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
detector = dlib.get_frontal_face_detector()

def face_encodings(face_image, number_of_times_to_upsample=1, num_jitters=1):
    """返回图像中每个人脸的 128D 描述符"""

    # 检测人脸
    face_locations = detector(face_image, number_of_times_to_upsample)
    # 检测面部特征点
    raw_landmarks = [pose_predictor_5_point(face_image, face_location) for face_location in face_locations]
    # 使用每个检测到的特征点计算每个检测到的人脸的编码
    return [np.array(face_encoder.compute_face_descriptor(face_image, raw_landmark_set, num_jitters)) for
            raw_landmark_set in raw_landmarks]

# 加载图像并转换为 RGB 模式
image = cv2.imread("jared_1.jpg")
rgb = image[:, :, ::-1]

# 计算图像中每个人脸的编码
encodings = face_encodings(rgb)
# 打印第一个编码的特征
print(encodings[0])
复制代码

如上所示,关键是调用 dlibface_encoder.compute_face_descriptor() 函数,使用检测到的每个人脸的特征点计算每个检测到的人脸的 128D 编码,其中 num_jitters 参数用于设置每个人脸随机抖动的次数,返回值为每次计算的平均 128D 描述符,输出的 128D 描述符如下:

[-0.09235165  0.11607055  0.03648872 -0.08326858 -0.12627071 -0.01334486 -0.11334236 -0.10083835  0.20534235 -0.1636433   0.16874117 -0.05276754 -0.17746128 -0.05377002 -0.02731067  0.24751744 -0.22732623 -0.20258367 -0.03421091 -0.00150665  0.05875423 0.03020219  0.03901095  0.03496565 -0.15658092 -0.34250638 -0.08725534 -0.06245319 -0.04688681 -0.04861078 -0.07620423  0.05013577 -0.18563135 -0.04075277  0.05248301  0.09195475 -0.00887688 -0.1192601   0.18633801  0.00056917 -0.29226956  0.01442468  0.09583923  0.19053322  0.15580602 -0.04580544  0.01866002 -0.15243134  0.13535264 -0.17270051  0.03029358  0.16308595  0.04719323  0.08862312  0.01600051 -0.112983    0.06787978  0.17171389 -0.09536573 -0.02140218  0.11402114 -0.04710582 -0.01966342 -0.0705786   0.21773803  0.12153016 -0.08498291 -0.24783675  0.06667361 -0.08091511 -0.11054871  0.08837797 -0.17216064 -0.18642734 -0.27270097 -0.03300989  0.31748736  0.06824204 -0.16750985  0.0599058  -0.00497202 -0.02882685  0.07890167  0.19422579 -0.02771271  0.05871597 -0.06130363  0.04929798  0.27234387 -0.04948008 -0.00844343  0.22556995  0.00912007  0.07115038  0.01273906  0.03535268 -0.05074561  0.05441948 -0.13103089 -0.00421767  0.07432865  0.0025964 -0.06208063  0.12578207 -0.16597968  0.09258381 -0.02716768  0.02978029 -0.00216489 -0.01805471 -0.04702468 -0.05231683  0.11994087 -0.16787212  0.17464222  0.16930985  0.05848085  0.09450675  0.11558257  0.0659898 -0.00265438 -0.01509937 -0.22738113  0.01624682  0.13056616 -0.04214386  0.06433617  0.00774699]
复制代码

获取到检测到的人脸编码后,下一步就是进行人脸识别。

使用 128D 描述符计算的某种距离度量可以用于执行人脸识别,如果两个人脸描述符向量之间的欧几里得距离小于 0.6 (欧几里得距离可以使用 numpy.linalg.norm() 计算),则可以认为它们属于同一个人;否则,他们是不同的人。

接下来,我们使用 5 张已知图像与另 1 张测试图像进行比较。为了比较人脸,我们需要编写两个函数:compare_faces()compare_faces_ordered()

compare_faces() 函数返回已知人脸编码与待识别人脸间的距离:

def compare_faces(encodings, encoding_to_check):
    return list(np.linalg.norm(encodings - encoding_to_check, axis=1))
复制代码

compare_faces_ordered() 函数返回排序后的已知人脸编码与待识别人脸间的距离和相应的名称:

def compare_faces_ordered(encodings, face_names, encoding_to_check):
    distances = list(np.linalg.norm(encodings - encoding_to_check, axis=1))
    return zip(*sorted(zip(distances, face_names)))
复制代码

接下来,将 5 个已标记图像与 1 个未标记图像进行比较,第一步是加载所有图像并转换为 RGB 格式:

# 加载所有图像并转换为 RGB 格式
known_image_1 = cv2.imread("小新_1.png")
known_image_2 = cv2.imread("小甜_1.png")
known_image_3 = cv2.imread("小甜_2.png")
known_image_4 = cv2.imread("小甜_3.png")
known_image_5 = cv2.imread("小新_2.png")
unknown_image = cv2.imread("test.png")
known_image_1 = known_image_1[:, :, ::-1]
known_image_2 = known_image_2[:, :, ::-1]
known_image_3 = known_image_3[:, :, ::-1]
known_image_4 = known_image_4[:, :, ::-1]
known_image_5 = known_image_5[:, :, ::-1]
unknown_image = unknown_image[:, :, ::-1]
# 标记人脸
names = ["小新_1.png", "小甜_1.png", "小甜_2.png", "小甜_3.png", "小新_2.png"]
复制代码

下一步是计算每个图像的 128D 编码:

# 计算每个图像的 128D 编码
known_image_1_encoding = face_encodings(known_image_1)[0]
known_image_2_encoding = face_encodings(known_image_2)[0]
known_image_3_encoding = face_encodings(known_image_3)[0]
known_image_4_encoding = face_encodings(known_image_4)[0]
known_image_5_encoding = face_encodings(known_image_5)[0]
known_encodings = [known_image_1_encoding, known_image_2_encoding, known_image_3_encoding, known_image_4_encoding, known_image_5_encoding]
unknown_encoding = face_encodings(unknown_image)[0]
复制代码

最后,可以使用 compare_faces_ordered() 函数比较与识别人脸:

computed_distances_ordered, ordered_names = compare_faces_ordered(known_encodings, names, unknown_encoding)
# 打印返回信息
print(computed_distances)
print(computed_distances_ordered)
print(ordered_names)
复制代码

打印的返回信息如下:

(0.26459402873041915, 0.2728113455078627, 0.2945116087102425, 0.42567525558949304, 0.42899966791571725)
('小甜', '小甜', '小甜', '小新', '小新')
复制代码

通过上示的打印信息,可以得出结论待识别的人脸属于小甜,而小新的人脸编码信息与待识别人脸的编码信息距离较远,表示不是同一个人:

使用 dlib 进行人脸识别

猜你喜欢

转载自juejin.im/post/7065888066379972638