iris/tracking/
subtractor.rs1use crate::error::Result;
2use crate::image::Image;
3use burn::tensor::backend::Backend;
4
5pub struct BackgroundSubtractor<B: Backend> {
7 pub learning_rate: f32,
8 pub threshold: f32,
9 background: Option<Image<B>>,
10}
11
12impl<B: Backend> BackgroundSubtractor<B> {
13 #[must_use]
15 pub fn new(learning_rate: f32, threshold: f32) -> Self {
16 Self {
17 learning_rate,
18 threshold,
19 background: None,
20 }
21 }
22
23 pub fn apply(&mut self, frame: &Image<B>) -> Result<Image<B>> {
25 let frame_gray = frame.grayscale()?;
26
27 let bg = if let Some(bg_img) = &self.background {
28 let updated = bg_img
30 .tensor
31 .clone()
32 .mul_scalar(1.0 - self.learning_rate)
33 .add(frame_gray.tensor.clone().mul_scalar(self.learning_rate));
34 let bg_new = Image::new(updated);
35 self.background = Some(bg_new.clone());
36 bg_new
37 } else {
38 self.background = Some(frame_gray.clone());
39 frame_gray.clone()
40 };
41
42 let diff = frame_gray.absdiff(&bg)?;
44 let mask = diff.threshold(self.threshold, 1.0, crate::threshold::ThresholdType::Binary)?;
45 Ok(mask)
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use crate::test_helpers::{TestBackend, test_device};
53 use burn::tensor::{Tensor, TensorData};
54
55 #[test]
56 fn test_background_subtractor() {
57 let device = test_device();
58 let flat_data1 = vec![0.5f32; 3 * 8 * 8];
59 let flat_data2 = vec![0.6f32; 3 * 8 * 8];
60
61 let img1 = Image::new(Tensor::<TestBackend, 3>::from_data(
62 TensorData::new(flat_data1, [3, 8, 8]),
63 &device,
64 ));
65 let img2 = Image::new(Tensor::<TestBackend, 3>::from_data(
66 TensorData::new(flat_data2, [3, 8, 8]),
67 &device,
68 ));
69
70 let mut bs = BackgroundSubtractor::new(0.1, 0.05);
71 let mask1 = bs.apply(&img1).unwrap();
72 assert_eq!(mask1.shape(), [1, 8, 8]);
73
74 let mask2 = bs.apply(&img2).unwrap();
75 assert_eq!(mask2.shape(), [1, 8, 8]);
76 }
77}