99 lines
2.5 KiB
Python
99 lines
2.5 KiB
Python
"""Abstract interfaces — all components code against these, never concretions.
|
|
|
|
Keeps Interface Segregation (I) and Dependency Inversion (D) satisfied.
|
|
Each protocol is tiny and single-purpose (S).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Protocol, runtime_checkable
|
|
|
|
import numpy as np
|
|
|
|
|
|
# ── Data transfer objects ────────────────────────────────────────────────
|
|
|
|
|
|
@dataclass
|
|
class Detection:
|
|
"""Single object detection."""
|
|
|
|
bbox: tuple[float, float, float, float] # x1, y1, x2, y2
|
|
confidence: float
|
|
class_id: int
|
|
class_name: str
|
|
track_id: int | None = None
|
|
mask: np.ndarray | None = None # segmentation mask (optional)
|
|
|
|
|
|
@dataclass
|
|
class FrameResult:
|
|
"""All detections for one frame."""
|
|
|
|
detections: list[Detection] = field(default_factory=list)
|
|
frame_index: int = 0
|
|
timestamp: float = 0.0
|
|
|
|
|
|
# ── Protocols ────────────────────────────────────────────────────────────
|
|
|
|
|
|
@runtime_checkable
|
|
class StreamSource(Protocol):
|
|
"""Reads frames from a video source."""
|
|
|
|
def open(self) -> bool: ...
|
|
def read(self) -> tuple[bool, np.ndarray | None]: ...
|
|
def release(self) -> None: ...
|
|
@property
|
|
def fps(self) -> float: ...
|
|
@property
|
|
def frame_size(self) -> tuple[int, int]: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class Detector(Protocol):
|
|
"""Runs inference on a frame and returns detections."""
|
|
|
|
def detect(self, frame: np.ndarray) -> list[Detection]: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class Tracker(Protocol):
|
|
"""Assigns persistent IDs to detections across frames."""
|
|
|
|
def update(
|
|
self, frame: np.ndarray, detections: list[Detection]
|
|
) -> list[Detection]: ...
|
|
|
|
def reset(self) -> None: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class Counter(Protocol):
|
|
"""Counts objects crossing a virtual boundary."""
|
|
|
|
def update(self, detections: list[Detection]) -> None: ...
|
|
|
|
@property
|
|
def loading_count(self) -> int: ...
|
|
|
|
@property
|
|
def unloading_count(self) -> int: ...
|
|
|
|
def reset(self) -> None: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class BatchManager(Protocol):
|
|
"""Manages batch lifecycle based on truck presence."""
|
|
|
|
def update(self, truck_detected: bool, timestamp: float) -> None: ...
|
|
|
|
@property
|
|
def current_batch_id(self) -> int | None: ...
|
|
|
|
@property
|
|
def is_active(self) -> bool: ...
|