pub(crate) mod seams;
mod fallback;
mod ocr_engine;
mod sceptre_engine;
use std::path::Path;
use std::sync::Arc;
use crate::config::{OcrConfig, init_thread_pools, resolve_thread_budget};
use crate::error::Result;
use crate::types::{Image, OcrResult, Quad, TextLine};
use sceptre_engine::SceptreEngine;
use seams::{DefaultModelProvider, ModelProvider, NoopProgress, ProgressSink};
pub use fallback::FallbackEngine;
pub use ocr_engine::OcrEngine;
#[derive(Debug, Clone)]
pub struct ReadOptions {
pub detail: bool,
}
impl Default for ReadOptions {
fn default() -> Self {
Self { detail: true }
}
}
#[derive(Clone)]
pub struct Reader {
inner: Arc<Inner>,
}
struct Inner {
config: OcrConfig,
engine: Arc<dyn OcrEngine>,
}
impl Reader {
pub fn builder() -> ReaderBuilder {
ReaderBuilder::default()
}
pub fn config(&self) -> &OcrConfig {
&self.inner.config
}
pub fn readtext(&self, image: &Path, options: &ReadOptions) -> Result<OcrResult> {
let decoded = Image::from_path(image)?;
self.recognize(&decoded, options)
}
pub fn recognize(&self, image: &Image, options: &ReadOptions) -> Result<OcrResult> {
self.inner.engine.recognize(image, options)
}
pub fn detect(&self, image: &Image, options: &ReadOptions) -> Result<Vec<Quad>> {
self.inner.engine.detect(image, options)
}
pub fn recognize_line(&self, image: &Image, options: &ReadOptions) -> Result<TextLine> {
self.inner.engine.recognize_line(image, options)
}
}
#[derive(Default)]
pub struct ReaderBuilder {
config: OcrConfig,
engine: Option<Arc<dyn OcrEngine>>,
models: Option<Arc<dyn ModelProvider>>,
progress: Option<Arc<dyn ProgressSink>>,
}
impl ReaderBuilder {
pub fn config(mut self, config: OcrConfig) -> Self {
self.config = config;
self
}
pub fn engine(mut self, engine: Arc<dyn OcrEngine>) -> Self {
self.engine = Some(engine);
self
}
pub fn model_provider(mut self, provider: Arc<dyn ModelProvider>) -> Self {
self.models = Some(provider);
self
}
pub fn progress(mut self, progress: Arc<dyn ProgressSink>) -> Self {
self.progress = Some(progress);
self
}
pub fn build(self) -> Result<Reader> {
let budget = resolve_thread_budget(Some(&self.config.concurrency));
init_thread_pools(budget);
let engine: Arc<dyn OcrEngine> = match self.engine {
Some(engine) => engine,
None => {
let models = match self.models {
Some(models) => models,
None => Arc::new(DefaultModelProvider::from_config(&self.config)?),
};
let progress = self.progress.unwrap_or_else(|| Arc::new(NoopProgress));
Arc::new(SceptreEngine::new(self.config.clone(), models, progress))
}
};
Ok(Reader {
inner: Arc::new(Inner {
config: self.config,
engine,
}),
})
}
}