基于学生课堂管理签到的AI人脸识别系统(四)

摄像头人脸检测与属性的窗口显示


在上一篇 文章中,我们实现了窗口时间的动态显示以及调用百度AI进行图片的人脸检测和属性的输出,这一次,我们将要把人脸识别的一些属性输出到界面中,然后实现使用摄像头的人脸属性检测

一、人脸属性窗口显示

在人脸检测返回的结果中,在face_list列表里,每一项值就是一个人脸信息,每个人脸信息用字典来表示,字典中包含什么信息由发送请求的参数face_field键值决定
返回结果内容的解析通过for循环取出每个字典数据(人脸),然后进行单独解析
在经过数据的解析后,进行显示
人脸信息显示主要有两个方面,一个是设计两个窗口部件(textEdit)用来显示信息,一个是解析完成后把信息依此添加到窗口中
所以,首先,我们要在窗口中重新添加两个窗口部件,用来显示信息,如下图,我们打开此次创建的界面,然后将之前的显示时间和日期的编辑框删除,替换为两个显示框,也可以再添加两个label用来提示方框中存放的数据是什么
在这里插入图片描述更改完成后保存,然后回到pycharm的终端中再次使用 pyuic5 源UI文件.ui -o 目标文件.py 命令将窗口转换为更改的窗口
然后回到 get_face 函数中,将内容修改为如下,其中的 face_num 是图片中的人脸数目,通过一个for循环,能够将每个检测到的人脸的信息都输出到显示框中

 	def get_face(self):

        # 获取一张图片(一帧画面)
        # getOpenFileName通过对话框的形式获取一张图片(.jpg)的路径
        path, ret = QFileDialog.getOpenFileName(self, "open picture", ".", "图片格式(*.jpg)")
        print(path)
        # 把图片转换成base64编码
        fp = open(path, 'rb')
        base64_image = base64.b64encode(fp.read())
        print(base64_image)
        # 发送请求的地址
        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:
            # print(response.json())
            data = response.json()
            # data是请求的结果数据,需要进行解析,单独拿出所需的结果数据,分开
            # 判断是否解析成功
            if data['error_msg'] == "SUCCESS":
                # 在data字典中,键为‘result’对应的值才是返回的检测结果
                # data['result']就是检测结果
                # 人脸数目
                self.plainTextEdit_2.clear()
                face_num = data['result']['face_num']
                if face_num == 0:
                    print("no face")
                    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(str(i+1)+"学生信息")
                    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))

修改之后,点击运行,同样首先我们需要点击 token 按钮,获取到访问令牌,然后再点击 face_post 进行图片的选择并检测
最后的结果如下图,在这里,我只选择了一个只有一张人脸的图片进行检测,所以在输出中也只有一个信息
在这里插入图片描述

二、摄像头的人脸检测

人脸识别操作:
1.从摄像头获取画面,进行人脸检测函数的调用,当调用一次函数就检测一次
2.百度网络API的QPS为2(每秒只有2次)
3.完成网络请求限制,需要单独去设置人脸检测(500ms),每500ms额外再获取一次摄像头画面,这次获取就用作检测
4.通过设置定时器,产生信号,关联槽函数,完成功能
5.原先10ms获取画面只用作,显示画面(流畅)
6.每500ms发送网络请求,调用post函数,等待返回结果,就会存在一定的等待时间,程序就在这里阻塞执行,那10ms会调用的显示函数就不会执行了,所以会存在一定卡顿现象
首先,我们需要将获取图片的操作改为调用摄像头的操作
所以需要在 get_face 函数中将下面的代码删除

# 获取一张图片(一帧画面)
# getOpenFileName通过对话框的形式获取一张图片(.jpg)的路径
path, ret = QFileDialog.getOpenFileName(self, "open picture", ".", "图片格式(*.jpg)")
print(path)
# 把图片转换成base64编码
fp = open(path, 'rb')
base64_image = base64.b64encode(fp.read())
print(base64_image)

更改为以下代码,调用摄像头获取人脸,然后再进行人脸检测

 # 摄像头获取画面
 camera_data = self.cameravideo.read_camera()
 # 把摄像头画面转化成图片,设置编码为base64编码格式数据
 _, enc = cv2.imencode('.jpg', camera_data)
 base64_image = base64.b64encode(enc.tobytes())

同时,我们也不需要在去点击按钮从而得到访问令牌和人脸检测,我们只需要在菜单栏中选择即可,所以我们就需要在程序开始运行时就获取到访问令牌
所以,我们在 __init__ 函数使用以下代码,直接获得访问令牌,而不用在通过点击按钮的方式

# 创建窗口就应该完成访问令牌的申请(获取)
self.get_accesstoken()

同时,我们需要在菜单栏中点击开始签到时调用摄像头,然后就进行人脸的检测,所以我们需要在点击开启签到的时候就调用人脸检测模块,所以我们在开启签到及开启摄像头的函数中添加代码,从而达到调用摄像头的同时开启人脸检测

def on_actionopen(self):
    # print("on_actionopen")
    # 启动摄像头
    self.cameravideo = camera()
    # 启动定时器,进行定时,每隔多长时间进行一次获取摄像头数据进行显示
    self.timeshow = QTimer(self)
    self.timeshow.start(50)
    # 50毫秒的定时器启动,每到50毫秒就会产生一个信号timeout
    self.timeshow.timeout.connect(lambda: self.show_cameradata())

    # 当开启检测启动时,创建定时器500毫秒,用作获取要检测的画面
    self.facedetecttime = QTimer(self)
    self.facedetecttime.start(500)
    self.facedetecttime.timeout.connect(self.get_face)

