百度AI人脸识别与检测四:学生人脸识别打卡签到系统之百度AI人脸检测及相应程序异常处理

《百度AI人脸识别与检测》专栏为项目专栏,从零到一,从无到有开发一个学生人脸识别签到系统;主要用到的技术有百度开放平台中的人脸检测、人脸识别、Python图形界面开发PyQt5、线程的管理、以及通过python调用百度接口实现人脸检测、百度开放平台中人脸检测技术文档的理解等,由浅入深、由局部到整体的一个项目学习过程,如果你想对人脸识别感兴趣,对python的图形界面设计感兴趣,可以订阅本专栏,因为对你可能有帮助哦!

前文参考:

百度AI人脸识别与检测一:学生人脸识别签到系统简介及百度AI开放平台账号注册和人脸实例应用创建
百度AI人脸识别与检测二:学生人脸识别打卡签到系统主界面功能需求和设计以及通过Python实现界面运行
百度AI人脸识别与检测三:学生人脸识别打卡签到系统通过OpenCV实现电脑摄像头数据在Label控件上的实时显示

时隔多月,学长再次提起CSDN创作之笔,鬼知道自己这几个月经历了什么,工作、游戏、堕落、总之,面临毕业的压力在我心中有貌似有解不开的压力,但总之,走过来,而且重新拿起了属于自己的创作之笔,希望现在写的东西还不是太过时。这几个月的沉沦,可惜了《百度AI人脸识别与检测》专栏的文章,该专栏本该早就应该结束的,却因为自己的原因,导致拖更到现在,特别对不起自己啦。话不多说,《百度AI人脸识别与检测》第四期,就这样开始吧!

上次博客,我们介绍了在PYQT5中的Label控件上面进行摄像头数据的实时显示,将摄像头的数据实时显示到我们设计界面的对应地方,为后续的人脸检测和人脸识别提供数据来源。

学生人脸识别打卡签到系统的前面框架便已经打好,有了数据,接下来就是对数据的识别了,而进行人脸识别的模型主要借助百度AI的人脸识别模型,通过调用百度的API接口,实现对笔记本摄像头数据的实时人脸检测,这里我们需要提出,人脸检测和人脸识别是有区别的,人脸检测主要检测人脸部分的特征包括性别、表情、预估年龄、是否佩戴眼镜、颜值等等;而人脸识别主要是将人脸定义到某个人,识别出他是谁。具体的本文章后面会介绍。一起学习吧!


一、了解百度AI人脸识别API接口调用形式

1、登录百度AI开放平台

1)、在该专栏的第一篇文章中,我们已经注册号百度AI开放平台的账号了,现在我们需要进入到百度AI开放平台控制台,查询其中人脸检测使用文档。
在这里插入图片描述
2)、进入控制台,点击左侧人脸识别部分
在这里插入图片描述
3)、点击管理应用,然后选择我们文章一中创建的应用实例《学生人脸打卡签到系统》
在这里插入图片描述
上面的AK和SK是我们需要记住的哦,因为后面访问百度AI需要用到!
4)、点击查看文档,然后选择API文档
在这里插入图片描述
在这里插入图片描述
API接口文档中的功能就是我们需要用到后续的各种功能,包括对人脸信息的增删改查、人脸检测、人脸识别等等,接下来,我们要做的就是选择人脸检测,读懂右边人脸检测的API文档

2、人脸检测API文档–Access_token

1)、access_token的获取理解
在文档中的数据中,我们可以知道,向百度官方发送请求是需要AK和SK的,而获取我们人脸检测的数据中,就需要AK和SK获取的access_token,如下所示:
在这里插入图片描述
2)、因此,在对一帧图像进行检测的时候,access_token是必须要发送的数据,而access_token的获取官网已经给出python代码,如下所示:

 # encoding:utf-8
import requests 

# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=【官网获取的AK】&client_secret=【官网获取的SK】'
response = requests.get(host)
if response:
    print(response.json())

其中,response.json() 就是我们需要得到的access_token,而AK和SK就是我们创建学生人脸打卡签到系统的实例中可以得到,如下所示:
在这里插入图片描述

3、人脸检测API文档–请求数据

