本科毕设——基于人脸识别的签到系统的研究

2023-11-17

        本人普通四非本科,毕设选了这个比较大众且成熟的选题,四处借鉴后完成了论文,现在写写一些我完成毕设期间的历程。

        在完成论文开题报告后我开始寻求代码以期完成一个简易人脸识别签到系统的设计,开始我用了舍友选修课人工智能的大作业,他所采用的是传统的haar算法,但发现其代码只能用于检测以及识别人脸,不能做到调用摄像头实时识别。于是我又去网上荡了几份代码下来跑(代码已放代码包里——基于haar算法的人脸识别)。

        本人采用的是vscode来实现代码的运行,考虑到vscode的多样性,以后也许其他语言的汇编也可以通用,所以我没有下pycharm。直接在官网下最新的vscode以及搭配最新的anaconda环境,由于都是新版本,所以后续有些报错会与我查阅的文章解决方法不同或不适用了,这给我带来不小的麻烦,具体是哪些我隔了半年我也忘了哈哈哈哈哈,连历史记录都找不到了,只能照着记忆粗略描述,第一次写不够严谨,出现很多问题还请各位大佬多多指正。

        初进vscode各种配置我就不一一介绍了,大体扩展处下个python的扩展包就可以了,然后在第一次运行.py文件时会要你选择编译环境,这个要选好,或者后续摁F1弹出搜索框搜Python:Select Interpreter选择也可。我的理解是相当于一个虚拟编译环境,你不同的项目所用到的模块导入下载后都在各自的环境里,并不通用,打比方你在这个项目刚导入了cv2模块,重开一个项目在另外的编译环境里又得重下,如图。我都选在了在anaconda下集成模块编译,因为同个环境换项目的时候模块导入也不用重新下载。

        要是缺少某个模块报错,如图,根据报错信息到百度搜一下也基本都有解决方案,例如第3行的就是缺少了opencv,可以在base环境下使用pip install opencv-python,本人由于在anaconda的编译环境,所以都是使用conda install安装各种模块,包括下文用到dlib的模型,也是采用conda install -c conda-forge dlib或直接conda install dlib。

         haar作为一款非常经典的算法而言,其优点是可以检测不同尺度的人脸,而且得益于其简单的架构,使其可以在CPU上近乎实时工作。haar算法主要是建立haar级联器,对输入的图像进行灰度化处理,然后调用函数接口来进行人脸识别。上文提到本文一开始的人脸识别是计划基于haar算法实现的,通过实验发现其对于完整的脸部检测效果不错,但其不适用于非正面图像,脸部受到轻微遮挡的情况下近乎起不到作用,同时识别率不高,经常给出许多错误预测,在近亲人脸识别中几乎识别不出两人的区别,经常混淆。如图.yml是是将训练对象的数据保存下来,然后照片则是点击拍照时截取并保存在本地文件夹的。

      

         于是又一次去借鉴了网上各路大佬,物色到了一个基于dlib改进的人脸识别签到系统。dlib曾于2017年2月刷新了LFW人脸数据库的识别榜单,准确率高达0.9938 ± 0.0027。该模型是本系统所用的人脸编码方法,具有27 层的 ResNet网络。它的实质是ResNet-34网络,但却是在这基础上进行了简化,删除了一部分网络层并减少了一半使用的滤波器,使其可以更加高效快速的完成任务。代码如下:

# 主程序
import sys
import cv2
import os
import numpy as np
import uuid  # 生成随机文件名
import dlib
from PIL import Image, ImageDraw, ImageFont
import datetime
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QLabel

a = 0
b = 0


def name(face_id):  # 创建班级表
    with open('info.csv', 'r+') as f:
        # 使用 with as 操作已经打开的文件对象(本身就是上下文管理器),无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件。
        # r+是open的参数,代表可读可写,若文件不存在就报错
        myInfo = f.readlines()
        namelist = []
        for line in myInfo:
            enty = line.split(',')
# 数据中遇到','就隔开
            namelist.append(enty[0])
        if face_id not in namelist:
            state = "已录入"
            f.writelines(f'\n{face_id},{state}')
        # 如果是新的面孔id,即不在现有面容列表里的(namelist)则显示已录入


def getMax_faces(face_rects):
    if len(face_rects) == 0:
        return 0, 0
    face_areas = []
    for rect in face_rects:
        # 计算每个人脸的面积(高x宽)只录入最大的那个人脸
        area = (rect.bottom() - rect.top()) * (rect.right() - rect.left())
        face_areas.append(area)
    index = np.argmax(face_areas)
    return face_areas[index], face_rects[index]


def gen_face_name(str_face_id):
    # 生成图片文件
    return str_face_id + '_' + str(uuid.uuid4()) + '.jpg'


def ChineseText(img, text, position, textColor=(255, 0, 0), textsize=30):
    if (isinstance(img, np.ndarray)):
        # isinstance(object, classinfo)如果参数object是classinfo的实例,或者object是classinfo类的子类的一个实例, 返回True。如果object不是一个给定类型的的对象, 则返回结果总是False。所以此句判断是否OpenCV图片类型
        # np.array 只是一个便捷的函数,用来创建一个ndarray,它本身不是一个类。ndarray数组,是用 np.ndarray类的对象 表示n维数组对象,所以ndarray是一个类对象,而array是一个方法。
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    # 创建一个可以在给定图像上绘图的对象
    fontSytle = ImageFont.truetype("simsun.ttc", textsize, encoding='utf-8')
    # 字体的格式
    draw.text(position, text, textColor, font=fontSytle)
    # 绘制文本
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# 转换回OpenCV格式


