from typing import MutableMapping, Sequence
from .groundtruth import GroundTruthPath
from .state import CategoricalState, CompositeState
from .state import State, GaussianState, StateVector
from ..base import Property
from ..models.measurement import MeasurementModel
[docs]
class Detection(State):
"""Detection type"""
measurement_model: MeasurementModel = Property(
default=None,
doc="The measurement model used to generate the detection (the default is ``None``)")
metadata: MutableMapping = Property(
default=None, doc='Dictionary of metadata items for Detections.')
def __init__(self, state_vector, *args, **kwargs):
super().__init__(state_vector, *args, **kwargs)
if self.metadata is None:
self.metadata = {}
[docs]
class GaussianDetection(Detection, GaussianState):
"""GaussianDetection type"""
[docs]
class Clutter(Detection):
"""Clutter type for detections classed as clutter
This is same as :class:`~.Detection`, but can be used to identify clutter
for metrics and analysis purposes.
"""
[docs]
class TrueDetection(Detection):
"""TrueDetection type for detections that come from ground truth
This is same as :class:`~.Detection`, but can be used to identify true
detections for metrics and analysis purposes.
"""
groundtruth_path: GroundTruthPath = Property(
doc="Ground truth path that this detection came from")
[docs]
class MissedDetection(Detection):
"""Detection type for a missed detection
This is same as :class:`~.Detection`, but it is used in
MultipleHypothesis to indicate the null hypothesis (no
detections are associated with the specified track).
"""
state_vector: StateVector = Property(default=None, doc="State vector. Default `None`.")
def __init__(self, state_vector=None, *args, **kwargs):
super().__init__(state_vector, *args, **kwargs)
def __bool__(self):
return False
[docs]
class CategoricalDetection(Detection, CategoricalState):
"""Categorical detection type."""
[docs]
class TrueCategoricalDetection(TrueDetection, CategoricalDetection):
"""TrueCategoricalDetection type for categorical detections that come from ground truth."""
[docs]
class CompositeDetection(CompositeState):
"""Composite detection type
Composition of :class:`~.Detection`.
"""
sub_states: Sequence[Detection] = Property(
doc="Sequence of sub-detections comprising the composite detection. All sub-detections "
"must have matching timestamp. Must not be empty.")
groundtruth_path: GroundTruthPath = Property(
default=None,
doc="Ground truth path that this detection came from.")
mapping: Sequence[int] = Property(
default=None,
doc="Mapping of detections to composite state space. Defaults to `None`, where "
"sub-detections map to sub-state spaces in order.")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.mapping and len(self.mapping) != len(self.sub_states):
raise ValueError("Mappings and sub-detections must have same count")
elif self.mapping is None:
self.mapping = list(range(len(self.sub_states)))
@property
def metadata(self):
"""Combined metadata of all sub-detections."""
metadata = dict()
for sub_detection in self.sub_states:
metadata.update(sub_detection.metadata)
return metadata
Detection.register(CompositeDetection) # noqa: E305