Developer documentation
Version 3.0.3-105-gd3941f44
cubic.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __interp_cubic_h__
18#define __interp_cubic_h__
19
20#include "types.h"
21#include "interp/base.h"
22#include "math/cubic_spline.h"
23#include "math/least_squares.h"
24
25namespace MR
26{
27 namespace Interp
28 {
29
31 // @{
32
34
67 // To avoid unnecessary computation, we want to partially specialize our template based
68 // on processing type (value/gradient or both), however each specialization has common core logic
69 // which we store in SplineInterpBase
70
71 template <class ImageType, class SplineType, Math::SplineProcessingType PType>
72 class SplineInterpBase : public Base<ImageType>
74 public:
75 using typename Base<ImageType>::value_type;
76
77 SplineInterpBase (const ImageType& parent, value_type value_when_out_of_bounds = Base<ImageType>::default_out_of_bounds_value()) :
78 Base<ImageType> (parent, value_when_out_of_bounds),
79 H { SplineType(PType), SplineType(PType), SplineType(PType) } { }
80
81 protected:
82 SplineType H[3];
83 Eigen::Vector3d P;
84
85 ssize_t clamp (ssize_t x, ssize_t dim) const {
86 if (x < 0) return 0;
87 if (x >= dim) return (dim-1);
88 return x;
89 }
90 };
91
92
93 template <class ImageType, class SplineType, Math::SplineProcessingType PType>
94 class SplineInterp : public SplineInterpBase <ImageType, SplineType, PType>
96 private:
97 SplineInterp ();
98 };
99
100
101 // Specialization of SplineInterp when we're only after interpolated values
102
103 template <class ImageType, class SplineType>
104 class SplineInterp<ImageType, SplineType, Math::SplineProcessingType::Value>:
105 public SplineInterpBase <ImageType, SplineType, Math::SplineProcessingType::Value>
107 public:
109
110 using value_type = typename SplineBase::value_type;
111 using SplineBase::P;
112 using SplineBase::H;
113 using SplineBase::clamp;
114
115 SplineInterp (const ImageType& parent, value_type value_when_out_of_bounds = SplineBase::default_out_of_bounds_value()) :
117 { }
118
120
121 template <class VectorType>
122 bool voxel (const VectorType& pos) {
123 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos);
125 return false;
126 P = pos;
127 for(size_t i = 0; i < 3; ++i)
128 H[i].set (f[i]);
129
130 // Precompute weights
131 size_t i(0);
132 for (ssize_t z = 0; z < 4; ++z) {
133 for (ssize_t y = 0; y < 4; ++y) {
134 value_type partial_weight = H[1].weights[y] * H[2].weights[z];
135 for (ssize_t x = 0; x < 4; ++x)
136 weights_vec[i++] = H[0].weights[x] * partial_weight;
137 }
138 }
139
140 return true;
141 }
142
144
145 template <class VectorType>
146 FORCE_INLINE bool image (const VectorType& pos) {
147 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
148 }
149
151
152 template <class VectorType>
153 FORCE_INLINE bool scanner (const VectorType& pos) {
154 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
155 }
156
158
159 value_type value () {
162
163 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
164
165 Eigen::Matrix<value_type, 64, 1> coeff_vec;
166
167 size_t i(0);
168 for (ssize_t z = 0; z < 4; ++z) {
169 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
170 for (ssize_t y = 0; y < 4; ++y) {
171 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
172 for (ssize_t x = 0; x < 4; ++x) {
173 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
174 coeff_vec[i++] = ImageType::value ();
175 }
176 }
177 }
178
179 return coeff_vec.dot (weights_vec);
180 }
181
183
184 Eigen::Matrix<value_type, Eigen::Dynamic, 1> row (size_t axis) {
186 Eigen::Matrix<value_type, Eigen::Dynamic, 1> out_of_bounds_row (ImageType::size(axis));
187 out_of_bounds_row.setOnes();
188 out_of_bounds_row *= Base<ImageType>::out_of_bounds_value;
189 return out_of_bounds_row;
190 }
191
192 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
193
194 Eigen::Matrix<value_type, Eigen::Dynamic, 64> coeff_matrix ( ImageType::size(3), 64 );
195
196 size_t i(0);
197 for (ssize_t z = 0; z < 4; ++z) {
198 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
199 for (ssize_t y = 0; y < 4; ++y) {
200 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
201 for (ssize_t x = 0; x < 4; ++x) {
202 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
203 coeff_matrix.col (i++) = ImageType::row (axis);
204 }
205 }
206 }
207
208 return coeff_matrix * weights_vec;
209 }
210
211 protected:
212 Eigen::Matrix<value_type, 64, 1> weights_vec;
213 };
214
215
216 // Specialization of SplineInterp when we're only after interpolated gradients
217
218 template <class ImageType, class SplineType>
219 class SplineInterp<ImageType, SplineType, Math::SplineProcessingType::Derivative>:
220 public SplineInterpBase <ImageType, SplineType, Math::SplineProcessingType::Derivative>
222 public:
224
225 using value_type = typename SplineBase::value_type;
226 using SplineBase::P;
227 using SplineBase::H;
228 using SplineBase::clamp;
229
230 SplineInterp (const ImageType& parent, value_type value_when_out_of_bounds = SplineBase::default_out_of_bounds_value()) :
232 out_of_bounds_vec (value_when_out_of_bounds, value_when_out_of_bounds, value_when_out_of_bounds),
233 wrt_scanner_transform (Transform::scanner2image.linear() * Transform::voxelsize.inverse())
234 {
235 if (ImageType::ndim() == 4) {
236 out_of_bounds_matrix.resize(ImageType::size(3), 3);
237 } else {
238 out_of_bounds_matrix.resize(1, 3);
239 }
240 out_of_bounds_matrix.fill(value_when_out_of_bounds);
241 }
242
244
245 template <class VectorType>
246 bool voxel (const VectorType& pos) {
247 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos);
249 return false;
250 P = pos;
251 for(size_t i =0; i <3; ++i)
252 H[i].set (f[i]);
253
254 // Precompute weights
255 size_t i(0);
256 for (ssize_t z = 0; z < 4; ++z) {
257 for (ssize_t y = 0; y < 4; ++y) {
258 value_type partial_weight = H[1].weights[y] * H[2].weights[z];
259 value_type partial_weight_dy = H[1].deriv_weights[y] * H[2].weights[z];
260 value_type partial_weight_dz = H[1].weights[y] * H[2].deriv_weights[z];
261
262 for (ssize_t x = 0; x < 4; ++x) {
263 weights_matrix(i,0) = H[0].deriv_weights[x] * partial_weight;
264 weights_matrix(i,1) = H[0].weights[x] * partial_weight_dy;
265 weights_matrix(i,2) = H[0].weights[x] * partial_weight_dz;
266 ++i;
267 }
268 }
269 }
270
271
272 return true;
273 }
274
276
277 template <class VectorType>
278 FORCE_INLINE bool image (const VectorType& pos) {
279 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
280 }
282
283 template <class VectorType>
284 FORCE_INLINE bool scanner (const VectorType& pos) {
285 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
286 }
287
289 Eigen::Matrix<value_type, 1, 3> gradient () {
291 return out_of_bounds_vec;
292
293 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
294
295 Eigen::Matrix<value_type, 1, 64> coeff_vec;
296
297 size_t i(0);
298 for (ssize_t z = 0; z < 4; ++z) {
299 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
300 for (ssize_t y = 0; y < 4; ++y) {
301 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
302 for (ssize_t x = 0; x < 4; ++x) {
303 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
304 coeff_vec[i++] = ImageType::value ();
305 }
306 }
307 }
308
309 return coeff_vec * weights_matrix;
310 }
311
312
314 Eigen::Matrix<default_type, Eigen::Dynamic, Eigen::Dynamic> gradient_wrt_scanner() {
315 return gradient().template cast<default_type>() * wrt_scanner_transform;
316 }
317
318 // Collectively interpolates gradients along axis 3
319 Eigen::Matrix<value_type, Eigen::Dynamic, 3> gradient_row() {
321 return out_of_bounds_matrix;
322 }
323
324 assert (ImageType::ndim() == 4);
325
326 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
327
328 Eigen::Matrix<value_type, Eigen::Dynamic, 64> coeff_matrix (ImageType::size(3), 64);
329
330 size_t i(0);
331 for (ssize_t z = 0; z < 4; ++z) {
332 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
333 for (ssize_t y = 0; y < 4; ++y) {
334 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
335 for (ssize_t x = 0; x < 4; ++x) {
336 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
337 coeff_matrix.col (i++) = ImageType::row (3);
338 }
339 }
340 }
341
342 return coeff_matrix * weights_matrix;
343 }
344
345
347 Eigen::Matrix<default_type, Eigen::Dynamic, 3> gradient_row_wrt_scanner() {
348 return gradient_row().template cast<default_type>() * wrt_scanner_transform;
349 }
350
351 protected:
352 const Eigen::Matrix<value_type, 1, 3> out_of_bounds_vec;
353 Eigen::Matrix<value_type, Eigen::Dynamic, 3> out_of_bounds_matrix;
354 Eigen::Matrix<value_type, 64, 3> weights_matrix;
355 const Eigen::Matrix<default_type, 3, 3> wrt_scanner_transform;
356
357 private:
358 Eigen::Matrix<value_type, Eigen::Dynamic, 1> row() { }
359 value_type value () { }
360 };
361
362
363 // Specialization of SplineInterp when we're after both interpolated gradients and values
364 template <class ImageType, class SplineType>
365 class SplineInterp<ImageType, SplineType, Math::SplineProcessingType::ValueAndDerivative>:
366 public SplineInterpBase <ImageType, SplineType, Math::SplineProcessingType::ValueAndDerivative>
368 public:
370
371 using value_type = typename SplineBase::value_type;
372 using SplineBase::P;
373 using SplineBase::H;
374 using SplineBase::clamp;
375
376 SplineInterp (const ImageType& parent, value_type value_when_out_of_bounds = SplineBase::default_out_of_bounds_value()) :
378 wrt_scanner_transform (Transform::scanner2image.linear() * Transform::voxelsize.inverse())
379 {
380 if (ImageType::ndim() == 4) {
381 out_of_bounds_vec.resize(ImageType::size(3), 1);
382 out_of_bounds_matrix.resize(ImageType::size(3), 3);
383 } else {
384 out_of_bounds_vec.resize(1, 1);
385 out_of_bounds_matrix.resize(1, 3);
386 }
387 out_of_bounds_vec.fill(value_when_out_of_bounds);
388 out_of_bounds_matrix.fill(value_when_out_of_bounds);
389 }
390
392
393 template <class VectorType>
394 FORCE_INLINE bool image (const VectorType& pos) {
395 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
396 }
398
399 template <class VectorType>
400 FORCE_INLINE bool scanner (const VectorType& pos) {
401 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
402 }
403
405
406 template <class VectorType>
407 bool voxel (const VectorType& pos) {
408 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos);
410 return false;
411 P = pos;
412 for (size_t i = 0; i < 3; ++i)
413 H[i].set (f[i]);
414
415 // Precompute weights
416 size_t i(0);
417 for (ssize_t z = 0; z < 4; ++z) {
418 for (ssize_t y = 0; y < 4; ++y) {
419 value_type partial_weight = H[1].weights[y] * H[2].weights[z];
420 value_type partial_weight_dy = H[1].deriv_weights[y] * H[2].weights[z];
421 value_type partial_weight_dz = H[1].weights[y] * H[2].deriv_weights[z];
422
423 for (ssize_t x = 0; x < 4; ++x) {
424 // Gradient
425 weights_matrix(i,0) = H[0].deriv_weights[x] * partial_weight;
426 weights_matrix(i,1) = H[0].weights[x] * partial_weight_dy;
427 weights_matrix(i,2) = H[0].weights[x] * partial_weight_dz;
428 // Value
429 weights_matrix(i,3) = H[0].weights[x] * partial_weight;
430 ++i;
431 }
432 }
433 }
434
435 return true;
436 }
437
438 void value_and_gradient (value_type& value, Eigen::Matrix<value_type, 1, 3>& gradient) {
440 value = out_of_bounds_vec(0);
441 gradient = out_of_bounds_matrix.row(0);
442 return;
443 }
444
445 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
446
447 Eigen::Matrix<value_type, 1, 64> coeff_vec;
448
449 size_t i(0);
450 for (ssize_t z = 0; z < 4; ++z) {
451 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
452 for (ssize_t y = 0; y < 4; ++y) {
453 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
454 for (ssize_t x = 0; x < 4; ++x) {
455 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
456 coeff_vec[i++] = ImageType::value ();
457 }
458 }
459 }
460 Eigen::Matrix<value_type, 1, 4> grad_and_value (coeff_vec * weights_matrix);
461
462 gradient = grad_and_value.head(3);
463 value = grad_and_value[3];
464 }
465
466 void value_and_gradient_wrt_scanner (value_type& value, Eigen::Matrix<value_type, 1, 3>& gradient) {
467 value_and_gradient(value, gradient);
469 return;
470 gradient = (gradient.template cast<default_type>() * wrt_scanner_transform).eval();
471 }
472
473 // Simultaneously get both the image value and gradient in 4D
474 void value_and_gradient_row (Eigen::Matrix<value_type, Eigen::Dynamic, 1>& value, Eigen::Matrix<value_type, Eigen::Dynamic, 3>& gradient) {
476 value = out_of_bounds_vec;
477 gradient = out_of_bounds_matrix;
478 return;
479 }
480
481 assert (ImageType::ndim() == 4);
482
483 ssize_t c[] = { ssize_t (std::floor (P[0])-1), ssize_t (std::floor (P[1])-1), ssize_t (std::floor (P[2])-1) };
484
485 Eigen::Matrix<value_type, Eigen::Dynamic, 64> coeff_matrix (ImageType::size(3), 64);
486
487 size_t i(0);
488 for (ssize_t z = 0; z < 4; ++z) {
489 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
490 for (ssize_t y = 0; y < 4; ++y) {
491 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
492 for (ssize_t x = 0; x < 4; ++x) {
493 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
494 coeff_matrix.col (i++) = ImageType::row (3);
495 }
496 }
497 }
498 Eigen::Matrix<value_type, Eigen::Dynamic, 4> grad_and_value (coeff_matrix * weights_matrix);
499 gradient = grad_and_value.block(0,0,ImageType::size(3),3);
500 value = grad_and_value.col(3);
501 }
502
503 void value_and_gradient_row_wrt_scanner (Eigen::Matrix<value_type, Eigen::Dynamic, 1>& value, Eigen::Matrix<value_type, Eigen::Dynamic, 3>& gradient) {
504 value_and_gradient_row(value, gradient);
506 return;
507 }
508 gradient = (gradient.template cast<default_type>() * wrt_scanner_transform).eval();
509 }
510
511 protected:
512 Eigen::Matrix<value_type, 64, 4> weights_matrix;
513 const Eigen::Matrix<default_type, 3, 3> wrt_scanner_transform;
514 Eigen::Matrix<value_type, Eigen::Dynamic, 1> out_of_bounds_vec;
515 Eigen::Matrix<value_type, Eigen::Dynamic, 3> out_of_bounds_matrix;
516 };
517
518
519 // Template alias for default Cubic interpolator
520 // This allows an interface that's consistent with other interpolators that all have one template argument
521 template <typename ImageType>
523
524 template <typename ImageType>
526
527
528 template <class ImageType, typename... Args>
529 inline Cubic<ImageType> make_cubic (const ImageType& parent, Args&&... args) {
530 return Cubic<ImageType> (parent, std::forward<Args> (args)...);
531 }
532
533
534
536
537 }
538}
539
540#endif
541
542
This class defines the interface for classes that perform image interpolation.
Definition: base.h:70
Eigen::Vector3d intravoxel_offset(const VectorType &pos)
Definition: base.h:211
This class provides access to the voxel intensities of an image using cubic spline interpolation.
Definition: cubic.h:73
Eigen::Vector3d P
Definition: cubic.h:83
ssize_t clamp(ssize_t x, ssize_t dim) const
Definition: cubic.h:85
const transform_type scanner2image
Definition: transform.h:43
const Eigen::DiagonalMatrix< default_type, 3 > voxelsize
Definition: transform.h:42
const transform_type scanner2voxel
Definition: transform.h:43
constexpr I floor(const T x)
template function with cast to different type
Definition: math.h:75
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
Definition: SH.h:233
Cubic< ImageType > make_cubic(const ImageType &parent, Args &&... args)
Definition: cubic.h:529
@ ValueAndDerivative
Definition: linear.h:89
@ Derivative
Definition: linear.h:88
@ Value
Definition: linear.h:87
T eval(const double *coef, const int order, const T lower, const T upper, const T x)
Definition: chebyshev.h:29
MR::default_type value_type
Definition: typedefs.h:33
void set(HeaderType &header, const List &stride)
set the strides of header from a vector<ssize_t>
Definition: stride.h:135
Definition: base.h:24
int axis
Eigen::MatrixXd H
size_t index
#define MEMALIGN(...)
Definition: types.h:185
#define FORCE_INLINE
Definition: types.h:156