Intelligent_PID / utils.py
msIntui
feat: initial clean deployment
910e0d4
Raw
History Blame Contribute Delete
4.46 kB
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))