use ofps::prelude::v1::*;
ofps::define_descriptor!(block_motion, Detector, |_| Ok(Box::new(
BlockMotionDetection::default()
)));
pub struct BlockMotionDetection {
pub min_size: f32,
pub subdivide: usize,
pub target_motion: f32,
}
impl Default for BlockMotionDetection {
fn default() -> Self {
Self {
min_size: 0.05,
subdivide: 3,
target_motion: 0.003,
}
}
}
impl Properties for BlockMotionDetection {
fn props_mut(&mut self) -> Vec<(&str, PropertyMut)> {
vec![
(
"Min size",
PropertyMut::float(&mut self.min_size, 0.01, 1.0),
),
(
"Subdivisions",
PropertyMut::usize(&mut self.subdivide, 1, 16),
),
(
"Target motion",
PropertyMut::float(&mut self.target_motion, 0.0001, 0.1),
),
]
}
}
impl Detector for BlockMotionDetection {
fn detect_motion(&self, motion: &[MotionEntry]) -> Option<(usize, MotionField)> {
let motion = motion.iter().copied();
let block_width = self.min_size.sqrt() / self.subdivide as f32;
let block_dim = (1.0 / block_width).ceil() as usize;
let mut mf = MotionFieldDensifier::new(block_dim, block_dim);
motion.for_each(|(pos, motion)| {
mf.add_vector(pos, motion);
});
let mf = MotionField::from(mf);
let mut map = vec![vec![false; block_dim]; block_dim];
mf.iter()
.filter(|(_, _, motion)| motion.magnitude() >= self.target_motion)
.for_each(|(x, y, _)| map[y][x] = true);
let mut biggest_area = 0;
let mut biggest_area_mf = None;
for y in 0..block_dim {
for x in 0..block_dim {
if map[y][x] {
let mut area = 0;
let mut mf2 = MotionField::new(block_dim, block_dim);
map[y][x] = false;
let mut to_fill = vec![(x, y); 1];
while let Some((x, y)) = to_fill.pop() {
area += 1;
let neighbor_offs = (-1..=1).flat_map(|x| (-1..=1).map(move |y| (x, y)));
for (x, y) in neighbor_offs
.map(|(ox, oy)| (x as isize + ox, y as isize + oy))
.filter(|&(ox, oy)| {
(0..block_dim as isize).contains(&ox)
&& (0..block_dim as isize).contains(&oy)
})
.map(|(x, y)| (x as usize, y as usize))
{
if map[y][x] {
mf2.set_motion(x, y, mf.get_motion(x, y));
to_fill.push((x, y));
map[y][x] = false;
}
}
}
if area > biggest_area {
biggest_area = area;
biggest_area_mf = Some(mf2);
}
}
}
}
if biggest_area as f32 / (block_dim * block_dim) as f32 >= self.min_size {
biggest_area_mf.map(|mf| (biggest_area, mf))
} else {
None
}
}
}