ppocr_rs/
table_classifier.rs1use crate::ocr_error::OcrError;
25use ndarray::{Array, Array4};
26use ort::{
27 inputs,
28 session::{builder::GraphOptimizationLevel, Session},
29 value::Tensor,
30};
31use std::path::Path;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum TableType {
38 Wired,
40 Wireless,
42}
43
44impl std::fmt::Display for TableType {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 TableType::Wired => write!(f, "wired"),
48 TableType::Wireless => write!(f, "wireless"),
49 }
50 }
51}
52
53#[derive(Debug)]
58pub struct TableTypeClassifier {
59 session: Session,
60}
61
62impl TableTypeClassifier {
63 pub fn from_path(model_path: impl AsRef<Path>) -> Result<Self, OcrError> {
64 let session = Session::builder()?
65 .with_optimization_level(GraphOptimizationLevel::Level3)?
66 .commit_from_file(model_path)?;
67 Ok(Self { session })
68 }
69
70 pub fn classify(&self, image: &image::RgbImage) -> Result<(TableType, f32), OcrError> {
73 let blob = preprocess_lcnet(image);
74 let name = self.session.inputs[0].name.clone();
75 let outputs = self.session.run(
76 inputs![name => Tensor::from_array(blob)?]?
77 )?;
78 let (_, first) = outputs.iter().next()
79 .ok_or_else(|| OcrError::ModelOutput("TableTypeCls: nessun output".into()))?;
80 let (_, raw) = crate::compat::tensor_extract_with_shape_f32(&first)?;
81 let (idx, score) = argmax_f32(&raw);
82 Ok((if idx == 0 { TableType::Wired } else { TableType::Wireless }, score))
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum DocOrientation {
94 Deg0,
96 Deg90,
98 Deg180,
100 Deg270,
102}
103
104impl DocOrientation {
105 pub fn degrees(self) -> u32 {
107 match self {
108 Self::Deg0 => 0,
109 Self::Deg90 => 90,
110 Self::Deg180 => 180,
111 Self::Deg270 => 270,
112 }
113 }
114}
115
116impl std::fmt::Display for DocOrientation {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{}°", self.degrees())
119 }
120}
121
122#[derive(Debug)]
131pub struct DocOrientationClassifier {
132 session: Session,
133}
134
135impl DocOrientationClassifier {
136 pub fn from_path(model_path: impl AsRef<Path>) -> Result<Self, OcrError> {
137 let session = Session::builder()?
138 .with_optimization_level(GraphOptimizationLevel::Level3)?
139 .commit_from_file(model_path)?;
140 Ok(Self { session })
141 }
142
143 pub fn classify(&self, image: &image::RgbImage) -> Result<(DocOrientation, f32), OcrError> {
146 let blob = preprocess_lcnet_sq224(image);
148 let name = self.session.inputs[0].name.clone();
149 let outputs = self.session.run(
150 inputs![name => Tensor::from_array(blob)?]?
151 )?;
152 let (_, first) = outputs.iter().next()
153 .ok_or_else(|| OcrError::ModelOutput("DocOriCls: nessun output".into()))?;
154 let (_, raw) = crate::compat::tensor_extract_with_shape_f32(&first)?;
155 let (idx, score) = argmax_f32(&raw);
156 let orient = match idx {
157 0 => DocOrientation::Deg0,
158 1 => DocOrientation::Deg90,
159 2 => DocOrientation::Deg180,
160 _ => DocOrientation::Deg270,
161 };
162 Ok((orient, score))
163 }
164}
165
166fn preprocess_lcnet(image: &image::RgbImage) -> Array4<f32> {
171 preprocess_lcnet_wh(image, 192, 48)
172}
173
174fn preprocess_lcnet_sq224(image: &image::RgbImage) -> Array4<f32> {
177 preprocess_lcnet_wh(image, 224, 224)
178}
179
180fn preprocess_lcnet_wh(image: &image::RgbImage, w: u32, h: u32) -> Array4<f32> {
181 let resized = image::imageops::resize(image, w, h, image::imageops::FilterType::Triangle);
182 let mean = [0.485f32, 0.456, 0.406];
183 let std = [0.229f32, 0.224, 0.225];
184 let mut blob: Array4<f32> = Array::zeros((1, 3, h as usize, w as usize));
185 for y in 0..h as usize {
186 for x in 0..w as usize {
187 let p = resized.get_pixel(x as u32, y as u32);
188 for c in 0..3usize {
189 blob[[0, c, y, x]] = (p[c] as f32 / 255.0 - mean[c]) / std[c];
190 }
191 }
192 }
193 blob
194}
195
196fn argmax_f32(logits: &[f32]) -> (usize, f32) {
198 logits.iter().copied().enumerate()
199 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
200 .unwrap_or((0, 0.0))
201}