cfb40 / scripts /diagnose_missing_punt.py
andytaylor-smg's picture
in a good spot, I think
5d257ae
#!/usr/bin/env python3
"""
Diagnose why the punt at 6107s (1:41:47) wasn't detected.
The freeze→25 detection fires at 6117.9s, but no play was started between
6065.4s (previous play end) and 6117.9s. Why?
"""
import json
import logging
import sys
from pathlib import Path
import cv2
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from detection.scorebug import DetectScoreBug
from readers import ReadPlayClock
from setup import DigitTemplateLibrary, PlayClockRegionConfig, PlayClockRegionExtractor
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
# Video and config paths
VIDEO_PATH = Path("/Users/andytaylor/Documents/Personal/cfb40/full_videos/OSU vs Oregon 01.01.25.mkv")
CONFIG_PATH = Path("/Users/andytaylor/Documents/Personal/cfb40/output/OSU_vs_Oregon_01_01_25_config.json")
TEMPLATE_DIR = Path("/Users/andytaylor/Documents/Personal/cfb40/output/debug/digit_templates")
# Time window to analyze: 6060s to 6125s
START_TIME = 6060
END_TIME = 6125
def main():
# Load config
with open(CONFIG_PATH, "r") as f:
config = json.load(f)
# Set up fixed coordinates
scorebug_bbox = (
config["scorebug_x"],
config["scorebug_y"],
config["scorebug_width"],
config["scorebug_height"],
)
playclock_coords = (
config["scorebug_x"] + config["playclock_x_offset"],
config["scorebug_y"] + config["playclock_y_offset"],
config["playclock_width"],
config["playclock_height"],
)
template_path = config["template_path"]
print(f"Video: {VIDEO_PATH}")
print(f"Scorebug bbox: {scorebug_bbox}")
print(f"Playclock coords: {playclock_coords}")
print()
# Initialize components
scorebug_detector = DetectScoreBug(template_path=template_path, fixed_region=scorebug_bbox, use_split_detection=True)
# Load digit templates
template_library = DigitTemplateLibrary()
if not template_library.load(str(TEMPLATE_DIR)):
print(f"ERROR: Could not load digit templates from {TEMPLATE_DIR}")
return
clock_reader = ReadPlayClock(template_library, config["playclock_width"], config["playclock_height"])
# Open video
cap = cv2.VideoCapture(str(VIDEO_PATH))
fps = cap.get(cv2.CAP_PROP_FPS)
frame_interval = 0.5 # Same as main pipeline
print(f"FPS: {fps:.2f}, Frame interval: {frame_interval}s")
print()
print(f"{'Time':>10} | {'Clock':>6} | {'SB Det':>7} | {'SB Match':>8} | {'Notes'}")
print("-" * 60)
# Track state
last_clock = None
freeze_start = None
freeze_value = None
current_time = START_TIME
while current_time <= END_TIME:
frame_num = int(current_time * fps)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
ret, frame = cap.read()
if not ret or frame is None:
print(f"{current_time:>10.1f} | {'???':>6} | {'NO FRAME':>7} |")
current_time += frame_interval
continue
# Detect scorebug
scorebug = scorebug_detector.detect(frame)
# Read clock
clock_value = None
sb_det = "No"
sb_match = "N/A"
notes = []
if scorebug.detected:
sb_det = "Yes"
sb_match = "Yes" if scorebug.template_matched else "No"
clock_result = clock_reader.read_from_fixed_location(frame, playclock_coords, padding=4)
if clock_result and clock_result.detected and clock_result.value is not None:
clock_value = clock_result.value
# Track freeze
if last_clock == clock_value:
if freeze_start is None:
freeze_start = current_time - frame_interval
freeze_value = clock_value
freeze_duration = current_time - freeze_start
if freeze_duration > 0.5:
notes.append(f"frozen {freeze_duration:.1f}s")
else:
# Clock changed
if freeze_start is not None and clock_value == 25 and freeze_value is not None and freeze_value <= 24:
freeze_duration = current_time - freeze_start
notes.append(f"*** FREEZE→25 from {freeze_value} (frozen {freeze_duration:.1f}s) ***")
freeze_start = None
freeze_value = None
# Check for play start indicators
if clock_value == 40:
notes.append("*** CLOCK AT 40 - should trigger play start ***")
if clock_value == 25 and last_clock is not None and last_clock != 25:
notes.append(f"jump to 25 from {last_clock}")
last_clock = clock_value
else:
sb_det = "No"
sb_match = "N/A"
notes.append("scorebug not detected")
# Reset tracking when scorebug lost
freeze_start = None
freeze_value = None
time_str = f"{current_time:.1f}s"
clock_str = str(clock_value) if clock_value is not None else "???"
note_str = " | ".join(notes) if notes else ""
print(f"{time_str:>10} | {clock_str:>6} | {sb_det:>7} | {sb_match:>8} | {note_str}")
current_time += frame_interval
cap.release()
print()
print("Analysis complete.")
if __name__ == "__main__":
main()