Developer documentation
Version 3.0.3-105-gd3941f44
linear.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_linear_h__
18#define __interp_linear_h__
19
20#include <complex>
21#include <type_traits>
22
23#include "datatype.h"
24#include "types.h"
25#include "interp/base.h"
26
27
28namespace MR
29{
30 namespace Interp
31 {
32
34 // @{
35
37
72 template <class C>
75 using type = C;
76 };
77
78 template <class X>
79 struct value_type_of<std::complex<X>>
81 using type = X;
82 };
83
84
86 {
87 Value = 1,
90 };
91
92 // To avoid unnecessary computation, we want to partially specialize our template based
93 // on processing type (value/gradient or both), however each specialization has common core logic
94 // which we store in LinearInterpBase
95
96 template <class ImageType, LinearInterpProcessingType PType>
98 public:
99 using typename Base<ImageType>::value_type;
100 using coef_type = typename value_type_of<value_type>::type;
101
102
103 LinearInterpBase (const ImageType& parent, value_type value_when_out_of_bounds = Base<ImageType>::default_out_of_bounds_value()) :
104 Base<ImageType> (parent, value_when_out_of_bounds),
105 zero (0.0),
106 eps (1.0e-6) { }
107
108 protected:
109 const coef_type zero, eps;
110 Eigen::Vector3d P;
111
112 ssize_t clamp (ssize_t x, ssize_t dim) const {
113 if (x < 0) return 0;
114 if (x >= dim) return (dim-1);
115 return x;
116 }
117 };
118
119
120 template <class ImageType, LinearInterpProcessingType PType>
122 private:
123 LinearInterp ();
124 };
125
126
127 // Specialization of LinearInterp when we're only after interpolated values
128
129 template <class ImageType>
131 public LinearInterpBase<ImageType, LinearInterpProcessingType::Value>
133 public:
135
136 using value_type = typename LinearBase::value_type;
137 using coef_type = typename LinearBase::coef_type;
138 using LinearBase::P;
139 using LinearBase::clamp;
140 using LinearBase::bounds;
141 using LinearBase::eps;
142
143 LinearInterp (const ImageType& parent, value_type value_when_out_of_bounds = Base<ImageType>::default_out_of_bounds_value()) :
145 { }
146
148
149 template <class VectorType>
150 bool voxel (const VectorType& pos) {
151 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos);
153 return false;
154 P = pos;
155 for (size_t i = 0; i < 3; ++i) {
156 if (pos[i] < 0.0 || pos[i] > bounds[i]-0.5)
157 f[i] = 0.0;
158 }
159
160 coef_type x_weights[2] = { coef_type(1 - f[0]), coef_type(f[0]) };
161 coef_type y_weights[2] = { coef_type(1 - f[1]), coef_type(f[1]) };
162 coef_type z_weights[2] = { coef_type(1 - f[2]), coef_type(f[2]) };
163
164 size_t i(0);
165 for (ssize_t z = 0; z < 2; ++z) {
166 for (ssize_t y = 0; y < 2; ++y) {
167 coef_type partial_weight = y_weights[y] * z_weights[z];
168 for (ssize_t x = 0; x < 2; ++x) {
169 factors[i] = x_weights[x] * partial_weight;
170
171 if (factors[i] < eps)
172 factors[i] = 0.0;
173
174 ++i;
175 }
176 }
177 }
178
179 return true;
180 }
181
183
184 template <class VectorType>
185 FORCE_INLINE bool image (const VectorType& pos) {
186 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
187 }
188
190
191 template <class VectorType>
192 FORCE_INLINE bool scanner (const VectorType& pos) {
193 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
194 }
195
196 FORCE_INLINE value_type value () {
199
200 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
201
202 Eigen::Matrix<value_type, 8, 1> coeff_vec;
203
204 size_t i(0);
205 for (ssize_t z = 0; z < 2; ++z) {
206 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
207 for (ssize_t y = 0; y < 2; ++y) {
208 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
209 for (ssize_t x = 0; x < 2; ++x) {
210 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
211 coeff_vec[i++] = ImageType::value ();
212 }
213 }
214 }
215
216 return coeff_vec.dot (factors);
217 }
218
220
221 Eigen::Matrix<value_type, Eigen::Dynamic, 1> row (size_t axis) {
223 Eigen::Matrix<value_type, Eigen::Dynamic, 1> out_of_bounds_row (ImageType::size(axis));
224 out_of_bounds_row.setOnes();
225 out_of_bounds_row *= Base<ImageType>::out_of_bounds_value;
226 return out_of_bounds_row;
227 }
228
229 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
230
231 Eigen::Matrix<value_type, Eigen::Dynamic, 8> coeff_matrix ( ImageType::size(3), 8 );
232
233 size_t i(0);
234 for (ssize_t z = 0; z < 2; ++z) {
235 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
236 for (ssize_t y = 0; y < 2; ++y) {
237 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
238 for (ssize_t x = 0; x < 2; ++x) {
239 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
240 coeff_matrix.col (i++) = ImageType::row (axis);
241 }
242 }
243 }
244
245 return coeff_matrix * factors;
246 }
247
248 protected:
249 Eigen::Matrix<coef_type, 8, 1> factors;
250 };
251
252
253 // Specialization of LinearInterp when we're only after interpolated gradients
254
255 template <class ImageType>
257 public LinearInterpBase<ImageType, LinearInterpProcessingType::Derivative>
259 public:
261
262 using value_type = typename LinearBase::value_type;
263 using coef_type = typename LinearBase::coef_type;
264 using LinearBase::P;
265 using LinearBase::clamp;
266 using LinearBase::bounds;
267 using LinearBase::voxelsize;
268
269 LinearInterp (const ImageType& parent, value_type value_when_out_of_bounds = Base<ImageType>::default_out_of_bounds_value()) :
271 out_of_bounds_vec (value_when_out_of_bounds, value_when_out_of_bounds, value_when_out_of_bounds),
272 wrt_scanner_transform (Transform::scanner2image.linear() * voxelsize.inverse())
273 { }
274
276
277 template <class VectorType>
278 bool voxel (const VectorType& pos) {
279 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos.template cast<default_type>());
281 return false;
282 P = pos;
283 for (size_t i = 0; i < 3; ++i) {
284 if (pos[i] < 0.0 || pos[i] > bounds[i]-0.5)
285 f[i] = 0.0;
286 }
287
288 coef_type x_weights[2] = {coef_type(1 - f[0]), coef_type(f[0])};
289 coef_type y_weights[2] = {coef_type(1 - f[1]), coef_type(f[1])};
290 coef_type z_weights[2] = {coef_type(1 - f[2]), coef_type(f[2])};
291
292 // For linear interpolation gradient weighting is independent of direction
293 // i.e. Simply looking at finite difference
294 coef_type diff_weights[2] = {-0.5, 0.5};
295
296 size_t i(0);
297 for (ssize_t z = 0; z < 2; ++z) {
298 for (ssize_t y = 0; y < 2; ++y) {
299 coef_type partial_weight = y_weights[y] * z_weights[z];
300 coef_type partial_weight_dy = diff_weights[y] * z_weights[z];
301 coef_type partial_weight_dz = y_weights[y] * diff_weights[z];
302
303 for (ssize_t x = 0; x < 2; ++x) {
304 weights_matrix(i,0) = diff_weights[x] * partial_weight;
305 weights_matrix(i,1) = x_weights[x] * partial_weight_dy;
306 weights_matrix(i,2) = x_weights[x] * partial_weight_dz;
307
308 ++i;
309 }
310 }
311 }
312
313 return true;
314 }
315
317
318 template <class VectorType>
319 FORCE_INLINE bool image (const VectorType& pos) {
320 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
321 }
322
324
325 template <class VectorType>
326 FORCE_INLINE bool scanner (const VectorType& pos) {
327 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
328 }
329
331 FORCE_INLINE Eigen::Matrix<value_type, 1, 3> gradient () {
333 return out_of_bounds_vec;
334
335 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
336
337 Eigen::Matrix<coef_type, 1, 8> coeff_vec;
338
339 size_t i(0);
340 for (ssize_t z = 0; z < 2; ++z) {
341 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
342 for (ssize_t y = 0; y < 2; ++y) {
343 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
344 for (ssize_t x = 0; x < 2; ++x) {
345 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
346 coeff_vec[i++] = ImageType::value ();
347 }
348 }
349 }
350
351 return coeff_vec * weights_matrix;
352 }
353
355 Eigen::Matrix<default_type, Eigen::Dynamic, Eigen::Dynamic> gradient_wrt_scanner() {
356 return gradient().template cast<default_type>() * wrt_scanner_transform;
357 }
358
359 // Collectively interpolates gradients along axis 3
360 Eigen::Matrix<coef_type, Eigen::Dynamic, 3> gradient_row() {
362 Eigen::Matrix<coef_type, Eigen::Dynamic, 3> out_of_bounds_matrix (ImageType::size(3), 3);
363 out_of_bounds_matrix.setOnes();
364 out_of_bounds_matrix *= Base<ImageType>::out_of_bounds_value;
365 return out_of_bounds_matrix;
366 }
367
368 assert (ImageType::ndim() == 4);
369
370 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
371
372 Eigen::Matrix<value_type, Eigen::Dynamic, 8> coeff_matrix (ImageType::size(3), 8);
373
374 size_t i(0);
375 for (ssize_t z = 0; z < 2; ++z) {
376 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
377 for (ssize_t y = 0; y < 2; ++y) {
378 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
379 for (ssize_t x = 0; x < 2; ++x) {
380 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
381 coeff_matrix.col (i++) = ImageType::row (3);
382 }
383 }
384 }
385
386 return coeff_matrix * weights_matrix;
387 }
388
389
391 Eigen::Matrix<default_type, Eigen::Dynamic, 3> gradient_row_wrt_scanner() {
392 return gradient_row().template cast<default_type>() * wrt_scanner_transform;
393 }
394
395 protected:
396 const Eigen::Matrix<coef_type, 1, 3> out_of_bounds_vec;
397 Eigen::Matrix<coef_type, 8, 3> weights_matrix;
398 const Eigen::Matrix<default_type, 3, 3> wrt_scanner_transform;
399 };
400
401
402
403 // Specialization of LinearInterp when we're after both interpolated gradients and values
404 template <class ImageType>
406 public LinearInterpBase <ImageType, LinearInterpProcessingType::ValueAndDerivative>
408 public:
410
411 using value_type = typename LinearBase::value_type;
412 using coef_type = typename LinearBase::coef_type;
413 using LinearBase::P;
414 using LinearBase::clamp;
415 using LinearBase::bounds;
416 using LinearBase::voxelsize;
417
418 LinearInterp (const ImageType& parent, coef_type value_when_out_of_bounds = Base<ImageType>::default_out_of_bounds_value()) :
420 wrt_scanner_transform (Transform::scanner2image.linear() * voxelsize.inverse())
421 {
422 if (ImageType::ndim() == 4) {
423 out_of_bounds_vec.resize(ImageType::size(3), 1);
424 out_of_bounds_matrix.resize(ImageType::size(3), 3);
425 } else {
426 out_of_bounds_vec.resize(1, 1);
427 out_of_bounds_matrix.resize(1, 3);
428 }
429 out_of_bounds_vec.fill(value_when_out_of_bounds);
430 out_of_bounds_matrix.fill(value_when_out_of_bounds);
431 }
432
433 value_type value () { assert( 0 && "do not call value() on ValueAndDerivative interpolator." ); }
434
436
437 template <class VectorType>
438 bool voxel (const VectorType& pos) {
439 Eigen::Vector3d f = Base<ImageType>::intravoxel_offset (pos.template cast<default_type>());
441 return false;
442 P = pos;
443 for (size_t i = 0; i < 3; ++i) {
444 if (pos[i] < 0.0 || pos[i] > bounds[i]-0.5)
445 f[i] = 0.0;
446 }
447
448 coef_type x_weights[2] = { coef_type(1 - f[0]), coef_type(f[0]) };
449 coef_type y_weights[2] = { coef_type(1 - f[1]), coef_type(f[1]) };
450 coef_type z_weights[2] = { coef_type(1 - f[2]), coef_type(f[2]) };
451
452 // For linear interpolation gradient weighting is independent of direction
453 // i.e. Simply looking at finite difference
454 coef_type diff_weights[2] = {coef_type(-0.5), coef_type(0.5) };
455
456 size_t i(0);
457 for (ssize_t z = 0; z < 2; ++z) {
458 for (ssize_t y = 0; y < 2; ++y) {
459 coef_type partial_weight = y_weights[y] * z_weights[z];
460 coef_type partial_weight_dy = diff_weights[y] * z_weights[z];
461 coef_type partial_weight_dz = y_weights[y] * diff_weights[z];
462
463 for (ssize_t x = 0; x < 2; ++x) {
464 // Gradient
465 weights_matrix(i,0) = diff_weights[x] * partial_weight;
466 weights_matrix(i,1) = x_weights[x] * partial_weight_dy;
467 weights_matrix(i,2) = x_weights[x] * partial_weight_dz;
468 // Value
469 weights_matrix(i,3) = x_weights[x] * partial_weight;
470
471 ++i;
472 }
473 }
474 }
475
476 return true;
477 }
478
480
481 template <class VectorType>
482 FORCE_INLINE bool image (const VectorType& pos) {
483 return voxel (Transform::voxelsize.inverse() * pos.template cast<default_type>());
484 }
485
487
488 template <class VectorType>
489 FORCE_INLINE bool scanner (const VectorType& pos) {
490 return voxel (Transform::scanner2voxel * pos.template cast<default_type>());
491 }
492
493
494 void value_and_gradient (value_type& value, Eigen::Matrix<coef_type, 1, 3>& gradient) {
496 value = out_of_bounds_vec(0);
497 gradient.fill(out_of_bounds_vec(0));
498 return;
499 }
500
501 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
502
503 Eigen::Matrix<value_type, 1, 8> coeff_vec;
504
505 size_t i(0);
506 for (ssize_t z = 0; z < 2; ++z) {
507 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
508 for (ssize_t y = 0; y < 2; ++y) {
509 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
510 for (ssize_t x = 0; x < 2; ++x) {
511 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
512 coeff_vec[i] = ImageType::value ();
513 ++i;
514 }
515 }
516 }
517
518 Eigen::Matrix<value_type, 1, 4> grad_and_value (coeff_vec * weights_matrix);
519
520 gradient = grad_and_value.head(3);
521 value = grad_and_value[3];
522 }
523
524 void value_and_gradient_wrt_scanner (value_type& value, Eigen::Matrix<coef_type, 1, 3>& gradient) {
526 value = out_of_bounds_vec(0);
527 gradient.fill(out_of_bounds_vec(0));
528 return;
529 }
530 value_and_gradient(value, gradient);
531 gradient = (gradient.template cast<default_type>() * wrt_scanner_transform).eval();
532 }
533
534 // Collectively interpolates gradients and values along axis 3
535 void value_and_gradient_row (Eigen::Matrix<value_type, Eigen::Dynamic, 1>& value, Eigen::Matrix<value_type, Eigen::Dynamic, 3>& gradient) {
537 value = out_of_bounds_vec;
538 gradient = out_of_bounds_matrix;
539 return;
540 }
541
542 assert (ImageType::ndim() == 4);
543
544 ssize_t c[] = { ssize_t (std::floor (P[0])), ssize_t (std::floor (P[1])), ssize_t (std::floor (P[2])) };
545
546 Eigen::Matrix<value_type, Eigen::Dynamic, 8> coeff_matrix (ImageType::size(3), 8);
547
548 size_t i(0);
549 for (ssize_t z = 0; z < 2; ++z) {
550 ImageType::index(2) = clamp (c[2] + z, ImageType::size (2));
551 for (ssize_t y = 0; y < 2; ++y) {
552 ImageType::index(1) = clamp (c[1] + y, ImageType::size (1));
553 for (ssize_t x = 0; x < 2; ++x) {
554 ImageType::index(0) = clamp (c[0] + x, ImageType::size (0));
555 coeff_matrix.col (i++) = ImageType::row (3);
556 }
557 }
558 }
559
560 Eigen::Matrix<value_type, Eigen::Dynamic, 4> grad_and_value (coeff_matrix * weights_matrix);
561 gradient = grad_and_value.block(0, 0, ImageType::size(3), 3);
562 value = grad_and_value.col(3);
563 }
564
565 void value_and_gradient_row_wrt_scanner (Eigen::Matrix<value_type, Eigen::Dynamic, 1>& value, Eigen::Matrix<value_type, Eigen::Dynamic, 3>& gradient) {
567 value = out_of_bounds_vec;
568 gradient = out_of_bounds_matrix;
569 return;
570 }
571 value_and_gradient_row(value, gradient);
572 gradient = (gradient.template cast<default_type>() * wrt_scanner_transform).eval();
573 }
574
575 protected:
576 Eigen::Matrix<default_type, 3, 3> wrt_scanner_transform;
577 Eigen::Matrix<value_type, 8, 4> weights_matrix;
578 Eigen::Matrix<coef_type, Eigen::Dynamic, 1> out_of_bounds_vec;
579 Eigen::Matrix<coef_type, Eigen::Dynamic, 3> out_of_bounds_matrix;
580
581 };
582
583 // Template alias for default Linear interpolator
584 // This allows an interface that's consistent with other interpolators that all have one template argument
585 template <typename ImageType>
587
588 template <class ImageType, typename... Args>
589 inline Linear<ImageType> make_linear (const ImageType& parent, Args&&... args) {
590 return Linear<ImageType> (parent, std::forward<Args> (args)...);
591 }
592
594
595 }
596}
597
598#endif
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
default_type bounds[3]
Definition: base.h:194
const Eigen::Matrix< default_type, 3, 3 > wrt_scanner_transform
Definition: linear.h:398
const coef_type eps
Definition: linear.h:109
ssize_t clamp(ssize_t x, ssize_t dim) const
Definition: linear.h:112
Eigen::Vector3d P
Definition: linear.h:110
const coef_type zero
Definition: linear.h:109
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
#define NOMEMALIGN
Definition: memory.h:22
Linear< ImageType > make_linear(const ImageType &parent, Args &&... args)
Definition: linear.h:589
LinearInterpProcessingType
Definition: linear.h:86
@ 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
Definition: base.h:24
Definition: types.h:303
int axis
size_t index
#define MEMALIGN(...)
Definition: types.h:185
#define FORCE_INLINE
Definition: types.h:156
This class provides access to the voxel intensities of a data set, using tri-linear interpolation.
Definition: linear.h:74