""" detect_text.py — POST /detect-text Runs PaddleOCR on the original image stored by /upload and returns a list of axis-aligned bounding boxes in original-image pixel coordinates. Why axis-aligned boxes, not raw quads? --------------------------------------- PaddleOCR returns rotated quadrilaterals (4 corner points per region). Converting to the minimum enclosing axis-aligned rect (min/max of the 4 x and y values) allows mask_service.build_masks() to rasterize them with zero changes — it already accepts {"x", "y", "width", "height"} dicts. The slight extra background at rotated-text corners is absorbed by the dilation and feathering steps, so the composite seam is invisible. Zero detections is not an error --------------------------------- If OCR finds no text, {"boxes": []} is returned with HTTP 200. The frontend displays a "nothing detected" hint; the user can still paint manually. """ from fastapi import APIRouter, HTTPException from pydantic import BaseModel from app.jobs_store import get_job from app.services.ocr_service import detect_text_regions router = APIRouter() class DetectTextRequest(BaseModel): job_id: str @router.post("") def detect_text(body: DetectTextRequest) -> dict: """Run OCR on the uploaded image and return detected text bounding boxes. Response -------- {"boxes": [{"x": int, "y": int, "width": int, "height": int}, ...]} All coordinates are in original-image pixels — the same space the mask JSON contract uses — so the frontend can put them directly into the ``boxes`` array of buildMaskJson() without any scaling. Raises 404 if the job_id is not found. Returns {"boxes": []} with HTTP 200 if no text is detected. """ job = get_job(body.job_id) if job is None: raise HTTPException( status_code=404, detail=f"Job '{body.job_id}' not found.", ) boxes = detect_text_regions(job.steps[0].original_image) return {"boxes": boxes}