from typing import Any, Dict, Literal, Optional from pydantic import BaseModel, Field class DetectionParams(BaseModel): canny_low: Optional[int] = Field(None, ge=0, le=255) canny_high: Optional[int] = Field(None, ge=0, le=255) harris_k: Optional[float] = Field(None, ge=0.0, le=1.0) harris_block: Optional[int] = Field(None, ge=1, le=16) harris_ksize: Optional[int] = Field(None, ge=1, le=15) hough_thresh: Optional[int] = Field(None, ge=1, le=500) hough_min_len: Optional[int] = Field(None, ge=1, le=1000) hough_max_gap: Optional[int] = Field(None, ge=0, le=200) ellipse_min_area: Optional[int] = Field(None, ge=10, le=100000) max_ellipses: Optional[int] = Field(None, ge=1, le=100) line_detector: Optional[Literal["hough", "lsd"]] = Field( None, description="Classical line detector variant to use: 'hough' (default) or 'lsd'.", ) class DetectionRequest(BaseModel): image: str = Field(..., description="Base64-encoded image (PNG/JPEG).") params: Optional[DetectionParams] = None mode: Literal["classical", "dl", "both"] = "classical" compare: bool = False dl_model: Optional[str] = Field( None, description="Optional ONNX filename override from ./models." ) class DetectionResponse(BaseModel): overlay: Optional[str] = None overlays: Dict[str, Optional[str]] features: Dict[str, Any] timings: Dict[str, float] fps_estimate: Optional[float] = None model: Dict[str, Any] models: Dict[str, Dict[str, Any]]