1)、既然是人脸检测,因此,我们需要将人脸数据发送到百度AI开放平台中进行检测,然后返回结果,而百度AI开放平台对于发送的数据具有一定的规范要求,我们需要按照API文档上面给出的规范要求发送对于的格式才能获取到我们想要的数据,所以,读懂请求数据的规范格式是我们必须要理解的,格式如下:
在这里插入图片描述

# encoding:utf-8

import requests

'''
人脸检测与属性分析
'''

request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"

params = "{\"image\":\"027d8308a2ec665acb1bdf63e513bcb9\",\"image_type\":\"FACE_TOKEN\",\"face_field\":\"faceshape,facetype\"}"
access_token = '[调用鉴权接口获取的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:
    print (response.json())

2)、在发送的请求数据中,我们除了发送需要检测的图像之外,还需要包括:
url:人脸检测地址
image:需要进行人脸检测的图像
image_type:图像格式
face_field:需要检测的内容
access_token:对应创建的哪一个实例

而对于以上的要求,API文档中也已经给出,如下所示:
在这里插入图片描述
在这里插入图片描述
例如上传的图像类型必须是BASE64编码格式的图像,这就要求我们在上传数据中需要对图像进行对于的编码。

以上就是我们需要发送的请求参数的对应要求,看懂这些要求,我们才能编写相应的代码。理解请求参数,我们还需要理解接收的数据,如何从接收的数据收取到我们想要的信息才是一张图像检测的整个环节。

4、人脸检测API文档–接收数据

1)、找到API文档中返回说明:
在这里插入图片描述
以上截图为返回参数的部分参数,具体参数需要小伙伴仔细看,在请求参数中,我们请求检测哪些人脸特征,这里就会返回哪种人脸特征,返回参数的类型如API文档中的类型。


二、人脸数据的实时检测

1、Access_token获取

对于Access_token的获取应该在程序一开始运行的时候就应该直接获取,因此,Access_token的获取函数应该放在function_window.py文件中
1)、编写Access_token获取函数

	'''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return data
        else:
            QMessageBox(self,"提示","请检查网络连接!")

在这里插入图片描述
2)、在程序运行初始化的时候调用Access_token函数

import requests
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from cameraVideo import camera
from mainWindow import Ui_MainWindow
class function_window(Ui_MainWindow,QMainWindow):
    '''
    初始化函数
    '''
    def __init__(self):
        super(function_window, self).__init__()
        self.setupUi(self)
        self.label.setScaledContents(True)#设置图像自适应label显示框
        self.pushButton.clicked.connect(self.open_Sign)#打开签到事件绑定
        self.pushButton_2.clicked.connect(self.close_Sign)#关闭签到事件绑定
        self.access_token=self.get_accessToken()#获取Access_token访问令牌,并复制为全局变量
    '''
        打开签到
    '''
    def open_Sign(self):
        #启动摄像头
        self.cameravideo = camera()
        # 启动定时器进行定时,每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(10)
        # 每隔10毫秒产生一个信号timeout
        self.timeshow.timeout.connect(self.show_cameradata)
    '''
        摄像头数据显示
    '''
    def show_cameradata(self):
        #获取摄像头数据
        pic=self.cameravideo.camera_to_pic()
        #在lebel框中显示数据、显示画面
        self.label.setPixmap(pic)
    '''
        关闭签到
    '''
    def close_Sign(self):
        #关闭定时器,不再获取摄像头的数据
        self.timeshow.stop()
        self.timeshow.timeout.disconnect(self.show_cameradata)
        # 关闭摄像头
        self.cameravideo.colse_camera()
        #判断定时器是否关闭,关闭,则显示为自己设定的图像
        if self.timeshow.isActive() == False:
            self.label.setPixmap(QPixmap("image/1.jpg"))
        else:
            QMessageBox.about(self,"警告","关闭失败,存在部分没有关闭成功!")
    '''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return data
        else:
            QMessageBox(self,"提示","请检查网络连接!")

在这里插入图片描述
以上对于Access_token的访问令牌就已经生成,接下来便是人脸检测部分的实现,如下所示。

2、百度AI人脸检测

