Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Compare playclock regions between working and non-working timestamps. | |
| This will help us understand why clock readings work at some timestamps | |
| but not others. | |
| """ | |
| import json | |
| import logging | |
| import sys | |
| from pathlib import Path | |
| import cv2 | |
| import numpy as np | |
| # Add src to path | |
| sys.path.insert(0, str(Path(__file__).parent.parent / "src")) | |
| from readers import ReadPlayClock | |
| from setup import DigitTemplateLibrary | |
| from video import iter_frames_ffmpeg | |
| from utils.regions import preprocess_playclock_region | |
| logging.basicConfig(level=logging.INFO, format="%(message)s") | |
| logger = logging.getLogger(__name__) | |
| # Video path | |
| VIDEO_PATH = Path("/Users/andytaylor/Documents/Personal/cfb40/full_videos/OSU vs Oregon 01.01.25.mkv") | |
| # Working timestamps (from detected plays in the output JSON) | |
| # - Play #94 at 4104.5s works | |
| # - Play #148 at 6645.4s works | |
| WORKING_TIMESTAMPS = [4104.5, 6645.5] | |
| # Non-working timestamps (from ground truth) | |
| FAILING_TIMESTAMPS = [4136, 6684] | |
| def load_session_config(config_path: str) -> dict: | |
| """Load session config.""" | |
| with open(config_path, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| def main(): | |
| if not VIDEO_PATH.exists(): | |
| print(f"ERROR: Video not found at {VIDEO_PATH}") | |
| return | |
| # Load config | |
| video_basename = VIDEO_PATH.stem.replace(" ", "_").replace(".", "_") | |
| config_path = Path("output") / f"{video_basename}_config.json" | |
| config = load_session_config(str(config_path)) | |
| # Calculate absolute play clock coordinates | |
| playclock_coords = ( | |
| config["scorebug_x"] + config["playclock_x_offset"], | |
| config["scorebug_y"] + config["playclock_y_offset"], | |
| config["playclock_width"], | |
| config["playclock_height"], | |
| ) | |
| print(f"Playclock coords: {playclock_coords}") | |
| # Load digit templates | |
| template_dir = Path("output/debug/digit_templates") | |
| library = DigitTemplateLibrary() | |
| if not library.load(str(template_dir)): | |
| print(f"ERROR: Could not load digit templates from {template_dir}") | |
| return | |
| # Create clock reader | |
| reader = ReadPlayClock(library, region_width=config["playclock_width"], region_height=config["playclock_height"]) | |
| # Create output directory | |
| output_dir = Path("output/debug/clock_comparison") | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| print("\n" + "=" * 70) | |
| print("WORKING TIMESTAMPS (plays were detected here)") | |
| print("=" * 70) | |
| for ts in WORKING_TIMESTAMPS: | |
| frames = list(iter_frames_ffmpeg(str(VIDEO_PATH), start_time=ts, end_time=ts + 5, frame_interval=0.5)) | |
| print(f"\nTimestamp {ts}s:") | |
| for frame_time, frame in frames[:5]: | |
| pc_x, pc_y, pc_w, pc_h = playclock_coords | |
| playclock_region = frame[pc_y : pc_y + pc_h, pc_x : pc_x + pc_w].copy() | |
| preprocessed = preprocess_playclock_region(playclock_region, reader.scale_factor) | |
| result = reader.read_from_fixed_location(frame, playclock_coords, padding=10) | |
| status = f"value={result.value}, conf={result.confidence:.2f}" if result.detected else f"NOT DETECTED (conf={result.confidence:.2f})" | |
| print(f" t={frame_time:.1f}s: {status}") | |
| # Save comparison | |
| pc_scaled = cv2.resize(playclock_region, (pc_w * 10, pc_h * 10), interpolation=cv2.INTER_NEAREST) | |
| prep_bgr = cv2.cvtColor(preprocessed, cv2.COLOR_GRAY2BGR) | |
| prep_scaled = cv2.resize(prep_bgr, (pc_w * 10, pc_h * 10), interpolation=cv2.INTER_NEAREST) | |
| composite = np.vstack([pc_scaled, prep_scaled]) | |
| cv2.imwrite(str(output_dir / f"working_{int(ts)}_{int(frame_time)}.png"), composite) | |
| print("\n" + "=" * 70) | |
| print("FAILING TIMESTAMPS (plays were NOT detected here)") | |
| print("=" * 70) | |
| for ts in FAILING_TIMESTAMPS: | |
| frames = list(iter_frames_ffmpeg(str(VIDEO_PATH), start_time=ts, end_time=ts + 5, frame_interval=0.5)) | |
| print(f"\nTimestamp {ts}s:") | |
| for frame_time, frame in frames[:5]: | |
| pc_x, pc_y, pc_w, pc_h = playclock_coords | |
| playclock_region = frame[pc_y : pc_y + pc_h, pc_x : pc_x + pc_w].copy() | |
| preprocessed = preprocess_playclock_region(playclock_region, reader.scale_factor) | |
| result = reader.read_from_fixed_location(frame, playclock_coords, padding=10) | |
| status = f"value={result.value}, conf={result.confidence:.2f}" if result.detected else f"NOT DETECTED (conf={result.confidence:.2f})" | |
| print(f" t={frame_time:.1f}s: {status}") | |
| # Save comparison | |
| pc_scaled = cv2.resize(playclock_region, (pc_w * 10, pc_h * 10), interpolation=cv2.INTER_NEAREST) | |
| prep_bgr = cv2.cvtColor(preprocessed, cv2.COLOR_GRAY2BGR) | |
| prep_scaled = cv2.resize(prep_bgr, (pc_w * 10, pc_h * 10), interpolation=cv2.INTER_NEAREST) | |
| composite = np.vstack([pc_scaled, prep_scaled]) | |
| cv2.imwrite(str(output_dir / f"failing_{int(ts)}_{int(frame_time)}.png"), composite) | |
| print(f"\nSaved comparison images to: {output_dir}") | |
| if __name__ == "__main__": | |
| main() | |