RegisteredImage.h
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_RegisteredImage_H
21 #define MIRTK_RegisteredImage_H
22 
23 #include "mirtk/GenericImage.h"
24 
25 #include "mirtk/Parallel.h"
26 #include "mirtk/Transformation.h"
27 #include "mirtk/InterpolationMode.h"
28 #include "mirtk/ExtrapolationMode.h"
29 
30 #include "mirtk/TestProd.h"
31 
32 
33 namespace mirtk {
34 
35 
36 /**
37  * Registered image such as fixed target image or transformed source image
38  *
39  * An instance of this (multi-channel) image class stores the intensities
40  * of a registered image after alignment using the current transformation
41  * estimate. If no (changing) transformation is set, the image is considered
42  * to be fixed and is thus only updated once during the initialization.
43  * If a transformation is set, however, upon every update of the input
44  * transformation, the new moving image intensities are computed by applying
45  * the current transformation and interpolating the input image intensities.
46  * For the computation of the similarity measure gradient, the warped image
47  * derivatives are stored along with the intensities in the consecutive image
48  * channels of the registered image instance (i.e., t dimension). In case of
49  * a gradient field similarity measure, these derivatives include not only the
50  * 1st order, but also 2nd order derivatives needed for the similarity measure
51  * gradient computation. An image similarity specifies which channels are
52  * required for its evaluation by calling the Initialize function with a suitable
53  * number of channels (i.e., 1, 4, or 10) and request their udpate using the
54  * appropriate parameters of the Update function.
55  *
56  * The assignment of the transformed intensities and derivatives to the fourth
57  * dimension (i.e. channels) of the registered image is as follows:
58  *
59  * - t=0: Transformed intensity
60  * - t=1: Transformed 1st order derivative w.r.t x
61  * - t=2: Transformed 1st order derivative w.r.t y
62  * - t=3: Transformed 1st order derivative w.r.t z
63  * - t=4: Transformed 2nd order derivative w.r.t xx
64  * - t=5: Transformed 2nd order derivative w.r.t xy
65  * - t=6: Transformed 2nd order derivative w.r.t xz
66  * - t=7: Transformed 2nd order derivative w.r.t yy
67  * - t=8: Transformed 2nd order derivative w.r.t yz
68  * - t=9: Transformed 2nd order derivative w.r.t zz
69  */
70 class RegisteredImage : public GenericImage<double>
71 {
72  mirtkObjectMacro(RegisteredImage);
73 
74 public:
75 
76  // Do not override other base class overloads
78 
79  // ---------------------------------------------------------------------------
80  // Types
81 public:
82 
83  /// Enumeration of registered image channel indices
84  enum Channel { I = 0, Dx, Dy, Dz, Dxx, Dxy, Dxz, Dyx, Dyy, Dyz, Dzx, Dzy, Dzz };
85 
86  /// Type of untransformed input image
88 
89  /// Type of untransformed gradient image
91 
92  /// Type of untransformed Hessian image
94 
95  /// Type of cached displacement fields
97 
98  // ---------------------------------------------------------------------------
99  // Attributes
100 
101  /// Untransformed input image
102  mirtkPublicAggregateMacro(InputImageType, InputImage);
103 
104  /// Untransformed input gradient image
105  mirtkPublicComponentMacro(InputGradientType, InputGradient);
106 
107  /// Untransformed input Hessian image
108  mirtkPublicComponentMacro(InputHessianType, InputHessian);
109 
110  /// Current transformation estimate
112 
113  /// Interpolation mode
115 
116  /// Extrapolation mode
118 
119  /// Pre-computed image to world coordinates
120  mirtkPublicAggregateMacro(WorldCoordsImage, WorldCoordinates);
121 
122  /// Pre-computed image to world coordinates
124 
125  /// Externally pre-computed displacements to use
126  mirtkPublicAggregateMacro(DisplacementImageType, ExternalDisplacement);
127 
128  /// Pre-computed fixed displacements
129  mirtkComponentMacro(DisplacementImageType, FixedDisplacement);
130 
131  /// Pre-computed displacements
132  mirtkComponentMacro(DisplacementImageType, Displacement);
133 
134  /// Whether to use pre-computed world coordinates
135  mirtkPublicAttributeMacro(bool, CacheWorldCoordinates);
136 
137  /// Whether to use pre-computed fixed displacements
138  mirtkPublicAttributeMacro(bool, CacheFixedDisplacement);
139 
140  /// Whether to use pre-computed displacements
141  mirtkPublicAttributeMacro(bool, CacheDisplacement);
142 
143  /// Whether self-update is enabled
144  mirtkPublicAttributeMacro(bool, SelfUpdate);
145 
146  /// Minimum foreground intensity of input image
147  mirtkReadOnlyAttributeMacro(double, MinInputIntensity);
148 
149  /// Maximum foreground intensity of input image
150  mirtkReadOnlyAttributeMacro(double, MaxInputIntensity);
151 
152  /// Minimum foreground intensity of warped image or NaN
153  mirtkPublicAttributeMacro(double, MinIntensity);
154 
155  /// Maximum foreground intensity of warped image or NaN
156  mirtkPublicAttributeMacro(double, MaxIntensity);
157 
158  /// Standard deviation of Gaussian smoothing kernel applied before
159  /// 1st order derivative computations in voxel units
160  mirtkPublicAttributeMacro(double, GradientSigma);
161 
162  /// Standard deviation of Gaussian smoothing kernel applied before
163  /// 2nd order derivative computations in voxel units
164  mirtkPublicAttributeMacro(double, HessianSigma);
165 
166  /// Whether to precompute image derivatives
167  /// true: Compute derivatives of input image and transform these
168  /// false: Use derivative of interpolation kernel to evaluate image derivative
169  mirtkPublicAttributeMacro(bool, PrecomputeDerivatives);
170 
171  /// Maximum gradient magnitude as percentile of all gradient magnitudes
172  ///
173  /// \note Used only when _PrecomputeDerivatives is enabled.
174  mirtkPublicAttributeMacro(int, MaxGradientPercentile);
175 
176  /// When positive and not inf, linearly rescale gradient vectors to this maximum magnitude
177  mirtkPublicAttributeMacro(double, MaxGradientMagnitude);
178 
179 protected:
180 
181  /// Number of active levels
183 
184  /// Number of passive levels (fixed transformation)
186 
187  /// Offsets of the different registered image channels
188  int _Offset[13];
189 
190  /// (Pre-)compute gradient of input image
191  /// \param[in] sigma Standard deviation of Gaussian smoothing filter in voxels.
192  void ComputeInputGradient(double sigma);
193 
194  /// (Pre-)compute Hessian of input image
195  /// \param[in] sigma Standard deviation of Gaussian smoothing filter in voxels.
196  void ComputeInputHessian(double sigma);
197 
198 private:
199 
200  /// Internal voxel-wise update functor object
201  SharedPtr<Object> _Evaluator;
202 
203  // ---------------------------------------------------------------------------
204  // Construction/Destruction
205 public:
206 
207  /// Constructor
208  RegisteredImage();
209 
210  /// Copy constructor
212 
213  /// Assignment operator
215 
216  /// Destructor
218 
219  /// Get interpolation mode
220  ///
221  /// When _InterpolationMode is Interpolation_Default, this function returns
222  /// the respective default interpolation mode with or without padding.
224 
225  // ---------------------------------------------------------------------------
226  // Channels
227 
228  /// Number of registered channels
229  int NumberOfChannels() const;
230 
231  /// Number of voxels per registered channel
232  int NumberOfVoxels() const;
233 
234  /// Offset of channel \p c with respect to the start of the image data
235  int Offset(int) const;
236 
237  // ---------------------------------------------------------------------------
238  // Initialization/Update
239 
240  /// Initialize image
241  ///
242  /// This function is called by the image similarity to set the attributes
243  /// of the registered image, i.e., the attributes of the discrete image domain
244  /// on which similarity is to be evaluated. It initializes the image to zero.
245  /// The Update function must be called to initialize the output image
246  /// intensities and derivatives before similarity can be evaluated.
247  void Initialize(const ImageAttributes &, int = 0);
248 
249  /// Update image intensity and transformed derivatives
250  ///
251  /// This function updates the requested output image channels only within the
252  /// specified image region. If the image is not transformed or only warped by
253  /// a fixed transformation, this function does nothing. Set \c force to \c true
254  /// to initialize this image even if it does not change over the course of the
255  /// registration or use the corresponding Recompute function.
256  ///
257  /// \param[in] region Image region to update.
258  /// \param[in] intensity Request update of scalar intensities.
259  /// \param[in] gradient Request update of 1st order derivatives.
260  /// \param[in] hessian Request update of 2nd order derivatives.
261  /// \param[in] force Force update in any case.
262  void Update(const blocked_range3d<int> &region,
263  bool intensity = true, bool gradient = false, bool hessian = false,
264  bool force = false);
265 
266  /// Update image intensity and transformed derivatives using custom displacements
267  ///
268  /// \param[in] region Image region to update.
269  /// \param[in] disp Custom displacement field to use.
270  /// \param[in] intensity Request update of scalar intensities.
271  /// \param[in] gradient Request update of 1st order derivatives.
272  /// \param[in] hessian Request update of 2nd order derivatives.
273  void Update(const blocked_range3d<int> &region,
274  const DisplacementImageType *disp,
275  bool intensity = true, bool gradient = false, bool hessian = false);
276 
277  /// Update image intensity and transformed derivatives
278  ///
279  /// This function only updates the specified image channels if the self-update
280  /// of this image is enabled and only if a (changing) transformation is set.
281  /// If the image is not transformed or only warped by a fixed transformation,
282  /// this function does nothing. Use \c force=true to initialize this image
283  /// even if does not change over the course of the registration.
284  ///
285  /// \param[in] intensity Request update of scalar intensities.
286  /// \param[in] gradient Request update of 1st order derivatives.
287  /// \param[in] hessian Request update of 2nd order derivatives.
288  /// \param[in] force Force update in any case.
289  void Update(bool intensity = true, bool gradient = false, bool hessian = false,
290  bool force = false);
291 
292  /// Force update of all output channels
293  ///
294  /// This convenience function always recomputes all output channels within
295  /// the specified image region. To do so, it calls the Update function with
296  /// \c force set to \c true. It is commonly used after Initialize in an
297  /// application where the input image and transformation are not being modified
298  /// or when the SelfUpdate of the image has been disabled to explicitly
299  /// recompute the image content after the input was modified.
300  void Recompute(const blocked_range3d<int> &region);
301 
302  /// Force update of all output channels
303  ///
304  /// This convenience function always recomputes all output channels.
305  /// To do so, it calls the Update function with \c force set to \c true.
306  /// It is commonly used after Initialize in an application where the
307  /// input image and transformation are not being modified or when the
308  /// SelfUpdate of the image has been disabled to explicitly recompute
309  /// the image content after the input was modified.
310  void Recompute();
311 
312 private:
313 
314  template <class> void Update1(const blocked_range3d<int> &, bool, bool, bool);
315  template <class, class, class, class> void Update2(const blocked_range3d<int> &, bool, bool, bool);
316  template <class, class> void Update3(const blocked_range3d<int> &, bool, bool, bool);
317 
318  FRIEND_TEST(RegisteredImage, GlobalAndLocalTransformation);
319 };
320 
321 ////////////////////////////////////////////////////////////////////////////////
322 // Inline definitions
323 ////////////////////////////////////////////////////////////////////////////////
324 
325 // -----------------------------------------------------------------------------
327 {
328  return _attr._t;
329 }
330 
331 // -----------------------------------------------------------------------------
333 {
334  return _attr._x * _attr._y * _attr._z;
335 }
336 
337 // -----------------------------------------------------------------------------
338 inline int RegisteredImage::Offset(int c) const
339 {
340  return _Offset[c];
341 }
342 
343 
344 } // namespace mirtk
345 
346 #endif // MIRTK_RegisteredImage_H
int NumberOfVoxels() const
Number of voxels per registered channel.
int _NumberOfActiveLevels
Number of active levels.
GenericImage< double > DisplacementImageType
Type of cached displacement fields.
int _y
Image y-dimension (in voxels)
enum InterpolationMode GetInterpolationMode() const
void ComputeInputHessian(double sigma)
Channel
Enumeration of registered image channel indices.
RegisteredImage & operator=(const RegisteredImage &)
Assignment operator.
InterpolationMode
Image interpolation modes.
ImageAttributes _attr
Image attributes.
Definition: BaseImage.h:99
void Update(const blocked_range3d< int > &region, bool intensity=true, bool gradient=false, bool hessian=false, bool force=false)
Definition: IOConfig.h:41
~RegisteredImage()
Destructor.
void ImageToWorld(double &, double &) const
Image to world coordinate conversion with two doubles.
Definition: BaseImage.h:1180
int _z
Image z-dimension (in voxels)
void ComputeInputGradient(double sigma)
int _t
Image t-dimension (in voxels)
mirtkComponentMacro(DisplacementImageType, FixedDisplacement)
Pre-computed fixed displacements.
Three-dimensional range.
Definition: Parallel.h:197
GenericImage< VoxelType > InputImageType
Type of untransformed input image.
ExtrapolationMode
Image extrapolation modes.
mirtkReadOnlyAttributeMacro(double, MinInputIntensity)
Minimum foreground intensity of input image.
mirtkPublicComponentMacro(InputGradientType, InputGradient)
Untransformed input gradient image.
mirtkPublicAggregateMacro(InputImageType, InputImage)
Untransformed input image.
GenericImage< VoxelType > InputHessianType
Type of untransformed Hessian image.
int Offset(int) const
Offset of channel c with respect to the start of the image data.
virtual void Initialize()
Initialize a previously allocated image.
GenericImage< VoxelType > InputGradientType
Type of untransformed gradient image.
int NumberOfChannels() const
Number of registered channels.
int _NumberOfPassiveLevels
Number of passive levels (fixed transformation)
int _Offset[13]
Offsets of the different registered image channels.
int _x
Image x-dimension (in voxels)
RegisteredImage()
Constructor.
mirtkPublicAttributeMacro(enum InterpolationMode, InterpolationMode)
Interpolation mode.