OpenCV-Python实战(17)——人脸识别详解

0. 前言

人脸处理是人工智能中的一个热门话题,人脸处理可以使用计算机视觉算法从人脸中自动提取大量信息,例如身份、意图和情感。随着计算机视觉、机器学习和深度学习的发展,人脸识别已经成为一个热门话题。在本文中,我们介绍OpenCV提供的与人脸识别相关的函数,同时还将探索一些用于人脸识别的深度学习方法,这些方法可以轻松集成到计算机视觉项目中以实现高精度的人脸识别。

1. 人脸识别简介

人脸识别具有广泛的应用前景,包括犯罪预防、智能监视以及社交网络。但自动人脸识别同样面临多种挑战,例如遮挡、装束变化、表情、年龄老化等。继在对象识别方面取得成功之后,CNN 已被广泛用于人脸识别。

2. 使用 OpenCV 进行人脸识别

OpenCV提供了三种不同的实现来执行人脸识别:

  • Eigenfaces
  • Fisherfaces
  • Local Binary Patterns Histograms (LBPH)

这些实现以不同的方式执行人脸识别。但是,我们只需要更改识别器的创建方式,以便能够独立于其内部算法使用它们:

# 创建识别器
face_recognizer = cv2.face.LBPHFaceRecognizer_create()
face_recognizer = cv2.face.EigenFaceRecognizer_create()
face_recognizer = cv2.face.FisherFaceRecognizer_create()

一旦创建,其将独立于特定内部算法,均可以分别使用方法train()和predict()来执行人脸识别系统的训练和测试,使用这些方法的方式与创建的识别器无关。
因此,可以很容易对比这三种识别器,然后针对特定任务选择性能最佳的识别器。但在涉及不同环境和光照条件的户外识别图像时,LBPH通常比另外两种方法具有更好的性能。此外,LBPH人脸识别器支持update()函数,可以在该方法中根据新数据更新人脸识别器,但Eigenfaces和Fisherfaces方法并不支持update()函数。
为了训练识别器,应该调用train()方法:

face_recognizer.train(faces, labels)

cv2.face_FaceRecognizer.train(src, labels)方法训练具体的人脸识别器,其中src表示图像(人脸)训练集,参数labels为训练集中的每张图像对应的标签。
要识别新面孔,应调用predict()方法:

label, confidence = face_recognizer.predict(face)

cv2.face_FaceRecognizer.predict(src)方法通过输出预测的标签和相应置信度来输出(预测)对新src图像的识别结果。
OpenCV还提供了write()和read()方法用于保存创建的模型和加载之前创建的模型。对于这两种方法,文件名参数设置要保存或加载的模型的名称:

cv2.face_FaceRecognizer.write(filename)
cv2.face_FaceRecognizer.read(filename)

如果使用的是LBPH人脸识别器,可以使用update()方法进行更新:

cv2.face_FaceRecognizer.update(src, labels)

其中,src和labels设置了用于更新LBPH识别器的新训练数据集。

2.1 使用 OpenCV 进行人脸识别流程示例

接下来,通过一个脚本来熟悉下使用OpenCV进行人脸识别的流程:

import cv2
import numpy as np
import matplotlib.pyplot as plt
import glob

face_recognizer = cv2.face.LBPHFaceRecognizer_create()

imgs = glob.glob('img/*.png')
faces = []
rects = []

def detect_img(img, faces, rects):
    img = cv2.imread(img)
    (h, w) = img.shape[:2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel")
    blob = cv2.dnn.blobFromImage(img, 1.0, (300, 300), [104., 117., 123.], False, False)
    net.setInput(blob)
    detections = net.forward()
    for i in range(0, detections.shape[2]):
        # 获取当前检测结果的置信度
        confidence = detections[0, 0, i, 2]
        # 如果置信大于最小置信度,则将其可视化
        if confidence > 0.7:
            # 获取当前检测结果的坐标
            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
            (startX, startY, endX, endY) = box.astype('int')
            face_w = endX - startX
            face_h = endY - startY
            rects.append([startX, startY, face_w, face_h])
            faces.append(gray[startY:startY+face_h, startX:startX+face_w])
    return faces, rects
# 构造训练数据集
for img in imgs:
    faces, rects = detect_img(img, faces, rects)
# 根据实际情况构造标签数组
labels = [0] * len(faces)
# 训练识别器模型
face_recognizer.train(faces, np.array(labels))
# 加载测试图像并进行测试
img_test = cv2.imread('test.png')
face_test, rect_test = [], []
face_test, rect_test =  detect_img(img, face_test, rect_test)

label, confidence = face_recognizer.predict(face_test[0])
print(label, confidence)

3. 使用 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])

