• 基于人脸识别的课堂考勤系统 tkinter+openpyxl+face_recognition


    本项目结合了上一篇文章基于face_recognition库实现人脸识别,通过利用Python的tkinter模块来设计考勤系统的图形化界面,此外结合openpyxl模块,实现将学生的信息和考勤打卡数据保存到表格中,方便查看和调用。该系统具备“刷脸打卡”、“新学生注册”等功能。

    一、界面效果

    主界面
    在主界面分别按下“新学生注册”按钮和“刷脸打卡”按钮的过程效果如图:
    实现过程

    二、开发环境

    Windows10+python3.5.5+Vs Code(开发工具)

    三、开发准备

    在桌面新建一个工作薄,命名为Data.xlsx(见文章最后,附完整程序和表格链接)其中有两个小表格,分别是“学生信息表”和“学生考勤表”,在第一列和第二列填写如下信息。见文末链接。
    Data工作薄

    四、实现过程

    1. 导入相关库函数
    注意face_recognition库没有的话需要pip install face_recognition进行安装。

    import cv2  #OpenCV库
    import openpyxl  #openpyxl库
    import os  #操作系统相关库,标准库
    import time  #time库
    import tkinter.messagebox  #messagebox消息框模块
    from tkinter import *  #tkinter库
    import face_recognition
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 主界面程序设计
    *(主界面程序放在最后,这里提前解释)*主界面放置了一个“刷脸打卡”、“新学生注册”、“退出系统”按钮。

    #主界面程序
    top = Tk()
    top.title("考勤系统")  #窗口标题
    top.geometry("500x300")  #窗口尺寸大小
    #主界面的框架组件main_frame
    main_frame = Frame(top) #第一层框架组件
    main_frame.place(relwidth=1,relheight= 1)  #参数设置为1表示框架铺满整个窗口
    Label(main_frame,text = "智能课堂考勤系统",bg = "white",font = ("黑体",20),
    width = 35,height = 2).place(x = 0,y = 0)  #放置“智能课堂考勤系统”标签
    Button(main_frame,text = "刷脸打卡",bg = "green",font = ("黑体",12),width = 12,height = 2,
    command = take_photo).place(x = 30,y = 150)  #放置“刷脸打卡”按钮
    Button(main_frame,text = "新学生注册",bg = "green",font = ("黑体",12),width = 12,height = 2,
    command = register).place(x = 200,y = 150)  #放置“新学生注册”按钮
    Button(main_frame,text = "退出系统",bg = "green",font = ("黑体",12),width = 12,height = 2,
    command = top.destroy).place(x =360,y = 150)  #放置“退出系统”按钮,destroy()直接销毁窗口
    #定义文件路径
    path = "C:/Users/Administrator/Desktop/face/"  #face文件夹
    path0 = "C:/Users/Administrator/Desktop/"  #桌面
    top.mainloop()  #进入进入等待与处理窗口事件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. 拍照函数get_photo()
    该函数用于调用笔记本摄像头或者其他外接摄像头。打开摄像头,然后通过capture.read()获取其中某一帧图像,通过cv2.imwrite()将图像保存到指定的路径,参数img是拍摄的图片名称,也包含指定的路经。

    #拍摄照片函数
    def get_photo(img):  #i参数mg,是拍摄的图片名称,也包含指定的路经
        capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)  #打开摄像头,参数0是摄像头端口,可以更换
        ref,frame=capture.read()  #将摄像头中的一帧数据保存下来
        cv2.imwrite(img,frame)  #将图片保存下来
        cv2.imshow(img,frame)  #窗口显示
        cv2.waitKey(1000) #延时1000ms,即1s
        cv2.destroyAllWindows()  #关闭窗口
        return img  #返回图片数据
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4. 刷脸打卡函数take_photo()
    在主界面按下“刷脸打卡”按钮后,跳转到该函数。通过调用get_photo()函数来拍摄一张学生刷脸的临时图片,将该图片进行图像处理编码后,通过os.listdir()方法遍历face文件夹里面的图片(这些图片同样需要进行图像处理编码),然后通过face_recognition.compare_faces进行人脸对比,人脸对比成功,获取图片的编号,通过编号num定位验证者在表格中的位置,然后将打卡时间和打卡日期填写在表格中。如果验证失败人脸标志位flag为0

    #刷脸打卡函数
    def take_photo():
        print("刷脸打卡")
        student = face_recognition.load_image_file(get_photo(path0 + "img.jpg" )) #加载人脸图像,参数调用拍照函数,获取一张验证者的图像
        student_rgb =cv2.cvtColor(student,cv2.COLOR_BGR2RGB)  #图像类型转换函数   
        student_encode = face_recognition.face_encodings(student_rgb)[0]  #给定一个图像,返回图像中每个面的128维人脸编码
        #返回face文件夹里面的内容,要从人脸库face文件夹中,获取验证者的身份
        pathDir = os.listdir(path)  #返回的是文件夹里的每一张图片
        flag = 0  #人脸标志位
        #测试另外一张图像
        for i in pathDir: #遍历6张图片
            face = face_recognition.load_image_file(path + i) #将文件夹里的每一张图片打开
            face_rgb = cv2.cvtColor(face,cv2.COLOR_BGR2RGB)  #将文件夹里的每一张图片进行转换
            face_encode = face_recognition.face_encodings(face_rgb)[0]  #将图像编码
            #compare_faces比较脸部编码列表和face文件夹内的候选编码,看看它们是否匹配,匹配成功返回[True]
            s = face_recognition.compare_faces([student_encode],face_encode,tolerance=0.49)  #参数tolerance是两张脸之间有多少距离才算匹配。该值越小对比越严格,0.49是我测出来最佳的
            if s == [True] :  #如果匹配成功则返回的是布尔型列表里的True
                flag = 1  #成功的话将标志位赋值为1
                num = i[:len(i)-4]  #获取图片的编号,去掉“.jpg”。例如是照片“7.jpg”,这里num就是7,就是注册者的编号
                break  #直接跳出循环
            else:
                flag = 0  #没有匹配成功则标志位为0
        if flag ==1:  #通过编号num在表格中找到验证者的位置
            #表格操作
            sheets = openpyxl.load_workbook(path0 + "Data.xlsx")  #打开“学生信息表”工作薄
            sheet1 = sheets[sheets.sheetnames[0]]  #获取Data工作薄里面的第一个表“学生信息表”
            max_row1 = sheet1.max_row  #获取第一个表格有数据最后一行的行数
            print("验证者的编号是:" , num)  #输出对比结果
            #print(type(num),type(sheet1.cell(max_row1,1).value))
            for j in range(2,max_row1+1):  #遍历‘学生信息表’
                if sheet1.cell(j,1).value == str(num):  #根据编号定位到对应学生在表格中的位置,例如杰克的编号是1,num=1,从“学生信息表”中第一列找到编号是1的那一行j,也就是第二行
                    name = sheet1.cell(j,2).value    #获取学生姓名
                    #获取当前打卡日期、时间,统计到学生考勤表
                    date = time.strftime("%Y-%m-%d",time.localtime()) #当天日期
                    Time = time.strftime("%H:%M",time.localtime()) #签到的时间
                    #打开学生考勤表,将学生的打卡日期添加到工作薄中
                    sheet2 = sheets[sheets.sheetnames[1]]  #获取Data工作薄里面的第二个表“学生考勤表”
                    max_row2 = sheet2.max_row  #获取表格有数据最后一行的行数
                    sheet2.cell(row = max_row2+1,column=1,value = date)  #写入签到日期
                    sheet2.cell(row = max_row2+1,column=2,value = name)  #写入签到学生
                    sheet2.cell(row = max_row2+1,column=3,value = Time)  #写入签到打卡时间
                    sheets.save(path0 + "Data.xlsx")  #保存到Data工作薄
                    print(str(sheet1.cell(j,2).value) +"签到成功," + "打卡时间是" + Time)  #播报签到情况
                    tkinter.messagebox.showinfo(title="提示",message=name + "打卡成功,"+ "打卡时间是" + Time)  #消息方框
                    break  #跳出当前遍历循环
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    5. 注册函数register()
    在主界面按下“新学生注册”按钮后,跳转到该函数。实现覆盖原来的框架组件main_frame,布局新的框架组件second_frame,用来放置新界面需要的按钮、标签组件。“确认”按钮关联Message函数,用来确认、保存新学生的注册信息的

    #注册函数
    def register():
        print("注册")
        main_frame.place_forget()  #隐藏main_frame框架
        second_frame = Frame(top)  #定义第二个框架second_frame,用于注册界面
        second_frame.place(relwidth=1,relheight= 1)  
        #布置新界面的组件:标签,输入文本框,按钮
        Label(second_frame,text = "欢迎使用人脸识别系统",font = ("黑体",18),
        width = 40,height = 2).place(x = 20,y = 0)
        Label(second_frame,text = "姓名",font = ("黑体",12),
        width = 20,height = 1).place(x = 50,y = 100)  #提示文字“姓名”
        name_entry = Entry(second_frame, font=("黑体", 12), width=20)
        name_entry.place(x = 240,y = 100)  #文本框组件,用于填写“姓名”
        Button(second_frame,text = "确认",font = ("黑体",12), width=10,
        command=lambda:Message(name_entry)).place(x = 100,y = 150)  #确认按钮
        Button(second_frame,text = "返回",font = ("黑体",12), width=10,
        command=lambda:back_main(second_frame)).place(x = 300,y = 150)  #返回按钮
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6. 学生信息存储Message()
    点击“确认”按钮后,该函数操作表格获取表格中有数据最后一行的行数max_row,作为新注册学生的序号,如下图所示。以该编号为新生图片命名“max_row.jpg”,然后调用摄像头给新学生拍照保存到face文件夹内。将学生的姓名、序号填写到“学生信息”表格中
    在这里插入图片描述

    #学生信息存储
    def Message(name_entry):
        #表格操作
        sheets = openpyxl.load_workbook(path0 + "Data.xlsx")  #打开“学生信息表”工作薄
        sheet1 = sheets[sheets.sheetnames[0]]  #获取Data工作薄里面的第一个表“学生信息表”
        max_row1 = sheet1.max_row  #获取第一个表格有数据最后一行的行数
        print("第几行:",max_row1)
        name = name_entry.get()  #利用get()方法获取Entry组件中输入的内容
        tkinter.messagebox.showinfo(title="提示",message="开始录入人脸信息!")  #消息方框
        image = str(max_row1)+".jpg"  #从表格获取行数,为照片进行编号
        get_photo(path + image)  #获取新学生的照片,保存到face文件夹里
        #填写学生信息,包括序号、姓名             
        sheet1.cell(max_row1 + 1,1).value = str(max_row1)  #学生序号,即照片编号
        print(type(sheet1.cell(max_row1 + 1,1).value))
        sheet1.cell(max_row1 + 1,2).value = name  #学生姓名
        sheets.save(path0 + "Data.xlsx")    
        print("注册成功,您的编号是"+ str(max_row1)+"号")
        tkinter.messagebox.showinfo(title="提示",message="完成注册!")  #消息方框
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7. 返回按钮back_main()函数

    #返回按钮,从当前界面(second_frame)返回主界面(main_frame)
    def back_main(second_frame):
        second_frame.place_forget()  #隐藏second_frame框架
        main_frame.place(relwidth=1,relheight= 1)  #显示main_frame框架
    
    • 1
    • 2
    • 3
    • 4

    五、备注

    Data工作薄和完成程序链接
    提取码:lyx4

  • 相关阅读:
    【ARM Trace32(劳特巴赫) 使用介绍 5 -- Trace32 scan dump 详细介绍】
    HTML5 游戏开发实战 | 黑白棋
    天锐绿盾 | 如何防止开发部门源代码泄露、外泄?
    现代挖掘机vr在线互动展示厅是实现业务增长的加速度
    转载 | 自动驾驶开源数据集总结
    搜索查找类指令
    mininet搭建SDN环境访问互联网【C4】
    Current request is not a multipart request 状态码:511 异常
    13SpringMVC中拦截器的配置(拦截规则)和多个拦截器的preHandle,postHandle执行顺序原理详解
    Java SE 16 新增特性
  • 原文地址:https://blog.csdn.net/lyx4949/article/details/125545962