博客汇总:Python | 人脸识别系统 — 博客索引

GitHub地址:Su-Face-Recognition

注:阅读本博客前请先参考

工具安装、环境配置:Python | 人脸识别系统 — 简介

UI界面设计:Python | 人脸识别系统 — UI界面设计

UI事件处理:Python | 人脸识别系统 — UI事件处理

摄像头画面展示:Python | 人脸识别系统 — 摄像头画面展示

更多关于人脸识别库 face_recognition 的基础使用教程请移步:face_recognition GitHub

一、初始化

初始化 isFaceRecognition_flag 标志判断当前人脸识别状态。

按钮绑定人脸识别判断器 recognize_face_judge 方法。

self.isFaceRecognition_flag = False# 是否打开人脸识别标志self.recognition_button.clicked.connect(self.recognize_face_judge)

二、判断器

通过self.isFaceRecognition_flag 标志判断当前人脸识别是否打开。

# 人脸识别判断器def recognize_face_judge(self):if not self.cap.isOpened():QMessageBox.information(self, "提示", self.tr(u"请先打开摄像头"))else:# 点击人脸识别时,人脸识别是关闭的if not self.isFaceRecognition_flag:self.isFaceRecognition_flag = Trueself.recognition_button.setText(u'关闭人脸识别')self.recognize_continuous_face()# 点击人脸识别时,人脸识别已经开启elif self.isFaceRecognition_flag:self.isFaceRecognition_flag = Falseself.recognition_button.setText(u'人脸识别')self.clear_information()self.show_camera()

三、识别器

1.瞬时人脸识别

a.核心方法:

  • face_locations ——定位图中所有的人脸的像素位置
    • 返回值是一个列表形式。列表中每一个元素是一张人脸的位置信息,包括[top,right,bottom, left]。
  • face_encodings—— 获取图像文件中所有面部编码信息
    • 返回值是一个编码列表,参数仍然是要识别的图像对象,如果后续访问时需要加上索引或遍历进行访问,每张人脸的编码信息为一个128维向量。
  • compare_faces —— 由面部编码信息进行面部识别匹配

    • 主要用于匹配两个面部特征编码,利用这两个特征向量的内积来衡量相似度,根据阈值确认是否是同一个人。
    • 第一个参数是一个面部编码列表(很多张脸), 第二个参数是给出单个面部编码(一张脸), compare_faces 会将第二个参数中的编码信息与第一个参数中的所有编码信息依次匹配,返回值是一个布尔列表,匹配成功则返回 True,匹配失败则返回 False,顺序与第一个参数中脸部编码顺序一致。
    • 参数tolerance 为识别阈值,默认值是 0.39。tolerance 值越小,匹配越严格。

b.核心代码:

# 定位当前帧中人脸位置unknown_locations = face_recognition.face_locations(frame)# 对当前帧中所有的人脸进行编码unknown_encodings = face_recognition.face_encodings(frame, unknown_locations)for unknown_encoding in unknown_encodings:# 将未知的人脸 unknown_encoding 与已知的人脸编码 recognize_encodings 进行比对,判断是否为同一张脸,结果保存到match中match = face_recognition.compare_faces(recognize_encodings, unknown_encoding, tolerance=TOLERANCE)# 如果在结果中存在True,说明用户识别成功if True in match:return True

c.全部代码:

def recognize_instant_face(self, recognize_encoding):# 隔帧处理,减少计算量process_this_frame = True# 超过10帧判断识别失败,因为隔帧处理,一共判断5帧total_frame = 0# 已知的人脸编码集合recognize_encodings = [recognize_encoding]# 当摄像头打开时开始检测,当 检测成功 或 超过10帧 时推出循环while self.cap.isOpened():# 获取当前摄像头画面帧 frameret, frame = self.cap.read()# 帧数加一total_frame += 1# 减少界面卡顿QApplication.processEvents()# 对帧进行裁剪处理,减少计算量small_frame = cv2.resize(frame, (0, 0), fx=SET_SIZE, fy=SET_SIZE)# 判断当前帧是否需要计算(隔帧处理)if process_this_frame:QApplication.processEvents()# 核心代码unknown_locations = face_recognition.face_locations(small_frame)unknown_encodings = face_recognition.face_encodings(small_frame, unknown_locations)for unknown_encoding in unknown_encodings:match = face_recognition.compare_faces(recognize_encodings, unknown_encoding, tolerance=TOLERANCE)if True in match:return True# 隔帧处理process_this_frame = not process_this_frame# 超时退出if total_frame >= 10.0:return False# 对传回来的画面进行处理show_video = cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT))show_video = cv2.cvtColor(show_video, cv2.COLOR_BGR2RGB)self.show_image = QImage(show_video.data, show_video.shape[1], show_video.shape[0], QImage.Format_RGB888)self.camera_label.setPixmap(QPixmap.fromImage(self.show_image))

