Python的人脸识别欧式距离比对机器训练人脸采集离线识别

31次阅读

共计 7972 个字符,预计需要花费 20 分钟才能阅读完成。

Python 人脸识别, 比对, 机器训练, 离线识别. 人脸采集的功能实现及 code

博主还是抱着开源精神, 因为下面这些 code 是经过了上千次 baidu 和 google 搜索出来得到的结果及其想法, 提供给大家共同学习进步, 注释的都很清楚, 功能完全实现
先上一下效果图


(我知道我很帅, 请勿吐槽, 谢谢. 否则请直接 exit())

很长时间没发文了, 今天跟大家分享一下关于 Python 原生的人脸识别(不调用任何第三方的接口), 离线版本.

刚开始编写的时候, 参考了很多的文章, 首先在此先进行感谢一波!

  • 感谢广州工业大学的煎鱼不可能有 BUG
  • 当然我后期发现煎鱼的文章中很多 Code 是来自于 CSDN 的大佬 HongBin_xu 关于 Python DLIB 库的论文
  • 感谢同济大学子豪兄 Tommy
  • 最后感谢机器学习的树莓派

文章可能讲的比较片面, 但是你把我在文章的 code copy 下去, 肯定包你能用, 识别率可以达到百分之 90 以上, 代码性能有待优化. 但是授之以鱼, 不如授之以渔. 下方我会开始从环境的部署开始讲解. 有讲错或者 bug 的地方, 欢迎给我留言.

  • 文章会从所调用的库进行介绍. 而后介绍库的安装及部署, 希望大家耐心看:

    1. OPENCVOpenCV 是一个基于 BSD 许可(开源)发行的跨平台计算机视觉库,可以运行在 Linux、Windows、Android 和 Mac OS 操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了 Python、Ruby、MATLAB 等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法
    2. Dlib is a modern C++ toolkit containing machine learning algorithms and tools for creating complex software in C++ to solve real world problems. It is used in both industry and academia in a wide range of domains including robotics, embedded devices, mobile phones, and large high performance computing environments. Dlib’s open source licensing allows you to use it in any application, free of charge. 摘抄自于 DLIB 的官网
    3. face_recognition 这是整个我们需要进行人脸识别, 比对. 欧式距离分析比较重要的库
    4. 剩下比较重要的两个库, 在此不做简绍:json numpy.
  • 下面将会介绍库的导入, 请务必按照文中顺序跟随(文中环境, 命令以 mac 为主)

    1. 在安装 dlib 之前, 首先必须安装 cmake 库, 直接命令 pip3 install cmake
    2. 安装完 cmake 之后继续安装 boost 库, 命令:pip3 install boost
    3. 安装 opecv 库, 命令 pip3 install opencv. 导入的时候注意,code 为:import cv2; 为什么是 cv2, 我 google 查过, 因为版本迭代的问题. 所以无需太在意
    4. 安装完上述两个库之后, 到了最麻烦的 dlib 库, 首先还是用命令:pip3 install dlib 进行安装. 但是这只是下载下来, 如下载过程比较慢, 建议使用切换一下源, 建议使用阿里巴巴源或者清华大学的源, 下载完毕后, 会提示你运行 setup.py 文件. 这时候你需要打开 python3 的库的目录(以 mac 示例: 路径为:/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/), 刚才下载的 dilb 库就在这里, 这时候你需要用命令进入该目录. 执行(请注意):python setup.py install 命令, 请别敲错了.
    5. 如果你成功完成了上一步, 下面就很简单了. 我们继续安装 face_recognition 库, 命令还是:pip3 install face_recognition
    6. 然后剩下的常用库不再做简绍

完成上述步骤后, 首先得恭喜你, 完成了 opencv,face_recognition,dlib 的部署. 继续下面的工作, 您需要先进行阅读下面的文章: 来自 Github 的 face_recognition 介绍, 中文翻译为同济大学的子豪兄 Tommy;

看完子豪兄 Tommy 的 github 翻译之后, 请继续看 face_recognition 的方法介绍, 这是经过博主综合评估及踩坑之后力荐的;

好的, 如果你看完了上述资料, 那么我开始给大家讲解这次整个人脸识别程序的流程

  1. 打开电脑的摄像头
    camera = cv2.VideoCapture(0) # 调用笔记本内置摄像头,所以参数为 0,如果有其他的摄像头可以调整参数为 1,2
  2. 调用 dlib 的面部 68 点识别训练库文件 (这个已经是训练好了的库, 请直接使用) 直接附上下载链接 传送门文件 90 多 M, 慢慢下.
    predictor = dlib.shape_predictor("lib/shape_predictor_68_face_landmarks.dat")
  3. 使用 dlib 进行面部识别
    detector = dlib.get_frontal_face_detector();

