Developer documentation
Version 3.0.3-105-gd3941f44
file.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_file_h__
18#define __dwi_tractography_file_h__
19
20#include <map>
21
22#include "app.h"
23#include "types.h"
24#include "memory.h"
25#include "file/config.h"
26#include "file/key_value.h"
27#include "file/ofstream.h"
31
32
33namespace MR
34{
35 namespace DWI
36 {
37 namespace Tractography
38 {
39
40
41 template <class ValueType>
44 public:
46 virtual ~ReaderInterface() { }
47 };
48
49
50 template <class ValueType>
53 public:
54 virtual bool operator() (const Streamline<ValueType>&) = 0;
55 virtual ~WriterInterface() { }
56 };
57
58
59
61 template <class ValueType = float>
62 class Reader : public __ReaderBase__, public ReaderInterface<ValueType>
64 public:
65
67 Reader (const std::string& file, Properties& properties)
68 {
69 open (file, "tracks", properties);
70 auto opt = App::get_options ("tck_weights_in");
71 if (opt.size())
72 weights = load_vector<ValueType> (opt[0][0]);
73 }
74
75
78 tck.clear();
79
80 if (!in.is_open())
81 return false;
82
83 do {
84 auto p = get_next_point();
85 if (std::isinf (p[0])) {
86 in.close();
88 return false;
89 }
90 if (in.eof()) {
91 in.close();
93 return false;
94 }
95
96 if (std::isnan (p[0])) {
97 tck.set_index (current_index++);
98
99 if (weights.size()) {
100
101 if (tck.get_index() < size_t(weights.size())) {
102 tck.weight = weights[tck.get_index()];
103 } else {
104 WARN ("Streamline weights file contains less entries (" + str(weights.size()) + ") than .tck file; "
105 "ceasing reading of streamline data");
106 in.close();
107 tck.clear();
108 return false;
109 }
110
111 } else {
112 tck.weight = 1.0;
113 }
114
115 return true;
116 }
117
118 tck.push_back (p);
119 } while (in.good());
120
121 in.close();
122 return false;
123 }
124
125
126
127 protected:
128 using __ReaderBase__::in;
129 using __ReaderBase__::dtype;
130 using __ReaderBase__::current_index;
131
132 Eigen::Matrix<ValueType, Eigen::Dynamic, 1> weights;
133
135
136 Eigen::Matrix<ValueType,3,1> get_next_point ()
137 {
138 using namespace ByteOrder;
139 switch (dtype()) {
141 {
142 float p[3];
143 in.read ((char*) p, sizeof (p));
144 return { ValueType(LE(p[0])), ValueType(LE(p[1])), ValueType(LE(p[2])) };
145 }
147 {
148 float p[3];
149 in.read ((char*) p, sizeof (p));
150 return { ValueType(BE(p[0])), ValueType(BE(p[1])), ValueType(BE(p[2])) };
151 }
153 {
154 double p[3];
155 in.read ((char*) p, sizeof (p));
156 return { ValueType(LE(p[0])), ValueType(LE(p[1])), ValueType(LE(p[2])) };
157 }
159 {
160 double p[3];
161 in.read ((char*) p, sizeof (p));
162 return { ValueType(BE(p[0])), ValueType(BE(p[1])), ValueType(BE(p[2])) };
163 }
164 default:
165 assert (0);
166 break;
167 }
168 return { NaN, NaN, NaN };
169 }
170
173 {
174 if (!weights.size())
175 return;
176 if (size_t(weights.size()) > current_index) {
177 WARN ("Streamline weights file contains more entries (" + str(weights.size()) + ") than .tck file (" + str(current_index) + ")");
178 }
179 }
180
181 Reader (const Reader&) = delete;
182
183 };
184
185
186
187
188
189
190
192
203 template <class ValueType = float>
204 class WriterUnbuffered : public __WriterBase__<ValueType>, public WriterInterface<ValueType>
205 { NOMEMALIGN
206 public:
207 using __WriterBase__<ValueType>::count;
208 using __WriterBase__<ValueType>::total_count;
210 using __WriterBase__<ValueType>::dtype;
212 using __WriterBase__<ValueType>::verify_stream;
213 using __WriterBase__<ValueType>::update_counts;
214 using __WriterBase__<ValueType>::open_success;
215
216 using vector_type = Eigen::Matrix<ValueType,3,1>;
217
219 WriterUnbuffered (const std::string& file, const Properties& properties) :
220 __WriterBase__<ValueType> (file) {
221
222 if (!Path::has_suffix (name, ".tck"))
223 throw Exception ("output track files must use the .tck suffix");
224
225 File::OFStream out;
226 try {
227 out.open (name, std::ios::out | std::ios::binary | std::ios::trunc);
228 } catch (Exception& e) {
229 throw Exception (e, "Unable to create output track file");
230 }
231
232 const_cast<Properties&> (properties).set_timestamp();
233 const_cast<Properties&> (properties).set_version_info();
234 const_cast<Properties&> (properties).update_command_history();
235
236 create (out, properties, "tracks");
237 barrier_addr = out.tellp();
238
239 vector_type x;
240 format_point (barrier(), x);
241 out.write (reinterpret_cast<char*> (&x[0]), sizeof (x));
242 if (!out.good())
243 throw Exception ("error writing tracks file \"" + name + "\": " + strerror (errno));
244 open_success = true;
245
246 auto opt = App::get_options ("tck_weights_out");
247 if (opt.size())
248 set_weights_path (opt[0][0]);
249 }
250
253 // allocate buffer on the stack for performance:
254 NON_POD_VLA (buffer, vector_type, tck.size()+2);
255 for (size_t n = 0; n < tck.size(); ++n) {
256 assert (tck[n].allFinite());
257 format_point (tck[n], buffer[n]);
258 }
259 format_point (delimiter(), buffer[tck.size()]);
260
261 commit (buffer, tck.size()+1);
262
263 if (weights_name.size())
264 write_weights (str(tck.weight) + "\n");
265
266 ++count;
267 ++total_count;
268 return true;
269 }
270
271
273 void set_weights_path (const std::string& path) {
274 if (weights_name.size())
275 throw Exception ("Cannot change output streamline weights file path");
276 weights_name = path;
278 File::OFStream out (weights_name, std::ios::out | std::ios::binary | std::ios::trunc);
279 }
280
281 protected:
282 std::string weights_name;
284
286 vector_type delimiter () const { return { ValueType(NaN), ValueType(NaN), ValueType(NaN) }; }
288 vector_type barrier () const { return { ValueType(Inf), ValueType(Inf), ValueType(Inf) }; }
289
291 void format_point (const vector_type& src, vector_type& dest) {
292 using namespace ByteOrder;
293 if (dtype.is_little_endian())
294 dest = { LE(src[0]), LE(src[1]), LE(src[2]) };
295 else
296 dest = { BE(src[0]), BE(src[1]), BE(src[2]) };
297 }
298
300 void write_weights (const std::string& contents) {
301 File::OFStream out (weights_name, std::ios::in | std::ios::out | std::ios::binary | std::ios::ate);
302 out << contents;
303 if (!out.good())
304 throw Exception ("error writing streamline weights file \"" + weights_name + "\": " + strerror (errno));
305 }
306
307
309
311 void commit (vector_type* data, size_t num_points) {
312 if (num_points == 0 || !open_success)
313 return;
314
315 int64_t prev_barrier_addr = barrier_addr;
316
317 format_point (barrier(), data[num_points]);
318 File::OFStream out (name, std::ios::in | std::ios::out | std::ios::binary | std::ios::ate);
319 out.write (reinterpret_cast<const char* const> (data+1), sizeof (vector_type) * num_points);
320 verify_stream (out);
321 barrier_addr = int64_t (out.tellp()) - sizeof(vector_type);
322 out.seekp (prev_barrier_addr, out.beg);
323 out.write (reinterpret_cast<const char* const> (data), sizeof(vector_type));
324 verify_stream (out);
325 update_counts (out);
326 }
327
328
331 };
332
333
334
335
336
338
351 template <typename ValueType = float>
352 class Writer : public WriterUnbuffered<ValueType>
353 { NOMEMALIGN
354 public:
355 using __WriterBase__<ValueType>::count;
356 using __WriterBase__<ValueType>::total_count;
362
364
368 //CONF option: TrackWriterBufferSize
369 //CONF default: 16777216
370 //CONF The size of the write-back buffer (in bytes) to use when
371 //CONF writing track files. MRtrix will store the output tracks in a
372 //CONF relatively large buffer to limit the number of write() calls,
373 //CONF avoid associated issues such as file fragmentation.
374 Writer (const std::string& file, const Properties& properties, size_t default_buffer_capacity = 16777216) :
375 WriterUnbuffered<ValueType> (file, properties),
376 buffer_capacity (File::Config::get_int ("TrackWriterBufferSize", default_buffer_capacity) / sizeof (vector_type)),
378 buffer_size (0) { }
379
380 Writer (const Writer& W) = delete;
381
384 commit();
385 }
386
389 if (buffer_size + tck.size() + 2 > buffer_capacity)
390 commit ();
391
392 for (const auto& i : tck) {
393 assert (i.allFinite());
394 add_point (i);
395 }
397
398 if (weights_name.size())
399 weights_buffer += str (tck.weight) + ' ';
400
401 ++count;
402 ++total_count;
403 return true;
404 }
405
406
407 protected:
408 const size_t buffer_capacity;
409 std::unique_ptr<vector_type[]> buffer;
411 std::string weights_buffer;
412
414 void add_point (const vector_type& p) {
416 }
417
418 void commit () {
420 buffer_size = 0;
421
422 if (weights_name.size()) {
424 weights_buffer.clear();
425 }
426 }
427
428 };
429
430
431
432 }
433 }
434}
435
436
437#endif
438
void set_index(const size_t i)
Definition: streamline.h:47
A class to read streamlines data.
Definition: file.h:63
bool operator()(Streamline< ValueType > &tck)
fetch next track from file
Definition: file.h:77
Eigen::Matrix< ValueType, 3, 1 > get_next_point()
takes care of byte ordering issues
Definition: file.h:136
void check_excess_weights()
Check that the weights file does not contain excess entries.
Definition: file.h:172
Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > weights
Definition: file.h:132
Reader(const std::string &file, Properties &properties)
open the file for reading and load header into properties
Definition: file.h:67
Reader(const Reader &)=delete
virtual bool operator()(Streamline< ValueType > &)=0
class to handle writing tracks to file, with RAM buffer
Definition: file.h:353
Writer(const Writer &W)=delete
~Writer()
commits any remaining data to file
Definition: file.h:383
typename WriterUnbuffered< ValueType >::vector_type vector_type
Definition: file.h:361
Writer(const std::string &file, const Properties &properties, size_t default_buffer_capacity=16777216)
create new RAM-buffered track file with specified properties
Definition: file.h:374
const size_t buffer_capacity
Definition: file.h:408
std::string weights_buffer
Definition: file.h:411
bool operator()(const Streamline< ValueType > &tck)
append track to file
Definition: file.h:388
std::unique_ptr< vector_type[]> buffer
Definition: file.h:409
void add_point(const vector_type &p)
add point to buffer and increment buffer_size accordingly
Definition: file.h:414
virtual bool operator()(const Streamline< ValueType > &)=0
class to handle unbuffered writing of tracks to file
Definition: file.h:205
WriterUnbuffered(const std::string &file, const Properties &properties)
create a new track file with the specified properties
Definition: file.h:219
void format_point(const vector_type &src, vector_type &dest)
perform per-point byte-swapping if required
Definition: file.h:291
void set_weights_path(const std::string &path)
set the path to the track weights
Definition: file.h:273
void commit(vector_type *data, size_t num_points)
write track point data to file
Definition: file.h:311
WriterUnbuffered(const WriterUnbuffered &)=delete
copy construction explicitly disabled
vector_type delimiter() const
indicates end of track and start of new track
Definition: file.h:286
Eigen::Matrix< ValueType, 3, 1 > vector_type
Definition: file.h:216
vector_type barrier() const
indicates end of data
Definition: file.h:288
void write_weights(const std::string &contents)
write track weights data to file
Definition: file.h:300
bool operator()(const Streamline< ValueType > &tck)
append track to file
Definition: file.h:252
static constexpr uint8_t Float64LE
Definition: datatype.h:169
static constexpr uint8_t Float32BE
Definition: datatype.h:168
static constexpr uint8_t Float32LE
Definition: datatype.h:167
static constexpr uint8_t Float64BE
Definition: datatype.h:170
open output files for writing, checking for pre-existing file if necessary
Definition: ofstream.h:39
void open(const std::string &path, const std::ios_base::openmode mode=std::ios_base::out|std::ios_base::binary)
#define WARN(msg)
Definition: exception.h:73
#define NON_POD_VLA(name, type, num)
Definition: types.h:147
constexpr double e
Definition: math.h:39
#define NOMEMALIGN
Definition: memory.h:22
const vector< ParsedOption > get_options(const std::string &name)
return all command-line options matching name
void check_overwrite(const std::string &name)
Definition: app.h:168
ValueType BE(ValueType v)
Definition: raw.h:94
ValueType LE(ValueType v)
Definition: raw.h:91
std::unique_ptr< ImageIO::Base > create(Header &H)
bool has_suffix(const std::string &name, const std::string &suffix)
Definition: path.h:130
Definition: base.h:24
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
constexpr default_type Inf
Definition: types.h:231
constexpr default_type NaN
Definition: types.h:230
const std::string name
Definition: thread.h:108