1use nalgebra::{RowVector3, Vector4};
27#[cfg(feature = "nifti_images")]
28use ndarray::{ArrayBase, Axis, DataMut, Dimension, Zip};
29#[cfg(feature = "nifti_images")]
30use nifti::DataElement;
31
32use crate::{Affine, Affine4, Translation};
33
34#[derive(Clone, Copy, Debug, PartialEq)]
35pub enum Direction {
36 Normal,
37 Reversed,
38}
39
40impl Direction {
41 pub fn to_f32(&self) -> f32 {
42 if *self == Direction::Normal {
43 1.0
44 } else {
45 -1.0
46 }
47 }
48}
49
50pub type Orientation = (usize, Direction);
51pub type Orientations = [Orientation; 3];
52
53pub fn affine_to_axcodes(affine: &Affine) -> String {
55 let orientations = io_orientations(affine);
56 orientations_to_axcodes(orientations)
57}
58
59pub fn io_orientations(affine: &Affine) -> Orientations {
68 let rzs2 = affine.component_mul(affine);
70 let mut zooms = RowVector3::new(
71 (rzs2[0] + rzs2[1] + rzs2[2]).sqrt(),
72 (rzs2[3] + rzs2[4] + rzs2[5]).sqrt(),
73 (rzs2[6] + rzs2[7] + rzs2[8]).sqrt(),
74 );
75
76 zooms.apply(|z| {
79 if *z == 0.0 {
80 *z = 1.0
81 }
82 });
83
84 #[rustfmt::skip]
85 let rs = Affine::new(
86 affine[0] / zooms[0], affine[3] / zooms[1], affine[6] / zooms[2],
87 affine[1] / zooms[0], affine[4] / zooms[1], affine[7] / zooms[2],
88 affine[2] / zooms[0], affine[5] / zooms[1], affine[8] / zooms[2],
89 );
90
91 let svd = rs.svd(true, true);
94 let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
95
96 let tol = s.as_slice().iter().cloned().fold(0.0, f32::max) * 3.0 * f32::EPSILON;
98
99 let s = Affine::from_rows(&[s.transpose(), s.transpose(), s.transpose()]);
100 let u = u.zip_map(&s, |u, s| if s > tol { u } else { 0.0 });
101 let v_t = v_t.zip_map(&s, |v, s| if s > tol { v } else { 0.0 });
102
103 let mut r = u * v_t;
111
112 let mut orientations = [(0, Direction::Normal), (0, Direction::Normal), (0, Direction::Normal)];
113 for c in 0..3 {
114 let mut argmax = 0;
115 let mut max = 0.0;
116 let mut sign_max = 0.0;
117 for (i, e) in r.column(c).iter().enumerate() {
118 let e_abs = e.abs();
119 if e_abs > max {
120 argmax = i;
121 max = e_abs;
122 sign_max = *e;
123 }
124 }
125
126 if sign_max >= 0.0 {
127 orientations[c] = (argmax, Direction::Normal);
128 } else {
129 orientations[c] = (argmax, Direction::Reversed);
130 }
131
132 for e in r.column_mut(c).iter_mut() {
135 *e = 0.0;
136 }
137 }
138
139 orientations
140}
141
142pub fn orientations_to_axcodes(orientations: Orientations) -> String {
150 let labels = [['R', 'L'], ['A', 'P'], ['S', 'I']];
151 orientations
152 .into_iter()
153 .map(|(axis, direction)| labels[axis][(direction == Direction::Reversed) as usize])
154 .collect()
155}
156
157pub fn axcodes_to_orientations(axcodes: &str) -> Orientations {
166 let labels = [('R', 'L'), ('A', 'P'), ('S', 'I')];
167 let mut orientations = [(0, Direction::Normal), (0, Direction::Normal), (0, Direction::Normal)];
168 for (code_idx, code) in axcodes.chars().enumerate() {
169 for (label_idx, codes) in labels.iter().enumerate() {
170 if code == codes.0 {
171 orientations[code_idx] = (label_idx, Direction::Normal);
172 } else if code == codes.1 {
173 orientations[code_idx] = (label_idx, Direction::Reversed);
174 }
175 }
176 }
177 orientations
178}
179
180pub fn orientations_transform(
182 start_orientations: &Orientations,
183 end_orientations: &Orientations,
184) -> Orientations {
185 let mut result = [(0, Direction::Normal), (0, Direction::Normal), (0, Direction::Normal)];
186 for (end_in_idx, &(ref end_out_idx, ref end_flip)) in end_orientations.iter().enumerate() {
187 for (start_in_idx, &(ref start_out_idx, ref start_flip)) in
188 start_orientations.iter().enumerate()
189 {
190 if end_out_idx == start_out_idx {
191 if start_flip == end_flip {
192 result[start_in_idx] = (end_in_idx, Direction::Normal)
193 } else {
194 result[start_in_idx] = (end_in_idx, Direction::Reversed)
195 }
196 break;
197 }
198 }
199 }
200 result
201}
202
203#[cfg(feature = "nifti_images")]
204pub fn apply_orientation<S, A, D>(
206 mut arr: ArrayBase<S, D>,
207 orientations: Orientations,
208) -> ArrayBase<S, D>
209where
210 S: DataMut<Elem = A>,
211 A: DataElement,
212 D: Dimension,
213{
214 for (axis, &(_, direction)) in orientations.iter().enumerate() {
216 if direction == Direction::Reversed {
217 Zip::from(arr.lanes_mut(Axis(axis))).for_each(|mut arr| {
218 let n = arr.len();
219 for i in 0..n / 2 {
220 let tmp = arr[n - 1 - i];
221 arr[n - 1 - i] = arr[i];
222 arr[i] = tmp;
223 }
224 });
225 }
226 }
227
228 let mut x = (orientations[0].0, 0);
231 let mut y = (orientations[1].0, 1);
232 let mut z = (orientations[2].0, 2);
233 if x > y {
234 std::mem::swap(&mut x, &mut y);
235 }
236 if y > z {
237 std::mem::swap(&mut y, &mut z);
238 }
239 if x > y {
240 std::mem::swap(&mut x, &mut y);
241 }
242
243 let mut axes = arr.raw_dim();
244 axes[0] = x.1;
245 axes[1] = y.1;
246 axes[2] = z.1;
247 for i in 3..arr.ndim() {
248 axes[i] = i;
249 }
250 arr.permuted_axes(axes)
251}
252
253pub fn inverse_orientations_affine(orientations: &Orientations, dim: [i16; 3]) -> Affine4 {
261 let mut undo_reorder = Affine4::zeros();
262 for (i, &(j, _)) in orientations.iter().enumerate() {
263 undo_reorder[(i, j)] = 1.0;
264 }
265 undo_reorder[(3, 3)] = 1.0;
266
267 let center = Translation::new(
268 -(dim[0] - 1) as f32 / 2.0,
269 -(dim[1] - 1) as f32 / 2.0,
270 -(dim[2] - 1) as f32 / 2.0,
271 );
272 let mut undo_flip = Affine4::from_diagonal(&Vector4::new(
273 orientations[0].1.to_f32(),
274 orientations[1].1.to_f32(),
275 orientations[2].1.to_f32(),
276 1.0,
277 ));
278 undo_flip[(0, 3)] = undo_flip[(0, 0)] * center[0] - center[0];
279 undo_flip[(1, 3)] = undo_flip[(1, 1)] * center[1] - center[1];
280 undo_flip[(2, 3)] = undo_flip[(2, 2)] * center[2] - center[2];
281
282 undo_flip * undo_reorder
283}