1use crate::grid::Grid;
20use crate::inversion::iqsm::sphere_erode;
21use crate::models::onnx::{OnnxModel, OnnxError, Tensor};
22
23#[allow(clippy::too_many_arguments)]
31pub fn iqsm_plus(
32 phase_rad: &[f64],
33 mask: &[u8],
34 grid: &Grid,
35 te: f64,
36 b0: f64,
37 b0_dir: (f64, f64, f64),
38 phase_sign: f64,
39 eroded_rad: i32,
40 model_onnx: &[u8],
41) -> Result<Vec<f64>, OnnxError> {
42 let model = OnnxModel::load(model_onnx)?;
43 iqsm_plus_with(&model, phase_rad, mask, grid, te, b0, b0_dir, phase_sign, eroded_rad)
44}
45
46#[allow(clippy::too_many_arguments)]
48pub fn iqsm_plus_multi_echo(
49 phases: &[&[f64]],
50 magnitudes: &[&[f64]],
51 mask: &[u8],
52 grid: &Grid,
53 tes: &[f64],
54 b0: f64,
55 b0_dir: (f64, f64, f64),
56 phase_sign: f64,
57 eroded_rad: i32,
58 model_onnx: &[u8],
59) -> Result<Vec<f64>, OnnxError> {
60 assert_eq!(phases.len(), tes.len(), "one TE per echo");
61 let n = grid.n_total();
62 let model = OnnxModel::load(model_onnx)?;
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_plus_with(&model, phase, mask, grid, tes[e], b0, b0_dir, 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
80#[allow(clippy::too_many_arguments)]
81fn iqsm_plus_with(
82 model: &OnnxModel,
83 phase_rad: &[f64],
84 mask: &[u8],
85 grid: &Grid,
86 te: f64,
87 b0: f64,
88 b0_dir: (f64, f64, f64),
89 phase_sign: f64,
90 eroded_rad: i32,
91) -> Result<Vec<f64>, OnnxError> {
92 let (nx, ny, nz) = grid.dims;
93 let n = nx * ny * nz;
94 assert_eq!(phase_rad.len(), n, "phase length must match grid");
95 assert_eq!(mask.len(), n, "mask length must match grid");
96 let idx = |x: usize, y: usize, z: usize| x + nx * (y + ny * z);
97
98 let eroded = if eroded_rad > 0 {
99 sphere_erode(mask, grid, eroded_rad)
100 } else {
101 mask.to_vec()
102 };
103
104 let (mut x0, mut y0, mut z0) = (nx, ny, nz);
106 let (mut x1, mut y1, mut z1) = (0usize, 0usize, 0usize);
107 let mut any = false;
108 for z in 0..nz {
109 for y in 0..ny {
110 for x in 0..nx {
111 if eroded[idx(x, y, z)] != 0 {
112 any = true;
113 x0 = x0.min(x); y0 = y0.min(y); z0 = z0.min(z);
114 x1 = x1.max(x); y1 = y1.max(y); z1 = z1.max(z);
115 }
116 }
117 }
118 }
119 if !any {
120 return Ok(vec![0.0; n]);
121 }
122 let pad = 16usize;
123 let bx0 = x0.saturating_sub(pad);
124 let by0 = y0.saturating_sub(pad);
125 let bz0 = z0.saturating_sub(pad);
126 let bx1 = (x1 + 1 + pad).min(nx);
127 let by1 = (y1 + 1 + pad).min(ny);
128 let bz1 = (z1 + 1 + pad).min(nz);
129 let (cx, cy, cz) = (bx1 - bx0, by1 - by0, bz1 - bz0);
130
131 let mpad = |s: usize| -> (usize, usize) {
133 let total = (16 - s % 16) % 16;
134 (total / 2, s + total)
135 };
136 let (ox, px) = mpad(cx);
137 let (oy, py) = mpad(cy);
138 let (oz, pz) = mpad(cz);
139
140 let mut phase_t = vec![0.0f32; px * py * pz];
142 let mut mask_t = vec![0.0f32; px * py * pz];
143 let mut border = vec![1.0f32; px * py * pz];
144 let rdst = |i: usize, j: usize, k: usize| (k + oz) + pz * ((j + oy) + py * (i + ox));
145 for k in 0..cz {
146 for j in 0..cy {
147 for i in 0..cx {
148 let src = idx(bx0 + i, by0 + j, bz0 + k);
149 let dst = rdst(i, j, k);
150 phase_t[dst] = (phase_sign * phase_rad[src]) as f32;
151 mask_t[dst] = eroded[src] as f32;
152 }
153 }
154 }
155 for a in 0..px {
156 for b in 0..py {
157 for c in 0..pz {
158 if a == 0 || a == px - 1 || b == 0 || b == py - 1 || c == 0 || c == pz - 1 {
159 border[c + pz * (b + py * a)] = 0.0;
160 }
161 }
162 }
163 }
164
165 let norm = (b0_dir.0.powi(2) + b0_dir.1.powi(2) + b0_dir.2.powi(2)).sqrt();
166 let zdir = if norm > 0.0 { norm } else { 1.0 };
167 let z_prjs = vec![
168 (b0_dir.0 / zdir) as f32,
169 (b0_dir.1 / zdir) as f32,
170 (b0_dir.2 / zdir) as f32,
171 ];
172
173 let shape = vec![1, 1, px, py, pz];
174 let inputs = [
175 Tensor::new(shape.clone(), phase_t),
176 Tensor::new(shape.clone(), mask_t),
177 Tensor::new(vec![1], vec![te as f32]),
178 Tensor::new(vec![1], vec![b0 as f32]),
179 Tensor::new(vec![1, 1, 3], z_prjs),
180 Tensor::new(shape, border),
181 ];
182 let out = model.run(&inputs)?;
183 let chi_pad = &out[0].data;
184
185 let mut chi = vec![0.0f64; n];
187 for k in 0..cz {
188 for j in 0..cy {
189 for i in 0..cx {
190 let src = idx(bx0 + i, by0 + j, bz0 + k);
191 if eroded[src] != 0 {
192 chi[src] = chi_pad[rdst(i, j, k)] as f64;
193 }
194 }
195 }
196 }
197 Ok(chi)
198}