# 创建已打卡表
def saveInfo(face_id):  # 创建已打卡表
    with open('sign.csv', 'r+') as f:
        myInfo = f.readlines()
        namelist = []
        for line in myInfo:
            enty = line.split(',')
            namelist.append(enty[0])
        if face_id not in namelist:
            now = datetime.datetime.now()  # 获取当前时间
            dtString = now.strftime("%H:%M:%S")  # 设置时间格式hour,minutes,second
            state = "已签到"
            f.writelines(f'\n{face_id},{dtString},{state}')


def absenteeism():  # 建立缺勤名单
    with open('sign.csv', 'r+') as f:
        myInfo = f.readlines()
        namelist = []
        for line in myInfo:
            enty = line.split(',')
            namelist.append(enty[0])
    with open('info.csv', 'r+') as f:
        myInfo = f.readlines()
        name = []
        for line in myInfo:
            en = line.split(',')
            name.append(en[0])
    ab = list(set(name).difference(set(namelist)))
    # set() 函数创建一个无序不重复元素集
    # difference():用于查找两个集合之间的差,该方法以该集合(set1)调用,另一个集合(set2)作为参数传递,并且它返回set2中不存在的元素集。注意:difference()可以获得差集,但是这个差集是“出现在第一个集合但不出现在第二个集合”的元素,也就是说如果第二个集合包含第一个,结果就是空。set_name1.difference(set_name2)
    print(ab)
    for i in ab:
        with open('absenteeism.csv', 'r+') as f:
            myInf = f.readlines()
            namelis = []
            for line in myInf:
                ent = line.split(',')
                namelis.append(ent[0])
            if i not in namelis:
                state = "未签到"
                f.writelines(f'\n{i},{state}')
        print(i)


def load_model(file_scp):  # 加载训练好的模型
    with open(file_scp, 'r', encoding='utf-8') as f:
        lines = f.read().splitlines()  # 拆分
    face_ids = [line.split()[0] for line in lines]  # 该列表存放人脸id
    face_models = [np.load(line.split()[-1]) for line in lines]  # 该列表存放人脸模型
    return face_ids, face_models


def face_recognize(face_vec, face_models, face_ids):  # 计算欧氏距离,越相似数值越小
    scores = []
    for model in face_models:
        N = model.shape[0]
        # shape[0]读取矩阵第一维度的长度,而对于图像来说等于图片高
        diffMat = np.tile(face_vec, (N, 1))-model
        # 计算欧式距离
        # np.tile(A,reps)函数可对输入的数组,元组或列表进行重复构造,其输出是数组,A:输入的数组,元组或列表;reps:重复构造的形状,可为数组,元组或列表,reps:(m,n),其中m控制纵向重复,n控制横向重复
        sqDiffMat = diffMat ** 2
        # **:将数组中每个元素进行平方;而matrix(a)**2则是矩阵乘积,而不是数组中的元素乘积。
        sqDistances = sqDiffMat.sum(axis=1)
        distances = sqDistances ** 0.5
        # 找到最小距离
        score = np.min(distances)
        scores.append(score)
    index = np.argmin(scores)
    return face_ids[index], scores[index]  # 返回id和距离


