cfb40 / docs /flag_plan.md
andytaylor-smg's picture
flag detection was pretty easy
fbeda03

A newer version of the Gradio SDK is available: 6.8.0

Upgrade

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

  1. FLAG Region Selection: User selects the "FLAG" region during initialization (same location as "1st and 10" marker on scorebug)

  2. Yellow Detection: Search for strong yellow presence in the FLAG region throughout the video to detect when flags are thrown

  3. 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
  4. 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
  5. Pre-Play Penalties: Capture penalties that occur before a play (false start, delay of game) even though no "real football play" occurred

  6. 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 to SessionConfig
  • src/ui/__init__.py - Export new select_flag_region function
  • src/ui/api.py - Add select_flag_region function (similar to select_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 - Add FlagInfo model and flag-related fields to state
  • src/tracking/play_tracker.py - Add FLAG state tracking
  • src/tracking/normal_play_tracker.py - Modify end-of-play logic for FLAG override
  • src/pipeline/play_extractor.py - Integrate FlagReader into frame processing

Key Logic Changes:

  1. 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
  1. 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)
  1. 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
    """
  1. 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 to TrackPlayStateConfig

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:

  1. Standard play with flag β†’ extended capture includes penalty announcement
  2. Pre-play penalty (false start) β†’ captured as play
  3. Multiple flags in quick succession β†’ handles correctly
  4. Flag during replay β†’ not double-counted
  5. 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

  1. Region Selection: User can select FLAG region in <30 seconds during setup βœ…
  2. Detection Accuracy: β‰₯90% recall on verified ground truth FLAGS βœ… (100%)
  3. False Positive Rate: <10% of detected FLAGS are false positives βœ… (0%)
  4. Extended Capture: FLAG plays include penalty announcement in 90%+ of cases
  5. Pre-Play Penalties: False starts and delay of game penalties captured
  6. No Regressions: Existing play detection performance unchanged for non-FLAG plays
  7. 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).