Developer documentation
Version 3.0.3-105-gd3941f44
compose.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __registration_warp_compose_h__
18#define __registration_warp_compose_h__
19
20#include "algo/threaded_loop.h"
21#include "image.h"
22#include "transform.h"
23#include "interp/linear.h"
24#include "adapter/jacobian.h" //TODO remove after debug
26#include "adapter/extract.h"
27
28namespace MR
29{
30 namespace Registration
31 {
32 namespace Warp
33 {
34
36 public:
39
40
41 template <class InputDeformationFieldType, class OutputDeformationFieldType>
42 void operator() (InputDeformationFieldType& deform_input, OutputDeformationFieldType& deform_output) {
43 deform_output.row(3) = transform * Eigen::Vector3d (deform_input.row(3));
44 }
45
46 protected:
48 };
49
50
52 public:
53 template<class DisplacementFieldType>
54 ComposeLinearDispKernel (const transform_type& linear_transform, const DisplacementFieldType& disp_in) :
56 image_transform (disp_in) {}
57
58
59 template <class DisplacementFieldType, class DeformationFieldType>
60 void operator() (DisplacementFieldType& disp_input, DeformationFieldType& deform_output) {
61 Eigen::Vector3d voxel (disp_input.index(0), disp_input.index(1), disp_input.index(2));
62 deform_output.row(3) = linear_transform * (image_transform.voxel2scanner * voxel + Eigen::Vector3d (disp_input.row(3)));
63 }
64
65 protected:
68 };
69
71 public:
73 disp1_transform (disp_input1), disp2_interp (disp_input2), step (step) {}
74
75
76 void operator() (Image<default_type>& disp_input1, Image<default_type>& disp_output) {
77 Eigen::Vector3d voxel ((default_type)disp_input1.index(0), (default_type)disp_input1.index(1), (default_type)disp_input1.index(2));
78 Eigen::Vector3d voxel_position = disp1_transform.voxel2scanner * voxel;
79 Eigen::Vector3d original_position = voxel_position + Eigen::Vector3d(disp_input1.row(3));
80 disp2_interp.scanner (original_position);
81 if (!disp2_interp) {
82 disp_output.row(3) = disp_input1.row(3);
83 } else {
84 Eigen::Vector3d displacement (Eigen::Vector3d(disp2_interp.row(3)).array() * step);
85 Eigen::Vector3d new_position = displacement + original_position;
86 disp_output.row(3) = new_position - voxel_position;
87 }
88 }
89
90 protected:
94 };
95
96
97 template <class DeformationField1Type, class DeformationField2Type>
99 public:
100 ComposeHalfwayKernel (const transform_type& linear1, DeformationField1Type& deform1,
101 DeformationField2Type& deform2, const transform_type& linear2) :
102 linear1 (linear1), deform1_interp (deform1), deform2_interp (deform2), linear2 (linear2) {
103 out_of_bounds.setOnes();
105 }
106
107
108 void operator() (Image<default_type>& deform) {
109 Eigen::Vector3d voxel ((default_type)deform.index(0), (default_type)deform.index(1), (default_type)deform.index(2));
110 Eigen::Vector3d position = linear1 * voxel;
111 deform1_interp.scanner (position);
112 if (!deform1_interp) {
113 deform.row(3) = out_of_bounds;
114 } else {
115 Eigen::Vector3d position2 = deform1_interp.row(3);
116 deform2_interp.scanner (position2);
117 if (!deform2_interp) {
118 deform.row(3) = out_of_bounds;
119 } else {
120 Eigen::Vector3d position3 = deform2_interp.row(3);
121 deform.row(3) = linear2 * position3;
122 }
123 }
124 }
125
126 protected:
131 Eigen::Vector3d out_of_bounds;
132 };
133
134
135 // Compose a linear transform and a displacement field. The output field is a deformation field. The input and output can be the same image.
136 template <class DisplacementFieldType, class DeformationFieldType>
137 FORCE_INLINE void compose_linear_displacement (const transform_type& transform, DisplacementFieldType& disp_in, DeformationFieldType& deform_out)
138 {
139 check_dimensions (disp_in, deform_out, 0, 3);
140 ThreadedLoop (disp_in, 0, 3).run (ComposeLinearDispKernel (transform, disp_in), disp_in, deform_out);
141 }
142
143 // Compose a linear transform and a deformation field. The output field is a deformation field. The input and output can be the same image.
144 template <class InputDeformationFieldType, class OutputDeformationFieldType>
145 FORCE_INLINE void compose_linear_deformation (const transform_type& transform, InputDeformationFieldType& deform_in, OutputDeformationFieldType& deform_out)
146 {
147 check_dimensions (deform_in, deform_out, 0, 3);
148 ThreadedLoop (deform_in, 0, 3).run (ComposeLinearDeformKernel (transform), deform_in, deform_out);
149 }
150
151 // Compose two displacement fields and output a displacement field. The input and output can be the same image.
153 {
154 check_dimensions (input, output, 0, 3);
155 ThreadedLoop (input, 0, 3).run (ComposeDispKernel (input, update, step), input, output);
156 }
157
158 // Compose two displacement fields and output a displacement field using scaling and squaring. The input and output can be the same image.
160 {
161 check_dimensions (input, output, 0, 3);
162
163 default_type max_norm = 0.0;
164 auto max_norm_func = [&max_norm](Image<default_type>& update) {
165 default_type norm = Eigen::Vector3d (update.row(3)).norm();
166 if (norm > max_norm)
167 max_norm = norm;
168 };
169 ThreadedLoop (update).run (max_norm_func, update);
170 default_type min_vox_size = static_cast<default_type> (std::min (input.spacing(0), std::min (input.spacing(1), input.spacing(2))));
171
172 // if the maximum update is larger than half a voxel, perform scaling and squaring to ensure the displacement field remains diffeomorphic
173
174 default_type scale_factor = 1.0;
175 if (max_norm * step < min_vox_size / 2.0) {
176 update_displacement (input, update, output, step);
177 } else {
178 scale_factor = std::pow (2, std::ceil (std::log ((max_norm * step) / (min_vox_size / 2.0)) / std::log (2.0)));
179
180 std::shared_ptr<Image<default_type>> scaled_update = make_shared<Image<default_type> >(Image<default_type>::scratch (update));
181 std::shared_ptr<Image<default_type>> composed = make_shared<Image<default_type> >(Image<default_type>::scratch (update));
182
183 // Scaling
184 default_type scaled_step = step / scale_factor; // apply the step size and scale factor at once
185 ThreadedLoop (update).run (
186 [&scaled_step](Image<default_type>& update, Image<default_type>& scaled_update) {
187 scaled_update.row(3) = Eigen::Vector3d (update.row(3)) * scaled_step;
188 }, update, *scaled_update);
189
190// CONSOLE ("composing " + str(std::log2 (scale_factor)) + "times");
191
192 // Squaring
193 for (size_t i = 0; i < std::log2 (scale_factor); ++i) {
194 update_displacement (*scaled_update, *scaled_update, *composed);
195 std::swap (scaled_update, composed);
196 }
197// save (*scaled_update, std::string("composed_update.mif"), false);
198// Adapter::Jacobian<Image<default_type> > jacobian (*scaled_update);
199// Header header (*scaled_update);
200// header.ndim() = 3;
201// bool is_neg = false;
202// auto jacobian_det = Image<default_type>::scratch (header);
203// Eigen::MatrixXd ident = Eigen::MatrixXd::Identity (3,3);
204// for (auto i = Loop (0,3) (jacobian, jacobian_det); i; ++i) {
205// auto jac_matrix = ident + jacobian.value();
206// jacobian_det.value() = jac_matrix.determinant();
207// if (jacobian_det.value() < 0.0)
208// is_neg = true;
209// }
210// save (jacobian_det, std::string("jacobian.mif"), false);
211// if (is_neg)
212// throw Exception ("negative jacobians in update");
213
214 update_displacement (input, *scaled_update, output);
215 }
216 }
217
218
219
220 // Compose linear1<->deform1<->[midway space]<->deform2<->linear2.
221 template <class DeformationField1Type, class DeformationField2Type, class OutputDeformationFieldType>
222 FORCE_INLINE void compute_full_deformation (const transform_type& linear1, DeformationField1Type& deform1,
223 DeformationField2Type& deform2, const transform_type& linear2,
224 OutputDeformationFieldType& deform_out)
225 {
226 MR::Transform deform_header_transform (deform_out);
227 ComposeHalfwayKernel<DeformationField1Type, DeformationField2Type> compose_kernel (linear1 * deform_header_transform.voxel2scanner, deform1, deform2, linear2);
228 ThreadedLoop (deform_out, 0, 3).run (compose_kernel, deform_out);
229 }
230
231 // Compose linear1<->deform1<->[midway space]<->deform2<->linear2.
232 template <class DeformationField1Type, class DeformationField2Type, class OutputDeformationFieldType>
233 FORCE_INLINE void compute_full_deformation (std::string message, const transform_type& linear1, DeformationField1Type& deform1,
234 DeformationField2Type& deform2, const transform_type& linear2,
235 OutputDeformationFieldType& deform_out)
236 {
237 MR::Transform deform_header_transform (deform_out);
238 ComposeHalfwayKernel<DeformationField1Type, DeformationField2Type> compose_kernel (linear1 * deform_header_transform.voxel2scanner, deform1, deform2, linear2);
239 ThreadedLoop (message, deform_out, 0, 3).run (compose_kernel, deform_out);
240 }
241
242 template <class WarpType>
243 FORCE_INLINE WarpType compute_midway_deformation (WarpType& warp, const int from) {
244 Header midway_header (warp);
245 midway_header.ndim() = 4;
246 midway_header.size(3) = 3;
247 WarpType deformation = WarpType::scratch (midway_header);
248
249 transform_type linear;
251 if (from == 1) {
253 index[0] = 0;
254 } else {
256 index[0] = 2;
257 }
259 Registration::Warp::compose_linear_deformation (linear, im_to_mid, deformation);
260 return deformation;
261 }
262
263 template <class WarpType, class TemplateType>
264 FORCE_INLINE WarpType compute_full_deformation (WarpType& warp, TemplateType& template_image, const int from) {
265 Header deform_header (template_image);
266 deform_header.ndim() = 4;
267 deform_header.size(3) = 3;
268 WarpType deform = WarpType::scratch (deform_header);
269
272
274 if (from == 1) {
275 index[0] = 0;
277 index[0] = 3;
279 Registration::Warp::compute_full_deformation (linear2.inverse(), mid_to_im2, im1_to_mid, linear1, deform);
280 } else {
281 index[0] = 1;
283 index[0] = 2;
285 Registration::Warp::compute_full_deformation (linear1.inverse(), mid_to_im1, im2_to_mid, linear2, deform);
286 }
287 return deform;
288 }
289
290 }
291 }
292}
293
294#endif
static Image scratch(const Header &template_header, const std::string &label="scratch image")
Definition: image.h:195
default_type spacing(size_t axis) const
Definition: image.h:67
Interp::Linear< Image< default_type > > disp2_interp
Definition: compose.h:92
Interp::Linear< DeformationField2Type > deform1_interp
Definition: compose.h:128
Interp::Linear< DeformationField2Type > deform2_interp
Definition: compose.h:129
const transform_type voxel2scanner
Definition: transform.h:43
constexpr I ceil(const T x)
template function with cast to different type
Definition: math.h:86
std::enable_if< std::is_fundamental< ValueType >::value &&sizeof(ValueType)==1, ValueType >::type swap(ValueType v)
Definition: raw.h:49
void warp(ImageTypeSource &source, ImageTypeDestination &destination, WarpType &warp, const typename ImageTypeDestination::value_type value_when_out_of_bounds=Interpolator< ImageTypeSource >::default_out_of_bounds_value(), const vector< uint32_t > oversample=Adapter::AutoOverSample, const bool jacobian_modulate=false)
convenience function to warp one image onto another
Definition: warp.h:65
FORCE_INLINE void update_displacement_scaling_and_squaring(Image< default_type > &input, Image< default_type > &update, Image< default_type > &output, const default_type step=1.0)
Definition: compose.h:159
FORCE_INLINE void compose_linear_displacement(const transform_type &transform, DisplacementFieldType &disp_in, DeformationFieldType &deform_out)
Definition: compose.h:137
FORCE_INLINE void update_displacement(Image< default_type > &input, Image< default_type > &update, Image< default_type > &output, default_type step=1.0)
Definition: compose.h:152
FORCE_INLINE WarpType compute_midway_deformation(WarpType &warp, const int from)
Definition: compose.h:243
transform_type parse_linear_transform(InputWarpType &input_warps, std::string name)
Definition: helpers.h:49
FORCE_INLINE void compose_linear_deformation(const transform_type &transform, InputDeformationFieldType &deform_in, OutputDeformationFieldType &deform_out)
Definition: compose.h:145
FORCE_INLINE void compute_full_deformation(const transform_type &linear1, DeformationField1Type &deform1, DeformationField2Type &deform2, const transform_type &linear2, OutputDeformationFieldType &deform_out)
Definition: compose.h:222
Definition: base.h:24
double default_type
the default type used throughout MRtrix
Definition: types.h:228
Eigen::Transform< default_type, 3, Eigen::AffineCompact > transform_type
the type for the affine transform of an image:
Definition: types.h:234
ThreadedLoopRunOuter< decltype(Loop(vector< size_t >()))> ThreadedLoop(const HeaderType &source, const vector< size_t > &outer_axes, const vector< size_t > &inner_axes)
Multi-threaded loop object.
constexpr default_type NaN
Definition: types.h:230
size_t index
#define FORCE_INLINE
Definition: types.h:156