| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | import os | 
					
						
							|  |  |  |  | import numpy as np | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | import gc | 
					
						
							|  |  |  |  | import time | 
					
						
							|  |  |  |  | import threading | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | from insightface.app import FaceAnalysis | 
					
						
							|  |  |  |  | from service.face_service import get_all_face_name_with_eigenvalue | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | # GPU状态检查支持 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | try: | 
					
						
							|  |  |  |  |     import pynvml | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     pynvml.nvmlInit() | 
					
						
							|  |  |  |  |     _nvml_available = True | 
					
						
							|  |  |  |  | except ImportError: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     print("警告: pynvml库未安装,无法检测GPU状态,默认尝试使用GPU") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     _nvml_available = False | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | # 全局人脸引擎与特征库 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | _face_app = None | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | _known_faces_embeddings = {}  # 姓名 -> 归一化特征值的映射 | 
					
						
							|  |  |  |  | _known_faces_names = []  # 已知人脸姓名列表 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | # GPU使用状态标记 | 
					
						
							|  |  |  |  | _using_gpu = False  # 是否使用GPU | 
					
						
							|  |  |  |  | _used_gpu_id = -1  # 使用的GPU ID(-1表示CPU) | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | # 资源管理变量 | 
					
						
							|  |  |  |  | _ref_count = 0  # 引擎引用计数(记录当前使用次数) | 
					
						
							| 
									
										
										
										
											2025-09-12 20:30:54 +08:00
										 |  |  |  | # 修复点1:初始值设为当前时间,避免未加载引擎时用0计算超时 | 
					
						
							|  |  |  |  | _last_used_time = time.time() | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | _lock = threading.Lock()  # 线程安全锁 | 
					
						
							|  |  |  |  | _release_timeout = 8  # 闲置超时时间(秒) | 
					
						
							|  |  |  |  | _is_releasing = False  # 资源释放中标记 | 
					
						
							|  |  |  |  | _monitor_thread_running = False  # 监控线程运行标记 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | # 调试计数器 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | _debug_counter = { | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     "engine_created": 0,  # 引擎创建次数 | 
					
						
							|  |  |  |  |     "engine_released": 0,  # 引擎释放次数 | 
					
						
							|  |  |  |  |     "detection_calls": 0  # 检测函数调用次数 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | def check_gpu_availability(gpu_id, memory_threshold=0.7): | 
					
						
							|  |  |  |  |     """检查指定GPU的内存使用率是否低于阈值(判定为“可用”)""" | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     if not _nvml_available: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         return True | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     try: | 
					
						
							|  |  |  |  |         handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id) | 
					
						
							|  |  |  |  |         mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         memory_usage = mem_info.used / mem_info.total | 
					
						
							|  |  |  |  |         return memory_usage < memory_threshold | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print(f"检查GPU {gpu_id} 状态失败: {e}") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         return False | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | def select_best_gpu(preferred_gpus=[0, 1]): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     """按优先级选择可用GPU,优先0号;均不可用则返回-1(CPU)""" | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     for gpu_id in preferred_gpus: | 
					
						
							|  |  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             # 验证GPU是否存在 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |             if _nvml_available: | 
					
						
							|  |  |  |  |                 pynvml.nvmlDeviceGetHandleByIndex(gpu_id) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             # 验证GPU内存是否充足 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |             if check_gpu_availability(gpu_id): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 print(f"GPU {gpu_id} 可用,将使用该GPU") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |                 return gpu_id | 
					
						
							|  |  |  |  |             else: | 
					
						
							|  |  |  |  |                 if gpu_id == 0: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                     print("GPU 0 内存使用率过高,尝试其他GPU") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             print(f"GPU {gpu_id} 不可用或访问失败: {e}") | 
					
						
							|  |  |  |  |     print("所有指定GPU均不可用,将使用CPU计算") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     return -1 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | def _release_engine_resources(): | 
					
						
							|  |  |  |  |     """释放人脸引擎的所有资源(模型、特征库、GPU缓存等)""" | 
					
						
							| 
									
										
										
										
											2025-09-12 20:30:54 +08:00
										 |  |  |  |     global _face_app, _is_releasing, _known_faces_embeddings, _known_faces_names, _last_used_time | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     if not _face_app or _is_releasing: | 
					
						
							|  |  |  |  |         return | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     try: | 
					
						
							|  |  |  |  |         _is_releasing = True | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print("开始释放人脸引擎资源...") | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # 释放InsightFace模型资源 | 
					
						
							|  |  |  |  |         if hasattr(_face_app, "model"): | 
					
						
							|  |  |  |  |             _face_app.model = None  # 显式置空模型引用 | 
					
						
							|  |  |  |  |         _face_app = None  # 释放引擎实例 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 清空人脸特征库 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         _known_faces_embeddings.clear() | 
					
						
							|  |  |  |  |         _known_faces_names.clear() | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         _debug_counter["engine_released"] += 1 | 
					
						
							|  |  |  |  |         print(f"人脸引擎已释放,调试统计: {_debug_counter}") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 强制垃圾回收 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         gc.collect() | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         # 清理各深度学习框架的GPU缓存 | 
					
						
							|  |  |  |  |         # Torch 缓存清理 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         try: | 
					
						
							|  |  |  |  |             import torch | 
					
						
							|  |  |  |  |             if torch.cuda.is_available(): | 
					
						
							|  |  |  |  |                 torch.cuda.empty_cache() | 
					
						
							|  |  |  |  |                 torch.cuda.ipc_collect() | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 print("Torch GPU缓存已清理") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         except ImportError: | 
					
						
							|  |  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         # TensorFlow 缓存清理 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         try: | 
					
						
							|  |  |  |  |             import tensorflow as tf | 
					
						
							|  |  |  |  |             tf.keras.backend.clear_session() | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             print("TensorFlow会话已清理") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         except ImportError: | 
					
						
							|  |  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         # MXNet 缓存清理(InsightFace底层常用MXNet) | 
					
						
							|  |  |  |  |         try: | 
					
						
							|  |  |  |  |             import mxnet as mx | 
					
						
							|  |  |  |  |             mx.nd.waitall()  # 等待所有计算完成并释放资源 | 
					
						
							|  |  |  |  |             print("MXNet资源已等待释放") | 
					
						
							|  |  |  |  |         except ImportError: | 
					
						
							|  |  |  |  |             pass | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     except Exception as e: | 
					
						
							|  |  |  |  |         print(f"释放资源过程中出错: {e}") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     finally: | 
					
						
							|  |  |  |  |         _is_releasing = False | 
					
						
							| 
									
										
										
										
											2025-09-12 20:30:54 +08:00
										 |  |  |  |         # 修复点2:释放完成后重置最后使用时间,避免下次加载时复用旧值 | 
					
						
							|  |  |  |  |         _last_used_time = time.time() | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | def _resource_monitor_thread(): | 
					
						
							|  |  |  |  |     """后台监控线程:检测引擎闲置超时,触发资源释放""" | 
					
						
							|  |  |  |  |     global _ref_count, _last_used_time, _face_app, _monitor_thread_running | 
					
						
							|  |  |  |  |     _monitor_thread_running = True | 
					
						
							|  |  |  |  |     while _monitor_thread_running: | 
					
						
							|  |  |  |  |         time.sleep(2)  # 缩短检查间隔,加快闲置检测响应 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         with _lock: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             # 当“引擎存在 + 无引用 + 未在释放中”时,检查闲置时间 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |             if _face_app and _ref_count == 0 and not _is_releasing: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 idle_time = time.time() - _last_used_time | 
					
						
							|  |  |  |  |                 if idle_time > _release_timeout: | 
					
						
							|  |  |  |  |                     print(f"引擎闲置超时({idle_time:.1f}s > {_release_timeout}s),释放资源") | 
					
						
							|  |  |  |  |                     _release_engine_resources() | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | def load_model(prefer_gpu=True, preferred_gpus=[0, 1]): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     """加载人脸识别引擎及已知人脸特征库(默认优先用0号GPU)""" | 
					
						
							| 
									
										
										
										
											2025-09-12 20:30:54 +08:00
										 |  |  |  |     global _face_app, _known_faces_embeddings, _known_faces_names, _using_gpu, _used_gpu_id, _last_used_time | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 启动后台监控线程(确保仅启动一次) | 
					
						
							|  |  |  |  |     if not _monitor_thread_running: | 
					
						
							|  |  |  |  |         threading.Thread( | 
					
						
							|  |  |  |  |             target=_resource_monitor_thread, | 
					
						
							|  |  |  |  |             daemon=True, | 
					
						
							|  |  |  |  |             name="FaceEngineMonitor" | 
					
						
							|  |  |  |  |         ).start() | 
					
						
							|  |  |  |  |         print("人脸引擎监控线程已启动") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 若正在释放资源,等待释放完成 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     while _is_releasing: | 
					
						
							|  |  |  |  |         time.sleep(0.1) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 若引擎已初始化,直接返回 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     if _face_app: | 
					
						
							|  |  |  |  |         return True | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 初始化InsightFace引擎 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         print("正在初始化InsightFace人脸识别引擎...") | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         _face_app = FaceAnalysis(name="buffalo_l", root=os.path.expanduser("~/.insightface")) | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 选择GPU(优先用0号) | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         ctx_id = 0 | 
					
						
							|  |  |  |  |         if prefer_gpu: | 
					
						
							|  |  |  |  |             ctx_id = select_best_gpu(preferred_gpus) | 
					
						
							|  |  |  |  |             _using_gpu = ctx_id != -1 | 
					
						
							|  |  |  |  |             _used_gpu_id = ctx_id if _using_gpu else -1 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         if _using_gpu: | 
					
						
							|  |  |  |  |             print(f"引擎初始化成功,将使用GPU {ctx_id} 计算") | 
					
						
							|  |  |  |  |         else: | 
					
						
							|  |  |  |  |             print("引擎初始化成功,将使用CPU计算") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 准备模型(加载到指定设备) | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         _face_app.prepare(ctx_id=ctx_id, det_size=(640, 640)) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print("InsightFace引擎初始化完成") | 
					
						
							| 
									
										
										
										
											2025-09-12 20:30:54 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         # 修复点3:引擎初始化成功后,立即更新“最后使用时间”(核心修复) | 
					
						
							|  |  |  |  |         _last_used_time = time.time() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         _debug_counter["engine_created"] += 1 | 
					
						
							|  |  |  |  |         print(f"引擎调试统计: {_debug_counter}") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print(f"引擎初始化失败: {e}") | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         return False | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 从服务加载已知人脸的姓名和特征值 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |     try: | 
					
						
							|  |  |  |  |         face_data = get_all_face_name_with_eigenvalue() | 
					
						
							|  |  |  |  |         for person_name, eigenvalue_data in face_data.items(): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             # 兼容“numpy数组”和“字符串”格式的特征值 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |             if isinstance(eigenvalue_data, np.ndarray): | 
					
						
							|  |  |  |  |                 eigenvalue = eigenvalue_data.astype(np.float32) | 
					
						
							|  |  |  |  |             elif isinstance(eigenvalue_data, str): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 # 清理字符串中的括号、换行等干扰符 | 
					
						
							|  |  |  |  |                 cleaned = eigenvalue_data.replace("[", "").replace("]", "").replace("\n", "").strip() | 
					
						
							|  |  |  |  |                 # 分割并转换为浮点数数组 | 
					
						
							|  |  |  |  |                 values = [v for v in cleaned.split() if v]  # 兼容空格/逗号分隔 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |                 eigenvalue = np.array(list(map(float, values)), dtype=np.float32) | 
					
						
							|  |  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 print(f"不支持的特征值类型({type(eigenvalue_data)}),跳过 {person_name}") | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |                 continue | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             # 特征值归一化(保证后续相似度计算的一致性) | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |             norm = np.linalg.norm(eigenvalue) | 
					
						
							|  |  |  |  |             if norm != 0: | 
					
						
							|  |  |  |  |                 eigenvalue = eigenvalue / norm | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             _known_faces_embeddings[person_name] = eigenvalue | 
					
						
							|  |  |  |  |             _known_faces_names.append(person_name) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print(f"成功加载 {len(_known_faces_names)} 个人脸的特征库") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print(f"加载人脸特征库失败: {e}") | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     return _face_app is not None | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  | def detect(frame, similarity_threshold=0.4): | 
					
						
							|  |  |  |  |     """
 | 
					
						
							|  |  |  |  |     检测并识别人脸 | 
					
						
							|  |  |  |  |     返回:(是否匹配到已知人脸, 结果描述字符串) | 
					
						
							|  |  |  |  |     """
 | 
					
						
							|  |  |  |  |     global _face_app, _known_faces_embeddings, _known_faces_names, _ref_count, _last_used_time | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 校验输入帧有效性 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     if frame is None or frame.size == 0: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         return (False, "无效的输入帧数据") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 加锁并更新引用计数、最后使用时间 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     engine = None | 
					
						
							|  |  |  |  |     with _lock: | 
					
						
							|  |  |  |  |         _ref_count += 1 | 
					
						
							|  |  |  |  |         _last_used_time = time.time() | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         _debug_counter["detection_calls"] += 1 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 若引擎未初始化且未在释放中,尝试初始化 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         if not _face_app and not _is_releasing: | 
					
						
							|  |  |  |  |             if not load_model(prefer_gpu=True): | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |                 # 初始化失败,恢复引用计数 | 
					
						
							|  |  |  |  |                 with _lock: | 
					
						
							|  |  |  |  |                     _ref_count = max(0, _ref_count - 1) | 
					
						
							|  |  |  |  |                 return (False, "人脸引擎初始化失败") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         engine = _face_app  # 获取引擎引用 | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 校验引擎可用性 | 
					
						
							|  |  |  |  |     if not engine or len(_known_faces_names) == 0: | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         with _lock: | 
					
						
							|  |  |  |  |             _ref_count = max(0, _ref_count - 1) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         return (False, "人脸引擎不可用或特征库为空") | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # GPU计算时,确保帧数据是连续内存(避免CUDA错误) | 
					
						
							|  |  |  |  |         if _using_gpu and engine is not None and not frame.flags.contiguous: | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |             frame = np.ascontiguousarray(frame) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 执行人脸检测与特征提取 | 
					
						
							|  |  |  |  |         faces = engine.get(frame) | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         print(f"人脸检测过程出错: {e}") | 
					
						
							|  |  |  |  |         # 出错时尝试重新初始化引擎(可能是GPU状态变化导致) | 
					
						
							|  |  |  |  |         print("尝试重新初始化人脸引擎...") | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |         with _lock: | 
					
						
							|  |  |  |  |             _ref_count = max(0, _ref_count - 1) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         load_model(prefer_gpu=True) | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         return (False, f"检测错误: {str(e)}") | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     result_parts = [] | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     has_matched_known_face = False  # 是否有任意人脸匹配到已知库 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     for face in faces: | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 归一化当前检测到的人脸特征 | 
					
						
							|  |  |  |  |         face_embedding = face.embedding.astype(np.float32) | 
					
						
							|  |  |  |  |         norm = np.linalg.norm(face_embedding) | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         if norm == 0: | 
					
						
							|  |  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         face_embedding = face_embedding / norm | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 与已知人脸特征逐一比对 | 
					
						
							|  |  |  |  |         max_similarity, best_match_name = -1.0, "Unknown" | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         for name in _known_faces_names: | 
					
						
							|  |  |  |  |             known_emb = _known_faces_embeddings[name] | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             similarity = np.dot(face_embedding, known_emb)  # 余弦相似度 | 
					
						
							|  |  |  |  |             if similarity > max_similarity: | 
					
						
							|  |  |  |  |                 max_similarity = similarity | 
					
						
							|  |  |  |  |                 best_match_name = name | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # 判断是否匹配成功 | 
					
						
							|  |  |  |  |         is_matched = max_similarity >= similarity_threshold | 
					
						
							|  |  |  |  |         if is_matched: | 
					
						
							|  |  |  |  |             has_matched_known_face = True | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # 记录该人脸的检测结果 | 
					
						
							|  |  |  |  |         bbox = face.bbox  # 人脸边界框 | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         result_parts.append( | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |             f"{'匹配' if is_matched else '未匹配'}: {best_match_name} " | 
					
						
							|  |  |  |  |             f"(相似度: {max_similarity:.2f}, 边界框: {bbox.astype(int).tolist()})" | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  |         ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 构建最终结果字符串 | 
					
						
							|  |  |  |  |     result_str = "未检测到人脸" if not result_parts else "; ".join(result_parts) | 
					
						
							| 
									
										
										
										
											2025-09-04 22:59:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     # 释放引用计数(线程安全) | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  |     with _lock: | 
					
						
							|  |  |  |  |         _ref_count = max(0, _ref_count - 1) | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |         # 若仍有引用,更新最后使用时间;若引用为0,也立即标记(加快闲置检测) | 
					
						
							|  |  |  |  |         _last_used_time = time.time() | 
					
						
							| 
									
										
										
										
											2025-09-05 17:23:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 16:30:12 +08:00
										 |  |  |  |     return (has_matched_known_face, result_str) |