最后,我们再运行代码
当窗口界面出现时,我们已经得到了访问令牌,所以我们这时只需要点击 签到->开启签到 ,然后摄像头就会被调用,同时在右边的人脸信息显示中就会出现人脸检测的结果中一些属性的输出,这些属性是我们在进行代码的编写是自己定义的

在这里插入图片描述
mywindow.py 完整代码:

from mainwindow import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QFileDialog
from PyQt5.QtCore import QTimer, QDateTime, QDate, QTime, pyqtSignal
from cameravideo import camera
import requests, json
import cv2
import base64
from detect import detect_thread

'''
子类,继承UI_mainwindow与qmainwindow
Ui_MainWindow
    包含界面的设计,窗口中的窗口部件
QMainWindow
    包含整个界面窗口,窗口的操作
mywindow
    完整的窗口类
'''
class mywindow(Ui_MainWindow, QMainWindow):
    detect_data_signal = pyqtSignal(str)

    def __init__(self):
        super(mywindow, self).__init__()
        self.setupUi(self)   # 创建界面内容

        # 创建一个定时器对象
        self.datetime = QTimer(self)
        # 启动获取系统/日期时间的定时器,定时时间为500毫秒,500毫秒产生一次信号
        self.datetime.start(500)
        # 关联时间/日期的定时器信号与槽函数
        self .datetime.timeout.connect(self.date_time)

        # 创建窗口就应该完成访问令牌的申请(获取)
        self.get_accesstoken()

        '''
        # 信号与槽的关联
        # self.actionopen:指定对象
        # connect:关联(槽函数)
        # self.on_actionopen:关联函数是什么
        '''
        # 开启摄像头
        self.actionopen.triggered.connect(self.on_actionopen)
        # 关闭摄像头
        self.actionclose.triggered.connect(self.on_actionclose)

    # 函数功能:获取日期时间,添加到对应编辑器
    def date_time(self):
        # 获取日期
        date = QDate.currentDate()
        # print(date)
        # self.dateEdit.setDate(date)
        self.label_3.setText("日期:"+date.toString())
        # 获取时间
        time = QTime.currentTime()
        # print(time)
        # self.timeEdit.setTime(time)
        self.label_2.setText("时间:"+time.toString())
    '''
    信号槽功能:
        当某个组件设计了信号槽功能时,当信号产生,会主动调用槽函数去完成对应的一个功能
        信号:当以某种特定的操作,操作到这个组件时,就会主动产生对应操作的信号
    '''
    def on_actionopen(self):
        # print("on_actionopen")
        # 启动摄像头
        self.cameravideo = camera()
        # 启动定时器,进行定时,每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(50)
        # 50毫秒的定时器启动,每到50毫秒就会产生一个信号timeout
        self.timeshow.timeout.connect(lambda: self.show_cameradata())
        # 当开启检测启动时,创建定时器500毫秒,用作获取要检测的画面
        self.facedetecttime = QTimer(self)
        self.facedetecttime.start(500)
        self.facedetecttime.timeout.connect(self.get_face)
 
    # 关闭摄像头操作
    def on_actionclose(self):
        # print("on_actionclose")
        # 关闭摄像头
        self.cameravideo.close_camera()
        # 关闭定时器,不再去获取摄像头数据进行显示
        self.timeshow.stop()
        # 界面显示恢复到开始之前的画面
        self.label.setText("摄像头画面显示")


    # 作为摄像头获取数据实时显示画面的功能
    # 只要能够不断的重复调用这个函数,不断的从摄像头获取数据进行显示
    # 可以通过信号,信号关联当前函数,只要信号产生,函数就会被调用
    # 信号需要不断的产生,可以通过定时器,定时时间到达就会产生信号
    def show_cameradata(self):
        # 获取摄像头数据,转换数据
        pic = self.cameravideo.camera_to_pic()
        # 显示数据,显示画面
        self.label.setPixmap(pic)

    # 获取访问令牌
    def get_accesstoken(self):
        # print("get_accesstoken")
        # host对象是字符串对象存储是授权的服务地址---获取accesstoken的地址
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=【官网获取的AK】&client_secret=【官网获取的SK】'
        # 发送网络请求  requests网络库
        # 使用get函数发送网络请求,参数为网络请求的地址,执行时会产生返回结果,结果就是请求的结果
        response = requests.get(host)
        if response:
            # print(response.json())
            # json.load(response)
            data = response.json()
            self.access_token = data.get('access_token')
            print(self.access_token)

    # 人脸属性检测
    def get_face(self):

        # 摄像头获取画面
        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:
            # print(response.json())
            data = response.json()
            # data是请求的结果数据,需要进行解析,单独拿出所需的结果数据,分开
            # 判断是否解析成功
            if data['error_msg'] == "SUCCESS":
                # 在data字典中,键为‘result’对应的值才是返回的检测结果
                # data['result']就是检测结果
                # 人脸数目
                self.plainTextEdit_2.clear()
                face_num = data['result']['face_num']
                if face_num == 0:
                    print("no face")
                    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(str(i+1)+"学生信息")
                    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))

到这里,就完成了打开摄像头进行人脸检测,并输出相应的人脸属性。不过,在运行过程中我也发现,这样的检测过程是非常卡顿的,原因上面也提过了,就是因为这样的原因,所以造成了卡顿,从而影响检测的流畅性。那么该怎么解决呢,所以,我们这里使用多线程来解决这个问题,下一次再详细的解决这个问题了…

  • 0
    点赞
  • 1
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值