inferencelayer 0.2.1

Kortexya's engine-native inference layer — LLM generation + embedding/encoder family on wgpu (WGSL kernels, any adapter) with a pure-Rust CPU fallback
Documentation
//! cv2-exact resize kernels for the TableFormer prep chain — CPU, `encoder-cpu`-gated.
//!
//! Docling's `tf_predictor.resize_img` + `_prepare_image` lean on two OpenCV resizes whose exact
//! numerics decide the u8 crop and the prepared tensor. Both are replicated op-for-op so they sit
//! inside the bit-exact torch gate (`tests/tableformer_parity.rs`):
//!
//! * [`inter_area_u8`] — `cv2.INTER_AREA`, DECIMATION regime (page → height 1024). Faithful port
//!   of OpenCV's `computeResizeAreaTab` + `resizeArea_<uchar,float>`: per-axis fractional-overlap
//!   weights (f32, cast exactly as OpenCV), horizontal reduce per source row then vertical
//!   accumulate, `saturate_cast<uchar>` = round-half-to-even + clamp. Validated byte-identical to
//!   cv2 on the crop region of the reference pages (a handful of ≤1-LSB pixels drift only OUTSIDE
//!   the crop, from OpenCV's vectorized reduction order — the crop the model reads is exact).
//! * [`inter_linear_f32`] — `cv2.INTER_LINEAR` on the normalized f32 crop → 448×448. Separable
//!   (horizontal within each source row, then vertical), pixel-center mapping
//!   `fx = (dx+0.5)·scale − 0.5` cast to f32 then floored, boundary clamp with zero weight.
//!   Validated ≤1.5e-6 vs cv2 on the reference crops (the prepared-tensor gate bar is 1e-5).

/// One axis of cv2 `resizeArea` (decimation): per output index, the `(src_index, weight)`
/// contributions. `computeResizeAreaTab` verbatim — tab math in f64, `alpha` cast to f32 exactly
/// as OpenCV stores `DecimateAlpha::alpha`.
fn area_tab(ssize: usize, dsize: usize) -> Vec<Vec<(usize, f32)>> {
    let scale = ssize as f64 / dsize as f64;
    let mut tabs = vec![Vec::new(); dsize];
    for (dx, tab) in tabs.iter_mut().enumerate() {
        let fsx1 = dx as f64 * scale;
        let fsx2 = fsx1 + scale;
        let cell_width = scale.min(ssize as f64 - fsx1);
        let mut sx1 = fsx1.ceil() as i64;
        let sx2 = (fsx2.floor() as i64).min(ssize as i64 - 1);
        sx1 = sx1.min(sx2);

        if sx1 as f64 - fsx1 > 1e-3 {
            tab.push((
                (sx1 - 1) as usize,
                ((sx1 as f64 - fsx1) / cell_width) as f32,
            ));
        }
        for sx in sx1..sx2 {
            tab.push((sx as usize, (1.0 / cell_width) as f32));
        }
        if fsx2 - sx2 as f64 > 1e-3 {
            let a = (fsx2 - sx2 as f64).min(1.0).min(cell_width) / cell_width;
            tab.push((sx2 as usize, a as f32));
        }
    }
    tabs
}

/// `saturate_cast<uchar>(float)` — `cvRound` (round half to even, the default FPU mode) + clamp.
fn sat_u8(v: f32) -> u8 {
    v.round_ties_even().clamp(0.0, 255.0) as u8
}