# 点击信息录入
def do_info():
    info_btn.hide()  # 隐藏信息录入
    sign_btn.hide()  # 隐藏开始签到
    oversign_btn.hide()  # 隐藏结束签到
    camera_btn.show()  # 显示开始拍照
    change_btn.show()  # 显示换人录入
    ovecam_btn.show()  # 显示结束录入
    user_edit.show()
    usr_label.show()
    global a
    # 定义全局变量
    faceid = user_edit.text()  # 获取文本框内容
    if __name__ == "__main__":
        os.makedirs('faces', exist_ok='True')  # 创建faces文件夹用于保存人脸数据
        det_face = dlib.get_frontal_face_detector()  # 创建人脸检测器
        cap = cv2.VideoCapture(0)  # 打开摄像头
        str_face_id = ""  # 创建空的人脸id字符串
        while True:
            if str_face_id.strip() == "":  # 判断id是否为空,为空创建face-id
                str_face_id = faceid  # 循环开始时输入id,并在faces目录下创建一个人脸id的目录
                print(a)
                print(str_face_id)
                path_face = os.path.join('faces', str_face_id)
                os.makedirs(path_face, exist_ok='True')
            success, img = cap.read()  # 读取每一帧的图像
            # sucess是布尔型,读取帧正确返回True;img是每一帧的图像(BGR存储格式)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度
            face_rects = det_face(gray, 0)  # 检测人脸区域
            max_area, max_rect = getMax_faces(face_rects)  # 得到最大的人脸

            k = cv2.waitKey(1)  # 获取按键值
            if a == 1:  # 进行人脸采集
                a = 0
                if max_area > 10:  # 保存最大人脸
                    roi = img[max_rect.top(): max_rect.bottom(),
                              max_rect.left(): max_rect.right()]
                    # 生成文件名
                    save_face_name = os.path.join(
                        'faces', str_face_id, gen_face_name(str_face_id))  # .encode('utf8')
                    # cv2.imwrite(save_face_name, roi)  # 保存文件
                    cv2.imencode('.jpg', roi)[1].tofile(save_face_name)  # 保存
                    print('save_face', save_face_name)

            elif a == 2:  # 切换人脸id
                a = 0
                str_face_id = ""
                faceid = user_edit.text()
            elif a == 3:  # 退出
                a = 0
                break
            if max_area > 10:  # 画框
                cv2.rectangle(img, (max_rect.left(), max_rect.top()), (max_rect.right(), max_rect.bottom()),
                              (0, 0, 255),
                              2)  # x               y                 x+w                 y+h
            cv2.imshow("FACE", img)

        cap.release()
        cv2.destroyAllWindows()  # 关闭摄像头

    # 加载人脸特征提取器
    facerec = dlib.face_recognition_model_v1(
        "dlib_face_recognition_resnet_model_v1.dat")
    # 加载人脸标志点检测器
    sp = dlib.shape_predictor("shape_predictor_68_face_landmarks_GTX.dat")
    # 记录所有模型信息
    with open('model.scp', 'w', encoding='utf-8') as f:  # 记录人脸id与人脸模型
        base_path = 'faces'  # 遍历faces文件夹
        for face_id in os.listdir(base_path):
            # os.listdir()用于返回一个由文件名和目录名组成的列表,需要注意的是它接收的参数需要是一个绝对的路径。
            face_dir = os.path.join(base_path, face_id)
            if os.path.isdir(face_dir):
                # os.path.isdir()用于判断对象是否为一个目录。

                file_face_model = os.path.join(
                    face_dir, face_id + '.npy')  # 遍历base_path/face_id文件夹
                face_vectors = []  # 人脸特征list

            for face_img in os.listdir(face_dir):  # 遍历,查找所有文件
                if os.path.splitext(face_img)[-1] == '.jpg':  # 寻找所有.jpg文件
                    # splitext分离文件名和扩展名
                    # img = cv2.imread(os.path.join(face_dir, face_img))  # 读取图片并转换格式

                    img = cv2.imdecode(np.fromfile(os.path.join(
                        face_dir, face_img), dtype=np.uint8), -1)
                    # cv2.imdecode()函数从指定的内存缓存中读取数据,并把数据转换(解码)成图像格式;主要用于从网络传输数据中恢复出图像。cv2.imencode()函数是将图片格式转换(编码)成流数据,赋值到内存缓存中;主要用于图像数据格式的压缩,方便网络传输。
                    # fromfile和tofile既可以读写二进制文件,也可以读写文本文件,是非常灵活的文件读取函数。他们之间互为对偶函数(对于任意一个逻辑函数,若把式中的运算符“.”换成“+”,“+”换成“.”;常量“0”换成“1”,“1”换成“0”,所得的新函数式为原函数式F的对偶式F′,也称对偶函数.对偶规则--如果两个函数式相等,则它们对应的对偶式也相等.)

                    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                    img = np.array(img)  # 存放的是已经截取好的人脸图片,所以在图内检测标志点
                    h, w, _ = np.shape(img)
                    rect = dlib.rectangle(0, 0, w, h)  # 整个区域
                    shape = sp(img, rect)  # 辅助人脸定位,获取关键位
                    print("Generate face vector of", face_img)
                    face_vector = facerec.compute_face_descriptor(
                        img, shape)  # 获取128维人脸特征
                    face_vectors.append(face_vector)  # 保存图像和人脸id
                    name(face_id)
            if len(face_vectors) > 0:  # 人脸模型保存,并写入model.scp文件
                np.save(file_face_model, face_vectors)

                f.write('%s %s\n' % (face_id, file_face_model))


# 点击开始拍照
def do_camera():
    info_btn.hide()  # 隐藏信息录入
    sign_btn.hide()  # 隐藏开始签到
    oversign_btn.hide()  # 隐藏结束签到
    camera_btn.show()  # 显示开始拍照
    change_btn.show()  # 显示换人录入
    ovecam_btn.show()  # 显示结束录入
    user_edit.show()
    usr_label.show()
    global a
    a = 1
    print(a)


# 点击切换名字录入
def do_change():
    info_btn.hide()  # 隐藏信息录入
    sign_btn.hide()  # 隐藏开始签到
    oversign_btn.hide()  # 隐藏结束签到
    camera_btn.show()  # 显示开始拍照
    change_btn.show()  # 显示换人录入
    ovecam_btn.show()  # 显示结束录入
    user_edit.show()
    usr_label.show()
    global a
    a = 2


# 点击结束录入
def do_ovecam():
    info_btn.show()  # 显示信息录入
    sign_btn.show()  # 显示开始签到
    oversign_btn.hide()  # 隐藏结束签到
    camera_btn.hide()  # 隐藏开始拍照
    change_btn.hide()  # 隐藏换人录入
    ovecam_btn.hide()  # 隐藏结束录入
    user_edit.show()
    usr_label.show()
    global a
    a = 3


