1use std::ops::{Add, Sub};
2
3use ndarray::{s, Array2, Array3, ArrayRef3, ArrayViewMut1, Zip};
4use num_traits::{FromPrimitive, Num, ToPrimitive};
5
6use crate::{array_like, pad, round_ties_even, spline_filter, BorderMode, PadMode};
7
8pub fn shift<A>(
22 data: &ArrayRef3<A>,
23 shift: [f64; 3],
24 order: usize,
25 mode: BorderMode<A>,
26 prefilter: bool,
27) -> Array3<A>
28where
29 A: Copy + Num + FromPrimitive + PartialOrd + ToPrimitive,
30{
31 let dim = [data.dim().0, data.dim().1, data.dim().2];
32 let shift = shift.map(|s| -s);
33 run_zoom_shift(data, dim, [1.0, 1.0, 1.0], shift, order, mode, prefilter)
34}
35
36pub fn zoom<A>(
49 data: &ArrayRef3<A>,
50 zoom: [f64; 3],
51 order: usize,
52 mode: BorderMode<A>,
53 prefilter: bool,
54) -> Array3<A>
55where
56 A: Copy + Num + FromPrimitive + PartialOrd + ToPrimitive,
57{
58 let mut o_dim = data.raw_dim();
59 for (ax, (&ax_len, zoom)) in data.shape().iter().zip(zoom.iter()).enumerate() {
60 o_dim[ax] = round_ties_even(ax_len as f64 * zoom) as usize;
61 }
62 let o_dim = [o_dim[0], o_dim[1], o_dim[2]];
63
64 let mut nom = data.raw_dim();
65 let mut div = o_dim.clone();
66 for ax in 0..data.ndim() {
67 nom[ax] -= 1;
68 div[ax] -= 1;
69 }
70 let zoom = [
71 nom[0] as f64 / div[0] as f64,
72 nom[1] as f64 / div[1] as f64,
73 nom[2] as f64 / div[2] as f64,
74 ];
75
76 run_zoom_shift(data, o_dim, zoom, [0.0, 0.0, 0.0], order, mode, prefilter)
77}
78
79fn run_zoom_shift<A>(
80 data: &ArrayRef3<A>,
81 odim: [usize; 3],
82 zooms: [f64; 3],
83 shifts: [f64; 3],
84 order: usize,
85 mode: BorderMode<A>,
86 prefilter: bool,
87) -> Array3<A>
88where
89 A: Copy + Num + FromPrimitive + PartialOrd + ToPrimitive,
90{
91 let idim = [data.dim().0, data.dim().1, data.dim().2];
92 let mut out = array_like(&data, odim, A::zero());
93 if prefilter && order > 1 {
94 let (data, nb_prepad) = match mode {
96 BorderMode::Nearest => {
97 let padded = pad(data, &[[12, 12]], PadMode::Edge);
98 (spline_filter(&padded, order, mode), 12)
99 }
100 _ => (spline_filter(data, order, mode), 0),
101 };
102 let reslicer = ZoomShiftReslicer::new(idim, odim, zooms, shifts, order, mode, nb_prepad);
103 Zip::indexed(&mut out).for_each(|idx, o| {
104 *o = A::from_f64(reslicer.interpolate(&data, idx)).unwrap();
105 });
106 } else {
107 let reslicer = ZoomShiftReslicer::new(idim, odim, zooms, shifts, order, mode, 0);
109 Zip::indexed(&mut out).for_each(|idx, o| {
110 *o = A::from_f64(reslicer.interpolate(data, idx)).unwrap();
111 });
112 }
113 out
114}
115
116struct ZoomShiftReslicer {
118 order: usize,
119 offsets: [Vec<isize>; 3],
120 edge_offsets: [Array2<isize>; 3],
121 is_edge_case: [Vec<bool>; 3],
122 splvals: [Array2<f64>; 3],
123 zeros: [Vec<bool>; 3],
124 cval: f64,
125}
126
127impl ZoomShiftReslicer {
128 pub fn new<A>(
130 idim: [usize; 3],
131 odim: [usize; 3],
132 zooms: [f64; 3],
133 shifts: [f64; 3],
134 order: usize,
135 mode: BorderMode<A>,
136 nb_prepad: isize,
137 ) -> ZoomShiftReslicer
138 where
139 A: Copy + ToPrimitive,
140 {
141 let offsets = [vec![0; odim[0]], vec![0; odim[1]], vec![0; odim[2]]];
142 let is_edge_case = [vec![false; odim[0]], vec![false; odim[1]], vec![false; odim[2]]];
143 let (edge_offsets, splvals) = if order > 0 {
144 let dim0 = (odim[0], order + 1);
145 let dim1 = (odim[1], order + 1);
146 let dim2 = (odim[2], order + 1);
147 let e = [Array2::zeros(dim0), Array2::zeros(dim1), Array2::zeros(dim2)];
148 let s = [Array2::zeros(dim0), Array2::zeros(dim1), Array2::zeros(dim2)];
149 (e, s)
150 } else {
151 let e = [Array2::zeros((0, 0)), Array2::zeros((0, 0)), Array2::zeros((0, 0))];
153 let s = [Array2::zeros((0, 0)), Array2::zeros((0, 0)), Array2::zeros((0, 0))];
154 (e, s)
155 };
156 let zeros = [vec![false; odim[0]], vec![false; odim[1]], vec![false; odim[2]]];
157 let cval = match mode {
158 BorderMode::Constant(cval) => cval.to_f64().unwrap(),
159 _ => 0.0,
160 };
161
162 let mut reslicer =
163 ZoomShiftReslicer { order, offsets, edge_offsets, is_edge_case, splvals, zeros, cval };
164 reslicer.build_arrays(idim, odim, zooms, shifts, order, mode, nb_prepad);
165 reslicer
166 }
167
168 fn build_arrays<A>(
169 &mut self,
170 idim: [usize; 3],
171 odim: [usize; 3],
172 zooms: [f64; 3],
173 shifts: [f64; 3],
174 order: usize,
175 mode: BorderMode<A>,
176 nb_prepad: isize,
177 ) where
178 A: Copy,
179 {
180 let spline_mode = match mode {
182 BorderMode::Constant(_) | BorderMode::Wrap => BorderMode::Mirror,
183 _ => mode,
184 };
185 let iorder = order as isize;
186 let idim = [
187 idim[0] as isize + 2 * nb_prepad,
188 idim[1] as isize + 2 * nb_prepad,
189 idim[2] as isize + 2 * nb_prepad,
190 ];
191 let nb_prepad = nb_prepad as f64;
192
193 for axis in 0..3 {
194 let splvals = &mut self.splvals[axis];
195 let offsets = &mut self.offsets[axis];
196 let edge_offsets = &mut self.edge_offsets[axis];
197 let is_edge_case = &mut self.is_edge_case[axis];
198 let zeros = &mut self.zeros[axis];
199 let len = idim[axis] as f64;
200 for from in 0..odim[axis] {
201 let mut to = (from as f64 + shifts[axis]) * zooms[axis] + nb_prepad;
202 match mode {
203 BorderMode::Nearest => {}
204 _ => to = map_coordinates(to, idim[axis] as f64, mode),
205 };
206 if to > -1.0 {
207 if order > 0 {
208 build_splines(to, &mut splvals.row_mut(from), order);
209 }
210 if order & 1 == 0 {
211 to += 0.5;
212 }
213
214 let start = to.floor() as isize - iorder / 2;
215 offsets[from] = start;
216 if start < 0 || start + iorder >= idim[axis] {
217 is_edge_case[from] = true;
218 for o in 0..=order {
219 let x = (start + o as isize) as f64;
220 let idx = map_coordinates(x, len, spline_mode) as isize;
221 edge_offsets[(from, o)] = idx - start;
222 }
223 }
224 } else {
225 zeros[from] = true;
226 }
227 }
228 }
229 }
230
231 pub fn interpolate<A>(&self, data: &ArrayRef3<A>, start: (usize, usize, usize)) -> f64
233 where
234 A: ToPrimitive + Add<Output = A> + Sub<Output = A> + Copy,
235 {
236 if self.zeros[0][start.0] || self.zeros[1][start.1] || self.zeros[2][start.2] {
237 return self.cval;
238 }
239
240 if self.edge_offsets[0].is_empty() {
245 let x = self.offsets[0][start.0] as usize;
246 let y = self.offsets[1][start.1] as usize;
247 let z = self.offsets[2][start.2] as usize;
248 return data[(x, y, z)].to_f64().unwrap();
249 }
250
251 let n = self.order + 1;
254 let valid_index = |original_offset, is_edge, start, d: usize, v| {
255 (original_offset + if is_edge { self.edge_offsets[d][(start, v)] } else { v as isize })
256 as usize
257 };
258
259 let original_offset_x = self.offsets[0][start.0];
260 let is_edge_x = self.is_edge_case[0][start.0];
261 let mut xs = [0; 6];
262 let original_offset_y = self.offsets[1][start.1];
263 let is_edge_y = self.is_edge_case[1][start.1];
264 let mut ys = [0; 6];
265 let original_offset_z = self.offsets[2][start.2];
266 let is_edge_z = self.is_edge_case[2][start.2];
267 let mut zs = [0; 6];
268 for i in 0..n {
269 xs[i] = valid_index(original_offset_x, is_edge_x, start.0, 0, i);
270 ys[i] = valid_index(original_offset_y, is_edge_y, start.1, 1, i);
271 zs[i] = valid_index(original_offset_z, is_edge_z, start.2, 2, i);
272 }
273
274 let mut t = 0.0;
275 for (z, &idx_z) in zs[..n].iter().enumerate() {
276 let spline_z = self.splvals[2][(start.2, z)];
277 for (y, &idx_y) in ys[..n].iter().enumerate() {
278 let spline_yz = self.splvals[1][(start.1, y)] * spline_z;
279 for (x, &idx_x) in xs[..n].iter().enumerate() {
280 let spline_xyz = self.splvals[0][(start.0, x)] * spline_yz;
281 t += data[(idx_x, idx_y, idx_z)].to_f64().unwrap() * spline_xyz;
282 }
283 }
284 }
285 t
286 }
287}
288
289fn build_splines(to: f64, spline: &mut ArrayViewMut1<f64>, order: usize) {
290 let x = to - if order & 1 == 1 { to } else { to + 0.5 }.floor();
291 match order {
292 1 => spline[0] = 1.0 - x,
293 2 => {
294 spline[0] = 0.5 * (0.5 - x).powi(2);
295 spline[1] = 0.75 - x * x;
296 }
297 3 => {
298 let z = 1.0 - x;
299 spline[0] = z * z * z / 6.0;
300 spline[1] = (x * x * (x - 2.0) * 3.0 + 4.0) / 6.0;
301 spline[2] = (z * z * (z - 2.0) * 3.0 + 4.0) / 6.0;
302 }
303 4 => {
304 let t = x * x;
305 let y = 1.0 + x;
306 let z = 1.0 - x;
307 spline[0] = (0.5 - x).powi(4) / 24.0;
308 spline[1] = y * (y * (y * (5.0 - y) / 6.0 - 1.25) + 5.0 / 24.0) + 55.0 / 96.0;
309 spline[2] = t * (t * 0.25 - 0.625) + 115.0 / 192.0;
310 spline[3] = z * (z * (z * (5.0 - z) / 6.0 - 1.25) + 5.0 / 24.0) + 55.0 / 96.0;
311 }
312 5 => {
313 let y = 1.0 - x;
314 let t = y * y;
315 spline[0] = y * t * t / 120.0;
316 let y = x + 1.0;
317 spline[1] = y * (y * (y * (y * (y / 24.0 - 0.375) + 1.25) - 1.75) + 0.625) + 0.425;
318 let t = x * x;
319 spline[2] = t * (t * (0.25 - x / 12.0) - 0.5) + 0.55;
320 let z = 1.0 - x;
321 let t = z * z;
322 spline[3] = t * (t * (0.25 - z / 12.0) - 0.5) + 0.55;
323 let z = z + 1.0;
324 spline[4] = z * (z * (z * (z * (z / 24.0 - 0.375) + 1.25) - 1.75) + 0.625) + 0.425;
325 }
326 _ => panic!("order must be between 1 and 5"),
327 }
328 spline[order] = 1.0 - spline.slice(s![..order]).sum();
329}
330
331fn map_coordinates<A>(mut idx: f64, len: f64, mode: BorderMode<A>) -> f64 {
332 match mode {
333 BorderMode::Constant(_) => {
334 if idx < 0.0 || idx >= len {
335 idx = -1.0;
336 }
337 }
338 BorderMode::Nearest => {
339 if idx < 0.0 {
340 idx = 0.0;
341 } else if idx >= len {
342 idx = len - 1.0;
343 }
344 }
345 BorderMode::Mirror => {
346 let s2 = 2.0 * len - 2.0;
347 if idx < 0.0 {
348 idx = s2 * (-idx / s2).floor() + idx;
349 idx = if idx <= 1.0 - len { idx + s2 } else { -idx };
350 } else if idx >= len {
351 idx -= s2 * (idx / s2).floor();
352 if idx >= len {
353 idx = s2 - idx;
354 }
355 }
356 }
357 BorderMode::Reflect => {
358 let s2 = 2.0 * len;
359 if idx < 0.0 {
360 if idx < -s2 {
361 idx = s2 * (-idx / s2).floor() + idx;
362 }
363 idx = if idx < -len { idx + s2 } else { -idx - 1.0 };
364 } else if idx >= len {
365 idx -= s2 * (idx / s2).floor();
366 if idx >= len {
367 idx = s2 - idx - 1.0;
368 }
369 }
370 }
371 BorderMode::Wrap => {
372 let s = len - 1.0;
373 if idx < 0.0 {
374 idx += s * ((-idx / s).floor() + 1.0);
375 } else if idx >= len {
376 idx -= s * (idx / s).floor();
377 }
378 }
379 };
380 idx
381}