| from fastapi import FastAPI, File, UploadFile |
| from fastapi.responses import JSONResponse |
| import numpy as np |
| import cv2 |
| import base64 |
|
|
| from sentinelscan.modeling.infer import CrackModel |
| from sentinelscan.utils.viz import overlay_mask |
| from sentinelscan.utils.severity import crack_metrics, severity_from_metrics |
|
|
| app = FastAPI(title="SentinelScan API", version="0.1.0") |
|
|
| model = CrackModel(ckpt_path="models/best.pt", size=512) |
|
|
| def _read_image(file_bytes: bytes): |
| arr = np.frombuffer(file_bytes, np.uint8) |
| bgr = cv2.imdecode(arr, cv2.IMREAD_COLOR) |
| if bgr is None: |
| raise ValueError("Could not decode image") |
| rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB) |
| return rgb |
|
|
| def _to_base64_png(rgb: np.ndarray): |
| bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) |
| ok, buf = cv2.imencode(".png", bgr) |
| if not ok: |
| raise ValueError("Could not encode image") |
| return base64.b64encode(buf.tobytes()).decode("utf-8") |
|
|
| @app.post("/predict") |
| async def predict(file: UploadFile = File(...)): |
| try: |
| rgb = _read_image(await file.read()) |
| pred = model.predict(rgb, threshold=0.5) |
|
|
| m = crack_metrics(pred["mask"]) |
| sev = severity_from_metrics(m) |
|
|
| crack_detected = m["area_px"] > 50 |
|
|
| overlay = overlay_mask(rgb, pred["mask"]) |
| overlay_b64 = _to_base64_png(overlay) |
|
|
| return JSONResponse({ |
| "crack_detected": bool(crack_detected), |
| "confidence": float(pred["confidence"]), |
| "severity": sev if crack_detected else "None", |
| "metrics": m, |
| "overlay_png_base64": overlay_b64, |
| }) |
| except Exception as e: |
| return JSONResponse({"error": str(e)}, status_code=400) |