File size: 5,698 Bytes
c6abe34 | 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 | """
Pydantic models for analysis and detection schemas.
"""
from datetime import datetime
from typing import Optional, List, Dict, Any
from uuid import UUID
from pydantic import BaseModel, Field
class Detection(BaseModel):
"""Single object detection in a frame."""
video_id: UUID
frame: int = Field(..., ge=0)
object_type: str = Field(..., description="'player' or 'ball'")
track_id: int
bbox: List[float] = Field(..., min_length=4, max_length=4, description="[x1, y1, x2, y2]")
confidence: float = Field(..., ge=0, le=1)
keypoints: Optional[List[List[float]]] = Field(None, description="Pose keypoints for players")
team_id: Optional[int] = None
has_ball: bool = False
tactical_x: Optional[float] = None
tactical_y: Optional[float] = None
class DetectionBatch(BaseModel):
"""Batch of detections for efficient storage."""
video_id: UUID
frame_start: int
frame_end: int
detections: List[Detection]
class AnalysisRequest(BaseModel):
"""Request schema for triggering analysis."""
video_id: UUID
options: Optional[Dict[str, Any]] = Field(default_factory=dict)
# Basic match setup
our_team_jersey: Optional[str] = None
opponent_jersey: Optional[str] = None
our_team_id: Optional[int] = None
# Detection parameters (HIGH PRIORITY)
player_confidence: Optional[float] = Field(default=0.5, ge=0.1, le=0.9, description="Player detection confidence threshold")
ball_confidence: Optional[float] = Field(default=0.15, ge=0.05, le=0.9, description="Ball detection confidence threshold")
detection_batch_size: Optional[int] = Field(default=10, ge=5, le=20, description="Batch size for detection processing")
image_size: Optional[int] = Field(default=1080, description="Input image size for detection model")
max_players_on_court: Optional[int] = Field(default=5, ge=5, le=12, description="Max players per team")
# Analysis options (MEDIUM PRIORITY)
use_cached_detections: Optional[bool] = Field(default=False, description="Use cached detections if available")
clear_cache_after: Optional[bool] = Field(default=True, description="Clear cache after analysis")
save_annotated_video: Optional[bool] = Field(default=True, description="Save output video with annotations")
# Display options (LOW PRIORITY)
render_speed_text: Optional[bool] = Field(default=True, description="Show speed overlay on video")
render_distance_text: Optional[bool] = Field(default=True, description="Show distance overlay on video")
render_tactical_view: Optional[bool] = Field(default=True, description="Show tactical view")
render_court_keypoints: Optional[bool] = Field(default=True, description="Show court keypoint detections")
class AnalysisEvent(BaseModel):
"""Detected event during analysis (pass, shot, etc.)."""
event_type: str
frame: int
timestamp_seconds: float
player_id: Optional[int] = None
team_id: Optional[int] = None
details: Dict[str, Any] = Field(default_factory=dict)
class AnalysisResult(BaseModel):
"""Complete analysis results for a video."""
id: UUID
video_id: UUID
total_frames: int
duration_seconds: float
fps: Optional[float] = 30.0
players_detected: int
# Team analysis specific
team_1_possession_percent: Optional[float] = None
team_2_possession_percent: Optional[float] = None
total_passes: Optional[int] = None
team_1_passes: Optional[int] = 0
team_2_passes: Optional[int] = 0
total_interceptions: Optional[int] = None
team_1_interceptions: Optional[int] = 0
team_2_interceptions: Optional[int] = 0
# Shot analysis
shot_attempts: Optional[int] = 0
shots_made: Optional[int] = 0
shots_missed: Optional[int] = 0
overall_shooting_percentage: Optional[float] = None
shooting_percentage: Optional[float] = None
# Defensive analysis
defensive_actions: Optional[int] = 0
# Movement metrics
total_distance_meters: Optional[float] = None
avg_speed_kmh: Optional[float] = None
max_speed_kmh: Optional[float] = None
# Team shooting
team_1_shot_attempts: Optional[int] = 0
team_1_shots_made: Optional[int] = 0
team_2_shot_attempts: Optional[int] = 0
team_2_shots_made: Optional[int] = 0
# Events
events: List[AnalysisEvent] = Field(default_factory=list)
# Advanced analytics metadata (hydrated from events)
advanced_analytics: Optional[Dict[str, Any]] = None
# Processing info
processing_time_seconds: float
created_at: datetime
class Config:
from_attributes = True
class PersonalAnalysisResult(BaseModel):
"""Personal analysis results with skill metrics."""
id: UUID
video_id: UUID
player_id: Optional[UUID] = None
total_frames: int
duration_seconds: float
# Skill metrics
shot_attempts: int = 0
shot_form_consistency: Optional[float] = Field(None, ge=0, le=100, description="Form consistency percentage")
dribble_count: int = 0
dribble_frequency_per_minute: Optional[float] = None
# Movement metrics
total_distance_meters: Optional[float] = None
avg_speed_kmh: Optional[float] = None
max_speed_kmh: Optional[float] = None
acceleration_events: int = 0
# Pose analysis
avg_knee_bend_angle: Optional[float] = None
avg_elbow_angle_shooting: Optional[float] = None
# Training load
training_load_score: Optional[float] = Field(None, ge=0, le=100)
# Processing info
processing_time_seconds: float
created_at: datetime
class Config:
from_attributes = True
|