Developer documentation
Version 3.0.3-105-gd3941f44
thread_kernel.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_metric_threadkernel_h__
18#define __registration_metric_threadkernel_h__
19
20#include "image.h"
21#include "algo/iterator.h"
22#include "transform.h"
24#include "algo/random_loop.h"
25
26namespace MR
27{
28 namespace Registration
29 {
30 namespace Metric
31 {
32
34 namespace {
35 template<class T>
36 struct Void { NOMEMALIGN
37 using type = void;
38 };
39
40 template <class MetricType, typename U = void>
41 struct is_neighbourhood_metric { NOMEMALIGN
42 using no = int;
43 };
44
45 template <class MetricType>
46 struct is_neighbourhood_metric<MetricType, typename Void<typename MetricType::is_neighbourhood>::type> { NOMEMALIGN
47 using yes = int;
48 };
49
50 template <class MetricType, typename U = void>
51 struct use_processed_image { NOMEMALIGN
52 using no = int;
53 };
54
55 template <class MetricType>
56 struct use_processed_image<MetricType, typename Void<typename MetricType::requires_precompute>::type> { NOMEMALIGN
57 using yes = int;
58 };
59
60 template <class MetricType, typename U = void>
61 struct cost_is_vector { NOMEMALIGN
62 using no = int;
63 };
64
65 template <class MetricType>
66 struct cost_is_vector<MetricType, typename Void<typename MetricType::is_vector_type>::type> { NOMEMALIGN
67 using yes = int;
68 };
69
70 template <class MetricType, typename U = void>
71 struct is_asymmetric { NOMEMALIGN
72 using no = int;
73 };
74
75 template <class MetricType>
76 struct is_asymmetric<MetricType, typename Void<typename MetricType::is_asymmetric_type>::type> { NOMEMALIGN
77 using yes = int;
78 };
79 }
81
82 template <class MetricType, class ParamType>
83 class ThreadKernel { MEMALIGN(ThreadKernel)
84 public:
86 const MetricType& metric,
87 const ParamType& parameters,
88 Eigen::VectorXd& overall_cost_function,
89 Eigen::VectorXd& overall_gradient,
90 ssize_t* overall_cnt = nullptr):
91 metric (metric),
92 params (parameters),
94 cnt (0),
100 if (params.robust_estimate_subset) {
101 // iterates over subset --> adjust voxel to scanner
102 assert (params.robust_estimate_subset_from.size() == 3);
103 transform_type i2s (params.midway_image.transform());
104 auto spacing = Eigen::DiagonalMatrix<default_type, 3> (params.midway_image.spacing(0), params.midway_image.spacing(1), params.midway_image.spacing(2));
105 for (size_t j = 0; j < 3; ++j)
106 for (size_t i = 0; i < 3; ++i)
107 i2s(i,3) += params.robust_estimate_subset_from[j] * params.midway_image.spacing(j) * i2s(i,j);
108 voxel2scanner = i2s * spacing;
109 }
110 gradient.setZero();
111 cost_function.setZero();
112 }
113
114 ~ThreadKernel () {
117 if (overall_cnt)
118 (*overall_cnt) += cnt;
119 }
120
121 template <class U = MetricType>
122 void operator() (const Iterator& iter,
123 typename is_neighbourhood_metric<U>::no = 0,
124 typename use_processed_image<U>::no = 0,
125 typename cost_is_vector<U>::no = 0,
126 typename is_asymmetric<U>::no = 0) {
127
128 Eigen::Vector3d voxel_pos ((default_type)iter.index(0), (default_type)iter.index(1), (default_type)iter.index(2));
129 Eigen::Vector3d midway_point = voxel2scanner * voxel_pos;
130
131
132
133 Eigen::Vector3d im2_point;
134 params.transformation.transform_half_inverse (im2_point, midway_point);
135 if (params.im2_mask_interp) {
136 params.im2_mask_interp->scanner (im2_point);
137 if (params.im2_mask_interp->value() < 0.5)
138 return;
139 }
140 if (params.robust_estimate_use_score && params.robust_estimate_score2_interp) {
141 params.robust_estimate_score2_interp->scanner (im2_point);
142 if (!(params.robust_estimate_score2_interp->value() >= 0.5))
143 return;
144 }
145
146 Eigen::Vector3d im1_point;
147 params.transformation.transform_half (im1_point, midway_point);
148 if (params.im1_mask_interp) {
149 params.im1_mask_interp->scanner (im1_point);
150 if (params.im1_mask_interp->value() < 0.5)
151 return;
152 }
153 if (params.robust_estimate_use_score && params.robust_estimate_score1_interp) {
154 params.robust_estimate_score1_interp->scanner (im1_point);
155 if (!(params.robust_estimate_score1_interp->value() >= 0.5))
156 return;
157 }
158
159 params.im1_image_interp->scanner (im1_point);
160 if (!(*params.im1_image_interp))
161 return;
162
163 params.im2_image_interp->scanner (im2_point);
164 if (!(*params.im2_image_interp))
165 return;
166
167 ++cnt;
168 cost_function(0) += metric (params, im1_point, im2_point, midway_point, gradient);
169 }
170
171
172 template <class U = MetricType>
173 void operator() (const Iterator& iter,
174 typename is_neighbourhood_metric<U>::no = 0,
175 typename use_processed_image<U>::no = 0,
176 typename cost_is_vector<U>::no = 0,
177 typename is_asymmetric<U>::yes = 0) {
178
179 Eigen::Vector3d voxel_pos ((default_type)iter.index(0), (default_type)iter.index(1), (default_type)iter.index(2));
180 Eigen::Vector3d im2_point = voxel2scanner * voxel_pos; // image 2 == midway_point == fixed image
181
182 // shift voxel position as evaluate iterates over a subset of the image
183 if (params.robust_estimate_subset) {
184 assert(params.robust_estimate_subset_from.size() == 3);
185 voxel_pos[0] += params.robust_estimate_subset_from[0];
186 voxel_pos[1] += params.robust_estimate_subset_from[1];
187 voxel_pos[2] += params.robust_estimate_subset_from[2];
188 }
189
190 if (!params.robust_estimate_subset && params.robust_estimate_score2_interp) {
191 params.robust_estimate_score2_interp->scanner (im2_point);
192 if (!(params.robust_estimate_score2_interp->value() >= 0.5))
193 return;
194 }
195
196 params.im2_image.index(0) = voxel_pos[0];
197 params.im2_image.index(1) = voxel_pos[1];
198 params.im2_image.index(2) = voxel_pos[2];
199
200 // assumes that im2_mask shares the coordinate system and voxel grid with image 2
201 // if (params.im2_mask.valid()) {
202 // params.im2_mask.index(0) = voxel_pos[0];
203 // params.im2_mask.index(1) = voxel_pos[1];
204 // params.im2_mask.index(2) = voxel_pos[2];
205 // if (params.im2_mask.value() < 0.5)
206 // return;
207 // }
208
209 if (params.im2_mask_interp) {
210 params.im2_mask_interp->scanner (im2_point);
211 if (params.im2_mask_interp->value() < 0.5)
212 return;
213 }
214
215 Eigen::Vector3d im1_point; // moving
216 params.transformation.transform_half (im1_point, im2_point); // transform_half is full transformation, transform_half_inverse is identity
217 if (params.im1_mask_interp) {
218 params.im1_mask_interp->scanner (im1_point);
219 if (params.im1_mask_interp->value() < 0.5)
220 return;
221 }
222 if (params.robust_estimate_use_score && params.robust_estimate_score1_interp) {
223 params.robust_estimate_score1_interp->scanner (im1_point);
224 if (!(params.robust_estimate_score1_interp->value() >= 0.5))
225 return;
226 }
227
228 params.im1_image_interp->scanner (im1_point);
229 if (!(*params.im1_image_interp))
230 return;
231
232 ++cnt;
233 cost_function(0) += metric (params, im1_point, im2_point, im2_point, gradient);
234 }
235
236 template <class U = MetricType>
237 void operator() (const Iterator& iter,
238 typename is_neighbourhood_metric<U>::no = 0,
239 typename use_processed_image<U>::no = 0,
240 typename cost_is_vector<U>::yes = 0,
241 typename is_asymmetric<U>::no = 0) {
242
243 Eigen::Vector3d voxel_pos ((default_type)iter.index(0), (default_type)iter.index(1), (default_type)iter.index(2));
244
245 Eigen::Vector3d midway_point = voxel2scanner * voxel_pos;
246
247 Eigen::Vector3d im2_point;
248 params.transformation.transform_half_inverse (im2_point, midway_point);
249 if (params.im2_mask_interp) {
250 params.im2_mask_interp->scanner (im2_point);
251 if (params.im2_mask_interp->value() < 0.5)
252 return;
253 }
254
255 Eigen::Vector3d im1_point;
256 params.transformation.transform_half (im1_point, midway_point);
257 if (params.im1_mask_interp) {
258 params.im1_mask_interp->scanner (im1_point);
259 if (params.im1_mask_interp->value() < 0.5)
260 return;
261 }
262
263 params.im1_image_interp->scanner (im1_point);
264 if (!(*params.im1_image_interp))
265 return;
266
267 params.im2_image_interp->scanner (im2_point);
268 if (!(*params.im2_image_interp))
269 return;
270
271 ++cnt;
272 cost_function.noalias() = cost_function + metric (params, im1_point, im2_point, midway_point, gradient);
273 }
274
275 template <class U = MetricType>
276 void operator() (const Iterator& iter,
277 typename is_neighbourhood_metric<U>::no = 0,
278 typename use_processed_image<U>::yes = 0,
279 typename cost_is_vector<U>::no = 0,
280 typename is_asymmetric<U>::no = 0) {
281 assert (params.processed_image.valid());
282 assign_pos_of (iter, 0, 3).to (params.processed_image);
283
284 if (params.processed_mask.valid()) {
285 assign_pos_of (iter, 0, 3).to (params.processed_mask);
286 if (!params.processed_mask.value())
287 return;
288 }
289 ++cnt;
290 cost_function(0) += metric (params, iter, gradient);
291 }
292
293 template <class U = MetricType>
294 void operator() (const Iterator& iter,
295 typename is_neighbourhood_metric<U>::yes = 0,
296 typename use_processed_image<U>::yes = 0,
297 typename cost_is_vector<U>::no = 0,
298 typename is_asymmetric<U>::no = 0) {
299 assert(params.processed_image.valid());
300
301 Eigen::Vector3d voxel_pos ((default_type)iter.index(0), (default_type)iter.index(1), (default_type)iter.index(2));
302
303 if (params.processed_mask.valid()){
304 assign_pos_of (iter, 0, 3).to (params.processed_mask);
305 assert(params.processed_mask.index(0) == iter.index(0));
306 assert(params.processed_mask.index(1) == iter.index(1));
307 assert(params.processed_mask.index(2) == iter.index(2));
308 if (!params.processed_mask.value())
309 return;
310 }
311
312 ++cnt;
313 cost_function(0) += metric (params, iter, gradient);
314 }
315
316 protected:
317 MetricType metric;
318 ParamType params;
319
320 Eigen::VectorXd cost_function;
321 ssize_t cnt;
322 Eigen::VectorXd gradient;
323 Eigen::VectorXd& overall_cost_function;
324 Eigen::VectorXd& overall_gradient;
325 ssize_t* overall_cnt;
327 // MR::Transform transform;
328 };
329
330 template <class MetricType, class ParamType>
332 public:
335 const default_type density,
336 const MetricType& metric,
337 const ParamType& parameters,
338 Eigen::VectorXd& overall_cost_function,
339 Eigen::VectorXd& overall_grad,
340 Math::RNG& rng_engine,
341 ssize_t* overlap_count = nullptr) :
344 metric (metric),
345 params (parameters),
347 gradient (overall_grad.size()),
349 overall_gradient (overall_grad),
350 rng (rng_engine),
352 gradient.setZero();
353 cost_function.setZero();
354 assert(inner_axes.size() >= 2);
355 assert(overall_cost_function.size() == 1);
356 }
357
361 }
362
363 void operator() (const Iterator& iter) {
364 auto engine = std::default_random_engine{static_cast<std::default_random_engine::result_type>(rng.get_seed())};
366 Iterator iterator (iter);
367 assign_pos_of(iter).to(iterator);
368 auto inner_loop = Random_loop<Iterator, std::default_random_engine>(iterator, engine, inner_axes[1], (float) iterator.size(inner_axes[1]) * density);
369 for (auto j = inner_loop; j; ++j)
370 for (auto k = Loop (inner_axes[0]) (iterator); k; ++k) {
371 kern (iterator);
372 }
373 }
374 protected:
377 MetricType metric;
378 ParamType params;
379 Eigen::VectorXd cost_function;
380 Eigen::VectorXd gradient;
381 Eigen::VectorXd& overall_cost_function;
382 Eigen::VectorXd& overall_gradient;
385 };
386 }
387 }
388}
389
390#endif
a dummy image to iterate over, useful for multi-threaded looping.
Definition: iterator.h:29
ssize_t size(size_t axis) const
Definition: iterator.h:41
const ssize_t & index(size_t axis) const
Definition: iterator.h:43
random number generator
Definition: rng.h:45
static std::mt19937::result_type get_seed()
Definition: rng.h:54
const transform_type voxel2scanner
Definition: transform.h:43
FORCE_INLINE LoopAlongAxes Loop()
Definition: loop.h:419
#define NOMEMALIGN
Definition: memory.h:22
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