# 点击开始签到
def do_sign():
    info_btn.hide()
    sign_btn.hide()
    oversign_btn.show()  # 显示结束签到
    camera_btn.hide()  # 隐藏开始拍照
    change_btn.hide()  # 隐藏换人录入
    ovecam_btn.hide()
    user_edit.hide()
    usr_label.hide()
    global b
    if __name__ == "__main__":
        face_ids, face_models = load_model("model.scp")  # 加载训练好的人脸模型
        print(face_ids)
        detector = dlib.get_frontal_face_detector()  # dlib人脸检测器
        sp = dlib.shape_predictor(
            "shape_predictor_68_face_landmarks_GTX.dat")  # 人脸标志检测器
        # 人脸特征提取器
        facerec = dlib.face_recognition_model_v1(
            "dlib_face_recognition_resnet_model_v1.dat")
        cap = cv2.VideoCapture(0)  # 打开摄像头
        while True:
            success, img = cap.read()  # 读取每一帧的图像
            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换格式
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度
            face_rects = detector(gray, 0)  # 检测人脸
            for k, rect in enumerate(face_rects):  # 遍历检测的人脸
                cv2.rectangle(img, (rect.left(), rect.top()),
                              (rect.right(), rect.bottom()), (0, 0, 255), 2)
                shape = sp(img_rgb, rect)  # 标志点检测
                face_vector = facerec.compute_face_descriptor(
                    img_rgb, shape)  # 获取人脸特征

                # 计算人脸特征和人脸模型的距离
                face_id, score = face_recognize(
                    np.array(face_vector), face_models, face_ids)  # 进行识别,返回id和距离
                if score < 0.40:  # 设定阈值来判定是否为同一人
                    str_face = face_id
                    str_confidence = " %.2f" % (score)
                    saveInfo(face_id)
                    st = "签到成功"
                else:
                    str_face = "unknow"
                    str_confidence = " %.2f" % (score)
                    st = "未知"
                # 检查结果画图显示
                img = ChineseText(img, str_face + str_confidence +
                                  st, (rect.left(), rect.top() - 30))
                # cv2.putText(img, str_face + str_confidence, (rect.left(), rect.top() - 5), cv2.FONT_HERSHEY_SIMPLEX, 1,
                #            (0, 0, 255), 2)
            cv2.imshow("FACE", img)  # 显示窗口
            key = cv2.waitKey(1)
            if b == 1:
                b = 0
                break
        absenteeism()
        cap.release()
        cv2.destroyAllWindows()  # 关闭摄像头


# 点击结束签到
def do_oversign():
    info_btn.show()  # 显示信息录入
    sign_btn.show()  # 显示开始签到
    oversign_btn.hide()  # 隐藏结束签到
    camera_btn.hide()  # 隐藏开始拍照
    change_btn.hide()  # 隐藏换人录入
    ovecam_btn.hide()  # 隐藏结束录入
    user_edit.show()
    usr_label.show()
    global b
    b = 1


if __name__ == "__main__":
    app = QApplication(sys.argv)  # 加载应用程序
    my_win = QWidget()  # 创建一个窗口
    my_win.setWindowTitle("签到系统")  # 窗口的名字
    my_win.resize(300, 300)  # 窗口大小
    my_win.move(382, 238)  # 窗口位置

    info_btn = QPushButton("信息录入", my_win)  # 创建第二个按钮,放在my_win里面
    info_btn.setGeometry(100, 80, 80, 40)  # 设置按钮的位置及大小,X Y W H
    info_btn.clicked.connect(do_info)  # 设置点击按钮的相应事件
    # train_btn.hide();  # 隐藏该按钮

    camera_btn = QPushButton("开始拍照", my_win)  # 创建按钮,放在my_win里面
    camera_btn.setGeometry(100, 140, 80, 40)  # 设置按钮的位置及大小,X Y W H
    camera_btn.clicked.connect(do_camera)  # 设置点击按钮的相应事件
    camera_btn.hide()  # 隐藏该按钮

    change_btn = QPushButton("换人录入", my_win)  # 创建按钮,放在my_win里面
    change_btn.setGeometry(100, 80, 80, 40)  # 设置按钮的位置及大小,X Y W H
    change_btn.clicked.connect(do_change)  # 设置点击按钮的相应事件
    change_btn.hide()  # 隐藏该按钮

    ovecam_btn = QPushButton("结束录入", my_win)  # 创建按钮,放在my_win里面
    ovecam_btn.setGeometry(100, 200, 80, 40)  # 设置按钮的位置及大小,X Y W H
    ovecam_btn.clicked.connect(do_ovecam)  # 设置点击按钮的相应事件
    ovecam_btn.hide()  # 隐藏该按钮

    sign_btn = QPushButton("开始签到", my_win)  # 创建第三个按钮,放在my_win里面
    sign_btn.setGeometry(100, 140, 80, 40)  # 设置按钮的位置及大小,X Y W H
    sign_btn.clicked.connect(do_sign)  # 设置点击按钮的相应事件

    oversign_btn = QPushButton("结束签到", my_win)  # 创建第三个按钮,放在my_win里面
    oversign_btn.setGeometry(100, 140, 80, 40)  # 设置按钮的位置及大小,X Y W H
    oversign_btn.clicked.connect(do_oversign)  # 设置点击按钮的相应事件
    oversign_btn.hide()

    usr_label = QLabel("姓名", my_win)  # 创建文本,放在my_win里面
    usr_label.setGeometry(55, 15, 100, 50)  # 设置文本的位置及大小,X Y W H
    # usr_label.hide();  # 隐藏该按钮

    user_edit = QLineEdit(my_win)  # 创建文本输入框,放在my_win里面
    user_edit.setPlaceholderText("请输入姓名")
    user_edit.setGeometry(90, 20, 100, 40)  # 设置文本框的位置及大小,X Y W H
    # user_edit.hide();  # 隐藏该按钮

    my_win.show()  # 显示窗口
    sys.exit(app.exec_())  # 不退出系统,保持窗口显示

        此次利用pythonqt5做了简易的签到界面,对比之前的方案此次输入信息还可以输入中文。通过调用dlib里的人脸特征提取器"dlib_face_recognition_resnet_model_v1.dat"和人脸标志点检测器"shape_predictor_68_face_landmarks_GTX.dat"截取人脸特征信息保存本地,相比上个方案,该系统截取时还会去除背景无用信息,只保留人脸关键点信息,如图。

        针对dlib的高识别率,本人也借鉴了波代码进行了粗略的验证,在BIT官网上下载了CASIA-FaceV5的数据集,它是由中国科学院的自动化研究所(CASIA)所收集的,都是亚洲的人脸,代码如下。我没能找到能自动迭代进行两两比较的代码,自己琢磨了也没写出来,只能手动更换要比对的两张照片一次次去跑害。