由于百度AI开放平台中的人脸检测数据的请求是免费的,但是一秒只让请求两次,也就是每500毫秒允许请求一次,这对于用户来说只是体验,而没有实时检测,如果想要实时检测我们就需要花钱购买。因此,对于每500毫秒上传一次图像进行检测来说,显示时延还是比较重的,但我们又不想花钱购买,怎么办呢?这时候我们便只有借助python线程来进行处理了,我们为人脸检测单独开一个线程,这个线程只做人脸检测,这样在原来的label画面上就不会出现卡顿的现象。

1)、创建检测线程文件detect.py

from PyQt5.QtCore import QThread
class detect_thread(QThread):
    def __init__(self):
        super(detect_thread,self).__init__()
        self.ok=True#循环控制变量
        self.condition = False#人脸检测控制变量,是否进行人脸检测
    #run函数执行结束代表线程结束
    def run(self):
        while self.ok==True:
            if self.condition==True:
                pass

当检测线程开始的时候,run函数自动执行,由于我们每隔500毫秒就需要向百度AI平台请求一次,因此,我们需要将run函数里面写成一个循环,当ok为True的时候,循环一直执行,当改变ok的状态时,循环就结束。而只有当每隔500毫秒主线程有图像传递过来的时候,condition的状态才会发生改变,线程才会调用人脸检测函数进行人脸检测。
2)、图像接收
主线程每隔500毫秒会向线程中发送一帧图像,因此线程中需要有一个函数接收传递过来的图像,如下:

from PyQt5.QtCore import QThread
class detect_thread(QThread):
    def __init__(self):
        super(detect_thread,self).__init__()
        self.ok=True#循环控制变量
        self.condition = False#人脸检测控制变量,是否进行人脸检测
    #run函数执行结束代表线程结束
    def run(self):
        while self.ok==True:
            if self.condition==True:
                pass
    '''
        接收主线程传递过来的图像
    '''
    def get_imgdata(self,data):
        #当窗口调用这个槽函数,就把传递的数据存放在线程的变量中
        self.imageData=data#将接收到图像数据赋值给全局变量
        self.condition=True#主线程有图像传递过来,改变condition的状态,run函数中运行人脸检测函数

在这里插入图片描述
3)、人脸检测请求
人脸检测是获取摄像头的数据然后送到百度AI开放平台,通过API文档,我们可以写出相应的Python的请求代码, 如下所示:

import requests
from PyQt5.QtCore import QThread, pyqtSignal


class detect_thread(QThread):
    transmit_data = pyqtSignal(dict)#定义信号,用于子线程与主线程中的数据交互
    def __init__(self,access_token):
        super(detect_thread,self).__init__()
        self.ok=True#循环控制变量
        self.condition = False#人脸检测控制变量,是否进行人脸检测
        self.access_token=access_token#主线程获取的access_token信息传递给子线程并设置为全局变量
    #run函数执行结束代表线程结束
    def run(self):
        while self.ok==True:
            if self.condition==True:
                pass
    '''
        接收主线程传递过来的图像
    '''
    def get_imgdata(self,data):
        #当窗口调用这个槽函数,就把传递的数据存放在线程的变量中
        self.imageData=data#将接收到图像数据赋值给全局变量
        self.condition=True#主线程有图像传递过来,改变condition的状态,run函数中运行人脸检测函数
    '''
        人脸检测
    '''
    def detect_face(self,base64_image):
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
        # 请求参数是一个字典,在字典中存储了,要识别的内容
        params = {
            "image": base64_image,  # 图片信息字符串
            "image_type": "BASE64",  # 图片信息的格式
            "face_field": "gender,age,beauty,mask,emotion,expression,glasses,face_shape",  # 请求识别人脸的熟悉,各个熟悉在字符中用,用逗号隔开
            "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:
            #print(response.json())
            data = response.json()
            self.transmit_data.emit(dict(data))#如果返回结果正确,则将返回信息传递给主线程

在这里插入图片描述
4)、run函数执行人脸检测

import requests
from PyQt5.QtCore import QThread, pyqtSignal


