ocr 0.1.1

A pure Rust CLI OCR tool for printed text extraction
Documentation
//! Image processing utilities for OCR API

use crate::api::error::{ApiError, ApiResult};
use crate::core::image::{ImageFormat, ImageStatistics, OcrImage};
use crate::utils::Result;
use image::DynamicImage;
use serde::{Deserialize, Serialize};

/// Image processing operations
pub struct ImageProcessor;

impl ImageProcessor {
    /// Load image from memory
    pub fn load_from_memory(data: &[u8]) -> ApiResult<DynamicImage> {
        let img = image::load_from_memory(data)
            .map_err(|e| ApiError::ImageProcessing(format!("Failed to load image: {}", e)))?;
        Ok(img)
    }

    /// Load image from file
    pub async fn load_from_file<P: AsRef<std::path::Path>>(path: P) -> ApiResult<DynamicImage> {
        let data = tokio::fs::read(path).await?;
        Self::load_from_memory(&data)
    }

    /// Convert dynamic image to OCR image
    pub fn to_ocr_image(img: DynamicImage, dpi: u32) -> OcrImage {
        OcrImage::new(img, dpi)
    }

    /// Preprocess image for OCR
    pub fn preprocess_for_ocr(img: &OcrImage) -> ApiResult<OcrImage> {
        // Convert to grayscale if needed
        let gray_img = if img.format != ImageFormat::Grayscale {
            img.to_grayscale()
        } else {
            img.clone()
        };

        // Apply basic preprocessing
        let preprocessed = Self::apply_basic_preprocessing(&gray_img)?;

        Ok(preprocessed)
    }

    /// Apply basic preprocessing
    fn apply_basic_preprocessing(img: &OcrImage) -> Result<OcrImage> {
        // TODO: Implement basic preprocessing
        // - Noise reduction
        // - Contrast enhancement
        // - Sharpening
        // - Deskewing
        Ok(img.clone())
    }

    /// Enhance image contrast
    pub fn enhance_contrast(img: &OcrImage, factor: f32) -> ApiResult<OcrImage> {
        // TODO: Implement contrast enhancement
        Ok(img.clone())
    }

    /// Reduce image noise
    pub fn reduce_noise(img: &OcrImage) -> ApiResult<OcrImage> {
        // TODO: Implement noise reduction
        Ok(img.clone())
    }

    /// Sharpen image
    pub fn sharpen(img: &OcrImage) -> ApiResult<OcrImage> {
        // TODO: Implement sharpening
        Ok(img.clone())
    }

    /// Deskew image
    pub fn deskew(img: &OcrImage) -> ApiResult<OcrImage> {
        // TODO: Implement deskewing
        Ok(img.clone())
    }

    /// Apply binarization
    pub fn binarize(img: &OcrImage, threshold: u8) -> ApiResult<OcrImage> {
        Ok(img.threshold(threshold))
    }

    /// Resize image
    pub fn resize(img: &OcrImage, width: u32, height: u32) -> OcrImage {
        img.resize(width, height).unwrap_or_else(|_| img.clone())
    }

    /// Crop image
    pub fn crop(img: &OcrImage, x: u32, y: u32, width: u32, height: u32) -> ApiResult<OcrImage> {
        img.crop(x, y, width, height)
            .map_err(|e| ApiError::ImageProcessing(e.to_string()))
    }

    /// Rotate image
    pub fn rotate(img: &OcrImage, angle: f32) -> OcrImage {
        img.rotate(angle).unwrap_or_else(|_| img.clone())
    }

    /// Get image statistics
    pub fn get_statistics(img: &OcrImage) -> ImageStatistics {
        img.statistics()
    }
}

/// Image enhancement configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageEnhancementConfig {
    /// Enable contrast enhancement
    pub enable_contrast_enhancement: bool,
    /// Contrast enhancement factor
    pub contrast_factor: f32,
    /// Enable noise reduction
    pub enable_noise_reduction: bool,
    /// Noise reduction strength
    pub noise_reduction_strength: f32,
    /// Enable sharpening
    pub enable_sharpening: bool,
    /// Sharpening strength
    pub sharpening_strength: f32,
    /// Enable deskewing
    pub enable_deskewing: bool,
    /// Deskewing angle threshold
    pub deskewing_threshold: f32,
}

impl Default for ImageEnhancementConfig {
    fn default() -> Self {
        Self {
            enable_contrast_enhancement: true,
            contrast_factor: 1.2,
            enable_noise_reduction: true,
            noise_reduction_strength: 0.5,
            enable_sharpening: false,
            sharpening_strength: 0.5,
            enable_deskewing: true,
            deskewing_threshold: 0.1,
        }
    }
}

/// Image preprocessing pipeline
pub struct ImagePreprocessingPipeline {
    /// Enhancement configuration
    config: ImageEnhancementConfig,
}

impl ImagePreprocessingPipeline {
    /// Create a new preprocessing pipeline
    pub fn new(config: ImageEnhancementConfig) -> Self {
        Self { config }
    }

