人脸识别模型的构建

环境准备

Python版本:3.7
系统版本:Windows OR Linux
编写软件:PyCharm or Vscode
复制代码

安装依赖

安装 CMake

pip install CMake
复制代码

安装VisualStudio 2017 点击下载

在安装它时,你必须把Python 和C++支持库勾选上

安装 dlib

pip install dlib
复制代码

安装 face_recognition

pip install face_recognition
复制代码

安装 openCV

 pip install opencv-python
复制代码

尝试读取一张人脸

在这里我尝试读取一张明星的照片

图片链接:p3-juejin.byteimg.com/tos-cn-i-k3… 来源于百度百科

我把它保存至项目中的img目录,并命名为huge.jpg

import face_recognition

# 读取图片并识别人脸
img = face_recognition.load_image_file("./img/huge.jpg")
face_locations = face_recognition.face_locations(img)
print(face_locations)
复制代码

若是识别出数据它应该会打印出类似下面的人像数据

扫描二维码关注公众号,回复: 13470894 查看本文章

图片.png

通过上面的代码我们知道 dlib 的 face_locations 是可以发现人脸,并且提取出一定的特征数据的,尽管我们还不知道怎么使用,以及它所代表的意义,那么接下来我们来剖析一下

通过查看源码我们可以得知 face_locations 返回的数据如下:

图片.png

我们可以猜测一下,既然返回的是一个数组,那么是否代表着这张图片中涵盖有多少张人脸呢?

为了验证这个猜测,我们使用一张多人脸的图像,我把它命名 huge2.jpg

import face_recognition
# 读取图片并识别人脸

img = face_recognition.load_image_file("./img/huge2.jpg")
face_locations = face_recognition.face_locations(img)
print(face_locations)
复制代码

可以得到以下结果

图片.png

其实出现这4个数字大概能猜出它所表达的含义了,应该就是人脸距离四周的边距,从而画出一个方框,按照顺时针 上右下左的顺序

图片.png

大概实现这么个效果 (猜测)

绘制人脸标记图像

我们可以使用OpenCV,将这个指标进行一个绘制

import face_recognition
import cv2

# 读取图片并识别人脸
img = face_recognition.load_image_file("./img/huge2.jpg")
face_locations = face_recognition.face_locations(img)
# print(face_locations)

# openCV读取图像
img = cv2.imread("./img/huge2.jpg")

# 调用opencv函数显示原始图片
cv2.namedWindow("raw")
cv2.imshow("raw", img)

# 遍历人脸信息
for face in face_locations:
    print(face)
    # 人脸上边距
    top = face[0]
    # 人脸右边距
    right = face[1]
    # 人脸下边距
    bottom = face[2]
    # 人脸左边距
    left = face[3]

    # 左上
    start = (left, top)
    # 右下
    end = (right, bottom)

    # 随便挑个颜色
    color = (255, 80, 80)  # 蓝,绿,红(0~255)
    # 画线的粗度
    thickness = 3
    # 绘制一个矩形  (图像,左上角坐标,右下角坐标,颜色,粗度)
    cv2.rectangle(img, start, end, color, thickness)

# 显示绘制分析结果
cv2.namedWindow("analysis")
cv2.imshow("analysis", img)

# 意思是在用户输入任意键时才关闭,不然图片会一闪就关闭
cv2.waitKey(0)
cv2.destroyAllWindows()
复制代码

图片.png

这样我们就可以得到一个最最基础的“人脸识别”效果源码了,但是这仅仅只是识别有人脸,但是并没有实现提取人脸信息特征,那么接下来我们就继续完善它

How are you (你是谁)

如果说人脸识别中最最重要的一个功能,我想应该就是告诉我们这个用户是谁,但是无论再牛逼的人脸识别模型都不能凭空告诉你,这个人姓甚名谁,那么平常我们所见到的人脸识别打卡,人只需要往相机前一站,系统便知道你是谁,这个是怎么实现的呢?

其实非常简单,那就是 参考对比,在模型所连接的数据库,在这我们称之为人脸底库,人脸底库中有一张你的原始照片,那么通过人脸识别模型,去对比一张张照片来提供出每个照片的相似度,从而 猜测出你到底是哪个人,对的没错,是 猜测,所以并没有百分百的对比值,只有尽可能的高的相似度

那么接下来我们就开始人脸比对

载入人脸对象数据

import face_recognition
import cv2

# 提交比对的人脸
unknown_image = face_recognition.load_image_file("img/face1.jpg")
# 打印人脸数据
print("人脸对象:", len(unknown_image))
print(unknown_image)
复制代码

在这里我导入了一张我自己的照片,然后我们看一下使用 face_recognition 导入的图像长什么样子

图片.png 可以看出是一个非常长的数组,准确的说是一个矩阵,我们来看一下这个方法源码的描述

def load_image_file(file, mode='RGB'):
    """
    Loads an image file (.jpg, .png, etc) into a numpy array

    :param file: image file name or file object to load
    :param mode: format to convert the image to. Only 'RGB' (8-bit RGB, 3 channels) and 'L' (black and white) are supported.
    :return: image contents as numpy array
    """
    im = PIL.Image.open(file)
    if mode:
        im = im.convert(mode)
    return np.array(im)

#将图像文件(.jpg, .png等)加载到numpy数组中

#:param file:要加载的图像文件名或文件对象

#:参数模式:将图像转换为的格式。只支持'RGB'(8位RGB, 3通道)和'L'(黑白)。

#:返回:图像内容为numpy数组

复制代码

