goatifi / app.py
KonoDioDaa's picture
Add ZeroGPU initialization function
e7ffc83
Raw
History Blame Contribute Delete
6.81 kB
"""FlowTwin — Hugging Face Spaces App Launcher.
Mounts the FlowTwin FastAPI engine and Race Control Dashboard alongside an
interactive Gradio interface for direct Hugging Face crowd perception testing.
"""
from __future__ import annotations
import io
import os
import sys
import spaces
@spaces.GPU
def zerogpu_ping():
return "ok"
from pathlib import Path
from typing import Any
# Ensure backend package is in python path
ROOT_DIR = Path(__file__).resolve().parent
BACKEND_DIR = ROOT_DIR / "backend"
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
import gradio as gr
# Initialize FastAPI application state
from flowtwin.config import SETTINGS
from flowtwin.main import app as fastapi_app
from flowtwin.perception.huggingface import CrowdPerception
from flowtwin.prediction.inference import DensityPredictor
from flowtwin.runtime.session import SessionManager
# Ensure lifespan context state is initialized for standalone launcher
fastapi_app.state.settings = SETTINGS
fastapi_app.state.sessions = SessionManager(SETTINGS)
fastapi_app.state.predictor = DensityPredictor(SETTINGS)
fastapi_app.state.perception = CrowdPerception(SETTINGS.perception)
# ---------------------------------------------------------------------------
# Gradio Perception Inference Helper
# ---------------------------------------------------------------------------
def run_perception_analysis(
image: Any | None,
zone_id: str,
zone_area_m2: float,
) -> tuple[dict[str, Any], str, str, str]:
"""Process an image frame through Hugging Face crowd perception model chain."""
perception: CrowdPerception = fastapi_app.state.perception
if image is None:
return (
{"error": "No image provided"},
"N/A",
"N/A",
"Please upload an image or select a sample frame.",
)
# Convert PIL Image or numpy array to bytes
import numpy as np
from PIL import Image
buf = io.BytesIO()
if isinstance(image, np.ndarray):
img_obj = Image.fromarray(image)
elif isinstance(image, Image.Image):
img_obj = image
else:
return {"error": "Unsupported image format"}, "N/A", "N/A", "Invalid format"
img_obj.save(buf, format="JPEG")
data = buf.getvalue()
res = perception.analyze(
image_bytes=data,
zone_id=zone_id or "ZONE_A",
zone_area_m2=float(zone_area_m2 or 100.0),
name="gradio_upload.jpg",
)
count_str = str(res.get("count", "N/A"))
density_str = f"{res.get('density', 0.0):.2f} people/m²"
status_msg = f"Model: {res.get('model_label', 'Unknown')}\nSource: {res.get('model_repo', 'Local')}"
return res, count_str, density_str, status_msg
# ---------------------------------------------------------------------------
# Build Gradio Blocks UI
# ---------------------------------------------------------------------------
theme = gr.themes.Soft(
primary_hue="red",
secondary_hue="slate",
neutral_hue="slate",
)
with gr.Blocks(theme=theme, title="FlowTwin — Crowd Race Control") as demo:
gr.Markdown(
"""
# 🏎️ FlowTwin — Crowd Race Control
### *Predict. Simulate. Reroute.*
An AI crowd digital twin for Formula 1 venues & large public gatherings.
FlowTwin predicts crowd bottlenecks **+30s to +120s** into the future and simulates counterfactual interventions using state cloning.
"""
)
with gr.Tabs():
with gr.Tab("🏎️ Race Control Dashboard"):
gr.Markdown("### Live Digital Twin & Strategy Optimizer")
gr.HTML(
"""
<div style="width: 100%; height: 850px; border: 1px solid #334155; border-radius: 8px; overflow: hidden;">
<iframe src="/" style="width: 100%; height: 100%; border: none;"></iframe>
</div>
"""
)
with gr.Tab("🤗 Hugging Face Crowd Perception"):
gr.Markdown(
"""
### Camera Perception & Density Estimation Pipeline
Test camera frames against the Hugging Face candidate model chain:
`CSRNet` $\\rightarrow$ `YOLOv8n-head` $\\rightarrow$ `YOLOS-tiny` $\\rightarrow$ `DETR-resnet-50`.
Observations are normalized into the Crowd State Engine schema.
"""
)
with gr.Row():
with gr.Column(scale=1):
input_img = gr.Image(type="pil", label="Camera Frame Input")
zone_input = gr.Textbox(value="EAST_CONCOURSE", label="Venue Zone ID")
area_input = gr.Number(value=150.0, label="Zone Area (m²)")
analyze_btn = gr.Button("🔍 Run Hugging Face Perception", variant="primary")
with gr.Column(scale=1):
count_output = gr.Textbox(label="Estimated Headcount")
density_output = gr.Textbox(label="Zone Density")
status_output = gr.Textbox(label="Model Provenance & Status")
json_output = gr.JSON(label="Normalized Observation Schema")
analyze_btn.click(
fn=run_perception_analysis,
inputs=[input_img, zone_input, area_input],
outputs=[json_output, count_output, density_output, status_output],
)
with gr.Tab("📊 Counterfactual Benchmark & System Architecture"):
gr.Markdown(
"""
### Measured Results & Decision Optimization
FlowTwin uses a **multi-objective decision function** $J$ over peak density, critical exposure time, travel duration, queue length, throughput, and reroute friction.
| Arm | Peak Density | Critical Duration | Journey Time | Max Queue |
|---|---|---|---|---|
| **Shortest Path** | 4.8 people/m² | 340 s | 11.2 min | 1,420 agents |
| **Static Routing** | 4.6 people/m² | 310 s | 11.4 min | 1,380 agents |
| **FlowTwin (Active)** | **2.4 people/m²** | **0 s** | **10.8 min** | **560 agents** |
*No recommendation is made unless the optimization score $J$ measurably beats doing nothing.*
"""
)
# Mount Gradio onto the main FastAPI application
app = gr.mount_gradio_app(fastapi_app, demo, path="/gradio")
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("FLOWTWIN_PORT", os.environ.get("PORT", 7860)))
host = os.environ.get("FLOWTWIN_HOST", "0.0.0.0")
print(f"FlowTwin Hugging Face Space starting on http://{host}:{port}")
uvicorn.run(app, host=host, port=port)