socks22's picture
two models
4bce717
"""FastAPI server for aerial car detection."""
import logging
from contextlib import asynccontextmanager
from typing import AsyncIterator
import cv2
import numpy as np
from fastapi import FastAPI, File, Query, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from server.detect import (
MODEL_CLASSES,
MODEL_PATHS,
annotate_image,
image_to_data_uri,
load_model,
run_detection,
)
from server.heatmap import generate_heatmap
logger = logging.getLogger(__name__)
_sessions: dict = {}
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
global _sessions
for key, path in MODEL_PATHS.items():
if path.exists():
_sessions[key] = load_model(path)
logger.info("Loaded model %s from %s", key, path)
else:
logger.warning("Model %s not found at %s, skipping", key, path)
yield
_sessions.clear()
app = FastAPI(title="Parking Car Detection", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
async def health() -> dict:
return {
"status": "ok",
"models_loaded": list(_sessions.keys()),
}
@app.post("/detect")
async def detect(
file: UploadFile = File(...),
threshold: float = Query(0.5, ge=0.0, le=1.0),
model: str = Query("cars"),
) -> JSONResponse:
if model not in _sessions:
return JSONResponse(
status_code=400,
content={"error": f"Model '{model}' not loaded. Available: {list(_sessions.keys())}"},
)
session = _sessions[model]
class_names = MODEL_CLASSES.get(model, ["object"])
contents = await file.read()
arr = np.frombuffer(contents, dtype=np.uint8)
image = cv2.imdecode(arr, cv2.IMREAD_COLOR)
if image is None:
return JSONResponse(
status_code=400,
content={"error": "Could not decode image"},
)
detections = run_detection(session, image, threshold, class_names)
annotated = annotate_image(image, detections)
# For spot model, heatmap shows only occupied spots
if model == "spots":
heatmap_dets = [d for d in detections if d.get("class_name") == "occupied"]
else:
heatmap_dets = detections
heatmap = generate_heatmap(image, heatmap_dets)
response: dict = {
"model": model,
"car_count": len(detections),
"detections": detections,
"annotated_image": image_to_data_uri(annotated),
"heatmap_image": image_to_data_uri(heatmap),
}
if model == "spots":
empty = sum(1 for d in detections if d.get("class_name") == "empty")
occupied = sum(1 for d in detections if d.get("class_name") == "occupied")
total = empty + occupied
response["occupancy"] = {
"empty_count": empty,
"occupied_count": occupied,
"total_spots": total,
"occupancy_rate": round(occupied / total, 3) if total > 0 else 0.0,
}
return response