class detect_thread(QThread):
    transmit_data = pyqtSignal(dict)#定义信号,用于子线程与主线程中的数据交互
    def __init__(self,access_token):
        super(detect_thread,self).__init__()
        self.ok=True#循环控制变量
        self.condition = False#人脸检测控制变量,是否进行人脸检测
        self.access_token=access_token#主线程获取的access_token信息传递给子线程并设置为全局变量
    #run函数执行结束代表线程结束
    def run(self):
        while self.ok==True:
            if self.condition==True:
                self.detect_face(self.imageData)
    '''
        接收主线程传递过来的图像
    '''
    def get_imgdata(self,data):
        #当窗口调用这个槽函数,就把传递的数据存放在线程的变量中
        self.imageData=data#将接收到图像数据赋值给全局变量
        self.condition=True#主线程有图像传递过来,改变condition的状态,run函数中运行人脸检测函数
    '''
        人脸检测
    '''
    def detect_face(self,base64_image):
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
        # 请求参数是一个字典,在字典中存储了,要识别的内容
        params = {
            "image": base64_image,  # 图片信息字符串
            "image_type": "BASE64",  # 图片信息的格式
            "face_field": "gender,age,beauty,mask,emotion,expression,glasses,face_shape",  # 请求识别人脸的熟悉,各个熟悉在字符中用,用逗号隔开
            "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:
            #print(response.json())
            data = response.json()
            self.transmit_data.emit(dict(data))#如果返回结果正确,则将返回信息传递给主线程

在这里插入图片描述
到这里,线程中的事情就做完了,接下来就是主线程中如何启动子线程了。
5)、打开function_window.py(主线程),在点击开始签到的按钮功能函数中,打开线程

import requests
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from cameraVideo import camera
from mainWindow import Ui_MainWindow
from detect import detect_thread
class function_window(Ui_MainWindow,QMainWindow):
    '''
    初始化函数
    '''
    def __init__(self):
        super(function_window, self).__init__()
        self.setupUi(self)
        self.label.setScaledContents(True)#设置图像自适应label显示框
        self.pushButton.clicked.connect(self.open_Sign)#打开签到事件绑定
        self.pushButton_2.clicked.connect(self.close_Sign)#关闭签到事件绑定
        self.access_token=self.get_accessToken()#获取Access_token访问令牌,并复制为全局变量
    '''
        打开签到
    '''
    def open_Sign(self):
        #启动摄像头
        self.cameravideo = camera()
        # 启动定时器进行定时,每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(10)
        # 每隔10毫秒产生一个信号timeout
        self.timeshow.timeout.connect(self.show_cameradata)
        self.detect=detect_thread(self.access_token)#创建线程
        self.detect.start()#启动线程
    '''
        摄像头数据显示
    '''
    def show_cameradata(self):
        #获取摄像头数据
        pic=self.cameravideo.camera_to_pic()
        #在lebel框中显示数据、显示画面
        self.label.setPixmap(pic)
    '''
        关闭签到
    '''
    def close_Sign(self):
        #关闭定时器,不再获取摄像头的数据
        self.timeshow.stop()
        self.timeshow.timeout.disconnect(self.show_cameradata)
        # 关闭摄像头
        self.cameravideo.colse_camera()
        #判断定时器是否关闭,关闭,则显示为自己设定的图像
        if self.timeshow.isActive() == False:
            self.label.setPixmap(QPixmap("image/1.jpg"))
        else:
            QMessageBox.about(self,"警告","关闭失败,存在部分没有关闭成功!")
    '''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return self.access_token
        else:
            QMessageBox(self,"提示","请检查网络连接!")

在这里插入图片描述
6)、每隔500毫秒获取图像,并传入子线程

import base64

import cv2
import requests
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from cameraVideo import camera
from mainWindow import Ui_MainWindow
from detect import detect_thread
class function_window(Ui_MainWindow,QMainWindow):
    '''
    初始化函数
    '''
    def __init__(self):
        super(function_window, self).__init__()
        self.setupUi(self)
        self.label.setScaledContents(True)#设置图像自适应label显示框
        self.pushButton.clicked.connect(self.open_Sign)#打开签到事件绑定
        self.pushButton_2.clicked.connect(self.close_Sign)#关闭签到事件绑定
        self.access_token=self.get_accessToken()#获取Access_token访问令牌,并复制为全局变量
    '''
        打开签到
    '''
    def open_Sign(self):
        #启动摄像头
        self.cameravideo = camera()
        # 启动定时器进行定时,每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(10)
        # 每隔10毫秒产生一个信号timeout
        self.timeshow.timeout.connect(self.show_cameradata)
        self.detect=detect_thread(self.access_token)#创建线程
        self.detect.start()#启动线程
        # 签到500毫秒获取一次,用来获取检测的画面
        self.faceshow = QTimer(self)
        self.faceshow.start(500)
        self.faceshow.timeout.connect(self.get_cameradata)
    '''
        获取图像,并转换为base64格式
    '''
    def get_cameradata(self):
        camera_data1 = self.cameravideo.read_camera()
        # 把摄像头画面转化为一张图片,然后设置编码为base64编码
        _, enc = cv2.imencode('.jpg', camera_data1)
        base64_image = base64.b64encode(enc.tobytes())
        #产生信号,传递数据
        self.detect.get_imgdata(base64_image)
    '''
        摄像头数据显示
    '''
    def show_cameradata(self):
        #获取摄像头数据
        pic=self.cameravideo.camera_to_pic()
        #在lebel框中显示数据、显示画面
        self.label.setPixmap(pic)
    '''
        关闭签到
    '''
    def close_Sign(self):
        #关闭定时器,不再获取摄像头的数据
        self.timeshow.stop()
        self.timeshow.timeout.disconnect(self.show_cameradata)
        # 关闭摄像头
        self.cameravideo.colse_camera()
        #判断定时器是否关闭,关闭,则显示为自己设定的图像
        if self.timeshow.isActive() == False:
            self.label.setPixmap(QPixmap("image/1.jpg"))
        else:
            QMessageBox.about(self,"警告","关闭失败,存在部分没有关闭成功!")
    '''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return self.access_token
        else:
            QMessageBox(self,"提示","请检查网络连接!")