2.连续人脸识别

核心方法同上,只不过会对当前画面中的人脸实时使用红框进行标识,并在红框下方进行用户名的标识。如果人脸未知,则使用UnKnow进行标识。同上在下方提示框进行用户信息的标识。

a.全部代码:

# 持续人脸识别def recognize_continuous_face(self):# 通过数据库读取已知的用户姓名以及人脸编码self.read_person_msg()known_names = []# 存放已知用户的名字known_encodings = []# 存放已知用户的人脸特征信息for i in range(len(self.all_list)):known_names.append(self.all_list[i][0])known_encodings.append(FaceEncodingUtil.decoding_FaceStr(self.all_list[i][5]))# 从摄像头实时读取未知用户编码unknown_locations = []# 存放未知用户的人脸位置unknown_names = [] # 存放经过识别的用户名字process_this_frame = Truewhile self.cap.isOpened():ret, frame = self.cap.read()QApplication.processEvents()small_frame = cv2.resize(frame, (0, 0), fx=SET_SIZE, fy=SET_SIZE)if process_this_frame:QApplication.processEvents()unknown_locations = face_recognition.face_locations(small_frame)unknown_encodings = face_recognition.face_encodings(small_frame, unknown_locations)unknown_names = []for unknown_encoding in unknown_encodings:name = "Unknown" # 默认未知matches = face_recognition.compare_faces(known_encodings, unknown_encoding, tolerance=TOLERANCE)if True in matches:first_match_index = matches.index(True)name = known_names[first_match_index]unknown_names.append(name)process_this_frame = not process_this_frame# 保存捕捉到的人脸姓名信息self.set_name = set(unknown_names)self.set_names = tuple(self.set_name)# 绘制图像for (top, right, bottom, left), name in zip(unknown_locations, unknown_names):top *= int(1 / SET_SIZE)right *= int(1 / SET_SIZE)bottom *= int(1 / SET_SIZE)left *= int(1 / SET_SIZE)# 画矩形框cv2.rectangle(frame, (left, top), (right, bottom), (60, 20, 220), 2)# 由于opencv无法显示汉字,用PIL进行转换cv2_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)pil_img = Image.fromarray(cv2_img)draw = ImageDraw.Draw(pil_img)# 参数1:字体文件路径,参数2:字体大小font = ImageFont.truetype(DATA_FOLDER_PATH + "MicrosoftYaHeiFont.ttf", 24, encoding="utf-8")# 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体draw.text((left + 10, bottom), name, (220, 20, 60), font=font)frame = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)self.show_results()show_video = cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT))show_video = cv2.cvtColor(show_video, cv2.COLOR_BGR2RGB)self.show_image = QImage(show_video.data, show_video.shape[1], show_video.shape[0], QImage.Format_RGB888)self.camera_label.setPixmap(QPixmap.fromImage(self.show_image))# 展示人脸识别结果def show_results(self):if self.isFaceRecognition_flag:msg_label = {0: self.msg_label_a, 1: self.msg_label_b, 2: self.msg_label_c}# 最多放置3个人的信息if len(self.set_names) > 3:show_person = 3else:show_person = len(self.set_names)if show_person != 0:for show_index in range(show_person):name = self.set_names[show_index]try:per_label = msg_label[show_index]index = self.search_person_index(self.all_list, name)if index != -1:infor_str = ' 姓名: ' + name + '' + \' 年龄: ' + self.all_list[index][2] + ' ' + \' 性别: ' + self.all_list[index][3] + ' ' + \' 更多: ' + self.all_list[index][4]per_label.setText(infor_str)per_label.setStyleSheet("color:white;font-size:20px;font-family:Microsoft YaHei;")per_label.setWordWrap(True)except:QMessageBox.about(self, '警告', '请检查' + name + '的信息')if show_person != 3:for empty in range(3)[show_person:]:per_label = msg_label[empty]per_label.setText("")# 读取用户信息def read_person_msg(self):all_msg = UserSqlUtil.search_all_msg()for tup in all_msg:per_list = []for i in tup:per_list.append(i)self.all_list.append(per_list)# 查找指定用户下标@staticmethoddef search_person_index(persons_infor, name):for i in range(len(persons_infor)):if persons_infor[i][0] == name:return ireturn -1

