PyQt人脸识别考勤应用UI卡顿:如何正确实现EncodeFaceThread?
解决PyQt5人脸识别线程导致UI卡顿的问题
问题核心原因
摄像头采集帧的速度远快于人脸识别逻辑的处理速度,导致Queue中积压了大量未处理的旧帧,EncodeFaceThread一直忙于处理过期帧,抢占了过多系统资源,间接拖慢了UI线程的响应。而替换为sleep逻辑时,线程没有密集计算任务,不会抢占资源,因此UI恢复流畅。
解决方案
1. 限制队列长度,只保留最新帧
给队列设置最大长度,当队列满时自动丢弃最早的帧,确保EncodeFaceThread始终处理最新的摄像头帧,避免旧帧积压。
2. 控制摄像头采集帧率
避免摄像头无限制快速采集帧,添加帧率控制,减少不必要的帧生成和队列推送,平衡流畅度与处理压力。
3. 优化人脸识别计算
缩小帧尺寸可显著加快face_locations和face_encodings的处理速度,因为人脸识别算法的计算量与图像像素数正相关,缩小帧对编码结果的影响极小。
修改后的完整代码
from queue import Queue from sys import argv from cv2 import COLOR_BGR2RGB, VideoCapture, cvtColor, resize from face_recognition import compare_faces, face_encodings, face_locations from numpy import ndarray from time import time, sleep from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow class WebcamThread(QThread): ndarray_frame_signal = pyqtSignal(ndarray) qimage_frame_signal = pyqtSignal(QImage) def __init__(self, fps=15): super().__init__() self.cap = VideoCapture(0) self.fps = fps self.frame_interval = 1.0 / fps # 每帧间隔时间 def run(self): while self.isRunning(): start_time = time() # 读取摄像头帧,判断读取是否成功 ret, frame = self.cap.read() if not ret: break frame = cvtColor(frame, COLOR_BGR2RGB) self.ndarray_frame_signal.emit(frame) # 转换为QImage用于UI显示 h, w, _ = frame.shape qimage_frame = QImage(frame.data, w, h, QImage.Format.Format_RGB888) self.qimage_frame_signal.emit(qimage_frame) # 控制帧率,确保每帧间隔符合设置 elapsed = time() - start_time if elapsed < self.frame_interval: sleep(self.frame_interval - elapsed) class EncodeFaceThread(QThread): def __init__(self): super().__init__() self.known_encodings = [] # 设置队列最大长度为2,满时自动丢弃旧帧 self.queue = Queue(maxsize=2) def run(self): while self.isRunning(): try: # 添加超时,避免线程无帧时一直阻塞 frame = self.queue.get(block=True, timeout=0.1) # 缩小帧尺寸到原大小的1/2,加快人脸识别速度 small_frame = resize(frame, (0, 0), fx=0.5, fy=0.5) f_locations = face_locations(small_frame) if len(f_locations) != 1: continue # 生成人脸编码 f_encoding = face_encodings(small_frame, f_locations)[0] # 匹配已知编码,未匹配则添加 if not any(compare_faces(self.known_encodings, f_encoding)): self.known_encodings.append(f_encoding) self.queue.task_done() except: continue class MainWindow(QMainWindow): def __init__(self): super().__init__() # UI设置 self.setMinimumSize(660, 500) self.image_display = QLabel(self) self.image_display.setGeometry(10, 10, 640, 480) # 初始化线程 self.webcam_thread = WebcamThread(fps=15) self.webcam_thread.ndarray_frame_signal.connect(self.push_in_queue) self.webcam_thread.qimage_frame_signal.connect(self.display) self.webcam_thread.start() self.encode_face_thread = EncodeFaceThread() self.encode_face_thread.start() @pyqtSlot(ndarray) def push_in_queue(self, frame): try: # 非阻塞放入队列,满时丢弃当前帧 self.encode_face_thread.queue.put_nowait(frame) except: pass @pyqtSlot(QImage) def display(self, frame): qpixmap_frame = QPixmap.fromImage(frame) self.image_display.setPixmap(qpixmap_frame) app = QApplication(argv) window = MainWindow() window.show() app.exec()
关键修改说明
- 队列限制:
Queue(maxsize=2)避免无限积压,put_nowait确保摄像头线程不会因队列满而阻塞。 - 帧率控制:通过计算帧处理耗时,动态补充睡眠时长,严格控制采集频率。
- 帧缩小优化:将帧尺寸减半,大幅降低人脸识别的计算量,同时不影响编码准确性。
- 线程超时处理:
queue.get添加超时,避免人脸识别线程在无帧时持续阻塞,提升响应性。
内容的提问来源于stack exchange,提问作者brownfox2k6
相关产品推荐
相关产品推荐

