Python人脸识别 Python3.7+OpenCV+Dlib+罗技C920摄像头 实现离线实时摄像头画面人脸检测+识别

5 篇文章 0 订阅
4 篇文章 1 订阅

示例效果 文末付完整工程链接

打码了
在这里插入图片描述

参考博客

基于python的dlib库的人脸识别 Legolas~
Dlib人脸检测的基本原理 普罗米修斯1024
本文中文乱码问题参考博客 天上飞下一毛雪

一、准备数据

  1. 需要事先下载的资料:

    shape_predictor_68_face_landmarks.dat
    dlib_face_recognition_resnet_model_v1.dat
    SIMYOU.TTF
    haarcascade_frontalface_default.xml

  2. 准备模型的训练数据
    新建一个文件夹取名:train_images,事先准备每个人的照片,并做好标签备注,图片名即标签。如下图所示:
    在这里插入图片描述

  3. 准备模型的测试数据
    新建另一个文件夹取名:test_images,放一些train_images里同一个人的不同的照片,去掉标签,如下图:
    在这里插入图片描述

二、训练数据

# -*- coding:utf-8 -*-
import cv2
import dlib
import numpy as np
import os
import glob
import json
import time


'''
# 参数说明:
# train_image_path 为 上文 train_images 文件夹路径
# predictor_path 为 shape_predictor_68_face_landmarks.dat 文件所在路径
# recognition_path 为 dlib_face_recognition_resnet_model_v1.dat文件所在路径
# isNew 为 判断是训练全新的模型还是说我需要在原来的模型基础上新增数据 True 为训练全新模型
# old_model_path 为 旧模型文件的路径 txt文件
# new_model_path 为 训练完后新模型的保存路径 txt文件
# class_name_path 为 每个人脸图片的标签保存路径 txt文件
'''
def train_face_jpg(train_image_path, predictor_path, recognition_path, isNew, old_model_path, new_model_path, class_name_path):
    global class_names  # 人脸标签的保存列表
    detector = dlib.get_frontal_face_detector()  # 获取图片中的人脸(dlib在这块的性能比opencv差点,计算的时候可以在前后用两个time.time()相减看一下dlib人脸检测耗费的时间,模型训练部分暂时就先用这个,实时画面检测改用opencv去实现)
    predictor = dlib.shape_predictor(predictor_path)  # 获取人脸68个关键点
    face_rec = dlib.face_recognition_model_v1(recognition_path)  # 人脸识别
    descriptors = []  # 保存训练后的人脸信息的列表,最终转化为数组形式保存到txt文件
    # 判断是整个重新训练新模型还是在旧模型增加训练数据
    if isNew:   # 训练全新的模型
        descriptors = []
        class_names = []
    else:  
        # 为旧模型增加新的训练数据,训练一次模型是非常耗时的,
        # 当人脸数据比较少还好,如果数据很多的话,每次添加人脸都要从头重新训练的话费时费力,
        # 所以我们可以在以前的模型基础上,更新模型数据
        # 例如调用方法:train_face_jpg('new_train_images', 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', False, 'result.txt', 'result.txt', 'class_names.txt')
        # 其中 new_train_images 为新添加的人脸图的文件夹路径,训练完后的数据保存到原来的result.txt和class_names.txt即可
        if os.path.isfile(old_model_path):  # 判断旧模型文件是否存在
            descriptors = np.loadtxt(old_model_path, dtype=list)  # 
            descriptors = descriptors.tolist()
        if os.path.isfile(class_name_path):  # 判断旧模型对应的标签文件是否存在
            class_names = get_class_names(class_name_path)
            
    # 读取train_images文件夹下的所有图片
    for path in glob.glob(os.path.join(train_image_path, "*.jpg")):  
        # 可读取中文路径
        image = cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1)  
        detection_train = detector(image, 1)
        # 计算每个图片的人脸数据
        for i, det in enumerate(detection_train):
            face_mark_train = predictor(image, det)
            face_des_train = face_rec.compute_face_descriptor(image, face_mark_train)
            # 将数据保存到列表中
            descriptors.append(np.array(face_des_train))
        # 将图片名作为人脸标签保存
        temp_name = path.split('\\')[-1].split('.')[0]
        class_names.append(temp_name)
    print(class_names)
    
    # 保存人脸标签到class_name_path
    with open(class_name_path, 'w+') as f:
        for i in class_names:
            f.write(json.dumps(i, ensure_ascii=False))
            if i == class_names[len(class_names)-1]:
                break
            f.write("\n")
    # 将训练后的模型数据保存到txt
    np.savetxt(new_model_path, descriptors, fmt="%s")
    # 返回 人脸标签列表
    return get_class_names(class_name_path)