在这里插入图片描述
以上,主线程与子线程的数据交互就完成,下面我们将接收的数据进行显示

3、人脸检测数据显示

1)、人脸检测数据的接收
将子线程中的信号与主线程中的槽绑定,当信号触发,主线程自动调用接收数据函数并进行显示
在这里插入图片描述
2)、人脸检测数据的显示

#获取人脸检测数据并显示到文本框中
    def get_data(self,data):
        if data['error_code']!=0:
            self.plainTextEdit_2.setPlainText(data['error_msg'])
            return
        elif data['error_msg'] == 'SUCCESS':
            self.plainTextEdit_2.clear()
            # 在data字典中键为result对应的值才是返回的检测结果
            face_num = data['result']['face_num']
            # print(face_num)
            if face_num == 0:
                self.plainTextEdit_2.setPlainText("当前没有人或人脸出现!")
                return
            else:
                self.plainTextEdit_2.clear()
                self.plainTextEdit_2.appendPlainText("检测到人脸!")
                self.plainTextEdit_2.appendPlainText("——————————————")
            # 人脸信息获取['result']['face_list']是列表,每个数据就是一个人脸信息,需要取出每个列表信息(0-i)
            for i in range(face_num):
                age = data['result']['face_list'][i]['age']  # 年龄
                # print(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']  # 是否戴口罩
                # 往窗口中添加文本,参数就是需要的文本信息
                # print(age,gender,expression,beauty,face_shape,emotion,glasses,mask)
                self.plainTextEdit_2.appendPlainText("第" + str(i + 1) + "个学生人脸信息:")
                self.plainTextEdit_2.appendPlainText("——————————————")
                self.plainTextEdit_2.appendPlainText("年龄:" + str(age))
                if gender == 'male':
                    gender = "男"
                else:
                    gender = "女"
                self.plainTextEdit_2.appendPlainText("性别:" + str(gender))
                self.plainTextEdit_2.appendPlainText("表情:" + str(expression))
                self.plainTextEdit_2.appendPlainText("颜值分数:" + str(beauty))
                self.plainTextEdit_2.appendPlainText("脸型:" + str(face_shape))
                self.plainTextEdit_2.appendPlainText("情绪:" + str(emotion))
                if glasses == "none":
                    glasses="否"
                elif glasses == "common":
                    glasses="是:普通眼镜"
                else:
                    glasses="是:太阳镜"
                self.plainTextEdit_2.appendPlainText("是否佩戴眼镜:" + str(glasses))
                if mask == 0:
                    mask = "否"
                else:
                    mask = "是"
                self.plainTextEdit_2.appendPlainText("是否佩戴口罩:" + str(mask))
                self.plainTextEdit_2.appendPlainText("——————————————")
        else:
            print("人脸获取失败!")

