一.前言概述
网络请求人脸检测线程与窗口获取画面线程通信:
通过信号与槽
在窗口中设计一个自定义信号,且信号存在参数(画面数据)
关联线程的一个函数(槽函数)
当窗口获取一次画面,就自定义产生一次信号,调用槽函数,获取到画面
在线程中读取画面数据,由线程的run函数进行网络请求,
窗口的信号与线程的槽函数关联就只是数据的传递,而不是直接执行网络请求
线程网络请求后,获取到百度AI的检测,需要在窗口中进行显示
需要由线程把数据传递给窗口:信号槽
打开人脸检测:
启动摄像头----摄像头进行工作
启动定时器----显示画面
创建线程------网络请求获取人脸检测数据
定时器2-------获取摄像头需要检测的画面
关闭人脸检测:
关闭定时器2(检测画面获取的定时器)
线程结束(停止检测工作)
人脸库管理功能:
创建一个库
添加人脸
删除人脸
修改
二.签到功能
2.1 启动签到
首先是对昨天的代码的修改。在数据传递过程中,数据类型是bytes,而之前定义的是string类型,所以只需修改成byte类型就可以成功上传画面信息
detect_data_signal = pyqtSignal(bytes)
`
def get_cameradata(self):
# 摄像头获取画面
camera_data = self.cameravideo.read_camera()
# 把摄像头画面转换成图片,然后设置编码base64编码格式数据
_, enc = cv2.imencode('.jpg', camera_data)
base64_image = base64.b64encode(enc.tobytes())
#产生信号,传递数据
self.detect_data_signal.emit(bytes(base64_image))
接下来只需将画面信息每隔500ms发送给detect.py
#当开启启动签到时,创建定时器,500ms,用作获取要检测的画面
self.facedetecttime = QTimer(self)
self.facedetecttime.start(500)
self.facedetecttime.timeout.connect(self.get_cameradata)
同样的,可以从detect.py中将百度返回的人脸信息传递回mywindow.py中
在detect.py中自定义一个信号槽
transmit_data = pyqtSignal(dict)
当窗口产生信号,调用槽函数,就把传递的数据,存放在线程的变量中
def get_base64(self,base64_image):
#当窗口产生信号,调用槽函数,就把传递的数据,存放在线程的变量中
self.base64_image = base64_image
self.condition = True
将编码格式为base64的图片发送给百度,获取人脸信息
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": 10
}
# 访问令牌
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()
#data是请求数据的结果,需要进行解析,单独拿出所需的数据内容,分开
self.transmit_data.emit(dict(data))
然后就可以通过定义的信号槽,将人脸信息返回到mywindow.py中
# facedetecttime定时器设置检测画面获取
self.detect_data_signal.connect(self.detectThread.get_base64)
并将获取到的人脸信息,打印到“学生人脸信息”中
#槽函数,获取检测数据
def get_detectdata(self,data):
if 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("-----------------")
这样当我们启动签到时,窗口就可以流畅地显示画面,同时在窗口右侧打印学生人脸信息。
2.2 结束签到
结束签到就需要将我们在启动签到时所设置地定时器全部关掉
#关闭定时器,不再设置检测画面获取
self.facedetecttime.stop()
#关闭定时器,不再去获取摄像头的数据
self.timeshow.stop()
self.timeshow.timeout.disconnect(self.show_cameradata)
同时还需要将detect.py,所启用地多线程关闭
这里通过定义一个变量OK,来完成while条件循序,当OK为True,则循环运行
OK = True
while self.OK:
if self.condition:
self.detect_face(self.base64_image)
self.condition = False
print("while finish")
当OK为False,线程关闭
#关闭检测线程
self.detectThread.OK = False
self.detectThread.quit()
self.detectThread.wait()
print(self.detectThread.isRunning())
最后关闭摄像头,画面设置为初始状态,清除学生人脸信息
# 关闭摄像头
self.cameravideo.close_camera()
#画面设置为初始状态
self.label.setPixmap(QPixmap("1.jpg"))
# 清除学生人脸信息(False)
self.plainTextEdit_2.setPlainText(" ")
三.创建用户
完成了启动和结束签到了后,我们就需要建立一个人脸库,通过百度提供的技术文档可以直接调用创建用户组的函数代码
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'])
但为了后续的方便的创建用户组,在函数中添加了QInputDialog,QMessageBox对话框来输入创建的用户组的名字以及是否创建成功
四.程序展示
启动程序,这里我对界面UI进行了改变,并在摄像头显示画面处设置了一张图片
点击签到,选择启动签到,可以明显感觉画面流畅了许多,同时左侧的学生人脸信息打印出了百度返回的人脸信息
点击结束签到,画面回到初始状态,但这里学生人脸信息的清除未能成功
点击人脸库管理,选择创建用户组,弹出对话框,输入用户组名字
这里说下百度的技术文档要求不能重名,如果是同一名字的创建用户组要求则会失败