cfb40 / src /utils /color.py
andytaylor-smg's picture
perfect mypy
719b8f7
"""
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)