# coding:utf-8
'''
本本次封装,我主要是做两张人脸对比。
就只人脸识别部分,简单应用。
# 调用注意事项,因为模型底层是外国人写的。所以路径图片名字千万别使用中文,这样它直接找不到
    好像是OpenCV的问题吧,一直没有解决。中文他会乱码。真的坑。
'''

from PIL import Image
import itertools
import dlib
import cv2
import glob
import numpy as np
import os


class face_recognition:
    '''
    模型路径
    predictor_path = "./face_model/shape_predictor_68_face_landmarks.dat"
    face_rec_model_path = "./face_model/dlib_face_recognition_resnet_model_v1.dat"

    # 调用注意事项,因为模型底层是外国人写的。所以路径图片名字千万别使用中文,这样它直接找不到
    好像是OpenCV的问题吧,一直没有解决。中文他会乱码。真的坑。
    '''

    def __init__(self, predictor_path, face_rec_model_path):
        self.predictor_path = predictor_path
        self.face_rec_model_path = face_rec_model_path
        self.detector = dlib.get_frontal_face_detector()
        self.shape_predictor = dlib.shape_predictor(self.predictor_path)
        self.face_rec_model = dlib.face_recognition_model_v1(
            self.face_rec_model_path)

    def face_detection(self, url_img_1, url_img_2):
        img_path_list = [url_img_1, url_img_2]
        dist = []
        for img_path in img_path_list:
            img = cv2.imread(img_path)
            # 转换rgb顺序的颜色。
            b, g, r = cv2.split(img)
            img2 = cv2.merge([r, g, b])
            # 检测人脸
            faces = self.detector(img, 1)
            if len(faces):
                for index, face in enumerate(faces):
                    # # 提取68个特征点
                    shape = self.shape_predictor(img2, face)
                    # 计算人脸的128维的向量
                    face_descriptor = self.face_rec_model.compute_face_descriptor(
                        img2, shape)

                    dist.append(list(face_descriptor))
            else:
                pass
        return dist

    # 欧式距离
    def dist_o(self, dist_1, dist_2):
        dis = np.sqrt(sum((np.array(dist_1)-np.array(dist_2))**2))
        return dis

    def score(self, url_img_1, url_img_2):
        url_img_1 = glob.glob(url_img_1)[0]
        url_img_2 = glob.glob(url_img_2)[0]
        data = self.face_detection(url_img_1, url_img_2)
        goal = self.dist_o(data[0], data[1])
        # 判断结果,如果goal小于0.6的话是同一个人,否则不是。我所用的是欧式距离判别
        # return 1-goal
        return goal


# 调用 模型下载地址:http://dlib.net/files/
predictor_path = "shape_predictor_68_face_landmarks_GTX.dat"
face_rec_model_path = "dlib_face_recognition_resnet_model_v1.dat"
face_ = face_recognition(predictor_path, face_rec_model_path)
img_1 = './test/052/052_2.bmp'
img_2 = './test/052/052_1.bmp'
goal = face_.score(img_1, img_2)
print(goal)
# # i=j=k=0
# # while i<1:
# #     while j<10:
# #         while k<10:

# base_path = 'test'  # 遍历test文件夹
# for face_id in os.listdir(base_path):
#           # os.listdir()用于返回一个由文件名和目录名组成的列表,需要注意的是它接收的参数需要是一个绝对的路径。
#     face_dir = os.path.join(base_path, face_id)


#     for face_img in os.listdir(face_dir):  # 遍历,查找所有文件
#         if os.path.splitext(face_img)[-1] == '.bmp':  # 寻找所有.bmp文件
#                     # splitext分离文件名和扩展名
#                     # img = cv2.imread(os.path.join(face_dir, face_img))  # 读取图片并转换格式

#             img = cv2.imdecode(np.fromfile(os.path.join(
#                         face_dir, face_img), dtype=np.uint8), -1)
#                     # cv2.imdecode()函数从指定的内存缓存中读取数据,并把数据转换(解码)成图像格式;主要用于从网络传输数据中恢复出图像。cv2.imencode()函数是将图片格式转换(编码)成流数据,赋值到内存缓存中;主要用于图像数据格式的压缩,方便网络传输。
#                     # fromfile和tofile既可以读写二进制文件,也可以读写文本文件,是非常灵活的文件读取函数。他们之间互为对偶函数(对于任意一个逻辑函数,若把式中的运算符“.”换成“+”,“+”换成“.”;常量“0”换成“1”,“1”换成“0”,所得的新函数式为原函数式F的对偶式F′,也称对偶函数.对偶规则--如果两个函数式相等,则它们对应的对偶式也相等.)

