from typing import Optional from pydantic import BaseModel import numpy as np import cv2 class KnownImagePoint(BaseModel): width: float height: float @classmethod def from_vec(cls, vec: tuple[float, float]) -> 'KnownImagePoint': return cls(width=vec[0], height=vec[1]) class BirdViewPoint(BaseModel): x: float y: float @classmethod def from_vec(cls, vec: tuple[float, float]) -> 'BirdViewPoint': return cls(x=vec[0], y=vec[1]) class KnownPoint(BaseModel): image: KnownImagePoint bird_view: BirdViewPoint class VideoMetadata(BaseModel): knownPoints: list[KnownPoint] def get_view_perspective_matrix(self) -> Optional[np.array]: if len(self.knownPoints) < 4: return None empirical_source = np.float32( [[k.image.width, k.image.height] for k in self.knownPoints]) empirical_target = np.float32( [[k.bird_view.x, k.bird_view.y] for k in self.knownPoints]) pixel_per_meter = 100 return cv2.getPerspectiveTransform(empirical_source, empirical_target * pixel_per_meter)