需要源码的朋友可私信我!!!!
客户端界面
代码思路
1、开发环境
- 开发平台:win10
- 开发软件:PyCharm
- 界面开发:PyQt5
2、文档说明
- face_lib文件夹
- align_dlib.py文件:主要进行人脸对齐。
- face_recg.py文件: 进行人脸识别,其中阈值为0.4,可根据相应情况进行修改。
- my.api.py文件: 自己写的各种函数方法。
- udp_recv.py文件:包含进行udp协议传输视频的类。
- faces文件夹 每一个文件夹名字必须是英文字母,代表一个类,其每一个类别中可以有多张图片,但数量过多,识别过慢。图片必须是96*96大小的经过对齐的jpg格式图片。
- model文件夹 存放你训练的模型。
- gui.py文件 一些界面相关的函数。
- inference.py文件 神经网络函数。
- main.py文件
使用说明
- 点击“打开本地摄像头”按钮,在“摄像头采集信息”栏目中即会显示摄像头采集到的信息。
- 点击“打开网络摄像头”按钮,在“摄像头采集信息”栏目中即会显示通过WIFI传输到的视频信息。传输协议是UDP,目的是显示树莓派采集到的视频。
- 在打开摄像头后,点击获取人脸,左边会显示对齐后的人脸图片,大小为96*96.第一次使用时,数据集为空,在获得人脸图片后,点击新建人脸数据按钮,输入姓名,此时人脸图片保存在faces文件夹下。
- 当人脸图片上出现图片时,才可点击“人脸识别”按钮,点击后会在人脸图片下方显示预测的姓名和欧式距离。欧式距离,值越小,表示越相似。当距离大于一定阈值(默认为0.4),其姓名会显示为unknown。
- 点击“报错”功能按钮,把误识的人脸存入对应的人脸数据集中。例如:数据集中有5种人脸,分别标号为1、2、3、4、5,采集人脸其实为5号,却被误识为1、2、3、4或者unknown,这时点击“报错”按钮,把采集人脸存为5号人脸文件夹中即可。
- 阈值是影响人脸识别的关键因素,默认为0.4,可自行调整。
- 有2种模型可供下载,在model文件夹中可得下载链接
- 此种模型在微软MS-Celeb-1M数据集上训练,在LFW上测试的准确率可达87%以上。
- 此种模型在模型1的基础上,在LFW上继续训练,在LFW上测试的准确率可达91%以上。
- 可以自己训练模型
部分源码
main.py主函数文件:
import sys import time import cv2 import os import numpy as np from PyQt5 import QtWidgets, QtCore, QtGui from face_lib import align_dlib, face_recg from face_lib.udp_recv import UdpGetVideo # import helpers from gui import Ui_widget class MyDesignerShow(QtWidgets.QWidget, Ui_widget): _signal = QtCore.pyqtSignal(int) def __init__(self): super(MyDesignerShow, self).__init__() self.timer_camera = QtCore.QTimer() # 本地摄像头定时器 self.timer_udp_video = QtCore.QTimer() # UDP获取视频定时器 self.cap = cv2.VideoCapture() # 获得摄像头对象 self.CAM_NUM = 0 # 获取摄像头编号 self.time = time # 获取时间对象 self.PREDICTOR_PATH = './face_lib/shape_predictor_68_face_landmarks.dat' # 关键点提取模型路径 self.my_align = align_dlib.AlignDlib(self.PREDICTOR_PATH) # 获取人脸对齐对象 self.pix = QtGui.QPixmap() # 获取QPixmap对象 self.pic_show = None self.face_photo = None # 人脸图片 self.face_recog = face_recg.Recognize() # 获取人脸识别对象 self.setupUi(self) # 加载窗体 self.btn_close.clicked.connect(self.close) # 关闭程序 self.btn_local_camera.clicked.connect(self.get_local_camera) # 打开本地相机 self.btn_web_camera.clicked.connect(self.get_udp_video) # 打开UDP视频数据 self.btn_get_face.clicked.connect(self.get_face) # 得到人脸图像 self.btn_debug.clicked.connect(self.debug) # 报错 self.btn_new_face.clicked.connect(self.new_face) # 新建人脸数据 self.btn_face_recognize.clicked.connect(self.face_recognize) # 人脸识别 self.timer_camera.timeout.connect(self.show_local_camera) # 计时结束调用show_camera()方法 self.timer_udp_video.timeout.connect(self.show_udp_video) # 计时结束调用show_udp_video()方法 # 获取本地摄像头视频 def get_local_camera(self): if self.timer_udp_video.isActive(): # 查询网络摄像头是否打开 QtWidgets.QMessageBox.warning(self, u"Warning", u"请先关闭网络摄像头", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) elif not self.timer_camera.isActive(): flag = self.cap.open(self.CAM_NUM) if not flag: QtWidgets.QMessageBox.warning(self, u"Warning", u"请检测相机与电脑是否连接正确", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.timer_camera.start(30) # 30ms刷新一次 self.btn_local_camera.setText(u'关闭本地摄像头') else: self.timer_camera.stop() # 定时器关闭 self.cap.release() # 摄像头释放 self.label_camera.clear() # 视频显示区域清屏 self.graphicsView.show() self.btn_local_camera.setText(u'打开本地摄像头') def show_local_camera(self): flag, image = self.cap.read() self.pic_show = cv2.resize(image, (640, 480)) self.pic_show = cv2.cvtColor(self.pic_show, cv2.COLOR_BGR2RGB) showimage = QtGui.QImage(self.pic_show.data, self.pic_show.shape[1], self.pic_show.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.close() self.label_camera.setPixmap(self.pix.fromImage(showimage)) def get_udp_video(self): if self.timer_camera.isActive(): # 查询本地摄像头 QtWidgets.QMessageBox.warning(self, u"Warning", u"请先关闭本地摄像头", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) elif not self.timer_udp_video.isActive(): self.time.sleep(1) self.udp_video = UdpGetVideo() # 获取udp视频对象 self.timer_udp_video.start(30) # 10ms刷新一次 self.btn_web_camera.setText(u'关闭网络摄像头') else: self.timer_udp_video.stop() # 定时器关闭 self.udp_video.close() # udp视频接受关闭 self.label_camera.clear() # 视频显示区域清屏 self.graphicsView.show() self.btn_web_camera.setText(u'打开网络摄像头') def show_udp_video(self): image = self.udp_video.receive() # 从内存缓存区中读取图像 decimg = cv2.imdecode(image, 1) self.pic_show = cv2.resize(decimg, (640, 480)) self.pic_show = cv2.cvtColor(self.pic_show, cv2.COLOR_BGR2RGB) showimage = QtGui.QImage(self.pic_show.data, self.pic_show.shape[1], self.pic_show.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.close() self.label_camera.setPixmap(self.pix.fromImage(showimage)) def get_face(self): flag_cam = True if not self.timer_camera.isActive() and not self.timer_udp_video.isActive(): # 查询摄像头 QtWidgets.QMessageBox.warning(self, u"Warning", u"请先打开摄像头", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) flag_cam = False if flag_cam: pic = self.pic_show if pic is not None: # 使用dlib自带的frontal_face_detector作为我们的特征提取器 face_align = self.my_align.align(96, pic) if face_align is None: QtWidgets.QMessageBox.warning(self, u"Warning", u"没有检测到人脸", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: face_align = cv2.cvtColor(face_align, cv2.COLOR_RGB2BGR) # 转为BGR图片 self.face_photo = face_align face_align = cv2.cvtColor(face_align, cv2.COLOR_BGR2RGB) # 转为RGB图片 showimage = QtGui.QImage(face_align.data, face_align.shape[1], face_align.shape[0], QtGui.QImage.Format_RGB888) self.label_face.setPixmap(QtGui.QPixmap.fromImage(showimage)) else: QtWidgets.QMessageBox.warning(self, u"Warning", u"没有检测到图片", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) def face_recognize(self): if self.face_photo is None: QtWidgets.QMessageBox.warning(self, u"Warning", u"请先获取人脸图片", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.face_recog.reload_data() # 重载人脸数据集 names = self.face_recog.names if len(names) < 1: QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集为空!", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: image_data = np.array(self.face_photo) image_data = image_data.astype('float32') / 255.0 face_like = self.face_recog.whose_face(image_data) # 识别人脸 for i in range(len(names)): print('姓名:%s 欧式距离: %s' % (names[i], face_like[i])) self.textEdit.append("姓名:" + str(names[i]) + " 欧式距离: " + str(face_like[i])) face_id, distance = self.face_recog.get_face_id(face_like) if face_id is not None: self.label_name.setText(str(names[face_id])) else: self.label_name.setText('unknown') self.label_look.setText(str(distance)) def new_face(self): text, ok = QtWidgets.QInputDialog.getText(self, '英文字符!', '请输入你的英文名字:') if ok: print(text) if self.face_photo is not None: # 创建文件夹 paths = './faces/' + text + '/' if not os.path.exists(paths): os.makedirs(paths) # 保存图片 s_time = time.ctime().replace(' ', '_').replace(':', '_') cv2.imwrite(str(paths) + str(s_time) + '.jpg', self.face_photo) self.textEdit.append("人脸已存放在 " + paths + ' 文件夹中!!') else: QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集中已有相同人名!", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: QtWidgets.QMessageBox.warning(self, u"Warning", u"输入错误", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) def debug(self): self.face_recog.reload_data() # 重载人脸数据集 num = self.face_recog.max_num file_names = self.face_recog.names print(file_names) if num > 0: result, ok = QtWidgets.QInputDialog.getItem(self, u"人脸数据校验", u"把人脸数据存入对应的文件夹中,可增加人脸识别的准确性。确定把图片存放在以下文件夹中吗?", file_names, 1, False) if ok: if self.face_photo is not None: # 保存图片 s_time = time.ctime().replace(' ', '_').replace(':', '_') cv2.imwrite('./faces/' + result + '/' + str(s_time) + '.jpg', self.face_photo) self.textEdit.append("已保存在./faces/" + result + '文件夹下!!') else: QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集为空,请新建人脸数据!", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) def closeEvent(self, event): ok = QtWidgets.QPushButton() cacel = QtWidgets.QPushButton() msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, u"关闭", u"是否关闭!") msg.addButton(ok, QtWidgets.QMessageBox.ActionRole) msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole) ok.setText(u'确定') cacel.setText(u'取消') if msg.exec_() == QtWidgets.QMessageBox.RejectRole: event.ignore() else: if self.cap.isOpened(): self.cap.release() if self.timer_camera.isActive(): self.timer_camera.stop() if self.timer_udp_video.isActive(): self.timer_udp_video.stop() event.accept() if __name__ == "__main__": if not os.path.exists("./faces"): os.makedirs("./faces") app = QtWidgets.QApplication(sys.argv) myshow = MyDesignerShow() # 创建实例 myshow.show() # 使用Qidget的show()方法 sys.exit(app.exec_())
UI界面文件:
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'gui.ui' # # Created by: PyQt5 UI code generator 5.9.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_widget(object): def setupUi(self, widget): widget.setObjectName("widget") widget.resize(1024, 768) widget.setMinimumSize(QtCore.QSize(1024, 768)) widget.setMaximumSize(QtCore.QSize(1024, 768)) self.label_camera = QtWidgets.QLabel(widget) self.label_camera.setEnabled(True) self.label_camera.setGeometry(QtCore.QRect(360, 40, 640, 480)) self.label_camera.setMinimumSize(QtCore.QSize(640, 480)) self.label_camera.setMaximumSize(QtCore.QSize(640, 480)) self.label_camera.setText("") self.label_camera.setObjectName("label_camera") self.textEdit = QtWidgets.QTextEdit(widget) self.textEdit.setGeometry(QtCore.QRect(360, 550, 641, 181)) self.textEdit.setObjectName("textEdit") self.graphicsView = QtWidgets.QGraphicsView(widget) self.graphicsView.setGeometry(QtCore.QRect(360, 40, 640, 480)) self.graphicsView.setMinimumSize(QtCore.QSize(640, 480)) self.graphicsView.setMaximumSize(QtCore.QSize(640, 480)) self.graphicsView.setObjectName("graphicsView") self.label_2 = QtWidgets.QLabel(widget) self.label_2.setGeometry(QtCore.QRect(640, 20, 91, 16)) self.label_2.setObjectName("label_2") self.label_3 = QtWidgets.QLabel(widget) self.label_3.setGeometry(QtCore.QRect(630, 530, 111, 20)) self.label_3.setObjectName("label_3") self.layoutWidget = QtWidgets.QWidget(widget) self.layoutWidget.setGeometry(QtCore.QRect(40, 430, 281, 101)) self.layoutWidget.setObjectName("layoutWidget") self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setObjectName("gridLayout") self.label_name = QtWidgets.QLabel(self.layoutWidget) self.label_name.setText("") self.label_name.setObjectName("label_name") self.gridLayout.addWidget(self.label_name, 0, 1, 1, 1) self.label_look = QtWidgets.QLabel(self.layoutWidget) self.label_look.setText("") self.label_look.setObjectName("label_look") self.gridLayout.addWidget(self.label_look, 2, 1, 1, 1) self.label_5 = QtWidgets.QLabel(self.layoutWidget) self.label_5.setObjectName("label_5") self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1) self.label = QtWidgets.QLabel(self.layoutWidget) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.label_face = QtWidgets.QLabel(widget) self.label_face.setEnabled(True) self.label_face.setGeometry(QtCore.QRect(50, 210, 96, 96)) self.label_face.setMinimumSize(QtCore.QSize(96, 96)) self.label_face.setMaximumSize(QtCore.QSize(96, 96)) self.label_face.setObjectName("label_face") self.textEdit_2 = QtWidgets.QTextEdit(widget) self.textEdit_2.setGeometry(QtCore.QRect(40, 550, 281, 181)) self.textEdit_2.setObjectName("textEdit_2") self.layoutWidget1 = QtWidgets.QWidget(widget) self.layoutWidget1.setGeometry(QtCore.QRect(40, 70, 281, 121)) self.layoutWidget1.setObjectName("layoutWidget1") self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget1) self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setObjectName("gridLayout_2") self.btn_local_camera = QtWidgets.QPushButton(self.layoutWidget1) self.btn_local_camera.setObjectName("btn_local_camera") self.gridLayout_2.addWidget(self.btn_local_camera, 0, 0, 1, 1) self.btn_web_camera = QtWidgets.QPushButton(self.layoutWidget1) self.btn_web_camera.setObjectName("btn_web_camera") self.gridLayout_2.addWidget(self.btn_web_camera, 1, 0, 1, 1) self.btn_close = QtWidgets.QPushButton(self.layoutWidget1) self.btn_close.setObjectName("btn_close") self.gridLayout_2.addWidget(self.btn_close, 2, 0, 1, 1) self.btn_get_face = QtWidgets.QPushButton(widget) self.btn_get_face.setEnabled(True) self.btn_get_face.setGeometry(QtCore.QRect(230, 220, 91, 41)) self.btn_get_face.setMinimumSize(QtCore.QSize(0, 0)) self.btn_get_face.setMaximumSize(QtCore.QSize(100, 60)) self.btn_get_face.setObjectName("btn_get_face") self.btn_face_recognize = QtWidgets.QPushButton(widget) self.btn_face_recognize.setGeometry(QtCore.QRect(230, 280, 91, 41)) self.btn_face_recognize.setMinimumSize(QtCore.QSize(30, 30)) self.btn_face_recognize.setMaximumSize(QtCore.QSize(16777215, 100)) self.btn_face_recognize.setObjectName("btn_face_recognize") self.btn_new_face = QtWidgets.QPushButton(widget) self.btn_new_face.setGeometry(QtCore.QRect(50, 350, 80, 31)) self.btn_new_face.setMaximumSize(QtCore.QSize(100, 100)) self.btn_new_face.setObjectName("btn_new_face") self.btn_debug = QtWidgets.QPushButton(widget) self.btn_debug.setGeometry(QtCore.QRect(230, 350, 91, 31)) self.btn_debug.setMaximumSize(QtCore.QSize(100, 100)) self.btn_debug.setObjectName("btn_debug") self.retranslateUi(widget) QtCore.QMetaObject.connectSlotsByName(widget) def retranslateUi(self, widget): _translate = QtCore.QCoreApplication.translate widget.setWindowTitle(_translate("widget", "Form")) self.label_2.setText(_translate("widget", "摄像头采集信息")) self.label_3.setText(_translate("widget", "调试信息显示窗口")) self.label_5.setText(_translate("widget", " 欧式距离:")) self.label.setText(_translate("widget", " 预测姓名:")) self.label_face.setText(_translate("widget", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; vertical-align:sub;\">人脸图片</span></p></body></html>")) self.textEdit_2.setHtml(_translate("widget", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" "p, li { white-space: pre-wrap; }\n" "</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt; font-weight:600;\">说明:</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">1.初次使用软件,请确保model文件夹存有训练好的权重</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">2.初次使用请新建人脸数据,名字必须为英文字符</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">3.欧式距离越大,代表越不相似,越小代表越相似</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">4.每次点击</span><span style=\" font-size:8pt; font-weight:600; text-decoration: underline;\">人脸识别</span><span style=\" font-size:8pt;\">前都必须先</span><span style=\" font-size:8pt; font-weight:600; text-decoration: underline;\">获取人脸</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt; font-weight:600;\">步骤:</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">1.打开本地摄像头或者网络摄像头</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">2.点击获取人脸</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">3.点击人脸识别</span></p></body></html>")) self.btn_local_camera.setText(_translate("widget", "打开本地摄像头")) self.btn_web_camera.setText(_translate("widget", "打开网络摄像头")) self.btn_close.setText(_translate("widget", "关闭程序")) self.btn_get_face.setText(_translate("widget", "获取人脸")) self.btn_face_recognize.setText(_translate("widget", "人脸识别")) self.btn_new_face.setText(_translate("widget", "新建人脸数据")) self.btn_debug.setText(_translate("widget", "报错"))