oar_ocr_core/models/detection/
rtdetr.rs1use crate::core::OCRError;
7use crate::core::inference::{OrtInfer, TensorInput};
8use crate::processors::{
9 DetResizeForTest, ImageScaleInfo, LimitType, NormalizeImage, TensorLayout,
10};
11use image::{DynamicImage, RgbImage};
12use ndarray::Array2;
13
14type RTDetrPreprocessArtifacts = (
15 ndarray::Array4<f32>,
16 Vec<ImageScaleInfo>,
17 Vec<[f32; 2]>,
18 Vec<[f32; 2]>,
19);
20type RTDetrPreprocessResult = Result<RTDetrPreprocessArtifacts, OCRError>;
21
22#[derive(Debug, Clone)]
24pub struct RTDetrPreprocessConfig {
25 pub image_shape: (u32, u32),
27 pub keep_ratio: bool,
29 pub limit_side_len: u32,
31 pub scale: f32,
33 pub mean: Vec<f32>,
35 pub std: Vec<f32>,
37}
38
39impl Default for RTDetrPreprocessConfig {
40 fn default() -> Self {
41 Self {
42 image_shape: (640, 640),
43 keep_ratio: false,
44 limit_side_len: 640,
45 scale: 1.0 / 255.0,
46 mean: vec![0.0, 0.0, 0.0],
48 std: vec![1.0, 1.0, 1.0],
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct RTDetrPostprocessConfig {
56 pub num_classes: usize,
58}
59
60#[derive(Debug, Clone)]
62pub struct RTDetrModelOutput {
63 pub predictions: ndarray::Array4<f32>,
66}
67
68#[derive(Debug)]
77pub struct RTDetrModel {
78 inference: OrtInfer,
79 resizer: DetResizeForTest,
80 normalizer: NormalizeImage,
81 _preprocess_config: RTDetrPreprocessConfig,
82}
83
84impl RTDetrModel {
85 pub fn new(
87 inference: OrtInfer,
88 preprocess_config: RTDetrPreprocessConfig,
89 ) -> Result<Self, OCRError> {
90 let resizer = DetResizeForTest::new(
92 None,
93 Some((
94 preprocess_config.image_shape.0,
95 preprocess_config.image_shape.1,
96 )),
97 Some(preprocess_config.keep_ratio),
98 Some(preprocess_config.limit_side_len),
99 Some(LimitType::Max),
100 None,
101 None,
102 );
103
104 let normalizer = NormalizeImage::with_color_order_from_rgb_stats(
107 Some(preprocess_config.scale),
108 preprocess_config.mean.clone(),
109 preprocess_config.std.clone(),
110 Some(TensorLayout::CHW),
111 crate::processors::types::ColorOrder::BGR,
112 )?;
113
114 Ok(Self {
115 inference,
116 resizer,
117 normalizer,
118 _preprocess_config: preprocess_config,
119 })
120 }
121
122 pub fn preprocess(&self, images: Vec<RgbImage>) -> RTDetrPreprocessResult {
130 let orig_shapes: Vec<[f32; 2]> = images
132 .iter()
133 .map(|img| [img.height() as f32, img.width() as f32])
134 .collect();
135
136 let dynamic_images: Vec<DynamicImage> =
138 images.into_iter().map(DynamicImage::ImageRgb8).collect();
139
140 let (resized_images, img_shapes) = self.resizer.apply(
142 dynamic_images,
143 None, None, None,
146 );
147
148 let resized_shapes: Vec<[f32; 2]> = resized_images
150 .iter()
151 .map(|img| [img.height() as f32, img.width() as f32])
152 .collect();
153
154 let batch_tensor = self.normalizer.normalize_batch_to(resized_images)?;
156
157 Ok((batch_tensor, img_shapes, orig_shapes, resized_shapes))
158 }
159
160 pub fn infer(
164 &self,
165 batch_tensor: &ndarray::Array4<f32>,
166 scale_factor: &Array2<f32>,
167 im_shape: &Array2<f32>,
168 ) -> Result<ndarray::Array4<f32>, OCRError> {
169 let inputs = vec![
170 ("image", TensorInput::Array4(batch_tensor)),
171 ("scale_factor", TensorInput::Array2(scale_factor)),
172 ("im_shape", TensorInput::Array2(im_shape)),
173 ];
174
175 let outputs = self
176 .inference
177 .infer(&inputs)
178 .map_err(|e| OCRError::Inference {
179 model_name: "RT-DETR".to_string(),
180 context: "failed to run inference".to_string(),
181 source: Box::new(e),
182 })?;
183
184 let output = outputs
186 .iter()
187 .find(|(name, _)| name == "fetch_name_0")
188 .or_else(|| outputs.first())
189 .ok_or_else(|| OCRError::InvalidInput {
190 message: "RT-DETR: no outputs found from model".to_string(),
191 })?;
192
193 let output_shape = output.1.shape();
194
195 match output_shape.len() {
197 2 => {
198 let output_array =
201 output
202 .1
203 .clone()
204 .try_into_array_f32()
205 .map_err(|e| OCRError::InvalidInput {
206 message: format!("Failed to extract output tensor: {}", e),
207 })?;
208 let num_boxes = output_shape[0] as usize;
209 let box_dim = output_shape[1] as usize;
210 let (data, _offset) = output_array.into_raw_vec_and_offset();
211 ndarray::Array::from_shape_vec((1, num_boxes, 1, box_dim), data).map_err(|e| {
212 OCRError::InvalidInput {
213 message: format!("Failed to reshape 2D output to 4D: {}", e),
214 }
215 })
216 }
217 4 => {
218 output
220 .1
221 .clone()
222 .try_into_array4_f32()
223 .map_err(|e| OCRError::InvalidInput {
224 message: format!("Failed to convert to 4D array: {}", e),
225 })
226 }
227 _ => Err(OCRError::InvalidInput {
228 message: format!(
229 "RT-DETR inference: expected 2D or 4D output, got {}D with shape {:?}",
230 output_shape.len(),
231 output_shape
232 ),
233 }),
234 }
235 }
236
237 pub fn postprocess(
242 &self,
243 predictions: ndarray::Array4<f32>,
244 _config: &RTDetrPostprocessConfig,
245 ) -> Result<RTDetrModelOutput, OCRError> {
246 Ok(RTDetrModelOutput { predictions })
247 }
248
249 pub fn forward(
251 &self,
252 images: Vec<RgbImage>,
253 config: &RTDetrPostprocessConfig,
254 ) -> Result<(RTDetrModelOutput, Vec<ImageScaleInfo>), OCRError> {
255 let (batch_tensor, img_shapes, _orig_shapes, resized_shapes) = self.preprocess(images)?;
256
257 let batch_size = batch_tensor.shape()[0];
258
259 let scale_data: Vec<f32> = img_shapes
261 .iter()
262 .flat_map(|shape| [shape.ratio_h, shape.ratio_w])
263 .collect();
264 let scale_factor = Array2::from_shape_vec((batch_size, 2), scale_data).map_err(|e| {
265 OCRError::InvalidInput {
266 message: format!("Failed to create scale_factor array: {}", e),
267 }
268 })?;
269
270 let im_shape_data: Vec<f32> = resized_shapes
272 .iter()
273 .flat_map(|shape| [shape[0], shape[1]])
274 .collect();
275 let im_shape = Array2::from_shape_vec((batch_size, 2), im_shape_data).map_err(|e| {
276 OCRError::InvalidInput {
277 message: format!("Failed to create im_shape array: {}", e),
278 }
279 })?;
280
281 let predictions = self.infer(&batch_tensor, &scale_factor, &im_shape)?;
282 let output = self.postprocess(predictions, config)?;
283 Ok((output, img_shapes))
284 }
285}
286
287#[derive(Debug, Default)]
289pub struct RTDetrModelBuilder {
290 preprocess_config: Option<RTDetrPreprocessConfig>,
291}
292
293impl RTDetrModelBuilder {
294 pub fn new() -> Self {
296 Self::default()
297 }
298
299 pub fn preprocess_config(mut self, config: RTDetrPreprocessConfig) -> Self {
301 self.preprocess_config = Some(config);
302 self
303 }
304
305 pub fn image_shape(mut self, height: u32, width: u32) -> Self {
307 let mut config = self.preprocess_config.unwrap_or_default();
308 config.image_shape = (height, width);
309 config.limit_side_len = height.max(width);
310 self.preprocess_config = Some(config);
311 self
312 }
313
314 pub fn build(self, inference: OrtInfer) -> Result<RTDetrModel, OCRError> {
316 let preprocess_config = self.preprocess_config.unwrap_or_default();
317 RTDetrModel::new(inference, preprocess_config)
318 }
319}