Spaces:
Sleeping
Sleeping
| #!/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) | |