# import sys # import cv2 # import os # import time # from PyQt5.QtCore import QThread, pyqtSignal, QObject # import face_recognition # current_dir = os.path.dirname(__file__) # share_code_dir = os.path.abspath(os.path.join(current_dir, '..', '..')) # sys.path.append(share_code_dir) # from print_color import * # DEFAULT_VIDEO_SLEEP_MS = 20 # # 图像处理线程 # class ImageProcessingThread(QThread): # processed_image_signal = pyqtSignal(object) # def __init__(self): # super().__init__() # def run(self): # while True: # # 在这里添加图像处理代码 # # 这里暂时只是将图像传递给下一个线程 # time.sleep(0.01) # self.processed_image_signal.emit(None) # # 摄像头采集线程 # class CameraThread(QThread): # image_signal = pyqtSignal(object) # def __init__(self, camera_url, circuit_id: int = 0): # super().__init__() # self.camera_url = camera_url # 摄像头url地址, 整数 或者 字符串 # self.face_detection: bool = False # if isinstance(camera_url, int): # self.camera_url_str = str(camera_url) # else: # self.camera_url_str = camera_url # self.cap: cv2.VideoCapture = None # self.running: bool = True # self.fps = 0 # self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS # self.cycle_ms = 0 # self.circuit_id = circuit_id # 摄像头对应的回路, 组合开关每一个回路都有对应的摄像头 # self.is_signal_connect = False # self.is_emit = False # self.set_video_cycle_ms(2000) # def signal_connect(self, slot_func): # self.image_signal.connect(slot_func) # self.is_signal_connect = True # def signal_disconnect(self): # # 判断信号是否已连接 # if self.is_signal_connect: # self.image_signal.disconnect() # self.is_signal_connect = False # def set_video_cycle_ms(self, cycle_ms: int = 10): # if self.cycle_limit != cycle_ms: # if cycle_ms <= DEFAULT_VIDEO_SLEEP_MS: # self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS # else: # self.cycle_limit = cycle_ms # # 新建函数,改变函数延时方法 # def change_camera_url(self, camera_url: str): # if self.circuit_id == camera_url: # self.set_video_cycle_ms(DEFAULT_VIDEO_SLEEP_MS) # else: # self.set_video_cycle_ms(1000) # def close(self): # if self.cap == None: # 初始化一直未完成 # self.terminate() # self.running = False # def run(self): # process_count = 0 # fps_time = 0 # while self.running == True: # inform_msg = "cameral init start, url = " + self.camera_url_str # print_inform_msg(inform_msg) # try: # if isinstance(self.camera_url, int): # # 在 Windows 平台下,使用默认的 cv2.VideoCapture 接口 # self.cap = cv2.VideoCapture(self.camera_url, cv2.CAP_DSHOW) # else: # # 在 Linux 平台下,使用默认的 cv2.VideoCapture 接口 # self.cap = cv2.VideoCapture(self.camera_url) # if self.cap != None: # inform_msg = "cameral Init Success, url = " + self.camera_url_str # self.is_emit = True # print_inform_msg(inform_msg) # else: # inform_msg = "cameral connection timeout, url = " + self.camera_url_str # print_inform_msg(inform_msg) # except Exception as e: # self.cap = None # inform_msg = "cameral camera Init Fail, url = " + self.camera_url_str # print_error_msg(inform_msg) # base_time = time.time() # while self.running and self.cap != None: # # 延时20ms # time.sleep(DEFAULT_VIDEO_SLEEP_MS / 1000) # if self.cycle_ms + DEFAULT_VIDEO_SLEEP_MS < self.cycle_limit: # self.cycle_ms += DEFAULT_VIDEO_SLEEP_MS # self.cap.grab() # 抛弃多余的帧,保持最新帧 # continue # else: # self.cycle_ms = 0 # try: # ret, frame = self.cap.read() # cur_time = time.time() # execution_time = cur_time - base_time # base_time = cur_time # process_count += 1 # fps_time += execution_time # if fps_time >= 1: # self.fps = process_count # process_count = 0 # fps_time = 0 # if execution_time < 5: # image_object: QObject = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # self.image_signal.emit(image_object) # else: # 时间差大于5秒, 表示网络可能中断过, 退出并重新连接 # err_message = "cameral read timeout, url = " + self.camera_url_str # print_error_msg(err_message) # break # except Exception as e: # err_message = "cameral read timeout, url = " + self.camera_url_str # self.is_emit = False # print_error_msg(err_message) # if self.running == True: # time.sleep(2) # break # print_inform_msg("cameral connection End") # time.sleep(0.01) # if self.cap != None: # self.cap.release() # self.cap = None # - * - coding:utf - 8 - * - import sys import cv2 import os import time import subprocess import face_recognition import numpy as np from PIL import Image, ImageDraw, ImageFont from PyQt5.QtCore import QThread, pyqtSignal, QObject from print_color import * current_dir = os.path.dirname(__file__) share_code_dir = os.path.abspath(os.path.join(current_dir, '..', '..')) sys.path.append(share_code_dir) DEFAULT_VIDEO_SLEEP_MS = 20 # 定义路径常量 npy_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data/face_data.npy')) image_folder_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data')) font_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'font/hanzi.ttc')) def load_face_data(): face_data = {} try: face_data = np.load(npy_file_path, allow_pickle=True).item() print_debug_msg(f"Loaded {len(face_data)} face encodings from {npy_file_path}.") except Exception as e: print_error_msg(f"Error loading face encodings: {e}") current_images = [f for f in os.listdir(image_folder_path) if f.endswith('.jpg')] current_image_set = set(os.path.splitext(f)[0] for f in current_images) known_image_set = set(face_data.keys()) added_images = current_image_set - known_image_set removed_images = known_image_set - current_image_set for image_name in added_images: image_path = os.path.join(image_folder_path, f"{image_name}.jpg") try: image = face_recognition.load_image_file(image_path) face_encodings = face_recognition.face_encodings(image) if face_encodings: face_data[image_name] = face_encodings[0].tolist() print_debug_msg(f"Added encoding for {image_name}.") else: print_warning_msg(f"[WARNING] No face detected in {image_name}.jpg.") except Exception as e: print_error_msg(f"[ERROR] Error processing {image_name}.jpg: {e}") for removed_image in removed_images: del face_data[removed_image] print_debug_msg(f"Removed encoding for {removed_image}.") np.save(npy_file_path, face_data) print_debug_msg(f"Updated face data saved with {len(face_data)} entries.") return face_data class ImageProcessingThread(QThread): processed_image_signal = pyqtSignal(object) def __init__(self, frame=None): super().__init__() self.frame = frame # 添加 frame 参数 self.face_encodings = [] self.face_names = [] self.face_locations = [] self.face_data = load_face_data() self.total_image_name = list(self.face_data.keys()) self.total_face_encoding = [np.array(self.face_data[name]) for name in self.total_image_name] def process_frame(self, frame): """实时处理帧,匹配已知人脸""" self.face_locations = face_recognition.face_locations(frame) face_encodings = face_recognition.face_encodings(frame, self.face_locations) names = [] for face_encoding in face_encodings: name = "Unknown" face_distances = face_recognition.face_distance(self.total_face_encoding, face_encoding) if face_distances.size > 0: min_distance = np.min(face_distances) best_match_index = np.argmin(face_distances) if min_distance < self.dynamic_tolerance(): # 使用动态容忍度 name = self.total_image_name[best_match_index] names.append(name) self.face_names = names return frame def dynamic_tolerance(self): # 动态调整容忍度,可以基于一些规则来调整,如帧内错误率、识别准确率等 error_rate = self.calculate_error_rate() if error_rate > 0.1: return 0.5 # 高容忍度以减少错误 else: return 0.4 # 适中容忍度 def calculate_error_rate(self): # 计算识别错误率,作为动态调整容忍度的依据 total_faces = len(self.total_face_encoding) unmatched_faces = sum(1 for name in self.face_names if name == "Unknown") return unmatched_faces / total_faces if total_faces > 0 else 0 def set_frame(self, frame): self.frame = frame def run(self): while True: if self.frame is not None: self.process_frame(self.frame) self.processed_image_signal.emit(self.frame) time.sleep(0.01) # 控制帧率 # 摄像头采集线程 class CameraThread(QThread): image_signal = pyqtSignal(object) def __init__(self, camera_url, circuit_id: int = 0): super().__init__() self.camera_url = camera_url self.face_detection: bool = True if isinstance(camera_url, int): self.camera_url_str = str(camera_url) else: self.camera_url_str = camera_url self.cap: cv2.VideoCapture = None self.running: bool = True self.fps = 0 self.frame_count = 0 self.fps_time = 0 self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS self.cycle_ms = 0 self.circuit_id = circuit_id self.is_signal_connect = False self.is_emit = False self.set_video_cycle_ms(2000) def signal_connect(self, slot_func): self.image_signal.connect(slot_func) self.is_signal_connect = True def signal_disconnect(self): if self.is_signal_connect: self.image_signal.disconnect() self.is_signal_connect = False def set_video_cycle_ms(self, cycle_ms: int = 10): if self.cycle_limit != cycle_ms: if cycle_ms <= DEFAULT_VIDEO_SLEEP_MS: self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS else: self.cycle_limit = cycle_ms def change_camera_url(self, camera_url: str): if self.circuit_id == camera_url: self.set_video_cycle_ms(DEFAULT_VIDEO_SLEEP_MS) else: self.set_video_cycle_ms(1000) def close(self): if self.cap is None: self.terminate() self.running = False def run(self): process_count = 0 fps_time = 0 face_thread = ImageProcessingThread() face_thread.start() self.match_count = 0 # 初始化匹配帧数计数器 while self.running: inform_msg = "Camera init start, URL = " + self.camera_url_str print_inform_msg(inform_msg) try: self.cap = cv2.VideoCapture(self.camera_url) # 强制使用 BGR 格式 if self.cap.isOpened(): inform_msg = "Camera Init Success, URL = " + self.camera_url_str self.is_emit = True print_inform_msg(inform_msg) else: inform_msg = "Camera connection timeout, URL = " + self.camera_url_str print_error_msg(inform_msg) except Exception as e: self.cap = None inform_msg = "Camera Init Fail, URL = " + self.camera_url_str print_error_msg(inform_msg) base_time = time.time() while self.running and self.cap: time.sleep(DEFAULT_VIDEO_SLEEP_MS / 1000) if self.cycle_ms + DEFAULT_VIDEO_SLEEP_MS < self.cycle_limit: self.cycle_ms += DEFAULT_VIDEO_SLEEP_MS self.cap.grab() continue else: self.cycle_ms = 0 try: ret, frame = self.cap.read() if not ret: break face_thread.set_frame(frame) cur_time = time.time() execution_time = cur_time - base_time base_time = cur_time process_count += 1 fps_time += execution_time if fps_time >= 1: # 每隔1秒重新计算 FPS self.fps = process_count process_count = 0 fps_time = 0 if execution_time < 5: frame: QObject = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for (top, right, bottom, left), name in zip(face_thread.face_locations, face_thread.face_names): if name != "Unknown": # 只绘制已匹配的结果 self.match_count += 1 # 计数成功匹配的帧 # cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) # cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1) # 将OpenCV图像转换为PIL图像格式(注意要转换色彩空间,OpenCV是BGR,PIL是RGB) pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(pil_image) # 这里选择合适的中文字体文件路径,比如系统自带的宋体(不同系统路径可能有差异),并设置字体大小 font = ImageFont.truetype(font_path, 15) # 在指定位置绘制中文 draw.text((left + 12, bottom + 12), name, font=font, fill=(255, 255, 255)) # 再将PIL图像转换回OpenCV图像格式 frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) else: name = "未在人脸库中,无操作权限" pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(pil_image) #设置字体大小 font = ImageFont.truetype(font_path, 15) # 在指定位置绘制中文 draw.text((left + 12, bottom + 12), name, font=font, fill=(255, 255, 255)) # 再将PIL图像转换回OpenCV图像格式 frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) cv2.rectangle(frame, (left, top), (right, bottom), (255, 0, 0), 2) self.image_signal.emit(frame) else: err_message = "Camera read timeout, URL = " + self.camera_url_str print_error_msg(err_message) break except Exception as e: err_message = "Camera read exception, URL = " + self.camera_url_str print_error_msg(err_message) if self.running: time.sleep(2) break if self.cap: self.cap.release() self.cap = None print_inform_msg("Camera connection ended") time.sleep(0.01)