from fastapi import FastAPI, UploadFile, File import cv2 import numpy as np import mediapipe as mp app = FastAPI(title="Human Anthropometry API") mp_pose = mp.solutions.pose pose = mp_pose.Pose(static_image_mode=False) # ---- Utility ---- def dist(a, b): return np.linalg.norm(a - b) def analyze_frame(image): h, w, _ = image.shape rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) res = pose.process(rgb) if not res.pose_landmarks: return None lm = res.pose_landmarks.landmark def p(i): return np.array([lm[i].x * w, lm[i].y * h]) head = p(0) l_sh = p(11) r_sh = p(12) l_hip = p(23) r_hip = p(24) l_heel = p(29) r_heel = p(30) shoulder_px = dist(l_sh, r_sh) torso_px = dist((l_sh + r_sh)/2, (l_hip + r_hip)/2) height_px = dist(head, (l_heel + r_heel)/2) # ---- INDUSTRIAL NORMALIZATION ---- # Average adult shoulder width ≈ 0.23 × height height_cm = (shoulder_px / height_px) * (1 / 0.23) * 100 shoulder_cm = height_cm * 0.23 torso_ratio = torso_px / height_px # Weight estimation via body volume proxy (not BMI) weight_kg = round((height_cm * shoulder_cm * torso_ratio) / 1000, 1) confidence = round(min(0.95, 0.6 + torso_ratio), 2) return { "height_cm": round(height_cm, 1), "shoulder_cm": round(shoulder_cm, 1), "weight_kg": weight_kg, "confidence": confidence } # ---- API ---- @app.post("/analyze") async def analyze(file: UploadFile = File(...)): data = await file.read() img = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR) result = analyze_frame(img) if not result: return {"error": "Human not detected"} return result