Files
tencent_face_recognition/face_recognition.py
T

285 lines
9.4 KiB
Python

"""人脸识别核心功能"""
import logging
import json
from typing import Dict, Any, List, Optional
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from .const import (
ATTR_IMAGE_URL,
ATTR_IMAGE_PATH,
ATTR_MAX_FACE_NUM,
ATTR_MIN_FACE_SIZE,
ATTR_MAX_USER_NUM,
ATTR_QUALITY_CONTROL,
ATTR_NEED_ROTATE_CHECK,
ATTR_FACE_MATCH_THRESHOLD,
ERROR_IMAGE_NOT_FOUND,
ERROR_FACE_NOT_DETECTED,
)
from .tencent_cloud_client import TencentCloudClient
_LOGGER = logging.getLogger(__name__)
class FaceRecognition:
"""人脸识别核心功能"""
def __init__(self, hass: HomeAssistant, client: TencentCloudClient):
"""初始化人脸识别功能"""
self.hass = hass
self.client = client
async def async_search_faces(
self,
image_url: str = None,
image_path: str = None,
image_file: str = None,
group_ids: List[str] = None,
max_face_num: int = 1,
min_face_size: int = 34,
max_user_num: int = 5,
quality_control: int = 1,
need_rotate_check: int = 1,
face_match_threshold: float = 60.0
) -> Dict[str, Any]:
"""搜索人脸"""
try:
# 验证参数
self._validate_image_params(image_url, image_path, image_file)
# 调用腾讯云API搜索人脸
result = await self.hass.async_add_executor_job(
self.client.search_faces,
image_url,
image_path,
image_file,
group_ids,
max_face_num,
min_face_size,
max_user_num,
quality_control,
need_rotate_check,
face_match_threshold
)
# 处理结果
if result.get("success", False):
processed_faces = self._process_search_results(result.get("faces", []))
return {
"success": True,
"faces": processed_faces,
"error": None,
"error_code": None,
"error_message": None
}
else:
# 返回错误信息
return {
"success": False,
"faces": [],
"error": result.get("error"),
"error_code": result.get("error_code"),
"error_message": result.get("error_message")
}
except ValueError as ex:
_LOGGER.error("人脸搜索参数错误: %s", ex)
return {
"success": False,
"faces": [],
"error": str(ex),
"error_code": "invalid_parameter",
"error_message": str(ex)
}
except Exception as ex:
_LOGGER.error("人脸搜索失败: %s", ex)
return {
"success": False,
"faces": [],
"error": str(ex),
"error_code": "unknown_error",
"error_message": str(ex)
}
def _validate_image_params(self, image_url: str, image_path: str, image_file: str) -> None:
"""验证图片参数"""
if not image_url and not image_path and not image_file:
raise ValueError("必须提供image_url、image_path或image_file")
def _process_search_results(self, results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""处理人脸搜索结果"""
processed_results = []
for result in results:
processed_result = {
"face_id": result.get("face_id"),
"candidates": []
}
for candidate in result.get("candidates", []):
processed_candidate = {
"person_id": candidate.get("person_id"),
"person_name": candidate.get("person_name"),
"score": candidate.get("score"),
"person_tag": candidate.get("person_tag")
}
processed_result["candidates"].append(processed_candidate)
processed_results.append(processed_result)
return processed_results
def _process_detect_results(self, results: List[Dict[str, Any]], include_attributes: bool = False) -> List[Dict[str, Any]]:
"""处理人脸检测结果"""
processed_results = []
for result in results:
face_result = {
"face_id": result.get("face_id", ""),
"x": result.get("x", 0),
"y": result.get("y", 0),
"width": result.get("width", 0),
"height": result.get("height", 0),
"face_rect": result.get("face_rect")
}
# 如果需要包含属性信息
if include_attributes:
face_result.update({
"gender": result.get("gender"),
"age": result.get("age"),
"expression": result.get("expression"),
"beauty": result.get("beauty")
})
processed_results.append(face_result)
return processed_results
def _handle_api_error(self, operation: str, ex: Exception) -> None:
"""处理API错误"""
_LOGGER.error("%s失败: %s", operation, ex)
if isinstance(ex, ValueError):
raise HomeAssistantError(str(ex))
else:
raise HomeAssistantError(f"{operation}失败: {str(ex)}")
def _get_config_entry(self):
"""获取配置项"""
from homeassistant.config_entries import ConfigEntry
from . import DOMAIN
for entry in self.hass.config_entries.async_entries(DOMAIN):
return entry
return None
async def async_get_face_attributes(
self,
image_url: str = None,
image_path: str = None,
image_file: str = None,
max_face_num: int = 1,
need_rotate_check: int = 1
) -> Dict[str, Any]:
"""获取人脸属性"""
try:
# 验证参数
self._validate_image_params(image_url, image_path, image_file)
# 调用腾讯云API获取人脸属性
result = await self.hass.async_add_executor_job(
self.client.get_face_attributes,
image_url,
image_path,
image_file,
max_face_num,
need_rotate_check
)
# 处理结果
if result.get("success", False):
return {
"success": True,
"faces": result.get("faces", []),
"error": None,
"error_code": None,
"error_message": None
}
else:
# 返回错误信息
return {
"success": False,
"faces": [],
"error": result.get("error"),
"error_code": result.get("error_code"),
"error_message": result.get("error_message")
}
except Exception as ex:
_LOGGER.error("获取人脸属性失败: %s", ex)
return {
"success": False,
"faces": [],
"error": str(ex),
"error_code": "unknown_error",
"error_message": str(ex)
}
# 移除 _get_face_attributes_sync 方法,因为现在直接使用客户端的 get_face_attributes 方法
async def async_detect_faces(
self,
image_url: str = None,
image_path: str = None,
image_file: str = None,
max_face_num: int = 1,
min_face_size: int = 34,
need_rotate_check: int = 1
) -> Dict[str, Any]:
"""检测人脸"""
try:
# 验证参数
self._validate_image_params(image_url, image_path, image_file)
# 调用腾讯云API检测人脸
result = await self.hass.async_add_executor_job(
self.client.detect_faces,
image_url,
image_path,
image_file,
max_face_num,
min_face_size,
need_rotate_check
)
# 处理结果
if result.get("success", False):
return {
"success": True,
"faces": result.get("faces", []),
"error": None,
"error_code": None,
"error_message": None
}
else:
# 返回错误信息
return {
"success": False,
"faces": [],
"error": result.get("error"),
"error_code": result.get("error_code"),
"error_message": result.get("error_message")
}
except Exception as ex:
_LOGGER.error("人脸检测失败: %s", ex)
return {
"success": False,
"faces": [],
"error": str(ex),
"error_code": "unknown_error",
"error_message": str(ex)
}
# 移除 _detect_faces_sync 方法,因为现在直接使用客户端的 detect_faces 方法