如上所示,关键是调用dlib的face_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 进行人脸识别

4. 使用 face_recognition 进行人脸识别

使用face_recognition进行人脸识别内部使用了dlib函数对人脸进行编码并计算已编码人脸的距离。因此,无需自己编写face_encodings()和compare_faces()函数,只需直接调用face_recognition库中已经封装好的相应函数。
接下来,首先使用face_recognition.face_encodings()函数创建128D描述符,然后使用face_recognition.compare_faces()比较人脸:

# 加载图像
known_image_1 = face_recognition.load_image_file("小美_1.png")
known_image_2 = face_recognition.load_image_file("小美_2.png")
known_image_3 = face_recognition.load_image_file("小美_3.png")
known_image_4 = face_recognition.load_image_file("小可_1.png")
# 为每个图像创建标签
names = ["小美_1.png", "小美_2.png", "小美_3.png", "小可_1.png"]
# 加载待识别图像(用于与已加载的标记图像进行比较)
unknown_image = face_recognition.load_image_file("unrecognition.jpg")
# 将每张图片中的人脸编码为 128D 向量
known_image_1_encoding = face_recognition.face_encodings(known_image_1)[0]
known_image_2_encoding = face_recognition.face_encodings(known_image_2)[0]
known_image_3_encoding = face_recognition.face_encodings(known_image_3)[0]
known_image_4_encoding = face_recognition.face_encodings(known_image_4)[0]
known_encodings = [known_image_1_encoding, known_image_2_encoding,
known_image_3_encoding, known_image_4_encoding]
unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
# 人脸对比
results = face_recognition.compare_faces(known_encodings, unknown_encoding)
# 打印结果
print(results)

得到的结果是[True, True, True, False]。因此,前三个加载的图像 (“小美_1.png”, “小美_2.png” 和 “小美_3.png”) 与待识别图像 (“unrecognition.jpg”) 是同一个人,而第四个加载的图像 (“小可_1.png”) 被判定为待识别图像属于不同的人。

使用 face_recognition 进行人脸识别

概括

在本文中,我们介绍OpenCV提供的与人脸识别相关的函数,同时还将探索一些用于人脸识别的深度学习方法,主要包括dlib和face_recognition库,这些方法可以轻松集成到计算机视觉项目中以实现高精度的人脸识别。

系列链接

OpenCV-Python实战(1)——OpenCV简介与图像处理基础
OpenCV-Python实战(2)——图像与视频文件的处理
OpenCV-Python实战(3)——OpenCV中绘制图形与文本
OpenCV-Python实战(4)——OpenCV常见图像处理技术
OpenCV-Python实战(5)——OpenCV图像运算
OpenCV-Python实战(6)——OpenCV中的色彩空间和色彩映射
OpenCV-Python实战(7)——直方图详解
OpenCV-Python实战(8)——直方图均衡化
OpenCV-Python实战(9)——OpenCV用于图像分割的阈值技术
OpenCV-Python实战(10)——OpenCV轮廓检测
OpenCV-Python实战(11)——OpenCV轮廓检测相关应用
OpenCV-Python实战(12)——一文详解AR增强现实
OpenCV-Python实战(13)——OpenCV与机器学习的碰撞
OpenCV-Python实战(14)——人脸检测详解
OpenCV-Python实战(15)——面部特征点检测详解
OpenCV-Python实战(16)——人脸追踪详解

版权声明:本文为博主盼小辉丶原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/LOVEmy134611/article/details/120070030

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(2)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年2月18日 下午3:35
下一篇 2022年2月18日 下午4:01

相关推荐