def _get_tracking_dataset(self): home_team = Team(team_id="home", name="home", ground=Ground.HOME) away_team = Team(team_id="away", name="away", ground=Ground.AWAY) teams = [home_team, away_team] periods = [ Period( id=1, start_timestamp=0.0, end_timestamp=10.0, attacking_direction=AttackingDirection.HOME_AWAY, ), Period( id=2, start_timestamp=15.0, end_timestamp=25.0, attacking_direction=AttackingDirection.AWAY_HOME, ), ] metadata = Metadata( flags=~(DatasetFlag.BALL_OWNING_TEAM | DatasetFlag.BALL_STATE), pitch_dimensions=PitchDimensions(x_dim=Dimension(0, 100), y_dim=Dimension(-50, 50)), orientation=Orientation.HOME_TEAM, frame_rate=25, periods=periods, teams=teams, score=None, provider=None, ) tracking_data = TrackingDataset( metadata=metadata, records=[ Frame( frame_id=1, timestamp=0.1, ball_owning_team=None, ball_state=None, period=periods[0], players_coordinates={}, ball_coordinates=Point(x=100, y=-50), ), Frame( frame_id=2, timestamp=0.2, ball_owning_team=None, ball_state=None, period=periods[0], players_coordinates={ Player(team=home_team, player_id="home_1", jersey_no=1): Point(x=15, y=35) }, ball_coordinates=Point(x=0, y=50), ), ], ) return tracking_data
def _frame_from_row(row: dict, metadata: EPTSMetadata) -> Frame: timestamp = row["timestamp"] if metadata.periods and row["period_id"]: # might want to search for it instead period = metadata.periods[row["period_id"] - 1] else: period = None players_coordinates = {} for team in metadata.teams: for player in team.players: if f"player_{player.player_id}_x" in row: players_coordinates[player] = Point( x=row[f"player_{player.player_id}_x"], y=row[f"player_{player.player_id}_y"], ) return Frame( frame_id=row["frame_id"], timestamp=timestamp, ball_owning_team=None, ball_state=None, period=period, players_coordinates=players_coordinates, ball_coordinates=Point(x=row["ball_x"], y=row["ball_y"]), )
def _frame_from_row(row: dict, meta_data: EPTSMetaData) -> Frame: timestamp = row["timestamp"] if meta_data.periods and row["period_id"]: # might want to search for it instead period = meta_data.periods[row["period_id"] - 1] else: period = None home_team_player_positions = {} away_team_player_positions = {} for player in meta_data.players: if player.team == Team.HOME: if f"player_home_{player.jersey_no}_x" in row: home_team_player_positions[player.jersey_no] = Point( x=row[f"player_home_{player.jersey_no}_x"], y=row[f"player_home_{player.jersey_no}_y"], ) elif player.team == Team.AWAY: if f"player_away_{player.jersey_no}_x" in row: away_team_player_positions[player.jersey_no] = Point( x=row[f"player_away_{player.jersey_no}_x"], y=row[f"player_away_{player.jersey_no}_y"], ) return Frame( frame_id=row["frame_id"], timestamp=timestamp, ball_owning_team=None, ball_state=None, period=period, home_team_player_positions=home_team_player_positions, away_team_player_positions=away_team_player_positions, ball_position=Point(x=row["ball_x"], y=row["ball_y"]), )
def _frame_from_row(row: dict, meta_data: EPTSMetaData) -> Frame: timestamp = row['timestamp'] if meta_data.periods: # might want to search for it instead period = meta_data.periods[row['period_id'] - 1] else: period = None home_team_player_positions = {} away_team_player_positions = {} for player in meta_data.players: if player.team == Team.HOME: home_team_player_positions[player.jersey_no] = Point( x=row[f'player_home_{player.jersey_no}_x'], y=row[f'player_home_{player.jersey_no}_y']) elif player.team == Team.AWAY: home_team_player_positions[player.jersey_no] = Point( x=row[f'player_away_{player.jersey_no}_x'], y=row[f'player_away_{player.jersey_no}_y']) return Frame(frame_id=row['frame_id'], timestamp=timestamp, ball_owning_team=None, ball_state=None, period=period, home_team_player_positions=home_team_player_positions, away_team_player_positions=away_team_player_positions, ball_position=Point(x=row['ball_x'], y=row['ball_y']))
def transform_frame(self, frame: Frame) -> Frame: flip = self.__needs_flip( ball_owning_team=frame.ball_owning_team, attacking_direction=frame.period.attacking_direction) return Frame( # doesn't change timestamp=frame.timestamp, frame_id=frame.frame_id, ball_owning_team=frame.ball_owning_team, ball_state=frame.ball_state, period=frame.period, # changes ball_position=self.transform_point(frame.ball_position, flip), home_team_player_positions={ jersey_no: self.transform_point(point, flip) for jersey_no, point in frame.home_team_player_positions.items() }, away_team_player_positions={ jersey_no: self.transform_point(point, flip) for jersey_no, point in frame.away_team_player_positions.items() })
def __change_frame_dimensions(self, frame: Frame): return Frame( # doesn't change timestamp=frame.timestamp, frame_id=frame.frame_id, ball_owning_team=frame.ball_owning_team, ball_state=frame.ball_state, period=frame.period, # changes ball_coordinates=self.change_point_dimensions( frame.ball_coordinates ), players_data={ key: PlayerData( coordinates=self.change_point_dimensions( player_data.coordinates ), distance=player_data.distance, speed=player_data.speed, other_data=player_data.other_data, ) for key, player_data in frame.players_data.items() }, other_data=frame.other_data, )
def _get_tracking_dataset(self): periods = [ Period( id=1, start_timestamp=0.0, end_timestamp=10.0, attacking_direction=AttackingDirection.HOME_AWAY, ), Period( id=2, start_timestamp=15.0, end_timestamp=25.0, attacking_direction=AttackingDirection.AWAY_HOME, ), ] tracking_data = TrackingDataset( flags=~(DatasetFlag.BALL_OWNING_TEAM | DatasetFlag.BALL_STATE), pitch_dimensions=PitchDimensions(x_dim=Dimension(0, 100), y_dim=Dimension(-50, 50)), orientation=Orientation.HOME_TEAM, frame_rate=25, records=[ Frame( frame_id=1, timestamp=0.1, ball_owning_team=None, ball_state=None, period=periods[0], away_team_player_positions={}, home_team_player_positions={}, ball_position=Point(x=100, y=-50), ), Frame( frame_id=2, timestamp=0.2, ball_owning_team=None, ball_state=None, period=periods[0], away_team_player_positions={"1": Point(x=10, y=20)}, home_team_player_positions={"1": Point(x=15, y=35)}, ball_position=Point(x=0, y=50), ), ], periods=periods, ) return tracking_data
def _frame_from_row(row: dict, metadata: EPTSMetadata, transformer: DatasetTransformer) -> Frame: timestamp = row["timestamp"] if metadata.periods and row["period_id"]: # might want to search for it instead period = metadata.periods[row["period_id"] - 1] else: period = None other_sensors = [] for sensor in metadata.sensors: if sensor.sensor_id not in ["position", "distance", "speed"]: other_sensors.append(sensor) players_data = {} for team in metadata.teams: for player in team.players: other_data = {} for sensor in other_sensors: other_data.update({ sensor.sensor_id: row[f"player_{player.player_id}_{sensor.channels[0].channel_id}"] }) players_data[player] = PlayerData( coordinates=Point( x=row[f"player_{player.player_id}_x"], y=row[f"player_{player.player_id}_y"], ) if f"player_{player.player_id}_x" in row else None, speed=row[f"player_{player.player_id}_s"] if f"player_{player.player_id}_s" in row else None, distance=row[f"player_{player.player_id}_d"] if f"player_{player.player_id}_d" in row else None, other_data=other_data, ) frame = Frame( frame_id=row["frame_id"], timestamp=timestamp, ball_owning_team=None, ball_state=None, period=period, players_data=players_data, other_data={}, ball_coordinates=Point3D(x=row["ball_x"], y=row["ball_y"], z=row.get("ball_z")), ) if transformer: frame = transformer.transform_frame(frame) return frame
def _frame_from_line(cls, period, line, frame_rate): line = str(line) frame_id, players, ball = line.strip().split(":")[:3] home_team_player_positions = {} away_team_player_positions = {} for player in players.split(";")[:-1]: team_id, target_id, jersey_no, x, y, speed = player.split(",") team_id = int(team_id) if team_id == 1: home_team_player_positions[jersey_no] = Point( float(x), float(y)) elif team_id == 0: away_team_player_positions[jersey_no] = Point( float(x), float(y)) ( ball_x, ball_y, ball_z, ball_speed, ball_owning_team, ball_state, ) = ball.rstrip(";").split(",")[:6] frame_id = int(frame_id) if ball_owning_team == "H": ball_owning_team = Team.HOME elif ball_owning_team == "A": ball_owning_team = Team.AWAY else: raise Exception(f"Unknown ball owning team: {ball_owning_team}") if ball_state == "Alive": ball_state = BallState.ALIVE elif ball_state == "Dead": ball_state = BallState.DEAD else: raise Exception(f"Unknown ball state: {ball_state}") return Frame( frame_id=frame_id, timestamp=frame_id / frame_rate - period.start_timestamp, ball_position=Point(float(ball_x), float(ball_y)), ball_state=ball_state, ball_owning_team=ball_owning_team, home_team_player_positions=home_team_player_positions, away_team_player_positions=away_team_player_positions, period=period, )
def _frame_from_framedata(cls, teams, period, frame_data): frame_id = frame_data["frameIdx"] frame_timestamp = frame_data["gameClock"] if frame_data["ball"]["xyz"]: ball_x, ball_y, ball_z = frame_data["ball"]["xyz"] ball_coordinates = Point3D( float(ball_x), float(ball_y), float(ball_z) ) else: ball_coordinates = None ball_state = BallState.ALIVE if frame_data["live"] else BallState.DEAD ball_owning_team = ( teams[0] if frame_data["lastTouch"] == "home" else teams[1] ) players_data = {} for team, team_str in zip(teams, ["homePlayers", "awayPlayers"]): for player_data in frame_data[team_str]: jersey_no = player_data["number"] x, y, _ = player_data["xyz"] player = team.get_player_by_jersey_number(jersey_no) if not player: player = Player( player_id=player_data["playerId"], team=team, jersey_no=int(jersey_no), ) team.players.append(player) players_data[player] = PlayerData( coordinates=Point(float(x), float(y)) ) return Frame( frame_id=frame_id, timestamp=frame_timestamp, ball_coordinates=ball_coordinates, ball_state=ball_state, ball_owning_team=ball_owning_team, players_data=players_data, period=period, other_data={}, )
def transform_frame(self, frame: Frame) -> Frame: flip = self.__needs_flip( ball_owning_team=frame.ball_owning_team, attacking_direction=frame.period.attacking_direction, ) return Frame( # doesn't change timestamp=frame.timestamp, frame_id=frame.frame_id, ball_owning_team=frame.ball_owning_team, ball_state=frame.ball_state, period=frame.period, # changes ball_coordinates=self.transform_point(frame.ball_coordinates, flip), players_coordinates={ key: self.transform_point(point, flip) for key, point in frame.players_coordinates.items() }, )
def __flip_frame(self, frame: Frame): players_data = {} for player, data in frame.players_data.items(): players_data[player] = PlayerData( coordinates=self.flip_point(data.coordinates), distance=data.distance, speed=data.speed, other_data=data.other_data, ) return Frame( # doesn't change timestamp=frame.timestamp, frame_id=frame.frame_id, ball_owning_team=frame.ball_owning_team, ball_state=frame.ball_state, period=frame.period, # changes ball_coordinates=self.flip_point(frame.ball_coordinates), players_data=players_data, other_data=frame.other_data, )
def deserialize(self, inputs: Dict[str, Readable], options: Dict = None) -> TrackingDataset: """ Deserialize Metrica tracking data into a `TrackingDataset`. Parameters ---------- inputs : dict input `raw_data_home` should point to a `Readable` object containing the 'csv' formatted raw data for the home team. input `raw_data_away` should point to a `Readable` object containing the 'csv' formatted raw data for the away team. options : dict Options for deserialization of the Metrica file. Possible options are `sample_rate` (float between 0 and 1) to specify the amount of frames that should be loaded, `limit` to specify the max number of frames that will be returned. Returns ------- dataset : TrackingDataset Raises ------ ValueError when both input files don't seem to belong to each other See Also -------- Examples -------- >>> serializer = MetricaTrackingSerializer() >>> with open("Sample_Game_1_RawTrackingData_Away_Team.csv", "rb") as raw_home, \ >>> open("Sample_Game_1_RawTrackingData_Home_Team.csv", "rb") as raw_away: >>> >>> dataset = serializer.deserialize( >>> inputs={ >>> 'raw_data_home': raw_home, >>> 'raw_data_away': raw_away >>> }, >>> options={ >>> 'sample_rate': 1/12 >>> } >>> ) """ self.__validate_inputs(inputs) if not options: options = {} sample_rate = float(options.get('sample_rate', 1.0)) limit = int(options.get('limit', 0)) # consider reading this from data frame_rate = 25 with performance_logging("prepare", logger=logger): home_iterator = self.__create_iterator(inputs['raw_data_home'], sample_rate, frame_rate) away_iterator = self.__create_iterator(inputs['raw_data_away'], sample_rate, frame_rate) partial_frames = zip(home_iterator, away_iterator) with performance_logging("loading", logger=logger): frames = [] periods = [] partial_frame_type = self.__PartialFrame home_partial_frame: partial_frame_type away_partial_frame: partial_frame_type for n, (home_partial_frame, away_partial_frame) in enumerate(partial_frames): self.__validate_partials(home_partial_frame, away_partial_frame) period: Period = home_partial_frame.period frame_id: int = home_partial_frame.frame_id frame = Frame(frame_id=frame_id, timestamp=frame_id / frame_rate - period.start_timestamp, ball_position=home_partial_frame.ball_position, home_team_player_positions=home_partial_frame. player_positions, away_team_player_positions=away_partial_frame. player_positions, period=period, ball_state=None, ball_owning_team=None) frames.append(frame) if not periods or period.id != periods[-1].id: periods.append(period) if not period.attacking_direction_set: period.set_attacking_direction( attacking_direction=attacking_direction_from_frame( frame)) n += 1 if limit and n >= limit: break orientation = ( Orientation.FIXED_HOME_AWAY if periods[0].attacking_direction == AttackingDirection.HOME_AWAY else Orientation.FIXED_AWAY_HOME) return TrackingDataset( flags=~(DatasetFlag.BALL_STATE | DatasetFlag.BALL_OWNING_TEAM), frame_rate=frame_rate, orientation=orientation, pitch_dimensions=PitchDimensions(x_dim=Dimension(0, 1), y_dim=Dimension(0, 1)), periods=periods, records=frames)
def _frame_from_line(cls, teams, period, line, frame_rate): line = str(line) frame_id, players, ball = line.strip().split(":")[:3] players_data = {} for player_data in players.split(";")[:-1]: team_id, target_id, jersey_no, x, y, speed = player_data.split(",") team_id = int(team_id) if team_id == 1: team = teams[0] elif team_id == 0: team = teams[1] else: # it's probably -1, but make sure it doesn't crash continue player = team.get_player_by_jersey_number(jersey_no) if not player: player = Player( player_id=f"{team.ground}_{jersey_no}", team=team, jersey_no=int(jersey_no), ) team.players.append(player) players_data[player] = PlayerData( coordinates=Point(float(x), float(y)), speed=float(speed) ) ( ball_x, ball_y, ball_z, ball_speed, ball_owning_team, ball_state, ) = ball.rstrip(";").split(",")[:6] frame_id = int(frame_id) if ball_owning_team == "H": ball_owning_team = teams[0] elif ball_owning_team == "A": ball_owning_team = teams[1] else: raise DeserializationError( f"Unknown ball owning team: {ball_owning_team}" ) if ball_state == "Alive": ball_state = BallState.ALIVE elif ball_state == "Dead": ball_state = BallState.DEAD else: raise DeserializationError(f"Unknown ball state: {ball_state}") return Frame( frame_id=frame_id, timestamp=frame_id / frame_rate - period.start_timestamp, ball_coordinates=Point3D( float(ball_x), float(ball_y), float(ball_z) ), ball_state=ball_state, ball_owning_team=ball_owning_team, players_data=players_data, period=period, other_data={}, )
def _get_frame_data( cls, teams, teamdict, players, player_id_to_team_dict, periods, player_dict, anon_players, ball_id, referee_dict, frame, ): frame_period = frame["period"] frame_id = frame["frame"] frame_time = cls._timestamp_from_timestring(frame["time"]) ball_coordinates = None players_data = {} # ball_carrier = frame["possession"].get("trackable_object") ball_owning_team = frame["possession"].get("group") if ball_owning_team == "home team": ball_owning_team = teams[0] elif ball_owning_team == "away team": ball_owning_team = teams[1] else: ball_owning_team = None for frame_record in frame["data"]: # containing x, y, trackable_object, track_id, group_name x = frame_record.get("x") y = frame_record.get("y") trackable_object = frame_record.get("trackable_object", None) track_id = frame_record.get("track_id", None) group_name = frame_record.get("group_name", None) if trackable_object == ball_id: group_name = "ball" z = frame_record.get("z") if z is not None: z = float(z) ball_coordinates = Point3D(x=float(x), y=float(y), z=z) continue elif trackable_object in referee_dict.keys(): group_name = "referee" continue # Skip Referee Coords if group_name is None: group_name = teamdict.get( player_id_to_team_dict.get(trackable_object)) if group_name == "home_team": player = players["HOME"][trackable_object] elif group_name == "away_team": player = players["AWAY"][trackable_object] if trackable_object is None: player_id = str(track_id) if group_name == "home team": if f"anon_{player_id}" not in anon_players["HOME"].keys(): player = cls.__create_anon_player(teams, frame_record) anon_players["HOME"][f"anon_home_{player_id}"] = player else: player = anon_players["HOME"][f"anon_home_{player_id}"] elif group_name == "away team": if f"anon_{player_id}" not in anon_players["AWAY"].keys(): player = cls.__create_anon_player(teams, frame_record) anon_players["AWAY"][f"anon_away_{player_id}"] = player else: player = anon_players["AWAY"][f"anon_away_{player_id}"] players_data[player] = PlayerData(coordinates=Point(x, y)) return Frame( frame_id=frame_id, timestamp=frame_time, ball_coordinates=ball_coordinates, players_data=players_data, period=periods[frame_period], ball_state=None, ball_owning_team=ball_owning_team, other_data={}, )
def _frame_from_line(cls, teams, period, line, frame_rate): line = str(line) frame_id, players, ball = line.strip().split(":")[:3] players_coordinates = {} for player_data in players.split(";")[:-1]: team_id, target_id, jersey_no, x, y, speed = player_data.split(",") team_id = int(team_id) if team_id == 1: team = teams[0] elif team_id == 0: team = teams[1] else: raise Exception(f"Unknown team {team_id}") player = team.get_player_by_jersey_number(jersey_no) if not player: player = Player( player_id=f"{team.ground}_{jersey_no}", team=team, jersey_no=int(jersey_no), ) team.players.append(player) players_coordinates[player] = Point(float(x), float(y)) ( ball_x, ball_y, ball_z, ball_speed, ball_owning_team, ball_state, ) = ball.rstrip(";").split(",")[:6] frame_id = int(frame_id) if ball_owning_team == "H": ball_owning_team = teams[0] elif ball_owning_team == "A": ball_owning_team = teams[1] else: raise Exception(f"Unknown ball owning team: {ball_owning_team}") if ball_state == "Alive": ball_state = BallState.ALIVE elif ball_state == "Dead": ball_state = BallState.DEAD else: raise Exception(f"Unknown ball state: {ball_state}") return Frame( frame_id=frame_id, timestamp=frame_id / frame_rate - period.start_timestamp, ball_coordinates=Point(float(ball_x), float(ball_y)), ball_state=ball_state, ball_owning_team=ball_owning_team, players_coordinates=players_coordinates, period=period, )