本项目结合了上一篇文章基于face_recognition库实现人脸识别,通过利用Python的tkinter模块来设计考勤系统的图形化界面,此外结合openpyxl模块,实现将学生的信息和考勤打卡数据保存到表格中,方便查看和调用。该系统具备“刷脸打卡”、“新学生注册”等功能。
一、界面效果
在主界面分别按下“新学生注册”按钮和“刷脸打卡”按钮的过程效果如图:
二、开发环境
Windows10+python3.5.5+Vs Code(开发工具)
三、开发准备
在桌面新建一个工作薄,命名为Data.xlsx(见文章最后,附完整程序和表格链接)其中有两个小表格,分别是“学生信息表”和“学生考勤表”,在第一列和第二列填写如下信息。见文末链接。
四、实现过程
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
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/class/face/" #face文件夹
path0 = "C:/Users/Administrator/Desktop/class/" #桌面
top.mainloop() #进入进入等待与处理窗口事件
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 #返回图片数据
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 #跳出当前遍历循环
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) #返回按钮
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="完成注册!") #消息方框
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框架
五、备注
点我:Data工作薄和完成程序链接
链接:https://pan.baidu.com/s/19xhbOF7xFhPKO7_nqDd-QA?pwd=lyx4
提取码:lyx4