mirror of
https://github.com/azaion/gps-denied-desktop.git
synced 2026-04-23 02:26:36 +00:00
initial structure implemented
docs -> _docs
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from .base import ConfigurationManagerBase
|
||||
from .configuration_manager import ConfigurationManager
|
||||
|
||||
__all__ = ["ConfigurationManagerBase", "ConfigurationManager"]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Any
|
||||
|
||||
from models.config import SystemConfig
|
||||
|
||||
|
||||
class ConfigurationManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def load_config(self, path: str) -> SystemConfig:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def save_config(self, config: SystemConfig, path: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_config(self) -> SystemConfig:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_value(self, key: str, default: Any = None) -> Any:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_value(self, key: str, value: Any) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def validate_config(self, config: SystemConfig) -> list[str]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def reload_config(self) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
from typing import Any
|
||||
|
||||
from .base import ConfigurationManagerBase
|
||||
from models.config import SystemConfig
|
||||
|
||||
|
||||
class ConfigurationManager(ConfigurationManagerBase):
|
||||
async def load_config(self, path: str) -> SystemConfig:
|
||||
raise NotImplementedError
|
||||
|
||||
async def save_config(self, config: SystemConfig, path: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_config(self) -> SystemConfig:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_value(self, key: str, default: Any = None) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_value(self, key: str, value: Any) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def validate_config(self, config: SystemConfig) -> list[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def reload_config(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import CoordinateTransformerBase
|
||||
from .coordinate_transformer import CoordinateTransformer
|
||||
|
||||
__all__ = ["CoordinateTransformerBase", "CoordinateTransformer"]
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numpy as np
|
||||
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileBounds
|
||||
|
||||
|
||||
class CoordinateTransformerBase(ABC):
|
||||
@abstractmethod
|
||||
def gps_to_local(
|
||||
self, gps: GPSPoint, origin: GPSPoint
|
||||
) -> tuple[float, float]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def local_to_gps(
|
||||
self, local: tuple[float, float], origin: GPSPoint
|
||||
) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def pixel_to_gps(
|
||||
self,
|
||||
pixel: tuple[float, float],
|
||||
homography: np.ndarray,
|
||||
tile_bounds: TileBounds,
|
||||
) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def gps_to_pixel(
|
||||
self,
|
||||
gps: GPSPoint,
|
||||
homography: np.ndarray,
|
||||
tile_bounds: TileBounds,
|
||||
) -> tuple[float, float]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def compute_distance_meters(
|
||||
self, gps1: GPSPoint, gps2: GPSPoint
|
||||
) -> float:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def compute_bearing(
|
||||
self, from_gps: GPSPoint, to_gps: GPSPoint
|
||||
) -> float:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def offset_gps(
|
||||
self, gps: GPSPoint, distance_m: float, bearing_deg: float
|
||||
) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import numpy as np
|
||||
|
||||
from .base import CoordinateTransformerBase
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileBounds
|
||||
|
||||
|
||||
class CoordinateTransformer(CoordinateTransformerBase):
|
||||
def gps_to_local(
|
||||
self, gps: GPSPoint, origin: GPSPoint
|
||||
) -> tuple[float, float]:
|
||||
raise NotImplementedError
|
||||
|
||||
def local_to_gps(
|
||||
self, local: tuple[float, float], origin: GPSPoint
|
||||
) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
def pixel_to_gps(
|
||||
self,
|
||||
pixel: tuple[float, float],
|
||||
homography: np.ndarray,
|
||||
tile_bounds: TileBounds,
|
||||
) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
def gps_to_pixel(
|
||||
self,
|
||||
gps: GPSPoint,
|
||||
homography: np.ndarray,
|
||||
tile_bounds: TileBounds,
|
||||
) -> tuple[float, float]:
|
||||
raise NotImplementedError
|
||||
|
||||
def compute_distance_meters(
|
||||
self, gps1: GPSPoint, gps2: GPSPoint
|
||||
) -> float:
|
||||
raise NotImplementedError
|
||||
|
||||
def compute_bearing(
|
||||
self, from_gps: GPSPoint, to_gps: GPSPoint
|
||||
) -> float:
|
||||
raise NotImplementedError
|
||||
|
||||
def offset_gps(
|
||||
self, gps: GPSPoint, distance_m: float, bearing_deg: float
|
||||
) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FactorGraphOptimizerBase
|
||||
from .factor_graph_optimizer import FactorGraphOptimizer
|
||||
|
||||
__all__ = ["FactorGraphOptimizerBase", "FactorGraphOptimizer"]
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from models.core import Pose, GPSPoint
|
||||
from models.results import OptimizationResult, RefinedFrameResult
|
||||
from models.processing import RelativePose
|
||||
|
||||
|
||||
class FactorGraphOptimizerBase(ABC):
|
||||
@abstractmethod
|
||||
async def initialize_graph(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def add_odometry_factor(
|
||||
self,
|
||||
flight_id: str,
|
||||
from_frame: int,
|
||||
to_frame: int,
|
||||
relative_pose: RelativePose,
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def add_gps_prior(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
gps: GPSPoint,
|
||||
covariance: np.ndarray,
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def optimize(
|
||||
self, flight_id: str, max_iterations: int = 100
|
||||
) -> OptimizationResult:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_optimized_poses(
|
||||
self, flight_id: str
|
||||
) -> list[RefinedFrameResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_pose(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[Pose]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def marginalize_old_poses(
|
||||
self, flight_id: str, keep_last_n: int
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from .base import FactorGraphOptimizerBase
|
||||
from models.core import Pose, GPSPoint
|
||||
from models.results import OptimizationResult, RefinedFrameResult
|
||||
from models.processing import RelativePose
|
||||
|
||||
|
||||
class FactorGraphOptimizer(FactorGraphOptimizerBase):
|
||||
async def initialize_graph(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def add_odometry_factor(
|
||||
self,
|
||||
flight_id: str,
|
||||
from_frame: int,
|
||||
to_frame: int,
|
||||
relative_pose: RelativePose,
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def add_gps_prior(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
gps: GPSPoint,
|
||||
covariance: np.ndarray,
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def optimize(
|
||||
self, flight_id: str, max_iterations: int = 100
|
||||
) -> OptimizationResult:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_optimized_poses(
|
||||
self, flight_id: str
|
||||
) -> list[RefinedFrameResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_pose(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[Pose]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def marginalize_old_poses(
|
||||
self, flight_id: str, keep_last_n: int
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FailureRecoveryCoordinatorBase
|
||||
from .failure_recovery_coordinator import FailureRecoveryCoordinator
|
||||
|
||||
__all__ = ["FailureRecoveryCoordinatorBase", "FailureRecoveryCoordinator"]
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from models.recovery import (
|
||||
SearchSession,
|
||||
ConfidenceAssessment,
|
||||
UserAnchor,
|
||||
UserInputRequest,
|
||||
)
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCandidate
|
||||
|
||||
|
||||
class FailureRecoveryCoordinatorBase(ABC):
|
||||
@abstractmethod
|
||||
async def start_search_session(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
estimated_center: GPSPoint,
|
||||
) -> SearchSession:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def expand_search(
|
||||
self, session_id: str
|
||||
) -> Optional[list[TileCandidate]]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def assess_confidence(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> ConfidenceAssessment:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def request_user_input(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
uav_image: np.ndarray,
|
||||
candidates: list[TileCandidate],
|
||||
) -> UserInputRequest:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def process_user_anchor(
|
||||
self, flight_id: str, anchor: UserAnchor
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def is_recovery_needed(
|
||||
self, confidence: ConfidenceAssessment
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_active_session(
|
||||
self, flight_id: str
|
||||
) -> Optional[SearchSession]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def cancel_session(self, session_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from .base import FailureRecoveryCoordinatorBase
|
||||
from models.recovery import (
|
||||
SearchSession,
|
||||
ConfidenceAssessment,
|
||||
UserAnchor,
|
||||
UserInputRequest,
|
||||
)
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCandidate
|
||||
|
||||
|
||||
class FailureRecoveryCoordinator(FailureRecoveryCoordinatorBase):
|
||||
async def start_search_session(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
estimated_center: GPSPoint,
|
||||
) -> SearchSession:
|
||||
raise NotImplementedError
|
||||
|
||||
async def expand_search(
|
||||
self, session_id: str
|
||||
) -> Optional[list[TileCandidate]]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def assess_confidence(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> ConfidenceAssessment:
|
||||
raise NotImplementedError
|
||||
|
||||
async def request_user_input(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
uav_image: np.ndarray,
|
||||
candidates: list[TileCandidate],
|
||||
) -> UserInputRequest:
|
||||
raise NotImplementedError
|
||||
|
||||
async def process_user_anchor(
|
||||
self, flight_id: str, anchor: UserAnchor
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def is_recovery_needed(
|
||||
self, confidence: ConfidenceAssessment
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_active_session(
|
||||
self, flight_id: str
|
||||
) -> Optional[SearchSession]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def cancel_session(self, session_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FlightAPIBase
|
||||
from .flight_api import FlightAPI
|
||||
|
||||
__all__ = ["FlightAPIBase", "FlightAPI"]
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import AsyncIterator
|
||||
from fastapi import UploadFile
|
||||
|
||||
from models.api import (
|
||||
FlightCreateRequest,
|
||||
FlightResponse,
|
||||
FlightDetailResponse,
|
||||
FlightStatusResponse,
|
||||
DeleteResponse,
|
||||
UpdateResponse,
|
||||
BatchResponse,
|
||||
UserFixRequest,
|
||||
UserFixResponse,
|
||||
ObjectGPSResponse,
|
||||
)
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class FlightAPIBase(ABC):
|
||||
@abstractmethod
|
||||
async def create_flight(self, request: FlightCreateRequest) -> FlightResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight(self, flight_id: str) -> FlightDetailResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight_status(self, flight_id: str) -> FlightStatusResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_flight(self, flight_id: str) -> DeleteResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[GPSPoint]
|
||||
) -> UpdateResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def upload_batch(
|
||||
self,
|
||||
flight_id: str,
|
||||
files: list[UploadFile],
|
||||
start_sequence: int,
|
||||
end_sequence: int,
|
||||
batch_number: int,
|
||||
) -> BatchResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def submit_user_fix(
|
||||
self, flight_id: str, request: UserFixRequest
|
||||
) -> UserFixResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_object_gps(
|
||||
self, flight_id: str, frame_id: int, pixel: tuple[float, float]
|
||||
) -> ObjectGPSResponse:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def stream_events(self, flight_id: str) -> AsyncIterator[dict]:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
from typing import AsyncIterator
|
||||
from fastapi import UploadFile
|
||||
|
||||
from .base import FlightAPIBase
|
||||
from models.api import (
|
||||
FlightCreateRequest,
|
||||
FlightResponse,
|
||||
FlightDetailResponse,
|
||||
FlightStatusResponse,
|
||||
DeleteResponse,
|
||||
UpdateResponse,
|
||||
BatchResponse,
|
||||
UserFixRequest,
|
||||
UserFixResponse,
|
||||
ObjectGPSResponse,
|
||||
)
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class FlightAPI(FlightAPIBase):
|
||||
async def create_flight(self, request: FlightCreateRequest) -> FlightResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight(self, flight_id: str) -> FlightDetailResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight_status(self, flight_id: str) -> FlightStatusResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def delete_flight(self, flight_id: str) -> DeleteResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[GPSPoint]
|
||||
) -> UpdateResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def upload_batch(
|
||||
self,
|
||||
flight_id: str,
|
||||
files: list[UploadFile],
|
||||
start_sequence: int,
|
||||
end_sequence: int,
|
||||
batch_number: int,
|
||||
) -> BatchResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def submit_user_fix(
|
||||
self, flight_id: str, request: UserFixRequest
|
||||
) -> UserFixResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_object_gps(
|
||||
self, flight_id: str, frame_id: int, pixel: tuple[float, float]
|
||||
) -> ObjectGPSResponse:
|
||||
raise NotImplementedError
|
||||
|
||||
async def stream_events(self, flight_id: str) -> AsyncIterator[dict]:
|
||||
raise NotImplementedError
|
||||
yield
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FlightDatabaseBase
|
||||
from .flight_database import FlightDatabase
|
||||
|
||||
__all__ = ["FlightDatabaseBase", "FlightDatabase"]
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from models.flight import Flight, FlightState, Waypoint
|
||||
from models.results import FrameResult
|
||||
from models.chunks import ChunkHandle
|
||||
from models.core import Pose
|
||||
|
||||
|
||||
class FlightDatabaseBase(ABC):
|
||||
@abstractmethod
|
||||
async def create_flight(self, flight: Flight) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight(self, flight_id: str) -> Optional[Flight]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_flight(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_flight_state(self, state: FlightState) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight_state(self, flight_id: str) -> Optional[FlightState]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def save_frame_result(self, flight_id: str, result: FrameResult) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_frame_result(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[FrameResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_all_frame_results(self, flight_id: str) -> list[FrameResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def save_chunk(self, chunk: ChunkHandle) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_chunk(self, chunk_id: str) -> Optional[ChunkHandle]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight_chunks(self, flight_id: str) -> list[ChunkHandle]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def save_pose(self, flight_id: str, pose: Pose) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_poses(self, flight_id: str) -> list[Pose]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[Waypoint]
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
from typing import Optional
|
||||
|
||||
from .base import FlightDatabaseBase
|
||||
from models.flight import Flight, FlightState, Waypoint
|
||||
from models.results import FrameResult
|
||||
from models.chunks import ChunkHandle
|
||||
from models.core import Pose
|
||||
|
||||
|
||||
class FlightDatabase(FlightDatabaseBase):
|
||||
async def create_flight(self, flight: Flight) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight(self, flight_id: str) -> Optional[Flight]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def delete_flight(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_flight_state(self, state: FlightState) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight_state(self, flight_id: str) -> Optional[FlightState]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def save_frame_result(self, flight_id: str, result: FrameResult) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_frame_result(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[FrameResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_all_frame_results(self, flight_id: str) -> list[FrameResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def save_chunk(self, chunk: ChunkHandle) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_chunk(self, chunk_id: str) -> Optional[ChunkHandle]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight_chunks(self, flight_id: str) -> list[ChunkHandle]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def save_pose(self, flight_id: str, pose: Pose) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_poses(self, flight_id: str) -> list[Pose]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[Waypoint]
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FlightLifecycleManagerBase
|
||||
from .flight_lifecycle_manager import FlightLifecycleManager
|
||||
|
||||
__all__ = ["FlightLifecycleManagerBase", "FlightLifecycleManager"]
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from models.flight import Flight, Waypoint
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class FlightLifecycleManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def create_flight(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
start_gps: GPSPoint,
|
||||
rough_waypoints: list[GPSPoint],
|
||||
camera_params: dict,
|
||||
altitude: float,
|
||||
) -> Flight:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_flight(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[Waypoint]
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight(self, flight_id: str) -> Optional[Flight]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def start_processing(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def stop_processing(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_processing_status(self, flight_id: str) -> dict:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
from typing import Optional
|
||||
|
||||
from .base import FlightLifecycleManagerBase
|
||||
from models.flight import Flight, Waypoint
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class FlightLifecycleManager(FlightLifecycleManagerBase):
|
||||
async def create_flight(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
start_gps: GPSPoint,
|
||||
rough_waypoints: list[GPSPoint],
|
||||
camera_params: dict,
|
||||
altitude: float,
|
||||
) -> Flight:
|
||||
raise NotImplementedError
|
||||
|
||||
async def delete_flight(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_waypoints(
|
||||
self, flight_id: str, waypoints: list[Waypoint]
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight(self, flight_id: str) -> Optional[Flight]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def start_processing(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def stop_processing(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_processing_status(self, flight_id: str) -> dict:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import FlightProcessingEngineBase
|
||||
from .flight_processing_engine import FlightProcessingEngine
|
||||
|
||||
__all__ = ["FlightProcessingEngineBase", "FlightProcessingEngine"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from models.images import ImageData
|
||||
from models.results import FrameResult
|
||||
from models.processing import RelativePose
|
||||
from models.recovery import UserAnchor
|
||||
|
||||
|
||||
class FlightProcessingEngineBase(ABC):
|
||||
@abstractmethod
|
||||
async def process_frame(
|
||||
self, flight_id: str, image: ImageData
|
||||
) -> Optional[FrameResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_relative_pose(
|
||||
self, prev_image: np.ndarray, curr_image: np.ndarray
|
||||
) -> RelativePose:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def apply_user_anchor(
|
||||
self, flight_id: str, frame_id: int, anchor: UserAnchor
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def is_blocked(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def resume_processing(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_current_chunk_id(self, flight_id: str) -> Optional[str]:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from .base import FlightProcessingEngineBase
|
||||
from models.images import ImageData
|
||||
from models.results import FrameResult
|
||||
from models.processing import RelativePose
|
||||
from models.recovery import UserAnchor
|
||||
|
||||
|
||||
class FlightProcessingEngine(FlightProcessingEngineBase):
|
||||
async def process_frame(
|
||||
self, flight_id: str, image: ImageData
|
||||
) -> Optional[FrameResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_relative_pose(
|
||||
self, prev_image: np.ndarray, curr_image: np.ndarray
|
||||
) -> RelativePose:
|
||||
raise NotImplementedError
|
||||
|
||||
async def apply_user_anchor(
|
||||
self, flight_id: str, frame_id: int, anchor: UserAnchor
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def is_blocked(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def resume_processing(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_current_chunk_id(self, flight_id: str) -> Optional[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import GlobalPlaceRecognitionBase
|
||||
from .global_place_recognition import GlobalPlaceRecognition
|
||||
|
||||
__all__ = ["GlobalPlaceRecognitionBase", "GlobalPlaceRecognition"]
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numpy as np
|
||||
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCandidate
|
||||
from models.processing import AlignmentResult
|
||||
|
||||
|
||||
class GlobalPlaceRecognitionBase(ABC):
|
||||
@abstractmethod
|
||||
async def extract_global_descriptor(self, image: np.ndarray) -> np.ndarray:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def search_candidates(
|
||||
self,
|
||||
descriptor: np.ndarray,
|
||||
search_center: GPSPoint,
|
||||
search_radius: float,
|
||||
top_k: int = 10,
|
||||
) -> list[TileCandidate]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def verify_candidate(
|
||||
self, uav_image: np.ndarray, satellite_image: np.ndarray
|
||||
) -> AlignmentResult:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def index_tile(
|
||||
self, tile_image: np.ndarray, tile_id: str, gps_center: GPSPoint
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def build_index_for_area(
|
||||
self, nw: GPSPoint, se: GPSPoint, zoom: int
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_indexed_tile_count(self) -> int:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import numpy as np
|
||||
|
||||
from .base import GlobalPlaceRecognitionBase
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCandidate
|
||||
from models.processing import AlignmentResult
|
||||
|
||||
|
||||
class GlobalPlaceRecognition(GlobalPlaceRecognitionBase):
|
||||
async def extract_global_descriptor(self, image: np.ndarray) -> np.ndarray:
|
||||
raise NotImplementedError
|
||||
|
||||
async def search_candidates(
|
||||
self,
|
||||
descriptor: np.ndarray,
|
||||
search_center: GPSPoint,
|
||||
search_radius: float,
|
||||
top_k: int = 10,
|
||||
) -> list[TileCandidate]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def verify_candidate(
|
||||
self, uav_image: np.ndarray, satellite_image: np.ndarray
|
||||
) -> AlignmentResult:
|
||||
raise NotImplementedError
|
||||
|
||||
async def index_tile(
|
||||
self, tile_image: np.ndarray, tile_id: str, gps_center: GPSPoint
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def build_index_for_area(
|
||||
self, nw: GPSPoint, se: GPSPoint, zoom: int
|
||||
) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_indexed_tile_count(self) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import ImageInputPipelineBase
|
||||
from .image_input_pipeline import ImageInputPipeline
|
||||
|
||||
__all__ = ["ImageInputPipelineBase", "ImageInputPipeline"]
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, AsyncIterator
|
||||
|
||||
from models.images import ImageData, ImageBatch, ProcessingStatus
|
||||
from models.core import ValidationResult
|
||||
|
||||
|
||||
class ImageInputPipelineBase(ABC):
|
||||
@abstractmethod
|
||||
async def receive_batch(
|
||||
self, flight_id: str, batch: ImageBatch
|
||||
) -> ValidationResult:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_next_image(self, flight_id: str) -> Optional[ImageData]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def stream_images(self, flight_id: str) -> AsyncIterator[ImageData]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_status(self, flight_id: str) -> ProcessingStatus:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def clear_queue(self, flight_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_queue_size(self, flight_id: str) -> int:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from typing import Optional, AsyncIterator
|
||||
|
||||
from .base import ImageInputPipelineBase
|
||||
from models.images import ImageData, ImageBatch, ProcessingStatus
|
||||
from models.core import ValidationResult
|
||||
|
||||
|
||||
class ImageInputPipeline(ImageInputPipelineBase):
|
||||
async def receive_batch(
|
||||
self, flight_id: str, batch: ImageBatch
|
||||
) -> ValidationResult:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_next_image(self, flight_id: str) -> Optional[ImageData]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def stream_images(self, flight_id: str) -> AsyncIterator[ImageData]:
|
||||
raise NotImplementedError
|
||||
yield
|
||||
|
||||
async def get_status(self, flight_id: str) -> ProcessingStatus:
|
||||
raise NotImplementedError
|
||||
|
||||
async def clear_queue(self, flight_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_queue_size(self, flight_id: str) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import ImageRotationManagerBase
|
||||
from .image_rotation_manager import ImageRotationManager
|
||||
|
||||
__all__ = ["ImageRotationManagerBase", "ImageRotationManager"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from models.processing import RotationResult
|
||||
from models.flight import HeadingRecord
|
||||
|
||||
|
||||
class ImageRotationManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def estimate_rotation(
|
||||
self, uav_image: np.ndarray, satellite_image: np.ndarray
|
||||
) -> RotationResult:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_rotation_for_frame(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[float]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_heading_history(
|
||||
self, flight_id: str, record: HeadingRecord
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def predict_heading(self, flight_id: str) -> Optional[float]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def is_sharp_turn(
|
||||
self, flight_id: str, current_heading: float
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def rotate_image(self, image: np.ndarray, angle: float) -> np.ndarray:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from .base import ImageRotationManagerBase
|
||||
from models.processing import RotationResult
|
||||
from models.flight import HeadingRecord
|
||||
|
||||
|
||||
class ImageRotationManager(ImageRotationManagerBase):
|
||||
async def estimate_rotation(
|
||||
self, uav_image: np.ndarray, satellite_image: np.ndarray
|
||||
) -> RotationResult:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_rotation_for_frame(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[float]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_heading_history(
|
||||
self, flight_id: str, record: HeadingRecord
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def predict_heading(self, flight_id: str) -> Optional[float]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def is_sharp_turn(
|
||||
self, flight_id: str, current_heading: float
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def rotate_image(self, image: np.ndarray, angle: float) -> np.ndarray:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import MetricRefinementBase
|
||||
from .metric_refinement import MetricRefinement
|
||||
|
||||
__all__ = ["MetricRefinementBase", "MetricRefinement"]
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numpy as np
|
||||
|
||||
from models.core import GPSPoint
|
||||
from models.processing import AlignmentResult
|
||||
|
||||
|
||||
class MetricRefinementBase(ABC):
|
||||
@abstractmethod
|
||||
async def refine_alignment(
|
||||
self,
|
||||
uav_image: np.ndarray,
|
||||
satellite_image: np.ndarray,
|
||||
initial_homography: np.ndarray,
|
||||
) -> AlignmentResult:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def compute_precise_gps(
|
||||
self,
|
||||
uav_center_pixel: tuple[float, float],
|
||||
homography: np.ndarray,
|
||||
tile_bounds: dict,
|
||||
) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def estimate_reprojection_error(
|
||||
self,
|
||||
correspondences: np.ndarray,
|
||||
homography: np.ndarray,
|
||||
) -> float:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def filter_outliers(
|
||||
self,
|
||||
correspondences: np.ndarray,
|
||||
homography: np.ndarray,
|
||||
threshold: float = 3.0,
|
||||
) -> np.ndarray:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import numpy as np
|
||||
|
||||
from .base import MetricRefinementBase
|
||||
from models.core import GPSPoint
|
||||
from models.processing import AlignmentResult
|
||||
|
||||
|
||||
class MetricRefinement(MetricRefinementBase):
|
||||
async def refine_alignment(
|
||||
self,
|
||||
uav_image: np.ndarray,
|
||||
satellite_image: np.ndarray,
|
||||
initial_homography: np.ndarray,
|
||||
) -> AlignmentResult:
|
||||
raise NotImplementedError
|
||||
|
||||
async def compute_precise_gps(
|
||||
self,
|
||||
uav_center_pixel: tuple[float, float],
|
||||
homography: np.ndarray,
|
||||
tile_bounds: dict,
|
||||
) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
async def estimate_reprojection_error(
|
||||
self,
|
||||
correspondences: np.ndarray,
|
||||
homography: np.ndarray,
|
||||
) -> float:
|
||||
raise NotImplementedError
|
||||
|
||||
async def filter_outliers(
|
||||
self,
|
||||
correspondences: np.ndarray,
|
||||
homography: np.ndarray,
|
||||
threshold: float = 3.0,
|
||||
) -> np.ndarray:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import ModelManagerBase
|
||||
from .model_manager import ModelManager
|
||||
|
||||
__all__ = ["ModelManagerBase", "ModelManager"]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Any
|
||||
import numpy as np
|
||||
|
||||
from models.config import ModelConfig
|
||||
|
||||
|
||||
class ModelManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def load_model(self, config: ModelConfig) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def unload_model(self, model_name: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_model(self, model_name: str) -> Optional[Any]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def run_inference(
|
||||
self, model_name: str, inputs: dict[str, np.ndarray]
|
||||
) -> dict[str, np.ndarray]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def warmup_model(
|
||||
self, model_name: str, iterations: int = 3
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_loaded_models(self) -> list[str]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_model_info(self, model_name: str) -> Optional[dict]:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
from typing import Optional, Any
|
||||
import numpy as np
|
||||
|
||||
from .base import ModelManagerBase
|
||||
from models.config import ModelConfig
|
||||
|
||||
|
||||
class ModelManager(ModelManagerBase):
|
||||
async def load_model(self, config: ModelConfig) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def unload_model(self, model_name: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_model(self, model_name: str) -> Optional[Any]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def run_inference(
|
||||
self, model_name: str, inputs: dict[str, np.ndarray]
|
||||
) -> dict[str, np.ndarray]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def warmup_model(
|
||||
self, model_name: str, iterations: int = 3
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_loaded_models(self) -> list[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_model_info(self, model_name: str) -> Optional[dict]:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import ResultManagerBase
|
||||
from .result_manager import ResultManager
|
||||
|
||||
__all__ = ["ResultManagerBase", "ResultManager"]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from models.results import FrameResult, FlightResults, RefinedFrameResult
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class ResultManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def save_frame_result(
|
||||
self, flight_id: str, result: FrameResult
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_frame_result(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[FrameResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight_results(self, flight_id: str) -> FlightResults:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_refined_result(
|
||||
self, flight_id: str, result: RefinedFrameResult
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_object_gps(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
pixel: tuple[float, float],
|
||||
) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def export_results(
|
||||
self, flight_id: str, format: str = "json"
|
||||
) -> bytes:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_statistics(self, flight_id: str) -> dict:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
from typing import Optional
|
||||
|
||||
from .base import ResultManagerBase
|
||||
from models.results import FrameResult, FlightResults, RefinedFrameResult
|
||||
from models.core import GPSPoint
|
||||
|
||||
|
||||
class ResultManager(ResultManagerBase):
|
||||
async def save_frame_result(
|
||||
self, flight_id: str, result: FrameResult
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_frame_result(
|
||||
self, flight_id: str, frame_id: int
|
||||
) -> Optional[FrameResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight_results(self, flight_id: str) -> FlightResults:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_refined_result(
|
||||
self, flight_id: str, result: RefinedFrameResult
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_object_gps(
|
||||
self,
|
||||
flight_id: str,
|
||||
frame_id: int,
|
||||
pixel: tuple[float, float],
|
||||
) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
async def export_results(
|
||||
self, flight_id: str, format: str = "json"
|
||||
) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_statistics(self, flight_id: str) -> dict:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import RouteChunkManagerBase
|
||||
from .route_chunk_manager import RouteChunkManager
|
||||
|
||||
__all__ = ["RouteChunkManagerBase", "RouteChunkManager"]
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from models.chunks import ChunkHandle, ChunkBounds, Sim3Transform
|
||||
from models.core import GPSPoint
|
||||
from models.processing import ChunkAlignmentResult
|
||||
|
||||
|
||||
class RouteChunkManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def create_chunk(
|
||||
self, flight_id: str, start_frame_id: int
|
||||
) -> ChunkHandle:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def add_frame_to_chunk(
|
||||
self, chunk_id: str, frame_id: int
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def finalize_chunk(self, chunk_id: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_chunk(self, chunk_id: str) -> Optional[ChunkHandle]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_active_chunk(
|
||||
self, flight_id: str
|
||||
) -> Optional[ChunkHandle]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_flight_chunks(
|
||||
self, flight_id: str
|
||||
) -> list[ChunkHandle]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def estimate_chunk_bounds(
|
||||
self, chunk_id: str
|
||||
) -> ChunkBounds:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def anchor_chunk(
|
||||
self, chunk_id: str, frame_id: int, gps: GPSPoint
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def match_chunk_to_satellite(
|
||||
self, chunk_id: str
|
||||
) -> Optional[ChunkAlignmentResult]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def apply_transform_to_chunk(
|
||||
self, chunk_id: str, transform: Sim3Transform
|
||||
) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
from typing import Optional
|
||||
|
||||
from .base import RouteChunkManagerBase
|
||||
from models.chunks import ChunkHandle, ChunkBounds, Sim3Transform
|
||||
from models.core import GPSPoint
|
||||
from models.processing import ChunkAlignmentResult
|
||||
|
||||
|
||||
class RouteChunkManager(RouteChunkManagerBase):
|
||||
async def create_chunk(
|
||||
self, flight_id: str, start_frame_id: int
|
||||
) -> ChunkHandle:
|
||||
raise NotImplementedError
|
||||
|
||||
async def add_frame_to_chunk(
|
||||
self, chunk_id: str, frame_id: int
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def finalize_chunk(self, chunk_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_chunk(self, chunk_id: str) -> Optional[ChunkHandle]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_active_chunk(
|
||||
self, flight_id: str
|
||||
) -> Optional[ChunkHandle]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_flight_chunks(
|
||||
self, flight_id: str
|
||||
) -> list[ChunkHandle]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def estimate_chunk_bounds(
|
||||
self, chunk_id: str
|
||||
) -> ChunkBounds:
|
||||
raise NotImplementedError
|
||||
|
||||
async def anchor_chunk(
|
||||
self, chunk_id: str, frame_id: int, gps: GPSPoint
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
async def match_chunk_to_satellite(
|
||||
self, chunk_id: str
|
||||
) -> Optional[ChunkAlignmentResult]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def apply_transform_to_chunk(
|
||||
self, chunk_id: str, transform: Sim3Transform
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import SatelliteDataManagerBase
|
||||
from .satellite_data_manager import SatelliteDataManager
|
||||
|
||||
__all__ = ["SatelliteDataManagerBase", "SatelliteDataManager"]
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCoords, TileBounds
|
||||
|
||||
|
||||
class SatelliteDataManagerBase(ABC):
|
||||
@abstractmethod
|
||||
async def get_tile(
|
||||
self, gps: GPSPoint, zoom: int
|
||||
) -> Optional[tuple[np.ndarray, TileBounds]]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_tile_by_coords(
|
||||
self, coords: TileCoords
|
||||
) -> Optional[tuple[np.ndarray, TileBounds]]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_tiles_in_radius(
|
||||
self, center: GPSPoint, radius_meters: float, zoom: int
|
||||
) -> list[tuple[np.ndarray, TileBounds]]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_tile_bounds(self, coords: TileCoords) -> TileBounds:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def prefetch_area(
|
||||
self, nw: GPSPoint, se: GPSPoint, zoom: int
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def gps_to_tile_coords(self, gps: GPSPoint, zoom: int) -> TileCoords:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def tile_coords_to_gps(self, coords: TileCoords) -> GPSPoint:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
from .base import SatelliteDataManagerBase
|
||||
from models.core import GPSPoint
|
||||
from models.satellite import TileCoords, TileBounds
|
||||
|
||||
|
||||
class SatelliteDataManager(SatelliteDataManagerBase):
|
||||
async def get_tile(
|
||||
self, gps: GPSPoint, zoom: int
|
||||
) -> Optional[tuple[np.ndarray, TileBounds]]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_tile_by_coords(
|
||||
self, coords: TileCoords
|
||||
) -> Optional[tuple[np.ndarray, TileBounds]]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_tiles_in_radius(
|
||||
self, center: GPSPoint, radius_meters: float, zoom: int
|
||||
) -> list[tuple[np.ndarray, TileBounds]]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_tile_bounds(self, coords: TileCoords) -> TileBounds:
|
||||
raise NotImplementedError
|
||||
|
||||
async def prefetch_area(
|
||||
self, nw: GPSPoint, se: GPSPoint, zoom: int
|
||||
) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
def gps_to_tile_coords(self, gps: GPSPoint, zoom: int) -> TileCoords:
|
||||
raise NotImplementedError
|
||||
|
||||
def tile_coords_to_gps(self, coords: TileCoords) -> GPSPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import SequentialVisualOdometryBase
|
||||
from .sequential_visual_odometry import SequentialVisualOdometry
|
||||
|
||||
__all__ = ["SequentialVisualOdometryBase", "SequentialVisualOdometry"]
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numpy as np
|
||||
|
||||
from models.processing import RelativePose, Matches
|
||||
|
||||
|
||||
class SequentialVisualOdometryBase(ABC):
|
||||
@abstractmethod
|
||||
async def compute_relative_pose(
|
||||
self, prev_image: np.ndarray, curr_image: np.ndarray
|
||||
) -> RelativePose:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def extract_keypoints(
|
||||
self, image: np.ndarray
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def match_features(
|
||||
self,
|
||||
keypoints1: np.ndarray,
|
||||
descriptors1: np.ndarray,
|
||||
keypoints2: np.ndarray,
|
||||
descriptors2: np.ndarray,
|
||||
) -> Matches:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def estimate_motion(
|
||||
self, matches: Matches, camera_matrix: np.ndarray
|
||||
) -> RelativePose:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_tracking_good(self, pose: RelativePose) -> bool:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import numpy as np
|
||||
|
||||
from .base import SequentialVisualOdometryBase
|
||||
from models.processing import RelativePose, Matches
|
||||
|
||||
|
||||
class SequentialVisualOdometry(SequentialVisualOdometryBase):
|
||||
async def compute_relative_pose(
|
||||
self, prev_image: np.ndarray, curr_image: np.ndarray
|
||||
) -> RelativePose:
|
||||
raise NotImplementedError
|
||||
|
||||
async def extract_keypoints(
|
||||
self, image: np.ndarray
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def match_features(
|
||||
self,
|
||||
keypoints1: np.ndarray,
|
||||
descriptors1: np.ndarray,
|
||||
keypoints2: np.ndarray,
|
||||
descriptors2: np.ndarray,
|
||||
) -> Matches:
|
||||
raise NotImplementedError
|
||||
|
||||
async def estimate_motion(
|
||||
self, matches: Matches, camera_matrix: np.ndarray
|
||||
) -> RelativePose:
|
||||
raise NotImplementedError
|
||||
|
||||
def is_tracking_good(self, pose: RelativePose) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from .base import SSEEventStreamerBase
|
||||
from .sse_event_streamer import SSEEventStreamer
|
||||
|
||||
__all__ = ["SSEEventStreamerBase", "SSEEventStreamer"]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import AsyncIterator
|
||||
|
||||
from models.results import FrameResult
|
||||
from models.recovery import UserInputRequest
|
||||
|
||||
|
||||
class SSEEventStreamerBase(ABC):
|
||||
@abstractmethod
|
||||
async def emit_frame_result(
|
||||
self, flight_id: str, result: FrameResult
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def emit_status_update(
|
||||
self, flight_id: str, status: dict
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def emit_user_input_request(
|
||||
self, flight_id: str, request: UserInputRequest
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def emit_error(
|
||||
self, flight_id: str, error: str
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def subscribe(self, flight_id: str) -> AsyncIterator[dict]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def unsubscribe(self, flight_id: str, subscriber_id: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_subscriber_count(self, flight_id: str) -> int:
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from typing import AsyncIterator
|
||||
|
||||
from .base import SSEEventStreamerBase
|
||||
from models.results import FrameResult
|
||||
from models.recovery import UserInputRequest
|
||||
|
||||
|
||||
class SSEEventStreamer(SSEEventStreamerBase):
|
||||
async def emit_frame_result(
|
||||
self, flight_id: str, result: FrameResult
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def emit_status_update(
|
||||
self, flight_id: str, status: dict
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def emit_user_input_request(
|
||||
self, flight_id: str, request: UserInputRequest
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def emit_error(
|
||||
self, flight_id: str, error: str
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def subscribe(self, flight_id: str) -> AsyncIterator[dict]:
|
||||
raise NotImplementedError
|
||||
yield
|
||||
|
||||
async def unsubscribe(self, flight_id: str, subscriber_id: str) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_subscriber_count(self, flight_id: str) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
Reference in New Issue
Block a user