114 lines
4.0 KiB
Python
114 lines
4.0 KiB
Python
|
import os
|
|||
|
import numpy as np
|
|||
|
import cv2
|
|||
|
from PIL import Image # 确保正确导入Image类
|
|||
|
from insightface.app import FaceAnalysis
|
|||
|
# 导入获取人脸信息的服务
|
|||
|
from service.face_service import get_all_face_name_with_eigenvalue
|
|||
|
|
|||
|
# 全局变量
|
|||
|
_face_app = None
|
|||
|
_known_faces_embeddings = {} # 存储姓名到特征值的映射
|
|||
|
_known_faces_names = [] # 存储所有已知姓名
|
|||
|
|
|||
|
|
|||
|
def load_model():
|
|||
|
"""加载人脸识别模型及已知人脸特征库"""
|
|||
|
global _face_app, _known_faces_embeddings, _known_faces_names
|
|||
|
|
|||
|
# 初始化InsightFace模型
|
|||
|
try:
|
|||
|
_face_app = FaceAnalysis(name='buffalo_l', root=os.path.expanduser('~/.insightface'))
|
|||
|
_face_app.prepare(ctx_id=0, det_size=(640, 640))
|
|||
|
except Exception as e:
|
|||
|
print(f"Face model load failed: {e}")
|
|||
|
return False
|
|||
|
|
|||
|
# 从服务获取所有人脸姓名和特征值
|
|||
|
try:
|
|||
|
face_data = get_all_face_name_with_eigenvalue()
|
|||
|
|
|||
|
# 处理获取到的人脸数据
|
|||
|
for person_name, eigenvalue_data in face_data.items():
|
|||
|
# 处理特征值数据 - 兼容数组和字符串两种格式
|
|||
|
if isinstance(eigenvalue_data, np.ndarray):
|
|||
|
# 如果已经是numpy数组,直接使用
|
|||
|
eigenvalue = eigenvalue_data.astype(np.float32)
|
|||
|
elif isinstance(eigenvalue_data, str):
|
|||
|
# 清理字符串:移除方括号、换行符和多余空格
|
|||
|
cleaned = eigenvalue_data.replace('[', '').replace(']', '').replace('\n', '').strip()
|
|||
|
# 按空格或逗号分割(处理可能的不同分隔符)
|
|||
|
values = [v for v in cleaned.split() if v]
|
|||
|
# 转换为数组
|
|||
|
eigenvalue = np.array(list(map(float, values)), dtype=np.float32)
|
|||
|
else:
|
|||
|
# 不支持的类型
|
|||
|
print(f"Unsupported eigenvalue type for {person_name}")
|
|||
|
continue
|
|||
|
|
|||
|
# 归一化处理
|
|||
|
norm = np.linalg.norm(eigenvalue)
|
|||
|
if norm != 0:
|
|||
|
eigenvalue = eigenvalue / norm
|
|||
|
|
|||
|
_known_faces_embeddings[person_name] = eigenvalue
|
|||
|
_known_faces_names.append(person_name)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"Error loading face data from service: {e}")
|
|||
|
|
|||
|
return True if _face_app else False
|
|||
|
|
|||
|
|
|||
|
def detect(frame, threshold=0.4):
|
|||
|
"""检测并识别人脸,返回结果元组(是否匹配到已知人脸, 结果字符串)"""
|
|||
|
global _face_app, _known_faces_embeddings, _known_faces_names
|
|||
|
|
|||
|
if not _face_app or not _known_faces_names or frame is None:
|
|||
|
return (False, "未初始化或无效帧")
|
|||
|
|
|||
|
try:
|
|||
|
faces = _face_app.get(frame)
|
|||
|
except Exception as e:
|
|||
|
print(f"Face detect error: {e}")
|
|||
|
return (False, f"检测错误: {str(e)}")
|
|||
|
|
|||
|
result_parts = []
|
|||
|
has_matched = False # 新增标记:是否有匹配到的已知人脸
|
|||
|
|
|||
|
for face in faces:
|
|||
|
# 特征归一化
|
|||
|
embedding = face.embedding.astype(np.float32)
|
|||
|
norm = np.linalg.norm(embedding)
|
|||
|
if norm == 0:
|
|||
|
continue
|
|||
|
embedding = embedding / norm
|
|||
|
|
|||
|
# 对比已知人脸
|
|||
|
max_sim, best_name = -1.0, "Unknown"
|
|||
|
for name in _known_faces_names:
|
|||
|
known_emb = _known_faces_embeddings[name]
|
|||
|
sim = np.dot(embedding, known_emb)
|
|||
|
if sim > max_sim:
|
|||
|
max_sim = sim
|
|||
|
best_name = name
|
|||
|
|
|||
|
# 判断匹配结果
|
|||
|
is_match = max_sim >= threshold
|
|||
|
if is_match:
|
|||
|
has_matched = True # 只要有一个匹配成功,就标记为True
|
|||
|
|
|||
|
bbox = face.bbox
|
|||
|
result_parts.append(
|
|||
|
f"{'匹配' if is_match else '不匹配'}: {best_name} (相似度: {max_sim:.2f}, 边界框: {bbox})"
|
|||
|
)
|
|||
|
|
|||
|
# 构建结果字符串
|
|||
|
if not result_parts:
|
|||
|
result_str = "未检测到人脸"
|
|||
|
else:
|
|||
|
result_str = "; ".join(result_parts)
|
|||
|
|
|||
|
# 第一个返回值改为:是否匹配到已知人脸
|
|||
|
return (has_matched, result_str)
|