Files
appRobotBodyTracker/scripts/api/server.py
2026-06-08 19:50:36 +02:00

84 lines
2.7 KiB
Python

"""FastAPI REST-API für appRobotBodyTrack."""
from __future__ import annotations
import json
import tempfile
from pathlib import Path
from typing import List, Optional
from fastapi import FastAPI, File, HTTPException, UploadFile
from fastapi.responses import JSONResponse
from scripts import __version__, estimate_from_dir
_robot_json: Optional[Path] = None
def create_app(robot_json: str | Path | None = None) -> FastAPI:
"""App-Fabrik — setzt optionale Server-weite robot.json-Konfig."""
global _robot_json
if robot_json:
_robot_json = Path(robot_json).resolve()
return _app
_app = FastAPI(title="approbot-pipeline", version=__version__)
@_app.get("/v1/health")
def health():
return {"status": "ok", "version": __version__}
@_app.get("/v1/config")
def config():
if _robot_json is None or not _robot_json.exists():
raise HTTPException(404, "Keine robot.json konfiguriert")
data = json.loads(_robot_json.read_text(encoding="utf-8"))
return data.get("pose_estimation", {})
@_app.post("/v1/estimate")
async def estimate(
images: List[UploadFile] = File(..., description="Kamerabilder (render_<id>.png)"),
intrinsics: List[UploadFile] = File(..., description="Kamera-Intrinsiken (render_<id>.npz)"),
robot_json: Optional[UploadFile] = File(default=None, description="robot.json (überschreibt Server-Konfig)"),
):
"""Pose-Schätzung aus Kamerabildern.
Multipart-Upload:
images[] — render_a.png, render_b.png, ...
intrinsics[] — render_a.npz, render_b.npz, ... (gleiche Reihenfolge)
robot_json — optional, überschreibt die Server-Konfiguration
"""
with tempfile.TemporaryDirectory(prefix="approbot_req_") as tmp:
tmp_path = Path(tmp)
if robot_json is not None:
rj_path = tmp_path / "robot.json"
rj_path.write_bytes(await robot_json.read())
elif _robot_json and _robot_json.exists():
rj_path = _robot_json
else:
raise HTTPException(400, "Keine robot.json angegeben (weder Upload noch Server-Konfig)")
for img in images:
(tmp_path / img.filename).write_bytes(await img.read())
for npz in intrinsics:
(tmp_path / npz.filename).write_bytes(await npz.read())
try:
result = estimate_from_dir(tmp_path, robot_json=rj_path, eval_dir=tmp_path)
except FileNotFoundError as exc:
raise HTTPException(400, str(exc))
except Exception as exc:
raise HTTPException(500, str(exc))
return JSONResponse({
"joints": result.joints,
"confidence": result.confidence,
"residual_rms": result.residual_rms,
"n_markers": result.n_markers,
"processing_ms": result.processing_ms,
})