5. 然后开始 while 死循环. 进行检测.
6. 检测到人脸后, 生成特征码 face_recognition.face_encodings, 而后利用 face_recognition 的 compare_faces 方法进行比对
7. 返回识别结果

8. 文件目录

ok, 废话已经很多.so, 下面直接上 code, 首先从功能性示范开始(代码注释的已经非常详细了, 因为我是 python 菜鸡)

import numpy as np;
import cv2;
import dlib;

camera = cv2.VideoCapture(0)  # 调用笔记本内置摄像头,所以参数为 0,如果有其他的摄像头可以调整参数为 1,2

# 注释掉 opencv 的面部分类识别器, 因为感觉不是很好使
# detector = cv2.CascadeClassifier('lib/haarcascade_frontalface_default.xml')  # 加载面部识别的分类器

# 改用 dlib 的面部识别
detector = dlib.get_frontal_face_detector();

# 调用 dlib 的面部 68 点识别训练库文件
predictor = dlib.shape_predictor("lib/shape_predictor_68_face_landmarks.dat")


while True:
    result, img = camera.read()  # 因为 read 方法反回了两个变量. 所以需要定义两个变量进行接收该方法的返回值
    # exit();
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换图片为灰度
    faces = detector(gray, 1) # 检测出人脸数
    # len(faces) > 1
    # 利用 cv2.putText 输出
    for i in range(len(faces)):
        landmarks = np.matrix([[p.x, p.y] for p in predictor(img, faces[i]).parts()])
        # enumerate 是一个 Python 的内置方法,用于遍历索引
        # index 是序号;face 是 dets 中取出的 dlib.rectangle 类的对象,包含了人脸的区域等信息
        # left()、top()、right()、bottom()都是 dlib.rectangle 类的方法,对应矩形四条边的位置
        for index, face in enumerate(faces):
            # 这里画出人脸框
            left = face.left()
            top = face.top()
            right = face.right()
            bottom = face.bottom()
            cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)

            shape = predictor(img, face)  # 寻找人脸的 68 个标定点
            print(shape)
            print(shape.num_parts)
            # 遍历所有点,打印出其坐标,并用蓝色的圈表示出来
            for index, pt in enumerate(shape.parts()):
                print('Part {}: {}'.format(index, pt))
                pt_pos = (pt.x, pt.y)
                cv2.circle(img, pt_pos, 2, (0, 255, 0), 1)
                
                cv2.imshow('frame', img)  # 展示

    if cv2.waitKey(1) & 0xFF == ord('q'):  # 监听键盘 每一秒检测一下是否按下了 Q.
        break
    else:
        pass  


camera.release()  # 释放摄像头资源
cv2.destroyAllWindows(); # 关闭所有弹出的窗口

下面的是人脸采集

import numpy as np
import cv2
import dlib
import face_recognition

# 注释掉 opencv 的面部分类识别器, 因为感觉不是很好使
# detector = cv2.CascadeClassifier('xml/haarcascade_frontalface_default.xml')

# 改用 dlib 的面部识别
detector = dlib.get_frontal_face_detector()

# 调用 dlib 的面部 68 点识别训练库文件
predictor = dlib.shape_predictor("lib/shape_predictor_68_face_landmarks.dat")


camera = cv2.VideoCapture(0)  # 调用笔记本内置摄像头,所以参数为 0,如果有其他的摄像头可以调整参数为 1,2

i = 0 # 设置计步器

UID = input('enter your id:')

