Developer documentation
Version 3.0.3-105-gd3941f44
model.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_sift_model_h__
18#define __dwi_tractography_sift_model_h__
19
20
21#include "app.h"
22#include "thread_queue.h"
23#include "types.h"
24
25#include "dwi/fixel_map.h"
26
27#include "dwi/directions/set.h"
28
32
37
42
43
44
45
46
47namespace MR
48{
49 namespace DWI
50 {
51 namespace Tractography
52 {
53 namespace SIFT
54 {
55
56
57 template <class Fixel>
58 class Model : public ModelBase<Fixel>
60
61 protected:
62 using MapVoxel = typename Fixel_map<Fixel>::MapVoxel;
64
65 public:
66 template <class Set>
69 {
70 Track_fixel_contribution::set_scaling (dwi);
71 }
72 Model (const Model& that) = delete;
73
74 virtual ~Model ();
75
76
77 // Over-rides the function defined in ModelBase; need to build contributions member also
78 void map_streamlines (const std::string&);
79
81
82 // For debugging purposes - make sure the sum of TD in the fixels is equal to the sum of TD in the streamlines
83 void check_TD();
84
85 track_t num_tracks() const { return contributions.size(); }
86
87 void output_non_contributing_streamlines (const std::string&) const;
88
89
91
92
93 protected:
94 std::string tck_file_path;
96
99
104
105
106 private:
107 // Some member classes to support multi-threaded processes
108 class TrackMappingWorker
109 { MEMALIGN(TrackMappingWorker)
110 public:
111 TrackMappingWorker (Model& i, const default_type upsample_ratio) :
112 master (i),
113 mapper (i.header(), i.dirs),
114 mutex (new std::mutex),
115 TD_sum (0.0),
116 fixel_TDs (master.fixels.size(), 0.0),
117 fixel_counts (master.fixels.size(), 0)
118 {
119 mapper.set_upsample_ratio (upsample_ratio);
120 mapper.set_use_precise_mapping (true);
121 }
122 TrackMappingWorker (const TrackMappingWorker& that) :
123 master (that.master),
124 mapper (that.mapper),
125 mutex (that.mutex),
126 TD_sum (0.0),
127 fixel_TDs (master.fixels.size(), 0.0),
128 fixel_counts (master.fixels.size(), 0) { }
129 ~TrackMappingWorker();
131 private:
132 Model& master;
134 std::shared_ptr<std::mutex> mutex;
135 double TD_sum;
136 vector<double> fixel_TDs;
137 vector<track_t> fixel_counts;
138 };
139
140 class FixelRemapper
141 { MEMALIGN(FixelRemapper)
142 public:
143 FixelRemapper (Model& i, vector<size_t>& r) :
144 master (i),
145 remapper (r) { }
146 bool operator() (const TrackIndexRange&);
147 private:
148 Model& master;
149 vector<size_t>& remapper;
150 };
151
152 };
153
154
155
156
157
158 template <class Fixel>
160 {
161 for (vector<TrackContribution*>::iterator i = contributions.begin(); i != contributions.end(); ++i) {
162 if (*i) {
163 delete *i;
164 *i = nullptr;
165 }
166 }
167 }
168
169
170
171
172 template <class Fixel>
173 void Model<Fixel>::map_streamlines (const std::string& path)
174 {
175 Tractography::Properties properties;
176 Tractography::Reader<> file (path, properties);
177
178 const track_t count = (properties.find ("count") == properties.end()) ? 0 : to<track_t>(properties["count"]);
179 if (!count)
180 throw Exception ("Cannot map streamlines: track file " + Path::basename(path) + " is empty");
181
182 contributions.assign (count, nullptr);
183
184 {
185 Mapping::TrackLoader loader (file, count);
186 TrackMappingWorker worker (*this, Mapping::determine_upsample_ratio (Fixel_map<Fixel>::header(), properties, 0.1));
187 Thread::run_queue (loader,
189 Thread::multi (worker));
190 }
191
192 if (!contributions.back()) {
193 track_t num_tracks = 0, max_index = 0;
194 for (track_t i = 0; i != contributions.size(); ++i) {
195 if (contributions[i]) {
196 ++num_tracks;
197 max_index = std::max (max_index, i);
198 }
199 WARN ("Only " + str (num_tracks) + " tracks read from input track file; expected " + str (contributions.size()));
200 contributions.resize (max_index + 1);
201 }
202 }
203
204 tck_file_path = path;
205
206 INFO ("Proportionality coefficient after streamline mapping is " + str (mu()));
207 }
208
209
210
211
212
213 template <class Fixel>
215 {
216
217 const bool remove_untracked_fixels = App::get_options ("remove_untracked").size();
218 auto opt = App::get_options ("fd_thresh");
219 const float min_fibre_density = opt.size() ? float(opt[0][0]) : 0.0;
220
221 if (!remove_untracked_fixels && !min_fibre_density)
222 return;
223
224 vector<size_t> fixel_index_mapping (fixels.size(), 0);
225 VoxelAccessor v (accessor());
226
227 vector<Fixel> new_fixels;
228 new_fixels.push_back (Fixel());
229 FOD_sum = 0.0;
230
231 for (auto l = Loop (v) (v); l; ++l) {
232 if (v.value()) {
233
234 size_t new_start_index = new_fixels.size();
235
236 for (typename Fixel_map<Fixel>::Iterator i = begin(v); i; ++i) {
237 if ((!remove_untracked_fixels || i().get_TD()) && (i().get_FOD() > min_fibre_density)) {
238 fixel_index_mapping [size_t (i)] = new_fixels.size();
239 new_fixels.push_back (i());
240 FOD_sum += i().get_weight() * i().get_FOD();
241 }
242 }
243
244 delete v.value();
245
246 if (new_fixels.size() == new_start_index)
247 v.value() = nullptr;
248 else
249 v.value() = new MapVoxel (new_start_index, new_fixels.size() - new_start_index);
250
251 }
252 }
253
254 INFO (str (fixels.size() - new_fixels.size()) + " out of " + str(fixels.size()) + " fixels removed from reconstruction (" + str(new_fixels.size()) + ") remaining)");
255
256 fixels.swap (new_fixels);
257
258 TrackIndexRangeWriter writer (SIFT_TRACK_INDEX_BUFFER_SIZE, num_tracks(), "Removing excluded fixels");
259 FixelRemapper remapper (*this, fixel_index_mapping);
260 Thread::run_queue (writer, TrackIndexRange(), Thread::multi (remapper));
261
262 TD_sum = 0.0;
263 for (typename vector<Fixel>::const_iterator i = fixels.begin(); i != fixels.end(); ++i)
264 TD_sum += i->get_weight() * i->get_TD();
265
266 INFO ("After fixel exclusion, the proportionality coefficient is " + str(mu()));
267
268 }
269
270
271
272
273
274
275
276 template <class Fixel>
278 {
279 VAR (TD_sum);
280 double sum_from_fixels = 0.0, sum_from_fixels_weighted = 0.0;
281 for (typename vector<Fixel>::const_iterator i = fixels.begin(); i != fixels.end(); ++i) {
282 sum_from_fixels += i->get_TD();
283 sum_from_fixels_weighted += i->get_TD() * i->get_weight();
284 }
285 VAR (sum_from_fixels);
286 VAR (sum_from_fixels_weighted);
287 double sum_from_tracks = 0.0;
288 for (vector<TrackContribution*>::const_iterator i = contributions.begin(); i != contributions.end(); ++i) {
289 if (*i)
290 sum_from_tracks += (*i)->get_total_contribution();
291 }
292 VAR (sum_from_tracks);
293 }
294
295
296
297
298
299 template <class Fixel>
300 void Model<Fixel>::output_non_contributing_streamlines (const std::string& output_path) const
301 {
303 Tractography::Reader<float> reader (tck_file_path, p);
304 Tractography::Writer<float> writer (output_path, p);
306 ProgressBar progress ("Writing non-contributing streamlines output file", contributions.size());
307 track_t tck_counter = 0;
308 while (reader (tck) && tck_counter < contributions.size()) {
309 if (contributions[tck_counter] && !contributions[tck_counter++]->get_total_contribution())
310 writer (tck);
311 else
312 writer.skip();
313 ++progress;
314 }
315 reader.close();
316 }
317
318
319
320
321
322 namespace {
323 // Split multi-threaded increment here based on whether or not the Fixel
324 // template class does or does not possess member add_TD (const double, const track_t)
325 template <typename... Ts>
326 using void_t = void;
327
328 template <typename T, typename = void>
329 struct has_add_TD_function : std::false_type { NOMEMALIGN };
330 template <typename T>
331 struct has_add_TD_function<T, decltype (std::declval<T>().add_TD(0.0, 0))> : std::true_type { NOMEMALIGN };
332
333 template <typename FixelType>
334 typename std::enable_if<has_add_TD_function<FixelType>::value, void>::type increment (FixelType& fixel, const double length, const track_t count) {
335 fixel.add_TD (length, count);
336 }
337 template <typename FixelType>
338 typename std::enable_if<!has_add_TD_function<FixelType>::value, void>::type increment (FixelType& fixel, const double length, const track_t count) {
339 fixel += length;
340 }
341 }
342
343 template <class Fixel>
344 Model<Fixel>::TrackMappingWorker::~TrackMappingWorker()
345 {
346 std::lock_guard<std::mutex> lock (*mutex);
347 master.TD_sum += TD_sum;
348 for (size_t i = 0; i != fixel_TDs.size(); ++i)
349 increment (master.fixels[i], fixel_TDs[i], fixel_counts[i]);
350 }
351
352
353
354 template <class Fixel>
355 bool Model<Fixel>::TrackMappingWorker::operator() (const Tractography::Streamline<>& in)
356 {
357 assert (in.get_index() < master.contributions.size());
358 assert (!master.contributions[in.get_index()]);
359
360 try {
361
362 Mapping::SetDixel dixels;
363 mapper (in, dixels);
364
365 vector<Track_fixel_contribution> masked_contributions;
366 default_type total_contribution = 0.0, total_length = 0.0;
367
368 for (Mapping::SetDixel::const_iterator i = dixels.begin(); i != dixels.end(); ++i) {
369 total_length += i->get_length();
370 const size_t fixel_index = master.dixel2fixel (*i);
371 if (fixel_index && (i->get_length() > Track_fixel_contribution::min())) {
372 total_contribution += i->get_length() * master.fixels[fixel_index].get_weight();
373 bool incremented = false;
374 for (vector<Track_fixel_contribution>::iterator c = masked_contributions.begin(); !incremented && c != masked_contributions.end(); ++c) {
375 if ((c->get_fixel_index() == fixel_index) && c->add (i->get_length()))
376 incremented = true;
377 }
378 if (!incremented)
379 masked_contributions.push_back (Track_fixel_contribution (fixel_index, i->get_length()));
380 }
381 }
382
383 master.contributions[in.get_index()] = new TrackContribution (masked_contributions, total_contribution, total_length);
384
385 TD_sum += total_contribution;
386 for (vector<Track_fixel_contribution>::const_iterator i = masked_contributions.begin(); i != masked_contributions.end(); ++i) {
387 fixel_TDs [i->get_fixel_index()] += i->get_length();
388 ++fixel_counts [i->get_fixel_index()];
389 }
390
391 return true;
392
393 } catch (...) {
394 throw Exception ("Error allocating memory for streamline visitations");
395 return false;
396 }
397 }
398
399
400
401
402
403 template <class Fixel>
404 bool Model<Fixel>::FixelRemapper::operator() (const TrackIndexRange& in)
405 {
406 for (track_t track_index = in.first; track_index != in.second; ++track_index) {
407 if (master.contributions[track_index]) {
408 TrackContribution& this_cont (*master.contributions[track_index]);
409 vector<Track_fixel_contribution> new_cont;
410 double total_contribution = 0.0;
411 for (size_t i = 0; i != this_cont.dim(); ++i) {
412 const size_t new_index = remapper[this_cont[i].get_fixel_index()];
413 if (new_index) {
414 new_cont.push_back (Track_fixel_contribution (new_index, this_cont[i].get_length()));
415 total_contribution += this_cont[i].get_length() * master[new_index].get_weight();
416 }
417 }
418 TrackContribution* new_contribution = new TrackContribution (new_cont, total_contribution, this_cont.get_total_length());
419 delete master.contributions[track_index];
420 master.contributions[track_index] = new_contribution;
421 }
422 }
423 return true;
424 }
425
426
427
428
429
430
431 }
432 }
433 }
434}
435
436
437#endif
const DWI::Directions::FastLookupSet & dirs
Definition: fixel_td_map.h:59
A class to read streamlines data.
Definition: file.h:63
virtual bool operator()(const FMLS::FOD_lobes &in)
Definition: model_base.h:261
Model(Set &dwi, const DWI::Directions::FastLookupSet &dirs)
Definition: model.h:67
void map_streamlines(const std::string &)
Definition: model.h:173
void output_non_contributing_streamlines(const std::string &) const
Definition: model.h:300
Model(const Model &that)=delete
vector< TrackContribution * > contributions
Definition: model.h:95
track_t num_tracks() const
Definition: model.h:85
class to handle writing tracks to file, with RAM buffer
Definition: file.h:353
implements a progress meter to provide feedback to the user
Definition: progressbar.h:58
#define WARN(msg)
Definition: exception.h:73
#define INFO(msg)
Definition: exception.h:74
#define VAR(variable)
Prints a variable name and its value, followed by the function, file and line number.
Definition: debug.h:41
FORCE_INLINE LoopAlongAxes Loop()
Definition: loop.h:419
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
Definition: SH.h:233
__Multi< typename std::remove_reference< Functor >::type > multi(Functor &&functor, size_t nthreads=threads_to_execute())
used to request multiple threads of the corresponding functor
Definition: thread.h:285
void run_queue(Source &&source, const Item &item, Sink &&sink, size_t capacity=128)
convenience function to set up and run a 2-stage multi-threaded pipeline.
__Batch< Item > batch(const Item &, size_t number=128)
used to request batched processing of items
Definition: thread_queue.h:858
#define NOMEMALIGN
Definition: memory.h:22
const vector< ParsedOption > get_options(const std::string &name)
return all command-line options matching name
size_t determine_upsample_ratio(const Header &, const float, const float)
unsigned int track_t
Definition: types.h:34
std::pair< track_t, track_t > TrackIndexRange
PointType::Scalar length(const vector< PointType > &tck)
Definition: streamline.h:134
std::string basename(const std::string &name)
Definition: path.h:60
Definition: base.h:24
double default_type
the default type used throughout MRtrix
Definition: types.h:228
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
Definition: types.h:303
InputImageType dwi
#define MEMALIGN(...)
Definition: types.h:185
#define SIFT_TRACK_INDEX_BUFFER_SIZE