Developer documentation
Version 3.0.3-105-gd3941f44
mapper.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 __dwi_tractography_mapping_mapper_h__
18#define __dwi_tractography_mapping_mapper_h__
19
20
21#include "image.h"
22#include "thread_queue.h"
23#include "transform.h"
24#include "types.h"
25
26#include "dwi/directions/set.h"
27
30
35
36#include "math/hermite.h"
37
38
39// Didn't bother making this a command-line option, since curvature contrast results were very poor regardless of smoothing
40#define CURVATURE_TRACK_SMOOTHING_FWHM 10.0 // In mm
41
42
43
44
45
46
47namespace MR {
48 namespace DWI {
49 namespace Tractography {
50 namespace Mapping {
51
52
53
54
56 { MEMALIGN(TrackMapperBase)
57
58 public:
59 template <class HeaderType>
60 TrackMapperBase (const HeaderType& template_image) :
61 info (template_image),
62 scanner2voxel (Transform(template_image).scanner2voxel.cast<float>()),
63 map_zero (false),
64 precise (false),
65 ends_only (false),
66 upsampler (1) { }
67
68 template <class HeaderType>
69 TrackMapperBase (const HeaderType& template_image, const DWI::Directions::FastLookupSet& dirs) :
70 info (template_image),
71 scanner2voxel (Transform(template_image).scanner2voxel.cast<float>()),
72 map_zero (false),
73 precise (false),
74 ends_only (false),
76 upsampler (1) { }
77
78 TrackMapperBase (const TrackMapperBase&) = default;
79 TrackMapperBase (TrackMapperBase&&) = default;
80
81
82 virtual ~TrackMapperBase() { }
83
84
85 void set_upsample_ratio (const size_t i) { upsampler.set_ratio (i); }
86 void set_map_zero (const bool i) { map_zero = i; }
87 void set_use_precise_mapping (const bool i) {
88 if (i && ends_only)
89 throw Exception ("Cannot do precise mapping and endpoint mapping together");
90 precise = i;
91 }
92 void set_map_ends_only (const bool i) {
93 if (i && precise)
94 throw Exception ("Cannot do precise mapping and endpoint mapping together");
95 ends_only = i;
96 }
97
98 void create_dixel_plugin (const DWI::Directions::FastLookupSet& dirs)
99 {
100 assert (!dixel_plugin && !tod_plugin);
101 dixel_plugin.reset (new DixelMappingPlugin (dirs));
102 }
103
104 void create_tod_plugin (const size_t N)
105 {
106 assert (!dixel_plugin && !tod_plugin);
107 tod_plugin.reset (new TODMappingPlugin (N));
108 }
109
110
111 template <class Cont>
112 bool operator() (const Streamline<>& in, Cont& out) const
113 {
114 out.clear();
115 out.index = in.get_index();
116 out.weight = in.weight;
117 if (in.empty())
118 return true;
119 if (preprocess (in, out) || map_zero) {
120 Streamline<> temp;
121 upsampler (in, temp);
122 if (precise)
123 voxelise_precise (temp, out);
124 else if (ends_only)
125 voxelise_ends (temp, out);
126 else
127 voxelise (temp, out);
128 postprocess (temp, out);
129 }
130 return true;
131 }
132
133
134
135 protected:
137 const Eigen::Transform<float,3,Eigen::AffineCompact> scanner2voxel;
141
142 std::shared_ptr<DixelMappingPlugin> dixel_plugin;
143 std::shared_ptr<TODMappingPlugin> tod_plugin;
144
145
146 // Specialist version of voxelise() is provided for the SetVoxel container:
147 // this is the simplest form of track mapping, and don't want to slow it down
148 // by unnecessarily computing tangents
149 // Second version does nothing fancy, but includes computation of the
150 // streamline tangent, and forces normalisation of the contribution from
151 // each streamline to each voxel it traverses
152 // Third version is the 'precise' mapping as described in the SIFT paper
153 // Fourth method only maps the streamline endpoints
154 void voxelise (const Streamline<>&, SetVoxel&) const;
155 template <class Cont> void voxelise (const Streamline<>&, Cont&) const;
156 template <class Cont> void voxelise_precise (const Streamline<>&, Cont&) const;
157 template <class Cont> void voxelise_ends (const Streamline<>&, Cont&) const;
158
159 virtual bool preprocess (const Streamline<>& tck, SetVoxelExtras& out) const { out.factor = 1.0; return true; }
160 virtual void postprocess (const Streamline<>& tck, SetVoxelExtras& out) const { }
161
162 // Used by voxelise() and voxelise_precise() to increment the relevant set
163 inline void add_to_set (SetVoxel& , const Eigen::Vector3i&, const Eigen::Vector3d&, const default_type) const;
164 inline void add_to_set (SetVoxelDEC&, const Eigen::Vector3i&, const Eigen::Vector3d&, const default_type) const;
165 inline void add_to_set (SetVoxelDir&, const Eigen::Vector3i&, const Eigen::Vector3d&, const default_type) const;
166 inline void add_to_set (SetDixel& , const Eigen::Vector3i&, const Eigen::Vector3d&, const default_type) const;
167 inline void add_to_set (SetVoxelTOD&, const Eigen::Vector3i&, const Eigen::Vector3d&, const default_type) const;
168
170
171 };
172
173
174
175 template <class Cont>
176 void TrackMapperBase::voxelise (const Streamline<>& tck, Cont& output) const
177 {
178
179 auto prev = tck.cbegin();
180 const auto last = tck.cend() - 1;
181
182 Eigen::Vector3i vox;
183 for (auto i = tck.cbegin(); i != last; ++i) {
184 vox = round (scanner2voxel * (*i));
185 if (check (vox, info)) {
186 const Eigen::Vector3d dir = (*(i+1) - *prev).cast<default_type>().normalized();
187 if (dir.allFinite() && !dir.isZero())
188 add_to_set (output, vox, dir, 1.0);
189 }
190 prev = i;
191 }
192
193 vox = round (scanner2voxel * (*last));
194 if (check (vox, info)) {
195 const Eigen::Vector3d dir = (*last - *prev).cast<default_type>().normalized();
196 if (dir.allFinite() && !dir.isZero())
197 add_to_set (output, vox, dir, 1.0);
198 }
199
200 for (auto& i : output)
201 i.normalize();
202
203 }
204
205
206
207
208
209
210 template <class Cont>
211 void TrackMapperBase::voxelise_precise (const Streamline<>& tck, Cont& out) const
212 {
213
215 using value_type = Streamline<>::value_type;
216
217 const default_type accuracy = Math::pow2 (0.005 * std::min (info.spacing (0), std::min (info.spacing (1), info.spacing (2))));
218
219 if (tck.size() < 2)
220 return;
221
222 Math::Hermite<value_type> hermite (0.1);
223
224 const point_type tck_proj_front = (tck[ 0 ] * 2.0) - tck[ 1 ];
225 const point_type tck_proj_back = (tck[tck.size()-1] * 2.0) - tck[tck.size()-2];
226
227 unsigned int p = 0;
228 point_type p_voxel_exit = tck.front();
229 default_type mu = 0.0;
230 bool end_track = false;
231 auto next_voxel = round (scanner2voxel * tck.front());
232
233 do {
234
235 const point_type p_voxel_entry (p_voxel_exit);
236 point_type p_prev (p_voxel_entry);
237 default_type length = 0.0;
238 const auto this_voxel = next_voxel;
239
240 while ((p != tck.size()) && ((next_voxel = round (scanner2voxel * tck[p])) == this_voxel)) {
241 length += (p_prev - tck[p]).norm();
242 p_prev = tck[p];
243 ++p;
244 mu = 0.0;
245 }
246
247 if (p == tck.size()) {
248 p_voxel_exit = tck.back();
249 end_track = true;
250 }
251 else {
252
253 default_type mu_min = mu;
254 default_type mu_max = 1.0;
255
256 const point_type* p_one = (p == 1) ? &tck_proj_front : &tck[p - 2];
257 const point_type* p_four = (p == tck.size() - 1) ? &tck_proj_back : &tck[p + 1];
258
259 point_type p_min = p_prev;
260 point_type p_max = tck[p];
261
262 while ((p_min - p_max).squaredNorm() > accuracy) {
263
264 mu = 0.5 * (mu_min + mu_max);
265 hermite.set (mu);
266 const point_type p_mu = hermite.value (*p_one, tck[p - 1], tck[p], *p_four);
267 const Eigen::Vector3i mu_voxel = round (scanner2voxel * p_mu);
268
269 if (mu_voxel == this_voxel) {
270 mu_min = mu;
271 p_min = p_mu;
272 }
273 else {
274 mu_max = mu;
275 p_max = p_mu;
276 next_voxel = mu_voxel;
277 }
278
279 }
280 p_voxel_exit = p_max;
281
282 }
283
284 length += (p_prev - p_voxel_exit).norm();
285 const Eigen::Vector3d traversal_vector = (p_voxel_exit - p_voxel_entry).cast<default_type>().normalized();
286 if (std::isfinite (traversal_vector[0]) && check (this_voxel, info))
287 add_to_set (out, this_voxel, traversal_vector, length);
288
289 } while (!end_track);
290
291 }
292
293
294
295 template <class Cont>
296 void TrackMapperBase::voxelise_ends (const Streamline<>& tck, Cont& out) const
297 {
298 if (!tck.size())
299 return;
300 if (tck.size() == 1) {
301 const auto vox = round (scanner2voxel * tck.front());
302 if (check (vox, info))
303 add_to_set (out, vox, Eigen::Vector3d(NaN, NaN, NaN), 1.0);
304 return;
305 }
306 for (size_t end = 0; end != 2; ++end) {
307 const auto vox = round (scanner2voxel * (end ? tck.back() : tck.front()));
308 if (check (vox, info)) {
309 Eigen::Vector3d dir { NaN, NaN, NaN };
310 if (tck.size() > 1)
311 dir = (end ? (tck[tck.size()-1] - tck[tck.size()-2]) : (tck[0] - tck[1])).cast<default_type>().normalized();
312 add_to_set (out, vox, dir, 1.0);
313 }
314 }
315 }
316
317
318
319
320 // These are inlined to make as fast as possible
321 inline void TrackMapperBase::add_to_set (SetVoxel& out, const Eigen::Vector3i& v, const Eigen::Vector3d& d, const default_type l) const
322 {
323 out.insert (v, l);
324 }
325 inline void TrackMapperBase::add_to_set (SetVoxelDEC& out, const Eigen::Vector3i& v, const Eigen::Vector3d& d, const default_type l) const
326 {
327 out.insert (v, d, l);
328 }
329 inline void TrackMapperBase::add_to_set (SetVoxelDir& out, const Eigen::Vector3i& v, const Eigen::Vector3d& d, const default_type l) const
330 {
331 out.insert (v, d, l);
332 }
333 inline void TrackMapperBase::add_to_set (SetDixel& out, const Eigen::Vector3i& v, const Eigen::Vector3d& d, const default_type l) const
334 {
335 assert (dixel_plugin);
336 const DWI::Directions::index_type bin = (*dixel_plugin) (d);
337 out.insert (v, bin, l);
338 }
339 inline void TrackMapperBase::add_to_set (SetVoxelTOD& out, const Eigen::Vector3i& v, const Eigen::Vector3d& d, const default_type l) const
340 {
341 assert (tod_plugin);
342 Eigen::Matrix<default_type, Eigen::Dynamic, 1> sh;
343 (*tod_plugin) (sh, d);
344 out.insert (v, sh, l);
345 }
346
347
348
349
350
351
352
353
354
355
356
357
359 { MEMALIGN(TrackMapperTWI)
360 public:
361 template <class HeaderType>
362 TrackMapperTWI (const HeaderType& template_image, const contrast_t c, const tck_stat_t s) :
363 TrackMapperBase (template_image),
364 contrast (c),
365 track_statistic (s) { }
366
367 TrackMapperTWI (const TrackMapperTWI& that) :
368 TrackMapperBase (static_cast<const TrackMapperBase&> (that)),
369 contrast (that.contrast),
372 {
373 if (that.image_plugin)
374 image_plugin.reset (that.image_plugin->clone());
375 }
376
377
378 void add_scalar_image (const std::string&);
379 void set_backtrack();
380 void add_fod_image (const std::string&);
381 void add_twdfc_static_image (Image<float>&);
382 void add_twdfc_dynamic_image (Image<float>&, const vector<float>&, const ssize_t);
383 void add_vector_data (const std::string&);
384
385
386 protected:
389
390 // Members for when the contribution of a track is not constant along its length
392 void load_factors (const Streamline<>&) const;
393
394 // Member for incorporating additional information from an external image into the TWI process
395 std::unique_ptr<TWIImagePluginBase> image_plugin;
396
397 // Member for incorporating contrast from an external vector data file into the TWI process
398 std::shared_ptr<Eigen::VectorXf> vector_data;
399
400
401 private:
402
403 virtual void set_factor (const Streamline<>&, SetVoxelExtras&) const;
404
405 // Overload virtual function
406 virtual bool preprocess (const Streamline<>& tck, SetVoxelExtras& out) const { set_factor (tck, out); return out.factor; }
407
408
409
410 };
411
412
413
414
415
416
417 }
418 }
419 }
420}
421
422#endif
423
424
425
void insert(const VoxelDEC &v)
Definition: voxel.h:312
void insert(const VoxelDir &v)
Definition: voxel.h:339
void insert(const VoxelTOD &v)
Definition: voxel.h:398
const Eigen::Transform< float, 3, Eigen::AffineCompact > scanner2voxel
Definition: mapper.h:137
void voxelise_precise(const Streamline<> &, Cont &) const
Definition: mapper.h:211
DWI::Tractography::Resampling::Upsampler upsampler
Definition: mapper.h:169
std::shared_ptr< TODMappingPlugin > tod_plugin
Definition: mapper.h:143
void add_to_set(SetVoxel &, const Eigen::Vector3i &, const Eigen::Vector3d &, const default_type) const
Definition: mapper.h:321
std::shared_ptr< DixelMappingPlugin > dixel_plugin
Definition: mapper.h:142
virtual void postprocess(const Streamline<> &tck, SetVoxelExtras &out) const
Definition: mapper.h:160
virtual bool preprocess(const Streamline<> &tck, SetVoxelExtras &out) const
Definition: mapper.h:159
void voxelise_ends(const Streamline<> &, Cont &) const
Definition: mapper.h:296
void voxelise(const Streamline<> &, SetVoxel &) const
std::unique_ptr< TWIImagePluginBase > image_plugin
Definition: mapper.h:395
void load_factors(const Streamline<> &) const
std::shared_ptr< Eigen::VectorXf > vector_data
Definition: mapper.h:398
void set(value_type position)
Definition: hermite.h:36
S value(const S *vals) const
Definition: hermite.h:50
constexpr T pow2(const T &v)
Definition: math.h:53
unsigned int index_type
Definition: set.h:34
Eigen::Vector3i round(const Eigen::Matrix< T, 3, 1 > &p)
Definition: voxel.h:38
bool check(const Eigen::Vector3i &V, const HeaderType &H)
Definition: voxel.h:45
typename Streamline<>::point_type point_type
Definition: resampling.h:40
PointType::Scalar length(const vector< PointType > &tck)
Definition: streamline.h:134
MR::default_type value_type
Definition: typedefs.h:33
Definition: base.h:24
double default_type
the default type used throughout MRtrix
Definition: types.h:228
constexpr default_type NaN
Definition: types.h:230