【Dlib人脸识别】1. Dlib人脸检测的基本原理

Dlib中,人脸识别的基本思路为:

  • 计算已知图片中所有人脸对应的特征向量;
  • 计算要识别的未知图片中所有人脸对应的特征向量;
  • 计算人脸之间的欧式距离;
  • 如果两张人脸之间的欧式距离小于设定的阈值,则认为是同一个人,否则认为不是同一个人

【 1. 计算特征向量 】

在人脸特征点检测中,我们学会了如何获取人脸的特征点。但是特征点只是用于标识人脸关键点的坐标而已,如果想要实现人脸识别,那么必须将特征点转换为特征向量。

1. 加载人脸识别模型

链接:dlib_face_recognition_resnet_model_v1.dat
提取码:b8zu

首先,我们得通过dlib.face_recognition_model_v1() 加载人脸识别的模型:

dlib.face_recognition_model_v1(人脸识别模型路径)
import dlib
import cv2
#人脸识别模型的路径
face_rec_model_path = 'dlib_face_recognition_resnet_model_v1.dat'
# 加载人脸识别模型
facerec = dlib.face_recognition_model_v1(face_rec_model_path)

2. 计算特征向量

在加载完模型之后,我们需要先使用前面学到的知识,先检测特征点:

import cv2

# 加载人脸识别模型
face_rec_model_path = 'dlib_face_recognition_resnet_model_v1.dat'
facerec = dlib.face_recognition_model_v1(face_rec_model_path)
# 加载特征点识别模型
predictor_path = "shape_predictor_5_face_landmarks.dat"
predictor = dlib.shape_predictor(predictor_path)

# 读取图片
img_path = "step1/image/face.jpg"
img = cv2.imread(img_path)
# 转换为灰阶图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 正向人脸检测器将图像
detector = dlib.get_frontal_face_detector()


# 使用人脸识别模型来检测图像中的人脸
faces = detector(gray, 1)
# 使用特征点识别模型来检测人脸中的特征
for i, face in enumerate(faces):
    # 获取人脸特征点
    shape = predictor(img, face)

通过上面的代码,我们检测到人脸的特征点,并将它们保存在shape中。下面我们通过facerec.compute_face_descriptor()来计算特征向量,它的函数声明如下:

facerec.compute_face_descriptor(图片对象, 特征点)

具体代码如下:

face_descriptor = facerec.compute_face_descriptor(img,shape)

这样,我们就计算出了人脸特征向量。

【 2. 计算欧氏距离 】

  • 欧氏距离(也称欧几里得距离)是一个常用的的距离度量,它指的是在空间中两个向量(点)之间的直线距离。
    欧氏距离越小,说明两个向量越接近,也就是两个向量差异越小。

  • 假设在两个向量分别为 x(x1,x2,…,xm) 和 y(y1,y2,…,ym),则这两个向量的欧氏距离的计算公式为:
    ( x 1 − y 1 ) 2 + ( x 2 − y 2 ) 2 + . . + ( x m − y m ) 2 ( x 1 − y 1 ) \sqrt{(x_1-y_1)^2+(x_2-y_2)^2+..+(x_m-y_m)^2}\sqrt{(x_1−y_1)} (x1y1)2+(x2y2)2+..+(xmym)2 (x1y1)

  • 在Python中可以通过长度为 m 的列表来表示维数为 m 的向量。
    例如x为(1,2,3,4,5),那么它可以表示为x=[1,2,3,4,5]。

  • 在Python中计算两个长度相同的列表的欧氏距离:

import numpy as np 	#	导入numpy库

x = [1,2,3,4,5] 	# 声明两个长度相同的列表
y = [3,4,5,2,1]

x_array = np.array(x)	# 将列表转换为numpy数组
y_array = np.array(y)

distance = np.linalg.norm(x_array-y_array) # 计算x_array与y_array之间的欧氏距离

【 3. 识别人脸 】

上面我们提到,两个向量的欧氏距离越小,说明两个向量越接近。将这个结论放在人脸识别中,可以得出以下结论:
两张人脸特征向量的欧氏距离越小,说明两个人越相似。当欧氏距离小于某一个值时,则可以认为他们是同一个人。
基于这个结论,我们可以实现人脸识别。

1. 准备数据

我们将事先准备的人脸图片作为本次识别的数据,并把图片分成两个部分。第一个部分为已知的图片,另一部分为待识别的图片。
已知图片:
【Dlib人脸识别】1. Dlib人脸检测的基本原理_第1张图片
待识别图片:
【Dlib人脸识别】1. Dlib人脸检测的基本原理_第2张图片
理想情况下,我们的人脸识别算法应该做到:如果待识别图片中的人出现在了已知图片中,那么应该正确地输出姓名,否则,就输出Unknow。

2. 计算所有已知图片中人脸的特征向量

因为需要获取所有已知图片中的特征向量,首先得获取到图片。
在这里我们借助glob模块下的glob()来获取图片:

