image-tool / backend /app /api /detect_text.py
JunyoungK7's picture
Claude Sonnet 4.6
Deploy to HF Spaces: Docker setup, CPU inference, env-gated config
a3953cb
Raw History Blame Contribute Delete
1.99 kB
"""
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}