while True:
    result, img = camera.read() # 接收噶你方法返回的结果

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度

    faces = detector(gray, 1)  # 检测出人脸数

    if len(faces) == 1:

        for index, face in enumerate(faces):
            left = face.left()
            top = face.top()
            right = face.right()
            bottom = face.bottom()
            cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3) # 画出框子
            
            i += 1 #计步器自增
            cv2.imwrite("img/user." + str(UID) + '.' + str(i) +
                        ".jpg", gray[top:bottom, left:right])  # 存储照片

            cv2.imshow('frame', img) # 展示图片
    else:
        cv2.putText(img, "Warning: can only appear for one person", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
        cv2.imshow('frame', img)
    

    if cv2.waitKey(100) & 0xFF == ord('q'): #每秒检查是否按下 Q
        break
    elif i >= 20: # 20 次后自动退出
        break

camera.release()
cv2.destroyAllWindows()

下面的是人脸训练

import cv2
import os
import numpy as np
from PIL import Image
import face_recognition
import json

def get_images_and_labels(path):
    image_paths = [os.path.join(path, f) for f in os.listdir(path)]
    # print(image_paths);
    faces = []
    for src in image_paths:
        data = {}
        img = cv2.imread(src)
        list_of_face_encodings = face_recognition.face_encodings(img)
        # print(list_of_face_encodings)
        if len(list_of_face_encodings):
            data['face'] = list_of_face_encodings[0].tolist()
            image_id = int(src.split('.')[1])
            data['id'] = image_id
            # print(data)
            faces.append(data)
            print(faces)
            

    return faces


result = get_images_and_labels('img')
# print(result);exit();
# print(json.jumps(result))
with open("data/data.json", "w") as f:
    json.dump(result, f, sort_keys=True, indent=4, ensure_ascii=False)
    print("加载入文件完成...")

人脸识别

import cv2
import numpy as np
import dlib
import face_recognition
import json


def bejson():
    f = open("data/data.json", encoding='utf-8') # 设置以 utf- 8 解码模式读取文件,encoding 参数必须设置,否则默认以 gbk 模式读取文件,当文件中包含中文时,会报错
    data = json.load(f)
    faces = []
    ids = []
    for value in data:
        faces.append(np.asarray(value['face']));
        ids.append(value['id']);
    return ids, faces


camera = cv2.VideoCapture(0)  # 调用笔记本内置摄像头,所以参数为 0,如果有其他的摄像头可以调整参数为 1,2

# 改用 dlib 的面部识别
detector = dlib.get_frontal_face_detector()

# 调用 dlib 的面部 68 点识别训练库文件
predictor = dlib.shape_predictor("lib/shape_predictor_68_face_landmarks.dat")


font = cv2.FONT_HERSHEY_SIMPLEX


def max_count(lt):
    # 定义一个字典,记录元素及次数
    d = {}
    # 记录最大次数的元素
    max_key = None
    for i in lt:
        # 判断字典中是否没有该元素
        if i not in d:
            # 计算该元素在列表中出现的次数
            count = lt.count(i)
            # 保存到字典中
            d[i] = count
            # 记录最大元素
            if count > d.get(max_key, 0):
                max_key = i
    # for key in d:
    #     if d.get(max_key, 0) < d[key]:
    #         max_key = key
    return max_key


while True:
    result, img = camera.read()  # 因为 read 方法反回了两个变量. 所以需要定义两个变量进行接收该方法的返回值
    # exit();
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换图片为灰度
    faces = detector(gray, 1)  # 检测出人脸数
    if len(faces) == 1:
        # print(face_recognition.face_encodings(img))
        face_encoding_to_check = face_recognition.face_encodings(img)
        # print(face_encoding_to_check[0]);exit()
        # 读取 json
        ids,data = bejson();
        # print(data);exit();
        result = face_recognition.compare_faces(data, face_encoding_to_check[0], tolerance=0.4)
        print(result)
        uidArray = []  # 识别通过的 id
        for index, value in enumerate(result):
            # print(value)
            if value:
                uidArray.append(index);
        # print(uidArray)

        if uidArray:
            key = max_count(uidArray)
                # 根据索引找到 json 中的这个人的 id
                # print(ids[key])
            cv2.putText(img, "Verify Success User: No--" +
                            str(ids[key])+"--", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
        else:
            cv2.putText(img, "Verify Fail-2", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2)

        # 利用 cv2.putText 输出
        for i in range(len(faces)):
            landmarks = np.matrix([[p.x, p.y] for p in predictor(img, faces[i]).parts()])
            # enumerate 是一个 Python 的内置方法,用于遍历索引
            # index 是序号;face 是 dets 中取出的 dlib.rectangle 类的对象,包含了人脸的区域等信息
            # left()、top()、right()、bottom()都是 dlib.rectangle 类的方法,对应矩形四条边的位置
            for index, face in enumerate(faces):
                # 这里画出人脸框
                left = face.left()
                top = face.top()
                right = face.right()
                bottom = face.bottom()
                cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)

                shape = predictor(img, face)  # 寻找人脸的 68 个标定点
                # print(shape)
                # print(shape.num_parts)
                # 遍历所有点,打印出其坐标,并用蓝色的圈表示出来
                for index, pt in enumerate(shape.parts()):
                    # print('Part {}: {}'.format(index, pt))
                    pt_pos = (pt.x, pt.y)
                    cv2.circle(img, pt_pos, 2, (0, 255, 0), 1)
    else:
        cv2.putText(img, "Warning: can only appear for one person", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
    cv2.imshow('face verify-3', img)
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

camera.release()
cv2.destroyAllWindows()

因为博主并不是很精通 python, 如果有不太 ok 的地方, 望谅解. 仅供学习!

happy coding! bye see you!

正文完
 0