编写工具:Pycharm,Pyqt5designer
语言:python 版本:python3.7.4
数据库:sqlite3
代码:【项目实训】 基于人脸识别的课堂签到管理系统.zip
签到功能是该项目最核心的功能,将会着重介绍几个核心函数
首先先看下启动签到,该功能所需要调用的函数。然后对重要函数进行分析,以及代码展示
def on_actionopen(self):
list = self.getlist()
self.group_id, ret = QInputDialog.getText(self, "请输入所在用户组", "用户组信息\n" + str(list['result']['group_id_list']))
if self.group_id == '':
QMessageBox.about(self, "签到失败", "用户组不能为空\n")
return
group_status = 0
for i in list['result']['group_id_list']:
if i == self.group_id:
group_status =1
break
if group_status == 0:
QMessageBox.about(self, "签到失败", "该用户组不存在\n")
return
#启动摄像头
self.cameravideo = camera()
self.camera_status = True
#启动定时器,进行定时,每个多长时间进行一次获取摄像头数据进行显示,用作流畅显示画面
self.timeshow = QTimer(self)
self.timeshow.start(10)
#10ms的定时器启动,每到10ms就会产生一个信号timeout,信号没有()
self.timeshow.timeout.connect(self.show_cameradata)
#self.timeshow.timeout().connect(self.show_cameradata)
# self.show_cameradata()
#创建检测线程
self.create_thread()
# self.group_id.emit(str(group_id))
# self.group_id.connect(self.detectThread.get_group_id)
#当开启启动签到时,创建定时器,500ms,用作获取要检测的画面
# facedetecttime定时器设置检测画面获取
self.facedetecttime = QTimer(self)
self.facedetecttime.start(500)
self.facedetecttime.timeout.connect(self.get_cameradata)
self.detect_data_signal.connect(self.detectThread.get_base64)
self.detectThread.transmit_data.connect(self.get_detectdata)
self.detectThread.search_data.connect(self.get_search_data)
首先是调用getlist函数,该函数是用来获取学生所在的用户组(可以理解为班级),通过输入所在班级,在该班级搜寻该学生人脸,同时通过if条件语句判断是否为空,或者错误班级,防止程序卡死
接着是启动摄像头,通过设置一个定时器,来不断获取摄像头一帧画面
为了使界面显示画面流畅,这里采用多线程方式,每500ms发送一次,将学生图片发送给另一个线程
多线程类就接受学生人脸图片以及相关参数,并将其上传至百度智能云,进行人脸检测
def detect_face(self,base64_image):
'''
#对话框获取图片
#获取一张图片(一帧画面)
#getOpenFileName通过对话框的形式获取一个图片(.JPG)路径
path,ret = QFileDialog.getOpenFileName(self,"open picture",".","图片格式(*.jpg)")
#把图片转换成base64编码格式
fp = open(path,'rb')
base64_imag = base64.b64encode(fp.read())
print(base64_imag)
'''
# 摄像头获取画面
# camera_data = self.cameravideo.read_camera()
# # 把摄像头画面转换成图片,然后设置编码base64编码格式数据
# _, enc = cv2.imencode('.jpg', camera_data)
# base64_image = base64.b64encode(enc.tobytes())
# 发送请求的地址
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
# 请求参数是一个字典,在字典中存储,百度AI要识别的图片信息,属性内容
params = {
"image": base64_image, # 图片信息字符串
"image_type": "BASE64", # 图片信息格式
"face_field": "gender,age,beauty,expression,face_shape,glasses,emotion,mask", # 请求识别人脸的属性, 各个属性在字符串中用,逗号隔开
"max_face_num": 1
}
# 访问令牌
access_token = self.access_token
# 把请求地址和访问令牌组成可用的网络请求
request_url = request_url + "?access_token=" + access_token
# 参数:设置请求的格式体
headers = {
'content-type': 'application/json'}
# 发送网络post请求,请求百度AI进行人脸检测,返回检测结果
# 发送网络请求,就会存在一定的等待时间,程序就在这里阻塞执行,所以会存在卡顿现象
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_code'] != 0:
self.transmit_data.emit(data)
self.search_data.emit(data['error_msg'])
return
if data['result']['face_num'] > 0:
#data是请求数据的结果,需要进行解析,单独拿出所需的数据内容,分开
self.transmit_data.emit(dict(data))
self.face_search(self.group_id)
读取百度智能云返回主线程相关的人脸信息,若人脸数大于0,则执行人脸识别功能
# 人脸识别检测,只检测一个人
def face_search(self,group_id):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
params = {
"image": self.base64_image,
"image_type": "BASE64",
"group_id_list": group_id,
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_code'] == 0:
if data['result']['user_list'][0]['score'] > 90:
#存储要保存的签到数据,方便显示
del(data['result']['user_list'][0]['score'])
datetime = QDateTime.currentDateTime()
datetime = datetime.toString()
data['result']['user_list'][0]['datetime'] = datetime
key = data['result']['user_list'][0]['group_id']+data['result']['user_list'][0]['user_id']
if key not in self.sign_list.keys():
self.sign_list[key] = data['result']['user_list'][0]
self.search_data.emit("学生签到成功\n学生信息:"+data['result']['user_list'][0]['user_info'])
stu_data = data['result']['user_list'][0]['user_info']
info = stu_data.split('\n')
_, info_name = info[0].split(':')
_, info_class = info[1].split(':')
id = data['result']['user_list'][0]['user_id']
# self.add_sqlite(id, info_name, info_class, datetime)
self.search_sqlite(id)
for i in self.values:
search_id = i[0]
if search_id == id:
self.update_sqlite(id,info_name,info_class,datetime)
else:
self.add_sqlite(id,info_name,info_class,datetime)
else:
self.search_data.emit("学生签到失败,找不到对应学生")
当人脸匹配可信度大于90时,将学生签到信息添加到数据库,下面代码是sqlite3数据库相关操作
#创建数据库
def create_sqlite(self):
con = sqlite3.connect(r"stu_data.db")
c = con.cursor()
c.execute("create table student(id primary key ,name ,stu_class,datetime)")
print("创建成功")
#添加学生数据到数据库
def add_sqlite(self,id,name,stu_class,datetime):
con = sqlite3.connect(r"stu_data.db")
c = con.cursor()
value = (id,name,stu_class,datetime)
sql = "insert into student(id,name,stu_class,datetime) values(?,?,?,?)"
c.execute(sql,value)
# 提交
con.commit()
#更新学生数据库信息
def update_sqlite(self,id,name,stu_class,datetime):
con = sqlite3.connect(r"stu_data.db")
c = con.cursor()
# value = (name,stu_class,datetime,id)
sql = "update student set name=?,stu_class=?,datetime=? where id =?"
c.execute(sql,(name,stu_class,datetime,id))
con.commit()
#查询学生数据库信息
def search_sqlite(self,id):
con = sqlite3.connect(r"stu_data.db")
c = con.cursor()
sql = "select * from student where id=?"
self.values = c.execute(sql,(id,))
当线程完成人脸识别后,就会将学生信息和签到信息返回,这时就检测数据,并打印到主窗口
#槽函数,获取检测数据
def get_detectdata(self,data):
if data['error_code'] != 0:
self.plainTextEdit_2.setPlainText(data['error_msg'])
return
elif data['error_msg'] == 'SUCCESS':
# 在data字典中,键为'result'对应的值才是返回的检查结果
# data['result']才是检测结果
# 人脸数目
self.plainTextEdit_2.clear()
face_num = data['result']['face_num']
if face_num == 0:
self.plainTextEdit_2.appendPlainText("未测到检人脸")
return
else:
self.plainTextEdit_2.appendPlainText("测到检人脸")
# 人脸信息data['result']['face_list'],是列表,每个数据就是一个人脸信息,需要取出每个列表数据
# 每个人脸信息:data['result']['face_list'][0~i]人脸信息字典
for i in range(face_num):
# 通过for循环,分别取出列表的每一个数据
# data['result']['face_list'][i],就是一个人脸信息的字典
age = data['result']['face_list'][i]['age']
beauty = data['result']['face_list'][i]['beauty']
gender = data['result']['face_list'][i]['gender']['type']
expression = data['result']['face_list'][i]['expression']['type']
face_shape = data['result']['face_list'][i]['face_shape']['type']
glasses = data['result']['face_list'][i]['glasses']['type']
emotion = data['result']['face_list'][i]['emotion']['type']
mask = data['result']['face_list'][i]['mask']['type']
# 往窗口中添加文本,参数就是需要的文本信息
self.plainTextEdit_2.appendPlainText("-----------------")
self.plainTextEdit_2.appendPlainText("第" + str(i + 1) + "个学生信息:")
self.plainTextEdit_2.appendPlainText("-----------------")
self.plainTextEdit_2.appendPlainText("年龄: " + str(age))
self.plainTextEdit_2.appendPlainText("颜值分数: " + str(beauty))
self.plainTextEdit_2.appendPlainText("性别: " + str(gender))
self.plainTextEdit_2.appendPlainText("表情: " + str(expression))
self.plainTextEdit_2.appendPlainText("脸型: " + str(face_shape))
self.plainTextEdit_2.appendPlainText("是否佩戴眼镜: " + str(glasses))
self.plainTextEdit_2.appendPlainText("情绪: " + str(emotion))
if mask == 0:
mask = "否"
else:
mask = "是"
self.plainTextEdit_2.appendPlainText("是否佩戴口罩: " + str(mask))
self.plainTextEdit_2.appendPlainText("-----------------")
def get_search_data(self,data):
self.plainTextEdit.setPlainText(data)
这样启动签到功能就基本实现了
接着是结束签到,该功能所需要调用的函数。然后对重要函数进行分析,以及代码展示
def on_actionclose(self):
# 清除学生人脸信息(False)
# self.plainTextEdit_2.setPlainText(" ")
#关闭定时器,不再设置检测画面获取
self.facedetecttime.stop()
#self.facedetecttime.timeout.disconnect(self.get_cameradata)
#self.detect_data_signal.disconnect(self.detectThread.get_base64)
#self.detectThread.transmit_data.connect(self.get_detectdata)
#关闭检测线程
self.detectThread.OK = False
self.detectThread.quit()
self.detectThread.wait()
print(self.detectThread.isRunning())
# 关闭定时器,不再去获取摄像头的数据
self.timeshow.stop()
self.timeshow.timeout.disconnect(self.show_cameradata)
# 关闭摄像头
self.cameravideo.close_camera()
self.camera_status = False
print("1")
#显示本次签到情况
self.signdata = sign_data(self.detectThread.sign_list,self)
self.signdata.exec_()
if self.timeshow.isActive() == False and self.facedetecttime.isActive() == False:
# 画面设置为初始状态
self.label.setPixmap(QPixmap("1.jpg"))
self.plainTextEdit.clear()
self.plainTextEdit_2.clear()
else:
QMessageBox.about(self, "错误", "关闭签到失败\n")
关闭定时器,不再设置检测画面获取
关闭检测线程
关闭定时器,不再去获取摄像头的数据
关闭摄像头
显示本次签到情况,这里会弹出一个窗口,将学生签到情况显示在窗口,并提供两个按钮,取消和导出。导出能够将学生签到信息导出成TXT文件
#设置窗口内容不能被修改
self.signdata = signdata
self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
for i in signdata.values():
info = i['user_info'].split('\n')
rowcount = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowcount)
info_name = info[0].split(':')
self.tableWidget.setItem(rowcount, 0, QTableWidgetItem(info_name[1]))
info_class = info[1].split(':')
self.tableWidget.setItem(rowcount, 1, QTableWidgetItem(info_class[1]))
self.tableWidget.setItem(rowcount, 2, QTableWidgetItem(i['user_id']))
self.tableWidget.setItem(rowcount, 3, QTableWidgetItem(i['datetime']))
#导出按钮
self.pushButton.clicked.connect(self.save_data)
#取消按钮
self.pushButton_2.clicked.connect(self.close_window)
def close_window(self):
self.reject()
def save_data(self):
#打开对话框,获取要导出的数据文件名
filename,ret = QFileDialog.getSaveFileName(self,"导出数据",".","TXT(*.txt)")
f = open(filename,"w")
for i in self.signdata.values():
info = i['user_info'].split('\n')
_,info_name = info[0].split(':')
_,info_class = info[1].split(':')
f.write(str(info_name+" "+info_class+" "+i['user_id']+" "+i['datetime'] ))
f.close()
self.accept()
添加用户组实现代码
def add_group(self):
#打开对话框,进行输入用户组
group,ret = QInputDialog.getText(self,"添加用户组","请输入用户组(由数字、字母、下划线组成)")
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/add"
params = {
"group_id":group
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
message = response.json()
if message['error_msg'] == 'SUCCESS':
QMessageBox.about(self,"用户组创建结果","用户组创建成功")
else:
QMessageBox.about(self,"用户组创建结果","用户组创建失败\n"+message['error_msg'])
删除用户组实现代码
def del_group(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/delete"
list = self.getlist()
group, ret = QInputDialog.getText(self, "用户组列表", "用户组信息\n" + str(list['result']['group_id_list']))
# 删除,需要知道那些组
params = {
"group_id": group # 要删除的用户组的id
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_msg'] == 'SUCCESS':
QMessageBox.about(self, "用户组删除结果", "用户组删除成功")
else:
QMessageBox.about(self, "用户组删除结果", "用户组删除失败\n" + data['error_msg'])
查询用户组实现代码
#获取用户组
def getlist(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/getlist"
params = {
"start":0,"length":100
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_msg'] == 'SUCCESS':
return data
else:
QMessageBox.about(self, "获取用户组结果", "获取用户组失败\n" + data['error_msg'])
添加用户实现代码
def add_user(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add"
if self.camera_status:
QMessageBox.about(self,"摄像头状态","摄像头已打开,正在进行人脸签到\n请关闭签到,再添加用户")
return
list = self.getlist()
#创建一个窗口来选择这些内容
window = adduserwindow(list['result']['group_id_list'],self)
#新创建窗口,通过exec()函数一直执行,阻塞执行,窗口不进行关闭
#exec()函数不会退出,关闭窗口才会结束
window_status = window.exec_()
#进行判断,判断是否点击确定进行关闭
if window_status != 1:
return
#请求参数,需要获取人脸:转换人脸编码,添加的组id,添加的用户id,新用户的id信息
params = {
"image":window.base64_image,#人脸图片
"image_type":"BASE64",#人脸图片编码
"group_id":window.group_id,#组id
"user_id":window.user_id,#新用户id
"user_info":'姓名:'+window.msg_name+'\n'
+'班级:'+window.msg_class#用户信息
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_msg'] == 'SUCCESS':
QMessageBox.about(self, "人脸创建结果", "人脸创建成功")
else:
QMessageBox.about(self, "人脸创建结果", "用户组创建失败\n"+ data['error_msg'])
删除用户实现代码
def del_face_token(self,group,user,face_token):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/face/delete"
params = {
"user_id": user,
"group_id": group,
"face_token":face_token
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_msg'] == 'SUCCESS':
QMessageBox.about(self, "人脸删除结果", "人脸删除成功")
else:
QMessageBox.about(self, "人脸删除结果", "用户组删除失败\n" + data['error_msg'])
def del_user(self):
#查询用户人脸信息(face_token)
#获取用户组
list = self.getlist()
group,ret = QInputDialog.getText(self,"用户组获取","用户组信息\n"+str(list['result']['group_id_list']))
group_status = 0
if self.group_id == '':
QMessageBox.about(self, "删除失败", "用户组不能为空\n")
return
for i in list['result']['group_id_list']:
if i == group:
group_status = 1
break
if group_status == 0:
QMessageBox.about(self, "删除失败", "该用户组不存在\n")
return
#获取用户
userlist = self.get_userlist(group)
user,ret = QInputDialog.getText(self,"用户获取","用户信息\n"+str(userlist['result']['user_id_list']))
user_status = 0
if user == '':
QMessageBox.about(self, "删除失败", "用户不能为空\n")
return
for i in userlist['result']['user_id_list']:
if i == user:
user_status = 1
break
if user_status == 0:
QMessageBox.about(self, "删除失败", "该用户不存在\n")
return
#获取用户人脸列表
face_list = self.user_face_list(group,user)
for i in face_list['result']['face_list']:
self.del_face_token(group,user,i['face_token'])
更新用户实现代码
#更新用户人脸
def update_user(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/update"
if self.camera_status:
QMessageBox.about(self,"摄像头状态","摄像头已打开,正在进行人脸签到\n请关闭签到,再添加用户")
return
list = self.getlist()
#创建一个窗口来选择这些内容
window = adduserwindow(list['result']['group_id_list'],self)
#新创建窗口,通过exec()函数一直执行,阻塞执行,窗口不进行关闭
#exec()函数不会退出,关闭窗口才会结束
window_status = window.exec_()
#进行判断,判断是否点击确定进行关闭
if window_status != 1:
return
#请求参数,需要获取人脸:转换人脸编码,添加的组id,添加的用户id,新用户的id信息
params = {
"image":window.base64_image,#人脸图片
"image_type":"BASE64",#人脸图片编码
"group_id":window.group_id,#组id
"user_id":window.user_id,#新用户id
"user_info":'姓名:'+window.msg_name+'\n'
+'班级:'+window.msg_class#用户信息
}
access_token = self.access_token
request_url = request_url + "?access_token=" + access_token
headers = {
'content-type': 'application/json'}
response = requests.post(request_url, data=params, headers=headers)
if response:
data = response.json()
if data['error_msg'] == 'SUCCESS':
QMessageBox.about(self, "人脸更新结果", "人脸更新成功")
else:
QMessageBox.about(self, "人脸更新结果", "用户组更新失败\n" + data['error_msg'])
摄像头类代码
import cv2
import numpy as np
from PyQt5.QtGui import QPixmap,QImage
'''
摄像头操作:创建类对象完成摄像头操作,所以可以把打开摄像头与创建类对象操作合并
__init__函数完成摄像头的配置打开
'''
class camera():
def __init__(self):
#VideoCapture类对视频或调用摄像头进行读取操作
#参数 filename;device
#0表示默认的摄像头进行打开
#self.capture表示打开的摄像头对象
self.capture = cv2.VideoCapture(0)
#isOpened函数返回一个布尔值,来判断是否摄像头初始化成功
# if self.capture.isOpened():
# print("isOpened")
#定义一个多维数组,存取画面
self.currentframe = np.array([])
#读取摄像头数据
def read_camera(self):
#ret是否成功,pic_data数据
ret,data = self.capture.read()
if not ret:
print("获取摄像头数据失败")
return None
return data
#数据转换成界面能显示的数据格式
def camera_to_pic(self):
pic = self.read_camera()
#摄像头是BGR方式存储,首先要转换成RGB
self.currentframe = cv2.cvtColor(pic,cv2.COLOR_BGR2RGB)
#设置宽高
#self.currentframe = cv2.cvtColor(self.currentframe,(640,480))
#转换格式(界面能够显示的格式)
#获取画面的宽度和高度
height,width = self.currentframe.shape[:2]
#先转换成QImage类型的图片(画面),创建QImage类对象,使用摄像头的画面
#QImage (data, width, height , format)创建:数据,宽度,高度,格式
qimg = QImage(self.currentframe,width,height,QImage.Format_RGB888)
qpixmap = QPixmap.fromImage(qimg)
return qpixmap
def close_camera(self):
self.capture.release()
代码下载:【项目实训】 基于人脸识别的课堂签到管理系统.zip
详细教程:
基于人脸识别的课堂签到管理系统(一)—环境设置以及简单的QT界面设计
基于人脸识别的课堂签到管理系统(二)—摄像头显示
基于人脸识别的课堂签到管理系统(三)—实时时间显示以及百度AI人脸识别
基于人脸识别的课堂签到管理系统(四)—摄像头上传实时数据,百度AI读取并返回信息以及多线程操作
基于人脸识别的课堂签到管理系统(五)—启动/结束签到,以及在百度智能云创建用户组
基于人脸识别的课堂签到管理系统(六)—删除,查询用户组以及人脸的添加,删除,更新
基于人脸识别的课堂签到管理系统(七)—实现人脸搜索,完善签到功能