1use image::{Rgb, RgbImage};
7use imageproc::geometric_transformations::{Border, 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 Border::Constant(Rgb([255u8, 255u8, 255u8])), );
170
171 Some(Arc::new(rotated))
172 }
173 (img_opt, _) => img_opt,
174 }
175 })
176 .collect();
177
178 Ok(rotated_images)
179 }
180
181 fn name(&self) -> &str {
182 "ImageRotation"
183 }
184}
185
186#[derive(Debug)]
191pub struct ChainProcessor<T> {
192 processors: Vec<Box<dyn EdgeProcessor<Input = T, Output = T>>>,
193}
194
195impl<T> ChainProcessor<T> {
196 pub fn new(processors: Vec<Box<dyn EdgeProcessor<Input = T, Output = T>>>) -> Self {
198 Self { processors }
199 }
200}
201
202impl<T> EdgeProcessor for ChainProcessor<T>
203where
204 T: Debug + Send + Sync,
205{
206 type Input = T;
207 type Output = T;
208
209 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
210 if self.processors.is_empty() {
211 return Err(OCRError::ConfigError {
212 message: "Empty processor chain".to_string(),
213 });
214 }
215
216 let mut current = input;
218
219 for processor in &self.processors {
220 current = processor.process(current)?;
221 }
222
223 Ok(current)
224 }
225
226 fn name(&self) -> &str {
227 "Chain"
228 }
229}
230
231type TextCroppingOutput = Box<
233 dyn EdgeProcessor<
234 Input = (Arc<RgbImage>, Vec<BoundingBox>),
235 Output = Vec<Option<Arc<RgbImage>>>,
236 >,
237>;
238
239type ImageRotationOutput = Box<
241 dyn EdgeProcessor<
242 Input = (Vec<Option<Arc<RgbImage>>>, Vec<Option<f32>>),
243 Output = Vec<Option<Arc<RgbImage>>>,
244 >,
245>;
246
247pub struct EdgeProcessorFactory;
249
250impl EdgeProcessorFactory {
251 pub fn create_text_cropping(handle_rotation: bool) -> TextCroppingOutput {
253 Box::new(TextCroppingProcessor::new(handle_rotation))
254 }
255
256 pub fn create_image_rotation(auto_rotate: bool) -> ImageRotationOutput {
258 Box::new(ImageRotationProcessor::new(auto_rotate))
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn test_text_cropping_processor_creation() {
268 let processor = TextCroppingProcessor::new(true);
269 assert_eq!(processor.name(), "TextCropping");
270 }
271
272 #[test]
273 fn test_image_rotation_processor_creation() {
274 let processor = ImageRotationProcessor::new(true);
275 assert_eq!(processor.name(), "ImageRotation");
276 }
277
278 #[test]
279 fn test_edge_processor_config_serialization() -> Result<(), Box<dyn std::error::Error>> {
280 let config = EdgeProcessorConfig::TextCropping {
281 handle_rotation: true,
282 };
283
284 let json = serde_json::to_string(&config)?;
285 assert!(json.contains("TextCropping"));
286
287 let deserialized: EdgeProcessorConfig = serde_json::from_str(&json)?;
288 if let EdgeProcessorConfig::TextCropping { handle_rotation } = deserialized {
289 assert!(handle_rotation);
290 } else {
291 panic!("Wrong variant");
292 }
293 Ok(())
294 }
295
296 #[test]
297 fn test_image_rotation_processor_rotates_images() -> Result<(), OCRError> {
298 let processor = ImageRotationProcessor::new(true);
299
300 let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
302
303 let images = vec![Some(img.clone())];
305 let angles = vec![Some(45.0)]; let result = processor.process((images, angles))?;
308
309 assert_eq!(result.len(), 1);
311 assert!(result[0].is_some());
312
313 let Some(rotated) = result[0].as_ref() else {
315 panic!("expected rotated image to be Some");
316 };
317 assert!(rotated.width() >= 10 || rotated.height() >= 10);
319 Ok(())
320 }
321
322 #[test]
323 fn test_image_rotation_processor_skips_small_angles() -> Result<(), OCRError> {
324 let processor = ImageRotationProcessor::new(true);
325
326 let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
327 let images = vec![Some(img.clone())];
328 let angles = vec![Some(0.05)]; let result = processor.process((images, angles))?;
331
332 assert_eq!(result.len(), 1);
334 assert!(result[0].is_some());
335 let Some(output) = result[0].as_ref() else {
336 panic!("expected output image to be Some");
337 };
338 assert_eq!(output.dimensions(), img.dimensions());
339 Ok(())
340 }
341
342 #[test]
343 fn test_image_rotation_processor_disabled() -> Result<(), OCRError> {
344 let processor = ImageRotationProcessor::new(false); let img = Arc::new(RgbImage::from_pixel(10, 10, Rgb([255u8, 255u8, 255u8])));
347 let images = vec![Some(img.clone())];
348 let angles = vec![Some(45.0)];
349
350 let result = processor.process((images, angles))?;
351
352 assert_eq!(result.len(), 1);
354 assert!(result[0].is_some());
355 let Some(output) = result[0].as_ref() else {
356 panic!("expected output image to be Some");
357 };
358 assert_eq!(output.dimensions(), img.dimensions());
359 Ok(())
360 }
361
362 #[derive(Debug)]
364 struct AddProcessor {
365 value: i32,
366 }
367
368 impl EdgeProcessor for AddProcessor {
369 type Input = i32;
370 type Output = i32;
371
372 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
373 Ok(input + self.value)
374 }
375
376 fn name(&self) -> &str {
377 "Add"
378 }
379 }
380
381 #[derive(Debug)]
383 struct MultiplyProcessor {
384 value: i32,
385 }
386
387 impl EdgeProcessor for MultiplyProcessor {
388 type Input = i32;
389 type Output = i32;
390
391 fn process(&self, input: Self::Input) -> Result<Self::Output, OCRError> {
392 Ok(input * self.value)
393 }
394
395 fn name(&self) -> &str {
396 "Multiply"
397 }
398 }
399
400 #[test]
401 fn test_chain_processor_single_processor() -> Result<(), OCRError> {
402 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> =
403 vec![Box::new(AddProcessor { value: 5 })];
404
405 let chain = ChainProcessor::new(processors);
406 let result = chain.process(10)?;
407
408 assert_eq!(result, 15);
410 Ok(())
411 }
412
413 #[test]
414 fn test_chain_processor_multiple_processors() -> Result<(), OCRError> {
415 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
416 Box::new(AddProcessor { value: 5 }), Box::new(MultiplyProcessor { value: 2 }), Box::new(AddProcessor { value: 10 }), ];
420
421 let chain = ChainProcessor::new(processors);
422 let result = chain.process(10)?;
423
424 assert_eq!(result, 40);
426 Ok(())
427 }
428
429 #[test]
430 fn test_chain_processor_empty_chain() {
431 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![];
432
433 let chain = ChainProcessor::new(processors);
434 let result = chain.process(10);
435
436 assert!(result.is_err());
438 if let Err(OCRError::ConfigError { message }) = result {
439 assert_eq!(message, "Empty processor chain");
440 } else {
441 panic!("Expected ConfigError");
442 }
443 }
444
445 #[test]
446 fn test_chain_processor_name() {
447 let processors: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> =
448 vec![Box::new(AddProcessor { value: 5 })];
449
450 let chain = ChainProcessor::new(processors);
451 assert_eq!(chain.name(), "Chain");
452 }
453
454 #[test]
455 fn test_chain_processor_order_matters() -> Result<(), OCRError> {
456 let processors1: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
458 Box::new(AddProcessor { value: 5 }), Box::new(MultiplyProcessor { value: 2 }), ];
461
462 let processors2: Vec<Box<dyn EdgeProcessor<Input = i32, Output = i32>>> = vec![
463 Box::new(MultiplyProcessor { value: 2 }), Box::new(AddProcessor { value: 5 }), ];
466
467 let chain1 = ChainProcessor::new(processors1);
468 let chain2 = ChainProcessor::new(processors2);
469
470 let result1 = chain1.process(10)?;
471 let result2 = chain2.process(10)?;
472
473 assert_eq!(result1, 30);
475 assert_eq!(result2, 25);
477 assert_ne!(result1, result2);
479 Ok(())
480 }
481}