四、全部代码

注:以下代码仅为项目部分代码,完整代码参考GitHub:Su-Face-Recognition

# 主界面class UserMainWindow(QMainWindow, UserMainUi):def __init__(self, parent=None):super(UserMainWindow, self).__init__(parent)self.setupUi(self)self.show_image = Noneself.set_name = Noneself.set_names = Noneself.need_record_name = ([])self.camera_start_time = Noneself.detect_start_time = Noneself.cap = cv2.VideoCapture()# 相机self.source = CAPTURE_SOURCE# 相机标号self.WIN_WIDTH = 800# 相机展示画面宽度self.WIN_HEIGHT = 500# 相机展示画面高度self.isFaceRecognition_flag = False# 是否打开人脸识别标志self.all_list = []# 窗口控制self.close_button.clicked.connect(self.close_window)# 关闭窗口self.minimize_button.clicked.connect(self.showMinimized)# 最小化窗口self.recognition_button.clicked.connect(self.recognize_face_judge)# 人脸识别# 人脸识别判断器def recognize_face_judge(self):if not self.cap.isOpened():QMessageBox.information(self, "提示", self.tr(u"请先打开摄像头"))else:# 点击人脸识别时,人脸识别是关闭的if not self.isFaceRecognition_flag:self.isFaceRecognition_flag = Trueself.recognition_button.setText(u'关闭人脸识别')self.recognize_continuous_face()# 点击人脸识别时,人脸识别已经开启elif self.isFaceRecognition_flag:self.isFaceRecognition_flag = Falseself.recognition_button.setText(u'人脸识别')self.clear_information()self.show_camera()# 瞬时人脸识别def recognize_instant_face(self, recognize_encoding):# 隔帧处理,减少计算量process_this_frame = True# 超过10帧判断识别失败,因为隔帧处理,一共判断5帧total_frame = 0# 已知的人脸编码集合recognize_encodings = [recognize_encoding]# 当摄像头打开时开始检测,当 检测成功 或 超过10帧 时推出循环while self.cap.isOpened():# 获取当前摄像头画面帧 frameret, frame = self.cap.read()# 帧数加一total_frame += 1# 减少界面卡顿QApplication.processEvents()# 对帧进行裁剪处理,减少计算量small_frame = cv2.resize(frame, (0, 0), fx=SET_SIZE, fy=SET_SIZE)# 判断当前帧是否需要计算(隔帧处理)if process_this_frame:QApplication.processEvents()# 核心代码unknown_locations = face_recognition.face_locations(small_frame)unknown_encodings = face_recognition.face_encodings(small_frame, unknown_locations)for unknown_encoding in unknown_encodings:match = face_recognition.compare_faces(recognize_encodings, unknown_encoding, tolerance=TOLERANCE)if True in match:return True# 隔帧处理process_this_frame = not process_this_frame# 超时退出if total_frame >= 10.0:return False# 对传回来的画面进行处理show_video = cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT))show_video = cv2.cvtColor(show_video, cv2.COLOR_BGR2RGB)self.show_image = QImage(show_video.data, show_video.shape[1], show_video.shape[0], QImage.Format_RGB888)self.camera_label.setPixmap(QPixmap.fromImage(self.show_image))# 持续人脸识别def recognize_continuous_face(self):# 通过数据库读取已知的用户姓名以及人脸编码self.read_person_msg()known_names = []# 存放已知用户的名字known_encodings = []# 存放已知用户的人脸特征信息for i in range(len(self.all_list)):known_names.append(self.all_list[i][0])known_encodings.append(FaceEncodingUtil.decoding_FaceStr(self.all_list[i][5]))# 从摄像头实时读取未知用户编码unknown_locations = []# 存放未知用户的人脸位置unknown_names = [] # 存放经过识别的用户名字process_this_frame = Truewhile self.cap.isOpened():ret, frame = self.cap.read()QApplication.processEvents()small_frame = cv2.resize(frame, (0, 0), fx=SET_SIZE, fy=SET_SIZE)if process_this_frame:QApplication.processEvents()unknown_locations = face_recognition.face_locations(small_frame)unknown_encodings = face_recognition.face_encodings(small_frame, unknown_locations)unknown_names = []for unknown_encoding in unknown_encodings:name = "Unknown" # 默认未知matches = face_recognition.compare_faces(known_encodings, unknown_encoding, tolerance=TOLERANCE)if True in matches:first_match_index = matches.index(True)name = known_names[first_match_index]unknown_names.append(name)process_this_frame = not process_this_frame# 保存捕捉到的人脸姓名信息self.set_name = set(unknown_names)self.set_names = tuple(self.set_name)# 绘制图像for (top, right, bottom, left), name in zip(unknown_locations, unknown_names):top *= int(1 / SET_SIZE)right *= int(1 / SET_SIZE)bottom *= int(1 / SET_SIZE)left *= int(1 / SET_SIZE)# 画矩形框cv2.rectangle(frame, (left, top), (right, bottom), (60, 20, 220), 2)# 由于opencv无法显示汉字,用PIL进行转换cv2_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)pil_img = Image.fromarray(cv2_img)draw = ImageDraw.Draw(pil_img)# 参数1:字体文件路径,参数2:字体大小font = ImageFont.truetype(DATA_FOLDER_PATH + "MicrosoftYaHeiFont.ttf", 24, encoding="utf-8")# 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体draw.text((left + 10, bottom), name, (220, 20, 60), font=font)frame = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)self.show_results()show_video = cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT))show_video = cv2.cvtColor(show_video, cv2.COLOR_BGR2RGB)self.show_image = QImage(show_video.data, show_video.shape[1], show_video.shape[0], QImage.Format_RGB888)self.camera_label.setPixmap(QPixmap.fromImage(self.show_image))# 展示人脸识别结果def show_results(self):if self.isFaceRecognition_flag:msg_label = {0: self.msg_label_a, 1: self.msg_label_b, 2: self.msg_label_c}# 最多放置3个人的信息if len(self.set_names) > 3:show_person = 3else:show_person = len(self.set_names)if show_person != 0:for show_index in range(show_person):name = self.set_names[show_index]try:per_label = msg_label[show_index]index = self.search_person_index(self.all_list, name)if index != -1:infor_str = ' 姓名: ' + name + '' + \' 年龄: ' + self.all_list[index][2] + ' ' + \' 性别: ' + self.all_list[index][3] + ' ' + \' 更多: ' + self.all_list[index][4]per_label.setText(infor_str)per_label.setStyleSheet("color:white;font-size:20px;font-family:Microsoft YaHei;")per_label.setWordWrap(True)except:QMessageBox.about(self, '警告', '请检查' + name + '的信息')if show_person != 3:for empty in range(3)[show_person:]:per_label = msg_label[empty]per_label.setText("")# 读取用户信息def read_person_msg(self):all_msg = UserSqlUtil.search_all_msg()for tup in all_msg:per_list = []for i in tup:per_list.append(i)self.all_list.append(per_list)# 查找指定用户下标@staticmethoddef search_person_index(persons_infor, name):for i in range(len(persons_infor)):if persons_infor[i][0] == name:return ireturn -1# 清除人脸识别结果信息def clear_information(self):self.msg_label_a.setText("")self.msg_label_b.setText("")self.msg_label_c.setText("")

继续阅读:

用户端逻辑:

  • 活体检测:Python | 人脸识别系统 — 活体检测
  • 背景模糊:Python | 人脸识别系统 — 背景模糊
  • 姿态检测:Python | 人脸识别系统 — 姿态检测
  • 人脸比对:Python | 人脸识别系统 — 人脸比对
  • 用户操作:Python | 人脸识别系统 — 用户操作

管理员端逻辑:

  • 管理员操作:Python | 人脸识别系统 —— 管理员操作

注:以上代码仅供参考,如需运行,参考GitHub源代码:Su-Face-Recognition