从返回的话中我们可推测出,在我们输入图像对象之后,可以得到一个numpy的分析结果数组,这里面应该包含了非常多有价值的数据,比如我们可以从对象解析出上一节里的人脸四周边距

import face_recognition
import cv2

# 提交比对的人脸
unknown_image = face_recognition.load_image_file("img/face1.jpg")
# 打印人脸数据
# print("人脸对象:", len(unknown_image))
# print(unknown_image)

# 获取人脸对象的四周边距
face_locations = face_recognition.face_locations(unknown_image)
print(face_locations)
复制代码

图片.png

获取人像特征数据

我们试着从这个人脸对象中提取出人像的特征值

import face_recognition
import cv2

# 提交比对的人脸
unknown_image = face_recognition.load_image_file("img/face1.jpg")
# 打印人脸数据
# print("人脸对象:", len(unknown_image))
# print(unknown_image)

# 获取人脸对象的四周边距
face_locations = face_recognition.face_locations(unknown_image)
print("人像边距", face_locations)

# 人像特征值
face_encodings = face_recognition.face_encodings(unknown_image, face_locations)
print(face_encodings)
复制代码

可以得到以下结果

图片.png 我们查看下这个方法源码的描述

def face_encodings(face_image, known_face_locations=None, num_jitters=1, model="small"):
    """
    Given an image, return the 128-dimension face encoding for each face in the image.

    :param face_image: The image that contains one or more faces
    :param known_face_locations: Optional - the bounding boxes of each face if you already know them.
    :param num_jitters: How many times to re-sample the face when calculating encoding. Higher is more accurate, but slower (i.e. 100 is 100x slower)
    :param model: Optional - which model to use. "large" (default) or "small" which only returns 5 points but is faster.
    :return: A list of 128-dimensional face encodings (one for each face in the image)
    """
    raw_landmarks = _raw_face_landmarks(face_image, known_face_locations, model)
    return [np.array(face_encoder.compute_face_descriptor(face_image, raw_landmark_set, num_jitters)) for raw_landmark_set in raw_landmarks]


"""
给定一个图像,返回对图像中每个人脸进行编码的128维人脸。
:param face_image:包含一个或多个人脸的图像
:param known_face_locations:可选-如果你已经知道每个面的边界框。
:param num_jitters:在计算编码时重新采样人脸的次数。更高更准确,但更慢(即100是100倍慢)
:参数模型:可选-使用哪个模型。“大”(默认)或“小”,只返回5点,但更快。

:return:一个128维面编码列表(图像中的每个面都有一个)
"""

复制代码

很明显这个能够返回代表整个人脸的128维特征值,并且我们可以通过调整入参使识别速度更快或者更准确,同样我们可以看到入参提示我们输入的图像可以包含一个或者多个人脸的图像,也就意味着这个数组也是按下标包含多个人脸的

我们现在尝试使用两个相似的人像图片进行比对,看他是否能判断出是否相同

我拿原本的胡歌1(单人)图片 和这张 胡歌3(单人)图片进行比对,为了区分,我们把 huge3.png 视为输入图片,其他图片都是用来比对的

图片.png

import face_recognition
import cv2

# 提交比对的人脸(假装我们不知道它是谁)
unknown_image = face_recognition.load_image_file("img/huge3.png")

# 一个已知的人脸 (胡歌的照片)
huge_img = face_recognition.load_image_file("img/huge.jpg")

# 未知的人脸对象
unknown_face_locations = face_recognition.face_locations(unknown_image)
# 未知人像特征值 (先默认拿第一张人脸  因为只有一张)
unknown_face_encoding = face_recognition.face_encodings(unknown_image, unknown_face_locations)[0]

# 胡歌人像特征值 (直接将图片输入提取特征函数,因为被比对的照片必须只能有一张人脸)
huge_face_encoding = face_recognition.face_encodings(huge_img)

# 使用比对函数 输入两个特征值对象 以及容忍度,便可以知道是不是同一个人
Results = face_recognition.compare_faces(huge_face_encoding, unknown_face_encoding,  tolerance=0.4)
print(Results)
复制代码

在这里运行我们可以得到一个结果

图片.png

我们在获取比对结果时用了一个新的方法 compare_faces ,我们来看下源码的描述

def compare_faces(known_face_encodings, face_encoding_to_check, tolerance=0.6):
    """
    Compare a list of face encodings against a candidate encoding to see if they match.

    :param known_face_encodings: A list of known face encodings
    :param face_encoding_to_check: A single face encoding to compare against the list
    :param tolerance: How much distance between faces to consider it a match. Lower is more strict. 0.6 is typical best performance.
    :return: A list of True/False values indicating which known_face_encodings match the face encoding to check
    """
    return list(face_distance(known_face_encodings, face_encoding_to_check) <= tolerance)
"""
将面部编码列表与候选编码进行比较,看看它们是否匹配。

:param known_face_encodings:已知的人脸编码列表
:param face_encoding_to_check:与列表进行比较的单个面编码
:参数容忍度:面与面之间的距离是否匹配。越低越严格。0.6是典型的最佳性能。
:return:一个True/False值的列表,指明哪些known_face_encoding匹配要检查的face encoding
"""
复制代码

我们可以看到,第一个传参是告诉我们,我们可以给定一个人脸编码的列表也就是很多人脸特征值,就是我们上章节讲到的人脸底库,再将一张用户扫描的人脸进行比对可以告诉你哪些是True,而容人度则代表的多少的相似度能被你认为同一个人,容忍度越高则代表着你能接受的误差越大。

至此我们就完成了两个人像之间特征的比对

猜你喜欢

转载自juejin.im/post/7036019151873245221