pico_detect/localize/
mod.rs

1mod localizer;
2pub mod perturbate;
3
4use image::Luma;
5pub use localizer::Localizer;
6
7use nalgebra::Point2;
8use perturbate::Perturbator;
9use pixelutil_image::ExtendedImageView;
10use rand::RngCore;
11
12use crate::Target;
13
14/// Implements object localization with perturbation.
15#[derive(Debug, Clone, Copy)]
16pub struct LocalizePerturbate {
17    /// Perturbator to apply to the target.
18    pub perturbator: Perturbator,
19    /// Number of perturbations to run.
20    pub runs: usize,
21}
22
23impl Default for LocalizePerturbate {
24    /// Creates with default perturbator and runs count set to 15.
25    #[inline]
26    fn default() -> Self {
27        Self {
28            perturbator: Default::default(),
29            runs: 15,
30        }
31    }
32}
33
34impl LocalizePerturbate {
35    /// Creates a new instance with the specified number of runs
36    /// with a default perturbator.
37    #[inline]
38    pub fn new(runs: usize) -> Self {
39        Self {
40            perturbator: Default::default(),
41            runs,
42        }
43    }
44
45    /// Applies perturbations to the target and runs the localizer on each perturbed target.
46    #[inline]
47    pub fn run<R, I>(
48        &self,
49        localizer: &Localizer,
50        rng: &mut R,
51        image: &I,
52        target: Target,
53    ) -> Point2<f32>
54    where
55        R: RngCore,
56        I: ExtendedImageView<Pixel = Luma<u8>>,
57    {
58        let mut xs: Vec<f32> = Vec::with_capacity(self.runs);
59        let mut ys: Vec<f32> = Vec::with_capacity(self.runs);
60
61        self.perturbator.run(rng, self.runs, target, |t| {
62            let p = localizer.localize(image, t);
63
64            xs.push(p.x);
65            ys.push(p.y);
66        });
67
68        xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
69        ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
70
71        let index = (self.runs - 1) / 2;
72
73        Point2::new(xs[index], ys[index])
74    }
75}