# # 定义源文件夹的路径
# source_dir = "C:/Dd/vscode/end campus/borrow code/FaceProject/test"
# # 遍历源文件夹及其子文件夹,找出所有的子文件夹
# for root, dirs, files in os.walk(source_dir):
#     # 如果有子文件夹,进入子文件夹
#     if dirs:
#         # 遍历所有的子文件夹
#         for dir in dirs:
#             # 获取子文件夹的绝对路径
#             dir_path = os.path.join(root, dir)
#             # 获取子文件夹下的所有图片文件
#             image_files = [file for file in os.listdir(
#                 dir_path) if file.lower().endswith((".jpg", ".png", ".gif","bmp"))]
#             # 如果有图片文件,生成所有可能的两两排列组合
#             if image_files:
#                 image_pairs = itertools.combinations(image_files, 2)
#                 # 遍历所有的排列组合
#                 for pair in image_pairs:
#                     # 定义两个空列表,用于存储两张图片的内容和属性
#                     image1 = []
#                     image2 = []
#                     # 遍历每一对图片
#                     for i, image in enumerate(pair):
#                         # 获取图片的绝对路径
#                         image_path = os.path.join(dir_path, image)
#                         # 用PIL模块打开图片文件,获取其内容和属性
#                         image_obj = Image.open(image_path)
#                         width, height = image_obj.size
#                         format, mode = image_obj.format, image_obj.mode
#                         # 如果是第一张图片,将图片对象和相关信息添加到第一个列表中
#                         if i == 0:
#                             image1.append(
#                                 (image_obj, width, height, format, mode))
#                         # 如果是第二张图片,将图片对象和相关信息添加到第二个列表中
#                         else:
#                             image2.append(
#                                 (image_obj, width, height, format, mode))
#                     # 将两个列表分别赋值给两个变量
#                     variable1 = image1[0]
#                     variable2 = image2[0]
#                     goal= face_.score(variable1,variable2)

        最终代码输出的是欧氏距离,结合文献以及自身的一些条件, 本人设置的阈值是0.4。最终在实验结果中,在实验的500张图片中,出现了3个人脸检测不到的情况,识别准确率为97%,而且都是在侧脸状态下,这说明侧脸对提取人脸特征的影响还是比较大。但实际情况的准确率应该会高于此值,一个是现实生活中签到人脸侧脸的幅度并不会很大,一般都会正对摄像头;另一方面本实验设置一个身份只要有一张人脸与其他四张任意一张被判定为不同的脸就会认为错误,有些过于严格。其余情况下基于dlib改进的人脸识别基本都能识别出人脸,识别准确率来到了100%,由此可见dlib的识别能力非常强悍。

        然后是我自己系统的实验,也是发现在光照充足的情况下,经过dlib改进的人脸识别算法十分强悍,速度快、准确率高。但是当光照不足时,越是阴暗的教室,摄像头进光量不够,导致像素点模糊,不能很好的提取到人脸特征,极大影响识别速度甚至准确率。

        其实本毕设还有很多不足,出于时间和精力和技术实在小白,简单提供些小小的展望:在针对光照不足导致的识别率和速度降低的情况下,可以考虑引进双分支人脸检测器(DSFD)或LFFD等算法提升检测率和鲁棒性。

        为了进一步提高识别的精度与速度,本文还考虑了利用GPU来提高识别的效率。但是可能受限于电脑配置原因,利用GPU版本进行人脸识别会产生极大的延迟,往往摄像头前人脸已经移动了,画面隔了2、3秒才开始卡顿移动,在捣鼓无果后,本文暂且搁置此版本,留待以后深究。

        本系统的签到界面还比较简陋,功能缺乏,未来时间充足可以重新打造UI,并完善注册登录功能。还可以考虑将签到系统的实时性进行优化,以满足实际应用中对快速签到的需求。

        设计的人脸识别签到系统虽然满足日常课堂使用,但可以将其打包成一个单独的APP,未来可以将此系统此技术推广应用到更多的场景中去,例如企业考勤、社区管理等。

        现实签到可能还会存在着诸多问题,本人认为还可以引入在线活体检测来避免个别学生利用照片或者动态视频来假签到。

        最后,因为是小白,本篇文章写的我总感觉像是叙事文,写了我做了什么事情,而不是偏技术向,可能让大佬看笑话了,还是慢慢成长吧。

ps:主代码借鉴了Your Mei老哥的,验证代码太久忘了(失礼了哈哈哈哈哈)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