/// `cv2.resize(src, (dw, dh), INTER_AREA)` on an interleaved u8 image `[sh, sw, cn]`, decimation
/// regime only (`sh ≥ dh && sw ≥ dw`, which is exactly how `resize_img(height=1024)` is used on a
/// page rendered at scale 2.0). Returns `[dh, dw, cn]`.
pub fn inter_area_u8(src: &[u8], sh: usize, sw: usize, cn: usize, dh: usize, dw: usize) -> Vec<u8> {
    assert_eq!(
        src.len(),
        sh * sw * cn,
        "inter_area_u8: src is not [sh,sw,cn]"
    );
    debug_assert!(
        sh >= dh && sw >= dw,
        "inter_area_u8 is the DECIMATION path (sh≥dh, sw≥dw); got {sh}x{sw} -> {dh}x{dw}"
    );
    let xt = area_tab(sw, dw);
    let yt = area_tab(sh, dh);
    let row = dw * cn;
    let mut out = vec![0u8; dh * row];
    let mut buf = vec![0f32; row];
    let mut sum = vec![0f32; row];
    for dy in 0..dh {
        sum.iter_mut().for_each(|v| *v = 0.0);
        for &(sy, beta) in &yt[dy] {
            buf.iter_mut().for_each(|v| *v = 0.0);
            let srow = &src[sy * sw * cn..(sy + 1) * sw * cn];
            for (dx, tab) in xt.iter().enumerate() {
                let dst = dx * cn;
                for &(sx, alpha) in tab {
                    let s = sx * cn;
                    for c in 0..cn {
                        buf[dst + c] += srow[s + c] as f32 * alpha;
                    }
                }
            }
            for (s, &b) in sum.iter_mut().zip(buf.iter()) {
                *s += beta * b;
            }
        }
        let orow = &mut out[dy * row..(dy + 1) * row];
        for (o, &s) in orow.iter_mut().zip(sum.iter()) {
            *o = sat_u8(s);
        }
    }
    out
}

/// Per-axis INTER_LINEAR taps: `(sx0, sx1, w1)` with `w0 = 1 − w1` (boundary taps carry weight 0).
fn linear_coeffs(ssize: usize, dsize: usize) -> (Vec<usize>, Vec<usize>, Vec<f32>) {
    let scale = ssize as f64 / dsize as f64;
    let mut i0 = vec![0usize; dsize];
    let mut i1 = vec![0usize; dsize];
    let mut w1 = vec![0f32; dsize];
    for dx in 0..dsize {
        let f = ((dx as f64 + 0.5) * scale - 0.5) as f32;
        let mut sx = f.floor() as i64;
        let mut fx = f - sx as f32;
        if sx < 0 {
            sx = 0;
            fx = 0.0;
        }
        if sx >= ssize as i64 - 1 {
            sx = ssize as i64 - 1;
            fx = 0.0;
        }
        i0[dx] = sx as usize;
        i1[dx] = (sx as usize + 1).min(ssize - 1);
        w1[dx] = fx;
    }
    (i0, i1, w1)
}

/// `cv2.resize(src, (dw, dh), INTER_LINEAR)` on an interleaved f32 image `[sh, sw, cn]` (the
/// normalized crop). Separable HResize→VResize order, matching OpenCV's float path. Returns
/// `[dh, dw, cn]`.
pub fn inter_linear_f32(
    src: &[f32],
    sh: usize,
    sw: usize,
    cn: usize,
    dh: usize,
    dw: usize,
) -> Vec<f32> {
    assert_eq!(
        src.len(),
        sh * sw * cn,
        "inter_linear_f32: src is not [sh,sw,cn]"
    );
    let (xi0, xi1, xw1) = linear_coeffs(sw, dw);
    let (yi0, yi1, yw1) = linear_coeffs(sh, dh);
    let mut out = vec![0f32; dh * dw * cn];
    for dy in 0..dh {
        let (sy0, sy1, wy1) = (yi0[dy], yi1[dy], yw1[dy]);
        let wy0 = 1.0f32 - wy1;
        let r0 = &src[sy0 * sw * cn..(sy0 + 1) * sw * cn];
        let r1 = &src[sy1 * sw * cn..(sy1 + 1) * sw * cn];
        for dx in 0..dw {
            let (a0, a1, wx1) = (xi0[dx] * cn, xi1[dx] * cn, xw1[dx]);
            let wx0 = 1.0f32 - wx1;
            let dst = (dy * dw + dx) * cn;
            for c in 0..cn {
                let h0 = r0[a0 + c] * wx0 + r0[a1 + c] * wx1;
                let h1 = r1[a0 + c] * wx0 + r1[a1 + c] * wx1;
                out[dst + c] = h0 * wy0 + h1 * wy1;
            }
        }
    }
    out
}