1use crate::grid::Grid;
18use crate::models::onnx::{OnnxModel, OnnxError, Tensor};
19
20const IN: usize = 64;
21const OUT: usize = 32;
22const MARGIN: usize = (IN - OUT) / 2; const SHIFT: usize = 24;
24const OVERLAP: usize = OUT - SHIFT; #[inline]
27fn ri(x: usize, y: usize, z: usize, dy: usize, dz: usize) -> usize {
28 (x * dy + y) * dz + z }
30
31pub fn autoqsm(
40 field_ppm: &[f64],
41 mask: &[u8],
42 grid: &Grid,
43 model_onnx: &[u8],
44) -> Result<Vec<f64>, OnnxError> {
45 let (nx, ny, nz) = grid.dims;
46 let n = nx * ny * nz;
47 assert_eq!(field_ppm.len(), n, "field length must match grid");
48 assert_eq!(mask.len(), n, "mask length must match grid");
49
50 let mut vol = vec![0.0f64; n];
52 for z in 0..nz {
53 for y in 0..ny {
54 for x in 0..nx {
55 vol[ri(x, y, z, ny, nz)] = field_ppm[x + nx * (y + ny * z)];
56 }
57 }
58 }
59
60 let pad_to = |d: usize| -> usize {
62 let e = d - OUT;
63 e.div_ceil(SHIFT) * SHIFT - e };
65 let (padx, pady, padz) = (pad_to(nx), pad_to(ny), pad_to(nz));
66 let (xp, yp, zp) = (nx + padx, ny + pady, nz + padz); let (bx, by, bz) = (MARGIN, MARGIN, MARGIN);
70 let (dx, dy, dz) = (xp + 2 * MARGIN, yp + 2 * MARGIN, zp + 2 * MARGIN);
71 let clamp = |v: isize, lo: usize, hi: usize| (v.clamp(lo as isize, hi as isize - 1)) as usize;
72 let mut padded = vec![0.0f64; dx * dy * dz];
73 for x in 0..dx {
74 let sx = clamp(x as isize - bx as isize, 0, nx);
75 for y in 0..dy {
76 let sy = clamp(y as isize - by as isize, 0, ny);
77 for z in 0..dz {
78 let sz = clamp(z as isize - bz as isize, 0, nz);
79 padded[ri(x, y, z, dy, dz)] = vol[ri(sx, sy, sz, ny, nz)];
80 }
81 }
82 }
83
84 let num_i = (xp - OUT) / SHIFT + 1;
85 let num_j = (yp - OUT) / SHIFT + 1;
86 let num_k = (zp - OUT) / SHIFT + 1;
87
88 let model = OnnxModel::load(model_onnx)?;
89 let mut output = vec![0.0f64; xp * yp * zp];
90 let mut patches: Vec<Vec<f64>> = Vec::with_capacity(num_i * num_j * num_k);
91
92 for k in 0..num_k {
93 for j in 0..num_j {
94 for i in 0..num_i {
95 let mut buf = vec![0.0f32; IN * IN * IN];
97 for px in 0..IN {
98 for py in 0..IN {
99 for pz in 0..IN {
100 let src = ri(SHIFT * i + px, SHIFT * j + py, SHIFT * k + pz, dy, dz);
101 buf[ri(px, py, pz, IN, IN)] = padded[src] as f32;
102 }
103 }
104 }
105 let out = model.run_single(&Tensor::new(vec![1, 1, IN, IN, IN], buf))?;
106 let mut patch: Vec<f64> = out.data.iter().map(|&v| v as f64).collect();
107
108 if i != 0 {
110 blend(&mut patch, &patches[patches.len() - 1], 0);
111 }
112 if j != 0 {
113 blend(&mut patch, &patches[patches.len() - num_i], 1);
114 }
115 if k != 0 {
116 blend(&mut patch, &patches[patches.len() - num_i * num_j], 2);
117 }
118
119 for ox in 0..OUT {
120 for oy in 0..OUT {
121 for oz in 0..OUT {
122 output[ri(SHIFT * i + ox, SHIFT * j + oy, SHIFT * k + oz, yp, zp)] =
123 patch[ri(ox, oy, oz, OUT, OUT)];
124 }
125 }
126 }
127 patches.push(patch);
128 }
129 }
130 }
131
132 let mut chi = vec![0.0f64; n];
134 for z in 0..nz {
135 for y in 0..ny {
136 for x in 0..nx {
137 let i = x + nx * (y + ny * z);
138 if mask[i] != 0 {
139 chi[i] = output[ri(x, y, z, yp, zp)];
140 }
141 }
142 }
143 }
144 Ok(chi)
145}
146
147fn blend(patch: &mut [f64], neighbor: &[f64], dir: usize) {
151 let denom = (OVERLAP - 1) as f64;
152 for t in 0..OVERLAP {
153 let w = 1.0 - t as f64 / denom; for a in 0..OUT {
155 for b in 0..OUT {
156 let (pi, ni) = match dir {
157 0 => (ri(t, a, b, OUT, OUT), ri(OUT - OVERLAP + t, a, b, OUT, OUT)),
158 1 => (ri(a, t, b, OUT, OUT), ri(a, OUT - OVERLAP + t, b, OUT, OUT)),
159 _ => (ri(a, b, t, OUT, OUT), ri(a, b, OUT - OVERLAP + t, OUT, OUT)),
160 };
161 patch[pi] = w * neighbor[ni] + (1.0 - w) * patch[pi];
162 }
163 }
164 }
165}