visual-cortex-vision 0.12.0

Detectors for visual-cortex: pixel/color conditions, template matching, the OcrEngine contract, and OCR text parsers.
Documentation
use visual_cortex_capture::FrameView;

/// Integer BT.601 luma approximation (identical on every platform).
/// Truncating division is intentional: deterministic, and the bias (never
/// exceeding the true rounded value) is irrelevant to correlation matching.
pub(crate) fn luma(r: u8, g: u8, b: u8) -> u8 {
    ((r as u32 * 299 + g as u32 * 587 + b as u32 * 114) / 1000) as u8
}

/// Convert a BGRA view to a row-major grayscale buffer.
pub(crate) fn view_to_luma(view: &FrameView<'_>) -> (Vec<u8>, u32, u32) {
    let (w, h) = (view.width(), view.height());
    let mut out = Vec::with_capacity(w as usize * h as usize);
    for row in view.rows() {
        for px in row.chunks_exact(4) {
            out.push(luma(px[2], px[1], px[0]));
        }
    }
    (out, w, h)
}

#[cfg(test)]
mod tests {
    use super::*;
    use visual_cortex_capture::{Frame, PxRect};

    #[test]
    fn luma_extremes() {
        assert_eq!(luma(255, 255, 255), 255);
        assert_eq!(luma(0, 0, 0), 0);
    }

    #[test]
    fn view_to_luma_is_row_major_rgb_weighted() {
        // 2x1 frame: pure red pixel then pure blue pixel (BGRA layout)
        let f = Frame::from_fn(2, 1, |x, _| {
            if x == 0 {
                [0, 0, 255, 255] // red
            } else {
                [255, 0, 0, 255] // blue
            }
        });
        let view = f
            .view(PxRect {
                x: 0,
                y: 0,
                w: 2,
                h: 1,
            })
            .unwrap();
        let (l, w, h) = view_to_luma(&view);
        assert_eq!((w, h), (2, 1));
        assert_eq!(l, vec![76, 29]); // 255*299/1000 = 76, 255*114/1000 = 29
    }
}