本科毕设——基于人脸识别的签到系统的研究 的相关文章

  • 【计算机开题报告】基于JSP的服装店销售管理系统

    1 选课目的意义 21世纪是一个信息化时代 随着中国经济的发展和人民生活水平的提高 服装商场的普及程度日益增大 竞争也在逐渐白炽化 为了进一步提高服装商场的经营效率 在服装店销售管理中引入计算机管理系统成为了必然的选择 由于中国环境的特殊性
  • 【计算机开题报告】二手车交易平台

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 选题目的 意义 如今时代网络技术正在快速发展 电子商务技术也以极为强势的姿态闯入人们的视野之中 随着人们生活质量的提升 为了对身边二手物品进行回收利用 二
  • 基于Java ssm教资考前指导系统的设计与实现

    一 技术介绍 Java语言 SSM框架 SpringBoot框架 JSP页面 Mysql数据库 IDEA Eclipse开发 有需要的同学 源代码和配套文档领取 加文章最下方的名片哦 二 资料介绍 完整源代码 前后端源代码 SQL脚本 配套
  • 基于Java ssm考研助手系统的设计与实现

    一 技术介绍 Java语言 SSM框架 SpringBoot框架 JSP页面 Mysql数据库 IDEA Eclipse开发 有需要的同学 源代码和配套文档领取 加文章最下方的名片哦 二 资料介绍 完整源代码 前后端源代码 SQL脚本 配套
  • Java毕业设计基于springboot企业车辆管理系统设计与实现

    一 项目介绍 随着时代在飞速进步 每个行业都在努力发展现在先进技术 通过这些先进的技术来提高自己的水平和优势 企业车辆管理系统当然不能排除在外 企业车辆管理系统是在实际应用和软件工程的开发原理之上 运用Java语言以及SpringBoot框
  • 【OCR】实战使用 - 如何提高识别文字的精准度?

    实战使用 如何提高文字识别的精准度 我们在平常使用OCR的时候 经常会出现文字识别不精准的情况 我们改如何提高文字识别的精度呢 以下是一些提高OCR Optical Character Recognition 光学字符识别 文字识别精准度的
  • 毕业设计:python图书管理系统+可视化+Django框架(附源码+论文)✅

    博主介绍 全网粉丝10W 前互联网大厂软件研发 集结硕博英豪成立工作室 专注于计算机相关专业 毕业设计 项目实战6年之久 选择我们就是选择放心 选择安心毕业 感兴趣的可以先收藏起来 点赞 关注不迷路 毕业设计 2023 2024年计算机毕业
  • 【计算机毕业设计】实验室预约管理

    身处网络时代 随着网络系统体系发展的不断成熟和完善 人们的生活也随之发生了很大的变化 人们在追求较高物质生活的同时 也在想着如何使自身的精神内涵得到提升 而读书就是人们获得精神享受非常重要的途径 为了满足人们随时随地只要有网络就可以看书的要
  • 【计算机毕业设计】基于web的山东红色旅游信息管理系统

    有效的处理想要的相关信息和如何传播有效的信息 一直是人类不断探索的动力 人类文明火种的传承都是通过了多种媒介作为载体 也是随着社会生产力的发展不断的更新 随着互联网的到来 信息传播与管理都上升了一个新的台阶 并且方便应用的同时也要考虑信息传
  • 【计算机毕业设计】汽车维修保养智能预约系统

    信息数据从传统到当代 是一直在变革当中 突如其来的互联网让传统的信息管理看到了革命性的曙光 因为传统信息管理从时效性 还是安全性 还是可操作性等各个方面来讲 遇到了互联网时代才发现能补上自古以来的短板 有效的提升管理的效率和业务水平 传统的
  • 用通俗易懂的方式讲解:图解 Transformer 架构

    文章目录 用通俗易懂方式讲解系列 1 导语 2 正文开始 现在我们开始 编码 从宏观视角看自注意力机制 从微观视角看自注意力机制 通过矩阵运算实现自注意力机制
  • 【图像融合】基于联合双边滤波和局部梯度能量的多模态医学图像融合研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 图像 文章
  • 【计算机毕业设计】二手图书交易系统

    随着世界经济信息化 全球化的到来和互联网的飞速发展 推动了各行业的改革 若想达到安全 快捷的目的 就需要拥有信息化的组织和管理模式 建立一套合理 动态的 交互友好的 高效的二手图书交易系统 当前的信息管理存在工作效率低 工作繁杂等问题 基于
  • 【计算机毕业设计】宝鸡文理学院学生成绩动态追踪系统

    研究开发宝鸡文理学院学生成绩动态追踪系统的目的是让使用者可以更方便的将人 设备和场景更立体的连接在一起 能让用户以更科幻的方式使用产品 体验高科技时代带给人们的方便 同时也能让用户体会到与以往常规产品不同的体验风格 与安卓 iOS相比较起来
  • 毕业设计- 基于深度学习的小样本时间序列预测算法 - Attention

    目录 前言 课题背景与意义 课题实现 一 数据集 二 设计思路 三 相关代码示例 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着准备考研 考公 考教资或者实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校
  • 【毕业设计选题】复杂背景下的无人机(UVA)夜间目标检测系统 python 人工智能 深度学习

    前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生级别难度的 对本科同学来说是充满挑战 为帮助大家顺利通过和节省时间
  • 2024 人工智能与大数据专业毕业设计(论文)选题指导

    目录 前言 毕设选题 选题迷茫 选题的重要性 更多选题指导 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生
  • 基于springboot+vue实现实企业任务管理追踪系统【附项目源码+论文说明】

    基于springboot vue实现实企业任务管理追踪系统 摘要 随着时代的进步 人们现在通过计算机线上化的办公方式成功的提升了日常办公的效率 通过线上办公能够有效地提升信息传递的效率 可以快速的完成任务的流程处理 邮件的发送等等功能 并且
  • 毕业设计:基于python人脸识别系统 LBPH算法 sqlite数据库 (源码)✅

    博主介绍 全网粉丝10W 前互联网大厂软件研发 集结硕博英豪成立工作室 专注于计算机相关专业 毕业设计 项目实战6年之久 选择我们就是选择放心 选择安心毕业 感兴趣的可以先收藏起来 点赞 关注不迷路 毕业设计 2023 2024年计算机毕业
  • 深度学习(5)--Keras实战

    一 Keras基础概念 Keras是深度学习中的一个神经网络框架 是一个高级神经网络API 用Python编写 可以在TensorFlow CNTK或Theano之上运行 Keras优点 1 允许简单快速的原型设计 用户友好性 模块化和可扩

