1use crate::grid::Grid;
17use crate::models::onnx::{OnnxModel, OnnxError, Tensor};
18
19#[allow(clippy::too_many_arguments)]
30pub fn iqsm(
31 phase_rad: &[f64],
32 mask: &[u8],
33 grid: &Grid,
34 te: f64,
35 b0: f64,
36 phase_sign: f64,
37 eroded_rad: i32,
38 model_onnx: &[u8],
39) -> Result<Vec<f64>, OnnxError> {
40 let model = OnnxModel::load(model_onnx)?;
41 iqsm_with(&model, phase_rad, mask, grid, te, b0, phase_sign, eroded_rad)
42}
43
44#[allow(clippy::too_many_arguments)]
48pub fn iqsm_multi_echo(
49 phases: &[&[f64]],
50 magnitudes: &[&[f64]],
51 mask: &[u8],
52 grid: &Grid,
53 tes: &[f64],
54 b0: f64,
55 phase_sign: f64,
56 eroded_rad: i32,
57 model_onnx: &[u8],
58) -> Result<Vec<f64>, OnnxError> {
59 assert_eq!(phases.len(), tes.len(), "one TE per echo");
60 let n = grid.n_total();
61 let model = OnnxModel::load(model_onnx)?;
62
63 let mut acc = vec![0.0f64; n];
64 let mut wsum = vec![0.0f64; n];
65 for (e, &phase) in phases.iter().enumerate() {
66 let chi = iqsm_with(&model, phase, mask, grid, tes[e], b0, phase_sign, eroded_rad)?;
67 let te2 = tes[e] * tes[e];
68 for i in 0..n {
69 let w = magnitudes.get(e).map(|m| m[i]).unwrap_or(1.0) * te2;
70 acc[i] += w * chi[i];
71 wsum[i] += w;
72 }
73 }
74 for i in 0..n {
75 acc[i] = if wsum[i] > 0.0 { acc[i] / wsum[i] } else { 0.0 };
76 }
77 Ok(acc)
78}
79
80fn iqsm_with(
81 model: &OnnxModel,
82 phase_rad: &[f64],
83 mask: &[u8],
84 grid: &Grid,
85 te: f64,
86 b0: f64,
87 phase_sign: f64,
88 eroded_rad: i32,
89) -> Result<Vec<f64>, OnnxError> {
90 let (nx, ny, nz) = grid.dims;
91 let n = nx * ny * nz;
92 assert_eq!(phase_rad.len(), n, "phase length must match grid");
93 assert_eq!(mask.len(), n, "mask length must match grid");
94
95 let eroded = if eroded_rad > 0 {
96 sphere_erode(mask, grid, eroded_rad)
97 } else {
98 mask.to_vec()
99 };
100
101 let pad = |s: usize| -> (usize, usize) {
103 let total = (16 - s % 16) % 16;
104 (total / 2, s + total)
105 };
106 let (bx, px) = pad(nx);
107 let (by, py) = pad(ny);
108 let (bz, pz) = pad(nz);
109
110 let mut phase_t = vec![0.0f32; px * py * pz];
112 let mut mask_t = vec![0.0f32; px * py * pz];
113 for z in 0..nz {
114 for y in 0..ny {
115 for x in 0..nx {
116 let src = x + nx * (y + ny * z);
117 let dst = (z + bz) + pz * ((y + by) + py * (x + bx));
118 phase_t[dst] = (phase_sign * phase_rad[src]) as f32;
119 mask_t[dst] = eroded[src] as f32;
120 }
121 }
122 }
123
124 let mut border = vec![1.0f32; px * py * pz];
128 for a in 0..px {
129 for b in 0..py {
130 for c in 0..pz {
131 if a == 0 || a == px - 1 || b == 0 || b == py - 1 || c == 0 || c == pz - 1 {
132 border[(c) + pz * ((b) + py * a)] = 0.0;
133 }
134 }
135 }
136 }
137
138 let shape = vec![1, 1, px, py, pz];
139 let inputs = [
140 Tensor::new(shape.clone(), phase_t),
141 Tensor::new(shape.clone(), mask_t),
142 Tensor::new(vec![1], vec![te as f32]),
143 Tensor::new(vec![1], vec![b0 as f32]),
144 Tensor::new(shape, border),
145 ];
146 let out = model.run(&inputs)?;
147 let chi_pad = &out[0].data;
148
149 let mut chi = vec![0.0f64; n];
151 for z in 0..nz {
152 for y in 0..ny {
153 for x in 0..nx {
154 let dst = x + nx * (y + ny * z);
155 if eroded[dst] != 0 {
156 let src = (z + bz) + pz * ((y + by) + py * (x + bx));
157 chi[dst] = chi_pad[src] as f64; }
159 }
160 }
161 }
162 Ok(chi)
163}
164
165pub(crate) fn sphere_erode(mask: &[u8], grid: &Grid, radius: i32) -> Vec<u8> {
168 let (nx, ny, nz) = grid.dims;
169 let r2 = radius * radius;
171 let mut offs: Vec<(i32, i32, i32)> = Vec::new();
172 for dz in -radius..=radius {
173 for dy in -radius..=radius {
174 for dx in -radius..=radius {
175 if dx * dx + dy * dy + dz * dz <= r2 {
176 offs.push((dx, dy, dz));
177 }
178 }
179 }
180 }
181 let (nxi, nyi, nzi) = (nx as i32, ny as i32, nz as i32);
182 let mut out = vec![0u8; nx * ny * nz];
183 for z in 0..nzi {
184 for y in 0..nyi {
185 for x in 0..nxi {
186 let mut keep = true;
187 for &(dx, dy, dz) in &offs {
188 let (xx, yy, zz) = (x + dx, y + dy, z + dz);
189 let inside = xx >= 0 && xx < nxi && yy >= 0 && yy < nyi && zz >= 0 && zz < nzi;
190 if !inside
191 || mask[xx as usize + nx * (yy as usize + ny * zz as usize)] == 0
192 {
193 keep = false;
194 break;
195 }
196 }
197 if keep {
198 out[x as usize + nx * (y as usize + ny * z as usize)] = 1;
199 }
200 }
201 }
202 }
203 out
204}