判断手臂高高举起的身体姿势识别的算法

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  # 返回 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)}