Spaces:
Runtime error
Runtime error
File size: 4,458 Bytes
910e0d4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | import time
import numpy as np
from contextlib import contextmanager
from loguru import logger
from typing import List, Dict, Optional, Tuple, Union
from detection_schema import BBox
from storage import StorageInterface
import cv2
class DebugHandler:
"""Production-grade debugging and performance tracking"""
def __init__(self, enabled: bool = False, storage: StorageInterface = None):
self.enabled = enabled
self.storage = storage
self.metrics = {}
self._start_time = None
@contextmanager
def track_performance(self, operation_name: str):
"""Context manager for performance tracking"""
if self.enabled:
self._start_time = time.perf_counter()
logger.debug(f"Starting {operation_name}")
yield
if self.enabled:
duration = time.perf_counter() - self._start_time
self.metrics[operation_name] = duration
logger.debug(f"{operation_name} completed in {duration:.2f}s")
def save_artifact(self, name: str, data: bytes, extension: str = "png"):
"""Generic artifact storage handler"""
if self.enabled and self.storage:
path = f"debug/{name}.{extension}"
# Check if data is an np.ndarray (image)
if isinstance(data, np.ndarray):
# Convert np.ndarray to PNG bytes
success, encoded_image = cv2.imencode(f".{extension}", data)
if not success:
logger.error("Failed to encode image for saving.")
return
data = encoded_image.tobytes()
self.storage.save_file(path, data)
logger.info(f"Saved debug artifact: {path}")
class CoordinateTransformer:
@staticmethod
def global_to_local_bbox(
bbox: Union[BBox, List[BBox]],
roi: Optional[np.ndarray]
) -> Union[BBox, List[BBox]]:
"""
Convert global BBox(es) to ROI-local coordinates
Handles both single BBox and lists of BBoxes
"""
if roi is None or len(roi) != 4:
return bbox
x_min, y_min, _, _ = roi
def convert(b: BBox) -> BBox:
return BBox(
xmin=b.xmin - x_min,
ymin=b.ymin - y_min,
xmax=b.xmax - x_min,
ymax=b.ymax - y_min
)
return map(convert, bbox) if isinstance(bbox, list) else convert(bbox)
@staticmethod
def local_to_global_bbox(
bbox: Union[BBox, List[BBox]],
roi: Optional[np.ndarray]
) -> Union[BBox, List[BBox]]:
"""
Convert ROI-local BBox(es) to global coordinates
Handles both single BBox and lists of BBoxes
"""
if roi is None or len(roi) != 4:
return bbox
x_min, y_min, _, _ = roi
def convert(b: BBox) -> BBox:
return BBox(
xmin=b.xmin + x_min,
ymin=b.ymin + y_min,
xmax=b.xmax + x_min,
ymax=b.ymax + y_min
)
return map(convert, bbox) if isinstance(bbox, list) else convert(bbox)
# Maintain legacy tuple support if needed
@staticmethod
def global_to_local(
bboxes: List[Tuple[int, int, int, int]],
roi: Optional[np.ndarray]
) -> List[Tuple[int, int, int, int]]:
"""Legacy tuple version for backward compatibility"""
if roi is None or len(roi) != 4:
return bboxes
x_min, y_min, _, _ = roi
return [(x1 - x_min, y1 - y_min, x2 - x_min, y2 - y_min)
for x1, y1, x2, y2 in bboxes]
@staticmethod
def local_to_global(
bboxes: List[Tuple[int, int, int, int]],
roi: Optional[np.ndarray]
) -> List[Tuple[int, int, int, int]]:
"""Legacy tuple version for backward compatibility"""
if roi is None or len(roi) != 4:
return bboxes
x_min, y_min, _, _ = roi
return [(x1 + x_min, y1 + y_min, x2 + x_min, y2 + y_min)
for x1, y1, x2, y2 in bboxes]
@staticmethod
def local_to_global_point(point: Tuple[int, int], roi: Optional[np.ndarray]) -> Tuple[int, int]:
"""Convert single point from local to global coordinates"""
if roi is None or len(roi) != 4:
return point
x_min, y_min, _, _ = roi
return (int(point[0] + x_min), int(point[1] + y_min)) |