Spaces:
Sleeping
Sleeping
| """ | |
| Pydantic models for per-frame detection. | |
| These models represent the results of detecting various elements in video frames: | |
| scorebug presence and timeout indicators. | |
| """ | |
| from typing import Any, Optional, Tuple, List | |
| from pydantic import BaseModel, Field | |
| class ScorebugDetection(BaseModel): | |
| """Results from scorebug detection.""" | |
| detected: bool = Field(..., description="Whether scorebug was detected (for play tracking)") | |
| confidence: float = Field(..., description="Confidence score (0.0 to 1.0)") | |
| bbox: Optional[Tuple[int, int, int, int]] = Field(None, description="Bounding box (x, y, width, height)") | |
| method: str = Field("unknown", description="Detection method used") | |
| left_confidence: Optional[float] = Field(None, description="Left half confidence (when split detection enabled)") | |
| right_confidence: Optional[float] = Field(None, description="Right half confidence (when split detection enabled)") | |
| # In fixed coords mode, detected=True (assumed present for play tracking), | |
| # but template_matched indicates actual visibility (for special play end detection) | |
| template_matched: Optional[bool] = Field(None, description="Actual template match result (None if not using fixed coords mode)") | |
| class TimeoutRegionConfig(BaseModel): | |
| """Configuration for a team's timeout indicator region.""" | |
| team_name: str = Field(..., description="'home' or 'away'") | |
| bbox: Tuple[int, int, int, int] = Field(..., description="x, y, width, height for the 3-oval group") | |
| def to_dict(self) -> dict[str, object]: | |
| """Convert to dictionary for JSON serialization.""" | |
| return { | |
| "team_name": self.team_name, | |
| "bbox": {"x": self.bbox[0], "y": self.bbox[1], "width": self.bbox[2], "height": self.bbox[3]}, | |
| } | |
| def from_dict(cls, data: dict[str, Any]) -> "TimeoutRegionConfig": | |
| """Create from dictionary.""" | |
| bbox = (data["bbox"]["x"], data["bbox"]["y"], data["bbox"]["width"], data["bbox"]["height"]) | |
| return cls(team_name=data["team_name"], bbox=bbox) | |
| class OvalLocation(BaseModel): | |
| """Precise location of a single timeout oval within a team's timeout region.""" | |
| x: int = Field(..., description="X position within timeout region (relative to region top-left)") | |
| y: int = Field(..., description="Y position within timeout region (relative to region top-left)") | |
| width: int = Field(..., description="Width of the oval bounding box") | |
| height: int = Field(..., description="Height of the oval bounding box") | |
| baseline_brightness: float = Field(0.0, description="Mean brightness when oval is 'available' (calibrated)") | |
| def to_dict(self) -> dict[str, object]: | |
| """Convert to dictionary for JSON serialization.""" | |
| return { | |
| "x": self.x, | |
| "y": self.y, | |
| "width": self.width, | |
| "height": self.height, | |
| "baseline_brightness": self.baseline_brightness, | |
| } | |
| def from_dict(cls, data: dict[str, Any]) -> "OvalLocation": | |
| """Create from dictionary.""" | |
| return cls( | |
| x=data["x"], | |
| y=data["y"], | |
| width=data["width"], | |
| height=data["height"], | |
| baseline_brightness=data.get("baseline_brightness", 0.0), | |
| ) | |
| class CalibratedTimeoutRegion(BaseModel): | |
| """Timeout region with discovered oval sub-coordinates from calibration.""" | |
| team_name: str = Field(..., description="'home' or 'away'") | |
| bbox: Tuple[int, int, int, int] = Field(..., description="Overall region: x, y, width, height") | |
| ovals: List[OvalLocation] = Field(default_factory=list, description="3 oval sub-coordinates within the region") | |
| calibration_timestamp: float = Field(0.0, description="Video timestamp when calibration was performed") | |
| def to_dict(self) -> dict[str, object]: | |
| """Convert to dictionary for JSON serialization.""" | |
| return { | |
| "team_name": self.team_name, | |
| "bbox": {"x": self.bbox[0], "y": self.bbox[1], "width": self.bbox[2], "height": self.bbox[3]}, | |
| "ovals": [oval.to_dict() for oval in self.ovals], | |
| "calibration_timestamp": self.calibration_timestamp, | |
| } | |
| def from_dict(cls, data: dict[str, Any]) -> "CalibratedTimeoutRegion": | |
| """Create from dictionary.""" | |
| bbox = (data["bbox"]["x"], data["bbox"]["y"], data["bbox"]["width"], data["bbox"]["height"]) | |
| ovals = [OvalLocation.from_dict(o) for o in data.get("ovals", [])] | |
| return cls( | |
| team_name=data["team_name"], | |
| bbox=bbox, | |
| ovals=ovals, | |
| calibration_timestamp=data.get("calibration_timestamp", 0.0), | |
| ) | |
| class TimeoutReading(BaseModel): | |
| """Results from timeout indicator reading.""" | |
| home_timeouts: int = Field(..., description="0-3 timeouts remaining") | |
| away_timeouts: int = Field(..., description="0-3 timeouts remaining") | |
| confidence: float = Field(..., description="Overall confidence in reading") | |
| home_oval_states: Optional[List[bool]] = Field(None, description="True = white (available), False = dark (used)") | |
| away_oval_states: Optional[List[bool]] = Field(None, description="True = white (available), False = dark (used)") | |