import numpy as np
from alibabacloud_facebody20191230.client import Client as facebody20191230Client
from alibabacloud_facebody20191230 import models as facebody_20191230_models
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
from app.core.config import settings
from app.utils.common import sanitize_float
class BodyService:
@staticmethod
def create_client() -> facebody20191230Client:
"""
初始化阿里云人体识别 Client
"""
config = open_api_models.Config(
access_key_id=settings.ALIBABA_ACCESS_KEY_ID,
access_key_secret=settings.ALIBABA_ACCESS_KEY_SECRET
)
config.endpoint = settings.FACEBODY_ENDPOINT
return facebody20191230Client(config)
@staticmethod
def calculate_angle(a, b, c):
"""
计算三点 (a, b, c) 形成的夹角(度数)
"""
ba = np.array([a[0] - b[0], a[1] - b[1]])
bc = np.array([c[0] - b[0], c[1] - b[1]])
norm_ba = np.linalg.norm(ba)
norm_bc = np.linalg.norm(bc)
if norm_ba == 0 or norm_bc == 0:
return None
cosine_angle = np.dot(ba, bc) / (norm_ba * norm_bc)
angle = np.arccos(np.clip(cosine_angle, -1.0, 1.0))
return float(np.degrees(angle))
@staticmethod
def detect_arm_straightness(json_data, angle_threshold=160, weakness_threshold=120, diff_threshold=40):
results = []
try:
width = json_data['body']['Data']['MetaObject']['Width']
height = json_data['body']['Data']['MetaObject']['Height']
for person_id, person in enumerate(json_data['body']['Data']['Outputs'][0]['Results']):
keypoints = {
item['Label']: (
item['Positions'][0]['Points'][0] * width,
item['Positions'][0]['Points'][1] * height
)
for item in person['Bodies']
}
print(f"检测到的关键点: {keypoints}")
required_keys = {
'left_shoulder', 'right_shoulder',
'left_elbow', 'right_elbow',
'left_wrist', 'right_wrist'
}
if not required_keys.issubset(keypoints.keys()):
results.append({
"person_id": person_id,
"left_arm_angle": "无法计算",
"right_arm_angle": "无法计算",
"possibly_stroke": False,
"explanation": "关键点数据不完整,无法计算手臂角度"
})
continue
left_arm_angle = BodyService.calculate_angle(
keypoints['left_shoulder'], keypoints['left_elbow'], keypoints['left_wrist']
)
right_arm_angle = BodyService.calculate_angle(
keypoints['right_shoulder'], keypoints['right_elbow'], keypoints['right_wrist']
)
if left_arm_angle is None:
left_arm_angle = "无法计算"
if right_arm_angle is None:
right_arm_angle = "无法计算"
left_straight = left_arm_angle != "无法计算" and left_arm_angle >= angle_threshold
right_straight = right_arm_angle != "无法计算" and right_arm_angle >= angle_threshold
left_weak = left_arm_angle != "无法计算" and left_arm_angle < weakness_threshold
right_weak = right_arm_angle != "无法计算" and right_arm_angle < weakness_threshold
angle_diff = abs(left_arm_angle - right_arm_angle) if isinstance(left_arm_angle, float) and isinstance(
right_arm_angle, float) else "无法计算"
possibly_stroke = (
angle_diff != "无法计算" and angle_diff > diff_threshold
) or left_weak or right_weak
explanation = []
if left_straight and right_straight:
explanation.append("双臂笔直")
if left_weak:
explanation.append("左臂角度异常(疑似无力)")
if right_weak:
explanation.append("右臂角度异常(疑似无力)")
if angle_diff != "无法计算" and angle_diff > diff_threshold:
explanation.append(f"双臂角度差异较大({angle_diff:.2f} 度)")
results.append({
"person_id": person_id,
"left_arm_angle": left_arm_angle,
"right_arm_angle": right_arm_angle,
"possibly_stroke": bool(possibly_stroke),
"explanation": ",".join(explanation) if explanation else "无异常"
})
except KeyError as e:
print(f"JSON 解析错误,缺少字段: {e}")
return results
@staticmethod
def analyze_body_posture(image_url: str):
client = BodyService.create_client()
request = facebody_20191230_models.BodyPostureRequest(
image_url=image_url
)
runtime = util_models.RuntimeOptions()
try:
response = client.body_posture_with_options(request, runtime)
json_data = response.to_map()
results = BodyService.detect_arm_straightness(json_data)
for r in results:
if isinstance(r["left_arm_angle"], float):
r["left_arm_angle"] = sanitize_float(r["left_arm_angle"])
if isinstance(r["right_arm_angle"], float):
r["right_arm_angle"] = sanitize_float(r["right_arm_angle"])
return {
"results": results}
except Exception as error:
print(f"人体识别出错: {error}")
UtilClient.assert_as_string(str(error))
return {
"error": str(error)}