""" Building Detection API — FastAPI Backend for Hugging Face Spaces. Endpoints: GET / → Health check + model info GET /health → Health check POST /detect → Detect buildings in a polygon area """ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from typing import List, Optional import uvicorn from model_manager import load_model, get_model_info from inference import detect_buildings # ========================================== # === App Setup === # ========================================== app = FastAPI( title="🏗️ Building Detection API", description="Detect buildings from satellite imagery using Mask R-CNN V5", version="1.0.0", ) # Allow CORS for Vercel frontend app.add_middleware( CORSMiddleware, allow_origins=["*"], # In production, restrict to your Vercel domain allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ========================================== # === Request / Response Models === # ========================================== class DetectRequest(BaseModel): coordinates: List[List[float]] = Field( ..., description="Polygon coordinates as [[lng, lat], ...] in GeoJSON format", example=[[31.24, 30.04], [31.25, 30.04], [31.25, 30.05], [31.24, 30.05]], ) threshold: Optional[float] = Field( default=0.5, ge=0.1, le=0.95, description="Detection confidence threshold", ) use_v51: Optional[bool] = Field( default=True, description="Enable V5.1 pipeline (MobileSAM + SigLIP) for better accuracy", ) class DetectResponse(BaseModel): geojson: dict stats: dict class HealthResponse(BaseModel): status: str model: dict # ========================================== # === Startup Event === # ========================================== @app.on_event("startup") async def startup(): """Load model when the server starts.""" print("🚀 Starting Building Detection API...") load_model() print("✅ API ready!") # ========================================== # === Endpoints === # ========================================== @app.get("/", response_model=HealthResponse) async def root(): """Health check and model info.""" return { "status": "🟢 online", "model": get_model_info(), } @app.get("/health", response_model=HealthResponse) async def health(): """Health check endpoint.""" return { "status": "🟢 online", "model": get_model_info(), } @app.post("/detect", response_model=DetectResponse) async def detect(request: DetectRequest): """ Detect buildings in the specified polygon area. Send polygon coordinates in GeoJSON format [[lng, lat], ...]. Returns a GeoJSON FeatureCollection with detected building polygons. """ try: result = detect_buildings( coordinates=request.coordinates, threshold=request.threshold, use_v51=request.use_v51, ) if "error" in result: raise HTTPException(status_code=400, detail=result["error"]) return result except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}") # ========================================== # === Run === # ========================================== if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)