《百度AI人脸识别与检测》专栏为项目专栏,从零到一,从无到有开发一个学生人脸识别签到系统;主要用到的技术有百度开放平台中的人脸检测、人脸识别、Python图形界面开发PyQt5、线程的管理、以及通过python调用百度接口实现人脸检测、百度开放平台中人脸检测技术文档的理解等,由浅入深、由局部到整体的一个项目学习过程,如果你想对人脸识别感兴趣,对python的图形界面设计感兴趣,可以订阅本专栏,因为对你可能有帮助哦!
前文参考:
百度AI人脸识别与检测一:学生人脸识别签到系统简介及百度AI开放平台账号注册和人脸实例应用创建
百度AI人脸识别与检测二:学生人脸识别打卡签到系统主界面功能需求和设计以及通过Python实现界面运行
百度AI人脸识别与检测三:学生人脸识别打卡签到系统通过OpenCV实现电脑摄像头数据在Label控件上的实时显示
百度AI人脸识别与检测四:学生人脸识别打卡签到系统之百度AI人脸检测及相应程序异常处理
最近博客发布于6个月之前,这半年时间,学长也接收到很多小伙伴对《百度AI人脸识别与检测》专栏的催更,但也只做了简单的回应。在这脱更的半年时间中,学长完成了自己的毕业设计、毕业论文,同时拿到了自己工作单位的offer,成为了一名简简单单的程序员;拿到了自己的驾驶证,成为了新手司机中的一份子;拿到了属于自己大学四年的毕业证和学位证,成为了广大校友中的一名新人。从学校到职场,从学生到校友,是林君学长这脱更6个月的变化。自己的大学四年,感谢每一位教育过自己的老师,同时也感谢物联一班的每一位同学、朋友。但总之,学长再次开始了属于自己的创作之旅。祝愿我们每一位小伙伴都能在学习中进步和成长,不只是简单的应付作业哦!
上次博客我们介绍了百度AI的人脸检测,并通过界面实现了基本的人脸检测,本次博客林君学长将带大家了解、并实现基于百度AI的人脸识别。人脸识别和人脸检测有本质上的区别,人脸检测只是将图像或者视频中的人脸进行一个简单的检测,包括面部表情、颜值分数、是否佩戴眼镜等等;而人脸识别是指将图像或者视频的人脸进行检测,然后进行识别,不仅检测到这个人,还要识别出这个人是谁,叫什么名字,对应的了解到这个人的一系列信息。因此,人脸识别是在人脸检测到的基础上,通过对应的特征对比,来判断数据库中的人脸特征是否有与检测的到的人脸特征一致,如果一致,找到此人;如果不一致,查无此人!
百度AI人脸识别API接口是本地代码与百度云服务器进行连接的访问规则,本次博客需要对百度人脸识别进行访问,因此需要查看对应的API接口规则,根据规则编写访问接口的Python代码。而对于对应接口地址,在上次博客中已经介绍如何查看,本次博客需要用到的人脸识别主要用的是其中的人脸搜索接口。
最新的百度AI开放平台好像要进行实名认证,然后才能领取免费的人脸识别模块,打开人脸识别模块的功能,所以小伙伴记得去实名认证,然后免费领取一下
既然是人脸搜索,对应的应该有一个用户的脸部信息库,但由于初次运用,需要我们手动录取信息,在之后的项目中,可以通过软件自动添加人脸信息或者更新人脸信息,便不再需要在百度AI平台上进行手动添加。由于初次进行识别,我们需要穿件用户组合人脸信息,具体步骤如下所示:
(1)、打开控制台,找到人脸识别版块后选择管理应用,上次博客我们创建了应用实例。
(2)、在学生人脸打开签到系统实例中,点击查看人脸库。
(3)、点击新建组,ID我们取为class1,寓意为第一个班级,然后点击确定。
通过以上步骤,我们的班级就创建好了!接下来,我们为班级中添加学生吧!
班级创建完成,但是目前班级中没有成员,需要我们手动添加学生的脸部信息
(1)点击刚刚创建好的class1的用户组,进入到用户组里面添加用户。
(2)、点击新建用户。填入用户的名字(拼音或者英文名字,暂不支持中文),然后选择一张该用户(学生)的照片上传到用户组中,如下所示:
(3)、多次循环操作,便为班级添加好了学生,学长只添加了自己的,因此列表只有一项。
这样,我们班级一的人脸数据库便创建成功,下面便可以通过API接口进行对应代码的编写了!
在编写相关人脸识别的代码之前,我们首先需要了解如何访问百度AI开放平台中人脸识别模块中的人脸搜索功能。官方给了对于的API文档说明,链接如下:
人脸搜索API官方请求文档说明
在API文档中,我们首先得找请求说明,该部分有请求代码格式,以及请求参数说明,具体如下步骤。
(1)、请求格式标准代码Python
# encoding:utf-8
import requests
'''
人脸搜索
'''
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
params = "{\"image\":\"027d8308a2ec665acb1bdf63e513bcb9\",\"image_type\":\"FACE_TOKEN\",\"group_id_list\":\"group_repeat,group_233\",\"quality_control\":\"LOW\",\"liveness_control\":\"NORMAL\"}"
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)请求参数说明
重要的是以上四个,后面的参数请通过API官方文档自行了解,下面给出后面参数的解释截图:
有访问参数,在访问成功后,进行人脸识别,我们需要的便是识别之后的信息,因此读懂API的返回参数是项目中非常重要的技能。百度AI人脸识别项目中,需要根据返回参数进行对应的信息接收、处理和显示。人脸搜索API文档中,请参考返回说明章节。
(1)、返回参数介绍
face_token:人脸标志 ,String类型
user_list:返回用户信息 ,字典类型
(2)返回参数示例
在经过以上第一大步骤的进行之后,便可以开始我们接下来的代码编写了,在我们的《百度AI人脸识别与检测》项目中,通过以上了解的到的相关知识,完成人脸识别模块的功能。请继续往下面看!
在人脸识别完成之后,我们首先需要处理的便是人脸识别信息数据,将这些数据送到PyQT对于功能的控件上显示。但前面的代码我们了解到,在进行人脸检测是在子线程中,而数据的显示是在主线程中的窗口上面,因此,首先我们需要定义一个变量,用该变量存储人脸识别信息,并将子线程中得到的变量传递到主线程。而pyqt5中通过pyqtSignal信号接口实现了子线程与主线程之间的变量传递。
(1)、在detect.py文件中,定义transmit_data1信号,用于子线程与主线程中的人脸识别数据交互,信号类型为字符串型。
transmit_data1 = pyqtSignal(str) # 定义信号,用于子线程与主线程中的人脸识别数据交互
(1)通过人脸搜索API文档规则,在detect.py文件尾部编写访问方法,代码如下所示:
# 人脸识别搜索检测,只识别一个人
def face_search(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
params = {
"image": self.imageData,
"image_type": "BASE64",
"group_id_list": "class1",
}
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':
if data['result']['user_list'][0]['score'] > 90: #大于90分,意味人脸识别成功
del [data['result']['user_list'][0]['score']]
datetime = QDateTime.currentDateTime()#获取人脸打开时间
datetime = datetime.toString()#将获取到的时间转为字符串
data['result']['user_list'][0]['datetime'] = datetime#将获取到的时间添加到返回的数据中
list1 = [data['result']['user_list'][0]['user_id'],data['result']['user_list'][0]['group_id']]#去除名字和班级
self.transmit_data1.emit("学生签到成功\n学生信息如下:\n" + "姓名:" + list1[0] + "\n" + "班级:" + list1[1])#将信号发送给主线程
(1)人脸识别的基础是在人脸检测的基础上进行的,在人脸检测到之后,方可进行人脸识别,因此,在detect.py文件中的人脸检测函数中进行人脸识别方法的调用。
self.face_search()
(2)、代码添加位置如下:
(3)detect.py文件中的代码便修改完成,修改后的代码如下所示:
import requests
from PyQt5.QtCore import QThread, pyqtSignal, QDateTime
class detect_thread(QThread):
transmit_data = pyqtSignal(dict)#定义信号,用于子线程与主线程中的人脸检测数据交互
transmit_data1 = pyqtSignal(str) # 定义信号,用于子线程与主线程中的人脸识别数据交互
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)
self.condition=False
'''
接收主线程传递过来的图像
'''
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:
data = response.json()
self.face_search()
self.transmit_data.emit(dict(data))#如果返回结果正确,则将返回信息传递给主线程
# 人脸识别搜索检测,只识别一个人
def face_search(self):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
params = {
"image": self.imageData,
"image_type": "BASE64",
"group_id_list": "class1",
}
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':
if data['result']['user_list'][0]['score'] > 90: #大于90分,意味人脸识别成功
del [data['result']['user_list'][0]['score']]
datetime = QDateTime.currentDateTime()#获取人脸打开时间
datetime = datetime.toString()#将获取到的时间转为字符串
data['result']['user_list'][0]['datetime'] = datetime#将获取到的时间添加到返回的数据中
list1 = [data['result']['user_list'][0]['user_id'],data['result']['user_list'][0]['group_id']]#去除名字和班级
self.transmit_data1.emit("学生签到成功\n学生信息如下:\n" + "姓名:" + list1[0] + "\n" + "班级:" + list1[1])#将信号发送给主线程
下面就需要在主线程中进行数据接收显示了!
(1)、在function_window.py文件末尾添加显示信息方法
def get_seach_data(self,data):
self.plainTextEdit.setPlainText(data)
(2)、在function_window.py文件中绑定子线程中信号接收
self.detect.transmit_data1.connect(self.get_seach_data)
添加位置如下图:
到这里,人脸识别模块基本完成,function_window.py文件也修改完成。
(3)function_window.py文件修改后的代码内容如下所示:
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.detect.transmit_data1.connect(self.get_seach_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,"提示","请检查网络连接!")
def get_seach_data(self,data):
self.plainTextEdit.setPlainText(data)
(1)运行程序,打开签到
(2)开始签到
可以看到,签到的数据显示到对应的信息框中,签到成功!
(3)关闭签到
以上就是本次博客的全部内容,遇到问题的小伙伴记得留言评论,学长看到会为大家进行解答的,这个学长不太冷!
没有人永远青春,但永远有人正青春;只是,林君学长的青春暂告一段路而已!
陈一月的又一天编程岁月^ _ ^