"""Color and pixel analysis utilities. Provides tools for reading health/mana bars, detecting UI states via color sampling, and pixel-level game state detection. """ from typing import Tuple, Optional, List import logging import numpy as np import cv2 logger = logging.getLogger(__name__) class ColorAnalyzer: """Analyze pixel colors and UI bar states.""" @staticmethod def get_pixel_color(screen: np.ndarray, x: int, y: int) -> Tuple[int, int, int]: """Get BGR color at pixel position.""" return tuple(screen[y, x].tolist()) @staticmethod def get_pixel_hsv(screen: np.ndarray, x: int, y: int) -> Tuple[int, int, int]: """Get HSV color at pixel position.""" hsv = cv2.cvtColor(screen[y:y+1, x:x+1], cv2.COLOR_BGR2HSV) return tuple(hsv[0, 0].tolist()) @staticmethod def color_matches( color: Tuple[int, int, int], target: Tuple[int, int, int], tolerance: int = 20, ) -> bool: """Check if a color matches target within tolerance.""" return all(abs(c - t) <= tolerance for c, t in zip(color, target)) @staticmethod def read_bar_percentage( screen: np.ndarray, bar_region: Tuple[int, int, int, int], filled_color_hsv: Tuple[Tuple[int, int, int], Tuple[int, int, int]], ) -> float: """Read a horizontal bar's fill percentage (health, mana, xp, etc.). Args: screen: Screenshot in BGR bar_region: (x, y, width, height) of the bar filled_color_hsv: (lower_hsv, upper_hsv) range of the filled portion Returns: Fill percentage 0.0 to 1.0 """ x, y, w, h = bar_region bar = screen[y:y+h, x:x+w] hsv = cv2.cvtColor(bar, cv2.COLOR_BGR2HSV) lower, upper = filled_color_hsv mask = cv2.inRange(hsv, np.array(lower), np.array(upper)) # Scan columns left to right to find the fill boundary col_fill = np.mean(mask, axis=0) / 255.0 # Find the rightmost column that's mostly filled threshold = 0.3 filled_cols = np.where(col_fill > threshold)[0] if len(filled_cols) == 0: return 0.0 return (filled_cols[-1] + 1) / w @staticmethod def sample_region_dominant_color( screen: np.ndarray, region: Tuple[int, int, int, int], ) -> Tuple[int, int, int]: """Get the dominant BGR color in a region.""" x, y, w, h = region roi = screen[y:y+h, x:x+w] pixels = roi.reshape(-1, 3).astype(np.float32) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) _, labels, centers = cv2.kmeans(pixels, 1, None, criteria, 3, cv2.KMEANS_RANDOM_CENTERS) return tuple(centers[0].astype(int).tolist())