use std::{ collections::HashMap, sync::{Arc, Mutex, RwLock}, }; use anyhow::Result; use image::RgbImage; use serde::{Deserialize, Serialize}; use crate::{ config::{Config, LearnedConfig}, image_processing::{hash_image, extract_and_filter}, }; #[derive(Serialize, Deserialize, Debug)] pub struct OcrRegion { pub confidence: f64, pub value: String, } #[derive(Serialize, Deserialize, Debug)] struct OcrResult { regions: Vec, error: Option, } async fn run_ocr(image: &RgbImage, url: &str) -> Result> { let client = reqwest::Client::new(); let response = client .post(url) .body(crate::image_processing::to_png_bytes(image)) .send() .await?; if !response.status().is_success() { eprintln!("failed to run OCR query"); anyhow::bail!("failed to run OCR query") } let result: OcrResult = response.json().await?; let result = if result.regions.is_empty() { None } else { let mut buffer = String::new(); for r in &result.regions { buffer += &r.value; } Some(buffer) }; Ok(result) } #[tokio::main(flavor = "current_thread")] pub async fn ocr_all_regions( image: &RgbImage, config: Arc, learned: Arc, ocr_cache: Arc>>>, ) -> HashMap> { let results = Arc::new(Mutex::new(HashMap::new())); let mut handles = Vec::new(); for region in &config.ocr_regions { let filtered_image = extract_and_filter(image, region); let region = region.clone(); let results = results.clone(); let config = config.clone(); let learned = learned.clone(); let ocr_cache = ocr_cache.clone(); handles.push(tokio::spawn(async move { let filtered_image = filtered_image; let hash = hash_image(&filtered_image); let value = if let Some(learned_value) = learned.learned_images.get(&hash) { Some(learned_value.clone()) } else { let cached = { let locked = ocr_cache.read().unwrap(); locked.get(&hash).cloned() }; if let Some(cached) = cached { cached } else { match run_ocr(&filtered_image, &config.ocr_server_endpoint).await { Ok(v) => { if config.use_ocr_cache.unwrap_or(true) { ocr_cache.write().unwrap().insert(hash.clone(), v.clone()); } v } Err(_) => None } } }; results.lock().unwrap().insert(region.name, value); })); } for handle in handles { handle.await.expect("failed to join task in OCR"); } let results = results.lock().unwrap().clone(); results }