1use image::{Rgb, RgbImage};
7use imageproc::geometric_transformations::{Interpolation, rotate_about_center};
8use oar_ocr_core::core::OCRError;
9use oar_ocr_core::processors::BoundingBox;
10use oar_ocr_core::utils::BBoxCrop;
11use serde::{Deserialize, Serialize};
12use std::fmt::Debug;
13use std::sync::Arc;
14
15pub trait EdgeProcessor: Debug + Send + Sync {
17 type Input;
19
20 type Output;
22
23 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError>;
25
26 fn name(&self) -> &str;
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(tag = "type")]
33pub enum EdgeProcessorConfig {
34 TextCropping {
36 #[serde(default = "default_true")]
38 handle_rotation: bool,
39 },
40
41 PerspectiveTransform {
43 target_width: Option<u32>,
45 target_height: Option<u32>,
47 },
48
49 ImageRotation {
51 #[serde(default = "default_true")]
53 auto_rotate: bool,
54 },
55
56 ImageResize {
58 width: u32,
60 height: u32,
62 #[serde(default)]
64 maintain_aspect_ratio: bool,
65 },
66
67 Chain {
69 processors: Vec<EdgeProcessorConfig>,
71 },
72}
73
74fn default_true() -> bool {
75 true
76}
77
78#[derive(Debug)]
80pub struct TextCroppingProcessor {
81 pub(crate) handle_rotation: bool,
82}
83
84impl TextCroppingProcessor {
85 pub fn new(handle_rotation: bool) -> Self {
86 Self { handle_rotation }
87 }
88
89 fn crop_single(&self, image: &RgbImage, bbox: &BoundingBox) -> Result<RgbImage, OCRError> {
91 if self.handle_rotation && bbox.points.len() == 4 {
92 BBoxCrop::crop_rotated_bounding_box(image, bbox)
94 } else {
95 BBoxCrop::crop_bounding_box(image, bbox)
97 }
98 }
99}
100
101impl EdgeProcessor for TextCroppingProcessor {
102 type Input = (Arc<RgbImage>, Vec<BoundingBox>);
103 type Output = Vec<Option<Arc<RgbImage>>>;
104
105 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
106 let (image, bboxes) = input;
107
108 let cropped_images: Vec<Option<Arc<RgbImage>>> = bboxes
109 .iter()
110 .map(|bbox| {
111 self.crop_single(&image, bbox)
112 .map(|img| Some(Arc::new(img)))
113 .unwrap_or_else(|_e| {
114 None
116 })
117 })
118 .collect();
119
120 Ok(cropped_images)
121 }
122
123 fn name(&self) -> &str {
124 "TextCropping"
125 }
126}
127
128#[derive(Debug)]
130pub struct ImageRotationProcessor {
131 auto_rotate: bool,
132}
133
134impl ImageRotationProcessor {
135 pub fn new(auto_rotate: bool) -> Self {
136 Self { auto_rotate }
137 }
138}
139
140impl EdgeProcessor for ImageRotationProcessor {
141 type Input = (Vec<Option<Arc<RgbImage>>>, Vec<Option<f32>>);
142 type Output = Vec<Option<Arc<RgbImage>>>;
143
144 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
145 let (images, angles) = input;
146
147 if !self.auto_rotate {
148 return Ok(images);
149 }
150
151 let rotated_images: Vec<Option<Arc<RgbImage>>> = images
152 .into_iter()
153 .zip(angles.iter())
154 .map(|(img_opt, angle_opt)| {
155 match (img_opt, angle_opt) {
156 (Some(img), Some(angle)) if angle.abs() > 0.1 => {
157 let angle_radians = -angle.to_radians(); let rotated = rotate_about_center(
163 &img,
164 angle_radians,
165 Interpolation::Bilinear,
166 Rgb([255u8, 255u8, 255u8]), );
168
169 Some(Arc::new(rotated))
170 }
171 (img_opt, _) => img_opt,
172 }
173 })
174 .collect();
175
176 Ok(rotated_images)
177 }
178
179 fn name(&self) -> &str {
180 "ImageRotation"
181 }
182}
183
184#[derive(Debug)]
189pub struct ChainProcessor<T> {
190 processors: Vec<Box<dyn EdgeProcessor<Input = T, Output = T>>>,
191}
192
193impl<T> ChainProcessor<T> {
194 pub fn new(processors: Vec<Box<dyn EdgeProcessor<Input = T, Output = T>>>) -> Self {
196 Self { processors }
197 }
198}
199
200impl<T> EdgeProcessor for ChainProcessor<T>
201where
202 T: Debug + Send + Sync,
203{
204 type Input = T;
205 type Output = T;
206
207 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
208 if self.processors.is_empty() {
209 return Err(OCRError::ConfigError {
210 message: "Empty processor chain".to_string(),
211 });
212 }
213
214 let mut current = input;
216
217 for processor in &self.processors {
218 current = processor.process(current)?;
219 }
220
221 Ok(current)
222 }
223
224 fn name(&self) -> &str {
225 "Chain"
226 }
227}
228
229type TextCroppingOutput = Box<
231 dyn EdgeProcessor<
232 Input = (Arc<RgbImage>, Vec<BoundingBox>),
233 Output = Vec<Option<Arc<RgbImage>>>,
234 >,
235>;
236
237type ImageRotationOutput = Box<
239 dyn EdgeProcessor<
240 Input = (Vec<Option<Arc<RgbImage>>>, Vec<Option<f32>>),
241 Output = Vec<Option<Arc<RgbImage>>>,
242 >,
243>;
244
245pub struct EdgeProcessorFactory;
247
248impl EdgeProcessorFactory {
249 pub fn create_text_cropping(handle_rotation: bool) -> TextCroppingOutput {
251 Box::new(TextCroppingProcessor::new(handle_rotation))
252 }
253
254 pub fn create_image_rotation(auto_rotate: bool) -> ImageRotationOutput {
256 Box::new(ImageRotationProcessor::new(auto_rotate))
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn test_text_cropping_processor_creation() {
266 let processor = TextCroppingProcessor::new(true);
267 assert_eq!(processor.name(), "TextCropping");
268 }
269
270 #[test]
271 fn test_image_rotation_processor_creation() {
272 let processor = ImageRotationProcessor::new(true);
273 assert_eq!(processor.name(), "ImageRotation");
274 }
275
276 #[test]
277 fn test_edge_processor_config_serialization() -> Result<(), Box<dyn std::error::Error>> {
278 let config = EdgeProcessorConfig::TextCropping {
279 handle_rotation: true,
280 };
281
282 let json = serde_json::to_string(&config)?;
283 assert!(json.contains("TextCropping"));
284
285 let deserialized: EdgeProcessorConfig = serde_json::from_str(&json)?;
286 if let EdgeProcessorConfig::TextCropping { handle_rotation } = deserialized {
287 assert!(handle_rotation);
288 } else {
289 panic!("Wrong variant");
290 }
291 Ok(())
292 }
293
294 #[test]
295 fn test_image_rotation_processor_rotates_images() -> Result<(), OCRError> {
296 let processor = ImageRotationProcessor::new(true);
297
298 let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
300
301 let images = vec![Some(img.clone())];
303 let angles = vec![Some(45.0)]; let result = processor.process((images, angles))?;
306
307 assert_eq!(result.len(), 1);
309 assert!(result[0].is_some());
310
311 let Some(rotated) = result[0].as_ref() else {
313 panic!("expected rotated image to be Some");
314 };
315 assert!(rotated.width() >= 10 || rotated.height() >= 10);
317 Ok(())
318 }
319
320 #[test]
321 fn test_image_rotation_processor_skips_small_angles() -> Result<(), OCRError> {
322 let processor = ImageRotationProcessor::new(true);
323
324 let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
325 let images = vec![Some(img.clone())];
326 let angles = vec![Some(0.05)]; let result = processor.process((images, angles))?;
329
330 assert_eq!(result.len(), 1);
332 assert!(result[0].is_some());
333 let Some(output) = result[0].as_ref() else {
334 panic!("expected output image to be Some");
335 };
336 assert_eq!(output.dimensions(), img.dimensions());
337 Ok(())
338 }
339
340 #[test]
341 fn test_image_rotation_processor_disabled() -> Result<(), OCRError> {
342 let processor = ImageRotationProcessor::new(false); let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
345 let images = vec![Some(img.clone())];
346 let angles = vec![Some(45.0)];
347
348 let result = processor.process((images, angles))?;
349
350 assert_eq!(result.len(), 1);
352 assert!(result[0].is_some());
353 let Some(output) = result[0].as_ref() else {
354 panic!("expected output image to be Some");
355 };
356 assert_eq!(output.dimensions(), img.dimensions());
357 Ok(())
358 }
359
360 #[derive(Debug)]
362 struct AddProcessor {
363 value: i32,
364 }
365
366 impl EdgeProcessor for AddProcessor {
367 type Input = i32;
368 type Output = i32;
369
370 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
371 Ok(input + self.value)
372 }
373
374 fn name(&self) -> &str {
375 "Add"
376 }
377 }
378
379 #[derive(Debug)]
381 struct MultiplyProcessor {
382 value: i32,
383 }
384
385 impl EdgeProcessor for MultiplyProcessor {
386 type Input = i32;
387 type Output = i32;
388
389 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
390 Ok(input * self.value)
391 }
392
393 fn name(&self) -> &str {
394 "Multiply"
395 }
396 }
397
398 #[test]
399 fn test_chain_processor_single_processor() -> Result<(), OCRError> {
400 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> =
401 vec![Box::new(AddProcessor { value: 5 })];
402
403 let chain = ChainProcessor::new(processors);
404 let result = chain.process(10)?;
405
406 assert_eq!(result, 15);
408 Ok(())
409 }
410
411 #[test]
412 fn test_chain_processor_multiple_processors() -> Result<(), OCRError> {
413 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
414 Box::new(AddProcessor { value: 5 }), Box::new(MultiplyProcessor { value: 2 }), Box::new(AddProcessor { value: 10 }), ];
418
419 let chain = ChainProcessor::new(processors);
420 let result = chain.process(10)?;
421
422 assert_eq!(result, 40);
424 Ok(())
425 }
426
427 #[test]
428 fn test_chain_processor_empty_chain() {
429 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![];
430
431 let chain = ChainProcessor::new(processors);
432 let result = chain.process(10);
433
434 assert!(result.is_err());
436 if let Err(OCRError::ConfigError { message }) = result {
437 assert_eq!(message, "Empty processor chain");
438 } else {
439 panic!("Expected ConfigError");
440 }
441 }
442
443 #[test]
444 fn test_chain_processor_name() {
445 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> =
446 vec![Box::new(AddProcessor { value: 5 })];
447
448 let chain = ChainProcessor::new(processors);
449 assert_eq!(chain.name(), "Chain");
450 }
451
452 #[test]
453 fn test_chain_processor_order_matters() -> Result<(), OCRError> {
454 let processors1: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
456 Box::new(AddProcessor { value: 5 }), Box::new(MultiplyProcessor { value: 2 }), ];
459
460 let processors2: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
461 Box::new(MultiplyProcessor { value: 2 }), Box::new(AddProcessor { value: 5 }), ];
464
465 let chain1 = ChainProcessor::new(processors1);
466 let chain2 = ChainProcessor::new(processors2);
467
468 let result1 = chain1.process(10)?;
469 let result2 = chain2.process(10)?;
470
471 assert_eq!(result1, 30);
473 assert_eq!(result2, 25);
475 assert_ne!(result1, result2);
477 Ok(())
478 }
479}