ConvolutionFunction.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2015 Imperial College London
5  * Copyright 2013-2015 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_ConvolutionFunction_H
21 #define MIRTK_ConvolutionFunction_H
22 
23 #include "mirtk/Voxel.h"
24 #include "mirtk/VoxelFunction.h"
25 #include "mirtk/BaseImage.h"
26 #include "mirtk/GenericImage.h"
27 
28 
29 namespace mirtk {
30 
31 /// Voxel functions implementing convolution of an image with a discrete kernel
32 namespace ConvolutionFunction {
33 
34 
35 /// Tolerance used for floating point comparisons
36 inline double Epsilon()
37 {
38  return 1e-6;
39 }
40 
41 
42 // =============================================================================
43 // Boundary conditions for image domain
44 // =============================================================================
45 
46 // -----------------------------------------------------------------------------
47 /// Mirror image at boundary
49 {
50  /// Apply mirror boundary condition to voxel index
51  ///
52  /// \param[in] i Voxel index along image dimension
53  /// \param[in] N Number of voxels in image dimension
54  int operator ()(int i, int N) const
55  {
56  N -= 1;
57  int m, n;
58  if (i < 0) {
59  i = -i;
60  n = i / N;
61  m = i - n * N;
62  if (n % 2) i = N - 1 - m;
63  else i = m;
64  } else if (i > N) {
65  i = i - N;
66  n = i / N;
67  m = i - n * N;
68  if (n % 2) i = m;
69  else i = N - m;
70  }
71  return i;
72  }
73 
74  /// Apply mirror boundary condition to voxel pointer
75  template <class T>
76  const T *operator ()(int i, int N, const T *p, int stride)
77  {
78  return p + (this->operator ()(i, N) - i) * stride;
79  }
80 };
81 
82 // =============================================================================
83 // Convolve at boundary truncated image with 1D kernel
84 // =============================================================================
85 
86 // -----------------------------------------------------------------------------
87 /// Perform convolution along x with truncation of kernel at boundary
88 template <class TKernel = double>
89 struct ConvolveInX : public VoxelFunction
90 {
91  const TKernel *_Kernel; ///< Convolution kernel
92  int _Radius; ///< Radius of kernel
93  bool _Normalize; ///< Wether to divide by sum of kernel weights
94  int _Offset; ///< Vector component offset
95  int _X; ///< Number of voxels in x
96 
97  ConvolveInX(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
98  :
99  _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
100  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X())
101  {}
102 
103  template <class T1, class T2>
104  void operator ()(int i, int, int, int, const T1 *in, T2 *out) const
105  {
106  typedef typename voxel_info<T2>::RealType AccType;
107  typedef typename voxel_info<AccType>::ScalarType RealType;
108  // Convolve l-th vector component
109  in += _Offset, out += _Offset;
110  // Go to start of input and end of kernel
111  i -= _Radius;
112  in -= _Radius;
113  int n = _Radius + _Radius/* + 1 - 1*/;
114  // Outside left boundary
115  if (i < 0) n += i, in -= i, i = 0;
116  // Inside image domain
117  AccType v, acc(0.);
118  RealType w, sum = .0;
119  while (i < _X && n >= 0) {
120  w = static_cast<RealType>(_Kernel[n]);
121  v = voxel_cast<AccType>(*in);
122  acc += (v *= w);
123  sum += w;
124  ++i, ++in, --n;
125  }
126  // Output result of convolution
127  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
128  *out = voxel_cast<T2>(acc);
129  }
130 };
131 
132 // -----------------------------------------------------------------------------
133 /// Perform convolution along y with truncation of kernel at boundary
134 template <class TKernel = double>
135 struct ConvolveInY : public VoxelFunction
136 {
137  const TKernel *_Kernel; ///< Convolution kernel
138  int _Radius; ///< Radius of kernel
139  bool _Normalize; ///< Wether to divide by sum of kernel weights
140  int _Offset; ///< Vector component offset
141  int _X; ///< Number of voxels in x
142  int _Y; ///< Number of voxels in y
143 
144  ConvolveInY(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
145  :
146  _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
147  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X()), _Y(image->Y())
148  {}
149 
150  template <class T1, class T2>
151  void operator ()(int, int j, int, int, const T1 *in, T2 *out) const
152  {
153  typedef typename voxel_info<T2>::RealType AccType;
154  typedef typename voxel_info<AccType>::ScalarType RealType;
155  // Convolve l-th vector component
156  in += _Offset, out += _Offset;
157  // Go to start of input and end of kernel
158  j -= _Radius;
159  in -= _Radius * _X;
160  int n = _Radius + _Radius/* + 1 - 1*/;
161  // Outside left boundary
162  if (j < 0) n += j, in -= j * _X, j = 0;
163  // Inside image domain
164  AccType v, acc = 0.;
165  RealType w, sum = 0.;
166  while (j < _Y && n >= 0) {
167  w = static_cast<RealType>(_Kernel[n]);
168  v = voxel_cast<AccType>(*in);
169  acc += (v *= w);
170  sum += w;
171  ++j, in += _X, --n;
172  }
173  // Output result of convolution
174  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
175  *out = voxel_cast<T2>(acc);
176  }
177 };
178 
179 // -----------------------------------------------------------------------------
180 /// Perform convolution along z with truncation of kernel at boundary
181 template <class TKernel = double>
182 struct ConvolveInZ : public VoxelFunction
183 {
184  const TKernel *_Kernel; ///< Convolution kernel
185  int _Radius; ///< Radius of kernel
186  bool _Normalize; ///< Wether to divide by sum of kernel weights
187  int _Offset; ///< Vector component offset
188  int _XY; ///< Number of voxels in slice (nx * ny)
189  int _Z; ///< Number of voxels in z
190 
191  ConvolveInZ(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
192  :
193  _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
194  _Offset(l * image->NumberOfSpatialVoxels()), _XY(image->X() * image->Y()), _Z(image->Z())
195  {}
196 
197  template <class T1, class T2>
198  void operator ()(int, int, int k, int, const T1 *in, T2 *out) const
199  {
200  typedef typename voxel_info<T2>::RealType AccType;
201  typedef typename voxel_info<AccType>::ScalarType RealType;
202  // Convolve l-th vector component
203  in += _Offset, out += _Offset;
204  // Go to start of input and end of kernel
205  k -= _Radius;
206  in -= _Radius * _XY;
207  int n = _Radius + _Radius/* + 1 - 1*/;
208  // Outside left boundary
209  if (k < 0) n += k, in -= k * _XY, k = 0;
210  // Inside image domain
211  AccType v, acc = 0.;
212  RealType w, sum = 0.;
213  while (k < _Z && n >= 0) {
214  w = static_cast<RealType>(_Kernel[n]);
215  v = voxel_cast<AccType>(*in);
216  acc += (v *= w);
217  sum += w;
218  ++k, in += _XY, --n;
219  }
220  // Output result of convolution
221  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
222  *out = voxel_cast<T2>(acc);
223  }
224 };
225 
226 // -----------------------------------------------------------------------------
227 /// Perform convolution along t with truncation of kernel at boundary
228 template <class TKernel = double>
229 struct ConvolveInT : public VoxelFunction
230 {
231  const TKernel *_Kernel; ///< Convolution kernel
232  int _Radius; ///< Radius of kernel
233  bool _Normalize; ///< Wether to divide by sum of kernel weights
234  int _XYZ; ///< Number of voxels in volume (nx * ny * nz)
235  int _T; ///< Number of voxels in t
236 
237  ConvolveInT(const BaseImage *image, const TKernel *kernel, int size, bool norm = true)
238  :
239  _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
240  _XYZ(image->X() * image->Y() * image->Z()), _T(image->T())
241  {}
242 
243  template <class T1, class T2>
244  void operator ()(int, int, int, int l, const T1 *in, T2 *out) const
245  {
246  typedef typename voxel_info<T2>::RealType AccType;
247  typedef typename voxel_info<AccType>::ScalarType RealType;
248  // Go to start of input and end of kernel
249  l -= _Radius;
250  in -= _Radius * _XYZ;
251  int n = _Radius + _Radius/* + 1 - 1*/;
252  // Outside left boundary
253  if (l < 0) n += l, in -= l * _XYZ, l = 0;
254  // Inside image domain
255  AccType v, acc = 0.;
256  RealType w, sum = 0.;
257  while (l < _T && n >= 0) {
258  w = static_cast<RealType>(_Kernel[n]);
259  v = voxel_cast<AccType>(*in);
260  acc += (v *= w);
261  sum += w;
262  ++l, in += _XYZ, --n;
263  }
264  // Output result of convolution
265  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
266  *out = voxel_cast<T2>(acc);
267  }
268 };
269 
270 // =============================================================================
271 // Convolve at boundary truncated image foreground with 1D kernel
272 // =============================================================================
273 
274 // -----------------------------------------------------------------------------
275 /// Perform convolution of image foreground along x with truncation of kernel at boundary
276 template <class TKernel = double>
278 {
279  const BaseImage *_Image; ///< Image defining foreground region
280  const TKernel *_Kernel; ///< Convolution kernel
281  int _Radius; ///< Radius of kernel
282  bool _Normalize; ///< Wether to divide by sum of kernel weights
283  int _Offset; ///< Vector component offset
284  int _X; ///< Number of voxels in x
285 
286  ConvolveForegroundInX(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
287  :
288  _Image(image), _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
289  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X())
290  {}
291 
292  template <class T1, class T2>
293  void operator ()(int i, int j, int k, int l, const T1 *in, T2 *out) const
294  {
295  typedef typename voxel_info<T2>::RealType AccType;
296  typedef typename voxel_info<AccType>::ScalarType RealType;
297  // Convolve l-th vector component
298  in += _Offset, out += _Offset;
299  // Go to start of input and end of kernel
300  i -= _Radius;
301  in -= _Radius;
302  int n = _Radius + _Radius/* + 1 - 1*/;
303  // Outside left boundary
304  if (i < 0) n += i, in -= i, i = 0;
305  // Inside image domain
306  AccType v, acc = 0.;
307  RealType w, sum = 0.;
308  while (i < _X && n >= 0) {
309  if (_Image->IsForeground(i, j, k, l)) {
310  w = static_cast<RealType>(_Kernel[n]);
311  v = voxel_cast<AccType>(*in);
312  acc += (v += w);
313  sum += w;
314  }
315  ++i, ++in, --n;
316  }
317  // Output result of convolution
318  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
319  *out = voxel_cast<T2>(acc);
320  }
321 };
322 
323 // -----------------------------------------------------------------------------
324 /// Perform convolution of image foreground along y with truncation of kernel at boundary
325 template <class TKernel = double>
327 {
328  const BaseImage *_Image; ///< Image defining foreground region
329  const TKernel *_Kernel; ///< Convolution kernel
330  int _Radius; ///< Radius of kernel
331  bool _Normalize; ///< Wether to divide by sum of kernel weights
332  int _Offset; ///< Vector component offset
333  int _X; ///< Number of voxels in x
334  int _Y; ///< Number of voxels in y
335 
336  ConvolveForegroundInY(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
337  :
338  _Image(image), _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
339  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X()), _Y(image->Y())
340  {}
341 
342  template <class T1, class T2>
343  void operator ()(int i, int j, int k, int l, const T1 *in, T2 *out) const
344  {
345  typedef typename voxel_info<T2>::RealType AccType;
346  typedef typename voxel_info<AccType>::ScalarType RealType;
347  // Convolve l-th vector component
348  in += _Offset, out += _Offset;
349  // Go to start of input and end of kernel
350  j -= _Radius;
351  in -= _Radius * _X;
352  int n = _Radius + _Radius/* + 1 - 1*/;
353  // Outside left boundary
354  if (j < 0) n += j, in -= j * _X, j = 0;
355  // Inside image domain
356  AccType v, acc = 0.;
357  RealType w, sum = 0.;
358  while (j < _Y && n >= 0) {
359  if (_Image->IsForeground(i, j, k, l)) {
360  w = static_cast<RealType>(_Kernel[n]);
361  v = voxel_cast<AccType>(*in);
362  acc += (v *= w);
363  sum += w;
364  }
365  ++j, in += _X, --n;
366  }
367  // Output result of convolution
368  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
369  *out = voxel_cast<T2>(acc);
370  }
371 };
372 
373 // -----------------------------------------------------------------------------
374 /// Perform convolution of image foreground along z with truncation of kernel at boundary
375 template <class TKernel = double>
377 {
378  const BaseImage *_Image; ///< Image defining foreground region
379  const TKernel *_Kernel; ///< Convolution kernel
380  int _Radius; ///< Radius of kernel
381  bool _Normalize; ///< Wether to divide by sum of kernel weights
382  int _Offset; ///< Vector component offset
383  int _XY; ///< Number of voxels in slice (nx * ny)
384  int _Z; ///< Number of voxels in z
385 
386  ConvolveForegroundInZ(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
387  :
388  _Image(image), _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
389  _Offset(l * image->NumberOfSpatialVoxels()), _XY(image->X() * image->Y()), _Z(image->Z())
390  {}
391 
392  template <class T1, class T2>
393  void operator ()(int i, int j, int k, int l, const T1 *in, T2 *out) const
394  {
395  typedef typename voxel_info<T2>::RealType AccType;
396  typedef typename voxel_info<AccType>::ScalarType RealType;
397  // Convolve l-th vector component
398  in += _Offset, out += _Offset;
399  // Go to start of input and end of kernel
400  k -= _Radius;
401  in -= _Radius * _XY;
402  int n = _Radius + _Radius/* + 1 - 1*/;
403  // Outside left boundary
404  if (k < 0) n += k, in -= k * _XY, k = 0;
405  // Inside image domain
406  AccType v, acc = 0.;
407  RealType w, sum = 0.;
408  while (k < _Z && n >= 0) {
409  if (_Image->IsForeground(i, j, k, l)) {
410  w = static_cast<RealType>(_Kernel[n]);
411  v = voxel_cast<AccType>(*in);
412  acc += (v *= w);
413  sum += w;
414  }
415  ++k, in += _XY, --n;
416  }
417  // Output result of convolution
418  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
419  *out = voxel_cast<T2>(acc);
420  }
421 };
422 
423 // -----------------------------------------------------------------------------
424 /// Perform convolution of image foreground along t with truncation of kernel at boundary
425 template <class TKernel = double>
427 {
428  const BaseImage *_Image; ///< Image defining foreground region
429  const TKernel *_Kernel; ///< Convolution kernel
430  int _Radius; ///< Radius of kernel
431  bool _Normalize; ///< Wether to divide by sum of kernel weights
432  int _XYZ; ///< Number of voxels in volume (nx * ny * nz)
433  int _T; ///< Number of voxels in t
434 
435  ConvolveForegroundInT(const BaseImage *image, const TKernel *kernel, int size, bool norm = true)
436  :
437  _Image(image), _Kernel(kernel), _Radius((size - 1) / 2), _Normalize(norm),
438  _XYZ(image->X() * image->Y() * image->Z()), _T(image->T())
439  {}
440 
441  template <class T1, class T2>
442  void operator ()(int i, int j, int k, int l, const T1 *in, T2 *out) const
443  {
444  typedef typename voxel_info<T2>::RealType AccType;
445  typedef typename voxel_info<AccType>::ScalarType RealType;
446  // Go to start of input and end of kernel
447  l -= _Radius;
448  in -= _Radius * _XYZ;
449  int n = _Radius + _Radius/* + 1 - 1*/;
450  // Outside left boundary
451  if (l < 0) n += l, in -= l * _XYZ, l = 0;
452  // Inside image domain
453  AccType v, acc = 0.;
454  RealType w, sum = 0.;
455  while (l < _T && n >= 0) {
456  if (_Image->IsForeground(i, j, k, l)) {
457  w = static_cast<RealType>(_Kernel[n]);
458  v = voxel_cast<AccType>(*in);
459  acc += (v *= w);
460  sum += w;
461  }
462  ++l, in += _XYZ, --n;
463  }
464  // Output result of convolution
465  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
466  *out = voxel_cast<T2>(acc);
467  }
468 };
469 
470 // =============================================================================
471 // Convolve at boundary truncated voxel-wise weighted image with 1D kernel
472 // =============================================================================
473 
474 // -----------------------------------------------------------------------------
475 /// Perform convolution of weighted image along x with truncation of kernel at boundary
476 template <class TKernel = double>
478 {
479  const TKernel *_Kernel; ///< Convolution kernel
480  int _Radius; ///< Radius of kernel
481  int _Offset1; ///< Image component offset
482  int _Offset2; ///< Weight component offset
483  int _X; ///< Number of voxels in x
484 
485  ConvolveWeightedImageInX(const BaseImage *image, const TKernel *kernel,
486  int size, int l1 = 0, int l2 = 0)
487  :
488  _Kernel(kernel), _Radius((size - 1) / 2),
489  _Offset1(l1 * image->NumberOfSpatialVoxels()),
490  _Offset2(l2 * image->NumberOfSpatialVoxels()),
491  _X(image->X())
492  {}
493 
494  template <class T1, class T2, class T3>
495  void operator ()(int i, int, int, int, const T1 *in, const T2 *win, T3 *out) const
496  {
497  // Convolve l1-th vector component using l2-th weight
498  in += _Offset1, win += _Offset2, out += _Offset1;
499  // Go to start of input and end of kernel
500  i -= _Radius;
501  in -= _Radius;
502  win -= _Radius;
503  int n = _Radius + _Radius/* + 1 - 1*/;
504  // Outside left boundary
505  if (i < 0) n += i, in -= i, win -= i, i = 0;
506  // Inside image domain
507  double w, acc = .0, sum = .0;
508  while (i < _X && n >= 0) {
509  w = static_cast<double>(*win) * static_cast<double>(_Kernel[n]);
510  acc += w * static_cast<double>(*in);
511  sum += w;
512  ++i, ++in, ++win, --n;
513  }
514  // Output result of convolution
515  acc = (sum > Epsilon() ? acc / sum : 0.);
516  *out = static_cast<T3>(acc);
517  }
518 };
519 
520 // -----------------------------------------------------------------------------
521 /// Perform convolution of weighted image along y with truncation of kernel at boundary
522 template <class TKernel = double>
524 {
525  const TKernel *_Kernel; ///< Convolution kernel
526  int _Radius; ///< Radius of kernel
527  int _Offset1; ///< Image component offset
528  int _Offset2; ///< Weight component offset
529  int _X; ///< Number of voxels in x
530  int _Y; ///< Number of voxels in y
531 
532  ConvolveWeightedImageInY(const BaseImage *image, const TKernel *kernel, int size, int l1 = 0, int l2 = 0)
533  :
534  _Kernel(kernel), _Radius((size - 1) / 2),
535  _Offset1(l1 * image->NumberOfSpatialVoxels()),
536  _Offset2(l2 * image->NumberOfSpatialVoxels()),
537  _X(image->X()), _Y(image->Y())
538  {}
539 
540  template <class T1, class T2, class T3>
541  void operator ()(int, int j, int, int, const T1 *in, const T2 *win, T3 *out) const
542  {
543  // Convolve l1-th vector component using l2-th weight
544  in += _Offset1, win += _Offset2, out += _Offset1;
545  // Go to start of input and end of kernel
546  j -= _Radius;
547  in -= _Radius * _X;
548  win -= _Radius * _X;
549  int n = _Radius + _Radius/* + 1 - 1*/;
550  // Outside left boundary
551  if (j < 0) n += j, in -= j * _X, win -= j * _X, j = 0;
552  // Inside image domain
553  double w, acc = .0, sum = .0;
554  while (j < _Y && n >= 0) {
555  w = static_cast<double>(_Kernel[n]) * static_cast<double>(*win);
556  acc += w * static_cast<double>(*in);
557  sum += w;
558  ++j, in += _X, win += _X, --n;
559  }
560  // Output result of convolution
561  acc = (sum > Epsilon() ? acc / sum : 0.);
562  *out = static_cast<T3>(acc);
563  }
564 };
565 
566 // -----------------------------------------------------------------------------
567 /// Perform convolution of weighted image along z with truncation of kernel at boundary
568 template <class TKernel = double>
570 {
571  const TKernel *_Kernel; ///< Convolution kernel
572  int _Radius; ///< Radius of kernel
573  int _Offset1; ///< Image component offset
574  int _Offset2; ///< Weight component offset
575  int _XY; ///< Number of voxels in slice (nx * ny)
576  int _Z; ///< Number of voxels in z
577 
578  ConvolveWeightedImageInZ(const BaseImage *image, const TKernel *kernel, int size, int l1 = 0, int l2 = 0)
579  :
580  _Kernel(kernel), _Radius((size - 1) / 2),
581  _Offset1(l1 * image->NumberOfSpatialVoxels()),
582  _Offset2(l2 * image->NumberOfSpatialVoxels()),
583  _XY(image->X() * image->Y()), _Z(image->Z())
584  {}
585 
586  template <class T1, class T2, class T3>
587  void operator ()(int, int, int k, int, const T1 *in, const T2 *win, T3 *out) const
588  {
589  // Convolve l1-th vector component using l2-th weight
590  in += _Offset1, win += _Offset2, out += _Offset1;
591  // Go to start of input and end of kernel
592  k -= _Radius;
593  in -= _Radius * _XY;
594  win -= _Radius * _XY;
595  int n = _Radius + _Radius/* + 1 - 1*/;
596  // Outside left boundary
597  if (k < 0) n += k, in -= k * _XY, win -= k * _XY, k = 0;
598  // Inside image domain
599  double w, acc = .0, sum = .0;
600  while (k < _Z && n >= 0) {
601  w = static_cast<double>(_Kernel[n]) * static_cast<double>(*win);
602  acc += w * static_cast<double>(*in);
603  sum += w;
604  ++k, in += _XY, win += _XY, --n;
605  }
606  // Output result of convolution
607  acc = (sum > Epsilon() ? acc / sum : 0.);
608  *out = static_cast<T3>(acc);
609  }
610 };
611 
612 // -----------------------------------------------------------------------------
613 /// Perform convolution of weighted image along t with truncation of kernel at boundary
614 template <class TKernel = double>
616 {
617  const TKernel *_Kernel; ///< Convolution kernel
618  int _Radius; ///< Radius of kernel
619  int _XYZ; ///< Number of voxels in volume (nx * ny * nz)
620  int _T; ///< Number of voxels in t
621 
622  ConvolveWeightedImageInT(const BaseImage *image, const TKernel *kernel, int size)
623  :
624  _Kernel(kernel), _Radius((size - 1) / 2),
625  _XYZ(image->X() * image->Y() * image->Z()), _T(image->T())
626  {}
627 
628  template <class T1, class T2, class T3>
629  void operator ()(int, int, int, int l, const T1 *in, const T2 *win, T3 *out) const
630  {
631  // Go to start of input and end of kernel
632  l -= _Radius;
633  in -= _Radius * _XYZ;
634  win -= _Radius * _XYZ;
635  int n = _Radius + _Radius/* + 1 - 1*/;
636  // Outside left boundary
637  if (l < 0) n += l, in -= l * _XYZ, win -= l * _XYZ, l = 0;
638  // Inside image domain
639  double w, acc = .0, sum = .0;
640  while (l < _T && n >= 0) {
641  w = static_cast<double>(_Kernel[n]) * static_cast<double>(*win);
642  acc += w * static_cast<double>(*in);
643  sum += w;
644  ++l, in += _XYZ, win += _XYZ, --n;
645  }
646  // Output result of convolution
647  acc = (sum > Epsilon() ? acc / sum : 0.);
648  *out = static_cast<T3>(acc);
649  }
650 };
651 
652 // =============================================================================
653 // Convolve at truncated image foreground with 1D kernel
654 // =============================================================================
655 
656 // -----------------------------------------------------------------------------
657 /// Base class of 1D convolution functions which truncate the kernel at the foreground boundary
658 template <class TKernel = double>
660 {
661 protected:
662 
663  // ---------------------------------------------------------------------------
664  /// Constructor
665  TruncatedForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, bool norm = true)
666  :
667  _Kernel(kernel), _Size(size), _Radius((size - 1) / 2), _Normalize(norm)
668  {
669  if (image->HasBackgroundValue()) {
670  _Background = image->GetBackgroundValueAsDouble();
671  } else {
672  _Background = NaN;
673  }
674  }
675 
676  // ---------------------------------------------------------------------------
677  /// Apply kernel initially at center voxel
678  template <class T>
679  bool ConvolveCenterVoxel(const T *in, double &acc, double &sum) const
680  {
681  if (AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
682  acc = _Background;
683  sum = .0;
684  return false;
685  } else {
686  acc = static_cast<double>(_Kernel[_Radius]) * static_cast<double>(*in);
687  sum = static_cast<double>(_Kernel[_Radius]);
688  return true;
689  }
690  }
691 
692  // ---------------------------------------------------------------------------
693  /// Apply kernel to left neighbors of given voxel
694  template <class T>
695  void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc, double &sum) const
696  {
697  int di = -1;
698  for (int k = _Radius + 1; k < _Size; ++k) {
699  // Move voxel index/pointer
700  i += di;
701  in -= s;
702  // Stop if outside image/foreground
703  if (i < 0 || i >= n || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) break;
704  // Multiply with kernel weight
705  acc += static_cast<double>(_Kernel[k]) * static_cast<double>(*in);
706  sum += static_cast<double>(_Kernel[k]);
707  }
708  }
709 
710  // ---------------------------------------------------------------------------
711  /// Apply kernel to right neighbors of given voxel
712  template <class T>
713  void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc, double &sum) const
714  {
715  int di = +1;
716  for (int k = _Radius - 1; k >= 0; --k) {
717  // Move voxel index/pointer
718  i += di;
719  in += s;
720  // Stop if outside image/foreground
721  if (i < 0 || i >= n || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) break;
722  // Multiply with kernel weight
723  acc += static_cast<double>(_Kernel[k]) * static_cast<double>(*in);
724  sum += static_cast<double>(_Kernel[k]);
725  }
726  }
727 
728  // ---------------------------------------------------------------------------
729  template <class T>
730  void Put(T *out, double acc, double sum) const
731  {
732  if (_Normalize && !AreEqual(sum, 0., Epsilon())) acc /= sum;
733  (*out) = static_cast<T>(acc);
734  }
735 
736  // ---------------------------------------------------------------------------
737  // Data members
738 protected:
739 
740  const TKernel *_Kernel; ///< Convolution kernel
741  int _Size; ///< Size of kernel = 2 * _Radius + 1
742  int _Radius; ///< Radius of kernel
743  bool _Normalize; ///< Whether to normalize by sum of overlapping kernel weights
744  double _Background; ///< Background value (padding)
745 };
746 
747 
748 // -----------------------------------------------------------------------------
749 /// Compute convolution of voxel with kernel in x dimension
750 template <class TKernel = double>
752 {
753  ConvolveTruncatedForegroundInX(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
754  :
755  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
756  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X())
757  {}
758 
759  ConvolveTruncatedForegroundInX(const BaseImage *image, double bg, const TKernel *kernel, int size, bool norm = true, int l = 0)
760  :
761  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
762  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X())
763  {
764  this->_Background = bg;
765  }
766 
767  ConvolveTruncatedForegroundInX(const BaseImage *image, const GenericImage<TKernel> *kernel, bool norm = true, int l = 0)
768  :
769  TruncatedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
770  _Offset(l * image->NumberOfSpatialVoxels()), _X(image->X())
771  {}
772 
773  template <class T1, class T2>
774  void operator ()(int i, int, int, int, const T1 *in, T2 *out) const
775  {
776  in += _Offset, out += _Offset;
777  double acc, sum;
778  if (this->ConvolveCenterVoxel(in, acc, sum)) {
779  this->ConvolveLeftNeighbors (i, _X, in, 1, acc, sum);
780  this->ConvolveRightNeighbors(i, _X, in, 1, acc, sum);
781  }
782  this->Put(out, acc, sum);
783  }
784 
785  int _Offset; ///< Vector component offset
786  int _X; ///< Number of voxels in x dimension (nx)
787 };
788 
789 // -----------------------------------------------------------------------------
790 /// Compute convolution for given voxel along y dimension
791 template <class TKernel = double>
793 {
794  ConvolveTruncatedForegroundInY(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
795  :
796  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
797  _Offset(l * image->NumberOfSpatialVoxels()),
798  _X(image->X()),
799  _Y(image->Y())
800  {}
801 
802  ConvolveTruncatedForegroundInY(const BaseImage *image, double bg, const TKernel *kernel, int size, bool norm = true, int l = 0)
803  :
804  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
805  _Offset(l * image->NumberOfSpatialVoxels()),
806  _X(image->X()),
807  _Y(image->Y())
808  {
809  this->_Background = bg;
810  }
811 
812  ConvolveTruncatedForegroundInY(const BaseImage *image, const GenericImage<TKernel> *kernel, bool norm = true, int l = 0)
813  :
814  TruncatedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
815  _Offset(l * image->NumberOfSpatialVoxels()),
816  _X(image->X()),
817  _Y(image->Y())
818  {}
819 
820  template <class T1, class T2>
821  void operator ()(int, int j, int, int, const T1 *in, T2 *out) const
822  {
823  in += _Offset, out += _Offset;
824  double acc, sum;
825  if (this->ConvolveCenterVoxel(in, acc, sum)) {
826  this->ConvolveLeftNeighbors (j, _Y, in, _X, acc, sum);
827  this->ConvolveRightNeighbors(j, _Y, in, _X, acc, sum);
828  }
829  this->Put(out, acc, sum);
830  }
831 
832  int _Offset; ///< Vector component offset
833  int _X; ///< Number of voxels in x dimension (nx)
834  int _Y; ///< Number of voxels in y dimension (ny)
835 };
836 
837 // -----------------------------------------------------------------------------
838 /// Compute convolution for given voxel along z dimension
839 template <class TKernel = double>
841 {
842  ConvolveTruncatedForegroundInZ(const BaseImage *image, const TKernel *kernel, int size, bool norm = true, int l = 0)
843  :
844  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
845  _Offset(l * image->NumberOfSpatialVoxels()),
846  _XY(image->X() * image->Y()),
847  _Z (image->Z())
848  {}
849 
850  ConvolveTruncatedForegroundInZ(const BaseImage *image, double bg, const TKernel *kernel, int size, bool norm = true, int l = 0)
851  :
852  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
853  _Offset(l * image->NumberOfSpatialVoxels()),
854  _XY(image->X() * image->Y()),
855  _Z (image->Z())
856  {
857  this->_Background = bg;
858  }
859 
860  ConvolveTruncatedForegroundInZ(const BaseImage *image, const GenericImage<TKernel> *kernel, bool norm = true, int l = 0)
861  :
862  TruncatedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
863  _Offset(l * image->NumberOfSpatialVoxels()),
864  _XY(image->X() * image->Y()),
865  _Z (image->Z())
866  {}
867 
868  template <class T1, class T2>
869  void operator ()(int, int, int k, int, const T1 *in, T2 *out) const
870  {
871  in += _Offset, out += _Offset;
872  double acc, sum;
873  if (this->ConvolveCenterVoxel(in, acc, sum)) {
874  this->ConvolveLeftNeighbors (k, _Z, in, _XY, acc, sum);
875  this->ConvolveRightNeighbors(k, _Z, in, _XY, acc, sum);
876  }
877  this->Put(out, acc, sum);
878  }
879 
880  int _Offset; ///< Vector component offset
881  int _XY; ///< Number of voxels in slice (nx * ny)
882  int _Z; ///< Number of voxels in z dimension (nz)
883 };
884 
885 // -----------------------------------------------------------------------------
886 /// Compute convolution for given voxel along t dimension
887 template <class TKernel = double>
889 {
890  ConvolveTruncatedForegroundInT(const BaseImage *image, const TKernel *kernel, int size, bool norm = true)
891  :
892  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
893  _XYZ(image->X() * image->Y() * image->Z()),
894  _T (image->T())
895  {}
896 
897  ConvolveTruncatedForegroundInT(const BaseImage *image, double bg, const TKernel *kernel, int size, bool norm = true)
898  :
899  TruncatedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
900  _XYZ(image->X() * image->Y() * image->Z()),
901  _T (image->T())
902  {
903  this->_Background = bg;
904  }
905 
906  ConvolveTruncatedForegroundInT(const BaseImage *image, const GenericImage<TKernel> *kernel, bool norm = true)
907  :
908  TruncatedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
909  _XYZ(image->X() * image->Y() * image->Z()),
910  _T (image->T())
911  {}
912 
913  template <class T1, class T2>
914  void operator ()(int, int, int, int t, const T1 *in, T2 *out) const
915  {
916  double acc, sum;
917  if (this->ConvolveCenterVoxel(in, acc, sum)) {
918  this->ConvolveLeftNeighbors (t, _T, in, _XYZ, acc, sum);
919  this->ConvolveRightNeighbors(t, _T, in, _XYZ, acc, sum);
920  }
921  this->Put(out, acc, sum);
922  }
923 
924  int _XYZ; ///< Number of voxels in frame (nx * ny * nz)
925  int _T; ///< Number of voxels in t dimension (nt)
926 };
927 
928 // =============================================================================
929 // Convolve at boundary mirrored image foreground with 1D kernel
930 // =============================================================================
931 
932 // -----------------------------------------------------------------------------
933 /// Base class of 1D convolution functions which mirror the foreground into background
934 template <class TKernel = double>
936 {
937 protected:
938 
939  // ---------------------------------------------------------------------------
940  /// Constructor
941  MirroredForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
942  :
943  _Kernel(kernel), _Size(size), _Radius((size - 1) / 2), _Norm(norm)
944  {
945  if (image->HasBackgroundValue()) {
946  _Background = image->GetBackgroundValueAsDouble();
947  } else {
948  _Background = NaN;
949  }
950  }
951 
952  // ---------------------------------------------------------------------------
953  /// Apply kernel initially at center voxel
954  template <class T>
955  bool ConvolveCenterVoxel(const T *in, double &acc) const
956  {
957  if (AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
958  acc = _Background / _Norm;
959  return false;
960  } else {
961  acc = static_cast<double>(*in) * static_cast<double>(_Kernel[_Radius]);
962  return true;
963  }
964  }
965 
966  // ---------------------------------------------------------------------------
967  /// Apply kernel to left neighbors of given voxel
968  template <class T>
969  void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc) const
970  {
971  int di = -1;
972  for (int k = _Radius + 1; k < _Size; ++k) {
973  // Move voxel index/pointer
974  i += di;
975  in -= s;
976  // Reverse if outside image/foreground
977  if (i < 0 || i >= n || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
978  di = -di;
979  s = -s;
980  i += di;
981  in -= s;
982  }
983  // Multiply with kernel weight
984  acc += static_cast<double>(*in) * static_cast<double>(_Kernel[k]);
985  }
986  }
987 
988  // ---------------------------------------------------------------------------
989  /// Apply kernel to right neighbors of given voxel
990  template <class T>
991  void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc) const
992  {
993  int di = +1;
994  for (int k = _Radius - 1; k >= 0; --k) {
995  // Move voxel index/pointer
996  i += di;
997  in += s;
998  // Reverse if outside image/foreground
999  if (i < 0 || i >= n || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
1000  di = -di;
1001  s = -s;
1002  i += di;
1003  in += s;
1004  }
1005  // Multiply with kernel weight
1006  acc += static_cast<double>(*in) * static_cast<double>(_Kernel[k]);
1007  }
1008  }
1009 
1010  // ---------------------------------------------------------------------------
1011  template <class T>
1012  void Put(T *out, double acc) const
1013  {
1014  (*out) = static_cast<T>(_Norm * acc);
1015  }
1016 
1017  // ---------------------------------------------------------------------------
1018  // Data members
1019 private:
1020 
1021  const TKernel *_Kernel; ///< Convolution kernel
1022  int _Size; ///< Size of kernel = 2 * _Radius + 1
1023  int _Radius; ///< Radius of kernel
1024  double _Norm; ///< Normalization factor
1025  double _Background; ///< Background value (padding)
1026 };
1027 
1028 
1029 // -----------------------------------------------------------------------------
1030 /// Compute convolution of voxel with kernel in x dimension
1031 template <class TKernel = double>
1033 {
1034  ConvolveMirroredForegroundInX(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1035  : MirroredForegroundConvolution1D<TKernel>(image, kernel, size, norm), _X(image->X()) {}
1036 
1037  ConvolveMirroredForegroundInX(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1038  :
1039  MirroredForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1040  _X(image->X())
1041  {}
1042 
1043  template <class T1, class T2>
1044  void operator ()(int i, int, int, int, const T1 *in, T2 *out) const
1045  {
1046  double acc;
1047  if (this->ConvolveCenterVoxel(in, acc)) {
1048  this->ConvolveLeftNeighbors (i, _X, in, 1, acc);
1049  this->ConvolveRightNeighbors(i, _X, in, 1, acc);
1050  }
1051  this->Put(out, acc);
1052  }
1053 
1054  int _X; ///< Number of voxels in x dimension (nx)
1055 };
1056 
1057 // -----------------------------------------------------------------------------
1058 /// Compute convolution for given voxel along y dimension
1059 template <class TKernel = double>
1061 {
1062  ConvolveMirroredForegroundInY(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1063  :
1064  MirroredForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1065  _X(image->X()),
1066  _Y(image->Y())
1067  {}
1068 
1069  ConvolveMirroredForegroundInY(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1070  :
1071  MirroredForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1072  _X(image->X()),
1073  _Y(image->Y())
1074  {}
1075 
1076  template <class T1, class T2>
1077  void operator ()(int, int j, int, int, const T1 *in, T2 *out) const
1078  {
1079  double acc;
1080  if (this->ConvolveCenterVoxel(in, acc)) {
1081  this->ConvolveLeftNeighbors (j, _Y, in, _X, acc);
1082  this->ConvolveRightNeighbors(j, _Y, in, _X, acc);
1083  }
1084  this->Put(out, acc);
1085  }
1086 
1087  int _X; ///< Number of voxels in x dimension (nx)
1088  int _Y; ///< Number of voxels in y dimension (ny)
1089 };
1090 
1091 // -----------------------------------------------------------------------------
1092 /// Compute convolution for given voxel along z dimension
1093 template <class TKernel = double>
1095 {
1096  ConvolveMirroredForegroundInZ(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1097  :
1098  MirroredForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1099  _XY(image->X() * image->Y()),
1100  _Z (image->Z())
1101  {}
1102 
1103  ConvolveMirroredForegroundInZ(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1104  :
1105  MirroredForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1106  _XY(image->X() * image->Y()),
1107  _Z (image->Z())
1108  {}
1109 
1110  template <class T1, class T2>
1111  void operator ()(int, int, int k, int, const T1 *in, T2 *out) const
1112  {
1113  double acc;
1114  if (this->ConvolveCenterVoxel(in, acc)) {
1115  this->ConvolveLeftNeighbors (k, _Z, in, _XY, acc);
1116  this->ConvolveRightNeighbors(k, _Z, in, _XY, acc);
1117  }
1118  this->Put(out, acc);
1119  }
1120 
1121  int _XY; ///< Number of voxels in slice (nx * ny)
1122  int _Z; ///< Number of voxels in z dimension (nz)
1123 };
1124 
1125 // -----------------------------------------------------------------------------
1126 /// Compute convolution for given voxel along t dimension
1127 template <class TKernel = double>
1129 {
1130  ConvolveMirroredForegroundInT(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1131  :
1132  MirroredForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1133  _XYZ(image->X() * image->Y() * image->Z()),
1134  _T (image->T())
1135  {}
1136 
1137  ConvolveMirroredForegroundInT(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1138  :
1139  MirroredForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1140  _XYZ(image->X() * image->Y() * image->Z()),
1141  _T (image->T())
1142  {}
1143 
1144  template <class T1, class T2>
1145  void operator ()(int, int, int, int t, const T1 *in, T2 *out) const
1146  {
1147  double acc;
1148  if (this->ConvolveCenterVoxel(in, acc)) {
1149  this->ConvolveLeftNeighbors (t, _T, in, _XYZ, acc);
1150  this->ConvolveRightNeighbors(t, _T, in, _XYZ, acc);
1151  }
1152  this->Put(out, acc);
1153  }
1154 
1155  int _XYZ; ///< Number of voxels in frame (nx * ny * nz)
1156  int _T; ///< Number of voxels in t dimension (nt)
1157 };
1158 
1159 // =============================================================================
1160 // Convolve at boundary extended image foreground with 1D kernel
1161 // =============================================================================
1162 
1163 // -----------------------------------------------------------------------------
1164 /// Base class of 1D convolution functions which extends the foreground into background
1165 template <class TKernel = double>
1167 {
1168 protected:
1169 
1170  // ---------------------------------------------------------------------------
1171  /// Constructor
1172  ExtendedForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1173  :
1174  _Kernel(kernel), _Size(size), _Radius((size - 1) / 2), _Norm(norm)
1175  {
1176  if (image->HasBackgroundValue()) {
1177  _Background = image->GetBackgroundValueAsDouble();
1178  } else {
1179  _Background = NaN;
1180  }
1181  }
1182 
1183  // ---------------------------------------------------------------------------
1184  /// Apply kernel initially at center voxel
1185  template <class T>
1186  bool ConvolveCenterVoxel(const T *in, double &acc) const
1187  {
1188  if (AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
1189  acc = _Background / _Norm;
1190  return false;
1191  } else {
1192  acc = (*in) * _Kernel[_Radius];
1193  return true;
1194  }
1195  }
1196 
1197  // ---------------------------------------------------------------------------
1198  /// Apply kernel to left neighbors of given voxel
1199  template <class T>
1200  void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc) const
1201  {
1202  int di = 1;
1203  for (int k = _Radius + 1; k < _Size; ++k) {
1204  i -= di;
1205  in -= s;
1206  if (i < 0 || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
1207  i += di;
1208  in += s;
1209  di = s = 0;
1210  }
1211  // Multiply with kernel weight
1212  acc += static_cast<double>(*in) * static_cast<double>(_Kernel[k]);
1213  }
1214  }
1215 
1216  // ---------------------------------------------------------------------------
1217  /// Apply kernel to right neighbors of given voxel
1218  template <class T>
1219  void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc) const
1220  {
1221  int di = 1;
1222  for (int k = _Radius - 1; k >= 0; --k) {
1223  i += di;
1224  in += s;
1225  if (n <= i || AreEqualOrNaN(static_cast<double>(*in), _Background, Epsilon())) {
1226  i -= di;
1227  in -= s;
1228  di = s = 0;
1229  }
1230  // Multiply with kernel weight
1231  acc += static_cast<double>(*in) * static_cast<double>(_Kernel[k]);
1232  }
1233  }
1234 
1235  // ---------------------------------------------------------------------------
1236  template <class T>
1237  void Put(T *out, double acc) const
1238  {
1239  (*out) = static_cast<T>(_Norm * acc);
1240  }
1241 
1242  // ---------------------------------------------------------------------------
1243  // Data members
1244 private:
1245 
1246  const TKernel *_Kernel; ///< Convolution kernel
1247  int _Size; ///< Size of kernel = 2 * _Radius + 1
1248  int _Radius; ///< Radius of kernel
1249  double _Norm; ///< Normalization factor
1250  double _Background; ///< Background value (padding)
1251 };
1252 
1253 
1254 // -----------------------------------------------------------------------------
1255 /// Compute convolution of voxel with kernel in x dimension
1256 template <class TKernel = double>
1258 {
1259  ConvolveExtendedForegroundInX(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1260  : ExtendedForegroundConvolution1D<TKernel>(image, kernel, size, norm), _X(image->X()) {}
1261 
1262  ConvolveExtendedForegroundInX(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1263  :
1264  ExtendedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1265  _X(image->X())
1266  {}
1267 
1268  template <class T1, class T2>
1269  void operator ()(int i, int, int, int, const T1 *in, T2 *out) const
1270  {
1271  double acc;
1272  if (this->ConvolveCenterVoxel(in, acc)) {
1273  this->ConvolveLeftNeighbors (i, _X, in, 1, acc);
1274  this->ConvolveRightNeighbors(i, _X, in, 1, acc);
1275  }
1276  this->Put(out, acc);
1277  }
1278 
1279  int _X; ///< Number of voxels in x dimension (nx)
1280 };
1281 
1282 // -----------------------------------------------------------------------------
1283 /// Compute convolution for given voxel along y dimension
1284 template <class TKernel = double>
1286 {
1287  ConvolveExtendedForegroundInY(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1288  :
1289  ExtendedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1290  _X(image->X()),
1291  _Y(image->Y())
1292  {}
1293 
1294  ConvolveExtendedForegroundInY(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1295  :
1296  ExtendedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1297  _X(image->X()),
1298  _Y(image->Y())
1299  {}
1300 
1301  template <class T1, class T2>
1302  void operator ()(int, int j, int, int, const T1 *in, T2 *out) const
1303  {
1304  double acc;
1305  if (this->ConvolveCenterVoxel(in, acc)) {
1306  this->ConvolveLeftNeighbors (j, _Y, in, _X, acc);
1307  this->ConvolveRightNeighbors(j, _Y, in, _X, acc);
1308  }
1309  this->Put(out, acc);
1310  }
1311 
1312  int _X; ///< Number of voxels in x dimension (nx)
1313  int _Y; ///< Number of voxels in y dimension (ny)
1314 };
1315 
1316 // -----------------------------------------------------------------------------
1317 /// Compute convolution for given voxel along z dimension
1318 template <class TKernel = double>
1320 {
1321  ConvolveExtendedForegroundInZ(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1322  :
1323  ExtendedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1324  _XY(image->X() * image->Y()),
1325  _Z (image->Z())
1326  {}
1327 
1328  ConvolveExtendedForegroundInZ(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1329  :
1330  ExtendedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1331  _XY(image->X() * image->Y()),
1332  _Z (image->Z())
1333  {}
1334 
1335  template <class T1, class T2>
1336  void operator ()(int, int, int k, int, const T1 *in, T2 *out) const
1337  {
1338  double acc;
1339  if (this->ConvolveCenterVoxel(in, acc)) {
1340  this->ConvolveLeftNeighbors (k, _Z, in, _XY, acc);
1341  this->ConvolveRightNeighbors(k, _Z, in, _XY, acc);
1342  }
1343  this->Put(out, acc);
1344  }
1345 
1346  int _XY; ///< Number of voxels in slice (nx * ny)
1347  int _Z; ///< Number of voxels in z dimension (nz)
1348 };
1349 
1350 // -----------------------------------------------------------------------------
1351 /// Compute convolution for given voxel along t dimension
1352 template <class TKernel = double>
1354 {
1355  ConvolveExtendedForegroundInT(const BaseImage *image, const TKernel *kernel, int size, double norm = 1.0)
1356  :
1357  ExtendedForegroundConvolution1D<TKernel>(image, kernel, size, norm),
1358  _XYZ(image->X() * image->Y() * image->Z()),
1359  _T (image->T())
1360  {}
1361 
1362  ConvolveExtendedForegroundInT(const BaseImage *image, const GenericImage<TKernel> *kernel, double norm = 1.0)
1363  :
1364  ExtendedForegroundConvolution1D<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1365  _XYZ(image->X() * image->Y() * image->Z()),
1366  _T (image->T())
1367  {}
1368 
1369  template <class T1, class T2>
1370  void operator ()(int, int, int, int t, const T1 *in, T2 *out) const
1371  {
1372  double acc;
1373  if (this->ConvolveCenterVoxel(in, acc)) {
1374  this->ConvolveLeftNeighbors (t, _T, in, _XYZ, acc);
1375  this->ConvolveRightNeighbors(t, _T, in, _XYZ, acc);
1376  }
1377  this->Put(out, acc);
1378  }
1379 
1380  int _XYZ; ///< Number of voxels in frame (nx * ny * nz)
1381  int _T; ///< Number of voxels in t dimension (nt)
1382 };
1383 
1384 // =============================================================================
1385 // Smooth and downsample mirrored foreground in one step
1386 // =============================================================================
1387 
1388 // -----------------------------------------------------------------------------
1389 /// Downsample image using convolution of original voxels with kernel in x dimension
1390 template <class TVoxel, class TKernel = RealPixel>
1392 {
1393  DownsampleConvolvedMirroredForegroundInX(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1394  :
1395  ConvolveMirroredForegroundInX<TKernel>(image, kernel, size, norm),
1396  _Input(image), _Offset((image->X() % m + 1) / 2), _Factor(m)
1397  {}
1398 
1399  DownsampleConvolvedMirroredForegroundInX(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1400  :
1401  ConvolveMirroredForegroundInX<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1402  _Input(image), _Offset((image->X() % m + 1) / 2), _Factor(m)
1403  {}
1404 
1405  template <class T>
1406  void operator ()(int i, int j, int k, int l, T *out) const
1407  {
1408  i = _Offset + i * _Factor;
1409  ConvolveMirroredForegroundInX<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1410  }
1411 
1412  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1413  int _Offset; ///< Offset of first voxel
1414  int _Factor; ///< Downsampling factor
1415 };
1416 
1417 // -----------------------------------------------------------------------------
1418 /// Downsample image using convolution of original voxels with kernel in y dimension
1419 template <class TVoxel, class TKernel = RealPixel>
1421 {
1422  DownsampleConvolvedMirroredForegroundInY(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1423  :
1424  ConvolveMirroredForegroundInY<TKernel>(image, kernel, size, norm),
1425  _Input(image), _Offset((image->Y() % m + 1) / 2), _Factor(m)
1426  {}
1427 
1428  DownsampleConvolvedMirroredForegroundInY(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1429  :
1430  ConvolveMirroredForegroundInY<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1431  _Input(image), _Offset((image->Y() % m + 1) / 2), _Factor(m)
1432  {}
1433 
1434  template <class T>
1435  void operator ()(int i, int j, int k, int l, T *out) const
1436  {
1437  j = _Offset + j * _Factor;
1438  ConvolveMirroredForegroundInY<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1439  }
1440 
1441  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1442  int _Offset; ///< Offset of first voxel
1443  int _Factor; ///< Downsampling factor
1444 };
1445 
1446 // -----------------------------------------------------------------------------
1447 /// Downsample image using convolution of original voxels with kernel in z dimension
1448 template <class TVoxel, class TKernel = RealPixel>
1450 {
1451  DownsampleConvolvedMirroredForegroundInZ(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1452  :
1453  ConvolveMirroredForegroundInZ<TKernel>(image, kernel, size, norm),
1454  _Input(image), _Offset((image->Z() % m + 1) / 2), _Factor(m)
1455  {}
1456 
1457  DownsampleConvolvedMirroredForegroundInZ(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1458  :
1459  ConvolveMirroredForegroundInZ<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1460  _Input(image), _Offset((image->Z() % m + 1) / 2), _Factor(m)
1461  {}
1462 
1463  template <class T>
1464  void operator ()(int i, int j, int k, int l, T *out) const
1465  {
1466  k = _Offset + k * _Factor;
1467  ConvolveMirroredForegroundInZ<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1468  }
1469 
1470  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1471  int _Offset; ///< Offset of first voxel
1472  int _Factor; ///< Downsampling factor
1473 };
1474 
1475 // =============================================================================
1476 // Smooth and downsample extended foreground in one step
1477 // =============================================================================
1478 
1479 // -----------------------------------------------------------------------------
1480 /// Downsample image using convolution of original voxels with kernel in x dimension
1481 template <class TVoxel, class TKernel = RealPixel>
1483 {
1484  DownsampleConvolvedExtendedForegroundInX(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1485  :
1486  ConvolveExtendedForegroundInX<TKernel>(image, kernel, size, norm),
1487  _Input(image), _Offset((image->X() % m + 1) / 2), _Factor(m)
1488  {}
1489 
1490  DownsampleConvolvedExtendedForegroundInX(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1491  :
1492  ConvolveExtendedForegroundInX<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1493  _Input(image), _Offset((image->X() % m + 1) / 2), _Factor(m)
1494  {}
1495 
1496  template <class T>
1497  void operator ()(int i, int j, int k, int l, T *out) const
1498  {
1499  i = _Offset + i * _Factor;
1500  ConvolveExtendedForegroundInX<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1501  }
1502 
1503  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1504  int _Offset; ///< Offset of first voxel
1505  int _Factor; ///< Downsampling factor
1506 };
1507 
1508 // -----------------------------------------------------------------------------
1509 /// Downsample image using convolution of original voxels with kernel in y dimension
1510 template <class TVoxel, class TKernel = RealPixel>
1512 {
1513  DownsampleConvolvedExtendedForegroundInY(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1514  :
1515  ConvolveExtendedForegroundInY<TKernel>(image, kernel, size, norm),
1516  _Input(image), _Offset((image->Y() % m + 1) / 2), _Factor(m)
1517  {}
1518 
1519  DownsampleConvolvedExtendedForegroundInY(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1520  :
1521  ConvolveExtendedForegroundInY<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1522  _Input(image), _Offset((image->Y() % m + 1) / 2), _Factor(m)
1523  {}
1524 
1525  template <class T>
1526  void operator ()(int i, int j, int k, int l, T *out) const
1527  {
1528  j = _Offset + j * _Factor;
1529  ConvolveExtendedForegroundInY<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1530  }
1531 
1532  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1533  int _Offset; ///< Offset of first voxel
1534  int _Factor; ///< Downsampling factor
1535 };
1536 
1537 // -----------------------------------------------------------------------------
1538 /// Downsample image using convolution of original voxels with kernel in z dimension
1539 template <class TVoxel, class TKernel = RealPixel>
1541 {
1542  DownsampleConvolvedExtendedForegroundInZ(const GenericImage<TVoxel> *image, int m, const TKernel *kernel, int size, double norm = 1.0)
1543  :
1544  ConvolveExtendedForegroundInZ<TKernel>(image, kernel, size, norm),
1545  _Input(image), _Offset((image->Z() % m + 1) / 2), _Factor(m)
1546  {}
1547 
1548  DownsampleConvolvedExtendedForegroundInZ(const GenericImage<TVoxel> *image, int m, const GenericImage<TKernel> *kernel, double norm = 1.0)
1549  :
1550  ConvolveExtendedForegroundInZ<TKernel>(image, kernel->Data(), kernel->NumberOfVoxels(), norm),
1551  _Input(image), _Offset((image->Z() % m) / 2), _Factor(m)
1552  {}
1553 
1554  template <class T>
1555  void operator ()(int i, int j, int k, int l, T *out) const
1556  {
1557  k = _Offset + k * _Factor;
1558  ConvolveExtendedForegroundInZ<TKernel>::operator ()(i, j, k, l, _Input->Data(i, j, k, l), out);
1559  }
1560 
1561  const GenericImage<TVoxel> *_Input; ///< Image to downsample
1562  int _Offset; ///< Offset of first voxel
1563  int _Factor; ///< Downsampling factor
1564 };
1565 
1566 
1567 } } // namespace mirtk::VoxelConvolution
1568 
1569 #endif // MIRTK_ConvolutionFunction_H
Downsample image using convolution of original voxels with kernel in y dimension. ...
TruncatedForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, bool norm=true)
Constructor.
bool HasBackgroundValue() const
Whether a background value has been set.
Definition: BaseImage.h:1425
double GetBackgroundValueAsDouble() const
Get background value.
Definition: BaseImage.h:1413
Compute convolution for given voxel along z dimension.
Perform convolution along t with truncation of kernel at boundary.
VoxelType * Data(int=0)
Get raw pointer to contiguous image data.
Definition: GenericImage.h:740
Compute convolution for given voxel along t dimension.
int _XYZ
Number of voxels in frame (nx * ny * nz)
MirroredForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, double norm=1.0)
Constructor.
const GenericImage< TVoxel > * _Input
Image to downsample.
const BaseImage * _Image
Image defining foreground region.
Perform convolution of weighted image along t with truncation of kernel at boundary.
Downsample image using convolution of original voxels with kernel in z dimension. ...
Compute convolution of voxel with kernel in x dimension.
const GenericImage< TVoxel > * _Input
Image to downsample.
bool _Normalize
Wether to divide by sum of kernel weights.
Perform convolution of image foreground along z with truncation of kernel at boundary.
bool _Normalize
Wether to divide by sum of kernel weights.
Compute convolution of voxel with kernel in x dimension.
void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc, double &sum) const
Apply kernel to right neighbors of given voxel.
Downsample image using convolution of original voxels with kernel in z dimension. ...
double Epsilon()
Tolerance used for floating point comparisons.
int _XY
Number of voxels in slice (nx * ny)
const BaseImage * _Image
Image defining foreground region.
Perform convolution of weighted image along x with truncation of kernel at boundary.
const BaseImage * _Image
Image defining foreground region.
bool _Normalize
Wether to divide by sum of kernel weights.
void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc) const
Apply kernel to right neighbors of given voxel.
bool ConvolveCenterVoxel(const T *in, double &acc, double &sum) const
Apply kernel initially at center voxel.
Base class of 1D convolution functions which truncate the kernel at the foreground boundary...
Base class of 1D convolution functions which extends the foreground into background.
Perform convolution along z with truncation of kernel at boundary.
Compute convolution for given voxel along t dimension.
int _XYZ
Number of voxels in frame (nx * ny * nz)
bool ConvolveCenterVoxel(const T *in, double &acc) const
Apply kernel initially at center voxel.
bool IsForeground(int) const
Whether voxel is within foreground without index-out-of-bounds check.
Definition: BaseImage.h:1455
Downsample image using convolution of original voxels with kernel in y dimension. ...
const GenericImage< TVoxel > * _Input
Image to downsample.
const TKernel * _Kernel
Convolution kernel.
MIRTKCU_API bool AreEqualOrNaN(double a, double b, double tol=1e-12)
Determine equality of two floating point numbers including check if both are NaN. ...
Definition: Math.h:122
bool _Normalize
Wether to divide by sum of kernel weights.
bool _Normalize
Wether to divide by sum of kernel weights.
bool ConvolveCenterVoxel(const T *in, double &acc) const
Apply kernel initially at center voxel.
Definition: IOConfig.h:41
Base class of 1D convolution functions which mirror the foreground into background.
int _XYZ
Number of voxels in volume (nx * ny * nz)
Compute convolution for given voxel along t dimension.
ExtendedForegroundConvolution1D(const BaseImage *image, const TKernel *kernel, int size, double norm=1.0)
Constructor.
int _XY
Number of voxels in slice (nx * ny)
void ConvolveRightNeighbors(int i, int n, const T *in, int s, double &acc) const
Apply kernel to right neighbors of given voxel.
Perform convolution along y with truncation of kernel at boundary.
int _XY
Number of voxels in slice (nx * ny)
const TKernel * _Kernel
Convolution kernel.
int _XYZ
Number of voxels in volume (nx * ny * nz)
Downsample image using convolution of original voxels with kernel in x dimension. ...
Compute convolution of voxel with kernel in x dimension.
Compute convolution for given voxel along z dimension.
Perform convolution of image foreground along x with truncation of kernel at boundary.
bool _Normalize
Wether to divide by sum of kernel weights.
Downsample image using convolution of original voxels with kernel in x dimension. ...
bool _Normalize
Wether to divide by sum of kernel weights.
const GenericImage< TVoxel > * _Input
Image to downsample.
Perform convolution of weighted image along z with truncation of kernel at boundary.
int _XYZ
Number of voxels in frame (nx * ny * nz)
MIRTKCU_API bool AreEqual(double a, double b, double tol=1e-12)
Determine equality of two floating point numbers.
Definition: Math.h:115
void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc) const
Apply kernel to left neighbors of given voxel.
Perform convolution of image foreground along t with truncation of kernel at boundary.
Compute convolution for given voxel along z dimension.
void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc, double &sum) const
Apply kernel to left neighbors of given voxel.
int NumberOfVoxels() const
Returns the total number of voxels.
Definition: BaseImage.h:862
bool _Normalize
Wether to divide by sum of kernel weights.
bool _Normalize
Whether to normalize by sum of overlapping kernel weights.
void ConvolveLeftNeighbors(int i, int n, const T *in, int s, double &acc) const
Apply kernel to left neighbors of given voxel.
const TKernel * _Kernel
Convolution kernel.
int _Offset
Vector component offset.
Compute convolution for given voxel along y dimension.
const GenericImage< TVoxel > * _Input
Image to downsample.
Compute convolution for given voxel along y dimension.
const GenericImage< TVoxel > * _Input
Image to downsample.
Perform convolution along x with truncation of kernel at boundary.
Perform convolution of weighted image along y with truncation of kernel at boundary.
const TKernel * _Kernel
Convolution kernel.
int _XYZ
Number of voxels in volume (nx * ny * nz)
const BaseImage * _Image
Image defining foreground region.
Perform convolution of image foreground along y with truncation of kernel at boundary.
Compute convolution for given voxel along y dimension.