186 lines
6.4 KiB
Python
186 lines
6.4 KiB
Python
from typing import Dict, List, Optional
|
||
|
||
from utils.compreface_util import ComprefaceUtil
|
||
from config.env import ComprefaceConfig
|
||
|
||
POSE_MAX_ANGLE = getattr(ComprefaceConfig, "COMPREFACE_POSE_MAX_ANGLE", 10)
|
||
MIN_FACE_WIDTH = getattr(ComprefaceConfig, "COMPREFACE_MIN_FACE_WIDTH", 200)
|
||
MIN_FACE_HEIGHT = getattr(ComprefaceConfig, "COMPREFACE_MIN_FACE_HEIGHT", 200)
|
||
MIN_DETECTION_PROBABILITY = getattr(ComprefaceConfig, "COMPREFACE_MIN_DETECTION_PROBABILITY", 0.9)
|
||
|
||
class ComprefaceService(ComprefaceUtil):
|
||
|
||
"""compreface服务类"""
|
||
|
||
@classmethod
|
||
def _is_pose_front(cls, pose: Dict | None) -> bool:
|
||
if not pose:
|
||
return False
|
||
return all(abs(float(pose.get(axis, 0))) <= POSE_MAX_ANGLE for axis in ("pitch", "roll", "yaw"))
|
||
|
||
@classmethod
|
||
def _is_face_close(cls, box: Dict | None) -> bool:
|
||
if not box:
|
||
return False
|
||
width = float(box.get("x_max", 0)) - float(box.get("x_min", 0))
|
||
height = float(box.get("y_max", 0)) - float(box.get("y_min", 0))
|
||
probability = float(box.get("probability", 0))
|
||
return (
|
||
width >= MIN_FACE_WIDTH
|
||
and height >= MIN_FACE_HEIGHT
|
||
and probability >= MIN_DETECTION_PROBABILITY
|
||
)
|
||
|
||
@classmethod
|
||
def _select_valid_detection(cls, detections: List[Dict]) -> Optional[Dict]:
|
||
for detection in detections:
|
||
pose = detection.get("pose")
|
||
box = detection.get("box")
|
||
if cls._is_pose_front(pose) and cls._is_face_close(box):
|
||
return detection
|
||
return None
|
||
|
||
@staticmethod
|
||
def _pending_response() -> Dict[str, str]:
|
||
return {
|
||
"name": "未识别",
|
||
"role": "等待正脸"
|
||
}
|
||
|
||
# 人脸检测
|
||
@classmethod
|
||
async def face_detection_service(cls, image, options: dict = {}):
|
||
""" 人脸检测
|
||
|
||
Args:
|
||
image_path (str): 图片路径
|
||
options (dict, optional): 检测参数. Defaults to {}.
|
||
|
||
Returns:
|
||
[type]: [description]
|
||
"""
|
||
options={
|
||
"limit": 0,
|
||
"det_prob_threshold": 0.8,
|
||
"prediction_count": 1,
|
||
# 可选参数 age,gender,landmarks,calculator
|
||
"face_plugins": "pose",
|
||
"status": "false",
|
||
}
|
||
result = await ComprefaceUtil.face_detection(image, options)
|
||
return result.get("result", [])
|
||
|
||
# 人脸识别
|
||
@classmethod
|
||
async def face_recognition_service(cls, image, options: dict = {}):
|
||
""" 人脸识别
|
||
|
||
Args:
|
||
image_path (str): 图片路径
|
||
options (dict, optional): 参数. Defaults to {}.
|
||
|
||
Returns:
|
||
[type]: [description]
|
||
"""
|
||
detections = await cls.face_detection_service(image)
|
||
detection = cls._select_valid_detection(detections)
|
||
if not detection:
|
||
return cls._pending_response()
|
||
|
||
result = await ComprefaceUtil.face_recognition(image, options)
|
||
recognition_list = result.get('result') or []
|
||
if not recognition_list:
|
||
return cls._pending_response()
|
||
|
||
subjects = recognition_list[0].get('subjects') or []
|
||
if not subjects:
|
||
return cls._pending_response()
|
||
|
||
subject = subjects[0]
|
||
name = subject.get('subject', '')
|
||
similarity = subject.get('similarity', 0)
|
||
if similarity < ComprefaceConfig.COMPREFACE_SIMILARITY_THRESHOLD:
|
||
return {
|
||
"name": "未知",
|
||
"role": "陌生人"
|
||
}
|
||
else:
|
||
t = name.split("_")
|
||
return {
|
||
"name": t[0],
|
||
"role": t[1] if len(t) > 1 else "员工"
|
||
}
|
||
|
||
# 添加图像到人脸库
|
||
@classmethod
|
||
async def face_addition_service(cls, image, name):
|
||
result = await ComprefaceUtil.face_addition(image, name)
|
||
return result
|
||
|
||
# 批量添加图像到人脸库
|
||
@classmethod
|
||
async def face_addition_batch_service(cls, t):
|
||
result_list = []
|
||
for image, name in t:
|
||
result = await ComprefaceUtil.face_addition(image, name)
|
||
result_list.append(result)
|
||
return result_list
|
||
|
||
# 删除过期的访客人脸
|
||
@classmethod
|
||
async def delete_expired_visitor_faces_service(cls, active_visitor_names: list):
|
||
"""删除CompreFace中已过期的访客人脸数据
|
||
|
||
Args:
|
||
active_visitor_names (list): 当前有效的访客名字列表
|
||
|
||
Returns:
|
||
dict: 包含删除成功和失败的subject列表
|
||
"""
|
||
try:
|
||
# 获取CompreFace中所有的subjects
|
||
all_subjects_result = await ComprefaceUtil.get_all_subjects()
|
||
all_subjects = all_subjects_result.get('subjects', [])
|
||
|
||
deleted_subjects = []
|
||
failed_subjects = []
|
||
|
||
# 遍历所有subjects,删除不在active_visitor_names中的访客
|
||
for subject in all_subjects:
|
||
# 假设访客的subject格式为 "姓名_访客" 或 "姓名"
|
||
# 只删除访客类型的人脸,员工人脸不删除
|
||
if subject not in active_visitor_names:
|
||
# 如果subject不在活跃访客列表中,且不是员工(员工一般包含"_员工"等标识)
|
||
# 这里假设访客没有特殊后缀,或者可以根据实际情况调整判断逻辑
|
||
# 如果subject包含"_员工"等标识,则跳过
|
||
if "_员工" in subject or "_职员" in subject or "_工作人员" in subject:
|
||
continue
|
||
|
||
try:
|
||
# 删除该subject
|
||
await ComprefaceUtil.delete_subject(subject)
|
||
deleted_subjects.append(subject)
|
||
except Exception as e:
|
||
failed_subjects.append({"subject": subject, "error": str(e)})
|
||
|
||
return {
|
||
"deleted_count": len(deleted_subjects),
|
||
"deleted_subjects": deleted_subjects,
|
||
"failed_count": len(failed_subjects),
|
||
"failed_subjects": failed_subjects
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"error": f"删除过期访客失败: {str(e)}",
|
||
"deleted_count": 0,
|
||
"deleted_subjects": [],
|
||
"failed_count": 0,
|
||
"failed_subjects": []
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|