3)、停止签到按钮改变
(1)、点击停止签到时,应该关闭线程,然后关闭计时器,如下:

import base64

import cv2
import requests
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from cameraVideo import camera
from mainWindow import Ui_MainWindow
from detect import detect_thread
class function_window(Ui_MainWindow,QMainWindow):
    '''
    初始化函数
    '''
    def __init__(self):
        super(function_window, self).__init__()
        self.setupUi(self)
        self.label.setScaledContents(True)#设置图像自适应label显示框
        self.pushButton.clicked.connect(self.open_Sign)#打开签到事件绑定
        self.pushButton_2.clicked.connect(self.close_Sign)#关闭签到事件绑定
        self.access_token=self.get_accessToken()#获取Access_token访问令牌,并复制为全局变量
    '''
        打开签到
    '''
    def open_Sign(self):
        #启动摄像头
        self.cameravideo = camera()
        # 启动定时器进行定时,每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(10)
        # 每隔10毫秒产生一个信号timeout
        self.timeshow.timeout.connect(self.show_cameradata)
        self.detect=detect_thread(self.access_token)#创建线程
        self.detect.start()#启动线程
        # 签到500毫秒获取一次,用来获取检测的画面
        self.faceshow = QTimer(self)
        self.faceshow.start(500)
        self.faceshow.timeout.connect(self.get_cameradata)
        self.detect.transmit_data.connect(self.get_data)
    #获取人脸检测数据并显示到文本框中
    def get_data(self,data):
        if data['error_code']!=0:
            self.plainTextEdit_2.setPlainText(data['error_msg'])
            return
        elif data['error_msg'] == 'SUCCESS':
            self.plainTextEdit_2.clear()
            # 在data字典中键为result对应的值才是返回的检测结果
            face_num = data['result']['face_num']
            # print(face_num)
            if face_num == 0:
                self.plainTextEdit_2.setPlainText("当前没有人或人脸出现!")
                return
            else:
                self.plainTextEdit_2.clear()
                self.plainTextEdit_2.appendPlainText("检测到人脸!")
                self.plainTextEdit_2.appendPlainText("——————————————")
            # 人脸信息获取['result']['face_list']是列表,每个数据就是一个人脸信息,需要取出每个列表信息(0-i)
            for i in range(face_num):
                age = data['result']['face_list'][i]['age']  # 年龄
                # print(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']  # 是否戴口罩
                # 往窗口中添加文本,参数就是需要的文本信息
                # print(age,gender,expression,beauty,face_shape,emotion,glasses,mask)
                self.plainTextEdit_2.appendPlainText("第" + str(i + 1) + "个学生人脸信息:")
                self.plainTextEdit_2.appendPlainText("——————————————")
                self.plainTextEdit_2.appendPlainText("年龄:" + str(age))
                if gender == 'male':
                    gender = "男"
                else:
                    gender = "女"
                self.plainTextEdit_2.appendPlainText("性别:" + str(gender))
                self.plainTextEdit_2.appendPlainText("表情:" + str(expression))
                self.plainTextEdit_2.appendPlainText("颜值分数:" + str(beauty))
                self.plainTextEdit_2.appendPlainText("脸型:" + str(face_shape))
                self.plainTextEdit_2.appendPlainText("情绪:" + str(emotion))
                if glasses == "none":
                    glasses="否"
                elif glasses == "common":
                    glasses="是:普通眼镜"
                else:
                    glasses="是:太阳镜"
                self.plainTextEdit_2.appendPlainText("是否佩戴眼镜:" + str(glasses))
                if mask == 0:
                    mask = "否"
                else:
                    mask = "是"
                self.plainTextEdit_2.appendPlainText("是否佩戴口罩:" + str(mask))
                self.plainTextEdit_2.appendPlainText("——————————————")
        else:
            print("人脸获取失败!")

    '''
        获取图像,并转换为base64格式
    '''
    def get_cameradata(self):
        camera_data1 = self.cameravideo.read_camera()
        # 把摄像头画面转化为一张图片,然后设置编码为base64编码
        _, enc = cv2.imencode('.jpg', camera_data1)
        base64_image = base64.b64encode(enc.tobytes())
        #产生信号,传递数据
        self.detect.get_imgdata(base64_image)
    '''
        摄像头数据显示
    '''
    def show_cameradata(self):
        #获取摄像头数据
        pic=self.cameravideo.camera_to_pic()
        #在lebel框中显示数据、显示画面
        self.label.setPixmap(pic)
    '''
        关闭签到
    '''
    def close_Sign(self):
        self.faceshow.stop()#计时器停止
        self.detect.ok=False#停止run函数运行
        self.detect.quit()#关闭线程
        #关闭定时器,不再获取摄像头的数据
        self.timeshow.stop()
        self.timeshow.timeout.disconnect(self.show_cameradata)
        # 关闭摄像头
        self.cameravideo.colse_camera()
        #判断定时器是否关闭,关闭,则显示为自己设定的图像
        if self.timeshow.isActive() == False:
            self.label.setPixmap(QPixmap("image/1.jpg"))
            self.plainTextEdit_2.clear()
        else:
            QMessageBox.about(self,"警告","关闭失败,存在部分没有关闭成功!")
    '''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return self.access_token
        else:
            QMessageBox(self,"提示","请检查网络连接!")

在这里插入图片描述

4、运行测试

1)、点击开始签到,进行人脸检测,检测结果如下所示:
在这里插入图片描述

5、程序异常处理

1)、当我们点击两次开始签到的时候程序会异常退出
这时候我们应该加一个状态变量,点击开始签到后判断该状态变量,进入后吗,状态改变,第二次点击不满足条件,就不会运行签到函数!
2)、当我们连续两次点击停止签到的时候程序也会异常退出
这个同理,也设置一个状态变量,最后完整代码如下所示:

import base64

import cv2
import requests
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from cameraVideo import camera
from mainWindow import Ui_MainWindow
from detect import detect_thread
class function_window(Ui_MainWindow,QMainWindow):
    '''
    初始化函数
    '''
    def __init__(self):
        super(function_window, self).__init__()
        self.setupUi(self)
        self.label.setScaledContents(True)#设置图像自适应label显示框
        self.pushButton.clicked.connect(self.open_Sign)#打开签到事件绑定
        self.pushButton_2.clicked.connect(self.close_Sign)#关闭签到事件绑定
        self.access_token=self.get_accessToken()#获取Access_token访问令牌,并复制为全局变量
        self.start_state=True
    '''
        打开签到
    '''
    def open_Sign(self):
        if self.start_state==True:
            # 启动摄像头
            self.cameravideo = camera()
            # 启动定时器进行定时,每隔多长时间进行一次获取摄像头数据进行显示
            self.timeshow = QTimer(self)
            self.timeshow.start(10)
            # 每隔10毫秒产生一个信号timeout
            self.timeshow.timeout.connect(self.show_cameradata)
            self.detect = detect_thread(self.access_token)  # 创建线程
            self.detect.start()  # 启动线程
            # 签到500毫秒获取一次,用来获取检测的画面
            self.faceshow = QTimer(self)
            self.faceshow.start(500)
            self.faceshow.timeout.connect(self.get_cameradata)
            self.detect.transmit_data.connect(self.get_data)
            self.start_state=False
        else:
            QMessageBox.about(self, "提示", "正在检测,请先关闭!")

    '''
        关闭签到
    '''
    def close_Sign(self):
        if self.start_state==False:
            self.faceshow.stop()  # 计时器停止
            self.detect.ok = False  # 停止run函数运行
            self.detect.quit()  # 关闭线程
            # 关闭定时器,不再获取摄像头的数据
            self.timeshow.stop()
            self.timeshow.timeout.disconnect(self.show_cameradata)
            # 关闭摄像头
            self.cameravideo.colse_camera()
            self.start_state=True
            # 判断定时器是否关闭,关闭,则显示为自己设定的图像
            if self.timeshow.isActive() == False:
                self.label.setPixmap(QPixmap("image/1.jpg"))
                self.plainTextEdit_2.clear()
            else:
                QMessageBox.about(self, "警告", "关闭失败,存在部分没有关闭成功!")
        else:
            QMessageBox.about(self, "提示", "请先开始检测!")
    #获取人脸检测数据并显示到文本框中
    def get_data(self,data):
        if data['error_code']!=0:
            self.plainTextEdit_2.setPlainText(data['error_msg'])
            return
        elif data['error_msg'] == 'SUCCESS':
            self.plainTextEdit_2.clear()
            # 在data字典中键为result对应的值才是返回的检测结果
            face_num = data['result']['face_num']
            # print(face_num)
            if face_num == 0:
                self.plainTextEdit_2.setPlainText("当前没有人或人脸出现!")
                return
            else:
                self.plainTextEdit_2.clear()
                self.plainTextEdit_2.appendPlainText("检测到人脸!")
                self.plainTextEdit_2.appendPlainText("——————————————")
            # 人脸信息获取['result']['face_list']是列表,每个数据就是一个人脸信息,需要取出每个列表信息(0-i)
            for i in range(face_num):
                age = data['result']['face_list'][i]['age']  # 年龄
                # print(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']  # 是否戴口罩
                # 往窗口中添加文本,参数就是需要的文本信息
                # print(age,gender,expression,beauty,face_shape,emotion,glasses,mask)
                self.plainTextEdit_2.appendPlainText("第" + str(i + 1) + "个学生人脸信息:")
                self.plainTextEdit_2.appendPlainText("——————————————")
                self.plainTextEdit_2.appendPlainText("年龄:" + str(age))
                if gender == 'male':
                    gender = "男"
                else:
                    gender = "女"
                self.plainTextEdit_2.appendPlainText("性别:" + str(gender))
                self.plainTextEdit_2.appendPlainText("表情:" + str(expression))
                self.plainTextEdit_2.appendPlainText("颜值分数:" + str(beauty))
                self.plainTextEdit_2.appendPlainText("脸型:" + str(face_shape))
                self.plainTextEdit_2.appendPlainText("情绪:" + str(emotion))
                if glasses == "none":
                    glasses="否"
                elif glasses == "common":
                    glasses="是:普通眼镜"
                else:
                    glasses="是:太阳镜"
                self.plainTextEdit_2.appendPlainText("是否佩戴眼镜:" + str(glasses))
                if mask == 0:
                    mask = "否"
                else:
                    mask = "是"
                self.plainTextEdit_2.appendPlainText("是否佩戴口罩:" + str(mask))
                self.plainTextEdit_2.appendPlainText("——————————————")
        else:
            print("人脸获取失败!")

    '''
        获取图像,并转换为base64格式
    '''
    def get_cameradata(self):
        camera_data1 = self.cameravideo.read_camera()
        # 把摄像头画面转化为一张图片,然后设置编码为base64编码
        _, enc = cv2.imencode('.jpg', camera_data1)
        base64_image = base64.b64encode(enc.tobytes())
        #产生信号,传递数据
        self.detect.get_imgdata(base64_image)
    '''
        摄像头数据显示
    '''
    def show_cameradata(self):
        #获取摄像头数据
        pic=self.cameravideo.camera_to_pic()
        #在lebel框中显示数据、显示画面
        self.label.setPixmap(pic)

    '''
        获取Access_token访问令牌
    '''
    def get_accessToken(self):

        # client_id 为官网获取的AK, client_secret 为官网获取的SK
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=TKGXdKC7WPWeADGHmFBN8xAr&client_secret=lsr1tAuxv3tRGmOgZTGgNyri667dfKGg'
        # 进行网络请求,使用get函数
        response = requests.get(host)
        if response:
            data = response.json()
            self.access_token = data['access_token']
            return self.access_token
        else:
            QMessageBox(self,"提示","请检查网络连接!")

现在当我们再次连续点击签到或者停止后,程序就不会退出啦,就会有一个异常提示,你该怎么做就怎么做啦!
在这里插入图片描述
以上就是本次博客的内容,希望通过对本次博客的阅读,大家能够合理的使用百度、阿里、腾讯、华为云的官方文档,并且了解AI人脸云检测的基本步骤,同时希望对学生人脸识别打卡签到系统有一定了解!


以上就是本次博客的全部内容,遇到问题的小伙伴记得留言评论,学长看到会为大家进行解答的,这个学长不太冷!

你想念一个人就会变成微风,轻轻掠过他的身边。就算他感觉不到,可就是你全部的努力。人生就是这样子的,每个人都会变成各自想念的风 ——从你的全世界路过

陈一月的又一天编程岁月^ _ ^

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈一月的编程岁月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值