# 调用
if __name__ == '__main__':
   train_face_jpg('train_images', 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', True, 'result.txt', 'result.txt', 'class_names.txt')

三、单张图片测试训练后的模型

模型训练完后,可以在工程路径下看到result.txt和class_names.txt,前者为模型的训练数据,后者为对应的标签文件

现在我们任取test_images文件夹中的一张照片来和训练数据做对比

# coding=utf-8
import dlib
import cv2
import numpy as np
import time
from PIL import Image, ImageDraw, ImageFont

class_names = []
detector = 0
predictor = 0
face_rec = 0
face_des_array = 0
descriptors = []
face_casecade = 0


# 初始化模型
'''
# 参数说明:
# predictor_path 为 shape_predictor_68_face_landmarks.dat 文件所在路径
# recognition_path 为 dlib_face_recognition_resnet_model_v1.dat 文件所在路径
# descriptors_path 为 模型的训练数据 也就是 result.txt 文件的路径
# face_casecade_path 为 haarcascade_frontalface_default.xml 文件所在路径
'''
def face_recognition_init_from_txt(predictor_path, recognition_path, descriptors_path, face_casecade_path):
    global detector, predictor, face_rec, descriptors, face_casecade
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(predictor_path)
    face_rec = dlib.face_recognition_model_v1(recognition_path)
    descriptors = np.loadtxt(descriptors_path)
    face_casecade = cv2.CascadeClassifier(face_casecade_path)

# 获取标签名数组
'''
# 参数说明:
# class_name_path 为 标签文件的路径 即 class_names.txt 文件的路径
'''
def get_class_names(class_name_path):
    label_list = []
    if os.path.exists(class_name_path):
        f = open(class_name_path, 'r', encoding='gb18030', errors='ignore').read()
        label_list = f.replace('\"', '').split('\n')
        #print(label_list)
    return label_list


# 单张图片对比
# 根据路径识别人脸
'''
# 参数说明:
# file_path 为 需要测试的图片路径
# class_names 为 人脸标签的数组 可通过加载 get_class_names(class_name_path) 方法获取
'''
def face_recognition_from_path(file_path, class_names):
    global detector, predictor, face_rec, face_des_array
    dist = []
    image = cv2.imread(file_path)
    detection_test = detector(image, 1) # 获取测试图片中人脸信息
    for i, det in enumerate(detection_test):
        face_mark_test = predictor(image, det)
        face_des_test = face_rec.compute_face_descriptor(image, face_mark_test)

        face_des_array = np.array(face_des_test)
    for v in descriptors:
        distance = np.linalg.norm(v - face_des_array) # 测试图片的数据与模型中的人脸数据做对比,求范数
        dist.append(distance)
    #   dist.append(np.sqrt(np.sum(np.square(face_des_array-v))))
    print(class_names)
    print(dist)
    # np.argsort获取列表中最小的数的下标
    test_result = np.argsort(dist)

    print(test_result)
    # 打印最小的的数,这个是和模型中所有训练数据对比后取一个最小数值
    print(dist[test_result[0]])
    # 数值越小,代表相似度越高,但是这个最小数值可能也很大,就是说测试的图片可能在模型中没有,即为录入,
    # 所以加个0.5的阈值判断,最小的数值小于0.45,才算匹配到,否侧视为人脸未录入,这个最小数值根据实际情况取调整
    if dist[test_result[0]] < 0.45:
        if class_names[test_result[0]] in class_names:
            print("识别结果:", class_names[test_result[0]])
        else:
            print('人脸信息未录入')
    else:
        print('人脸信息未录入')

# 调用
if __name__ == '__main__':
    class_names = get_class_names('class_names.txt')
    face_recognition_from_path('test_images/test0.jpg', class_names)

根据上图准备的测试数据 test0.jpg 为局座,对比后最小的数值为0.269,下标为2 ,在标签列表中,下表2的标签为局座
识别结果:
在这里插入图片描述

多试几个: test4.jpg 为 乌蝇哥

face_recognition_from_path('test_images/test4.jpg', class_names)

在这里插入图片描述

test1.jpg 为 断水流大师兄 ,我们的模型里面是没有训练大师兄的数据的,所以即使返回了下标1,但是最小数值0.4895是大于0.45的,所以判断为未录入。

face_recognition_from_path('test_images/test1.jpg', class_names)

在这里插入图片描述

那我们来更新一下训练数据 ,新建一个文件夹取名 new_train_images,然后将断水流大师兄的照片放进去 如下图:
在这里插入图片描述

然后只需再调用一下train_face_jpg() ,修改一下参数设置,参数说明如下:

# 其中 new_train_images 为新添加的人脸图的文件夹路径,训练完后的数据保存到原来的result.txt和class_names.txt即可
# False 为 不是全新的模型,而是更新旧模型

train_face_jpg('new_train_images', 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', False, 'result.txt', 'result.txt', 'class_names.txt')
        

再次识别:
在这里插入图片描述

四、实时获取摄像头画面中的人脸并识别

dlib在人脸检测方面相比opencv更耗时,但是在识别方面更加专业,所以此处采用opecv去获取摄像头画面中人脸位置信息,然后再用dlib去做对比识别


# 首先将上文根据图片路径测试的方法改为根据Mat来识别
# 根据Mat识别人脸
def face_recognition_from_Mat(face_Mat, class_names):
    global detector, predictor, face_des_array
    dist = []
    name = ''
    image = face_Mat
    detection_test = detector(image, 1)
    for i, det in enumerate(detection_test):
        face_mark_test = predictor(image, det)
        face_des_test = face_rec.compute_face_descriptor(image, face_mark_test)
        face_des_array = np.array(face_des_test)

    for v in descriptors:
        distance = np.linalg.norm(v - face_des_array)
        dist.append(distance)
    #   dist.append(np.sqrt(np.sum(np.square(face_des_array-v))))

    test_result = np.argsort(dist)

    if dist[test_result[0]] < 0.45:
        if class_names[test_result[0]] in class_names:
            name = class_names[test_result[0]]
            print("识别结果是:", name)
        else:
            name = '未录入'

    else:
        name = '未录入'
        print('人脸信息未录入')
    return name

# 判断画面中是否存在人脸,存在再识别
def get_face(frame, gray, train_images_names):
    global face_casecade # face_casecade 为上文初始化模型的时候的 face_casecade 
    face = face_casecade.detectMultiScale(gray, 1.1, 5)  # 利用opencv检测画面中是否存在人脸
    face_img = []
    for x, y, w, h in face:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
        face_img = gray[y:y + h, x:w + x]
        face_img = cv2.cvtColor(face_img, cv2.COLOR_GRAY2BGR)
        name = face_recognition_from_Mat(img, train_images_names) # 人脸识别并返回识别到的人名
        frame = change_cv2_draw(frame, name, (x, y - 50), 50, (255, 0, 0)) # 显示中文人名
    return frame

#由于直接用opencv显示中文会乱码,所以先将图片格式转化为PIL库的格式,用PIL的方法写入中文,然后在转化为CV的格式
'''
# 参数说明:
# image 为 输入的图像
# strs 为 需要显示的中文
# local 为 中文显示的坐标位置
# sizes 为 文字大小
# colour 为 颜色 RBG 空间   opencv则为BGR
'''
def change_cv2_draw(image,strs,local,sizes,colour):
    cv2img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    pilimg = Image.fromarray(cv2img)
    draw = ImageDraw.Draw(pilimg)
    font = ImageFont.truetype("SIMYOU.TTF",sizes, encoding="utf-8")  #SIMYOU.TTF为字体文件
    draw.text(local, strs, colour, font=font)
    image = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)
    return image



cap = cv2.VideoCapture(0) # 

while True:
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    frame = get_face(frame, gray, class_names)
    cv2.imshow('frame', frame)
    cv2.waitKey(10)

到此结束

dlib库的人脸识别其实还是挺耗时的,一秒钟,大概能运算3次左右吧,如果觉得性能不够,可以自行尝试有没有什么方式可以优化一下性能

五、完整代码

train_face_data.py

# -*- coding:utf-8 -*-
import dlib
import cv2
import numpy as np
import os
import glob
import json
import time

class_names = []


def get_class_names(class_name_path):
    label_list = []
    if os.path.exists(class_name_path):
        f = open(class_name_path, 'r', encoding='gb18030', errors='ignore').read()

        label_list = f.replace('\"', '').split('\n')

        #print(label_list)

    return label_list




def train_face_jpg(train_image_path, predictor_path, recognition_path, isNew, old_model_path, new_model_path, class_name_path):
    global class_names

    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(predictor_path)
    face_rec = dlib.face_recognition_model_v1(recognition_path)
    descriptors = []
    # 判断是整个重新训练新模型还是在旧模型增加训练数据
    if isNew:
        descriptors = []
        class_names = []
    else:
        if os.path.isfile(old_model_path):
            descriptors = np.loadtxt(old_model_path, dtype=list)
            descriptors = descriptors.tolist()
        if os.path.isfile(class_name_path):
            class_names = get_class_names(class_name_path)

    for path in glob.glob(os.path.join(train_image_path, "*.jpg")):
        image = cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1)
        detection_train = detector(image, 1)
        for i, det in enumerate(detection_train):
            face_mark_train = predictor(image, det)
            face_des_train = face_rec.compute_face_descriptor(image, face_mark_train)
            descriptors.append(np.array(face_des_train))
        temp_name = path.split('\\')[-1].split('.')[0]
        class_names.append(temp_name)
    print(class_names)

    with open(class_name_path, 'w+') as f:
        for i in class_names:
            f.write(json.dumps(i, ensure_ascii=False))
            if i == class_names[len(class_names)-1]:
                break
            f.write("\n")

    np.savetxt(new_model_path, descriptors, fmt="%s")
    return get_class_names(class_name_path)

