FastLinearImageGradientFunction.hxx
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2017 Imperial College London
5  * Copyright 2013-2017 Andreas Schuh
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifndef MIRTK_FastLinearImageGradientFunction_HXX
21 #define MIRTK_FastLinearImageGradientFunction_HXX
22 
23 #include "mirtk/FastLinearImageGradientFunction.h"
24 #include "mirtk/ImageGradientFunction.hxx"
25 
26 
27 namespace mirtk {
28 
29 
30 // =============================================================================
31 // Construction/Destruction
32 // =============================================================================
33 
34 // -----------------------------------------------------------------------------
35 template <class TImage>
38 {
39 }
40 
41 // -----------------------------------------------------------------------------
42 template <class TImage>
45 {
46 }
47 
48 // -----------------------------------------------------------------------------
49 template <class TImage>
51 {
52  // Initialize base class
53  Superclass::Initialize(coeff);
54 
55  // Domain on which linear interpolation is defined: [0, x)
56  switch (this->NumberOfDimensions()) {
57  case 4:
58  this->_t1 = 0.;
59  this->_t2 = fdec(this->Input()->T() - 1);
60  case 3:
61  this->_z1 = 0.;
62  this->_z2 = fdec(this->Input()->Z() - 1);
63  default:
64  this->_y1 = 0.;
65  this->_y2 = fdec(this->Input()->Y() - 1);
66  this->_x1 = 0.;
67  this->_x2 = fdec(this->Input()->X() - 1);
68  }
69 }
70 
71 // =============================================================================
72 // Domain checks
73 // =============================================================================
74 
75 // -----------------------------------------------------------------------------
76 template <class TImage>
78 ::BoundingInterval(double x, int &i, int &I) const
79 {
80  i = ifloor(x), I = i + 1;
81 }
82 
83 // =============================================================================
84 // Interpolation weights
85 // =============================================================================
86 
87 // -----------------------------------------------------------------------------
88 template <class TImage>
90 ::ComputeWeights(double x, Real w[2])
91 {
92  const int i = ifloor(x);
93  w[1] = static_cast<Real>(x) - static_cast<Real>(i);
94  w[0] = static_cast<Real>(1) - w[1];
95  return i;
96 }
97 
98 // =============================================================================
99 // Evaluation
100 // =============================================================================
101 
102 // -----------------------------------------------------------------------------
103 template <class TImage>
106 ::Get2D(double x, double y, double z, double t) const
107 {
108  Real wx[2], wy[2], wd[2] = {Real(-1), Real(1)};
109  const int i = ComputeWeights(x, wx);
110  const int j = ComputeWeights(y, wy);
111  const int k = iround(z);
112  const int l = iround(t);
113 
114  if (k < 0 || k >= this->Input()->Z() ||
115  l < 0 || l >= this->Input()->T()) {
116  return this->DefaultValue();
117  }
118 
119  GradientType val(.0);
120  Real nrm(0), coeff;
121 
122  int ia, jb;
123  for (int b = 0; b <= 1; ++b) {
124  jb = j + b;
125  if (0 <= jb && jb < this->Input()->Y()) {
126  for (int a = 0; a <= 1; ++a) {
127  ia = i + a;
128  if (0 <= ia && ia < this->Input()->X()) {
129  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, k, l));
130  val._x += wd[a] * wy[b] * coeff;
131  val._y += wx[a] * wd[b] * coeff;
132  nrm += wx[a] * wy[b];
133  }
134  }
135  }
136  }
137 
138  if (nrm > 1e-3) val /= nrm;
139  else val = this->DefaultValue();
140 
141  return val;
142 }
143 
144 // -----------------------------------------------------------------------------
145 template <class TImage>
148 ::GetWithPadding2D(double x, double y, double z, double t) const
149 {
150  Real wx[2], wy[2], wd[2] = {Real(-1), Real(1)};
151  const int i = ComputeWeights(x, wx);
152  const int j = ComputeWeights(y, wy);
153  const int k = iround(z);
154  const int l = iround(t);
155 
156  if (k < 0 || k >= this->Input()->Z() ||
157  l < 0 || l >= this->Input()->T()) {
158  return this->DefaultValue();
159  }
160 
161  GradientType val(.0);
162  Real coeff;
163 
164  int ia, jb;
165  for (int b = 0; b <= 1; ++b) {
166  jb = j + b;
167  for (int a = 0; a <= 1; ++a) {
168  ia = i + a;
169  if (this->Input()->IsInsideForeground(ia, jb, k, l)) {
170  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, k, l));
171  val._x += wd[a] * wy[b] * coeff;
172  val._y += wx[a] * wd[b] * coeff;
173  } else {
174  return this->DefaultValue();
175  }
176  }
177  }
178 
179  return val;
180 }
181 
182 // -----------------------------------------------------------------------------
183 template <class TImage> template <class TOtherImage>
186 ::Get2D(const TOtherImage *input, double x, double y, double z, double t) const
187 {
188  Real wx[2], wy[2], wd[2] = {Real(-1), Real(1)};
189  const int i = ComputeWeights(x, wx);
190  const int j = ComputeWeights(y, wy);
191  const int k = iround(z);
192  const int l = iround(t);
193 
194  GradientType val(.0);
195  Real coeff;
196 
197  int ia, jb;
198  for (int b = 0; b <= 1; ++b) {
199  jb = j + b;
200  for (int a = 0; a <= 1; ++a) {
201  ia = i + a;
202  coeff = voxel_cast<Real>(input->Get(ia, jb, k, l));
203  val._x += wd[a] * wy[b] * coeff;
204  val._y += wx[a] * wd[b] * coeff;
205  }
206  }
207 
208  return val;
209 }
210 
211 // -----------------------------------------------------------------------------
212 template <class TImage> template <class TOtherImage>
215 ::GetWithPadding2D(const TOtherImage *input, double x, double y, double z, double t) const
216 {
217  Real wx[2], wy[2], wd[2] = {Real(-1), Real(1)};
218  const int i = ComputeWeights(x, wx);
219  const int j = ComputeWeights(y, wy);
220  const int k = iround(z);
221  const int l = iround(t);
222 
223  if (k < 0 || k >= input->Z() ||
224  l < 0 || l >= input->T()) {
225  return this->DefaultValue();
226  }
227 
228  GradientType val(.0);
229  Real coeff;
230 
231  int ia, jb;
232  for (int b = 0; b <= 1; ++b) {
233  jb = j + b;
234  for (int a = 0; a <= 1; ++a) {
235  ia = i + a;
236  if (input->IsForeground(ia, jb, k, l)) {
237  coeff = voxel_cast<Real>(input->Get(ia, jb, k, l));
238  val._x += wd[a] * wy[b] * coeff;
239  val._y += wx[a] * wd[b] * coeff;
240  } else {
241  return this->DefaultValue();
242  }
243  }
244  }
245 
246  return val;
247 }
248 
249 // -----------------------------------------------------------------------------
250 template <class TImage>
253 ::Get3D(double x, double y, double z, double t) const
254 {
255  Real wx[2], wy[2], wz[2], wd[2] = {Real(-1), Real(1)};
256  const int i = ComputeWeights(x, wx);
257  const int j = ComputeWeights(y, wy);
258  const int k = ComputeWeights(z, wz);
259  const int l = iround(t);
260 
261  if (l < 0 || l >= this->Input()->T()) {
262  return this->DefaultValue();
263  }
264 
265  GradientType val(.0);
266  Real nrm(0), coeff;
267 
268  int ia, jb, kc;
269  for (int c = 0; c <= 1; ++c) {
270  kc = k + c;
271  if (0 <= kc && kc < this->Input()->Z()) {
272  for (int b = 0; b <= 1; ++b) {
273  jb = j + b;
274  if (0 <= jb && jb < this->Input()->Y()) {
275  for (int a = 0; a <= 1; ++a) {
276  ia = i + a;
277  if (0 <= ia && ia < this->Input()->X()) {
278  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, kc, l));
279  val._x += wd[a] * wy[b] * wz[c] * coeff;
280  val._y += wx[a] * wd[b] * wz[c] * coeff;
281  val._z += wx[a] * wy[b] * wd[c] * coeff;
282  nrm += wx[a] * wy[b] * wz[c];
283  }
284  }
285  }
286  }
287  }
288  }
289 
290  if (nrm > 1e-3) val /= nrm;
291  else val = this->DefaultValue();
292 
293  return val;
294 }
295 
296 // -----------------------------------------------------------------------------
297 template <class TImage>
300 ::GetWithPadding3D(double x, double y, double z, double t) const
301 {
302  Real wx[2], wy[2], wz[2], wd[2] = {Real(-1), Real(1)};
303  const int i = ComputeWeights(x, wx);
304  const int j = ComputeWeights(y, wy);
305  const int k = ComputeWeights(z, wz);
306  const int l = iround(t);
307 
308  if (l < 0 || l >= this->Input()->T()) {
309  return this->DefaultValue();
310  }
311 
312  GradientType val(.0);
313  Real coeff;
314 
315  int ia, jb, kc;
316  for (int c = 0; c <= 1; ++c) {
317  kc = k + c;
318  for (int b = 0; b <= 1; ++b) {
319  jb = j + b;
320  for (int a = 0; a <= 1; ++a) {
321  ia = i + a;
322  if (this->Input()->IsInsideForeground(ia, jb, kc, l)) {
323  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, kc, l));
324  val._x += wd[a] * wy[b] * wz[c] * coeff;
325  val._y += wx[a] * wd[b] * wz[c] * coeff;
326  val._z += wx[a] * wy[b] * wd[c] * coeff;
327  } else {
328  return this->DefaultValue();
329  }
330  }
331  }
332  }
333 
334  return val;
335 }
336 
337 // -----------------------------------------------------------------------------
338 template <class TImage> template <class TOtherImage>
341 ::Get3D(const TOtherImage *input, double x, double y, double z, double t) const
342 {
343  Real wx[2], wy[2], wz[2], wd[2] = {Real(-1), Real(1)};
344  const int i = ComputeWeights(x, wx);
345  const int j = ComputeWeights(y, wy);
346  const int k = ComputeWeights(z, wz);
347  const int l = iround(t);
348 
349  GradientType val(.0);
350  Real coeff;
351 
352  int ia, jb, kc;
353  for (int c = 0; c <= 1; ++c) {
354  kc = k + c;
355  for (int b = 0; b <= 1; ++b) {
356  jb = j + b;
357  for (int a = 0; a <= 1; ++a) {
358  ia = i + a;
359  coeff = voxel_cast<Real>(input->Get(ia, jb, kc, l));
360  val._x += wd[a] * wy[b] * wz[c] * coeff;
361  val._y += wx[a] * wd[b] * wz[c] * coeff;
362  val._z += wx[a] * wy[b] * wd[c] * coeff;
363  }
364  }
365  }
366 
367  return val;
368 }
369 
370 // -----------------------------------------------------------------------------
371 template <class TImage> template <class TOtherImage>
374 ::GetWithPadding3D(const TOtherImage *input, double x, double y, double z, double t) const
375 {
376  Real wx[2], wy[2], wz[2], wd[2] = {Real(-1), Real(1)};
377  const int i = ComputeWeights(x, wx);
378  const int j = ComputeWeights(y, wy);
379  const int k = ComputeWeights(z, wz);
380  const int l = iround(t);
381 
382  if (l < 0 || l >= input->T()) {
383  return this->DefaultValue();
384  }
385 
386  GradientType val(.0);
387  Real coeff;
388 
389  int ia, jb, kc;
390  for (int c = 0; c <= 1; ++c) {
391  kc = k + c;
392  for (int b = 0; b <= 1; ++b) {
393  jb = j + b;
394  for (int a = 0; a <= 1; ++a) {
395  ia = i + a;
396  if (input->IsForeground(ia, jb, kc, l)) {
397  coeff = voxel_cast<Real>(input->Get(ia, jb, kc, l));
398  val._x += wd[a] * wy[b] * wz[c] * coeff;
399  val._y += wx[a] * wd[b] * wz[c] * coeff;
400  val._z += wx[a] * wy[b] * wd[c] * coeff;
401  } else {
402  return this->DefaultValue();
403  }
404  }
405  }
406  }
407 
408  return val;
409 }
410 
411 // -----------------------------------------------------------------------------
412 template <class TImage>
415 ::Get4D(double x, double y, double z, double t) const
416 {
417  Real wx[2], wy[2], wz[2], wt[2], wd[2] = {Real(-1), Real(1)};
418  const int i = ComputeWeights(x, wx);
419  const int j = ComputeWeights(y, wy);
420  const int k = ComputeWeights(z, wz);
421  const int l = ComputeWeights(t, wt);
422 
423  GradientType val(.0);
424  Real nrm(0), coeff;
425 
426  int ia, jb, kc, ld;
427  for (int d = 0; d <= 1; ++d) {
428  ld = l + d;
429  if (0 <= ld && ld < this->Input()->T()) {
430  for (int c = 0; c <= 1; ++c) {
431  kc = k + c;
432  if (0 <= kc && kc < this->Input()->Z()) {
433  for (int b = 0; b <= 1; ++b) {
434  jb = j + b;
435  if (0 <= jb && jb < this->Input()->Y()) {
436  for (int a = 0; a <= 1; ++a) {
437  ia = i + a;
438  if (0 <= ia && ia < this->Input()->X()) {
439  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, kc, ld));
440  val._x += wd[a] * wy[b] * wz[c] * wt[d] * coeff;
441  val._y += wx[a] * wd[b] * wz[c] * wt[d] * coeff;
442  val._z += wx[a] * wy[b] * wd[c] * wt[d] * coeff;
443  nrm += wx[a] * wy[b] * wz[c] * wt[d];
444  }
445  }
446  }
447  }
448  }
449  }
450  }
451  }
452 
453  if (nrm > 1e-3) val /= nrm;
454  else val = this->DefaultValue();
455 
456  return val;
457 }
458 
459 // -----------------------------------------------------------------------------
460 template <class TImage>
463 ::GetWithPadding4D(double x, double y, double z, double t) const
464 {
465  Real wx[2], wy[2], wz[2], wt[2], wd[2] = {Real(-1), Real(1)};
466  const int i = ComputeWeights(x, wx);
467  const int j = ComputeWeights(y, wy);
468  const int k = ComputeWeights(z, wz);
469  const int l = ComputeWeights(t, wt);
470 
471  GradientType val(.0);
472  Real coeff;
473 
474  int ia, jb, kc, ld;
475  for (int d = 0; d <= 1; ++d) {
476  ld = l + d;
477  for (int c = 0; c <= 1; ++c) {
478  kc = k + c;
479  for (int b = 0; b <= 1; ++b) {
480  jb = j + b;
481  for (int a = 0; a <= 1; ++a) {
482  ia = i + a;
483  if (this->Input()->IsInsideForeground(ia, jb, kc, ld)) {
484  coeff = voxel_cast<Real>(this->Input()->Get(ia, jb, kc, ld));
485  val._x += wd[a] * wy[b] * wz[c] * wt[d] * coeff;
486  val._y += wx[a] * wd[b] * wz[c] * wt[d] * coeff;
487  val._z += wx[a] * wy[b] * wd[c] * wt[d] * coeff;
488  } else {
489  return this->DefaultValue();
490  }
491  }
492  }
493  }
494  }
495 
496  return val;
497 }
498 
499 // -----------------------------------------------------------------------------
500 template <class TImage> template <class TOtherImage>
503 ::Get4D(const TOtherImage *input, double x, double y, double z, double t) const
504 {
505  Real wx[2], wy[2], wz[2], wt[2], wd[2] = {Real(-1), Real(1)};
506  const int i = ComputeWeights(x, wx);
507  const int j = ComputeWeights(y, wy);
508  const int k = ComputeWeights(z, wz);
509  const int l = ComputeWeights(t, wt);
510 
511  GradientType val(.0);
512  Real coeff;
513 
514  int ia, jb, kc, ld;
515  for (int d = 0; d <= 1; ++d) {
516  ld = l + d;
517  for (int c = 0; c <= 1; ++c) {
518  kc = k + c;
519  for (int b = 0; b <= 1; ++b) {
520  jb = j + b;
521  for (int a = 0; a <= 1; ++a) {
522  ia = i + a;
523  coeff = voxel_cast<Real>(input->Get(ia, jb, kc, ld));
524  val._x += wd[a] * wy[b] * wz[c] * wt[d] * coeff;
525  val._y += wx[a] * wd[b] * wz[c] * wt[d] * coeff;
526  val._z += wx[a] * wy[b] * wd[c] * wt[d] * coeff;
527  }
528  }
529  }
530  }
531 
532  return val;
533 }
534 
535 // -----------------------------------------------------------------------------
536 template <class TImage> template <class TOtherImage>
539 ::GetWithPadding4D(const TOtherImage *input, double x, double y, double z, double t) const
540 {
541  Real wx[2], wy[2], wz[2], wt[2], wd[2] = {Real(-1), Real(1)};
542  const int i = ComputeWeights(x, wx);
543  const int j = ComputeWeights(y, wy);
544  const int k = ComputeWeights(z, wz);
545  const int l = ComputeWeights(t, wt);
546 
547  GradientType val(.0);
548  Real coeff;
549 
550  int ia, jb, kc, ld;
551  for (int d = 0; d <= 1; ++d) {
552  ld = l + d;
553  for (int c = 0; c <= 1; ++c) {
554  kc = k + c;
555  for (int b = 0; b <= 1; ++b) {
556  jb = j + b;
557  for (int a = 0; a <= 1; ++a) {
558  ia = i + a;
559  if (input->IsForeground(ia, jb, kc, ld)) {
560  coeff = voxel_cast<Real>(input->Get(ia, jb, kc, ld));
561  val._x += wd[a] * wy[b] * wz[c] * wt[d] * coeff;
562  val._y += wx[a] * wd[b] * wz[c] * wt[d] * coeff;
563  val._z += wx[a] * wy[b] * wd[c] * wt[d] * coeff;
564  } else {
565  return this->DefaultValue();
566  }
567  }
568  }
569  }
570  }
571 
572  return val;
573 }
574 
575 // -----------------------------------------------------------------------------
576 template <class TImage>
579 ::Get(double x, double y, double z, double t) const
580 {
581  switch (this->NumberOfDimensions()) {
582  case 3: return Get3D(x, y, z, t);
583  case 2: return Get2D(x, y, z, t);
584  default: return Get4D(x, y, z, t);
585  }
586 }
587 
588 // -----------------------------------------------------------------------------
589 template <class TImage>
592 ::GetWithPadding(double x, double y, double z, double t) const
593 {
594  switch (this->NumberOfDimensions()) {
595  case 3: return GetWithPadding3D(x, y, z, t);
596  case 2: return GetWithPadding2D(x, y, z, t);
597  default: return GetWithPadding4D(x, y, z, t);
598  }
599 }
600 
601 // -----------------------------------------------------------------------------
602 template <class TImage> template <class TOtherImage>
605 ::Get(const TOtherImage *input, double x, double y, double z, double t) const
606 {
607  switch (this->NumberOfDimensions()) {
608  case 3: return Get3D(input, x, y, z, t);
609  case 2: return Get2D(input, x, y, z, t);
610  default: return Get4D(input, x, y, z, t);
611  }
612 }
613 
614 // -----------------------------------------------------------------------------
615 template <class TImage> template <class TOtherImage>
618 ::GetWithPadding(const TOtherImage *input, double x, double y, double z, double t) const
619 {
620  switch (this->NumberOfDimensions()) {
621  case 3: return GetWithPadding3D(input, x, y, z, t);
622  case 2: return GetWithPadding2D(input, x, y, z, t);
623  default: return GetWithPadding4D(input, x, y, z, t);
624  }
625 }
626 
627 // -----------------------------------------------------------------------------
628 template <class TImage>
631 ::GetInside(double x, double y, double z, double t) const
632 {
633  return Get(this->Input(), x, y, z, t);
634 }
635 
636 // -----------------------------------------------------------------------------
637 template <class TImage>
640 ::GetOutside(double x, double y, double z, double t) const
641 {
642  if (this->Extrapolator()) {
643  return Get(this->Extrapolator(), x, y, z, t);
644  } else {
645  return Get(x, y, z, t);
646  }
647 }
648 
649 // -----------------------------------------------------------------------------
650 template <class TImage>
653 ::GetWithPaddingInside(double x, double y, double z, double t) const
654 {
655  return GetWithPadding(this->Input(), x, y, z, t);
656 }
657 
658 // -----------------------------------------------------------------------------
659 template <class TImage>
662 ::GetWithPaddingOutside(double x, double y, double z, double t) const
663 {
664  if (this->Extrapolator()) {
665  return GetWithPadding(this->Extrapolator(), x, y, z, t);
666  } else {
667  return GetWithPadding(x, y, z, t);
668  }
669 }
670 
671 
672 } // namespace mirtk
673 
674 #endif // MIRTK_FastLinearImageGradientFunction_HXX
virtual GradientType GetWithPadding(double, double, double=0, double=0) const
GradientType Get2D(double, double, double=0, double=0) const
GradientType Get3D(double, double, double=0, double=0) const
string Get(const ParameterList &params, string name)
Get parameter value from parameters list.
Definition: Object.h:202
virtual GradientType GetOutside(double, double, double=0, double=0) const
Evaluate generic image at an arbitrary location (in pixels)
virtual GradientType GetWithPaddingInside(double, double, double=0, double=0) const
MIRTKCU_API int ifloor(T x)
Round floating-point value to next smaller integer and cast to int.
Definition: Math.h:154
virtual void Initialize(bool=false)
Initialize interpolation function.
MIRTKCU_API double fdec(double f)
Definition: Math.h:188
GradientType GetWithPadding3D(double, double, double=0, double=0) const
Definition: IOConfig.h:41
virtual void BoundingInterval(double, int &, int &) const
GradientType GetWithPadding4D(double, double, double=0, double=0) const
T _z
The z component.
Definition: Vector3D.h:60
T _y
The y component.
Definition: Vector3D.h:59
MIRTKCU_API int iround(T x)
Round floating-point value and cast to int.
Definition: Math.h:170
virtual GradientType Get(double, double, double=0, double=0) const
T _x
The x component.
Definition: Vector3D.h:58
GradientType GetWithPadding2D(double, double, double=0, double=0) const
GradientType Get4D(double, double, double=0, double=0) const
virtual GradientType GetInside(double, double, double=0, double=0) const
virtual GradientType GetWithPaddingOutside(double, double, double=0, double=0) const