lmocr 0.1.2

Convert PDFs and images to Markdown using OpenRouter multimodal LLMs
use anyhow::Result;
use image::ImageReader;
use std::io::Cursor;

const SAMPLE_GRID_SIZE: u32 = 200;

// ITU-R BT.709 luminance coefficients
const LUMA_R: f32 = 0.2126;
const LUMA_G: f32 = 0.7152;
const LUMA_B: f32 = 0.0722;

const DARK_PIXEL_THRESHOLD: f32 = 0.9;
const MEAN_BRIGHTNESS_MIN: f32 = 0.99;
const DARK_RATIO_MAX: f32 = 0.002;

pub fn is_blank_image(bytes: &[u8]) -> Result<bool> {
    let img = ImageReader::new(Cursor::new(bytes))
        .with_guessed_format()?
        .decode()?;
    let rgb = img.to_rgb8();
    let (width, height) = rgb.dimensions();
    if width == 0 || height == 0 {
        return Ok(true);
    }

    let step_x = (width / SAMPLE_GRID_SIZE).max(1);
    let step_y = (height / SAMPLE_GRID_SIZE).max(1);

    let mut total = 0u64;
    let mut dark = 0u64;
    let mut sum = 0f32;

    for y in (0..height).step_by(step_y as usize) {
        for x in (0..width).step_by(step_x as usize) {
            let pixel = rgb.get_pixel(x, y);
            let [r, g, b] = pixel.0;
            let luma = (LUMA_R * r as f32 + LUMA_G * g as f32 + LUMA_B * b as f32) / 255.0;
            sum += luma;
            if luma < DARK_PIXEL_THRESHOLD {
                dark += 1;
            }
            total += 1;
        }
    }

    if total == 0 {
        return Ok(true);
    }
    let mean = sum / total as f32;
    let dark_ratio = dark as f32 / total as f32;

    Ok(mean > MEAN_BRIGHTNESS_MIN && dark_ratio < DARK_RATIO_MAX)
}