face_recog.py


# 根据现有训练数据进行人脸识别类

# coding=utf-8
import dlib
import cv2
import numpy as np
import time
from PIL import Image, ImageDraw, ImageFont

detector = 0
predictor = 0
face_rec = 0
face_des_array = 0
descriptors = []
face_casecade = 0

# 初始化模型
def face_recognition_init_from_txt(predictor_path, recognition_path, descriptors_path, face_casecade_path):
    global detector, predictor, face_rec, descriptors, face_casecade
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(predictor_path)
    face_rec = dlib.face_recognition_model_v1(recognition_path)
    descriptors = np.loadtxt(descriptors_path)
    face_casecade = cv2.CascadeClassifier(face_casecade_path)

#根据路径识别人脸
def face_recognition_from_path(file_path, class_names):
    global detector, predictor, face_rec, face_des_array
    dist = []
    image = cv2.imread(file_path)
    detection_test = detector(image, 1)
    for i, det in enumerate(detection_test):
        face_mark_test = predictor(image, det)
        face_des_test = face_rec.compute_face_descriptor(image, face_mark_test)

        face_des_array = np.array(face_des_test)
    for v in descriptors:
        distance = np.linalg.norm(v - face_des_array)
        dist.append(distance)
    #   dist.append(np.sqrt(np.sum(np.square(face_des_array-v))))
    print(class_names)
    print(dist)
    test_result = np.argsort(dist)

    print(test_result[0])
    print(dist[test_result[0]])
    if dist[test_result[0]] < 0.45:
        if class_names[test_result[0]] in class_names:
            print("人脸识别的结果是:", class_names[test_result[0]])
        else:
            pass
    else:
        print('人脸信息未录入')


