Spaces:
Sleeping
A newer version of the Gradio SDK is available:
6.8.0
Flag Detection Implementation Plan
Overview
This document outlines the implementation plan for detecting penalty flags ("FLAG" indicator) on the scorebug and using this information to enhance play capture. When a flag is thrown, the system should persist video capture beyond normal play boundaries to include the referee's penalty announcement.
Requirements
Core Requirements
FLAG Region Selection: User selects the "FLAG" region during initialization (same location as "1st and 10" marker on scorebug)
Yellow Detection: Search for strong yellow presence in the FLAG region throughout the video to detect when flags are thrown
Decision Override: FLAG detection should override any other play-capturing decisions:
- If a flag is detected but it looks like "weird clock behavior" (normally rejected), capture it anyway
- If a flag is detected during enforced quiet time, capture it anyway
- All FLAG events should be captured, no matter what
Extended Capture Duration: When a flag is thrown, persist video capture until EITHER:
- The yellow "FLAG" indicator disappears (scorebug is present but FLAG is gone)
- OR a configurable timeout (default: 15 seconds) after the play normally ends
- Note: If FLAG appears β replay β FLAG still visible when scorebug returns, continue capturing
Pre-Play Penalties: Capture penalties that occur before a play (false start, delay of game) even though no "real football play" occurred
Single-Pass Detection: FLAG detection must be done in the same pass as normal play detection (no second video pass)
Expected FLAG Scenarios
| Scenario | Expected Behavior |
|---|---|
| Flag during play | Extend capture until FLAG clears or timeout |
| Flag before play (false start, delay of game) | Capture as play even without snap |
| Flag on replay | Ignore FLAG during replay (no scorebug) |
| Multiple flags on same play | Capture extends to last FLAG clearing |
| Flag during quiet time | Override quiet time and capture |
| Flag with weird clock behavior | Override rejection and capture |
Implementation Steps
Step 1: Test Script for FLAG Region Selection β COMPLETE
Goal: Create a short standalone script to interactively select the FLAG region and save it.
File: scripts/test_flag_region_selection.py
Tasks:
- Load sample frames from the test video
- Display frame with existing scorebug region highlighted for reference
- Prompt user to click/drag to select the FLAG region (typically where "1st & 10" appears)
- Save the selected region to
data/config/flag_region.json - Display a cropped preview of the selected region for verification
Output: data/config/flag_region.json
{
"flag_region": {
"x_offset": 701,
"y_offset": -27,
"width": 260,
"height": 35
}
}
Step 2: Integrate FLAG Region into Main Pipeline
Goal: Add FLAG region selection to the main.py interactive setup flow.
File Changes:
src/config/models.py- Add FLAG region fields toSessionConfigsrc/ui/__init__.py- Export newselect_flag_regionfunctionsrc/ui/api.py- Addselect_flag_regionfunction (similar toselect_playclock_region)main.py- Add FLAG region selection after timeout region selection (respect--use-saved-regions)
SessionConfig Additions:
# FLAG region (relative to scorebug, like play clock)
flag_x_offset: int = 0
flag_y_offset: int = 0
flag_width: int = 0
flag_height: int = 0
Step 3: FLAG Detection Ground Truth Script β COMPLETE
Goal: Create a script to scan the video and identify potential FLAG timestamps for manual verification.
File: scripts/find_flag_ground_truth.py
Tasks:
- Load FLAG region from config
- Scan video at 2 FPS
- For each frame, extract FLAG region and compute "yellow-ness" score
- Apply section-level filters (duration, peak yellow, avg yellow, mean hue)
- Merge sections into FLAG events with gap tolerance for replays
- Output candidates to
output/cache/flag_candidates.json
Results (from docs/flag_ground_truth.md):
- Precision: 100% (8/8 true positives, 0 false positives)
- Recall: 100%
- Key filters: min 3s duration, peak>=70%, avg>=60%, hue>=22 (rejects orange)
Step 4: Create FlagReader Module
Goal: Implement the core FLAG reading logic.
File: src/readers/flags.py (NOT detection/flags.py - FlagReader goes in readers)
Class: FlagReader
Key Methods:
class FlagReader:
"""Reads FLAG indicator from scorebug using yellow color detection."""
# Detection parameters (from ground truth analysis)
YELLOW_HUE_MIN = 15
YELLOW_HUE_MAX = 35
YELLOW_SAT_MIN = 100
YELLOW_VAL_MIN = 100
YELLOW_RATIO_THRESHOLD = 0.25
MIN_MEAN_HUE = 22 # Distinguishes yellow from orange
def __init__(self, flag_region_offset: Tuple[int, int, int, int]):
"""Initialize with configured FLAG region offset (relative to scorebug)."""
def read(self, frame: np.ndarray, scorebug_bbox: Tuple[int, int, int, int]) -> FlagReading:
"""
Read FLAG status from a frame.
Args:
frame: Full video frame (BGR)
scorebug_bbox: Current scorebug bounding box (x, y, w, h)
Returns:
FlagReading with detected status, yellow_ratio, and mean_hue
"""
Reading Model:
class FlagReading(BaseModel):
"""Result of reading FLAG region."""
detected: bool
yellow_ratio: float
mean_hue: float
is_valid_yellow: bool # True if mean_hue >= MIN_MEAN_HUE (not orange)
Step 5: Integrate FLAG Detection into Play Tracking
Goal: Modify the play tracker to use FLAG detection for extended capture.
File Changes:
src/tracking/models.py- AddFlagInfomodel and flag-related fields to statesrc/tracking/play_tracker.py- Add FLAG state trackingsrc/tracking/normal_play_tracker.py- Modify end-of-play logic for FLAG overridesrc/pipeline/play_extractor.py- Integrate FlagReader into frame processing
Key Logic Changes:
- FlagInfo Model:
class FlagInfo(BaseModel):
"""Flag information for a frame."""
detected: bool = False
yellow_ratio: float = 0.0
mean_hue: float = 0.0
is_valid_yellow: bool = False
- State Tracking Additions:
# In NormalTrackerState
flag_detected_at: Optional[float] = None # When FLAG first appeared
flag_last_seen_at: Optional[float] = None # Last frame with FLAG visible
flag_cleared_at: Optional[float] = None # When FLAG disappeared (scorebug present, no FLAG)
- Play End Override Logic:
def _should_extend_for_flag(self, timestamp: float, scorebug_visible: bool, flag_detected: bool) -> bool:
"""
Determine if play capture should extend due to FLAG.
Returns True if:
- FLAG was seen during this play AND
- Either FLAG is still visible OR we're within the flag timeout window
"""
- Override quiet time and weird clock behavior:
- If FLAG detected, ignore quiet_time restrictions
- If FLAG detected, capture even if clock behavior looks weird
Step 6: Add Configuration Options
Goal: Make FLAG-related parameters configurable.
File Changes:
src/tracking/models.py- Add toTrackPlayStateConfig
Configuration Additions:
class TrackPlayStateConfig(BaseModel):
# ... existing fields ...
# FLAG detection settings
flag_extension_timeout: float = Field(15.0, description="Max seconds to extend capture after FLAG detected")
flag_yellow_threshold: float = Field(0.25, description="Yellow pixel ratio threshold for FLAG detection")
flag_min_mean_hue: float = Field(22.0, description="Min mean hue to distinguish yellow from orange")
capture_pre_play_penalties: bool = Field(True, description="Capture penalties without plays (false start, etc.)")
Step 7: Test FLAG Detection
Goal: Validate FLAG detection against ground truth.
File: scripts/test_flag_detection.py
Note: Ground truth already validated in Step 3 with 100% precision/recall.
Step 8: Integration Testing
Goal: Test full pipeline with FLAG detection enabled.
Test Cases:
- Standard play with flag β extended capture includes penalty announcement
- Pre-play penalty (false start) β captured as play
- Multiple flags in quick succession β handles correctly
- Flag during replay β not double-counted
- Plays without flags β unchanged behavior
File Structure After Implementation
src/
βββ config/
β βββ models.py # + flag region fields in SessionConfig
βββ readers/
β βββ __init__.py # + export FlagReader
β βββ flags.py # NEW: FlagReader, FlagReading
β βββ models.py # + FlagReading model
βββ tracking/
β βββ models.py # + FlagInfo, flag state fields
β βββ play_tracker.py # + FLAG coordination
β βββ normal_play_tracker.py # + FLAG override logic
βββ ui/
β βββ __init__.py # + export select_flag_region
β βββ api.py # + select_flag_region function
βββ pipeline/
βββ play_extractor.py # + FlagReader integration
data/config/
βββ flag_region.json # Default FLAG region config
scripts/
βββ test_flag_region_selection.py # Step 1 β
βββ find_flag_ground_truth.py # Step 3 β
Implementation Order
| Step | Description | Dependencies | Status |
|---|---|---|---|
| 1 | Test FLAG region selection script | None | β Complete |
| 2 | Integrate into main.py | Step 1 | In Progress |
| 3 | Ground truth scanning script | Step 1 | β Complete |
| 4 | FlagReader module | Step 1 | In Progress |
| 5 | Play tracker integration | Steps 3, 4 | In Progress |
| 6 | Configuration options | Step 5 | In Progress |
| 7 | Detection testing | Steps 3, 4 | Skipped (100% in Step 3) |
| 8 | Integration testing | Steps 5, 6 | Pending |
Success Criteria
- Region Selection: User can select FLAG region in <30 seconds during setup β
- Detection Accuracy: β₯90% recall on verified ground truth FLAGS β (100%)
- False Positive Rate: <10% of detected FLAGS are false positives β (0%)
- Extended Capture: FLAG plays include penalty announcement in 90%+ of cases
- Pre-Play Penalties: False starts and delay of game penalties captured
- No Regressions: Existing play detection performance unchanged for non-FLAG plays
- Single Pass: No second video pass required β (integrated into main processing)
Validated Detection Parameters
From ground truth analysis (docs/flag_ground_truth.md):
# Yellow detection (HSV color space)
YELLOW_HUE_MIN = 15
YELLOW_HUE_MAX = 35
YELLOW_SAT_MIN = 100
YELLOW_VAL_MIN = 100
# Detection threshold
YELLOW_RATIO_THRESHOLD = 0.25 # 25% of region must be yellow
# Orange discrimination
MIN_MEAN_HUE = 22 # Rejects orange (hue ~16-17), accepts yellow (hue ~28-29)
Orange vs Yellow Discrimination
| Metric | True FLAG Yellow | False Positive Orange |
|---|---|---|
| Mean Hue | 27-29 | 16-17 |
| Mean Value | 205-207 (bright) | 161-162 (darker) |
The MIN_MEAN_HUE filter is essential for distinguishing true FLAG yellow from team colors (e.g., Tennessee orange).