glob(正则表达式)#	glob()返回匹配的文件列表,

glob()会返回匹配的文件列表,这样我们就可以获取到在known_image_path下所有以jpg结尾的的图片,如下:

import glob
glob.glob(os.path.join(known_image_path, "*.jpg"))

然后我们就可以来计算所有已知图片中人脸的特征向量,代码如下所示:

known_image_path = "step1/image/known_image"
# 声明descriptors,用于存放已知图片对应的人脸特征向量
descriptors = []
# 声明names,用于存放于人脸特征向量对应的名字。
names = ["TongDaWei","XiaYu","ZhangYiShan"] 
# 遍历known_image_path文件夹下所有以.jpg结尾的文件。
for f in glob.glob(os.path.join(known_image_path, "*.jpg")):
    img = dlib.load_rgb_image(f)
    # 1.人脸检测
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        # 2.关键点检测
        shape = predictor(img, d)
        # 3.计算特征向量(也成为人脸描述符)
        descriptor = facerec.compute_face_descriptor(img, shape)
        # 转换为numpy数组
        v = np.array(descriptor)
        descriptors.append(v)

3. 识别人脸

当欧氏距离小于某个阈值时,则可以认为他们是同一个人。那这个阈值应该设定为多少呢?

  • 原理:计算待识别的图片特征向量与已知图片特征向量的欧氏距离,如果欧氏距离小于阈值,则认为是同一张图片。
  • 在Dlib中,对于在LFW人脸数据集,当阈值设置为0.6时,的识别精度能达到99.38%。
  • 但是,当阈值设置为0.6时,对于小孩和亚洲人的识别率不是很高。故我们要识别的图片是亚洲人时,要将阈值适当地降低,在这里我们设置为0.4。

具体代码片段如下

tolerance = 0.4
current_name = "Unknow"
for i in range(len(descriptors)):
#假设当前带识别的图片特征为current
distance = np.linalg.norm(descriptors[i]-current)
if distance<tolerance:
#names用于存放于已知人脸特征向量对应的名字
current_name = names[i]
break
print("当前图片识别结果为:"+current_name)

【 4. 范例 】

# 0. 导入模块
import os
import dlib
import glob
import numpy as np
import cv2

# 1. 加载模型、图片

## 正向人脸检测器
detector = dlib.get_frontal_face_detector()
## 加载特征点提取模型
predictor_path = 'shape_predictor_68_face_landmarks.dat'
predictor = dlib.shape_predictor(predictor_path)
## 加载面部识别模型
face_rec_model_path = 'dlib_face_recognition_resnet_model_v1.dat'
facerec = dlib.face_recognition_model_v1(face_rec_model_path)
## 已知图片
known_image_path = 'know'
## 测试图片
test_image_path = "unknow"

# 2. 声明存放数据的变量

## 声明descriptors,用于存放已知图片对应的人脸特征向量
descriptors = []
## 声明names,用于存放于人脸特征向量对应的名字。
names = ["reba"]

# 3. 开始检测

## 遍历known_image_path文件夹下所有以.jpg结尾的文件。
for f in glob.glob(os.path.join(known_image_path, "*.jpg")):
    img = dlib.load_rgb_image(f)
    ## 使用 detector 检测器来检测图像中的人脸
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        ## 获取人脸特征点
        shape = predictor(img, d)
        ## 计算特征向量
        face_descriptor = facerec.compute_face_descriptor(img, shape)
        ## 特征向量转换为numpy array
        v = np.array(face_descriptor)
        ## 把此次数据存到人脸特征向量列表里面
        descriptors.append(v)

# 4. 进行判断

## 遍历test_image_path文件夹下所有以.jpg结尾的文件。
for f in glob.glob(os.path.join(test_image_path, "*.jpg")):
    img = dlib.load_rgb_image(f)
    ## 使用 detector 检测器来检测图像中的人脸
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        ## 获取人脸特征点
        shape = predictor(img, d)
        ## 计算特征向量
        face_descriptor = facerec.compute_face_descriptor(img, shape)
        ## 将当前待判断的图片特征向量转化为 current
        current = np.array(face_descriptor)

        ## 计算欧式距离,识别人脸
        ### 设置阈值
        tolerance = 0.1
        ### 设置该图片名字初值为:Unknow
        current_name = "Unknow name"
        ### 对这个存放着已知图片特征向量的列表descriptors[]遍历
        for i in range(len(descriptors)):
            ### 计算欧氏距离
            distance = np.linalg.norm(descriptors[i] - current)
            ### 对欧氏距离判断
            if distance < tolerance:
                ### names用于存放着人脸特征向量对应的名字
                current_name = names[i]
                break
        ## 输出对当前图片的识别结果
        print("当前图片识别结果为:" + current_name)

运行结果:
【Dlib人脸识别】1. Dlib人脸检测的基本原理_第3张图片

你可能感兴趣的:(【Dlib人脸识别】1. Dlib人脸检测的基本原理)