#!/usr/bin/env python3 """ Test that Play #136 at 1:52:06 (6726.5s) is detected with template matching. This play was noted in missed_plays_analysis.md as being missed in some scenarios. The v3 baseline shows it was detected at 6726.5s. This test verifies the template matching pipeline can detect this play. Usage: cd /Users/andytaylor/Documents/Personal/cfb40 source .venv/bin/activate python tests/test_digit_templates/test_missed_play.py """ import logging import sys from pathlib import Path from pipeline.play_detector import PlayDetector, DetectionConfig logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) # Test configuration VIDEO_PATH = "full_videos/OSU vs Tenn 12.21.24.mkv" TEMPLATE_PATH = "output/OSU_vs_Tenn_12_21_24_template.png" PLAYCLOCK_CONFIG_PATH = "output/OSU_vs_Tenn_12_21_24_playclock_config.json" # Segment around the missed play (6680s - 6800s = ~2 minutes) # Play #136 should be at ~6726.5s START_TIME = 6680 # 1:51:20 END_TIME = 6800 # 1:53:20 # Expected play time from v3 baseline EXPECTED_PLAY_TIME = 6726.5 TOLERANCE = 5.0 # Allow 5 second tolerance def test_missed_play_detection(): """Test that Play #136 at 6726.5s is detected with template matching.""" logger.info("=" * 60) logger.info("TEST: Missed Play Detection (1:52:06 / 6726.5s)") logger.info("=" * 60) # Check files exist for path, name in [(VIDEO_PATH, "Video"), (TEMPLATE_PATH, "Template"), (PLAYCLOCK_CONFIG_PATH, "Config")]: if not Path(path).exists(): logger.error("%s not found: %s", name, path) return False # Create detection config with template matching enabled config = DetectionConfig( video_path=VIDEO_PATH, template_path=TEMPLATE_PATH, clock_region_config_path=PLAYCLOCK_CONFIG_PATH, start_time=START_TIME, end_time=END_TIME, frame_interval=0.5, use_template_matching=True, template_collection_frames=200, # Use first 200 frames for template building ) logger.info("\nConfiguration:") logger.info(" Segment: %.1fs - %.1fs (%.1f minutes)", START_TIME, END_TIME, (END_TIME - START_TIME) / 60) logger.info(" Template matching: ENABLED") logger.info(" Expected play at: %.1fs (±%.1fs)", EXPECTED_PLAY_TIME, TOLERANCE) # Run detection logger.info("\n[Step 1] Running detection pipeline...") detector = PlayDetector(config) # Set fixed region from known scorebug location detector.scorebug_detector.set_fixed_region((128, 975, 1669, 46)) result = detector.detect() # Analyze results logger.info("\n[Step 2] Analyzing results...") logger.info(" Total frames processed: %d", result.total_frames_processed) logger.info(" Frames with scorebug: %d", result.frames_with_scorebug) logger.info(" Frames with clock: %d", result.frames_with_clock) logger.info(" Plays detected: %d", len(result.plays)) # Check timing breakdown logger.info("\nTiming breakdown:") for key, value in result.timing.items(): if value > 0: logger.info(" %s: %.2fs", key, value) total_time = sum(result.timing.values()) logger.info(" TOTAL: %.2fs", total_time) # Check for the expected play found_play = False closest_play = None closest_distance = float("inf") for play in result.plays: play_time = play.get("start_time", 0) distance = abs(play_time - EXPECTED_PLAY_TIME) if distance < closest_distance: closest_distance = distance closest_play = play if distance <= TOLERANCE: found_play = True logger.info("\n✓ FOUND expected play at %.1fs (distance: %.1fs)", play_time, distance) logger.info(" Play details: %s", play) if not found_play: logger.info("\n✗ Expected play at %.1fs NOT FOUND within tolerance", EXPECTED_PLAY_TIME) if closest_play: logger.info(" Closest play: %.1fs (distance: %.1fs)", closest_play.get("start_time", 0), closest_distance) # List all detected plays logger.info("\nAll detected plays:") for play in result.plays: play_time = play.get("start_time", 0) duration = play.get("duration", 0) play_type = play.get("play_type", "unknown") marker = " <-- TARGET" if abs(play_time - EXPECTED_PLAY_TIME) <= TOLERANCE else "" logger.info(" Play #%d: %.1fs - %.1fs (%.1fs) [%s]%s", play.get("play_number", 0), play_time, play.get("end_time", 0), duration, play_type, marker) # Summary logger.info("\n" + "=" * 60) logger.info("TEST SUMMARY") logger.info("=" * 60) logger.info("Plays detected: %d", len(result.plays)) logger.info("Expected play found: %s", "YES" if found_play else "NO") logger.info("Processing time: %.2fs", total_time) if found_play: logger.info("\nTEST: PASSED") else: logger.info("\nTEST: FAILED") return found_play if __name__ == "__main__": success = test_missed_play_detection() sys.exit(0 if success else 1)