block_motion_detector/
lib.rs1use ofps::prelude::v1::*;
4
5ofps::define_descriptor!(block_motion, Detector, |_| Ok(Box::new(
6 BlockMotionDetection::default()
7)));
8
9pub struct BlockMotionDetection {
14 pub min_size: f32,
15 pub subdivide: usize,
16 pub target_motion: f32,
17}
18
19impl Default for BlockMotionDetection {
20 fn default() -> Self {
21 Self {
22 min_size: 0.05,
23 subdivide: 3,
24 target_motion: 0.003,
25 }
26 }
27}
28
29impl Properties for BlockMotionDetection {
30 fn props_mut(&mut self) -> Vec<(&str, PropertyMut)> {
31 vec![
32 (
33 "Min size",
34 PropertyMut::float(&mut self.min_size, 0.01, 1.0),
35 ),
36 (
37 "Subdivisions",
38 PropertyMut::usize(&mut self.subdivide, 1, 16),
39 ),
40 (
41 "Target motion",
42 PropertyMut::float(&mut self.target_motion, 0.0001, 0.1),
43 ),
44 ]
45 }
46}
47
48impl Detector for BlockMotionDetection {
49 fn detect_motion(&self, motion: &[MotionEntry]) -> Option<(usize, MotionField)> {
50 let motion = motion.iter().copied();
51
52 let block_width = self.min_size.sqrt() / self.subdivide as f32;
54 let block_dim = (1.0 / block_width).ceil() as usize;
55
56 let mut mf = MotionFieldDensifier::new(block_dim, block_dim);
58 motion.for_each(|(pos, motion)| {
59 mf.add_vector(pos, motion);
60 });
61 let mf = MotionField::from(mf);
62
63 let mut map = vec![vec![false; block_dim]; block_dim];
64
65 mf.iter()
67 .filter(|(_, _, motion)| motion.magnitude() >= self.target_motion)
68 .for_each(|(x, y, _)| map[y][x] = true);
69
70 let mut biggest_area = 0;
72 let mut biggest_area_mf = None;
73
74 for y in 0..block_dim {
75 for x in 0..block_dim {
76 if map[y][x] {
77 let mut area = 0;
78 let mut mf2 = MotionField::new(block_dim, block_dim);
79
80 map[y][x] = false;
81 let mut to_fill = vec![(x, y); 1];
82
83 while let Some((x, y)) = to_fill.pop() {
84 area += 1;
85
86 let neighbor_offs = (-1..=1).flat_map(|x| (-1..=1).map(move |y| (x, y)));
87
88 for (x, y) in neighbor_offs
91 .map(|(ox, oy)| (x as isize + ox, y as isize + oy))
92 .filter(|&(ox, oy)| {
93 (0..block_dim as isize).contains(&ox)
94 && (0..block_dim as isize).contains(&oy)
95 })
96 .map(|(x, y)| (x as usize, y as usize))
97 {
98 if map[y][x] {
99 mf2.set_motion(x, y, mf.get_motion(x, y));
100 to_fill.push((x, y));
101 map[y][x] = false;
102 }
103 }
104 }
105
106 if area > biggest_area {
107 biggest_area = area;
108 biggest_area_mf = Some(mf2);
109 }
110 }
111 }
112 }
113
114 if biggest_area as f32 / (block_dim * block_dim) as f32 >= self.min_size {
115 biggest_area_mf.map(|mf| (biggest_area, mf))
116 } else {
117 None
118 }
119 }
120}