    /// Process image through the pipeline
    pub fn process(&self, img: &OcrImage) -> ApiResult<OcrImage> {
        let mut processed = img.clone();

        // Apply contrast enhancement
        if self.config.enable_contrast_enhancement {
            processed = ImageProcessor::enhance_contrast(&processed, self.config.contrast_factor)?;
        }

        // Apply noise reduction
        if self.config.enable_noise_reduction {
            processed = ImageProcessor::reduce_noise(&processed)?;
        }

        // Apply sharpening
        if self.config.enable_sharpening {
            processed = ImageProcessor::sharpen(&processed)?;
        }

        // Apply deskewing
        if self.config.enable_deskewing {
            processed = ImageProcessor::deskew(&processed)?;
        }

        Ok(processed)
    }
}

/// Image quality assessment
pub struct ImageQualityAssessor;

impl ImageQualityAssessor {
    /// Assess image quality for OCR
    pub fn assess_quality(img: &OcrImage) -> ImageQualityScore {
        let stats = img.statistics();

        // Calculate quality metrics
        let contrast = Self::calculate_contrast(&stats);
        let sharpness = Self::calculate_sharpness(img);
        let noise_level = Self::calculate_noise_level(&stats);
        let resolution = Self::calculate_resolution_score(img);

        // Calculate overall quality score
        let overall_score = (contrast + sharpness + (1.0 - noise_level) + resolution) / 4.0;

        ImageQualityScore {
            overall_score,
            contrast,
            sharpness,
            noise_level,
            resolution,
            recommendations: Self::generate_recommendations(
                overall_score,
                contrast,
                sharpness,
                noise_level,
                resolution,
            ),
        }
    }

    /// Calculate contrast score
    fn calculate_contrast(stats: &ImageStatistics) -> f32 {
        if stats.max == stats.min {
            0.0
        } else {
            (stats.max - stats.min) as f32 / 255.0
        }
    }

    /// Calculate sharpness score
    fn calculate_sharpness(_img: &OcrImage) -> f32 {
        // TODO: Implement sharpness calculation using edge detection
        0.5 // Placeholder
    }

    /// Calculate noise level
    fn calculate_noise_level(stats: &ImageStatistics) -> f32 {
        // Simple noise estimation based on pixel count and variance
        if stats.pixel_count == 0 {
            0.0
        } else {
            // This is a simplified noise estimation
            // In practice, you'd use more sophisticated methods
            let variance = stats.mean * (1.0 - stats.mean / 255.0);
            (variance / 255.0).min(1.0)
        }
    }

    /// Calculate resolution score
    fn calculate_resolution_score(img: &OcrImage) -> f32 {
        // Score based on image dimensions and DPI
        let pixel_count = img.width * img.height;
        let dpi_score = (img.dpi as f32 / 300.0).min(1.0);
        let size_score = (pixel_count as f32 / (1920.0 * 1080.0)).min(1.0);

        (dpi_score + size_score) / 2.0
    }

    /// Generate quality improvement recommendations
    fn generate_recommendations(
        overall_score: f32,
        contrast: f32,
        sharpness: f32,
        noise_level: f32,
        resolution: f32,
    ) -> Vec<String> {
        let mut recommendations = Vec::new();

        if overall_score < 0.5 {
            recommendations.push(
                "Image quality is poor. Consider using a higher resolution image.".to_string(),
            );
        }

        if contrast < 0.3 {
            recommendations.push("Low contrast detected. Consider enhancing contrast.".to_string());
        }

        if sharpness < 0.3 {
            recommendations.push(
                "Image appears blurry. Consider sharpening or using a higher resolution image."
                    .to_string(),
            );
        }

        if noise_level > 0.7 {
            recommendations
                .push("High noise level detected. Consider applying noise reduction.".to_string());
        }

        if resolution < 0.5 {
            recommendations
                .push("Low resolution detected. Consider using a higher DPI image.".to_string());
        }

        if recommendations.is_empty() {
            recommendations.push("Image quality is good for OCR processing.".to_string());
        }

        recommendations
    }
}

/// Image quality score
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageQualityScore {
    /// Overall quality score (0.0 to 1.0)
    pub overall_score: f32,
    /// Contrast score (0.0 to 1.0)
    pub contrast: f32,
    /// Sharpness score (0.0 to 1.0)
    pub sharpness: f32,
    /// Noise level (0.0 to 1.0, higher is worse)
    pub noise_level: f32,
    /// Resolution score (0.0 to 1.0)
    pub resolution: f32,
    /// Quality improvement recommendations
    pub recommendations: Vec<String>,
}

impl ImageQualityScore {
    /// Check if image quality is acceptable for OCR
    pub fn is_acceptable(&self, threshold: f32) -> bool {
        self.overall_score >= threshold
    }

    /// Get quality grade
    pub fn get_grade(&self) -> QualityGrade {
        match self.overall_score {
            score if score >= 0.8 => QualityGrade::Excellent,
            score if score >= 0.6 => QualityGrade::Good,
            score if score >= 0.4 => QualityGrade::Fair,
            score if score >= 0.2 => QualityGrade::Poor,
            _ => QualityGrade::VeryPoor,
        }
    }
}

/// Quality grade enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QualityGrade {
    /// Excellent quality
    Excellent,
    /// Good quality
    Good,
    /// Fair quality
    Fair,
    /// Poor quality
    Poor,
    /// Very poor quality
    VeryPoor,
}