mod utils;
use clap::{Parser, ValueEnum};
use oar_ocr::indra::IndraOcrBuilder;
use oar_ocr::utils::load_image;
use std::path::PathBuf;
use std::time::Instant;
use tracing::info;
use utils::device_config::parse_device_config;
#[derive(Clone, Copy, ValueEnum)]
enum Format {
Markdown,
Json,
Text,
}
#[derive(Parser)]
#[command(name = "indra")]
#[command(about = "Read pages with Indra OCR (lines, layout blocks, Markdown)")]
struct Args {
#[arg(long)]
model_dir: PathBuf,
#[arg(required = true)]
images: Vec<PathBuf>,
#[arg(long, value_enum, default_value = "markdown")]
format: Format,
#[arg(long)]
math: bool,
#[arg(long)]
keep_furniture: bool,
#[arg(long)]
table_heads: bool,
#[arg(long, default_value = "cpu")]
device: String,
#[arg(long, default_value_t = 1)]
workers: usize,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
utils::init_tracing();
let args = Args::parse();
let mut builder = IndraOcrBuilder::new(&args.model_dir)
.math(args.math)
.keep_furniture(args.keep_furniture)
.table_heads(args.table_heads)
.workers(args.workers);
if let Some(config) = parse_device_config(&args.device)? {
builder = builder.ort_session(config);
}
let start = Instant::now();
let ocr = builder.build()?;
info!("Loaded the models in {:.0?}", start.elapsed());
let images = args
.images
.iter()
.map(|path| {
load_image(path).map_err(|error| format!("Failed to read {}: {error}", path.display()))
})
.collect::<Result<_, _>>()?;
let start = Instant::now();
let pages = ocr.predict(images)?;
info!("Read {} pages in {:.0?}", pages.len(), start.elapsed());
for (path, page) in args.images.iter().zip(pages) {
info!(
"{}: {} lines, {} blocks, rotation {}, skew {}, vertical {}",
path.display(),
page.lines.len(),
page.blocks.len(),
page.rotation,
page.skew,
page.vertical
);
match args.format {
Format::Markdown => println!("{}", page.markdown),
Format::Json => println!("{}", serde_json::to_string_pretty(&page.to_json())?),
Format::Text => {
for line in &page.lines {
println!("{}", line.text);
}
}
}
}
Ok(())
}