随机推荐

  • jinfo如何使用

    jinfo是一个用于查看和调整Java虚拟机 JVM 配置参数的命令行工具 它可以在运行时获取和修改JVM的配置信息 包括内存分配 垃圾回收 运行时编译和其他设置 要使用jinfo 需要在命令行中输入以下命令 jinfo options
  • C# 学生成绩管理系统

    学生成绩管理系统 前言 一 我是一个菜鸟 二 已经有快一年没有发文章 一直在忙着毕业和找工作的事 如今生活逐渐趋于稳定 那么 勤劳的博主又开始更新了 哈哈 本次更新的是非常经典的学生成绩管理系统 这作为一个计算机专业的你来说 可能在大一 大
  • 让chatgpt从一个查询接口写接口测试用例

    要求 一个查询接口 入参有currPage order pageSize sortBy 所有入参为非必须 请帮我写出这个接口所有接口用例 根据你提供的信息 下面是该查询接口所有可能的接口用例 当所有参数都不传递时 查询接口会返回所有符合条件
  • UE44如何使用Geometry(BSP)笔刷,快速创建游戏原型?

    游戏原型搭建 如何快速搭建场景 一 好处1 防止同时也放大了对应的网格 如果像unity里面一样 R放大缩小以后 网格也会对应拉伸 失真 导致了材质会被拉伸或者缩小 1 选择Geometry 在BrushSetting里面 将X Y Z对应
  • ubuntu 20.04 安装 pycharm 2022.1 .3 及其卸载

    下载 官网下载 https www jetbrains com pycharm 安装 下载好的文件显示如下 打开终端 进入你刚下载的文件所在的文件夹目录 例如我的放在Downloads这个文件夹 cd Downloads 查看文件夹里的文件
  • ROS navigation分析:navigation框架

    前言 ROS navigation stack是ROS提供的一个非常重要且常用的模块 它的主要作用是实现机器人的定位 导航和避障功能 在ROS wiki上 Maintainer把它的功能归纳为 It takes in information
  • 从零实现DevOps(五):Jenkins+SSH远程部署SpringBoot项目

    上篇文章 我从安装Jenkins插件开始 给大家讲解了如何从Jenkis本地环境中 以启动jar包脚本的方式部署SpringBoot项目 但是呢 以咱们日常的开发来说 所有服务都部署在一台服务器上根本就不是一个合理的方案 更不可能在所有服务
  • 基于FPGA的一维卷积神经网络CNN的实现(五)数据量化(附代码)

    数据量化 环境 Pytorch Pycham Matlab 订阅后有问题 或者需要该节的文件直接加微信 Crazzy M 说明 上一节已经通过Matlab中基础的乘加运算进行了CNN网络的前向计算过程 该节利用Matlab将导出的CNN网络
  • Object365数据/论文说明

    总览 1 目标检测数据 365类 约600k训练图片 超过一千万的bboxes 迄今为止最大的目标检测数据集 全注释的 2 服务于更好的未来研究 局部敏感类型的任务 如目标检测 语义分割 3 在COCO测试下 Objects365上预训练的
  • IDEA插件系列(33):RestfulTool插件——Restful服务开发辅助工具集

    1 插件介绍 RestfulTool插件 一套 Restful 服务开发辅助工具集 提供了一个 Services tree 的显示窗口 双击 URL 直接跳转到对应的方法定义 一个简单的 http 请求工具 支持 Spring 体系 Spr
  • npm install安装依赖总结

    node下载地址 https nodejs org en download releases 可以看到node版本 npm版本 node module版本 1 npm的全局安装路径 查看默认值 npm get prefix 默认是C Use
  • 五、网络编程之网络 IO 模型的本质.md

    IO 网络模型的本质 前置概念 1 用户空间和内核空间 学习 Linux 或者 IO 网络模型时 经常可以看到两个词 User space 用户空间 和 Kernel space 内核空间 简单说 Kernel space 是 Linux
  • 实例创建流程_ABAQUS复合材料建模及基本分析流程

    案例1 创建开孔矩形复合材料常规壳层合板 层合板一端固定 另一端施加拉伸载荷 对模型进行分析 查看每层单方向的应力 对比云图和加载时的铺层额方向 理解铺层方向与lamina材料的概念 0 1建立几何模型 利用Abaqus中composite
  • javaSE基础 数据库操作3之数据库恢复

    java基础 数据库操作3之数据库恢复 流程 上一篇写了数据的备份 本篇将写数据库的恢复操作 思想 备份文件中写入的是将数据库表转换为sql后的语句 所以备份就只需将备份文件中的sql语句执行即可 数据库恢复 public static v
  • uni-app转小程序遇到的问题 (组件使用插槽的问题)(跨端兼容、条件编译)(小程序自定义胶囊按钮封装)(uni-app挂载原型链)

    1 uni app转小程序组件使用插槽的问题 uni app封装的组件使用问题 1 插槽样式 H5页面编译是有效果的 在小程序中编译的位置错误 它会跳出本来的插槽位置到最后 解决方法 使用父传子传递值 就可以继承组件的样式 封装的组件 使用
  • python使用from keras.utils import to_categorical出错

    python使用from keras utils import to categorical出错 我使用的python编辑环境Sublime Text keras的版本2 6 0 结果语句from keras utils import to
  • 【WiFi】国产WiFi芯片

    目录 1 概述 2 WiFi芯片的市场格局 3 中国的WiFi芯片公司 3 1 华为海思 3 2 乐鑫科技 3 3 博通集成 3 4 紫光展锐 3 5 康希通信 3 6 南方硅谷 4 国产WiFi芯片竞争格局 4 1 内卷WiFi 4 4
  • 芯片后端开发基础知识(二)

    目录 1 静态时序分析 Static Timing Analysis 2 波形的压摆 Slew 3 信号偏斜 Skew 4 时序路径 Clock Path 5 时序弧 Timing Arc 6 时钟域 Clock Domain 7 工作环境
  • 【IPC-UNIX网络编程】第4章管道和FIFO

    1 一个简单的客户 服务器例子 Client从标准输入 stdin 读进一个路径名 并把它写入IPC通道 Server从该IPC通道读出这个路径名 并尝试打开其文件 若server能打开该文件 它能读出其中的内容 并写入 不一定同一个 IP
  • 本科毕设——基于人脸识别的签到系统的研究

    本人普通四非本科 毕设选了这个比较大众且成熟的选题 四处借鉴后完成了论文 现在写写一些我完成毕设期间的历程 在完成论文开题报告后我开始寻求代码以期完成一个简易人脸识别签到系统的设计 开始我用了舍友选修课人工智能的大作业 他所采用的是传统的h