oar_ocr_core/core/traits/
task.rs1use crate::core::OCRError;
8use image::RgbImage;
9use std::fmt::Debug;
10use std::sync::Arc;
11
12crate::with_task_registry!(crate::impl_task_type_enum);
14
15pub trait Task: Send + Sync + Debug {
20 type Config: Send + Sync + Debug + Clone;
22
23 type Input: Send + Sync + Debug;
25
26 type Output: Send + Sync + Debug;
28
29 fn task_type(&self) -> TaskType;
31
32 fn validate_input(&self, input: &Self::Input) -> Result<(), OCRError>;
42
43 fn validate_output(&self, output: &Self::Output) -> Result<(), OCRError>;
53
54 fn empty_output(&self) -> Self::Output;
56
57 fn description(&self) -> String {
59 format!("Task: {}", self.task_type().name())
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct ImageTaskInput {
66 pub images: Vec<Arc<RgbImage>>,
68 pub metadata: Vec<Option<String>>,
70}
71
72impl ImageTaskInput {
73 pub fn new(images: Vec<RgbImage>) -> Self {
75 let count = images.len();
76 Self {
77 images: images.into_iter().map(Arc::new).collect(),
78 metadata: vec![None; count],
79 }
80 }
81
82 pub fn from_arc_images(images: Vec<Arc<RgbImage>>) -> Self {
84 let count = images.len();
85 Self {
86 images,
87 metadata: vec![None; count],
88 }
89 }
90
91 pub fn with_metadata(images: Vec<RgbImage>, metadata: Vec<Option<String>>) -> Self {
93 Self {
94 images: images.into_iter().map(Arc::new).collect(),
95 metadata,
96 }
97 }
98
99 pub fn into_owned_images(self) -> Vec<RgbImage> {
104 self.images
105 .into_iter()
106 .map(|img| Arc::try_unwrap(img).unwrap_or_else(|img| (*img).clone()))
107 .collect()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use std::sync::Arc;
115
116 #[test]
117 fn test_task_type_name() {
118 assert_eq!(TaskType::TextDetection.name(), "text_detection");
119 assert_eq!(TaskType::TextRecognition.name(), "text_recognition");
120 }
121
122 #[test]
123 fn test_image_task_input_creation() {
124 let images = vec![RgbImage::new(100, 100), RgbImage::new(200, 200)];
125 let input = ImageTaskInput::new(images.clone());
126
127 assert_eq!(input.images.len(), 2);
128 assert_eq!(input.metadata.len(), 2);
129 assert!(input.metadata.iter().all(|m| m.is_none()));
130 }
131
132 #[test]
133 fn test_image_task_input_from_owned() {
134 let images = vec![RgbImage::new(100, 100), RgbImage::new(200, 200)];
135 let input = ImageTaskInput::new(images);
136
137 assert_eq!(input.images.len(), 2);
138 assert_eq!(input.metadata.len(), 2);
139 assert!(input.metadata.iter().all(|m| m.is_none()));
140 }
141
142 #[test]
143 fn test_into_owned_images_reuses_unique_arc() {
144 let mut image = RgbImage::new(2, 1);
145 image.put_pixel(0, 0, image::Rgb([1, 2, 3]));
146 let input = ImageTaskInput::from_arc_images(vec![Arc::new(image)]);
147
148 let owned = input.into_owned_images();
149
150 assert_eq!(owned.len(), 1);
151 assert_eq!(owned[0].get_pixel(0, 0).0, [1, 2, 3]);
152 }
153
154 #[test]
155 fn test_into_owned_images_clones_when_arc_is_shared() {
156 let mut image = RgbImage::new(2, 1);
157 image.put_pixel(1, 0, image::Rgb([9, 8, 7]));
158 let shared = Arc::new(image);
159 let input = ImageTaskInput::from_arc_images(vec![Arc::clone(&shared)]);
160
161 let owned = input.into_owned_images();
162
163 assert_eq!(Arc::strong_count(&shared), 1);
164 assert_eq!(owned.len(), 1);
165 assert_eq!(owned[0].get_pixel(1, 0).0, [9, 8, 7]);
166 assert_eq!(shared.get_pixel(1, 0).0, [9, 8, 7]);
167 }
168}