Spaces:
Sleeping
Sleeping
| """Geometry normalization — convert provider formats to canonical format. | |
| Providers return coordinates in various formats. This module provides | |
| explicit converters for each known convention. The canonical format is | |
| always (x, y, width, height) with origin at top_left, unit px. | |
| """ | |
| from __future__ import annotations | |
| from typing import TYPE_CHECKING | |
| from src.app.geometry.polygon import PolygonPoints, polygon_to_bbox | |
| if TYPE_CHECKING: | |
| from src.app.geometry.bbox import BBoxTuple | |
| def xyxy_to_xywh(xyxy: tuple[float, float, float, float]) -> BBoxTuple: | |
| """Convert (x1, y1, x2, y2) to canonical (x, y, width, height). | |
| Many providers (PaddleOCR rec_boxes, etc.) use this format. | |
| """ | |
| x1, y1, x2, y2 = xyxy | |
| if x2 < x1: | |
| x1, x2 = x2, x1 | |
| if y2 < y1: | |
| y1, y2 = y2, y1 | |
| return (x1, y1, x2 - x1, y2 - y1) | |
| def xywh_to_xyxy(bbox: BBoxTuple) -> tuple[float, float, float, float]: | |
| """Convert canonical (x, y, width, height) to (x1, y1, x2, y2).""" | |
| return (bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]) | |
| def cxcywh_to_xywh(cxcywh: tuple[float, float, float, float]) -> BBoxTuple: | |
| """Convert center-based (cx, cy, width, height) to canonical (x, y, w, h).""" | |
| cx, cy, w, h = cxcywh | |
| return (cx - w / 2, cy - h / 2, w, h) | |
| def xywh_to_cxcywh(bbox: BBoxTuple) -> tuple[float, float, float, float]: | |
| """Convert canonical (x, y, w, h) to center-based (cx, cy, w, h).""" | |
| return (bbox[0] + bbox[2] / 2, bbox[1] + bbox[3] / 2, bbox[2], bbox[3]) | |
| def four_point_to_xywh(points: list[list[float]] | list[tuple[float, float]]) -> BBoxTuple: | |
| """Convert a 4-point quadrilateral (e.g. PaddleOCR detection) to canonical bbox. | |
| PaddleOCR returns [[x1,y1],[x2,y2],[x3,y3],[x4,y4]] — 4 corners of | |
| a (possibly rotated) quadrilateral. We compute the axis-aligned | |
| bounding box enclosing all 4 points. | |
| """ | |
| if len(points) != 4: | |
| raise ValueError(f"Expected 4 points, got {len(points)}") | |
| polygon: PolygonPoints = [(float(p[0]), float(p[1])) for p in points] | |
| return polygon_to_bbox(polygon) | |
| def four_point_to_polygon(points: list[list[float]] | list[tuple[float, float]]) -> PolygonPoints: | |
| """Convert a 4-point list to a PolygonPoints list.""" | |
| if len(points) != 4: | |
| raise ValueError(f"Expected 4 points, got {len(points)}") | |
| return [(float(p[0]), float(p[1])) for p in points] | |
| def normalize_bbox_to_pixels( | |
| bbox: BBoxTuple, | |
| image_width: int, | |
| image_height: int, | |
| ) -> BBoxTuple: | |
| """Convert a normalized [0,1] bbox to pixel coordinates.""" | |
| return ( | |
| bbox[0] * image_width, | |
| bbox[1] * image_height, | |
| bbox[2] * image_width, | |
| bbox[3] * image_height, | |
| ) | |
| def pixels_to_normalized_bbox( | |
| bbox: BBoxTuple, | |
| image_width: int, | |
| image_height: int, | |
| ) -> BBoxTuple: | |
| """Convert pixel bbox to normalized [0,1] coordinates.""" | |
| if image_width <= 0 or image_height <= 0: | |
| raise ValueError( | |
| f"Image dimensions must be > 0, got {image_width}x{image_height}" | |
| ) | |
| return ( | |
| bbox[0] / image_width, | |
| bbox[1] / image_height, | |
| bbox[2] / image_width, | |
| bbox[3] / image_height, | |
| ) | |