use visual_cortex_capture::FrameView;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum DetectorOutput {
Bool(bool),
Text(String),
Number(f64),
Score(f32),
None,
}
impl DetectorOutput {
pub fn as_number(&self) -> Option<f64> {
match self {
DetectorOutput::Number(n) => Some(*n),
DetectorOutput::Score(s) => Some(*s as f64),
_ => None,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum DetectorError {
#[error("ocr error: {0}")]
Ocr(String),
#[error("{0}")]
Other(String),
}
pub trait Detector: Send + 'static {
fn evaluate(&mut self, view: &FrameView<'_>) -> Result<DetectorOutput, DetectorError>;
fn is_heavy(&self) -> bool {
false
}
}
pub struct FnDetector<F>(F);
impl<F> Detector for FnDetector<F>
where
F: FnMut(&FrameView<'_>) -> Result<DetectorOutput, DetectorError> + Send + 'static,
{
fn evaluate(&mut self, view: &FrameView<'_>) -> Result<DetectorOutput, DetectorError> {
(self.0)(view)
}
}
pub fn from_fn<F>(f: F) -> FnDetector<F>
where
F: FnMut(&FrameView<'_>) -> Result<DetectorOutput, DetectorError> + Send + 'static,
{
FnDetector(f)
}
#[cfg(test)]
mod tests {
use super::*;
use visual_cortex_capture::{Frame, PxRect};
#[test]
fn from_fn_adapts_a_closure() {
let mut det =
from_fn(|view: &FrameView<'_>| Ok(DetectorOutput::Number(view.width() as f64)));
let frame = Frame::solid(8, 8, [0, 0, 0, 255]);
let view = frame
.view(PxRect {
x: 0,
y: 0,
w: 4,
h: 4,
})
.unwrap();
assert_eq!(det.evaluate(&view).unwrap(), DetectorOutput::Number(4.0));
}
#[test]
fn as_number_extracts_numeric_outputs() {
assert_eq!(DetectorOutput::Number(3.5).as_number(), Some(3.5));
assert_eq!(DetectorOutput::Score(0.5).as_number(), Some(0.5));
assert_eq!(DetectorOutput::Bool(true).as_number(), None);
assert_eq!(DetectorOutput::Text("7".into()).as_number(), None);
}
#[test]
fn detector_trait_is_object_safe() {
let mut det: Box<dyn Detector> = Box::new(from_fn(|_| Ok(DetectorOutput::Bool(true))));
let frame = Frame::solid(2, 2, [0, 0, 0, 255]);
let view = frame
.view(PxRect {
x: 0,
y: 0,
w: 2,
h: 2,
})
.unwrap();
assert_eq!(det.evaluate(&view).unwrap(), DetectorOutput::Bool(true));
}
}