use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcrConfig {
pub recognition: RecognitionConfig,
pub image_processing: ImageProcessingConfig,
pub layout_analysis: LayoutAnalysisConfig,
pub language: LanguageConfig,
pub performance: PerformanceConfig,
pub debug: DebugConfig,
}
impl Default for OcrConfig {
fn default() -> Self {
Self {
recognition: RecognitionConfig::default(),
image_processing: ImageProcessingConfig::default(),
layout_analysis: LayoutAnalysisConfig::default(),
language: LanguageConfig::default(),
performance: PerformanceConfig::default(),
debug: DebugConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecognitionConfig {
pub engine: RecognitionEngine,
pub language: String,
pub confidence_threshold: f32,
pub character_whitelist: Option<Vec<char>>,
pub character_blacklist: Option<Vec<char>>,
pub enable_dictionary_correction: bool,
pub enable_language_model: bool,
pub enable_font_attribute_detection: bool,
pub parameters: HashMap<String, String>,
}
impl Default for RecognitionConfig {
fn default() -> Self {
Self {
engine: RecognitionEngine::PatternMatching,
language: "en".to_string(),
confidence_threshold: 0.5,
character_whitelist: None,
character_blacklist: None,
enable_dictionary_correction: true,
enable_language_model: true,
enable_font_attribute_detection: true,
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecognitionEngine {
LSTM,
PatternMatching,
Hybrid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageProcessingConfig {
pub enable_preprocessing: bool,
pub enable_enhancement: bool,
pub enable_noise_reduction: bool,
pub enable_contrast_enhancement: bool,
pub enable_sharpening: bool,
pub enable_deskewing: bool,
pub enable_binarization: bool,
pub binarization_threshold: f32,
pub binarization_method: BinarizationMethod,
pub parameters: HashMap<String, String>,
}
impl Default for ImageProcessingConfig {
fn default() -> Self {
Self {
enable_preprocessing: true,
enable_enhancement: true,
enable_noise_reduction: true,
enable_contrast_enhancement: true,
enable_sharpening: false,
enable_deskewing: true,
enable_binarization: true,
binarization_threshold: 0.5,
binarization_method: BinarizationMethod::Otsu,
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinarizationMethod {
Otsu,
Adaptive,
Fixed,
Sauvola,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutAnalysisConfig {
pub enable_layout_analysis: bool,
pub enable_text_region_detection: bool,
pub enable_image_region_detection: bool,
pub enable_table_detection: bool,
pub enable_reading_order_detection: bool,
pub enable_orientation_detection: bool,
pub enable_vertical_text_detection: bool,
pub page_seg_mode: PageSegMode,
pub min_text_region_size: (u32, u32),
pub max_text_region_size: (u32, u32),
pub parameters: HashMap<String, String>,
}
impl Default for LayoutAnalysisConfig {
fn default() -> Self {
Self {
enable_layout_analysis: true,
enable_text_region_detection: true,
enable_image_region_detection: true,
enable_table_detection: true,
enable_reading_order_detection: true,
enable_orientation_detection: true,
enable_vertical_text_detection: true,
page_seg_mode: PageSegMode::Auto,
min_text_region_size: (10, 10),
max_text_region_size: (10000, 10000),
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PageSegMode {
Auto,
SingleBlock,
SingleColumn,
SparseText,
SparseTextWithOsd,
SingleLine,
SingleLineRaw,
SingleWord,
SingleChar,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageConfig {
pub primary_language: String,
pub secondary_languages: Vec<String>,
pub enable_language_detection: bool,
pub language_detection_threshold: f32,
pub character_set: CharacterSet,
pub text_direction: TextDirection,
pub parameters: HashMap<String, String>,
}
impl Default for LanguageConfig {
fn default() -> Self {
Self {
primary_language: "en".to_string(),
secondary_languages: Vec::new(),
enable_language_detection: true,
language_detection_threshold: 0.7,
character_set: CharacterSet::Unicode,
text_direction: TextDirection::LeftToRight,
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CharacterSet {
Ascii,
Latin1,
Unicode,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TextDirection {
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
pub max_threads: usize,
pub enable_simd: bool,
pub enable_gpu: bool,
pub memory_limit_mb: usize,
pub cache_size_mb: usize,
pub enable_parallel_processing: bool,
pub device: String,
pub parameters: HashMap<String, String>,
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
max_threads: num_cpus::get(),
enable_simd: true,
enable_gpu: false,
memory_limit_mb: 1024,
cache_size_mb: 256,
enable_parallel_processing: true,
device: "auto".to_string(),
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DebugConfig {
pub enable_debug_logging: bool,
pub enable_profiling: bool,
pub enable_intermediate_saving: bool,
pub debug_output_dir: Option<String>,
pub log_level: LogLevel,
pub parameters: HashMap<String, String>,
}
impl Default for DebugConfig {
fn default() -> Self {
Self {
enable_debug_logging: false,
enable_profiling: false,
enable_intermediate_saving: false,
debug_output_dir: None,
log_level: LogLevel::Info,
parameters: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LogLevel {
Error,
Warning,
Info,
Debug,
Trace,
}
impl OcrConfig {
pub fn new() -> Self {
Self::default()
}
pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let content = std::fs::read_to_string(path)?;
let config: Self = serde_json::from_str(&content)?;
Ok(config)
}
pub fn to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
let content = serde_json::to_string_pretty(self)?;
std::fs::write(path, content)?;
Ok(())
}
pub fn validate(&self) -> Result<()> {
if self.recognition.confidence_threshold < 0.0
|| self.recognition.confidence_threshold > 1.0
{
return Err(anyhow::anyhow!(
"Confidence threshold must be between 0.0 and 1.0"
));
}
if self.image_processing.binarization_threshold < 0.0
|| self.image_processing.binarization_threshold > 1.0
{
return Err(anyhow::anyhow!(
"Binarization threshold must be between 0.0 and 1.0"
));
}
if self.layout_analysis.min_text_region_size.0
>= self.layout_analysis.max_text_region_size.0
|| self.layout_analysis.min_text_region_size.1
>= self.layout_analysis.max_text_region_size.1
{
return Err(anyhow::anyhow!(
"Minimum text region size must be smaller than maximum"
));
}
if self.language.language_detection_threshold < 0.0
|| self.language.language_detection_threshold > 1.0
{
return Err(anyhow::anyhow!(
"Language detection threshold must be between 0.0 and 1.0"
));
}
if self.performance.max_threads == 0 {
return Err(anyhow::anyhow!("Maximum threads must be greater than 0"));
}
if self.performance.memory_limit_mb == 0 {
return Err(anyhow::anyhow!("Memory limit must be greater than 0"));
}
Ok(())
}
pub fn get_parameter(&self, key: &str) -> Option<&String> {
if let Some(value) = self.recognition.parameters.get(key) {
return Some(value);
}
if let Some(value) = self.image_processing.parameters.get(key) {
return Some(value);
}
if let Some(value) = self.layout_analysis.parameters.get(key) {
return Some(value);
}
if let Some(value) = self.language.parameters.get(key) {
return Some(value);
}
if let Some(value) = self.performance.parameters.get(key) {
return Some(value);
}
if let Some(value) = self.debug.parameters.get(key) {
return Some(value);
}
None
}
pub fn set_parameter(&mut self, key: &str, value: String) {
self.recognition.parameters.insert(key.to_string(), value);
}
}