Spaces:
Paused
Paused
Download sensorflow/studio_ux/api.py from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 6.66 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/studio_ux/api.py
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/sensorflow/studio_ux/api.py
-
curl -L -o api.py https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/studio_ux/api.py
6.66 kB
| """FastAPI router for studio-UX support (/api/studio-ux/*). | |
| Endpoints: | |
| GET /layouts/{key} fetch a persisted UI layout/preset (JSON blob) | |
| PUT /layouts/{key} persist a UI layout/preset | |
| GET /bev/replay deterministically re-generate one BEV-fusion scene | |
| sequence (same generators/engines as /api/bevfusion, | |
| read-only) and return per-frame GT boxes, camera and | |
| LiDAR detections with covariances, fused+tracked | |
| boxes (masklet-propagated flagged) and the camera | |
| baseline boxes — the data behind the interactive | |
| top-down BEV canvas. | |
| This module only *reads* from other subsystems (pure functions on synthetic | |
| scenes); the only writes are its own layout files under runs/studio_ux/. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import re | |
| from pathlib import Path | |
| from typing import Any, Dict, List, Optional | |
| from fastapi import APIRouter, HTTPException, Query | |
| from pydantic import BaseModel | |
| router = APIRouter(prefix="/api/studio-ux", tags=["studio-ux"]) | |
| LAYOUTS_DIR = Path("runs/studio_ux/layouts") | |
| _KEY_RE = re.compile(r"^[A-Za-z0-9._-]{1,120}$") | |
| def _layout_path(key: str) -> Path: | |
| if not _KEY_RE.match(key): | |
| raise HTTPException(422, "layout key must match [A-Za-z0-9._-]{1,120}") | |
| return LAYOUTS_DIR / f"{key}.json" | |
| def get_layout(key: str) -> Dict[str, Any]: | |
| path = _layout_path(key) | |
| if not path.exists(): | |
| return {"key": key, "value": None} | |
| try: | |
| with open(path) as f: | |
| return {"key": key, "value": json.load(f)} | |
| except Exception: | |
| return {"key": key, "value": None} | |
| class LayoutBody(BaseModel): | |
| value: Any | |
| def put_layout(key: str, body: LayoutBody) -> Dict[str, Any]: | |
| path = _layout_path(key) | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with open(path, "w") as f: | |
| json.dump(body.value, f) | |
| return {"key": key, "saved": True} | |
| # ------------------------------------------------------------------ BEV replay | |
| def _det_dict(det) -> Dict[str, Any]: | |
| return { | |
| "modality": det.modality, | |
| "x": round(det.x, 3), | |
| "y": round(det.y, 3), | |
| "cov": [[round(v, 5) for v in row] for row in det.cov], | |
| "dims": [round(v, 3) for v in det.dims], | |
| "yaw": round(det.yaw, 4), | |
| "class_name": det.class_name, | |
| "confidence": round(det.confidence, 3), | |
| } | |
| def bev_replay(seed: int = Query(7), | |
| n_sequences: int = Query(6, ge=1, le=24), | |
| frames_per_sequence: int = Query(24, ge=8, le=60), | |
| sequence: int = Query(0, ge=0)) -> Dict[str, Any]: | |
| """Deterministic replay of one scene sequence through both engines.""" | |
| from sensorflow.bevfusion.engines import FrameToFrameTracker | |
| from sensorflow.bevfusion.fusion import build_modality_map, decode_bev, fuse_maps | |
| from sensorflow.bevfusion.geometry import BEVGrid | |
| from sensorflow.bevfusion.masklet import BEVMaskletTracker | |
| from sensorflow.bevfusion.scenes import generate_sequences | |
| from sensorflow.bevfusion.sensors import camera_rng, lidar_rng, simulate_camera, simulate_lidar | |
| from sensorflow.schemas.unified_frame import Object3D | |
| if sequence >= n_sequences: | |
| raise HTTPException(422, f"sequence must be < n_sequences ({n_sequences})") | |
| sequences = generate_sequences(n_sequences=n_sequences, | |
| frames_per_sequence=frames_per_sequence, | |
| seed=seed) | |
| seq = sequences[sequence] | |
| grid = BEVGrid() | |
| cam_gen = camera_rng(seed, sequence) | |
| lid_gen = lidar_rng(seed, sequence) | |
| base_gen = camera_rng(seed, sequence) # baseline uses the same camera stream | |
| masklet = BEVMaskletTracker(bounds=(grid.x_min, grid.x_max, grid.y_min, grid.y_max)) | |
| base_tracker = FrameToFrameTracker(gate=3.0) | |
| frames: List[Dict[str, Any]] = [] | |
| for frame in seq.frames: | |
| cam_dets = simulate_camera(frame, seq, cam_gen) | |
| lid_dets = simulate_lidar(frame, seq, lid_gen) | |
| # fused engine (mirrors engines.run_fused, but keeps the raw detections) | |
| cam_map = build_modality_map(cam_dets, grid, "camera") | |
| lid_map = build_modality_map(lid_dets, grid, "lidar") | |
| decoded = decode_bev(fuse_maps(cam_map, lid_map)) | |
| proposals = [Object3D(bbox_3d=b["bbox_3d"], class_name=b["class_name"], | |
| confidence=b["confidence"]) for b in decoded] | |
| tracked = masklet.update(frame.frame_id, proposals) | |
| propagated_ids = getattr(masklet, "last_propagated", set()) | |
| fused = [{ | |
| "bbox_3d": [round(v, 3) for v in t.bbox_3d], | |
| "class_name": t.class_name, | |
| "confidence": round(float(t.confidence or 0.0), 3), | |
| "track_id": t.track_id, | |
| "propagated": t.track_id in propagated_ids, | |
| } for t in tracked] | |
| # camera-only baseline (mirrors engines.run_baseline; identical rng stream) | |
| base_boxes = [{ | |
| "bbox_3d": [d.x, d.y, d.z, d.dims[0], d.dims[1], d.dims[2], d.yaw], | |
| "class_name": d.class_name, | |
| "confidence": d.confidence, | |
| "propagated": False, | |
| } for d in simulate_camera(frame, seq, base_gen)] | |
| base_boxes = base_tracker.update(base_boxes) | |
| baseline = [{ | |
| "bbox_3d": [round(float(v), 3) for v in b["bbox_3d"]], | |
| "class_name": b["class_name"], | |
| "confidence": round(float(b["confidence"]), 3), | |
| "track_id": b["track_id"], | |
| "propagated": False, | |
| } for b in base_boxes] | |
| frames.append({ | |
| "frame_id": frame.frame_id, | |
| "index": frame.index, | |
| "gt": [{ | |
| "instance_id": g.instance_id, | |
| "class_name": g.class_name, | |
| "bbox_3d": g.bbox_3d, | |
| "occluded": g.occluded, | |
| "distance": g.distance, | |
| } for g in frame.gt], | |
| "camera": [_det_dict(d) for d in cam_dets], | |
| "lidar": [_det_dict(d) for d in lid_dets], | |
| "fused": fused, | |
| "baseline": baseline, | |
| }) | |
| return { | |
| "sequence_id": seq.sequence_id, | |
| "sequence_index": sequence, | |
| "n_sequences": n_sequences, | |
| "time_of_day": seq.time_of_day, | |
| "weather": seq.weather, | |
| "params": {"n_sequences": n_sequences, | |
| "frames_per_sequence": frames_per_sequence, "seed": seed}, | |
| "frames": frames, | |
| } | |