File size: 6,452 Bytes
5d257ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python3
"""
Debug script to trace special play state machine during extraction.

This script runs extraction on a small segment and logs detailed state
information from the special play tracker to understand why scorebug
disappearance isn't triggering early end.
"""

import argparse
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 detection.timeouts import CalibratedTimeoutDetector
from readers import PlayClockReading, ReadPlayClock
from setup import DigitTemplateLibrary, PlayClockRegionConfig, PlayClockRegionExtractor

# Set up verbose logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

# Also enable debug on the special play tracker
logging.getLogger("tracking.special_play_tracker").setLevel(logging.DEBUG)
logging.getLogger("tracking.play_tracker").setLevel(logging.DEBUG)


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():
    parser = argparse.ArgumentParser(description="Debug special play state machine")
    parser.add_argument("--video", required=True, help="Path to video file")
    parser.add_argument("--config", required=True, help="Path to session config JSON")
    parser.add_argument("--start", type=float, required=True, help="Start time in seconds")
    parser.add_argument("--end", type=float, required=True, help="End time in seconds")
    parser.add_argument("--template-dir", default="output/debug/digit_templates", help="Path to digit templates")

    args = parser.parse_args()

    # Load config
    config = load_session_config(args.config)

    # 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"]

    logger.info("Video: %s", args.video)
    logger.info("Segment: %.1f - %.1f", args.start, args.end)
    logger.info("Scorebug bbox: %s", scorebug_bbox)
    logger.info("Playclock coords: %s", playclock_coords)

    # 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(args.template_dir):
        logger.error("Could not load digit templates from %s", args.template_dir)
        return

    template_reader = ReadPlayClock(template_library, config["playclock_width"], config["playclock_height"])

    # Initialize play tracker with custom logging
    from tracking import TrackPlayState, TimeoutInfo

    play_tracker = TrackPlayState()

    # Open video
    cap = cv2.VideoCapture(args.video)
    fps = cap.get(cv2.CAP_PROP_FPS)
    frame_interval = 0.5

    logger.info("FPS: %.2f, Frame interval: %.2f", fps, frame_interval)

    # Process frames
    current_time = args.start
    frame_count = 0

    while current_time <= args.end:
        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:
            logger.warning("Could not read frame at %.1fs", current_time)
            current_time += frame_interval
            continue

        frame_count += 1

        # Detect scorebug
        scorebug = scorebug_detector.detect(frame)

        # Read clock
        clock_result = template_reader.read_from_fixed_location(frame, playclock_coords, padding=4)
        clock_reading = PlayClockReading(
            detected=clock_result.detected if clock_result else False,
            value=clock_result.value if clock_result and clock_result.detected else None,
            confidence=clock_result.confidence if clock_result else 0.0,
            raw_text="DEBUG",
        )

        # Log state BEFORE update
        # Access the internal PlayTracker for detailed state
        inner_tracker = play_tracker._tracker
        mode = inner_tracker.active_mode.value
        state = play_tracker.state.value if hasattr(play_tracker.state, "value") else str(play_tracker.state)

        # Check if we're in special mode
        if mode == "special":
            special_state = inner_tracker._special_tracker._state
            logger.info(
                "  t=%.1f | sb=%s (%.3f) | clk=%s | MODE=%s | phase=%s | last_sb_ts=%s",
                current_time,
                "Y" if scorebug.detected else "N",
                scorebug.confidence,
                clock_reading.value if clock_reading.detected else "---",
                mode,
                special_state.phase.value,
                special_state.last_scorebug_timestamp,
            )
        else:
            logger.info(
                "  t=%.1f | sb=%s (%.3f) | clk=%s | MODE=%s | state=%s",
                current_time,
                "Y" if scorebug.detected else "N",
                scorebug.confidence,
                clock_reading.value if clock_reading.detected else "---",
                mode,
                state,
            )

        # Update tracker
        play = play_tracker.update(current_time, scorebug, clock_reading, None, None)

        if play:
            logger.info(
                "  >>> PLAY CREATED: #%d, %.1f-%.1f, type=%s, end_method=%s",
                play.play_number,
                play.start_time,
                play.end_time,
                play.play_type,
                play.end_method,
            )

        current_time += frame_interval

    cap.release()

    # Summary
    plays = play_tracker.get_plays()
    logger.info("\n=== SUMMARY ===")
    logger.info("Frames processed: %d", frame_count)
    logger.info("Plays detected: %d", len(plays))
    for p in plays:
        logger.info("  #%d: %.1f-%.1f (%.1fs) type=%s end=%s", p.play_number, p.start_time, p.end_time, p.end_time - p.start_time, p.play_type, p.end_method)


if __name__ == "__main__":
    main()