#根据Mat识别人脸
def face_recognition_from_Mat(face_Mat, class_names):
    global detector, predictor, face_des_array
    dist = []
    name = ''
    image = face_Mat
    detection_test = detector(image, 1)
    for i, det in enumerate(detection_test):
        face_mark_test = predictor(image, det)
        face_des_test = face_rec.compute_face_descriptor(image, face_mark_test)
        face_des_array = np.array(face_des_test)

    for v in descriptors:
        distance = np.linalg.norm(v - face_des_array)
        dist.append(distance)
    #   dist.append(np.sqrt(np.sum(np.square(face_des_array-v))))

    test_result = np.argsort(dist)

    if dist[test_result[0]] < 0.45:
        if class_names[test_result[0]] in class_names:
            name = class_names[test_result[0]]
            print("识别结果是:", name)
        else:
            name = '未录入'

    else:
        name = '未录入'
        print('人脸信息未录入')
    return name

# 判断画面中是否存在人脸
def get_face(frame, gray, train_images_names):
    global face_casecade
    face = face_casecade.detectMultiScale(gray, 1.1, 5)
    face_img = []
    for x, y, w, h in face:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
        face_img = gray[y:y + h, x:w + x]
        face_img = cv2.cvtColor(face_img, cv2.COLOR_GRAY2BGR)
        for img in face_img:
            name = face_recognition_from_Mat(img, train_images_names)
            frame = change_cv2_draw(frame, name, (x, y - 50), 50, (255, 0, 0))



    return frame


#由于直接用opencv显示中文会乱码,所以先将图片格式转化为PIL库的格式,用PIL的方法写入中文,然后在转化为CV的格式
def change_cv2_draw(image,strs,local,sizes,colour):
    cv2img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    pilimg = Image.fromarray(cv2img)
    draw = ImageDraw.Draw(pilimg)
    font = ImageFont.truetype("SIMYOU.TTF",sizes, encoding="utf-8")  #SIMYOU.TTF为字体文件
    draw.text(local, strs, colour, font=font)
    image = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)
    return image


main.py

import face_recog as fr
import train_face_data as tfd
import json
import cv2
import time

# 在原有的模型基础上添加数据
# tfd.train_face_jpg('new_train_images', 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', False, 'result.txt', 'result.txt', 'class_names.txt')

# 训练全新的模型
# class_names = tfd.train_face_jpg('train_images', 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', True, 'result.txt', 'result.txt', 'class_names.txt')

#初始化模型
fr.face_recognition_init_from_txt('shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat', 'result.txt', 'haarcascade_frontalface_default.xml')

class_names = tfd.get_class_names('class_names.txt')

# 单张图片测试
fr.face_recognition_from_path('test_images/test1.jpg', class_names)

# 实时摄像头画面识别
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    frame = fr.get_face(frame, gray, class_names)
    cv2.imshow('frame', frame)
    cv2.waitKey(10)

六、工程链接

python离线人脸检测识别

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值