""" Color detection and normalization utilities for play clock digit processing. This module provides utilities for detecting red digits (shown when play clock <= 5) and normalizing play clock regions to grayscale for consistent template matching. These utilities are shared across: - readers/playclock.py (template matching) - setup/template_builder.py (template building) - setup/playclock_region.py (OCR preprocessing) """ import logging from typing import Any import cv2 import numpy as np logger = logging.getLogger(__name__) def detect_red_digits(region: np.ndarray[Any, Any]) -> bool: """ Detect if the play clock digits are red. Red digits appear when the play clock has 5 seconds or less remaining. Red digits have high red channel values with very low green and blue. Args: region: Play clock region (BGR format) Returns: True if red digits detected, False otherwise """ # Split into BGR channels (np.asarray normalizes cv2.Mat type for numpy) b, g, r = cv2.split(region) # Calculate mean values for each channel r_mean = np.mean(np.asarray(r)) g_mean = np.mean(np.asarray(g)) b_mean = np.mean(np.asarray(b)) # Red digits: high red channel, very low green/blue, red > 2x green max_gb = max(g_mean, b_mean) is_red = bool(r_mean > 15 > max_gb and r_mean > g_mean * 2) if is_red: logger.debug("Red digits detected: R=%.1f, G=%.1f, B=%.1f", r_mean, g_mean, b_mean) return is_red def normalize_to_grayscale(region: np.ndarray[Any, Any]) -> np.ndarray[Any, Any]: """ Normalize a play clock region to grayscale, handling both red and white digits. Red digits (displayed when clock <= 5) are converted to white-like grayscale by extracting the red channel. White digits use standard grayscale conversion. This allows a single set of templates to match both color variants. Args: region: Play clock region (BGR format) Returns: Grayscale image where digits appear as bright pixels on dark background """ is_red = detect_red_digits(region) if is_red: # For red digits, use the red channel directly as grayscale # This converts red digits to white-like appearance _, _, r = cv2.split(region) return r # Standard grayscale conversion for white digits return cv2.cvtColor(region, cv2.COLOR_BGR2GRAY)