pico_detect/localize/
perturbate.rs

1use std::{
2    convert::{TryFrom, TryInto},
3    ops::Range,
4};
5
6use rand::{
7    distr::{uniform::Error, Uniform},
8    Rng, RngCore,
9};
10
11use crate::geometry::Target;
12
13#[derive(Clone, Copy, Debug, PartialEq)]
14/// A perturbator for applying random perturbations to a target's size and position.
15pub struct Perturbator {
16    pub scale: Uniform<f32>,
17    pub translate: Uniform<f32>,
18}
19
20impl Perturbator {
21    /// Creates a new perturbator with the specified scale and translation ranges.
22    #[inline]
23    pub fn from_ranges(scale: Range<f32>, translate: Range<f32>) -> Result<Self, Error> {
24        Ok(Self {
25            scale: scale.try_into()?,
26            translate: translate.try_into()?,
27        })
28    }
29}
30
31impl Default for Perturbator {
32    /// Creates a default perturbator with scale in the range `0.925..0.94` and
33    /// translation in the range `-0.075..0.075`.
34    #[inline]
35    fn default() -> Self {
36        Self {
37            scale: Uniform::try_from(0.925..0.94).unwrap(),
38            translate: Uniform::try_from(-0.075..0.075).unwrap(),
39        }
40    }
41}
42
43impl Perturbator {
44    /// Applies perturbations to a target's size and position, calling the provided closure
45    /// `f` with each perturbed target. See the [`perturbate`] function for more details.
46    #[inline]
47    pub fn run<R, F>(&self, rng: &mut R, count: usize, init: Target, f: F)
48    where
49        R: RngCore,
50        F: FnMut(Target),
51    {
52        perturbate(rng, self.scale, self.translate, count, init, f)
53    }
54}
55
56/// Applies perturbations to a target's size and position, calling the provided closure
57/// `f` with each perturbed target.
58///
59/// # Arguments
60///
61/// * `rng` - A mutable reference to a random number generator.
62/// * `scale` - A uniform distribution for scaling the target's size.
63/// * `translate` - A uniform distribution for translating the target's position.
64/// * `count` - The number of perturbations to apply.
65/// * `init` - The initial target to perturb.
66#[inline]
67pub fn perturbate<R, F>(
68    rng: &mut R,
69    scale: Uniform<f32>,
70    translate: Uniform<f32>,
71    count: usize,
72    init: Target,
73    mut f: F,
74) where
75    R: RngCore,
76    F: FnMut(Target),
77{
78    let size = init.size();
79
80    for _ in 0..count {
81        let s = size * rng.sample(scale);
82
83        let x = s.mul_add(rng.sample(translate), init.x());
84        let y = s.mul_add(rng.sample(translate), init.y());
85
86        f(Target::new(x, y, s));
87    }
88}