Skip to main content

visual_cortex_vision/
frame_diff.rs

1use std::hash::{Hash, Hasher};
2
3use visual_cortex_capture::FrameView;
4
5use crate::detector::{Detector, DetectorError, DetectorOutput};
6
7/// Boolean gate: did the region's pixels change since the last evaluation?
8/// Hashes the region's bytes; the first evaluation always reports changed.
9#[derive(Default)]
10pub struct FrameDiff {
11    last_hash: Option<u64>,
12}
13
14impl FrameDiff {
15    pub fn new() -> Self {
16        Self::default()
17    }
18}
19
20impl Detector for FrameDiff {
21    fn evaluate(&mut self, view: &FrameView<'_>) -> Result<DetectorOutput, DetectorError> {
22        let mut hasher = std::collections::hash_map::DefaultHasher::new();
23        for row in view.rows() {
24            row.hash(&mut hasher);
25        }
26        let hash = hasher.finish();
27        let changed = self.last_hash != Some(hash);
28        self.last_hash = Some(hash);
29        Ok(DetectorOutput::Bool(changed))
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use visual_cortex_capture::{Frame, PxRect};
37
38    fn full_view(frame: &Frame) -> FrameView<'_> {
39        frame
40            .view(PxRect {
41                x: 0,
42                y: 0,
43                w: frame.width(),
44                h: frame.height(),
45            })
46            .unwrap()
47    }
48
49    #[test]
50    fn first_evaluation_reports_changed() {
51        let f = Frame::solid(4, 4, [0, 0, 0, 255]);
52        let mut gate = FrameDiff::new();
53        assert_eq!(
54            gate.evaluate(&full_view(&f)).unwrap(),
55            DetectorOutput::Bool(true)
56        );
57    }
58
59    #[test]
60    fn identical_pixels_report_unchanged() {
61        let a = Frame::solid(4, 4, [9, 9, 9, 255]);
62        let b = Frame::solid(4, 4, [9, 9, 9, 255]); // different Frame, same pixels
63        let mut gate = FrameDiff::new();
64        gate.evaluate(&full_view(&a)).unwrap();
65        assert_eq!(
66            gate.evaluate(&full_view(&b)).unwrap(),
67            DetectorOutput::Bool(false)
68        );
69    }
70
71    #[test]
72    fn different_pixels_report_changed() {
73        let a = Frame::solid(4, 4, [0, 0, 0, 255]);
74        let b = Frame::solid(4, 4, [255, 0, 0, 255]);
75        let mut gate = FrameDiff::new();
76        gate.evaluate(&full_view(&a)).unwrap();
77        assert_eq!(
78            gate.evaluate(&full_view(&b)).unwrap(),
79            DetectorOutput::Bool(true)
80        );
81    }
82}