from dataclasses import dataclass import cv2 from src.logger import logger from src.utils.image import ImageUtils # ✅ Safe import for headless environments try: from screeninfo import get_monitors monitor_window = get_monitors()[0] SCREEN_WIDTH, SCREEN_HEIGHT = monitor_window.width, monitor_window.height except Exception as e: # Headless fallback — e.g., Hugging Face Spaces, servers, CI print(f"[Headless Mode] No monitor detected. Using default size. Error: {e}") SCREEN_WIDTH, SCREEN_HEIGHT = 1920, 1080 @dataclass class ImageMetrics: # Window dimensions window_width: int = SCREEN_WIDTH window_height: int = SCREEN_HEIGHT # For positioning image windows window_x: int = 0 window_y: int = 0 reset_pos = [0, 0] class InteractionUtils: """Perform primary functions such as displaying images and reading responses""" image_metrics = ImageMetrics() @staticmethod def show(name, origin, pause=1, resize=False, reset_pos=None, config=None): image_metrics = InteractionUtils.image_metrics if origin is None: logger.info(f"'{name}' - NoneType image to show!") if pause: cv2.destroyAllWindows() return # Resize if required if resize: if not config: raise Exception("config not provided for resizing the image to show") img = ImageUtils.resize_util(origin, config.dimensions.display_width) else: img = origin # Try to show the image only if display is available try: if not is_window_available(name): cv2.namedWindow(name) cv2.imshow(name, img) cv2.moveWindow(name, image_metrics.window_x, image_metrics.window_y) except cv2.error: # Skip visualization entirely in headless environments logger.info(f"Skipping cv2.imshow for '{name}' (headless mode).") # Update next window position h, w = img.shape[:2] margin = 25 w += margin h += margin w, h = w // 2, h // 2 if image_metrics.window_x + w > image_metrics.window_width: image_metrics.window_x = 0 if image_metrics.window_y + h > image_metrics.window_height: image_metrics.window_y = 0 else: image_metrics.window_y += h else: image_metrics.window_x += w if pause: logger.info(f"Showing '{name}' (press Q to continue, Ctrl+C to exit)") wait_q() InteractionUtils.image_metrics.window_x = 0 InteractionUtils.image_metrics.window_y = 0 @dataclass class Stats: files_moved = 0 files_not_moved = 0 def wait_q(): esc_key = 27 try: while cv2.waitKey(1) & 0xFF not in [ord("q"), esc_key]: pass cv2.destroyAllWindows() except cv2.error: # Skip in headless mode pass def is_window_available(name: str) -> bool: """Checks if a window is available""" try: cv2.getWindowProperty(name, cv2.WND_PROP_VISIBLE) return True except Exception: return False