cecile-supercool-tracker 0.0.1

Machine learning framework for building object trackers and similarity search engines
Documentation
use crate::track::{Observation, ObservationAttributes};
use crate::utils::bbox::Universal2DBox;
use crate::EPS;
use std::fmt::Formatter;

#[derive(Clone, Debug, Default)]
pub struct VisualObservationAttributes {
    bbox: Option<Universal2DBox>,
    visual_quality: f32,
    own_area_percentage: Option<f32>,
}

impl VisualObservationAttributes {
    pub fn new(q: f32, b: Universal2DBox) -> Self {
        Self {
            visual_quality: q,
            bbox: Some(b),
            own_area_percentage: None,
        }
    }

    pub fn with_own_area_percentage(q: f32, b: Universal2DBox, own_area_percentage: f32) -> Self {
        assert!(
            (0.0..=1.0).contains(&own_area_percentage),
            "Own area percentage must be contained in (0.0..=1.0)"
        );

        Self {
            visual_quality: q,
            bbox: Some(b),
            own_area_percentage: Some(own_area_percentage),
        }
    }

    pub fn unchecked_bbox_ref(&self) -> &Universal2DBox {
        self.bbox.as_ref().unwrap()
    }

    pub fn bbox_opt(&self) -> &Option<Universal2DBox> {
        &self.bbox
    }

    pub fn own_area_percentage_opt(&self) -> &Option<f32> {
        &self.own_area_percentage
    }

    pub fn drop_bbox(&mut self) {
        self.bbox = None;
    }

    pub fn visual_quality(&self) -> f32 {
        self.visual_quality
    }
}

impl std::fmt::Debug for Observation<VisualObservationAttributes> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "({:?}, {:?})", self.attr(), self.feature())
    }
}

impl ObservationAttributes for VisualObservationAttributes {
    type MetricObject = f32;

    fn calculate_metric_object(l: &Option<&Self>, r: &Option<&Self>) -> Option<Self::MetricObject> {
        if let (Some(l), Some(r)) = (l, r) {
            if let (Some(l), Some(r)) = (&l.bbox, &r.bbox) {
                let intersection = Universal2DBox::intersection(l, r);
                if intersection == 0.0 {
                    None
                } else {
                    let union = (l.height * l.height * l.aspect + r.height * r.height * r.aspect)
                        as f64
                        - intersection;
                    let res = intersection / union;
                    Some(res as f32)
                }
            } else {
                None
            }
        } else {
            None
        }
    }
}

impl PartialEq<Self> for VisualObservationAttributes {
    fn eq(&self, other: &Self) -> bool {
        if let (Some(my_bbox), Some(other_bbox)) = (&self.bbox, &other.bbox) {
            my_bbox.eq(other_bbox) && (self.visual_quality - other.visual_quality).abs() < EPS
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::track::ObservationAttributes;
    use crate::trackers::visual_sort::observation_attributes::VisualObservationAttributes;
    use crate::utils::bbox::BoundingBox;
    use crate::EPS;

    #[test]
    fn operations() {
        let mut attrs1 =
            VisualObservationAttributes::new(0.7, BoundingBox::new(0.0, 0.0, 3.0, 5.0).as_xyaah());
        let attrs2 =
            VisualObservationAttributes::new(0.7, BoundingBox::new(0.0, 0.0, 3.0, 5.0).as_xyaah());

        let dist =
            VisualObservationAttributes::calculate_metric_object(&Some(&attrs1), &Some(&attrs2))
                .unwrap();
        assert!((dist - 1.0).abs() < EPS);

        assert_eq!(&attrs1, &attrs2);

        attrs1.bbox = None;
        let dist =
            VisualObservationAttributes::calculate_metric_object(&Some(&attrs1), &Some(&attrs2));
        assert_eq!(dist, None);

        let dist = VisualObservationAttributes::calculate_metric_object(&None, &Some(&attrs2));
        assert_eq!(dist, None);

        let dist = VisualObservationAttributes::calculate_metric_object(&Some(&attrs1), &None);
        assert_eq!(dist, None);
    }
}