Developer documentation
Version 3.0.3-105-gd3941f44
image.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 __image_h__
18#define __image_h__
19
20#include <functional>
21#include <type_traits>
22#include <tuple>
23
24#include "debug.h"
25#include "header.h"
27#include "image_helpers.h"
29#include "algo/copy.h"
30#include "algo/threaded_copy.h"
31
32namespace MR
33{
34
35 constexpr int SpatiallyContiguous = -1;
36
37
38 template <typename ValueType>
39 class Image :
40 public ImageBase<Image<ValueType>, ValueType>
42 public:
43 using value_type = ValueType;
44 class Buffer;
45
47 FORCE_INLINE Image (const Image&) = default;
48 FORCE_INLINE Image (Image&&) = default;
49 FORCE_INLINE Image& operator= (const Image& image) = default;
52
54 Image (const std::shared_ptr<Buffer>&, const Stride::List& = Stride::List());
55
56 FORCE_INLINE bool valid () const { return bool(buffer); }
57 FORCE_INLINE bool operator! () const { return !valid(); }
58
60 FORCE_INLINE const KeyValues& keyval () const { return buffer->keyval(); }
61
62 FORCE_INLINE const std::string& name() const { return buffer->name(); }
63 FORCE_INLINE const transform_type& transform() const { return buffer->transform(); }
64
65 FORCE_INLINE size_t ndim () const { return buffer->ndim(); }
66 FORCE_INLINE ssize_t size (size_t axis) const { return buffer->size (axis); }
67 FORCE_INLINE default_type spacing (size_t axis) const { return buffer->spacing (axis); }
68 FORCE_INLINE ssize_t stride (size_t axis) const { return strides[axis]; }
69
71 FORCE_INLINE size_t offset () const { return data_offset; }
72
75 for (size_t n = 0; n < ndim(); ++n)
76 this->index(n) = 0;
77 }
78
80 FORCE_INLINE ssize_t get_index (size_t axis) const { return x[axis]; }
82 FORCE_INLINE void move_index (size_t axis, ssize_t increment) { data_offset += stride (axis) * increment; x[axis] += increment; }
83
84 FORCE_INLINE bool is_direct_io () const { return data_pointer; }
85
87 FORCE_INLINE ValueType get_value () const {
88 if (data_pointer) return Raw::fetch_native<ValueType> (data_pointer, data_offset);
89 return buffer->get_value (data_offset);
90 }
92 FORCE_INLINE void set_value (ValueType val) {
93 if (data_pointer) Raw::store_native<ValueType> (val, data_pointer, data_offset);
94 else buffer->set_value (data_offset, val);
95 }
96
98 friend std::ostream& operator<< (std::ostream& stream, const Image& V) {
99 stream << "\"" << V.name() << "\", datatype " << DataType::from<Image::value_type>().specifier() << ", index [ ";
100 for (size_t n = 0; n < V.ndim(); ++n) stream << V.index(n) << " ";
101 stream << "], current offset = " << V.offset() << ", ";
102 if (is_out_of_bounds(V))
103 stream << "outside FoV";
104 else
105 stream << "value = " << V.value();
106 if (!V.data_pointer) stream << " (using indirect IO)";
107 else stream << " (using direct IO, data at " << V.data_pointer << ")";
108 return stream;
109 }
110
112
129 std::string dump_to_mrtrix_file (std::string filename, bool use_multi_threading = true) const;
130
132
145
147
175 return with_direct_io ( axis < 0 ?
178 }
179
180
182
185 ValueType* address () const {
186 assert (data_pointer != nullptr && "Image::address() can only be used when image access is via direct RAM access");
187 return data_pointer ? static_cast<ValueType*>(data_pointer) + data_offset : nullptr; }
188
189 static Image open (const std::string& image_name, bool read_write_if_existing = false) {
190 return Header::open (image_name).get_image<ValueType> (read_write_if_existing);
191 }
192 static Image create (const std::string& image_name, const Header& template_header, bool add_to_command_history = true) {
193 return Header::create (image_name, template_header, add_to_command_history).get_image<ValueType>();
194 }
195 static Image scratch (const Header& template_header, const std::string& label = "scratch image") {
196 return Header::scratch (template_header, label).get_image<ValueType>();
197 }
198
200 std::shared_ptr<Buffer> buffer;
201 protected:
210 };
211
213
214
215
216
217
218
219 template <typename ValueType>
220 class Image<ValueType>::Buffer : public Header { MEMALIGN (Image<ValueType>::Buffer)
221 public:
222 Buffer() {} // TODO: delete this line! Only for testing memory alignment issues.
224 Buffer (Header& H, bool read_write_if_existing = false);
225 Buffer (Buffer&&) = default;
226 Buffer& operator= (const Buffer&) = delete;
227 Buffer& operator= (Buffer&&) = default;
228 Buffer (const Buffer& b) :
229 Header (b), fetch_func (b.fetch_func), store_func (b.store_func) { }
230
231
232 FORCE_INLINE ValueType get_value (size_t offset) const {
233 ssize_t nseg = offset / io->segment_size();
234 return fetch_func (io->segment (nseg), offset - nseg*io->segment_size(), intensity_offset(), intensity_scale());
235 }
236
237 FORCE_INLINE void set_value (size_t offset, ValueType val) const {
238 ssize_t nseg = offset / io->segment_size();
239 store_func (val, io->segment (nseg), offset - nseg*io->segment_size(), intensity_offset(), intensity_scale());
240 }
241
242 std::unique_ptr<uint8_t[]> data_buffer;
244
245 FORCE_INLINE ImageIO::Base* get_io () const { return io.get(); }
246
247 protected:
248 std::function<ValueType(const void*,size_t,default_type,default_type)> fetch_func;
249 std::function<void(ValueType,void*,size_t,default_type,default_type)> store_func;
250
252 __set_fetch_store_functions (fetch_func, store_func, datatype());
253 }
254 };
255
257
258
259
260
261
262
263
264
265
266
267
268
270
271 namespace
272 {
273
274 // lightweight struct to copy data into:
275 template <typename ValueType>
276 struct TmpImage :
277 public ImageBase<TmpImage<ValueType>, ValueType>
278 { MEMALIGN (TmpImage<ValueType>)
279 using value_type = ValueType;
280
281 TmpImage (const typename Image<ValueType>::Buffer& b, void* const data,
282 vector<ssize_t> x, const Stride::List& strides, size_t offset) :
283 b (b), data (data), x (x), strides (strides), offset (offset) { }
284
285 const typename Image<ValueType>::Buffer& b;
286 void* const data;
287 vector<ssize_t> x;
288 const Stride::List& strides;
289 size_t offset;
290
291 bool valid () const { return true; }
292 const std::string name () const { return "direct IO buffer"; }
293 FORCE_INLINE size_t ndim () const { return b.ndim(); }
294 FORCE_INLINE ssize_t size (size_t axis) const { return b.size(axis); }
295 FORCE_INLINE ssize_t stride (size_t axis) const { return strides[axis]; }
296
297 FORCE_INLINE ssize_t get_index (size_t axis) const { return x[axis]; }
298 FORCE_INLINE void move_index (size_t axis, ssize_t increment) { offset += stride (axis) * increment; x[axis] += increment; }
299
300 FORCE_INLINE value_type get_value () const { return Raw::fetch_native<ValueType> (data, offset); }
301 FORCE_INLINE void set_value (ValueType val) { Raw::store_native<ValueType> (val, data, offset); }
302 };
303
304 CHECK_MEM_ALIGN (TmpImage<float>);
305
306 }
307
308
309
310
311
312
313
314
315 template <typename ValueType>
316 Image<ValueType>::Buffer::Buffer (Header& H, bool read_write_if_existing) :
317 Header (H) {
318 assert (H.valid() && "IO handler must be set when creating an Image");
319 assert ((H.is_file_backed() ? is_data_type<ValueType>::value : true) && "class types cannot be stored on file using the Image class");
320
321 acquire_io (H);
322 io->set_readwrite_if_existing (read_write_if_existing);
323 io->open (*this, footprint<ValueType> (voxel_count (*this)));
324 if (io->is_file_backed())
326 }
327
328
329
330
331
332
333 template <typename ValueType>
335 {
336 if (data_buffer) // already allocated via with_direct_io()
337 return data_buffer.get();
338
339 assert (io && "data pointer will only be set for valid Images");
340 if (!io->is_file_backed()) // this is a scratch image
341 return io->segment(0);
342
343 // check whether we can still do direct IO
344 // if so, return address where mapped
345 if (io->nsegments() == 1 && datatype() == DataType::from<ValueType>() && intensity_offset() == 0.0 && intensity_scale() == 1.0)
346 return io->segment(0);
347
348 // can't do direct IO
349 return nullptr;
350 }
351
352
353
354 template <typename ValueType>
355 Image<ValueType> Header::get_image (bool read_write_if_existing)
356 {
357 if (!valid())
358 throw Exception ("FIXME: don't invoke get_image() with invalid Header!");
359 std::shared_ptr<typename Image<ValueType>::Buffer> buffer (new typename Image<ValueType>::Buffer (*this, read_write_if_existing));
360 return { buffer };
361 }
362
363
364
365
366
367
368 template <typename ValueType>
370 data_pointer (nullptr),
371 data_offset (0) { }
372
373 template <typename ValueType>
374 Image<ValueType>::Image (const std::shared_ptr<Image<ValueType>::Buffer>& buffer_p, const Stride::List& desired_strides) :
375 buffer (buffer_p),
376 data_pointer (buffer->get_data_pointer()),
377 x (ndim(), 0),
378 strides (desired_strides.size() ? desired_strides : Stride::get (*buffer)),
379 data_offset (Stride::offset (*this))
380 {
381 assert (buffer);
382 assert (data_pointer || buffer->get_io());
383 DEBUG ("image \"" + name() + "\" initialised with strides = " + str(strides) + ", start = " + str(data_offset)
384 + ", using " + ( is_direct_io() ? "" : "in" ) + "direct IO");
385 }
386
387
388
389
390
391 template <typename ValueType>
393 {
394 if (buffer.unique()) {
395 // was image preloaded and read/write? If so,need to write back:
396 if (buffer->get_io()) {
397 if (buffer->get_io()->is_image_readwrite() && buffer->data_buffer) {
398 auto data_buffer = std::move (buffer->data_buffer);
399 TmpImage<ValueType> src = { *buffer, data_buffer.get(), vector<ssize_t> (ndim(), 0), strides, Stride::offset (*this) };
400 Image<ValueType> dest (buffer);
401 threaded_copy_with_progress_message ("writing back direct IO buffer for \"" + name() + "\"", src, dest);
402 }
403 }
404 }
405 }
406
407
408
409 template <typename ValueType>
410 Image<ValueType> Image<ValueType>::with_direct_io (Stride::List with_strides)
411 {
412 if (buffer->data_buffer)
413 throw Exception ("FIXME: don't invoke 'with_direct_io()' on images already using direct IO!");
414 if (!buffer->get_io())
415 throw Exception ("FIXME: don't invoke 'with_direct_io()' on non-validated images!");
416 if (!buffer.unique())
417 throw Exception ("FIXME: don't invoke 'with_direct_io()' on images if other copies exist!");
418
419 bool preload = ( buffer->datatype() != DataType::from<ValueType>() ) || ( buffer->get_io()->files.size() > 1 );
420 if (with_strides.size()) {
421 auto new_strides = Stride::get_actual (Stride::get_nearest_match (*this, with_strides), *this);
422 preload |= ( new_strides != Stride::get (*this) );
423 with_strides = new_strides;
424 }
425 else
426 with_strides = Stride::get (*this);
427
428 if (!preload)
429 return std::move (*this);
430
431 // do the preload:
432
433 // the buffer into which to copy the data:
434 const auto buffer_size = footprint<ValueType> (voxel_count (*this));
435 buffer->data_buffer = std::unique_ptr<uint8_t[]> (new uint8_t [buffer_size]);
436
437 if (buffer->get_io()->is_image_new()) {
438 // no need to preload if data is zero anyway:
439 memset (buffer->data_buffer.get(), 0, buffer_size);
440 }
441 else {
442 auto src (*this);
443 TmpImage<ValueType> dest = { *buffer, buffer->data_buffer.get(), vector<ssize_t> (ndim(), 0), with_strides, Stride::offset (with_strides, *this) };
444 threaded_copy_with_progress_message ("preloading data for \"" + name() + "\"", src, dest);
445 }
446
447 return Image (buffer, with_strides);
448 }
449
450
451
452
453
454 template <typename ValueType>
455 std::string Image<ValueType>::dump_to_mrtrix_file (std::string filename, bool) const
456 {
457 if (!data_pointer || ( !Path::has_suffix (filename, ".mih") && !Path::has_suffix (filename, ".mif") ))
458 throw Exception ("FIXME: image not suitable for use with 'Image::dump_to_mrtrix_file()'");
459
460 // try to dump file to mrtrix format if possible (direct IO)
461 if (is_dash (filename))
462 filename = File::create_tempfile (0, "mif");
463
464 DEBUG ("dumping image \"" + name() + "\" to file \"" + filename + "\"...");
465
466 File::OFStream out (filename, std::ios::out | std::ios::binary);
467 out << "mrtrix image\n";
469
470 const bool single_file = Path::has_suffix (filename, ".mif");
471 std::string data_filename = filename;
472
473 int64_t offset = 0;
474 out << "file: ";
475 if (single_file) {
476 offset = int64_t(out.tellp()) + int64_t(18);
477 offset += ((4 - (offset % 4)) % 4);
478 out << ". " << offset << "\nEND\n";
479 }
480 else {
481 data_filename = filename.substr (0, filename.size()-4) + ".dat";
482 out << Path::basename (data_filename) << "\n";
483 out.close();
484 out.open (data_filename, std::ios::out | std::ios::binary);
485 }
486
487 const int64_t data_size = footprint (*buffer);
488 out.seekp (offset, out.beg);
489 out.write ((const char*) data_pointer, data_size);
490 if (!out.good())
491 throw Exception ("error writing back contents of file \"" + data_filename + "\": " + strerror(errno));
492 out.close();
493
494 // If data_size exceeds some threshold, ostream artificially increases the file size beyond that required at close()
495 // TODO check whether this is still needed...?
496 File::resize (data_filename, offset + data_size);
497
498 return filename;
499 }
500
501
502
503
504
505
506
507
508 template <class ImageType>
509 std::string __save_generic (ImageType& x, const std::string& filename, bool use_multi_threading) {
511 if (use_multi_threading)
512 threaded_copy (x, out);
513 else
514 copy (x, out);
515 return out.name();
516 }
517
519
521 template <class ImageType>
522 typename std::enable_if<is_adapter_type<typename std::remove_reference<ImageType>::type>::value, std::string>::type
523 save (ImageType&& x, const std::string& filename, bool use_multi_threading = true)
524 {
525 return __save_generic (x, filename, use_multi_threading);
526 }
527
529 template <class ImageType>
530 typename std::enable_if<is_pure_image<typename std::remove_reference<ImageType>::type>::value, std::string>::type
531 save (ImageType&& x, const std::string& filename, bool use_multi_threading = true)
532 {
533 try { return x.dump_to_mrtrix_file (filename, use_multi_threading); }
534 catch (...) { }
535 return __save_generic (x, filename, use_multi_threading);
536 }
537
538
540 template <class ImageType>
541 typename enable_if_image_type<ImageType,void>::type display (ImageType& x) {
542 std::string filename = save (x, "-");
543 CONSOLE ("displaying image \"" + filename + "\"");
544 if (system (("bash -c \"mrview " + filename + "\"").c_str()))
545 WARN (std::string("error invoking viewer: ") + strerror(errno));
546 }
547
548
549}
550
551#endif
552
553
void acquire_io(Header &H)
Definition: header.h:375
std::unique_ptr< ImageIO::Base > io
additional information relevant for images stored on file
Definition: header.h:368
void * get_data_pointer()
std::function< ValueType(const void *, size_t, default_type, default_type)> fetch_func
Definition: image.h:248
ValueType get_value(size_t offset) const
Definition: image.h:232
void set_fetch_store_functions()
Definition: image.h:251
Buffer(Header &H, bool read_write_if_existing=false)
construct a Buffer object to access the data in the image specified
ImageIO::Base * get_io() const
Definition: image.h:245
void set_value(size_t offset, ValueType val) const
Definition: image.h:237
Buffer(Buffer &&)=default
std::unique_ptr< uint8_t[]> data_buffer
Definition: image.h:242
std::function< void(ValueType, void *, size_t, default_type, default_type)> store_func
Definition: image.h:249
Buffer(const Buffer &b)
Definition: image.h:228
functions and classes related to image data input/output
Definition: image.h:41
Image with_direct_io(Stride::List with_strides=Stride::List())
return a new Image using direct IO
Image with_direct_io(int axis)
return a new Image using direct IO
Definition: image.h:174
bool is_direct_io() const
Definition: image.h:84
static Image scratch(const Header &template_header, const std::string &label="scratch image")
Definition: image.h:195
static Image create(const std::string &image_name, const Header &template_header, bool add_to_command_history=true)
Definition: image.h:192
size_t ndim() const
Definition: image.h:65
size_t offset() const
offset to current voxel from start of data
Definition: image.h:71
ValueType get_value() const
get voxel value at current location
Definition: image.h:87
void set_value(ValueType val)
set voxel value at current location
Definition: image.h:92
const std::string & name() const
Definition: image.h:62
ssize_t get_index(size_t axis) const
get position of current voxel location along axis
Definition: image.h:80
std::shared_ptr< Buffer > buffer
shared reference to header/buffer
Definition: image.h:200
const KeyValues & keyval() const
get generic key/value text attributes
Definition: image.h:60
const transform_type & transform() const
Definition: image.h:63
ValueType * address() const
return RAM address of current voxel
Definition: image.h:185
bool valid() const
Definition: image.h:56
friend std::ostream & operator<<(std::ostream &stream, const Image &V)
use for debugging
Definition: image.h:98
std::string dump_to_mrtrix_file(std::string filename, bool use_multi_threading=true) const
write out the contents of a direct IO image to file
size_t data_offset
offset to currently pointed-to voxel
Definition: image.h:209
Image(const std::shared_ptr< Buffer > &, const Stride::List &=Stride::List())
used internally to instantiate Image objects
default_type spacing(size_t axis) const
Definition: image.h:67
Stride::List strides
voxel indices
Definition: image.h:207
void move_index(size_t axis, ssize_t increment)
move position of current voxel location along axis
Definition: image.h:82
Image(Image &&)=default
static Image open(const std::string &image_name, bool read_write_if_existing=false)
Definition: image.h:189
void reset()
reset index to zero (origin)
Definition: image.h:74
Image(const Image &)=default
ssize_t stride(size_t axis) const
Definition: image.h:68
Image & operator=(const Image &image)=default
ValueType value_type
Definition: image.h:43
void * data_pointer
pointer to data address whether in RAM or MMap
Definition: image.h:203
vector< ssize_t > x
voxel indices
Definition: image.h:205
ssize_t size(size_t axis) const
Definition: image.h:66
bool operator!() const
Definition: image.h:57
#define WARN(msg)
Definition: exception.h:73
#define DEBUG(msg)
Definition: exception.h:75
#define CONSOLE(msg)
Definition: exception.h:71
index_type offset
Definition: loop.h:33
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
std::unique_ptr< ImageIO::Base > create(Header &H)
void resize(const std::string &filename, int64_t size)
Definition: utils.h:187
std::string create_tempfile(int64_t size=0, const char *suffix=NULL)
Definition: utils.h:216
void write_mrtrix_header(const Header &, StreamType &)
Definition: mrtrix_utils.h:149
MR::default_type value_type
Definition: typedefs.h:33
std::string basename(const std::string &name)
Definition: path.h:60
bool has_suffix(const std::string &name, const std::string &suffix)
Definition: path.h:130
vector< ssize_t > List
Definition: stride.h:63
List contiguous_along_axis(size_t axis)
convenience function to get volume-contiguous strides
Definition: stride.h:386
List contiguous_along_spatial_axes(const HeaderType &header)
convenience function to get spatially contiguous strides
Definition: stride.h:402
List get_nearest_match(const HeaderType &current, const List &desired)
produce strides from current that match those specified in desired
Definition: stride.h:364
List get_actual(HeaderType &header)
get actual strides:
Definition: stride.h:261
size_t offset(const HeaderType &header)
calculate offset to start of data
Definition: stride.h:319
List get(const HeaderType &header)
return the strides of header as a vector<ssize_t>
Definition: stride.h:125
Definition: base.h:24
std::map< std::string, std::string > KeyValues
used in various places for storing key-value pairs
Definition: types.h:238
constexpr int SpatiallyContiguous
Definition: image.h:35
CHECK_MEM_ALIGN(Header)
enable_if_image_type< ImageType, void >::type display(ImageType &x)
display the contents of an image in MRView (for debugging only)
Definition: image.h:541
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
Eigen::Transform< default_type, 3, Eigen::AffineCompact > transform_type
the type for the affine transform of an image:
Definition: types.h:234
std::enable_if<!is_data_type< ValueType >::value, void >::type __set_fetch_store_functions(std::function< ValueType(const void *, size_t, default_type, default_type)> &, std::function< void(ValueType, void *, size_t, default_type, default_type)> &, DataType)
Definition: fetch_store.h:28
void copy(InputImageType &&source, OutputImageType &&destination, size_t from_axis=0, size_t to_axis=std::numeric_limits< size_t >::max())
Definition: copy.h:27
void threaded_copy(InputImageType &source, OutputImageType &destination, const vector< size_t > &axes, size_t num_axes_in_thread=1)
Definition: threaded_copy.h:43
size_t is_dash(const std::string &arg)
match whole string to a dash or any Unicode character that looks like one
Definition: mrtrix.h:225
std::enable_if< is_adapter_type< typenamestd::remove_reference< ImageType >::type >::value, std::string >::type save(ImageType &&x, const std::string &filename, bool use_multi_threading=true)
save contents of an existing image to file (for debugging only)
Definition: image.h:523
void threaded_copy_with_progress_message(const std::string &message, InputImageType &source, OutputImageType &destination, const vector< size_t > &axes, size_t num_axes_in_thread=1)
Definition: threaded_copy.h:69
int axis
Eigen::MatrixXd H
size_t index
#define MEMALIGN(...)
Definition: types.h:185
#define FORCE_INLINE
Definition: types.h:156
const std::string name
Definition: thread.h:108