1use pagesift_infer::Tensor;
6
7use crate::Image;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum LimitType {
12 Max,
14 Min,
16 ResizeLong,
18}
19
20#[derive(Debug, Clone)]
22pub struct Preprocess {
23 pub limit_side_len: usize,
24 pub limit_type: LimitType,
25 pub max_side_limit: Option<usize>,
26 pub rescale: f32,
27 pub mean: [f32; 3],
29 pub std: [f32; 3],
30}
31
32impl Preprocess {
33 pub fn from_json(v: &serde_json::Value) -> Result<Preprocess, String> {
34 let limit_type = match v["limit_type"].as_str().unwrap_or("max") {
35 "max" => LimitType::Max,
36 "min" => LimitType::Min,
37 "resize_long" => LimitType::ResizeLong,
38 t => return Err(format!("unsupported limit_type: {t}")),
39 };
40 let triple = |key: &str, def: [f32; 3]| -> Result<[f32; 3], String> {
41 match v[key].as_array() {
42 None => Ok(def),
43 Some(a) if a.len() == 3 => {
44 let mut out = [0f32; 3];
45 for (o, x) in out.iter_mut().zip(a) {
46 *o = x.as_f64().ok_or(format!("{key} must be a number"))? as f32;
47 }
48 Ok(out)
49 }
50 Some(_) => Err(format!("{key} must have 3 numbers")),
51 }
52 };
53 if v["do_resize"].as_bool() == Some(false)
54 || v["do_rescale"].as_bool() == Some(false)
55 || v["do_normalize"].as_bool() == Some(false)
56 {
57 return Err(
58 "only preprocessing with resize, rescale and normalize all on is supported".into(),
59 );
60 }
61 Ok(Preprocess {
64 limit_side_len: v["limit_side_len"].as_u64().unwrap_or(960) as usize,
65 limit_type,
66 max_side_limit: match &v["max_side_limit"] {
67 serde_json::Value::Null => Some(4000),
68 x => x.as_u64().map(|n| n as usize),
69 },
70 rescale: v["rescale_factor"].as_f64().unwrap_or(1.0 / 255.0) as f32,
71 mean: triple("image_mean", [0.406, 0.456, 0.485])?,
72 std: triple("image_std", [0.225, 0.224, 0.229])?,
73 })
74 }
75
76 pub fn target_size(&self, h: usize, w: usize) -> (usize, usize) {
78 let (hf, wf) = (h as f64, w as f64);
79 let lim = self.limit_side_len as f64;
80 let ratio = match self.limit_type {
81 LimitType::Max if hf.max(wf) > lim => lim / hf.max(wf),
82 LimitType::Min => lim / hf.min(wf),
85 LimitType::ResizeLong => lim / hf.max(wf),
86 _ => 1.0,
87 };
88 let mut rh = (hf * ratio) as usize;
90 let mut rw = (wf * ratio) as usize;
91 if let Some(m) = self.max_side_limit
92 && rh.max(rw) > m
93 {
94 let r = m as f64 / rh.max(rw) as f64;
95 rh = (rh as f64 * r) as usize;
96 rw = (rw as f64 * r) as usize;
97 }
98 let align = |n: usize| (((n as f64 / 32.0).round_ties_even() as usize) * 32).max(32);
100 (align(rh), align(rw))
101 }
102
103 pub fn run(&self, img: &Image) -> Tensor {
105 let (th, tw) = self.target_size(img.height, img.width);
106 let plane = img.width * img.height;
108 let mut rgb = vec![0f32; 3 * plane];
109 for y in 0..img.height {
110 for x in 0..img.width {
111 for c in 0..3 {
112 rgb[c * plane + y * img.width + x] = img.at(x, y, c) as f32;
113 }
114 }
115 }
116 let resized = resize_bilinear_aa(&rgb, 3, img.height, img.width, th, tw);
117 let out_plane = th * tw;
118 let mut out = vec![0f32; 3 * out_plane];
119 for c in 0..3 {
120 let dst = 2 - c;
122 let (s, m, d) = (self.rescale, self.mean[c], self.std[c]);
123 for (o, v) in out[dst * out_plane..(dst + 1) * out_plane]
124 .iter_mut()
125 .zip(&resized[c * out_plane..(c + 1) * out_plane])
126 {
127 *o = (v * s - m) / d;
128 }
129 }
130 Tensor::new(vec![3, th, tw], out)
131 }
132}
133
134struct Taps {
136 start: Vec<usize>,
137 weights: Vec<Vec<f32>>,
138}
139
140fn taps(inp: usize, out: usize) -> Taps {
143 let scale = inp as f64 / out as f64;
144 let support = scale.max(1.0);
146 let inv = 1.0 / scale.max(1.0);
147 let mut start = Vec::with_capacity(out);
148 let mut weights = Vec::with_capacity(out);
149 for i in 0..out {
150 let center = scale * (i as f64 + 0.5);
151 let lo = ((center - support + 0.5).floor().max(0.0)) as usize;
152 let hi = ((center + support + 0.5).floor() as usize).min(inp);
153 let mut w: Vec<f64> = (lo..hi)
154 .map(|j| (1.0 - ((j as f64 - center + 0.5) * inv).abs()).max(0.0))
155 .collect();
156 let sum: f64 = w.iter().sum();
157 if sum > 0.0 {
158 for v in &mut w {
159 *v /= sum;
160 }
161 }
162 start.push(lo);
163 weights.push(w.into_iter().map(|v| v as f32).collect());
164 }
165 Taps { start, weights }
166}
167
168pub(crate) fn resize_bilinear_aa(
170 src: &[f32],
171 c: usize,
172 h: usize,
173 w: usize,
174 oh: usize,
175 ow: usize,
176) -> Vec<f32> {
177 if (h, w) == (oh, ow) {
178 return src.to_vec();
179 }
180 let tx = taps(w, ow);
181 let ty = taps(h, oh);
182 let mut mid = vec![0f32; c * h * ow];
183 for (srow, drow) in src.chunks_exact(w).zip(mid.chunks_exact_mut(ow)) {
184 for (x, d) in drow.iter_mut().enumerate() {
185 let s0 = tx.start[x];
186 *d = tx.weights[x]
187 .iter()
188 .enumerate()
189 .map(|(k, wt)| srow[s0 + k] * wt)
190 .sum();
191 }
192 }
193 let mut out = vec![0f32; c * oh * ow];
194 for ch in 0..c {
195 let m = &mid[ch * h * ow..(ch + 1) * h * ow];
196 for y in 0..oh {
197 let drow = &mut out[(ch * oh + y) * ow..(ch * oh + y + 1) * ow];
198 for (k, wt) in ty.weights[y].iter().enumerate() {
199 let srow = &m[(ty.start[y] + k) * ow..(ty.start[y] + k + 1) * ow];
200 for (d, s) in drow.iter_mut().zip(srow) {
201 *d += s * wt;
202 }
203 }
204 }
205 }
206 out
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 fn pre(limit_type: LimitType) -> Preprocess {
214 Preprocess {
215 limit_side_len: 736,
216 limit_type,
217 max_side_limit: Some(4000),
218 rescale: 1.0 / 255.0,
219 mean: [0.406, 0.456, 0.485],
220 std: [0.225, 0.224, 0.229],
221 }
222 }
223
224 #[test]
225 fn 尺寸规则() {
226 let p = pre(LimitType::Min);
227 assert_eq!(p.target_size(2902, 2244), (960, 736));
229 assert_eq!(p.target_size(500, 300), (1216, 736));
231 assert_eq!(p.target_size(9000, 1000), (4000, 448));
233 assert_eq!(pre(LimitType::Max).target_size(1000, 500), (736, 384));
234 }
235
236 #[test]
237 fn 缩放保常数且放大是双线性() {
238 let src = vec![5.0f32; 6 * 4];
239 assert!(
240 resize_bilinear_aa(&src, 1, 6, 4, 3, 2)
241 .iter()
242 .all(|v| (v - 5.0).abs() < 1e-5)
243 );
244 let up = resize_bilinear_aa(&[0.0, 1.0], 1, 1, 2, 1, 4);
246 let want = [0.0, 0.25, 0.75, 1.0];
247 for (a, b) in up.iter().zip(want) {
248 assert!((a - b).abs() < 1e-6, "{up:?}");
249 }
250 }
251
252 #[test]
253 fn 通道换成_bgr() {
254 let img = Image {
255 width: 32,
256 height: 32,
257 channels: 3,
258 data: [255u8, 0, 0].repeat(32 * 32),
259 };
260 let t = pre(LimitType::Max).run(&img);
261 let plane = 32 * 32;
262 assert!((t.data[0] - (0.0 - 0.485) / 0.229).abs() < 1e-5);
264 assert!((t.data[2 * plane] - (1.0 - 0.406) / 0.225).abs() < 1e-5);
265 }
266}