Skip to main content

ppocr_rs/
db_net.rs

1use crate::{
2    base_net::BaseNet,
3    ocr_error::OcrError,
4    ocr_result::{self, TextBox},
5    ocr_utils::OcrUtils,
6    scale_param::ScaleParam,
7};
8use geo_clipper::{Clipper, EndType, JoinType};
9use geo_types::{Coord, LineString, Polygon};
10use ort::{inputs, session::SessionOutputs};
11use ort::{session::Session, value::Tensor};
12use std::cmp::Ordering;
13
14const MEAN_VALUES: [f32; 3] = [
15    0.485_f32 * 255_f32,
16    0.456_f32 * 255_f32,
17    0.406_f32 * 255_f32,
18];
19const NORM_VALUES: [f32; 3] = [
20    1.0_f32 / 0.229_f32 / 255.0_f32,
21    1.0_f32 / 0.224_f32 / 255.0_f32,
22    1.0_f32 / 0.225_f32 / 255.0_f32,
23];
24
25#[derive(Debug)]
26pub struct DbNet {
27    session: Option<Session>,
28    input_names: Vec<String>,
29}
30
31impl BaseNet for DbNet {
32    fn new() -> Self {
33        Self {
34            session: None,
35            input_names: Vec::new(),
36        }
37    }
38
39    fn set_input_names(&mut self, input_names: Vec<String>) {
40        self.input_names = input_names;
41    }
42
43    fn set_session(&mut self, session: Option<Session>) {
44        self.session = session;
45    }
46}
47
48impl DbNet {
49    pub fn get_text_boxes(
50        &mut self,
51        img_src: &image::RgbImage,
52        scale: &ScaleParam,
53        box_score_thresh: f32,
54        box_thresh: f32,
55        un_clip_ratio: f32,
56    ) -> Result<Vec<TextBox>, OcrError> {
57        let Some(session) = &mut self.session else {
58            return Err(OcrError::SessionNotInitialized);
59        };
60
61        let src_resize = image::imageops::resize(
62            img_src,
63            scale.dst_width,
64            scale.dst_height,
65            image::imageops::FilterType::Triangle,
66        );
67
68        let input_tensors =
69            OcrUtils::substract_mean_normalize(&src_resize, &MEAN_VALUES, &NORM_VALUES);
70
71        let tensor = Tensor::from_array(input_tensors)?;
72
73        // [downport rc.11→rc.9] in rc.9 il macro `inputs![...]` ritorna
74        // un `Result<Vec<(Cow<str>, SessionInputValue)>>` che va `?`-unwrapped
75        // prima di passare a `session.run`.
76        let outputs = session.run(inputs![self.input_names[0].clone() => tensor]?)?;
77
78        let text_boxes = Self::get_text_boxes_core(
79            &outputs,
80            src_resize.height(),
81            src_resize.width(),
82            &ScaleParam::new(
83                scale.src_width,
84                scale.src_height,
85                scale.dst_width,
86                scale.dst_height,
87                scale.scale_width,
88                scale.scale_height,
89            ),
90            box_score_thresh,
91            box_thresh,
92            un_clip_ratio,
93        )?;
94
95        Ok(text_boxes)
96    }
97
98    fn get_text_boxes_core(
99        output_tensor: &SessionOutputs,
100        rows: u32,
101        cols: u32,
102        s: &ScaleParam,
103        box_score_thresh: f32,
104        box_thresh: f32,
105        un_clip_ratio: f32,
106    ) -> Result<Vec<TextBox>, OcrError> {
107        let max_side_thresh = 3.0;
108        let mut rs_boxes = Vec::new();
109
110        let (_, red_data) = output_tensor.iter().next().unwrap();
111
112        // [downport rc.11→rc.9] in rc.11 try_extract_tensor ritorna `(Vec<i64> shape, &[T] data)`,
113        // in rc.9 ritorna `ArrayViewD<T>`. Compat helper: vedi src/compat.rs::tensor_to_vec.
114        let pred_data: Vec<f32> = crate::compat::tensor_to_vec_f32(&red_data)?;
115
116        let cbuf_data: Vec<u8> = pred_data
117            .iter()
118            .map(|pixel| (pixel * 255.0) as u8)
119            .collect();
120
121        let pred_img: image::ImageBuffer<image::Luma<f32>, Vec<f32>> =
122            image::ImageBuffer::from_vec(cols, rows, pred_data).unwrap();
123
124        let cbuf_img = image::GrayImage::from_vec(cols, rows, cbuf_data).unwrap();
125
126        let threshold_img = imageproc::contrast::threshold(
127            &cbuf_img,
128            (box_thresh * 255.0) as u8,
129            imageproc::contrast::ThresholdType::Binary,
130        );
131
132        let dilate_img = imageproc::morphology::dilate(
133            &threshold_img,
134            imageproc::distance_transform::Norm::LInf,
135            1,
136        );
137
138        let img_contours: Vec<imageproc::contours::Contour<i32>> =
139            imageproc::contours::find_contours(&dilate_img);
140
141        for contour in img_contours {
142            if contour.points.len() <= 2 {
143                continue;
144            }
145
146            let mut max_side = 0.0;
147            let min_box = Self::get_mini_box(&contour.points, &mut max_side)?;
148            if max_side < max_side_thresh {
149                continue;
150            }
151
152            let score = Self::get_score(&contour, &pred_img)?;
153            if score < box_score_thresh {
154                continue;
155            }
156
157            let clip_box = Self::unclip(&min_box, un_clip_ratio)?;
158            if clip_box.is_empty() {
159                continue;
160            }
161
162            let mut clip_contour = Vec::new();
163            for point in &clip_box {
164                clip_contour.push(*point);
165            }
166
167            let mut max_side_clip = 0.0;
168            let clip_min_box = Self::get_mini_box(&clip_contour, &mut max_side_clip)?;
169            if max_side_clip < max_side_thresh + 2.0 {
170                continue;
171            }
172
173            let mut final_points = Vec::new();
174            for item in clip_min_box {
175                let x = (item.x / s.scale_width) as u32;
176                let ptx = x.min(s.src_width);
177
178                let y = (item.y / s.scale_height) as u32;
179                let pty = y.min(s.src_height);
180
181                final_points.push(ocr_result::Point { x: ptx, y: pty });
182            }
183
184            let text_box = TextBox {
185                score,
186                points: final_points,
187            };
188
189            rs_boxes.push(text_box);
190        }
191
192        Ok(rs_boxes)
193    }
194
195    fn get_mini_box(
196        contour_points: &[imageproc::point::Point<i32>],
197        min_edge_size: &mut f32,
198    ) -> Result<Vec<imageproc::point::Point<f32>>, OcrError> {
199        let rect = imageproc::geometry::min_area_rect(contour_points);
200
201        let mut rect_points: Vec<imageproc::point::Point<f32>> = rect
202            .iter()
203            .map(|p| imageproc::point::Point::new(p.x as f32, p.y as f32))
204            .collect();
205
206        let width = ((rect_points[0].x - rect_points[1].x).powi(2)
207            + (rect_points[0].y - rect_points[1].y).powi(2))
208        .sqrt();
209        let height = ((rect_points[1].x - rect_points[2].x).powi(2)
210            + (rect_points[1].y - rect_points[2].y).powi(2))
211        .sqrt();
212
213        *min_edge_size = width.min(height);
214
215        rect_points.sort_by(|a, b| {
216            if a.x > b.x {
217                return Ordering::Greater;
218            }
219            if a.x == b.x {
220                return Ordering::Equal;
221            }
222            Ordering::Less
223        });
224
225        let mut box_points = Vec::new();
226        let index_1;
227        let index_4;
228        if rect_points[1].y > rect_points[0].y {
229            index_1 = 0;
230            index_4 = 1;
231        } else {
232            index_1 = 1;
233            index_4 = 0;
234        }
235
236        let index_2;
237        let index_3;
238        if rect_points[3].y > rect_points[2].y {
239            index_2 = 2;
240            index_3 = 3;
241        } else {
242            index_2 = 3;
243            index_3 = 2;
244        }
245
246        box_points.push(rect_points[index_1]);
247        box_points.push(rect_points[index_2]);
248        box_points.push(rect_points[index_3]);
249        box_points.push(rect_points[index_4]);
250
251        Ok(box_points)
252    }
253
254    fn get_score(
255        contour: &imageproc::contours::Contour<i32>,
256        f_map_mat: &image::ImageBuffer<image::Luma<f32>, Vec<f32>>,
257    ) -> Result<f32, OcrError> {
258        // 初始化边界值
259        let mut xmin = i32::MAX;
260        let mut xmax = i32::MIN;
261        let mut ymin = i32::MAX;
262        let mut ymax = i32::MIN;
263
264        // 找到轮廓的边界框
265        for point in contour.points.iter() {
266            let x = point.x;
267            let y = point.y;
268
269            if x < xmin {
270                xmin = x;
271            }
272            if x > xmax {
273                xmax = x;
274            }
275            if y < ymin {
276                ymin = y;
277            }
278            if y > ymax {
279                ymax = y;
280            }
281        }
282
283        let width = f_map_mat.width() as i32;
284        let height = f_map_mat.height() as i32;
285
286        xmin = xmin.max(0).min(width - 1);
287        xmax = xmax.max(0).min(width - 1);
288        ymin = ymin.max(0).min(height - 1);
289        ymax = ymax.max(0).min(height - 1);
290
291        let roi_width = xmax - xmin + 1;
292        let roi_height = ymax - ymin + 1;
293
294        if roi_width <= 0 || roi_height <= 0 {
295            return Ok(0.0);
296        }
297
298        let mut mask = image::GrayImage::new(roi_width as u32, roi_height as u32);
299
300        let mut pts = Vec::<imageproc::point::Point<i32>>::new();
301        for point in contour.points.iter() {
302            pts.push(imageproc::point::Point::new(point.x - xmin, point.y - ymin));
303        }
304
305        imageproc::drawing::draw_polygon_mut(&mut mask, pts.as_slice(), image::Luma([255]));
306
307        let cropped_img = image::imageops::crop_imm(
308            f_map_mat,
309            xmin as u32,
310            ymin as u32,
311            roi_width as u32,
312            roi_height as u32,
313        )
314        .to_image();
315
316        let mean = OcrUtils::calculate_mean_with_mask(&cropped_img, &mask);
317
318        Ok(mean)
319    }
320
321    fn unclip(
322        box_points: &[imageproc::point::Point<f32>],
323        unclip_ratio: f32,
324    ) -> Result<Vec<imageproc::point::Point<i32>>, OcrError> {
325        let points_arr = box_points.to_vec();
326
327        let clip_rect_width = ((points_arr[0].x - points_arr[1].x).powi(2)
328            + (points_arr[0].y - points_arr[1].y).powi(2))
329        .sqrt();
330        let clip_rect_height = ((points_arr[1].x - points_arr[2].x).powi(2)
331            + (points_arr[1].y - points_arr[2].y).powi(2))
332        .sqrt();
333
334        if clip_rect_height < 1.001 && clip_rect_width < 1.001 {
335            return Ok(Vec::new());
336        }
337
338        let mut the_cliper_pts = Vec::new();
339        for pt in box_points {
340            let a1 = Coord {
341                x: pt.x as f64,
342                y: pt.y as f64,
343            };
344            the_cliper_pts.push(a1);
345        }
346
347        let area = Self::signed_polygon_area(box_points).abs();
348        let length = Self::length_of_points(box_points);
349        let distance = area * unclip_ratio / length as f32;
350
351        let co = Polygon::new(LineString::new(the_cliper_pts), vec![]);
352        let solution = co
353            .offset(
354                distance as f64,
355                JoinType::Round(2.0),
356                EndType::ClosedPolygon,
357                1.0,
358            )
359            .0;
360
361        if solution.is_empty() {
362            return Ok(Vec::new());
363        }
364
365        let mut ret_pts = Vec::new();
366        for ip in solution.first().unwrap().exterior().points() {
367            ret_pts.push(imageproc::point::Point::new(ip.x() as i32, ip.y() as i32));
368        }
369
370        Ok(ret_pts)
371    }
372
373    fn signed_polygon_area(points: &[imageproc::point::Point<f32>]) -> f32 {
374        let num_points = points.len();
375        let mut pts = Vec::with_capacity(num_points + 1);
376        pts.extend_from_slice(points);
377        pts.push(points[0]);
378
379        let mut area = 0.0;
380        for i in 0..num_points {
381            area += (pts[i + 1].x - pts[i].x) * (pts[i + 1].y + pts[i].y) / 2.0;
382        }
383
384        area
385    }
386
387    fn length_of_points(box_points: &[imageproc::point::Point<f32>]) -> f64 {
388        if box_points.is_empty() {
389            return 0.0;
390        }
391
392        let mut length = 0.0;
393        let pt = box_points[0];
394        let mut x0 = pt.x as f64;
395        let mut y0 = pt.y as f64;
396
397        let mut box_with_first = Vec::from(box_points);
398        box_with_first.push(pt);
399
400        (1..box_with_first.len()).for_each(|idx| {
401            let pts = box_with_first[idx];
402            let x1 = pts.x as f64;
403            let y1 = pts.y as f64;
404            let dx = x1 - x0;
405            let dy = y1 - y0;
406
407            length += (dx * dx + dy * dy).sqrt();
408
409            x0 = x1;
410            y0 = y1;
411        });
412
413        length
414    }
415}