visual_cortex_vision/
detector.rs1use visual_cortex_capture::FrameView;
2
3#[derive(Debug, Clone, PartialEq)]
5#[non_exhaustive]
6pub enum DetectorOutput {
7 Bool(bool),
9 Text(String),
11 Number(f64),
17 Score(f32),
20 None,
22}
23
24impl DetectorOutput {
25 pub fn as_number(&self) -> Option<f64> {
28 match self {
29 DetectorOutput::Number(n) => Some(*n),
30 DetectorOutput::Score(s) => Some(*s as f64),
31 _ => None,
32 }
33 }
34}
35
36#[derive(Debug, thiserror::Error)]
37pub enum DetectorError {
38 #[error("ocr error: {0}")]
39 Ocr(String),
40 #[error("{0}")]
41 Other(String),
42}
43
44pub trait Detector: Send + 'static {
47 fn evaluate(&mut self, view: &FrameView<'_>) -> Result<DetectorOutput, DetectorError>;
48
49 fn is_heavy(&self) -> bool {
53 false
54 }
55}
56
57pub struct FnDetector<F>(F);
59
60impl<F> Detector for FnDetector<F>
61where
62 F: FnMut(&FrameView<'_>) -> Result<DetectorOutput, DetectorError> + Send + 'static,
63{
64 fn evaluate(&mut self, view: &FrameView<'_>) -> Result<DetectorOutput, DetectorError> {
65 (self.0)(view)
66 }
67}
68
69pub fn from_fn<F>(f: F) -> FnDetector<F>
71where
72 F: FnMut(&FrameView<'_>) -> Result<DetectorOutput, DetectorError> + Send + 'static,
73{
74 FnDetector(f)
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use visual_cortex_capture::{Frame, PxRect};
81
82 #[test]
83 fn from_fn_adapts_a_closure() {
84 let mut det =
85 from_fn(|view: &FrameView<'_>| Ok(DetectorOutput::Number(view.width() as f64)));
86 let frame = Frame::solid(8, 8, [0, 0, 0, 255]);
87 let view = frame
88 .view(PxRect {
89 x: 0,
90 y: 0,
91 w: 4,
92 h: 4,
93 })
94 .unwrap();
95 assert_eq!(det.evaluate(&view).unwrap(), DetectorOutput::Number(4.0));
96 }
97
98 #[test]
99 fn as_number_extracts_numeric_outputs() {
100 assert_eq!(DetectorOutput::Number(3.5).as_number(), Some(3.5));
101 assert_eq!(DetectorOutput::Score(0.5).as_number(), Some(0.5));
102 assert_eq!(DetectorOutput::Bool(true).as_number(), None);
103 assert_eq!(DetectorOutput::Text("7".into()).as_number(), None);
104 }
105
106 #[test]
107 fn detector_trait_is_object_safe() {
108 let mut det: Box<dyn Detector> = Box::new(from_fn(|_| Ok(DetectorOutput::Bool(true))));
109 let frame = Frame::solid(2, 2, [0, 0, 0, 255]);
110 let view = frame
111 .view(PxRect {
112 x: 0,
113 y: 0,
114 w: 2,
115 h: 2,
116 })
117 .unwrap();
118 assert_eq!(det.evaluate(&view).unwrap(), DetectorOutput::Bool(true));
119 }
120}