[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

hdf5impex.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 2009 by Michael Hanselmann and Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_HDF5IMPEX_HXX
37 #define VIGRA_HDF5IMPEX_HXX
38 
39 #include <string>
40 #include <algorithm>
41 #include <utility>
42 
43 #define H5Gcreate_vers 2
44 #define H5Gopen_vers 2
45 #define H5Dopen_vers 2
46 #define H5Dcreate_vers 2
47 #define H5Acreate_vers 2
48 #define H5Eset_auto_vers 2
49 #define H5Eget_auto_vers 2
50 
51 #include <hdf5.h>
52 
53 #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6)
54 # ifndef H5Gopen
55 # define H5Gopen(a, b, c) H5Gopen(a, b)
56 # endif
57 # ifndef H5Gcreate
58 # define H5Gcreate(a, b, c, d, e) H5Gcreate(a, b, 1)
59 # endif
60 # ifndef H5Dopen
61 # define H5Dopen(a, b, c) H5Dopen(a, b)
62 # endif
63 # ifndef H5Dcreate
64 # define H5Dcreate(a, b, c, d, e, f, g) H5Dcreate(a, b, c, d, f)
65 # endif
66 # ifndef H5Acreate
67 # define H5Acreate(a, b, c, d, e, f) H5Acreate(a, b, c, d, e)
68 # endif
69 # ifndef H5Pset_obj_track_times
70 # define H5Pset_obj_track_times(a, b) do {} while (0)
71 # endif
72 # include <H5LT.h>
73 #else
74 # include <hdf5_hl.h>
75 #endif
76 
77 #include "impex.hxx"
78 #include "multi_array.hxx"
79 #include "multi_iterator_coupled.hxx"
80 #include "multi_impex.hxx"
81 #include "utilities.hxx"
82 #include "error.hxx"
83 
84 #if defined(_MSC_VER)
85 # include <io.h>
86 #else
87 # include <unistd.h>
88 #endif
89 
90 namespace vigra {
91 
92 /** \addtogroup VigraHDF5Impex Import/Export of Images and Arrays in HDF5 Format
93 
94  Supports arrays with arbitrary element types and arbitrary many dimensions.
95  See the <a href="http://www.hdfgroup.org/HDF5/">HDF5 Website</a> for more
96  information on the HDF5 file format.
97 */
98 //@{
99 
100  /** \brief Check if given filename refers to a HDF5 file.
101  */
102 inline bool isHDF5(char const * filename)
103 {
104 #ifdef _MSC_VER
105  return _access(filename, 0) != -1 && H5Fis_hdf5(filename);
106 #else
107  return access(filename, F_OK) == 0 && H5Fis_hdf5(filename);
108 #endif
109 }
110 
111  /** \brief Temporarily disable HDF5's native error output.
112 
113  This should be used when you want to call an HDF5 function
114  that is known to fail (e.g. during testing), or when you want
115  to use an alternative error reporting mechanism (e.g. exceptions).
116 
117  <b>Usage:</b>
118 
119  <b>\#include</b> <vigra/hdf5impex.hxx><br>
120  Namespace: vigra
121  \code
122  {
123  HDF5DisableErrorOutput hdf5DisableErrorOutput;
124 
125  ... // call some HDF5 function
126 
127  } // restore the original error reporting in the destructor of HDF5DisableErrorOutput
128  \endcode
129  */
131 {
132  H5E_auto1_t old_func1_;
133  H5E_auto2_t old_func2_;
134  void *old_client_data_;
135  int error_handler_version_;
136 
138  HDF5DisableErrorOutput & operator=(HDF5DisableErrorOutput const &);
139 
140  public:
142  : old_func1_(0)
143  , old_func2_(0)
144  , old_client_data_(0)
145  , error_handler_version_(-1)
146  {
147  if(H5Eget_auto2(H5E_DEFAULT, &old_func2_, &old_client_data_) >= 0)
148  {
149  // prefer new-style error handling
150  H5Eset_auto2(H5E_DEFAULT, NULL, NULL);
151  error_handler_version_ = 2;
152  }
153  else if(H5Eget_auto1(&old_func1_, &old_client_data_) >= 0)
154  {
155  // fall back to old-style if another module (e.g. h5py)
156  // prevents us from using new-style (i.e. H5Eget_auto2()
157  // returned a negative error code)
158  H5Eset_auto1(NULL, NULL);
159  error_handler_version_ = 1;
160  }
161  }
162 
164  {
165  if(error_handler_version_ == 1)
166  H5Eset_auto1(old_func1_, old_client_data_);
167  else if(error_handler_version_ == 2)
168  H5Eset_auto2(H5E_DEFAULT, old_func2_, old_client_data_);
169  }
170 };
171 
172  /** \brief Wrapper for unique hid_t objects.
173 
174  This class offers the functionality of <tt>std::unique_ptr</tt> for HDF5 handles
175  (type <tt>hid_t</tt>). Unfortunately, <tt>std::unique_ptr</tt> cannot be used directly
176  for this purpose because it only works with pointers, whereas <tt>hid_t</tt> is an integer type.
177 
178  Newly created or opened HDF5 handles are stored as objects of type <tt>hid_t</tt>. When the handle
179  is no longer needed, the appropriate close function must be called. However, if a function is
180  aborted by an exception, this is difficult to ensure. Class HDF5Handle is a smart pointer that
181  solves this problem by calling the close function in the destructor (This is analogous to how
182  <tt>std::unique_ptr</tt> calls 'delete' on the contained pointer). A pointer to the close function
183  must be passed to the constructor, along with an error message that is raised when
184  creation/opening fails.
185 
186  When a <tt>HDF5Handle</tt> is created or assigned from another one, ownership passes on to the
187  left-hand-side handle object, and the right-hand-side objects is resest to a NULL handle.
188 
189  Since <tt>HDF5Handle</tt> objects are convertible to <tt>hid_t</tt>, they can be used in the code
190  in place of the latter.
191 
192  <b>Usage:</b>
193 
194  \code
195  HDF5Handle file_id(H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT),
196  &H5Fclose,
197  "Error message when H5Fopen() fails.");
198 
199  ... // use file_id in the same way as a plain hid_t object
200 
201  // pass ownership to a new handle object
202  HDF5Handle new_handle(file_id);
203 
204  assert(file_id.get() == 0);
205  \endcode
206 
207  <b>\#include</b> <vigra/hdf5impex.hxx><br>
208  Namespace: vigra
209  */
211 {
212 public:
213  typedef herr_t (*Destructor)(hid_t);
214 
215 private:
216  hid_t handle_;
217  Destructor destructor_;
218 
219 public:
220 
221  /** \brief Default constructor.
222  Creates a NULL handle.
223  **/
225  : handle_( 0 ),
226  destructor_(0)
227  {}
228 
229  /** \brief Create a wrapper for a hid_t object.
230 
231  The hid_t object \a h is assumed to be the return value of an open or create function.
232  It will be closed with the given close function \a destructor as soon as this
233  HDF5Handle is destructed, except when \a destructor is a NULL pointer (in which
234  case nothing happens at destruction time). If \a h has a value that indicates
235  failed opening or creation (by HDF5 convention, this means that \a h is negative),
236  an exception is raised by calling <tt>vigra_fail(error_message)</tt>.
237 
238  <b>Usage:</b>
239 
240  \code
241  HDF5Handle file_id(H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT),
242  &H5Fclose,
243  "Error message.");
244 
245  ... // use file_id in the same way
246  \endcode
247  */
248  HDF5Handle(hid_t h, Destructor destructor, const char * error_message)
249  : handle_( h ),
250  destructor_(destructor)
251  {
252  if(handle_ < 0)
253  vigra_fail(error_message);
254  }
255 
256  /** \brief Copy constructor.
257 
258  Hands over ownership of the RHS handle (analogous to <tt>std::unique_pt</tt>).
259  */
261  : handle_( h.handle_ ),
262  destructor_(h.destructor_)
263  {
264  const_cast<HDF5Handle &>(h).handle_ = 0;
265  }
266 
267  /** \brief Assignment.
268  Calls close() for the LHS handle and hands over ownership of the
269  RHS handle (analogous to <tt>std::unique_pt</tt>).
270  */
272  {
273  if(h.handle_ != handle_)
274  {
275  close();
276  handle_ = h.handle_;
277  destructor_ = h.destructor_;
278  const_cast<HDF5Handle &>(h).handle_ = 0;
279  }
280  return *this;
281  }
282 
283  /** \brief Destructor.
284  Calls close() for the contained handle.
285  */
287  {
288  close();
289  }
290 
291  /** \brief Explicitly call the stored destructor (if one has been registered in the
292  constructor) for the contained handle and set the wrapper to NULL. Returns
293  a negative value when the destructor call for the handle fails, and
294  a non-negative value otherwise.
295  */
296  herr_t close()
297  {
298  herr_t res = 1;
299  if(handle_ && destructor_)
300  res = (*destructor_)(handle_);
301  handle_ = 0;
302  destructor_ = 0;
303  return res;
304  }
305 
306  /** \brief Return the contained handle and set the wrapper to NULL
307  without calling <tt>close()</tt>.
308  */
309  hid_t release()
310  {
311  hid_t res = handle_;
312  handle_ = 0;
313  destructor_ = 0;
314  return res;
315  }
316 
317  /** \brief Reset the wrapper to a new handle.
318 
319  Equivalent to <tt>handle = HDF5Handle(h, destructor, error_message)</tt>.
320  */
321  void reset(hid_t h, Destructor destructor, const char * error_message)
322  {
323  if(h < 0)
324  vigra_fail(error_message);
325  if(h != handle_)
326  {
327  close();
328  handle_ = h;
329  destructor_ = destructor;
330  }
331  }
332 
333  /** \brief Swap the contents of two handle wrappers.
334 
335  Also available as <tt>std::swap(handle1, handle2)</tt>.
336  */
337  void swap(HDF5Handle & h)
338  {
339  std::swap(handle_, h.handle_);
340  std::swap(destructor_, h.destructor_);
341  }
342 
343  /** \brief Get a temporary hid_t object for the contained handle.
344  Do not call a close function on the return value - a crash will be likely
345  otherwise.
346  */
347  hid_t get() const
348  {
349  return handle_;
350  }
351 
352  /** \brief Convert to a plain hid_t object.
353 
354  This function ensures that hid_t objects can be transparently replaced with
355  HDF5Handle objects in user code. Do not call a close function on the return
356  value - a crash will be likely otherwise.
357  */
358  operator hid_t() const
359  {
360  return handle_;
361  }
362 
363  /** \brief Equality comparison of the contained handle.
364  */
365  bool operator==(HDF5Handle const & h) const
366  {
367  return handle_ == h.handle_;
368  }
369 
370  /** \brief Equality comparison of the contained handle.
371  */
372  bool operator==(hid_t h) const
373  {
374  return handle_ == h;
375  }
376 
377  /** \brief Inequality comparison of the contained handle.
378  */
379  bool operator!=(HDF5Handle const & h) const
380  {
381  return handle_ != h.handle_;
382  }
383 
384  /** \brief Inequality comparison of the contained handle.
385  */
386  bool operator!=(hid_t h) const
387  {
388  return handle_ != h;
389  }
390 };
391 
392 
393  /** \brief Wrapper for shared hid_t objects.
394 
395  This class offers the functionality of <tt>std::shared_ptr</tt> for HDF5 handles
396  (type <tt>hid_t</tt>). Unfortunately, <tt>std::shared_ptr</tt> cannot be used directly
397  for this purpose because it only works with pointers, whereas <tt>hid_t</tt> is an integer type.
398 
399  Newly created or opened HDF5 handles are stored as objects of type <tt>hid_t</tt>. When the handle
400  is no longer needed, the appropriate close function must be called. However, if a function is
401  aborted by an exception, this is difficult to ensure. Class HDF5HandleShared is a smart pointer
402  that solves this problem by calling the close function in the destructor of the handle's last
403  owner (This is analogous to how <tt>std::shared_ptr</tt> calls 'delete' on the contained
404  pointer). A pointer to the close function must be passed to the constructor, along with an error
405  message that is raised when creation/opening fails.
406 
407  When a <tt>HDF5HandleShared</tt> is created or assigned from another one, ownership is shared
408  between the two handles, and the value returned by <tt>use_count()</tt> increases by one.
409 
410  Since <tt>HDF5HandleShared</tt> objects are convertible to <tt>hid_t</tt>, they can be used in the code
411  in place of the latter.
412 
413  <b>Usage:</b>
414 
415  \code
416  HDF5HandleShared file_id(H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT),
417  &H5Fclose,
418  "Error message when H5Fopen() fails.");
419 
420  ... // use file_id in the same way as a plain hid_t object
421 
422  // share ownership between same_id and file_id
423  HDF5HandleShared same_id(file_id);
424  assert(same_id.use_count() == 2);
425  assert(same_id.get() == file_id.get());
426  \endcode
427 
428  <b>\#include</b> <vigra/hdf5impex.hxx><br>
429  Namespace: vigra
430  */
432 {
433 public:
434  typedef herr_t (*Destructor)(hid_t);
435 
436 private:
437  hid_t handle_;
438  Destructor destructor_;
439  size_t * refcount_;
440 
441 public:
442 
443  /** \brief Default constructor.
444  Creates a NULL handle.
445  **/
447  : handle_( 0 ),
448  destructor_(0),
449  refcount_(0)
450  {}
451 
452  /** \brief Create a shared wrapper for a plain hid_t object.
453 
454  The hid_t object \a h is assumed to be the return value of an open or create function.
455  It will be closed with the given close function \a destructor as soon as this
456  HDF5HandleShared is destructed, except when \a destructor is a NULL pointer (in which
457  case nothing happens at destruction time). If \a h has a value that indicates
458  failed opening or creation (by HDF5 convention, this means that \a h is negative),
459  an exception is raised by calling <tt>vigra_fail(error_message)</tt>.
460 
461  <b>Usage:</b>
462 
463  \code
464  HDF5HandleShared file_id(H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT),
465  &H5Fclose,
466  "Error message.");
467 
468  ... // use file_id in the same way
469  \endcode
470  */
471  HDF5HandleShared(hid_t h, Destructor destructor, const char * error_message)
472  : handle_( h ),
473  destructor_(destructor),
474  refcount_(0)
475  {
476  if(handle_ < 0)
477  vigra_fail(error_message);
478  if(handle_ > 0)
479  refcount_ = new size_t(1);
480  }
481 
482  /** \brief Copy constructor.
483  Shares ownership with the RHS handle (analogous to <tt>std::shared_ptr</tt>).
484  */
486  : handle_( h.handle_ ),
487  destructor_(h.destructor_),
488  refcount_(h.refcount_)
489  {
490  if(refcount_)
491  ++(*refcount_);
492  }
493 
494  /** \brief Assignment.
495  Call close() for the present LHS handle and share ownership with the
496  RHS handle (analogous to <tt>std::shared_ptr</tt>).
497  */
499  {
500  if(h.handle_ != handle_)
501  {
502  close();
503  handle_ = h.handle_;
504  destructor_ = h.destructor_;
505  refcount_ = h.refcount_;
506  if(refcount_)
507  ++(*refcount_);
508  }
509  return *this;
510  }
511 
512  /** \brief Destructor (calls close())
513  */
515  {
516  close();
517  }
518 
519  /** \brief Close the handle if this is the unique (i.e. last) owner.
520 
521  Decrements the reference counter and calls the destructor function of
522  the handle (if one has been registered in the constructor) when the counter
523  reaches zero. Sets this wrapper to NULL in any case. Returns
524  a negative value when the destructor call for the handle fails, and
525  a non-negative value otherwise.
526  */
527  herr_t close()
528  {
529  herr_t res = 1;
530  if(refcount_)
531  {
532  --(*refcount_);
533  if(*refcount_ == 0)
534  {
535  if(destructor_)
536  res = (*destructor_)(handle_);
537  delete refcount_;
538  }
539  }
540  handle_ = 0;
541  destructor_ = 0;
542  refcount_ = 0;
543  return res;
544  }
545 
546  /** \brief Reset the handle to a new value.
547 
548  Equivalent to <tt>handle = HDF5HandleShared(h, destructor, error_message)</tt>.
549  */
550  void reset(hid_t h, Destructor destructor, const char * error_message)
551  {
552  if(h < 0)
553  vigra_fail(error_message);
554  if(h != handle_)
555  {
556  close();
557  handle_ = h;
558  destructor_ = destructor;
559  if(handle_ > 0)
560  refcount_ = new size_t(1);
561  }
562  }
563 
564  /** \brief Get the number of owners of the contained handle.
565  */
566  size_t use_count() const
567  {
568  return refcount_
569  ? *refcount_
570  : 0;
571  }
572 
573  /** \brief Check if this is the unique owner of the conatined handle.
574 
575  Equivalent to <tt>handle.use_count() == 1</tt>.
576  */
577  bool unique() const
578  {
579  return use_count() == 1;
580  }
581 
582  /** \brief Swap the contents of two handle wrappers.
583 
584  Also available as <tt>std::swap(handle1, handle2)</tt>.
585  */
587  {
588  std::swap(handle_, h.handle_);
589  std::swap(destructor_, h.destructor_);
590  std::swap(refcount_, h.refcount_);
591  }
592 
593  /** \brief Get a temporary hid_t object for the contained handle.
594  Do not call a close function on the return value - a crash will be likely
595  otherwise.
596  */
597  hid_t get() const
598  {
599  return handle_;
600  }
601 
602  /** \brief Convert to a plain hid_t object.
603 
604  This function ensures that hid_t objects can be transparently replaced with
605  HDF5HandleShared objects in user code. Do not call a close function on the return
606  value - a crash will be likely otherwise.
607  */
608  operator hid_t() const
609  {
610  return handle_;
611  }
612 
613  /** \brief Equality comparison of the contained handle.
614  */
615  bool operator==(HDF5HandleShared const & h) const
616  {
617  return handle_ == h.handle_;
618  }
619 
620  /** \brief Equality comparison of the contained handle.
621  */
622  bool operator==(hid_t h) const
623  {
624  return handle_ == h;
625  }
626 
627  /** \brief Inequality comparison of the contained handle.
628  */
629  bool operator!=(HDF5HandleShared const & h) const
630  {
631  return handle_ != h.handle_;
632  }
633 
634  /** \brief Inequality comparison of the contained handle.
635  */
636  bool operator!=(hid_t h) const
637  {
638  return handle_ != h;
639  }
640 };
641 
642 //@}
643 
644 } // namespace vigra
645 
646 namespace std {
647 
648 inline void swap(vigra::HDF5Handle & l, vigra::HDF5Handle & r)
649 {
650  l.swap(r);
651 }
652 
653 inline void swap(vigra::HDF5HandleShared & l, vigra::HDF5HandleShared & r)
654 {
655  l.swap(r);
656 }
657 
658 } // namespace std
659 
660 namespace vigra {
661 
662 /** \addtogroup VigraHDF5Impex
663 */
664 //@{
665 
666 
667 /********************************************************/
668 /* */
669 /* HDF5ImportInfo */
670 /* */
671 /********************************************************/
672 
673 /** \brief Argument object for the function readHDF5().
674 
675 See \ref readHDF5() for a usage example. This object must be
676 used to read an image or array from an HDF5 file
677 and enquire about its properties.
678 
679 <b>\#include</b> <vigra/hdf5impex.hxx><br>
680 Namespace: vigra
681 */
683 {
684  public:
685  enum PixelType { UINT8, UINT16, UINT32, UINT64,
686  INT8, INT16, INT32, INT64,
687  FLOAT, DOUBLE };
688 
689  /** Construct HDF5ImportInfo object.
690 
691  The dataset \a pathInFile in the HDF5 file \a filename is accessed to
692  read its properties. \a pathInFile may contain '/'-separated group
693  names, but must end with the name of the desired dataset:
694 
695  \code
696  HDF5ImportInfo info(filename, "/group1/group2/my_dataset");
697  \endcode
698  */
699  VIGRA_EXPORT HDF5ImportInfo( const char* filePath, const char* pathInFile );
700 
701  VIGRA_EXPORT ~HDF5ImportInfo();
702 
703  /** Get the filename of this HDF5 object.
704  */
705  VIGRA_EXPORT const std::string& getFilePath() const;
706 
707  /** Get the dataset's full name in the HDF5 file.
708  */
709  VIGRA_EXPORT const std::string& getPathInFile() const;
710 
711  /** Get a handle to the file represented by this info object.
712  */
713  VIGRA_EXPORT hid_t getH5FileHandle() const;
714 
715  /** Get a handle to the dataset represented by this info object.
716  */
717  VIGRA_EXPORT hid_t getDatasetHandle() const;
718 
719  /** Get the number of dimensions of the dataset represented by this info object.
720  */
721  VIGRA_EXPORT MultiArrayIndex numDimensions() const;
722 
723  /** Get the shape of the dataset represented by this info object.
724 
725  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
726  Fortran-order, while HDF5 uses C-order. This function therefore reverses the axis
727  order relative to the file contents. That is, when the axes in the file are
728  ordered as 'z', 'y', 'x', this function will return the shape in the order
729  'x', 'y', 'z'.
730  */
731  VIGRA_EXPORT ArrayVector<hsize_t> const & shape() const;
732 
733  /** Get the shape (length) of the dataset along dimension \a dim.
734 
735  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
736  Fortran-order, while HDF5 uses C-order. This function therefore reverses the axis
737  order relative to the file contents. That is, when the axes in the file are
738  ordered as 'z', 'y', 'x', this function will return the shape in the order
739  'x', 'y', 'z'.
740  */
741  VIGRA_EXPORT MultiArrayIndex shapeOfDimension(const int dim) const;
742 
743  /** Query the pixel type of the dataset.
744 
745  Possible values are:
746  <DL>
747  <DT>"INT8"<DD> 8-bit signed integer (unsigned char)
748  <DT>"UINT8"<DD> 8-bit unsigned integer (unsigned char)
749  <DT>"INT16"<DD> 16-bit signed integer (short)
750  <DT>"UINT16"<DD> 16-bit unsigned integer (unsigned short)
751  <DT>"INT32"<DD> 32-bit signed integer (long)
752  <DT>"UINT32"<DD> 32-bit unsigned integer (unsigned long)
753  <DT>"INT64"<DD> 64-bit signed integer (long long)
754  <DT>"UINT64"<DD> 64-bit unsigned integer (unsigned long long)
755  <DT>"FLOAT"<DD> 32-bit floating point (float)
756  <DT>"DOUBLE"<DD> 64-bit floating point (double)
757  </DL>
758  */
759  VIGRA_EXPORT const char * getPixelType() const;
760 
761  /** Query the pixel type of the dataset.
762 
763  Same as getPixelType(), but the result is returned as a
764  ImageImportInfo::PixelType enum. This is useful to implement
765  a switch() on the pixel type.
766 
767  Possible values are:
768  <DL>
769  <DT>UINT8<DD> 8-bit unsigned integer (unsigned char)
770  <DT>INT16<DD> 16-bit signed integer (short)
771  <DT>UINT16<DD> 16-bit unsigned integer (unsigned short)
772  <DT>INT32<DD> 32-bit signed integer (long)
773  <DT>UINT32<DD> 32-bit unsigned integer (unsigned long)
774  <DT>FLOAT<DD> 32-bit floating point (float)
775  <DT>DOUBLE<DD> 64-bit floating point (double)
776  </DL>
777  */
778  VIGRA_EXPORT PixelType pixelType() const;
779 
780  private:
781  HDF5HandleShared m_file_handle, m_dataset_handle;
782  std::string m_filename, m_path, m_pixeltype;
783  hssize_t m_dimensions;
784  ArrayVector<hsize_t> m_dims;
785 };
786 
787 
788 namespace detail {
789 
790 template <class T>
791 struct HDF5TypeTraits
792 {
793  static hid_t getH5DataType()
794  {
795  std::runtime_error("getH5DataType(): invalid type");
796  return 0;
797  }
798 
799  static int numberOfBands()
800  {
801  std::runtime_error("numberOfBands(): invalid type");
802  return 0;
803  }
804 };
805 
806 template<class T>
807 inline hid_t getH5DataType()
808 {
809  return HDF5TypeTraits<T>::getH5DataType();
810 }
811 
812 #define VIGRA_H5_DATATYPE(type, h5type) \
813 template <> \
814 struct HDF5TypeTraits<type> \
815 { \
816  static hid_t getH5DataType() \
817  { \
818  return h5type; \
819  } \
820  static int numberOfBands() \
821  { \
822  return 1; \
823  } \
824  typedef type value_type; \
825 }; \
826 template <int M> \
827 struct HDF5TypeTraits<TinyVector<type, M> > \
828 { \
829  static hid_t getH5DataType() \
830  { \
831  return h5type; \
832  } \
833  static int numberOfBands() \
834  { \
835  return M; \
836  } \
837  typedef type value_type; \
838 }; \
839 template <> \
840 struct HDF5TypeTraits<RGBValue<type> > \
841 { \
842  static hid_t getH5DataType() \
843  { \
844  return h5type; \
845  } \
846  static int numberOfBands() \
847  { \
848  return 3; \
849  } \
850  typedef type value_type; \
851 }; \
852 
853 VIGRA_H5_DATATYPE(char, H5T_NATIVE_CHAR)
854 VIGRA_H5_DATATYPE(signed char, H5T_NATIVE_SCHAR)
855 VIGRA_H5_DATATYPE(unsigned char, H5T_NATIVE_UCHAR)
856 VIGRA_H5_DATATYPE(signed short, H5T_NATIVE_SHORT)
857 VIGRA_H5_DATATYPE(unsigned short, H5T_NATIVE_USHORT)
858 VIGRA_H5_DATATYPE(signed int, H5T_NATIVE_INT)
859 VIGRA_H5_DATATYPE(unsigned int, H5T_NATIVE_UINT)
860 VIGRA_H5_DATATYPE(signed long, H5T_NATIVE_LONG)
861 VIGRA_H5_DATATYPE(unsigned long, H5T_NATIVE_ULONG)
862 VIGRA_H5_DATATYPE(signed long long, H5T_NATIVE_LLONG)
863 VIGRA_H5_DATATYPE(unsigned long long, H5T_NATIVE_ULLONG)
864 VIGRA_H5_DATATYPE(float, H5T_NATIVE_FLOAT)
865 VIGRA_H5_DATATYPE(double, H5T_NATIVE_DOUBLE)
866 VIGRA_H5_DATATYPE(long double, H5T_NATIVE_LDOUBLE)
867 
868 // char arrays with flexible length require 'handcrafted' H5 datatype
869 template <>
870 struct HDF5TypeTraits<char*>
871 {
872  static hid_t getH5DataType()
873  {
874  hid_t stringtype = H5Tcopy (H5T_C_S1);
875  H5Tset_size(stringtype, H5T_VARIABLE);
876  return stringtype;
877  }
878 
879  static int numberOfBands()
880  {
881  return 1;
882  }
883 };
884 
885 template <>
886 struct HDF5TypeTraits<const char*>
887 {
888  static hid_t getH5DataType()
889  {
890  hid_t stringtype = H5Tcopy (H5T_C_S1);
891  H5Tset_size(stringtype, H5T_VARIABLE);
892  return stringtype;
893  }
894 
895  static int numberOfBands()
896  {
897  return 1;
898  }
899 };
900 
901 #undef VIGRA_H5_DATATYPE
902 
903 #if 0
904 template<>
905 inline hid_t getH5DataType<FFTWComplex<float> >()
906 {
907  hid_t complex_id = H5Tcreate (H5T_COMPOUND, sizeof (FFTWComplex<float>));
908  H5Tinsert (complex_id, "real", 0, H5T_NATIVE_FLOAT);
909  H5Tinsert (complex_id, "imaginary", sizeof(float), H5T_NATIVE_FLOAT);
910  return complex_id;
911 }
912 
913 template<>
914 inline hid_t getH5DataType<FFTWComplex<double> >()
915 {
916  hid_t complex_id = H5Tcreate (H5T_COMPOUND, sizeof (FFTWComplex<double>));
917  H5Tinsert (complex_id, "real", 0, H5T_NATIVE_DOUBLE);
918  H5Tinsert (complex_id, "imaginary", sizeof(double), H5T_NATIVE_DOUBLE);
919  return complex_id;
920 }
921 #endif
922 
923 
924 } // namespace detail
925 
926 // helper friend function for callback HDF5_ls_inserter_callback()
927 void HDF5_ls_insert(void*, const std::string &);
928 // callback function for ls(), called via HDF5File::H5Literate()
929 // see http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2
930 // for as to why.
931 
932 VIGRA_EXPORT H5O_type_t HDF5_get_type(hid_t, const char*);
933 extern "C" VIGRA_EXPORT herr_t HDF5_ls_inserter_callback(hid_t, const char*, const H5L_info_t*, void*);
934 extern "C" VIGRA_EXPORT herr_t HDF5_listAttributes_inserter_callback(hid_t, const char*, const H5A_info_t*, void*);
935 
936 /********************************************************/
937 /* */
938 /* HDF5File */
939 /* */
940 /********************************************************/
941 
942 
943 /** \brief Access to HDF5 files
944 
945 HDF5File provides a convenient way of accessing data in HDF5 files. vigra::MultiArray
946 structures of any dimension can be stored to / loaded from HDF5 files. Typical
947 HDF5 features like subvolume access, chunks and data compression are available,
948 string attributes can be attached to any dataset or group. Group- or dataset-handles
949 are encapsulated in the class and managed automatically. The internal file-system like
950 structure can be accessed by functions like "cd()" or "mkdir()".
951 
952 Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
953 Fortran-order, while HDF5 uses C-order. This means that a VIGRA MultiArray,
954 whose indices represent the 'x'-, 'y'-, and 'z'-axis in that order, is reversed
955 upon writing to an HDF5 file, i.e. in the file the axis order is 'z', 'y', 'x'.
956 Likewise, the order is reversed upon reading.
957 
958 <b>Example:</b>
959 Write the MultiArray out_multi_array to file. Change the current directory to
960 "/group" and read in the same MultiArray as in_multi_array.
961 \code
962 HDF5File file("/path/to/file",HDF5File::New);
963 file.mkdir("group");
964 file.write("/group/dataset", out_multi_array);
965 
966 file.cd("/group");
967 file.read("dataset", in_multi_array);
968 
969 \endcode
970 
971 <b>\#include</b> <vigra/hdf5impex.hxx><br>
972 Namespace: vigra
973 */
974 class HDF5File
975 {
976  protected:
977  HDF5HandleShared fileHandle_;
978 
979  // current group handle
980  HDF5Handle cGroupHandle_;
981 
982  private:
983  // time tagging of datasets, turned off (= 0) by default.
984  int track_time;
985 
986  bool read_only_;
987 
988  // helper classes for ls() and listAttributes()
989  struct ls_closure
990  {
991  virtual void insert(const std::string &) = 0;
992  virtual ~ls_closure() {}
993  };
994 
995  // datastructure to hold a std::vector<std::string>
996  struct lsOpData : public ls_closure
997  {
998  std::vector<std::string> & objects;
999  lsOpData(std::vector<std::string> & o) : objects(o) {}
1000  void insert(const std::string & x)
1001  {
1002  objects.push_back(x);
1003  }
1004  };
1005 
1006  // datastructure to hold an associative container
1007  template<class Container>
1008  struct ls_container_data : public ls_closure
1009  {
1010  Container & objects;
1011  ls_container_data(Container & o) : objects(o) {}
1012  void insert(const std::string & x)
1013  {
1014  objects.insert(std::string(x));
1015  }
1016  };
1017 
1018  public:
1019 
1020  // helper for callback HDF5_ls_inserter_callback(), used by ls()
1021  friend void HDF5_ls_insert(void*, const std::string &);
1022 
1023  /** \brief Set how a file is opened.
1024 
1025  OpenMode::New creates a new file. If the file already exists, it is overwritten.
1026 
1027  OpenMode::ReadWrite opens a file for reading/writing. The file will be created if it doesn't exist.
1028 
1029  OpenMode::ReadOnly opens a file for reading. The file as well as any dataset to be accessed must already exist.
1030  */
1031  enum OpenMode {
1032  New, // Create new empty file (existing file will be deleted).
1033  Open, // Open file. Create if not existing.
1034  ReadWrite = Open, // Alias for Open.
1035  OpenReadOnly, // Open file in read-only mode.
1036  ReadOnly = OpenReadOnly, // Alias for OpenReadOnly
1037  Replace, // for ChunkedArrayHDF5: replace dataset if it exists, create otherwise
1038  Default // for ChunkedArrayHDF5: use New if file doesn't exist,
1039  // ReadOnly if file and dataset exist
1040  // Open otherwise
1041  };
1042 
1043  /** \brief Default constructor.
1044 
1045  A file can later be opened via the open() function. Time tagging of datasets is disabled.
1046  */
1048  : track_time(0)
1049  {}
1050 
1051  /** \brief Construct with time tagging of datasets enabled.
1052 
1053  If \a track_creation_times is 'true', time tagging of datasets will be enabled.
1054  */
1055  explicit HDF5File(bool track_creation_times)
1056  : track_time(track_creation_times ? 1 : 0),
1057  read_only_(true)
1058  {}
1059 
1060  /** \brief Open or create an HDF5File object.
1061 
1062  Creates or opens HDF5 file with given filename.
1063  The current group is set to "/". By default, the files is opened in read-only mode.
1064  */
1065  explicit HDF5File(std::string filePath, OpenMode mode = ReadOnly, bool track_creation_times = false)
1066  : track_time(track_creation_times ? 1 : 0)
1067  {
1068  open(filePath, mode);
1069  }
1070 
1071  /** \brief Open or create an HDF5File object.
1072 
1073  Creates or opens HDF5 file with given filename.
1074  The current group is set to "/". By default, the files is opened in read-only mode.
1075  */
1076  explicit HDF5File(char const * filePath, OpenMode mode = ReadOnly, bool track_creation_times = false)
1077  : track_time(track_creation_times ? 1 : 0)
1078  {
1079  open(std::string(filePath), mode);
1080  }
1081 
1082  /** \brief Initialize an HDF5File object from HDF5 file handle
1083 
1084  Initializes an HDF5File object corresponding to the HDF5 file
1085  opened elsewhere. If \a fileHandle is constructed with a
1086  <tt>NULL</tt> destructor, ownership is not transferred
1087  to the new HDF5File object, and you must ensure that the file is
1088  not closed while the new HDF5File object is in use. Otherwise,
1089  ownership will be shared.
1090 
1091  The current group is set to the specified \a pathname. If
1092  \a read_only is 'true', you cannot create new datasets or
1093  overwrite data.
1094 
1095  \warning When VIRA is linked against a different HDF5 library than the one
1096  used to open the file with the given id, this method will lead to crashes.
1097  */
1098  explicit HDF5File(HDF5HandleShared const & fileHandle,
1099  const std::string & pathname = "",
1100  bool read_only = false)
1101  : fileHandle_(fileHandle),
1102  read_only_(read_only)
1103 
1104  {
1105  // get group handle for given pathname
1106  // calling openCreateGroup_ without setting a valid cGroupHandle does
1107  // not work. Starting from root() is a safe bet.
1108  root();
1109  cGroupHandle_ = HDF5Handle(openCreateGroup_(pathname), &H5Gclose,
1110  "HDF5File(fileHandle, pathname): Failed to open group");
1111 
1112  // extract track_time attribute
1113  hbool_t track_times_tmp;
1114  HDF5Handle plist_id(H5Fget_create_plist(fileHandle_), &H5Pclose,
1115  "HDF5File(fileHandle, pathname): Failed to open file creation property list");
1116  herr_t status = H5Pget_obj_track_times(plist_id, &track_times_tmp );
1117  vigra_postcondition(status >= 0,
1118  "HDF5File(fileHandle, pathname): cannot access track time attribute");
1119  track_time = track_times_tmp;
1120  }
1121 
1122  /** \brief Copy a HDF5File object.
1123 
1124  The new object will refer to the same file and group as \a other.
1125  */
1126  HDF5File(HDF5File const & other)
1127  : fileHandle_(other.fileHandle_),
1128  track_time(other.track_time),
1129  read_only_(other.read_only_)
1130  {
1131  cGroupHandle_ = HDF5Handle(openCreateGroup_(other.currentGroupName_()), &H5Gclose,
1132  "HDF5File(HDF5File const &): Failed to open group.");
1133  }
1134 
1135  /** \brief The destructor flushes and closes the file.
1136  */
1138  {
1139  // The members fileHandle_ and cGroupHandle_ are automatically closed
1140  // as they are of type HDF5Handle and are properly initialised.
1141  // The closing of fileHandle_ implies flushing the file to
1142  // the operating system, see
1143  // http://www.hdfgroup.org/HDF5/doc/RM/RM_H5F.html#File-Close .
1144  }
1145 
1146  /** \brief Assign a HDF5File object.
1147 
1148  Calls close() on the present file and refers to the same file and group as \a other afterwards.
1149  */
1150  HDF5File & operator=(HDF5File const & other)
1151  {
1152  if(this != &other)
1153  {
1154  close();
1155  fileHandle_ = other.fileHandle_;
1156  cGroupHandle_ = HDF5Handle(openCreateGroup_(other.currentGroupName_()), &H5Gclose,
1157  "HDF5File::operator=(): Failed to open group.");
1158  track_time = other.track_time;
1159  read_only_ = other.read_only_;
1160  }
1161  return *this;
1162  }
1163 
1164  int file_use_count() const
1165  {
1166  return fileHandle_.use_count();
1167  }
1168 
1169  bool isOpen() const
1170  {
1171  return fileHandle_ != (hid_t)0;
1172  }
1173 
1174  bool isReadOnly() const
1175  {
1176  return read_only_;
1177  }
1178 
1179  void setReadOnly(bool stat=true)
1180  {
1181  read_only_ = stat;
1182  }
1183 
1184  /** \brief Open or create the given file in the given mode and set the group to "/".
1185  If another file is currently open, it is first closed.
1186  */
1187  void open(std::string filePath, OpenMode mode)
1188  {
1189  close();
1190 
1191  std::string errorMessage = "HDF5File.open(): Could not open or create file '" + filePath + "'.";
1192  fileHandle_ = HDF5HandleShared(createFile_(filePath, mode), &H5Fclose, errorMessage.c_str());
1193  cGroupHandle_ = HDF5Handle(openCreateGroup_("/"), &H5Gclose, "HDF5File.open(): Failed to open root group.");
1194  setReadOnly(mode == OpenReadOnly);
1195  }
1196 
1197  /** \brief Close the current file.
1198  */
1199  void close()
1200  {
1201  bool success = cGroupHandle_.close() >= 0 && fileHandle_.close() >= 0;
1202  vigra_postcondition(success, "HDF5File.close() failed.");
1203  }
1204 
1205  /** \brief Change current group to "/".
1206  */
1207  inline void root()
1208  {
1209  std::string message = "HDF5File::root(): Could not open group '/'.";
1210  cGroupHandle_ = HDF5Handle(H5Gopen(fileHandle_, "/", H5P_DEFAULT),&H5Gclose,message.c_str());
1211  }
1212 
1213  /** \brief Change the current group.
1214  Both absolute and relative group names are allowed.
1215  */
1216  inline void cd(std::string groupName)
1217  {
1218  cGroupHandle_ = getGroupHandle(groupName, "HDF5File::cd()");
1219  }
1220 
1221  /** \brief Change the current group to its parent group.
1222  Returns true if successful, false otherwise. If unsuccessful,
1223  the group will not change.
1224  */
1225  inline bool cd_up()
1226  {
1227  std::string groupName = currentGroupName_();
1228 
1229  //do not try to move up if we already in "/"
1230  if(groupName == "/"){
1231  return false;
1232  }
1233 
1234  size_t lastSlash = groupName.find_last_of('/');
1235 
1236  std::string parentGroup (groupName.begin(), groupName.begin()+lastSlash+1);
1237 
1238  cd(parentGroup);
1239 
1240  return true;
1241  }
1242 
1243  /** \brief Change the current group to its parent group.
1244  Returns true if successful, false otherwise. If unsuccessful,
1245  the group will not change.
1246  */
1247  inline bool cd_up(int levels)
1248  {
1249  std::string groupName = currentGroupName_();
1250 
1251  for(int i = 0; i<levels; i++)
1252  {
1253  if(!cd_up())
1254  {
1255  // restore old group if neccessary
1256  if(groupName != currentGroupName_())
1257  cd(groupName);
1258  return false;
1259  }
1260  }
1261  return true;
1262  }
1263 
1264  /** \brief Create a new group.
1265  If the first character is a "/", the path will be interpreted as absolute path,
1266  otherwise it will be interpreted as path relative to the current group.
1267  */
1268  inline void mkdir(std::string groupName)
1269  {
1270  vigra_precondition(!isReadOnly(),
1271  "HDF5File::mkdir(): file is read-only.");
1272 
1273  std::string message = "HDF5File::mkdir(): Could not create group '" + groupName + "'.\n";
1274 
1275  // make groupName clean
1276  groupName = get_absolute_path(groupName);
1277 
1278  HDF5Handle(openCreateGroup_(groupName.c_str()),&H5Gclose,message.c_str());
1279  }
1280 
1281  /** \brief Change the current group; create it if necessary.
1282  If the first character is a "/", the path will be interpreted as absolute path,
1283  otherwise it will be interpreted as path relative to the current group.
1284  */
1285  inline void cd_mk(std::string groupName)
1286  {
1287  vigra_precondition(!isReadOnly(),
1288  "HDF5File::cd_mk(): file is read-only.");
1289 
1290  std::string message = "HDF5File::cd_mk(): Could not create group '" + groupName + "'.";
1291 
1292  // make groupName clean
1293  groupName = get_absolute_path(groupName);
1294 
1295  cGroupHandle_ = HDF5Handle(openCreateGroup_(groupName.c_str()),&H5Gclose,message.c_str());
1296  }
1297 
1298  // helper function for the various ls() variants.
1299  void ls_H5Literate(ls_closure & data) const
1300  {
1301  H5Literate(cGroupHandle_, H5_INDEX_NAME, H5_ITER_NATIVE, NULL,
1302  HDF5_ls_inserter_callback, static_cast<void*>(&data));
1303  }
1304 
1305  /** \brief List the contents of the current group.
1306  The function returns a vector of strings holding the entries of the
1307  current group. Only datasets and groups are listed, other objects
1308  (e.g. datatypes) are ignored. Group names always have a trailing "/".
1309  */
1310  inline std::vector<std::string> ls() const
1311  {
1312  std::vector<std::string> list;
1313  lsOpData data(list);
1314  ls_H5Literate(data);
1315  return list;
1316  }
1317 
1318  /** \brief List the contents of the current group into a container-like
1319  object via insert().
1320 
1321  Only datasets and groups are inserted, other objects (e.g., datatypes) are ignored.
1322  Group names always have a trailing "/".
1323 
1324  The argument cont is presumably an associative container, however,
1325  only its member function <tt>cont.insert(std::string)</tt> will be
1326  called.
1327  \param cont reference to a container supplying a member function
1328  <tt>insert(const i_type &)</tt>, where <tt>i_type</tt>
1329  is convertible to <tt>std::string</tt>.
1330  */
1331  template<class Container>
1332  void ls(Container & cont) const
1333  {
1334  ls_container_data<Container> data(cont);
1335  ls_H5Literate(data);
1336  }
1337 
1338  /** \brief Get the path of the current group.
1339  */
1340  inline std::string pwd() const
1341  {
1342  return currentGroupName_();
1343  }
1344 
1345  /** \brief Get the name of the associated file.
1346  */
1347  inline std::string filename() const
1348  {
1349  return fileName_();
1350  }
1351 
1352  /** \brief Check if given datasetName exists.
1353  */
1354  inline bool existsDataset(std::string datasetName) const
1355  {
1356  // make datasetName clean
1357  datasetName = get_absolute_path(datasetName);
1358  return (H5Lexists(fileHandle_, datasetName.c_str(), H5P_DEFAULT) > 0);
1359  }
1360 
1361  /** \brief Get the number of dimensions of a certain dataset
1362  If the first character is a "/", the path will be interpreted as absolute path,
1363  otherwise it will be interpreted as path relative to the current group.
1364  */
1365  hssize_t getDatasetDimensions(std::string datasetName) const
1366  {
1367  HDF5Handle datasetHandle = getDatasetHandle(datasetName);
1368 
1369  return getDatasetDimensions_(datasetHandle);
1370  }
1371 
1372  hssize_t getDatasetDimensions_(hid_t dataset) const
1373  {
1374  std::string errorMessage = "HDF5File::getDatasetDimensions(): Unable to access dataspace.";
1375  HDF5Handle dataspaceHandle(H5Dget_space(dataset), &H5Sclose, errorMessage.c_str());
1376 
1377  //return dimension information
1378  return H5Sget_simple_extent_ndims(dataspaceHandle);
1379  }
1380 
1381  /** \brief Get the shape of each dimension of a certain dataset.
1382 
1383  Normally, this function is called after determining the dimension of the
1384  dataset using \ref getDatasetDimensions().
1385  If the first character is a "/", the path will be interpreted as absolute path,
1386  otherwise it will be interpreted as path relative to the current group.
1387 
1388  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1389  Fortran-order, while HDF5 uses C-order. This function therefore reverses the axis
1390  order relative to the file contents. That is, when the axes in the file are
1391  ordered as 'z', 'y', 'x', this function will return the shape in the order
1392  'x', 'y', 'z'.
1393  */
1394  ArrayVector<hsize_t> getDatasetShape(std::string datasetName) const
1395  {
1396  // make datasetName clean
1397  datasetName = get_absolute_path(datasetName);
1398 
1399  //Open dataset and dataspace
1400  std::string errorMessage = "HDF5File::getDatasetShape(): Unable to open dataset '" + datasetName + "'.";
1401  HDF5Handle datasetHandle = HDF5Handle(getDatasetHandle_(datasetName), &H5Dclose, errorMessage.c_str());
1402 
1403  errorMessage = "HDF5File::getDatasetShape(): Unable to access dataspace.";
1404  HDF5Handle dataspaceHandle(H5Dget_space(datasetHandle), &H5Sclose, errorMessage.c_str());
1405  //get dimension information
1406  ArrayVector<hsize_t>::size_type dimensions = H5Sget_simple_extent_ndims(dataspaceHandle);
1407 
1408  ArrayVector<hsize_t> shape(dimensions);
1409  ArrayVector<hsize_t> maxdims(dimensions);
1410  H5Sget_simple_extent_dims(dataspaceHandle, shape.data(), maxdims.data());
1411 
1412  // invert the dimensions to guarantee VIGRA-compatible order.
1413  std::reverse(shape.begin(), shape.end());
1414  return shape;
1415  }
1416 
1417  /** \brief Get the shape of chunks along each dimension of a certain dataset.
1418 
1419  Normally, this function is called after determining the dimension of the
1420  dataset using \ref getDatasetDimensions().
1421  If the first character is a "/", the path will be interpreted as absolute path,
1422  otherwise it will be interpreted as path relative to the current group.
1423 
1424  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1425  Fortran-order, while HDF5 uses C-order. This function therefore reverses the axis
1426  order relative to the file contents. That is, when the axes in the file are
1427  ordered as 'z', 'y', 'x', this function will return the shape in the order
1428  'x', 'y', 'z'.
1429  */
1430  ArrayVector<hsize_t> getChunkShape(std::string datasetName) const
1431  {
1432  // make datasetName clean
1433  datasetName = get_absolute_path(datasetName);
1434 
1435  //Open dataset and dataspace
1436  std::string errorMessage = "HDF5File::getChunkShape(): Unable to open dataset '" + datasetName + "'.";
1437  HDF5Handle datasetHandle = HDF5Handle(getDatasetHandle_(datasetName), &H5Dclose, errorMessage.c_str());
1438 
1439  errorMessage = "HDF5File::getChunkShape(): Unable to access dataspace.";
1440  HDF5Handle dataspaceHandle(H5Dget_space(datasetHandle), &H5Sclose, errorMessage.c_str());
1441  HDF5Handle properties(H5Dget_create_plist(datasetHandle),
1442  &H5Pclose, "HDF5File::read(): failed to get property list");
1443 
1444 
1445  //get dimension information
1446  ArrayVector<hsize_t>::size_type dimensions = H5Sget_simple_extent_ndims(dataspaceHandle);
1447 
1448  ArrayVector<hsize_t> shape(dimensions);
1449  H5Pget_chunk(properties, dimensions, shape.data());
1450 
1451  // invert the dimensions to guarantee VIGRA-compatible order.
1452  std::reverse(shape.begin(), shape.end());
1453  return shape;
1454  }
1455 
1456  /** Query the pixel type of the dataset.
1457 
1458  Possible values are:
1459  <DL>
1460  <DT>"INT8"<DD> 8-bit signed integer (unsigned char)
1461  <DT>"UINT8"<DD> 8-bit unsigned integer (unsigned char)
1462  <DT>"INT16"<DD> 16-bit signed integer (short)
1463  <DT>"UINT16"<DD> 16-bit unsigned integer (unsigned short)
1464  <DT>"INT32"<DD> 32-bit signed integer (long)
1465  <DT>"UINT32"<DD> 32-bit unsigned integer (unsigned long)
1466  <DT>"INT64"<DD> 64-bit signed integer (long long)
1467  <DT>"UINT64"<DD> 64-bit unsigned integer (unsigned long long)
1468  <DT>"FLOAT"<DD> 32-bit floating point (float)
1469  <DT>"DOUBLE"<DD> 64-bit floating point (double)
1470  <DT>"UNKNOWN"<DD> any other type
1471  </DL>
1472  */
1473  std::string getDatasetType(std::string const & datasetName) const
1474  {
1475  HDF5Handle datasetHandle = getDatasetHandle(datasetName);
1476 
1477  hid_t datatype = H5Dget_type(datasetHandle);
1478  H5T_class_t dataclass = H5Tget_class(datatype);
1479  size_t datasize = H5Tget_size(datatype);
1480  H5T_sign_t datasign = H5Tget_sign(datatype);
1481 
1482  if(dataclass == H5T_FLOAT)
1483  {
1484  if(datasize == 4)
1485  return "FLOAT";
1486  else if(datasize == 8)
1487  return "DOUBLE";
1488  }
1489  else if(dataclass == H5T_INTEGER)
1490  {
1491  if(datasign == H5T_SGN_NONE)
1492  {
1493  if(datasize == 1)
1494  return "UINT8";
1495  else if(datasize == 2)
1496  return "UINT16";
1497  else if(datasize == 4)
1498  return "UINT32";
1499  else if(datasize == 8)
1500  return "UINT64";
1501  }
1502  else
1503  {
1504  if(datasize == 1)
1505  return "INT8";
1506  else if(datasize == 2)
1507  return "INT16";
1508  else if(datasize == 4)
1509  return "INT32";
1510  else if(datasize == 8)
1511  return "INT64";
1512  }
1513  }
1514  return "UNKNOWN";
1515  }
1516 
1517  /** \brief Obtain the HDF5 handle of a dataset.
1518  */
1519  HDF5Handle getDatasetHandle(std::string const & datasetName) const
1520  {
1521  std::string errorMessage = "HDF5File::getDatasetHandle(): Unable to open dataset '" + datasetName + "'.";
1522  return HDF5Handle(getDatasetHandle_(get_absolute_path(datasetName)), &H5Dclose, errorMessage.c_str());
1523  }
1524 
1525  /** \brief Obtain a shared HDF5 handle of a dataset.
1526  */
1527  HDF5HandleShared getDatasetHandleShared(std::string const & datasetName) const
1528  {
1529  std::string errorMessage = "HDF5File::getDatasetHandle(): Unable to open dataset '" + datasetName + "'.";
1530  return HDF5HandleShared(getDatasetHandle_(get_absolute_path(datasetName)), &H5Dclose, errorMessage.c_str());
1531  }
1532 
1533  /** \brief Obtain the HDF5 handle of a group (create the group if it doesn't exist).
1534  */
1535  HDF5Handle getGroupHandle(std::string group_name,
1536  std::string function_name = "HDF5File::getGroupHandle()")
1537  {
1538  std::string errorMessage = function_name + ": Group '" + group_name + "' not found.";
1539 
1540  // make group_name clean
1541  group_name = get_absolute_path(group_name);
1542 
1543  // group must exist
1544  vigra_precondition(group_name == "/" || H5Lexists(fileHandle_, group_name.c_str(), H5P_DEFAULT) != 0,
1545  errorMessage.c_str());
1546 
1547  // open group and return group handle
1548  return HDF5Handle(openCreateGroup_(group_name), &H5Gclose, "Internal error");
1549  }
1550 
1551  // helper function for the various listAttributes() variants.
1552  void ls_H5Aiterate(std::string const & group_or_dataset, ls_closure & data) const
1553  {
1554  H5O_type_t h5_type = get_object_type_(group_or_dataset);
1555  vigra_precondition(h5_type == H5O_TYPE_GROUP || h5_type == H5O_TYPE_DATASET,
1556  "HDF5File::listAttributes(): object \"" + group_or_dataset + "\" is neither a group nor a dataset.");
1557  // get object handle
1558  HDF5Handle object_handle(h5_type == H5O_TYPE_GROUP
1559  ? const_cast<HDF5File*>(this)->openCreateGroup_(group_or_dataset)
1560  : getDatasetHandle_(group_or_dataset),
1561  h5_type == H5O_TYPE_GROUP
1562  ? &H5Gclose
1563  : &H5Dclose,
1564  "HDF5File::listAttributes(): unable to open object.");
1565  hsize_t n = 0;
1566  H5Aiterate2(object_handle, H5_INDEX_NAME, H5_ITER_NATIVE, &n,
1567  HDF5_listAttributes_inserter_callback, static_cast<void*>(&data));
1568  }
1569 
1570  /** \brief List the attribute names of the given group or dataset.
1571 
1572  If \a group_or_dataset is empty or <tt>"."</tt> (a dot), the command
1573  refers to the current group of this file object.
1574  */
1575  inline std::vector<std::string> listAttributes(std::string const & group_or_dataset) const
1576  {
1577  std::vector<std::string> list;
1578  lsOpData data(list);
1579  ls_H5Aiterate(group_or_dataset, data);
1580  return list;
1581  }
1582 
1583  /** \brief Insert the attribute names of the given group or dataset into the given
1584  \a container by calling <tt>container.insert(std::string)</tt>.
1585 
1586  If \a group_or_dataset is empty or <tt>"."</tt> (a dot), the command
1587  refers to the current group of this file object.
1588  */
1589  template<class Container>
1590  void listAttributes(std::string const & group_or_dataset, Container & container) const
1591  {
1592  ls_container_data<Container> data(container);
1593  ls_H5Aiterate(group_or_dataset, data);
1594  }
1595 
1596  /** \brief Obtain the HDF5 handle of a attribute.
1597  */
1598  HDF5Handle getAttributeHandle(std::string dataset_name, std::string attribute_name) const
1599  {
1600  std::string message = "HDF5File::getAttributeHandle(): Attribute '" + attribute_name + "' not found.";
1601  return HDF5Handle(H5Aopen(getDatasetHandle(dataset_name), attribute_name.c_str(), H5P_DEFAULT),
1602  &H5Aclose, message.c_str());
1603  }
1604 
1605  /* Writing Attributes */
1606 
1607  /** \brief Write MultiArray Attributes.
1608  * In contrast to datasets, subarray access, chunks and compression are not available.
1609  */
1610  template<unsigned int N, class T, class Stride>
1611  inline void writeAttribute(std::string object_name,
1612  std::string attribute_name,
1613  const MultiArrayView<N, T, Stride> & array)
1614  {
1615  // make object_name clean
1616  object_name = get_absolute_path(object_name);
1617 
1618  write_attribute_(object_name, attribute_name, array, detail::getH5DataType<T>(), 1);
1619  }
1620 
1621  template<unsigned int N, class T, int SIZE, class Stride>
1622  inline void writeAttribute(std::string datasetName,
1623  std::string attributeName,
1624  const MultiArrayView<N, TinyVector<T, SIZE>, Stride> & array)
1625  {
1626  // make datasetName clean
1627  datasetName = get_absolute_path(datasetName);
1628 
1629  write_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), SIZE);
1630  }
1631 
1632  template<unsigned int N, class T, class Stride>
1633  inline void writeAttribute(std::string datasetName,
1634  std::string attributeName,
1635  const MultiArrayView<N, RGBValue<T>, Stride> & array)
1636  {
1637  // make datasetName clean
1638  datasetName = get_absolute_path(datasetName);
1639 
1640  write_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), 3);
1641  }
1642 
1643  /** \brief Write a single value.
1644  Specialization of the write function for simple datatypes
1645  */
1646  inline void writeAttribute(std::string object_name, std::string attribute_name, char data)
1647  { writeAtomicAttribute(object_name,attribute_name,data); }
1648  inline void writeAttribute(std::string datasetName, std::string attributeName, signed char data)
1649  { writeAtomicAttribute(datasetName,attributeName,data); }
1650  inline void writeAttribute(std::string datasetName, std::string attributeName, signed short data)
1651  { writeAtomicAttribute(datasetName,attributeName,data); }
1652  inline void writeAttribute(std::string datasetName, std::string attributeName, signed int data)
1653  { writeAtomicAttribute(datasetName,attributeName,data); }
1654  inline void writeAttribute(std::string datasetName, std::string attributeName, signed long data)
1655  { writeAtomicAttribute(datasetName,attributeName,data); }
1656  inline void writeAttribute(std::string datasetName, std::string attributeName, signed long long data)
1657  { writeAtomicAttribute(datasetName,attributeName,data); }
1658  inline void writeAttribute(std::string datasetName, std::string attributeName, unsigned char data)
1659  { writeAtomicAttribute(datasetName,attributeName,data); }
1660  inline void writeAttribute(std::string datasetName, std::string attributeName, unsigned short data)
1661  { writeAtomicAttribute(datasetName,attributeName,data); }
1662  inline void writeAttribute(std::string datasetName, std::string attributeName, unsigned int data)
1663  { writeAtomicAttribute(datasetName,attributeName,data); }
1664  inline void writeAttribute(std::string datasetName, std::string attributeName, unsigned long data)
1665  { writeAtomicAttribute(datasetName,attributeName,data); }
1666  inline void writeAttribute(std::string datasetName, std::string attributeName, unsigned long long data)
1667  { writeAtomicAttribute(datasetName,attributeName,data); }
1668  inline void writeAttribute(std::string datasetName, std::string attributeName, float data)
1669  { writeAtomicAttribute(datasetName,attributeName,data); }
1670  inline void writeAttribute(std::string datasetName, std::string attributeName, double data)
1671  { writeAtomicAttribute(datasetName,attributeName,data); }
1672  inline void writeAttribute(std::string datasetName, std::string attributeName, long double data)
1673  { writeAtomicAttribute(datasetName,attributeName,data); }
1674  inline void writeAttribute(std::string datasetName, std::string attributeName, const char* data)
1675  { writeAtomicAttribute(datasetName,attributeName,data); }
1676  inline void writeAttribute(std::string datasetName, std::string attributeName, std::string const & data)
1677  { writeAtomicAttribute(datasetName,attributeName,data.c_str()); }
1678 
1679  /** \brief Test if attribute exists.
1680  */
1681  bool existsAttribute(std::string object_name, std::string attribute_name)
1682  {
1683  std::string obj_path = get_absolute_path(object_name);
1684  htri_t exists = H5Aexists_by_name(fileHandle_, obj_path.c_str(),
1685  attribute_name.c_str(), H5P_DEFAULT);
1686  vigra_precondition(exists >= 0, "HDF5File::existsAttribute(): "
1687  "object '" + object_name + "' "
1688  "not found.");
1689  return exists != 0;
1690  }
1691 
1692  // Reading Attributes
1693 
1694  /** \brief Read MultiArray Attributes.
1695  * In contrast to datasets, subarray access is not available.
1696  */
1697  template<unsigned int N, class T, class Stride>
1698  inline void readAttribute(std::string object_name,
1699  std::string attribute_name,
1701  {
1702  // make object_name clean
1703  object_name = get_absolute_path(object_name);
1704 
1705  read_attribute_(object_name, attribute_name, array, detail::getH5DataType<T>(), 1);
1706  }
1707 
1708  template<unsigned int N, class T, int SIZE, class Stride>
1709  inline void readAttribute(std::string datasetName,
1710  std::string attributeName,
1711  MultiArrayView<N, TinyVector<T, SIZE>, Stride> array)
1712  {
1713  // make datasetName clean
1714  datasetName = get_absolute_path(datasetName);
1715 
1716  read_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), SIZE);
1717  }
1718 
1719  template<unsigned int N, class T, class Stride>
1720  inline void readAttribute(std::string datasetName,
1721  std::string attributeName,
1722  MultiArrayView<N, RGBValue<T>, Stride> array)
1723  {
1724  // make datasetName clean
1725  datasetName = get_absolute_path(datasetName);
1726 
1727  read_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), 3);
1728  }
1729 
1730  /** \brief Read a single value.
1731  Specialization of the read function for simple datatypes
1732  */
1733  inline void readAttribute(std::string object_name, std::string attribute_name, char &data)
1734  { readAtomicAttribute(object_name,attribute_name,data); }
1735  inline void readAttribute(std::string datasetName, std::string attributeName, signed char &data)
1736  { readAtomicAttribute(datasetName,attributeName,data); }
1737  inline void readAttribute(std::string datasetName, std::string attributeName, signed short &data)
1738  { readAtomicAttribute(datasetName,attributeName,data); }
1739  inline void readAttribute(std::string datasetName, std::string attributeName, signed int &data)
1740  { readAtomicAttribute(datasetName,attributeName,data); }
1741  inline void readAttribute(std::string datasetName, std::string attributeName, signed long &data)
1742  { readAtomicAttribute(datasetName,attributeName,data); }
1743  inline void readAttribute(std::string datasetName, std::string attributeName, signed long long &data)
1744  { readAtomicAttribute(datasetName,attributeName,data); }
1745  inline void readAttribute(std::string datasetName, std::string attributeName, unsigned char &data)
1746  { readAtomicAttribute(datasetName,attributeName,data); }
1747  inline void readAttribute(std::string datasetName, std::string attributeName, unsigned short &data)
1748  { readAtomicAttribute(datasetName,attributeName,data); }
1749  inline void readAttribute(std::string datasetName, std::string attributeName, unsigned int &data)
1750  { readAtomicAttribute(datasetName,attributeName,data); }
1751  inline void readAttribute(std::string datasetName, std::string attributeName, unsigned long &data)
1752  { readAtomicAttribute(datasetName,attributeName,data); }
1753  inline void readAttribute(std::string datasetName, std::string attributeName, unsigned long long &data)
1754  { readAtomicAttribute(datasetName,attributeName,data); }
1755  inline void readAttribute(std::string datasetName, std::string attributeName, float &data)
1756  { readAtomicAttribute(datasetName,attributeName,data); }
1757  inline void readAttribute(std::string datasetName, std::string attributeName, double &data)
1758  { readAtomicAttribute(datasetName,attributeName,data); }
1759  inline void readAttribute(std::string datasetName, std::string attributeName, long double &data)
1760  { readAtomicAttribute(datasetName,attributeName,data); }
1761  inline void readAttribute(std::string datasetName, std::string attributeName, std::string &data)
1762  { readAtomicAttribute(datasetName,attributeName,data); }
1763 
1764  // Writing data
1765 
1766  /** \brief Write multi arrays.
1767 
1768  Chunks can be activated by setting
1769  \code iChunkSize = size; //size > 0
1770  \endcode .
1771  The chunks will be hypercubes with edge length size. When <tt>iChunkSize == 0</tt>
1772  (default), the behavior depends on the <tt>compression</tt> setting: If no
1773  compression is requested, the data is written without chunking. Otherwise,
1774  chuning is required, and the chunk size is automatically selected such that
1775  each chunk contains about 300k pixels.
1776 
1777  Compression can be activated by setting
1778  \code compression = parameter; // 0 < parameter <= 9
1779  \endcode
1780  where 0 stands for no compression and 9 for maximum compression.
1781 
1782  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1783  otherwise it will be interpreted as path relative to the current group.
1784 
1785  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1786  Fortran-order, while HDF5 uses C-order. This means that a VIGRA MultiArray,
1787  whose indices represent the 'x'-, 'y'-, and 'z'-axis in that order, is reversed
1788  upon writing to an HDF5 file, i.e. in the file the axis order is 'z', 'y', 'x'.
1789  */
1790  template<unsigned int N, class T, class Stride>
1791  inline void write(std::string datasetName,
1792  const MultiArrayView<N, T, Stride> & array,
1793  int iChunkSize = 0, int compression = 0)
1794  {
1795  // make datasetName clean
1796  datasetName = get_absolute_path(datasetName);
1797 
1798  typename MultiArrayShape<N>::type chunkSize;
1799  for(unsigned int i = 0; i < N; i++){
1800  chunkSize[i] = iChunkSize;
1801  }
1802  write_(datasetName, array, detail::getH5DataType<T>(), 1, chunkSize, compression);
1803  }
1804 
1805  /** \brief Write multi arrays.
1806  Chunks can be activated by providing a MultiArrayShape as chunkSize.
1807  chunkSize must have equal dimension as array.
1808 
1809  Compression can be activated by setting
1810  \code compression = parameter; // 0 < parameter <= 9
1811  \endcode
1812  where 0 stands for no compression and 9 for maximum compression.
1813 
1814  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1815  otherwise it will be interpreted as path relative to the current group.
1816 
1817  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1818  Fortran-order, while HDF5 uses C-order. This means that a VIGRA MultiArray,
1819  whose indices represent the 'x'-, 'y'-, and 'z'-axis in that order, is reversed
1820  upon writing to an HDF5 file, i.e. in the file the axis order is 'z', 'y', 'x'.
1821  */
1822  template<unsigned int N, class T, class Stride>
1823  inline void write(std::string datasetName,
1824  const MultiArrayView<N, T, Stride> & array,
1825  typename MultiArrayShape<N>::type chunkSize, int compression = 0)
1826  {
1827  // make datasetName clean
1828  datasetName = get_absolute_path(datasetName);
1829 
1830  write_(datasetName, array, detail::getH5DataType<T>(), 1, chunkSize, compression);
1831  }
1832 
1833  /** \brief Write a multi array into a larger volume.
1834  blockOffset determines the position, where array is written.
1835 
1836  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1837  otherwise it will be interpreted as path relative to the current group.
1838 
1839  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1840  Fortran-order, while HDF5 uses C-order. This means that a VIGRA MultiArray,
1841  whose indices represent the 'x'-, 'y'-, and 'z'-axis in that order, is reversed
1842  upon writing to an HDF5 file, i.e. in the file the axis order is 'z', 'y', 'x'.
1843  */
1844  template<unsigned int N, class T, class Stride>
1845  inline void writeBlock(std::string datasetName,
1846  typename MultiArrayShape<N>::type blockOffset,
1847  const MultiArrayView<N, T, Stride> & array)
1848  {
1849  // make datasetName clean
1850  datasetName = get_absolute_path(datasetName);
1851  typedef detail::HDF5TypeTraits<T> TypeTraits;
1852  writeBlock_(datasetName, blockOffset, array,
1853  TypeTraits::getH5DataType(), TypeTraits::numberOfBands());
1854  }
1855 
1856  template<unsigned int N, class T, class Stride>
1857  inline herr_t writeBlock(HDF5HandleShared dataset,
1858  typename MultiArrayShape<N>::type blockOffset,
1859  const MultiArrayView<N, T, Stride> & array)
1860  {
1861  typedef detail::HDF5TypeTraits<T> TypeTraits;
1862  return writeBlock_(dataset, blockOffset, array,
1863  TypeTraits::getH5DataType(), TypeTraits::numberOfBands());
1864  }
1865 
1866  // non-scalar (TinyVector) and unstrided multi arrays
1867  template<unsigned int N, class T, int SIZE, class Stride>
1868  inline void write(std::string datasetName,
1869  const MultiArrayView<N, TinyVector<T, SIZE>, Stride> & array,
1870  int iChunkSize = 0, int compression = 0)
1871  {
1872  // make datasetName clean
1873  datasetName = get_absolute_path(datasetName);
1874 
1875  typename MultiArrayShape<N>::type chunkSize;
1876  for(unsigned i = 0; i < N; i++){
1877  chunkSize[i] = iChunkSize;
1878  }
1879  write_(datasetName, array, detail::getH5DataType<T>(), SIZE, chunkSize, compression);
1880  }
1881 
1882  template<unsigned int N, class T, int SIZE, class Stride>
1883  inline void write(std::string datasetName,
1884  const MultiArrayView<N, TinyVector<T, SIZE>, Stride> & array,
1885  typename MultiArrayShape<N>::type chunkSize, int compression = 0)
1886  {
1887  // make datasetName clean
1888  datasetName = get_absolute_path(datasetName);
1889 
1890  write_(datasetName, array, detail::getH5DataType<T>(), SIZE, chunkSize, compression);
1891  }
1892 
1893  /** \brief Write array vectors.
1894 
1895  Compression can be activated by setting
1896  \code compression = parameter; // 0 < parameter <= 9
1897  \endcode
1898  where 0 stands for no compression and 9 for maximum compression.
1899 
1900  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1901  otherwise it will be interpreted as path relative to the current group.
1902  */
1903  template<class T>
1904  void write(const std::string & datasetName,
1905  const ArrayVectorView<T> & array,
1906  int compression = 0)
1907  {
1908  // convert to a (trivial) MultiArrayView and forward.
1909  MultiArrayShape<1>::type shape(static_cast<MultiArrayIndex>(array.size()));
1910  const MultiArrayView<1, T> m_array(shape, const_cast<T*>(array.data()));
1911  write(datasetName, m_array, compression);
1912  }
1913 
1914  // non-scalar (RGBValue) and unstrided multi arrays
1915  template<unsigned int N, class T, class Stride>
1916  inline void write(std::string datasetName,
1917  const MultiArrayView<N, RGBValue<T>, Stride> & array,
1918  int iChunkSize = 0, int compression = 0)
1919  {
1920  // make datasetName clean
1921  datasetName = get_absolute_path(datasetName);
1922 
1923  typename MultiArrayShape<N>::type chunkSize;
1924  for(unsigned i = 0; i < N; i++){
1925  chunkSize[i] = iChunkSize;
1926  }
1927  write_(datasetName, array, detail::getH5DataType<T>(), 3, chunkSize, compression);
1928  }
1929 
1930  template<unsigned int N, class T, class Stride>
1931  inline void write(std::string datasetName,
1932  const MultiArrayView<N, RGBValue<T>, Stride> & array,
1933  typename MultiArrayShape<N>::type chunkSize, int compression = 0)
1934  {
1935  // make datasetName clean
1936  datasetName = get_absolute_path(datasetName);
1937 
1938  write_(datasetName, array, detail::getH5DataType<T>(), 3, chunkSize, compression);
1939  }
1940 
1941  /** \brief Write a single value.
1942  Specialization of the write function for simple datatypes
1943  */
1944  inline void write(std::string datasetName, char data) { writeAtomic(datasetName,data); }
1945  inline void write(std::string datasetName, signed char data) { writeAtomic(datasetName,data); }
1946  inline void write(std::string datasetName, signed short data) { writeAtomic(datasetName,data); }
1947  inline void write(std::string datasetName, signed int data) { writeAtomic(datasetName,data); }
1948  inline void write(std::string datasetName, signed long data) { writeAtomic(datasetName,data); }
1949  inline void write(std::string datasetName, signed long long data) { writeAtomic(datasetName,data); }
1950  inline void write(std::string datasetName, unsigned char data) { writeAtomic(datasetName,data); }
1951  inline void write(std::string datasetName, unsigned short data) { writeAtomic(datasetName,data); }
1952  inline void write(std::string datasetName, unsigned int data) { writeAtomic(datasetName,data); }
1953  inline void write(std::string datasetName, unsigned long data) { writeAtomic(datasetName,data); }
1954  inline void write(std::string datasetName, unsigned long long data) { writeAtomic(datasetName,data); }
1955  inline void write(std::string datasetName, float data) { writeAtomic(datasetName,data); }
1956  inline void write(std::string datasetName, double data) { writeAtomic(datasetName,data); }
1957  inline void write(std::string datasetName, long double data) { writeAtomic(datasetName,data); }
1958  inline void write(std::string datasetName, const char* data) { writeAtomic(datasetName,data); }
1959  inline void write(std::string datasetName, std::string const & data) { writeAtomic(datasetName,data.c_str()); }
1960 
1961  // Reading data
1962 
1963  /** \brief Read data into a multi array.
1964  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1965  otherwise it will be interpreted as path relative to the current group.
1966 
1967  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1968  Fortran-order, while HDF5 uses C-order. This means that a HDF5 dataset,
1969  whose indices represent the 'z'-, 'y'-, and 'x'-axis in that order, is reversed
1970  upon reading into a MultiArrayView, i.e. in the array axis order must be 'x', 'y', 'z'.
1971  */
1972  template<unsigned int N, class T, class Stride>
1973  inline void read(std::string datasetName, MultiArrayView<N, T, Stride> array)
1974  {
1975  // make datasetName clean
1976  datasetName = get_absolute_path(datasetName);
1977 
1978  read_(datasetName, array, detail::getH5DataType<T>(), 1);
1979  }
1980 
1981  /** \brief Read data into a MultiArray. Resize MultiArray to the correct size.
1982  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
1983  otherwise it will be interpreted as path relative to the current group.
1984 
1985  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
1986  Fortran-order, while HDF5 uses C-order. This means that a HDF5 dataset,
1987  whose indices represent the 'z'-, 'y'-, and 'x'-axis in that order, is reversed
1988  upon reading into a MultiArray, i.e. in the array axis order will be 'x', 'y', 'z'.
1989  */
1990  template<unsigned int N, class T, class Alloc>
1991  inline void readAndResize(std::string datasetName, MultiArray<N, T, Alloc> & array)
1992  {
1993  // make datasetName clean
1994  datasetName = get_absolute_path(datasetName);
1995 
1996  // get dataset dimension
1997  ArrayVector<hsize_t> dimshape = getDatasetShape(datasetName);
1998 
1999  // check if dimensions are correct
2000  vigra_precondition(N == MultiArrayIndex(dimshape.size()), // the object in the HDF5 file may have one additional dimension which we then interpret as the pixel type bands
2001  "HDF5File::readAndResize(): Array dimension disagrees with dataset dimension.");
2002 
2003  // reshape target MultiArray
2004  typename MultiArrayShape<N>::type shape;
2005  for(int k=0; k < static_cast<int>(dimshape.size()); ++k)
2006  shape[k] = static_cast<MultiArrayIndex>(dimshape[k]);
2007  array.reshape(shape);
2008 
2009  read_(datasetName, array, detail::getH5DataType<T>(), 1);
2010  }
2011 
2012  /** \brief Read data into an array vector.
2013  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2014  otherwise it will be interpreted as path relative to the current group.
2015  */
2016  template<class T>
2017  inline void read(const std::string & datasetName, ArrayVectorView<T> array)
2018  {
2019  // convert to a (trivial) MultiArrayView and forward.
2020  MultiArrayShape<1>::type shape(array.size());
2021  MultiArrayView<1, T> m_array(shape, (array.data()));
2022  read(datasetName, m_array);
2023  }
2024 
2025  /** \brief Read data into an array vector. Resize the array vector to the correct size.
2026  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2027  otherwise it will be interpreted as path relative to the current group.
2028  */
2029  template<class T>
2030  inline void readAndResize(std::string datasetName,
2031  ArrayVector<T> & array)
2032  {
2033  // make dataset name clean
2034  datasetName = get_absolute_path(datasetName);
2035 
2036  // get dataset dimension
2037  ArrayVector<hsize_t> dimshape = getDatasetShape(datasetName);
2038 
2039  // check if dimensions are correct
2040  vigra_precondition(1 == MultiArrayIndex(dimshape.size()),
2041  "HDF5File::readAndResize(): Array dimension disagrees with Dataset dimension must equal one for vigra::ArrayVector.");
2042 
2043  // resize target array vector
2044  array.resize((typename ArrayVector<T>::size_type)dimshape[0]);
2045  // convert to a (trivial) MultiArrayView and forward.
2046  MultiArrayShape<1>::type shape(static_cast<MultiArrayIndex>(array.size()));
2047  MultiArrayView<1, T> m_array(shape, (array.data()));
2048 
2049  read_(datasetName, m_array, detail::getH5DataType<T>(), 1);
2050  }
2051 
2052  /** \brief Read a block of data into a multi array.
2053  This function allows to read a small block out of a larger volume stored
2054  in an HDF5 dataset.
2055 
2056  blockOffset determines the position of the block.
2057  blockSize determines the size in each dimension of the block.
2058 
2059  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2060  otherwise it will be interpreted as path relative to the current group.
2061 
2062  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
2063  Fortran-order, while HDF5 uses C-order. This means that a HDF5 dataset,
2064  whose indices represent the 'z'-, 'y'-, and 'x'-axis in that order, is reversed
2065  upon reading into a MultiArray, i.e. in the array axis order will be 'x', 'y', 'z'.
2066  */
2067  template<unsigned int N, class T, class Stride>
2068  inline void readBlock(std::string datasetName,
2069  typename MultiArrayShape<N>::type blockOffset,
2070  typename MultiArrayShape<N>::type blockShape,
2072  {
2073  // make datasetName clean
2074  datasetName = get_absolute_path(datasetName);
2075  typedef detail::HDF5TypeTraits<T> TypeTraits;
2076  readBlock_(datasetName, blockOffset, blockShape, array,
2077  TypeTraits::getH5DataType(), TypeTraits::numberOfBands());
2078  }
2079 
2080  template<unsigned int N, class T, class Stride>
2081  inline herr_t readBlock(HDF5HandleShared dataset,
2082  typename MultiArrayShape<N>::type blockOffset,
2083  typename MultiArrayShape<N>::type blockShape,
2085  {
2086  typedef detail::HDF5TypeTraits<T> TypeTraits;
2087  return readBlock_(dataset, blockOffset, blockShape, array,
2088  TypeTraits::getH5DataType(), TypeTraits::numberOfBands());
2089  }
2090 
2091  // non-scalar (TinyVector) and unstrided target MultiArrayView
2092  template<unsigned int N, class T, int SIZE, class Stride>
2093  inline void read(std::string datasetName, MultiArrayView<N, TinyVector<T, SIZE>, Stride> array)
2094  {
2095  // make datasetName clean
2096  datasetName = get_absolute_path(datasetName);
2097 
2098  read_(datasetName, array, detail::getH5DataType<T>(), SIZE);
2099  }
2100 
2101  // non-scalar (TinyVector) MultiArray
2102  template<unsigned int N, class T, int SIZE, class Alloc>
2103  inline void readAndResize(std::string datasetName, MultiArray<N, TinyVector<T, SIZE>, Alloc> & array)
2104  {
2105  // make datasetName clean
2106  datasetName = get_absolute_path(datasetName);
2107 
2108  // get dataset dimension
2109  ArrayVector<hsize_t> dimshape = getDatasetShape(datasetName);
2110 
2111  // check if dimensions are correct
2112  vigra_precondition((N+1) == MultiArrayIndex(dimshape.size()) &&
2113  SIZE == dimshape[0], // the object in the HDF5 file must have one additional dimension which we interpret as the pixel type bands
2114  "HDF5File::readAndResize(): Array dimension disagrees with dataset dimension.");
2115 
2116  // reshape target MultiArray
2117  typename MultiArrayShape<N>::type shape;
2118  for(int k=1; k < static_cast<int>(dimshape.size()); ++k)
2119  shape[k-1] = static_cast<MultiArrayIndex>(dimshape[k]);
2120  array.reshape(shape);
2121 
2122  read_(datasetName, array, detail::getH5DataType<T>(), SIZE);
2123  }
2124 
2125  // non-scalar (RGBValue) and unstrided target MultiArrayView
2126  template<unsigned int N, class T, class Stride>
2127  inline void read(std::string datasetName, MultiArrayView<N, RGBValue<T>, Stride> array)
2128  {
2129  // make datasetName clean
2130  datasetName = get_absolute_path(datasetName);
2131 
2132  read_(datasetName, array, detail::getH5DataType<T>(), 3);
2133  }
2134 
2135  // non-scalar (RGBValue) MultiArray
2136  template<unsigned int N, class T, class Alloc>
2137  inline void readAndResize(std::string datasetName, MultiArray<N, RGBValue<T>, Alloc> & array)
2138  {
2139  // make datasetName clean
2140  datasetName = get_absolute_path(datasetName);
2141 
2142  // get dataset dimension
2143  ArrayVector<hsize_t> dimshape = getDatasetShape(datasetName);
2144 
2145  // check if dimensions are correct
2146  vigra_precondition((N+1) == MultiArrayIndex(dimshape.size()) &&
2147  3 == dimshape[0], // the object in the HDF5 file must have one additional dimension which we interpret as the pixel type bands
2148  "HDF5File::readAndResize(): Array dimension disagrees with dataset dimension.");
2149 
2150  // reshape target MultiArray
2151  typename MultiArrayShape<N>::type shape;
2152  for(int k=1; k < static_cast<int>(dimshape.size()); ++k)
2153  shape[k-1] = static_cast<MultiArrayIndex>(dimshape[k]);
2154  array.reshape(shape);
2155 
2156  read_(datasetName, array, detail::getH5DataType<T>(), 3);
2157  }
2158 
2159  /** \brief Read a single value.
2160  Specialization of the read function for simple datatypes
2161  */
2162  inline void read(std::string datasetName, char &data) { readAtomic(datasetName,data); }
2163  inline void read(std::string datasetName, signed char &data) { readAtomic(datasetName,data); }
2164  inline void read(std::string datasetName, signed short &data) { readAtomic(datasetName,data); }
2165  inline void read(std::string datasetName, signed int &data) { readAtomic(datasetName,data); }
2166  inline void read(std::string datasetName, signed long &data) { readAtomic(datasetName,data); }
2167  inline void read(std::string datasetName, signed long long &data) { readAtomic(datasetName,data); }
2168  inline void read(std::string datasetName, unsigned char &data) { readAtomic(datasetName,data); }
2169  inline void read(std::string datasetName, unsigned short &data) { readAtomic(datasetName,data); }
2170  inline void read(std::string datasetName, unsigned int &data) { readAtomic(datasetName,data); }
2171  inline void read(std::string datasetName, unsigned long &data) { readAtomic(datasetName,data); }
2172  inline void read(std::string datasetName, unsigned long long &data) { readAtomic(datasetName,data); }
2173  inline void read(std::string datasetName, float &data) { readAtomic(datasetName,data); }
2174  inline void read(std::string datasetName, double &data) { readAtomic(datasetName,data); }
2175  inline void read(std::string datasetName, long double &data) { readAtomic(datasetName,data); }
2176  inline void read(std::string datasetName, std::string &data) { readAtomic(datasetName,data); }
2177 
2178  /** \brief Create a new dataset.
2179  This function can be used to create a dataset filled with a default value \a init,
2180  for example before writing data into it using \ref writeBlock().
2181 
2182  shape determines the dimension and the size of the dataset.
2183 
2184  Chunks can be activated by providing a MultiArrayShape as chunkSize.
2185  chunkSize must have equal dimension as array.
2186 
2187  Compression can be activated by setting
2188  \code compression = parameter; // 0 < parameter <= 9
2189  \endcode
2190  where 0 stands for no compression and 9 for maximum compression. If
2191  a non-zero compression level is specified, but the chunk size is zero,
2192  a default chunk size will be chosen (compression always requires chunks).
2193 
2194  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2195  otherwise it will be interpreted as path relative to the current group.
2196 
2197  Note that the memory order between VIGRA and HDF5 files differs: VIGRA uses
2198  Fortran-order, while HDF5 uses C-order. This means that a VIGRA MultiArray,
2199  whose indices represent the 'x'-, 'y'-, and 'z'-axis in that order, is reversed
2200  upon writing to an HDF5 file, i.e. in the file the axis order is 'z', 'y', 'x'.
2201  */
2202  template<int N, class T>
2203  HDF5HandleShared
2204  createDataset(std::string datasetName,
2205  TinyVector<MultiArrayIndex, N> const & shape,
2206  typename detail::HDF5TypeTraits<T>::value_type init =
2207  typename detail::HDF5TypeTraits<T>::value_type(),
2208 #ifdef _MSC_VER
2209  TinyVector<MultiArrayIndex, N> const & chunkSize = TinyVector<MultiArrayIndex, N>(),
2210 #else
2211  TinyVector<MultiArrayIndex, N> const & chunkSize = (TinyVector<MultiArrayIndex, N>()),
2212 #endif
2213  int compressionParameter = 0);
2214 
2215  // for backwards compatibility
2216  template<int N, class T>
2217  HDF5HandleShared
2218  createDataset(std::string datasetName,
2219  TinyVector<MultiArrayIndex, N> const & shape,
2220  T init,
2221  int iChunkSize,
2222  int compressionParameter = 0)
2223  {
2224  typename MultiArrayShape<N>::type chunkSize;
2225  for(int i = 0; i < N; i++){
2226  chunkSize[i] = iChunkSize;
2227  }
2228  return this->template createDataset<N, T>(datasetName, shape, init,
2229  chunkSize, compressionParameter);
2230  }
2231 
2232  /** \brief Immediately write all data to disk
2233  */
2234  inline void flushToDisk()
2235  {
2236  if(fileHandle_)
2237  H5Fflush(fileHandle_, H5F_SCOPE_GLOBAL);
2238  }
2239 
2240  private:
2241 
2242  /* Simple extension of std::string for splitting into two parts
2243  *
2244  * Strings (in particular: file/dataset paths) will be split into two
2245  * parts. The split is made at the last occurrence of the delimiter.
2246  *
2247  * For example, "/path/to/some/file" will be split (delimiter = "/") into
2248  * first() = "/path/to/some" and last() = "file".
2249  */
2250  class SplitString: public std::string {
2251  public:
2252  SplitString(std::string &sstring): std::string(sstring) {};
2253 
2254  // return the part of the string before the delimiter
2255  std::string first(char delimiter = '/')
2256  {
2257  size_t lastPos = find_last_of(delimiter);
2258  if(lastPos == std::string::npos) // delimiter not found --> no first
2259  return "";
2260 
2261  return std::string(begin(), begin()+lastPos+1);
2262  }
2263 
2264  // return the part of the string after the delimiter
2265  std::string last(char delimiter = '/')
2266  {
2267  size_t lastPos = find_last_of(delimiter);
2268  if(lastPos == std::string::npos) // delimiter not found --> only last
2269  return std::string(*this);
2270  return std::string(begin()+lastPos+1, end());
2271  }
2272  };
2273 
2274  template <class Shape>
2275  ArrayVector<hsize_t>
2276  defineChunks(Shape chunks, Shape const & shape, int numBands, int compression = 0)
2277  {
2278  if(prod(chunks) > 0)
2279  {
2280  ArrayVector<hsize_t> res(chunks.begin(), chunks.end());
2281  if(numBands > 1)
2282  res.insert(res.begin(), static_cast<hsize_t>(numBands));
2283  return res;
2284  }
2285  else if(compression > 0)
2286  {
2287  // set default chunks to enable compression
2288  chunks = min(detail::ChunkShape<Shape::static_size>::defaultShape(), shape);
2289  ArrayVector<hsize_t> res(chunks.begin(), chunks.end());
2290  if(numBands > 1)
2291  res.insert(res.begin(), static_cast<hsize_t>(numBands));
2292  return res;
2293  }
2294  else
2295  {
2296  return ArrayVector<hsize_t>();
2297  }
2298  }
2299 
2300  public:
2301 
2302  /** \brief takes any path and converts it into an absolute path
2303  in the current file.
2304 
2305  Elements like "." and ".." are treated as expected.
2306  Links are not supported or resolved.
2307  */
2308  inline std::string get_absolute_path(std::string path) const {
2309  // check for empty input or "." and return the current folder
2310  if(path.length() == 0 || path == "."){
2311  return currentGroupName_();
2312  }
2313 
2314  std::string str;
2315  // convert to absolute path
2316  if(relativePath_(path)){
2317  std::string cname = currentGroupName_();
2318  if (cname == "/")
2319  str = currentGroupName_()+path;
2320  else
2321  str = currentGroupName_()+"/"+path;
2322  }else{
2323  str = path;
2324  }
2325 
2326  // cut out "./"
2327  std::string::size_type startpos = 0;
2328  while(str.find(std::string("./"), startpos) != std::string::npos){
2329  std::string::size_type pos = str.find(std::string("./"), startpos);
2330  startpos = pos+1;
2331  // only cut if "./" is not part of "../" (see below)
2332  if(str.substr(pos-1,3) != "../"){
2333  // cut out part of the string
2334  str = str.substr(0,pos) + str.substr(pos+2,str.length()-pos-2);
2335  startpos = pos;
2336  }
2337  }
2338 
2339  // cut out pairs of "bla/../"
2340  while(str.find(std::string("..")) != std::string::npos){
2341  std::string::size_type pos = str.find(std::string(".."));
2342 
2343  // find first slash after ".."
2344  std::string::size_type end = str.find("/",pos);
2345  if(end != std::string::npos){
2346  // also include slash
2347  end++;
2348  }else{
2349  // no "/" after ".." --> this is a group, add a "/"
2350  str = str + "/";
2351  end = str.length();
2352  }
2353 
2354  // find first slash before ".."
2355  std::string::size_type prev_slash = str.rfind("/",pos);
2356  // if the root slash is the first before ".." --> Error
2357  vigra_invariant(prev_slash != 0 && prev_slash != std::string::npos,
2358  "Error parsing path: "+str);
2359  // find second slash before ".."
2360  std::string::size_type begin = str.rfind("/",prev_slash-1);
2361 
2362  // cut out part of the string
2363  str = str.substr(0,begin+1) + str.substr(end,str.length()-end);
2364  }
2365 
2366  return str;
2367  }
2368 
2369  protected:
2370 
2371  /* checks if the given path is a relative path.
2372  */
2373  inline bool relativePath_(std::string & path) const
2374  {
2375  std::string::size_type pos = path.find('/') ;
2376  if(pos == 0)
2377  return false;
2378 
2379  return true;
2380  }
2381 
2382  /* return the name of the current group
2383  */
2384  inline std::string currentGroupName_() const
2385  {
2386  int len = H5Iget_name(cGroupHandle_,NULL,1000);
2387  ArrayVector<char> name (len+1,0);
2388  H5Iget_name(cGroupHandle_,name.begin(),len+1);
2389 
2390  return std::string(name.begin());
2391  }
2392 
2393  /* return the name of the current file
2394  */
2395  inline std::string fileName_() const
2396  {
2397  int len = H5Fget_name(fileHandle_,NULL,1000);
2398  ArrayVector<char> name (len+1,0);
2399  H5Fget_name(fileHandle_,name.begin(),len+1);
2400 
2401  return std::string(name.begin());
2402  }
2403 
2404  /* create an empty file or open an existing one
2405  */
2406  inline hid_t createFile_(std::string filePath, OpenMode mode = Open)
2407  {
2408  // try to open file
2409  FILE * pFile;
2410  pFile = fopen ( filePath.c_str(), "r" );
2411  hid_t fileId;
2412 
2413  // check if opening was successful (= file exists)
2414  if ( pFile == NULL )
2415  {
2416  vigra_precondition(mode != OpenReadOnly,
2417  "HDF5File::open(): cannot open non-existing file in read-only mode.");
2418  fileId = H5Fcreate(filePath.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
2419  }
2420  else
2421  {
2422  fclose(pFile);
2423  if(mode == OpenReadOnly)
2424  {
2425  fileId = H5Fopen(filePath.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
2426  }
2427  else if(mode == New)
2428  {
2429  std::remove(filePath.c_str());
2430  fileId = H5Fcreate(filePath.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
2431  }
2432  else
2433  {
2434  fileId = H5Fopen(filePath.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
2435  }
2436  }
2437  return fileId;
2438  }
2439 
2440  /* \brief Open a group.
2441 
2442  A negative value is returned when the group does not exist or when opening
2443  fails for other reasons.
2444  */
2445  hid_t openGroup_(std::string groupName) const
2446  {
2447  return const_cast<HDF5File *>(this)->openCreateGroup_(groupName, false);
2448  }
2449 
2450  /* \brief Open or create a group.
2451 
2452  If \a create is <tt>true</tt> and the group does not exist, it will be created,
2453  including all necessary parent groups. If group creation fails, a negative
2454  value is returned. Likewise, a negative value is returned when \a create
2455  is <tt>false</tt> and the group does not exist or when opening of the group
2456  fails for other reasons.
2457  */
2458  hid_t openCreateGroup_(std::string groupName, bool create = true)
2459  {
2460  // make groupName clean
2461  groupName = get_absolute_path(groupName);
2462 
2463  // open root group
2464  hid_t parent = H5Gopen(fileHandle_, "/", H5P_DEFAULT);
2465  if(groupName == "/")
2466  {
2467  return parent;
2468  }
2469 
2470  // remove leading /
2471  groupName = std::string(groupName.begin()+1, groupName.end());
2472 
2473  // check if the groupName has finishing slash
2474  if( groupName.size() != 0 && *groupName.rbegin() != '/')
2475  {
2476  groupName = groupName + '/';
2477  }
2478 
2479  // We determine if the group exists by checking the return value of H5Gopen.
2480  // To do so, we must temporarily disable error reporting.
2481  // Alternatively, we could use H5LTfind_dataset(), but this is much slower.
2482  HDF5DisableErrorOutput disable_error;
2483 
2484  // Open or create subgroups one by one
2485  std::string::size_type begin = 0, end = groupName.find('/');
2486  while (end != std::string::npos)
2487  {
2488  std::string group(groupName.begin()+begin, groupName.begin()+end);
2489 
2490  hid_t prevParent = parent;
2491  parent = H5Gopen(prevParent, group.c_str(), H5P_DEFAULT);
2492  if(parent < 0 && create) // group doesn't exist, but we are supposed to create it
2493  parent = H5Gcreate(prevParent, group.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
2494  H5Gclose(prevParent);
2495 
2496  if(parent < 0)
2497  break;
2498 
2499  begin = end + 1;
2500  end = groupName.find('/', begin);
2501  }
2502 
2503  return parent;
2504  }
2505 
2506  /* delete a dataset by unlinking it from the file structure. This does not
2507  delete the data!
2508  */
2509  inline void deleteDataset_(hid_t parent, std::string datasetName)
2510  {
2511  // delete existing data and create new dataset
2512  if(H5LTfind_dataset(parent, datasetName.c_str()))
2513  {
2514 
2515  #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6)
2516  if(H5Gunlink(parent, datasetName.c_str()) < 0)
2517  {
2518  vigra_postcondition(false, "HDF5File::deleteDataset_(): Unable to delete existing data.");
2519  }
2520  #else
2521  if(H5Ldelete(parent, datasetName.c_str(), H5P_DEFAULT ) < 0)
2522  {
2523  vigra_postcondition(false, "HDF5File::deleteDataset_(): Unable to delete existing data.");
2524  }
2525  #endif
2526  }
2527  }
2528 
2529  /* get the handle of a dataset specified by a string
2530  */
2531  hid_t getDatasetHandle_(std::string datasetName) const
2532  {
2533  // make datasetName clean
2534  datasetName = get_absolute_path(datasetName);
2535 
2536  std::string groupname = SplitString(datasetName).first();
2537  std::string setname = SplitString(datasetName).last();
2538 
2539  if(H5Lexists(fileHandle_, datasetName.c_str(), H5P_DEFAULT) <= 0)
2540  {
2541  std::cerr << "HDF5File::getDatasetHandle_(): Dataset '" << datasetName << "' does not exist.\n";
2542  return -1;
2543  }
2544 
2545  // Open parent group
2546  HDF5Handle groupHandle(openGroup_(groupname), &H5Gclose, "HDF5File::getDatasetHandle_(): Internal error");
2547 
2548  return H5Dopen(groupHandle, setname.c_str(), H5P_DEFAULT);
2549  }
2550 
2551  /* get the type of an object specified by a string
2552  */
2553  H5O_type_t get_object_type_(std::string name) const
2554  {
2555  name = get_absolute_path(name);
2556  std::string group_name = SplitString(name).first();
2557  std::string object_name = SplitString(name).last();
2558  if (!object_name.size())
2559  return H5O_TYPE_GROUP;
2560 
2561  htri_t exists = H5Lexists(fileHandle_, name.c_str(), H5P_DEFAULT);
2562  vigra_precondition(exists > 0, "HDF5File::get_object_type_(): "
2563  "object \"" + name + "\" "
2564  "not found.");
2565  // open parent group
2566  HDF5Handle group_handle(openGroup_(group_name), &H5Gclose, "Internal error");
2567  return HDF5_get_type(group_handle, name.c_str());
2568  }
2569 
2570  /* low-level write function to write vigra MultiArray data as an attribute
2571  */
2572  template<unsigned int N, class T, class Stride>
2573  void write_attribute_(std::string name,
2574  const std::string & attribute_name,
2575  const MultiArrayView<N, T, Stride> & array,
2576  const hid_t datatype,
2577  const int numBandsOfType);
2578 
2579  /* Write single value attribute
2580  This function allows to write data of atomic datatypes (int, long, double)
2581  as an attribute in the HDF5 file. So it is not necessary to create a MultiArray
2582  of size 1 to write a single number.
2583  */
2584  template<class T>
2585  inline void writeAtomicAttribute(std::string datasetName, std::string attributeName, const T data)
2586  {
2587  // make datasetName clean
2588  datasetName = get_absolute_path(datasetName);
2589 
2590  typename MultiArrayShape<1>::type chunkSize;
2591  chunkSize[0] = 0;
2592  MultiArray<1,T> array(MultiArrayShape<1>::type(1));
2593  array[0] = data;
2594  write_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), 1);
2595  }
2596 
2597  /* low-level read function to read vigra MultiArray data from attributes
2598  */
2599  template<unsigned int N, class T, class Stride>
2600  void read_attribute_(std::string datasetName,
2601  std::string attributeName,
2602  MultiArrayView<N, T, Stride> array,
2603  const hid_t datatype, const int numBandsOfType);
2604 
2605  /* Read a single value attribute.
2606  This functions allows to read a single value attribute of atomic datatype (int, long, double)
2607  from the HDF5 file. So it is not necessary to create a MultiArray
2608  of size 1 to read a single number.
2609  */
2610  template<class T>
2611  inline void readAtomicAttribute(std::string datasetName, std::string attributeName, T & data)
2612  {
2613  // make datasetName clean
2614  datasetName = get_absolute_path(datasetName);
2615 
2616  MultiArray<1,T> array(MultiArrayShape<1>::type(1));
2617  read_attribute_(datasetName, attributeName, array, detail::getH5DataType<T>(), 1);
2618  data = array[0];
2619  }
2620 
2621  inline void readAtomicAttribute(std::string datasetName, std::string attributeName, std::string & data)
2622  {
2623  // make datasetName clean
2624  datasetName = get_absolute_path(datasetName);
2625 
2626  MultiArray<1,const char *> array(MultiArrayShape<1>::type(1));
2627  read_attribute_(datasetName, attributeName, array, detail::getH5DataType<const char *>(), 1);
2628  data = std::string(array[0]);
2629  }
2630 
2631  /* low-level write function to write vigra unstrided MultiArray data
2632  */
2633  template<unsigned int N, class T, class Stride>
2634  void write_(std::string &datasetName,
2635  const MultiArrayView<N, T, Stride> & array,
2636  const hid_t datatype,
2637  const int numBandsOfType,
2638  typename MultiArrayShape<N>::type &chunkSize,
2639  int compressionParameter = 0);
2640 
2641  /* Write single value as dataset.
2642  This functions allows to write data of atomic datatypes (int, long, double)
2643  as a dataset in the HDF5 file. So it is not necessary to create a MultiArray
2644  of size 1 to write a single number.
2645 
2646  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2647  otherwise it will be interpreted as path relative to the current group.
2648  */
2649  template<class T>
2650  inline void writeAtomic(std::string datasetName, const T data)
2651  {
2652  // make datasetName clean
2653  datasetName = get_absolute_path(datasetName);
2654 
2655  typename MultiArrayShape<1>::type chunkSize;
2656  chunkSize[0] = 0;
2657  MultiArray<1,T> array(MultiArrayShape<1>::type(1));
2658  array[0] = data;
2659  write_(datasetName, array, detail::getH5DataType<T>(), 1, chunkSize,0);
2660  }
2661 
2662  /* low-level read function to read vigra unstrided MultiArray data
2663  */
2664  template<unsigned int N, class T, class Stride>
2665  void read_(std::string datasetName,
2666  MultiArrayView<N, T, Stride> array,
2667  const hid_t datatype, const int numBandsOfType);
2668 
2669  /* Read a single value.
2670  This functions allows to read a single datum of atomic datatype (int, long, double)
2671  from the HDF5 file. So it is not necessary to create a MultiArray
2672  of size 1 to read a single number.
2673 
2674  If the first character of datasetName is a "/", the path will be interpreted as absolute path,
2675  otherwise it will be interpreted as path relative to the current group.
2676  */
2677  template<class T>
2678  inline void readAtomic(std::string datasetName, T & data)
2679  {
2680  // make datasetName clean
2681  datasetName = get_absolute_path(datasetName);
2682 
2683  MultiArray<1,T> array(MultiArrayShape<1>::type(1));
2684  read_(datasetName, array, detail::getH5DataType<T>(), 1);
2685  data = array[0];
2686  }
2687 
2688  inline void readAtomic(std::string datasetName, std::string & data)
2689  {
2690  // make datasetName clean
2691  datasetName = get_absolute_path(datasetName);
2692 
2693  MultiArray<1,const char *> array(MultiArrayShape<1>::type(1));
2694  read_(datasetName, array, detail::getH5DataType<const char *>(), 1);
2695  data = std::string(array[0]);
2696  }
2697 
2698  /* low-level write function to write vigra unstrided MultiArray data into a sub-block of a dataset
2699  */
2700  template<unsigned int N, class T, class Stride>
2701  void writeBlock_(std::string datasetName,
2702  typename MultiArrayShape<N>::type &blockOffset,
2703  const MultiArrayView<N, T, Stride> & array,
2704  const hid_t datatype,
2705  const int numBandsOfType)
2706  {
2707  // open dataset if it exists
2708  std::string errorMessage = "HDF5File::writeBlock(): Error opening dataset '" + datasetName + "'.";
2709  HDF5HandleShared dataset(getDatasetHandle_(datasetName), &H5Dclose, errorMessage.c_str());
2710  herr_t status = writeBlock_(dataset, blockOffset, array, datatype, numBandsOfType);
2711  vigra_postcondition(status >= 0,
2712  "HDF5File::writeBlock(): write to dataset '" + datasetName + "' via H5Dwrite() failed.");
2713  }
2714 
2715  /* low-level write function to write vigra unstrided MultiArray data into a
2716  sub-block of a dataset. Returns the result of the internal call
2717  to <tt>H5Dwrite()</tt>.
2718  */
2719  template<unsigned int N, class T, class Stride>
2720  herr_t writeBlock_(HDF5HandleShared dataset,
2721  typename MultiArrayShape<N>::type &blockOffset,
2722  const MultiArrayView<N, T, Stride> & array,
2723  const hid_t datatype,
2724  const int numBandsOfType);
2725 
2726  /* low-level read function to read vigra unstrided MultiArray data from a sub-block of a dataset.
2727 
2728  The array must have the same shape as the block.
2729  */
2730  template<unsigned int N, class T, class Stride>
2731  void readBlock_(std::string datasetName,
2732  typename MultiArrayShape<N>::type &blockOffset,
2733  typename MultiArrayShape<N>::type &blockShape,
2734  MultiArrayView<N, T, Stride> array,
2735  const hid_t datatype, const int numBandsOfType)
2736  {
2737  std::string errorMessage ("HDF5File::readBlock(): Unable to open dataset '" + datasetName + "'.");
2738  HDF5HandleShared dataset(getDatasetHandle_(datasetName), &H5Dclose, errorMessage.c_str());
2739  herr_t status = readBlock_(dataset, blockOffset, blockShape, array, datatype, numBandsOfType);
2740  vigra_postcondition(status >= 0,
2741  "HDF5File::readBlock(): read from dataset '" + datasetName + "' via H5Dread() failed.");
2742  }
2743 
2744  /* low-level read function to read vigra unstrided MultiArray data from a sub-block of a dataset.
2745 
2746  The array must have the same shape as the block. Returns the result of the internal call
2747  to <tt>H5Dread()</tt>.
2748  */
2749  template<unsigned int N, class T, class Stride>
2750  herr_t readBlock_(HDF5HandleShared dataset,
2751  typename MultiArrayShape<N>::type &blockOffset,
2752  typename MultiArrayShape<N>::type &blockShape,
2753  MultiArrayView<N, T, Stride> array,
2754  const hid_t datatype, const int numBandsOfType);
2755 }; /* class HDF5File */
2756 
2757 /********************************************************************/
2758 
2759 template<int N, class T>
2760 HDF5HandleShared
2761 HDF5File::createDataset(std::string datasetName,
2762  TinyVector<MultiArrayIndex, N> const & shape,
2763  typename detail::HDF5TypeTraits<T>::value_type init,
2764  TinyVector<MultiArrayIndex, N> const & chunkSize,
2765  int compressionParameter)
2766 {
2767  vigra_precondition(!isReadOnly(),
2768  "HDF5File::createDataset(): file is read-only.");
2769 
2770  // make datasetName clean
2771  datasetName = get_absolute_path(datasetName);
2772 
2773  std::string groupname = SplitString(datasetName).first();
2774  std::string setname = SplitString(datasetName).last();
2775 
2776  hid_t parent = openCreateGroup_(groupname);
2777 
2778  // delete the dataset if it already exists
2779  deleteDataset_(parent, setname);
2780 
2781  // invert dimensions to guarantee c-order
2782  // add an extra dimension in case that the data is non-scalar
2783  typedef detail::HDF5TypeTraits<T> TypeTraits;
2784  ArrayVector<hsize_t> shape_inv;
2785  if(TypeTraits::numberOfBands() > 1)
2786  {
2787  shape_inv.resize(N+1);
2788  shape_inv[N] = TypeTraits::numberOfBands();
2789  }
2790  else
2791  {
2792  shape_inv.resize(N);
2793  }
2794  for(int k=0; k<N; ++k)
2795  shape_inv[N-1-k] = shape[k];
2796 
2797  // create dataspace
2798  HDF5Handle
2799  dataspaceHandle = HDF5Handle(H5Screate_simple(shape_inv.size(), shape_inv.data(), NULL),
2800  &H5Sclose, "HDF5File::createDataset(): unable to create dataspace for scalar data.");
2801 
2802  // set fill value
2803  HDF5Handle plist ( H5Pcreate(H5P_DATASET_CREATE), &H5Pclose, "HDF5File::createDataset(): unable to create property list." );
2804  H5Pset_fill_value(plist, TypeTraits::getH5DataType(), &init);
2805 
2806  // turn off time tagging of datasets by default.
2807  H5Pset_obj_track_times(plist, track_time);
2808 
2809  // enable chunks
2810  ArrayVector<hsize_t> chunks(defineChunks(chunkSize, shape, TypeTraits::numberOfBands(), compressionParameter));
2811  if(chunks.size() > 0)
2812  {
2813  std::reverse(chunks.begin(), chunks.end());
2814  H5Pset_chunk (plist, chunks.size(), chunks.begin());
2815  }
2816 
2817  // enable compression
2818  if(compressionParameter > 0)
2819  {
2820  H5Pset_deflate(plist, compressionParameter);
2821  }
2822 
2823  //create the dataset.
2824  HDF5HandleShared datasetHandle(H5Dcreate(parent, setname.c_str(),
2825  TypeTraits::getH5DataType(),
2826  dataspaceHandle, H5P_DEFAULT, plist, H5P_DEFAULT),
2827  &H5Dclose,
2828  "HDF5File::createDataset(): unable to create dataset.");
2829  if(parent != cGroupHandle_)
2830  H5Gclose(parent);
2831 
2832  return datasetHandle;
2833 }
2834 
2835 /********************************************************************/
2836 
2837 template<unsigned int N, class T, class Stride>
2838 void HDF5File::write_(std::string &datasetName,
2839  const MultiArrayView<N, T, Stride> & array,
2840  const hid_t datatype,
2841  const int numBandsOfType,
2842  typename MultiArrayShape<N>::type &chunkSize,
2843  int compressionParameter)
2844 {
2845  vigra_precondition(!isReadOnly(),
2846  "HDF5File::write(): file is read-only.");
2847 
2848  std::string groupname = SplitString(datasetName).first();
2849  std::string setname = SplitString(datasetName).last();
2850 
2851  // shape of the array. Add one dimension, if array contains non-scalars.
2852  ArrayVector<hsize_t> shape(array.shape().begin(), array.shape().end());
2853  std::reverse(shape.begin(), shape.end());
2854 
2855  if(numBandsOfType > 1)
2856  shape.push_back(numBandsOfType);
2857 
2858  HDF5Handle dataspace(H5Screate_simple(shape.size(), shape.begin(), NULL), &H5Sclose,
2859  "HDF5File::write(): Can not create dataspace.");
2860 
2861  // create and open group:
2862  std::string errorMessage ("HDF5File::write(): can not create group '" + groupname + "'.");
2863  HDF5Handle groupHandle(openCreateGroup_(groupname), &H5Gclose, errorMessage.c_str());
2864 
2865  // delete dataset, if it already exists
2866  deleteDataset_(groupHandle, setname.c_str());
2867 
2868  // set up properties list
2869  HDF5Handle plist(H5Pcreate(H5P_DATASET_CREATE), &H5Pclose,
2870  "HDF5File::write(): unable to create property list." );
2871 
2872  // turn off time tagging of datasets by default.
2873  H5Pset_obj_track_times(plist, track_time);
2874 
2875  // enable chunks
2876  ArrayVector<hsize_t> chunks(defineChunks(chunkSize, array.shape(), numBandsOfType, compressionParameter));
2877  if(chunks.size() > 0)
2878  {
2879  std::reverse(chunks.begin(), chunks.end());
2880  H5Pset_chunk (plist, chunks.size(), chunks.begin());
2881  }
2882 
2883  // enable compression
2884  if(compressionParameter > 0)
2885  {
2886  H5Pset_deflate(plist, compressionParameter);
2887  }
2888 
2889  // create dataset
2890  HDF5Handle datasetHandle(H5Dcreate(groupHandle, setname.c_str(), datatype, dataspace,H5P_DEFAULT, plist, H5P_DEFAULT),
2891  &H5Dclose, "HDF5File::write(): Can not create dataset.");
2892 
2893  herr_t status = 0;
2894  if(array.isUnstrided())
2895  {
2896  // Write the data directly from the array data buffer
2897  status = H5Dwrite(datasetHandle, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, array.data());
2898  }
2899  else
2900  {
2901  // otherwise, we need an intermediate buffer
2902  // FIXME: right now, the buffer has the same size as the array to be read
2903  // incomplete code for better solutions is below
2904  // MultiArray<N, T> buffer(array);
2905  // status = H5Dwrite(datasetHandle, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer.data());
2906 
2907  int offset = numBandsOfType > 1 ? 1 : 0;
2908  std::reverse(shape.begin(), shape.end());
2909  if(chunks.size() > 0)
2910  {
2911  // if the file is chunked, we use a buffer that matches the chunk size.
2912  std::reverse(chunks.begin(), chunks.end());
2913  }
2914  else
2915  {
2916  // otherwise, we compute a suitable chunk size.
2917  ArrayVector<hsize_t>(shape.size(), 1).swap(chunks);
2918  chunks[0] = numBandsOfType;
2919  MultiArrayIndex prod = 1;
2920  for(unsigned int k=0; k<N; ++k)
2921  {
2922  chunks[k+offset] = array.shape(k);
2923  prod *= array.shape(k);
2924  if(prod > 300000)
2925  break;
2926  }
2927  }
2928 
2929  ArrayVector<hsize_t> null(shape.size(), 0),
2930  start(shape.size(), 0),
2931  count(shape.size(), 1);
2932 
2933  count[N-1-offset] = numBandsOfType;
2934 
2935  typedef typename MultiArrayShape<N>::type Shape;
2936  Shape chunkCount, chunkMaxShape;
2937  for(unsigned int k=offset; k<chunks.size(); ++k)
2938  {
2939  chunkMaxShape[k-offset] = chunks[k];
2940  chunkCount[k-offset] = static_cast<MultiArrayIndex>(std::ceil(double(shape[k]) / chunks[k]));
2941  }
2942 
2943  typename CoupledIteratorType<N>::type chunkIter = createCoupledIterator(chunkCount),
2944  chunkEnd = chunkIter.getEndIterator();
2945  for(; chunkIter != chunkEnd; ++chunkIter)
2946  {
2947  Shape chunkStart(chunkIter.point() * chunkMaxShape),
2948  chunkStop(min(chunkStart + chunkMaxShape, array.shape()));
2949  MultiArray<N, T> buffer(array.subarray(chunkStart, chunkStop));
2950 
2951  for(unsigned int k=0; k<N; ++k)
2952  {
2953  start[N-1-k] = chunkStart[k];
2954  count[N-1-k] = buffer.shape(k);
2955  }
2956  if(offset == 1)
2957  {
2958  start[N] = 0;
2959  count[N] = numBandsOfType;
2960  }
2961  HDF5Handle filespace(H5Dget_space(datasetHandle),
2962  &H5Sclose, "HDF5File::write(): unable to create hyperslabs.");
2963  status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, start.data(), NULL, count.data(), NULL);
2964  if(status < 0)
2965  break;
2966 
2967  HDF5Handle dataspace2(H5Screate_simple(count.size(), count.data(), NULL),
2968  &H5Sclose, "HDF5File::write(): unable to create hyperslabs.");
2969  status = H5Sselect_hyperslab(dataspace2, H5S_SELECT_SET, null.data(), NULL, count.data(), NULL);
2970  if(status < 0)
2971  break;
2972 
2973  status = H5Dwrite(datasetHandle, datatype, dataspace2, filespace, H5P_DEFAULT, buffer.data());
2974  if(status < 0)
2975  break;
2976  }
2977  }
2978  vigra_postcondition(status >= 0,
2979  "HDF5File::write(): write to dataset '" + datasetName + "' via H5Dwrite() failed.");
2980 }
2981 
2982 /********************************************************************/
2983 
2984 template<unsigned int N, class T, class Stride>
2985 herr_t HDF5File::writeBlock_(HDF5HandleShared datasetHandle,
2986  typename MultiArrayShape<N>::type &blockOffset,
2987  const MultiArrayView<N, T, Stride> & array,
2988  const hid_t datatype,
2989  const int numBandsOfType)
2990 {
2991  vigra_precondition(!isReadOnly(),
2992  "HDF5File::writeBlock(): file is read-only.");
2993 
2994  ArrayVector<hsize_t> boffset, bshape, bones(N+1, 1);
2995  hssize_t dimensions = getDatasetDimensions_(datasetHandle);
2996  if(numBandsOfType > 1)
2997  {
2998  vigra_precondition(N+1 == dimensions,
2999  "HDF5File::readBlock(): Array dimension disagrees with data dimension.");
3000  bshape.resize(N+1);
3001  boffset.resize(N+1);
3002  bshape[N] = numBandsOfType;
3003  boffset[N] = 0;
3004  }
3005  else
3006  {
3007  vigra_precondition(N == dimensions,
3008  "HDF5File::readBlock(): Array dimension disagrees with data dimension.");
3009  bshape.resize(N);
3010  boffset.resize(N);
3011  }
3012 
3013  for(unsigned i = 0; i < N; ++i)
3014  {
3015  // vigra and hdf5 use different indexing
3016  bshape[N-1-i] = array.shape(i);
3017  boffset[N-1-i] = blockOffset[i];
3018  }
3019 
3020  // create a target dataspace in memory with the shape of the desired block
3021  HDF5Handle memspace_handle (H5Screate_simple(bshape.size(), bshape.data(), NULL),
3022  &H5Sclose,
3023  "Unable to get origin dataspace");
3024 
3025  // get file dataspace and select the desired block
3026  HDF5Handle dataspaceHandle (H5Dget_space(datasetHandle),&H5Sclose,"Unable to create target dataspace");
3027  H5Sselect_hyperslab(dataspaceHandle, H5S_SELECT_SET,
3028  boffset.data(), bones.data(), bones.data(), bshape.data());
3029 
3030  herr_t status = 0;
3031  if(array.isUnstrided())
3032  {
3033  // when the array is unstrided, we can read the data directly from the array buffer
3034  status = H5Dwrite( datasetHandle, datatype, memspace_handle, dataspaceHandle, H5P_DEFAULT, array.data());
3035  }
3036  else
3037  {
3038  // otherwise, we must copy the data into an unstrided extra buffer
3039  MultiArray<N, T> buffer(array);
3040  status = H5Dwrite( datasetHandle, datatype, memspace_handle, dataspaceHandle, H5P_DEFAULT, buffer.data());
3041  }
3042  return status;
3043 }
3044 
3045 /********************************************************************/
3046 
3047 template<unsigned int N, class T, class Stride>
3048 void HDF5File::write_attribute_(std::string name,
3049  const std::string & attribute_name,
3050  const MultiArrayView<N, T, Stride> & array,
3051  const hid_t datatype,
3052  const int numBandsOfType)
3053 {
3054  vigra_precondition(!isReadOnly(),
3055  "HDF5File::writeAttribute(): file is read-only.");
3056 
3057  // shape of the array. Add one dimension, if array contains non-scalars.
3058  ArrayVector<hsize_t> shape(array.shape().begin(), array.shape().end());
3059  std::reverse(shape.begin(), shape.end());
3060  if(numBandsOfType > 1)
3061  shape.push_back(numBandsOfType);
3062 
3063  HDF5Handle dataspace(H5Screate_simple(shape.size(),
3064  shape.begin(), NULL),
3065  &H5Sclose, "HDF5File::writeAttribute(): Can not"
3066  " create dataspace.");
3067 
3068  std::string errorMessage ("HDF5File::writeAttribute(): can not find "
3069  "object '" + name + "'.");
3070 
3071  H5O_type_t h5_type = get_object_type_(name);
3072  bool is_group = h5_type == H5O_TYPE_GROUP;
3073  if (!is_group && h5_type != H5O_TYPE_DATASET)
3074  vigra_precondition(0, "HDF5File::writeAttribute(): object \""
3075  + name + "\" is neither a group nor a "
3076  "dataset.");
3077  // get parent object handle
3078  HDF5Handle object_handle(is_group
3079  ? openCreateGroup_(name)
3080  : getDatasetHandle_(name),
3081  is_group
3082  ? &H5Gclose
3083  : &H5Dclose,
3084  errorMessage.c_str());
3085  // create / open attribute
3086  bool exists = existsAttribute(name, attribute_name);
3087  HDF5Handle attributeHandle(exists
3088  ? H5Aopen(object_handle,
3089  attribute_name.c_str(),
3090  H5P_DEFAULT)
3091  : H5Acreate(object_handle,
3092  attribute_name.c_str(), datatype,
3093  dataspace, H5P_DEFAULT,
3094  H5P_DEFAULT),
3095  &H5Aclose,
3096  "HDF5File::writeAttribute(): Can not create"
3097  " attribute.");
3098  herr_t status = 0;
3099  if(array.isUnstrided())
3100  {
3101  // write the data directly from the array data buffer
3102  status = H5Awrite(attributeHandle, datatype, array.data());
3103  }
3104  else
3105  {
3106  // write the data via an unstrided copy
3107  // (we assume that attributes are small arrays, so that the wasted memory is uncritical)
3108  MultiArray<N, T> buffer(array);
3109  status = H5Awrite(attributeHandle, datatype, buffer.data());
3110  }
3111  vigra_postcondition(status >= 0,
3112  "HDF5File::writeAttribute(): write to attribute '" + attribute_name + "' via H5Awrite() failed.");
3113 }
3114 
3115 /********************************************************************/
3116 
3117 template<unsigned int N, class T, class Stride>
3118 void HDF5File::read_(std::string datasetName,
3119  MultiArrayView<N, T, Stride> array,
3120  const hid_t datatype, const int numBandsOfType)
3121 {
3122  //Prepare to read without using HDF5ImportInfo
3123  ArrayVector<hsize_t> dimshape = getDatasetShape(datasetName);
3124 
3125  std::string errorMessage ("HDF5File::read(): Unable to open dataset '" + datasetName + "'.");
3126  HDF5Handle datasetHandle(getDatasetHandle_(datasetName), &H5Dclose, errorMessage.c_str());
3127 
3128  // the object in the HDF5 file may have one additional dimension which we
3129  // interprete as the pixel type's bands
3130  int offset = (numBandsOfType > 1)
3131  ? 1
3132  : 0;
3133 
3134  vigra_precondition(MultiArrayIndex(N + offset) == MultiArrayIndex(dimshape.size()),
3135  "HDF5File::read(): Array dimension disagrees with dataset dimension.");
3136 
3137  typename MultiArrayShape<N>::type shape;
3138  for(int k=offset; k < (int)dimshape.size(); ++k)
3139  shape[k-offset] = (MultiArrayIndex)dimshape[k];
3140 
3141  vigra_precondition(shape == array.shape(),
3142  "HDF5File::read(): Array shape disagrees with dataset shape.");
3143  if (offset)
3144  vigra_precondition(dimshape[0] == static_cast<hsize_t>(numBandsOfType),
3145  "HDF5File::read(): Band count doesn't match destination array compound type.");
3146 
3147  herr_t status = 0;
3148  if(array.isUnstrided())
3149  {
3150  // when the array is unstrided, we can read the data directly into the array buffer
3151  status = H5Dread(datasetHandle, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, array.data() );
3152  }
3153  else
3154  {
3155  // otherwise, we need an intermediate buffer
3156 
3157  ArrayVector<hsize_t> null(dimshape.size(), 0),
3158  chunks(dimshape.size(), 1),
3159  start(dimshape.size(), 0),
3160  count(dimshape.size(), 1);
3161 
3162  HDF5Handle properties(H5Dget_create_plist(datasetHandle),
3163  &H5Pclose, "HDF5File::read(): failed to get property list");
3164  if(H5D_CHUNKED == H5Pget_layout(properties))
3165  {
3166  // if the file is chunked, we use a buffer that matches the chunk size.
3167  H5Pget_chunk(properties, static_cast<int>(chunks.size()), chunks.data());
3168  std::reverse(chunks.begin(), chunks.end());
3169  }
3170  else
3171  {
3172  // otherwise, we compute a suitable chunk size.
3173  chunks[0] = numBandsOfType;
3174  MultiArrayIndex prod = 1;
3175  for(unsigned int k=0; k<N; ++k)
3176  {
3177  chunks[k+offset] = array.shape(k);
3178  prod *= array.shape(k);
3179  if(prod > 300000)
3180  break;
3181  }
3182  }
3183 
3184  count[N-1-offset] = static_cast<hsize_t>(numBandsOfType);
3185 
3186  typedef typename MultiArrayShape<N>::type Shape;
3187  Shape chunkCount, chunkMaxShape;
3188  for(unsigned int k=offset; k<chunks.size(); ++k)
3189  {
3190  chunkMaxShape[k-offset] = chunks[k];
3191  chunkCount[k-offset] = (MultiArrayIndex)std::ceil(double(dimshape[k]) / chunks[k]);
3192  }
3193 
3194  typename CoupledIteratorType<N>::type chunkIter = createCoupledIterator(chunkCount),
3195  chunkEnd = chunkIter.getEndIterator();
3196  for(; chunkIter != chunkEnd; ++chunkIter)
3197  {
3198  Shape chunkStart(chunkIter.point() * chunkMaxShape),
3199  chunkStop(min(chunkStart + chunkMaxShape, array.shape()));
3200  MultiArray<N, T> buffer(chunkStop - chunkStart);
3201 
3202  for(unsigned int k=0; k<N; ++k)
3203  {
3204  start[N-1-k] = chunkStart[k];
3205  count[N-1-k] = buffer.shape(k);
3206  }
3207  if(offset == 1)
3208  {
3209  start[N] = 0;
3210  count[N] = numBandsOfType;
3211  }
3212  HDF5Handle filespace(H5Dget_space(datasetHandle),
3213  &H5Sclose, "HDF5File::read(): unable to create hyperslabs.");
3214  status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, start.data(), NULL, count.data(), NULL);
3215  if(status < 0)
3216  break;
3217 
3218  HDF5Handle dataspace(H5Screate_simple(count.size(), count.data(), NULL),
3219  &H5Sclose, "HDF5File::read(): unable to create hyperslabs.");
3220  status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, null.data(), NULL, count.data(), NULL);
3221  if(status < 0)
3222  break;
3223 
3224  status = H5Dread(datasetHandle, datatype, dataspace, filespace, H5P_DEFAULT, buffer.data());
3225  if(status < 0)
3226  break;
3227 
3228  array.subarray(chunkStart, chunkStop) = buffer;
3229  }
3230  }
3231  vigra_postcondition(status >= 0,
3232  "HDF5File::read(): read from dataset '" + datasetName + "' via H5Dread() failed.");
3233 }
3234 
3235 /********************************************************************/
3236 
3237 template<unsigned int N, class T, class Stride>
3238 herr_t HDF5File::readBlock_(HDF5HandleShared datasetHandle,
3239  typename MultiArrayShape<N>::type &blockOffset,
3240  typename MultiArrayShape<N>::type &blockShape,
3241  MultiArrayView<N, T, Stride> array,
3242  const hid_t datatype, const int numBandsOfType)
3243 {
3244  vigra_precondition(blockShape == array.shape(),
3245  "HDF5File::readBlock(): Array shape disagrees with block size.");
3246 
3247  ArrayVector<hsize_t> boffset, bshape, bones(N+1, 1);
3248  hssize_t dimensions = getDatasetDimensions_(datasetHandle);
3249  if(numBandsOfType > 1)
3250  {
3251  vigra_precondition(N+1 == dimensions,
3252  "HDF5File::readBlock(): Array dimension disagrees with data dimension.");
3253  bshape.resize(N+1);
3254  boffset.resize(N+1);
3255  bshape[N] = numBandsOfType;
3256  boffset[N] = 0;
3257  }
3258  else
3259  {
3260  vigra_precondition(N == dimensions,
3261  "HDF5File::readBlock(): Array dimension disagrees with data dimension.");
3262  bshape.resize(N);
3263  boffset.resize(N);
3264  }
3265 
3266  for(unsigned i = 0; i < N; ++i)
3267  {
3268  // vigra and hdf5 use different indexing
3269  bshape[N-1-i] = blockShape[i];
3270  boffset[N-1-i] = blockOffset[i];
3271  }
3272 
3273  // create a target dataspace in memory with the shape of the desired block
3274  HDF5Handle memspace_handle(H5Screate_simple(bshape.size(), bshape.data(), NULL),
3275  &H5Sclose,
3276  "Unable to create target dataspace");
3277 
3278  // get file dataspace and select the desired block
3279  HDF5Handle dataspaceHandle(H5Dget_space(datasetHandle), &H5Sclose,
3280  "Unable to get dataspace");
3281  H5Sselect_hyperslab(dataspaceHandle, H5S_SELECT_SET,
3282  boffset.data(), bones.data(), bones.data(), bshape.data());
3283 
3284  herr_t status = 0;
3285  if(array.isUnstrided())
3286  {
3287  // when the array is unstrided, we can read the data directly into the array buffer
3288  status = H5Dread( datasetHandle, datatype, memspace_handle, dataspaceHandle, H5P_DEFAULT, array.data());
3289  }
3290  else
3291  {
3292  // otherwise, we need an unstrided extra buffer ...
3293  MultiArray<N, T> buffer(array.shape());
3294  status = H5Dread( datasetHandle, datatype, memspace_handle, dataspaceHandle, H5P_DEFAULT, buffer.data());
3295  // ... and must copy the values
3296  if(status >= 0)
3297  array = buffer;
3298  }
3299  return status;
3300 }
3301 
3302 /********************************************************************/
3303 
3304 template<unsigned int N, class T, class Stride>
3305 void HDF5File::read_attribute_(std::string datasetName,
3306  std::string attributeName,
3307  MultiArrayView<N, T, Stride> array,
3308  const hid_t datatype, const int numBandsOfType)
3309 {
3310  std::string dataset_path = get_absolute_path(datasetName);
3311  // open Attribute handle
3312  std::string message = "HDF5File::readAttribute(): could not get handle for attribute '"+attributeName+"'' of object '"+dataset_path+"'.";
3313  HDF5Handle attr_handle (H5Aopen_by_name(fileHandle_,dataset_path.c_str(),attributeName.c_str(),H5P_DEFAULT,H5P_DEFAULT),&H5Aclose, message.c_str());
3314 
3315  // get Attribute dataspace
3316  message = "HDF5File::readAttribute(): could not get dataspace for attribute '"+attributeName+"'' of object '"+dataset_path+"'.";
3317  HDF5Handle attr_dataspace_handle (H5Aget_space(attr_handle),&H5Sclose,message.c_str());
3318 
3319  // obtain Attribute shape
3320  int raw_dims = H5Sget_simple_extent_ndims(attr_dataspace_handle);
3321  int dims = std::max(raw_dims, 1); // scalar attributes may be stored with raw_dims==0
3322  ArrayVector<hsize_t> dimshape(dims);
3323  if(raw_dims > 0)
3324  H5Sget_simple_extent_dims(attr_dataspace_handle, dimshape.data(), NULL);
3325  else
3326  dimshape[0] = 1;
3327 
3328  // invert the dimensions to guarantee VIGRA-compatible order
3329  std::reverse(dimshape.begin(), dimshape.end());
3330 
3331  int offset = (numBandsOfType > 1)
3332  ? 1
3333  : 0;
3334  message = "HDF5File::readAttribute(): Array dimension disagrees with dataset dimension.";
3335  // the object in the HDF5 file may have one additional dimension which we then interpret as the pixel type bands
3336  vigra_precondition(MultiArrayIndex(N + offset) == MultiArrayIndex(dims), message);
3337 
3338  for(int k=offset; k < (int)dimshape.size(); ++k)
3339  vigra_precondition(array.shape()[k-offset] == (MultiArrayIndex)dimshape[k],
3340  "HDF5File::readAttribute(): Array shape disagrees with dataset shape");
3341 
3342  herr_t status = 0;
3343  if(array.isUnstrided())
3344  {
3345  // when the array is unstrided, we can read the data directly into the array buffer
3346  status = H5Aread( attr_handle, datatype, array.data());
3347  }
3348  else
3349  {
3350  // otherwise, we need an unstrided extra buffer ...
3351  // (we assume that attributes are small arrays, so that the wasted memory is uncritical)
3352  MultiArray<N, T> buffer(array.shape());
3353  status = H5Aread( attr_handle, datatype, buffer.data() );
3354  // ... and must copy the values
3355  if(status >= 0)
3356  array = buffer;
3357  }
3358  vigra_postcondition(status >= 0,
3359  "HDF5File::readAttribute(): read from attribute '" + attributeName + "' via H5Aread() failed.");
3360 }
3361 
3362 /********************************************************************/
3363 
3364 /** \brief Read the data specified by the given \ref vigra::HDF5ImportInfo object
3365  and write the into the given 'array'.
3366 
3367  The array must have the correct number of dimensions and shape for the dataset
3368  represented by 'info'. When the element type of 'array' differs from the stored element
3369  type, HDF5 will convert the type on the fly (except when the HDF5 version is 1.6 or below,
3370  in which case an error will result). Multi-channel element types (i.e. \ref vigra::RGBValue,
3371  \ref vigra::TinyVector, and \ref vigra::FFTWComplex) are recognized and handled correctly.
3372 
3373  <b> Declaration:</b>
3374 
3375  \code
3376  namespace vigra {
3377  template<unsigned int N, class T, class StrideTag>
3378  void
3379  readHDF5(const HDF5ImportInfo &info, MultiArrayView<N, T, StrideTag> array);
3380  }
3381  \endcode
3382 
3383  <b> Usage:</b>
3384 
3385  <b>\#include</b> <vigra/hdf5impex.hxx><br>
3386  Namespace: vigra
3387 
3388  \code
3389 
3390  HDF5ImportInfo info(filename, dataset_name);
3391  vigra_precondition(info.numDimensions() == 3, "Dataset must be 3-dimensional.");
3392 
3393  MultiArrayShape<3>::type shape(info.shape().begin());
3394  MultiArray<3, int> array(shape);
3395 
3396  readHDF5(info, array);
3397  \endcode
3398 */
3399 doxygen_overloaded_function(template <...> void readHDF5)
3400 
3401 template<unsigned int N, class T, class StrideTag>
3402 inline void readHDF5(const HDF5ImportInfo &info, MultiArrayView<N, T, StrideTag> array)
3403 {
3404  HDF5File file(info.getFilePath(), HDF5File::OpenReadOnly);
3405  file.read(info.getPathInFile(), array);
3406 }
3407 
3408 inline hid_t openGroup(hid_t parent, std::string group_name)
3409 {
3410  //std::cout << group_name << std::endl;
3411  size_t last_slash = group_name.find_last_of('/');
3412  if (last_slash == std::string::npos || last_slash != group_name.size() - 1)
3413  group_name = group_name + '/';
3414  std::string::size_type begin = 0, end = group_name.find('/');
3415  int ii = 0;
3416  while (end != std::string::npos)
3417  {
3418  std::string group(group_name.begin()+begin, group_name.begin()+end);
3419  hid_t prev_parent = parent;
3420  parent = H5Gopen(prev_parent, group.c_str(), H5P_DEFAULT);
3421 
3422  if(ii != 0) H5Gclose(prev_parent);
3423  if(parent < 0) return parent;
3424  ++ii;
3425  begin = end + 1;
3426  end = group_name.find('/', begin);
3427  }
3428  return parent;
3429 }
3430 
3431 inline hid_t createGroup(hid_t parent, std::string group_name)
3432 {
3433  if(group_name.size() == 0 ||*group_name.rbegin() != '/')
3434  group_name = group_name + '/';
3435  if(group_name == "/")
3436  return H5Gopen(parent, group_name.c_str(), H5P_DEFAULT);
3437 
3438  std::string::size_type begin = 0, end = group_name.find('/');
3439  int ii = 0;
3440  while (end != std::string::npos)
3441  {
3442  std::string group(group_name.begin()+begin, group_name.begin()+end);
3443  hid_t prev_parent = parent;
3444 
3445  if(H5LTfind_dataset(parent, group.c_str()) == 0)
3446  {
3447  parent = H5Gcreate(prev_parent, group.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
3448  } else {
3449  parent = H5Gopen(prev_parent, group.c_str(), H5P_DEFAULT);
3450  }
3451 
3452  if(ii != 0) H5Gclose(prev_parent);
3453  if(parent < 0) return parent;
3454  ++ii;
3455  begin = end + 1;
3456  end = group_name.find('/', begin);
3457  }
3458  return parent;
3459 }
3460 
3461 inline void deleteDataset(hid_t parent, std::string dataset_name)
3462 {
3463  // delete existing data and create new dataset
3464  if(H5LTfind_dataset(parent, dataset_name.c_str()))
3465  {
3466  //std::cout << "dataset already exists" << std::endl;
3467 #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6)
3468  if(H5Gunlink(parent, dataset_name.c_str()) < 0)
3469  {
3470  vigra_postcondition(false, "writeToHDF5File(): Unable to delete existing data.");
3471  }
3472 #else
3473  if(H5Ldelete(parent, dataset_name.c_str(), H5P_DEFAULT ) < 0)
3474  {
3475  vigra_postcondition(false, "createDataset(): Unable to delete existing data.");
3476  }
3477 #endif
3478  }
3479 }
3480 
3481 inline hid_t createFile(std::string filePath, bool append_ = true)
3482 {
3483  FILE * pFile;
3484  pFile = fopen ( filePath.c_str(), "r" );
3485  hid_t file_id;
3486  if ( pFile == NULL )
3487  {
3488  file_id = H5Fcreate(filePath.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
3489  }
3490  else if(append_)
3491  {
3492  fclose( pFile );
3493  file_id = H5Fopen(filePath.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
3494  }
3495  else
3496  {
3497  fclose(pFile);
3498  std::remove(filePath.c_str());
3499  file_id = H5Fcreate(filePath.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
3500  }
3501  return file_id;
3502 }
3503 
3504 template<unsigned int N, class T, class Tag>
3505 void createDataset(const char* filePath, const char* pathInFile, const MultiArrayView<N, T, Tag> & array, const hid_t datatype, const int numBandsOfType, HDF5Handle & file_handle, HDF5Handle & dataset_handle)
3506 {
3507  std::string path_name(pathInFile), group_name, data_set_name, message;
3508  std::string::size_type delimiter = path_name.rfind('/');
3509 
3510  //create or open file
3511  file_handle = HDF5Handle(createFile(filePath), &H5Fclose,
3512  "createDataset(): unable to open output file.");
3513 
3514  // get the groupname and the filename
3515  if(delimiter == std::string::npos)
3516  {
3517  group_name = "/";
3518  data_set_name = path_name;
3519  }
3520  else
3521  {
3522  group_name = std::string(path_name.begin(), path_name.begin()+delimiter);
3523  data_set_name = std::string(path_name.begin()+delimiter+1, path_name.end());
3524  }
3525 
3526  // create all groups
3527  HDF5Handle group(createGroup(file_handle, group_name), &H5Gclose,
3528  "createDataset(): Unable to create and open group. generic v");
3529 
3530  // delete the dataset if it already exists
3531  deleteDataset(group, data_set_name);
3532 
3533  // create dataspace
3534  // add an extra dimension in case that the data is non-scalar
3535  HDF5Handle dataspace_handle;
3536  if(numBandsOfType > 1) {
3537  // invert dimensions to guarantee c-order
3538  hsize_t shape_inv[N+1]; // one additional dimension for pixel type channel(s)
3539  for(unsigned int k=0; k<N; ++k) {
3540  shape_inv[N-1-k] = array.shape(k); // the channels (eg of an RGB image) are represented by the first dimension (before inversion)
3541  //std::cout << shape_inv[N-k] << " (" << N << ")";
3542  }
3543  shape_inv[N] = numBandsOfType;
3544 
3545  // create dataspace
3546  dataspace_handle = HDF5Handle(H5Screate_simple(N+1, shape_inv, NULL),
3547  &H5Sclose, "createDataset(): unable to create dataspace for non-scalar data.");
3548  } else {
3549  // invert dimensions to guarantee c-order
3550  hsize_t shape_inv[N];
3551  for(unsigned int k=0; k<N; ++k)
3552  shape_inv[N-1-k] = array.shape(k);
3553 
3554  // create dataspace
3555  dataspace_handle = HDF5Handle(H5Screate_simple(N, shape_inv, NULL),
3556  &H5Sclose, "createDataset(): unable to create dataspace for scalar data.");
3557  }
3558 
3559  //alloc memory for dataset.
3560  dataset_handle = HDF5Handle(H5Dcreate(group,
3561  data_set_name.c_str(),
3562  datatype,
3563  dataspace_handle,
3564  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT),
3565  &H5Dclose, "createDataset(): unable to create dataset.");
3566 }
3567 
3568 
3569 
3570 
3571 /** \brief Store array data in an HDF5 file.
3572 
3573  The number of dimensions, shape and element type of the stored dataset is automatically
3574  determined from the properties of the given \a array. Strided arrays are stored in an
3575  unstrided way, i.e. in contiguous scan-order. Multi-channel element types
3576  (i.e. \ref vigra::RGBValue, \ref vigra::TinyVector and \ref vigra::FFTWComplex)
3577  are recognized and handled correctly
3578  (in particular, the will form the innermost dimension of the stored dataset).
3579  \a pathInFile may contain '/'-separated group names, but must end with the name
3580  of the dataset to be created.
3581 
3582  <b> Declaration:</b>
3583 
3584  \code
3585  namespace vigra {
3586  template<unsigned int N, class T, class StrideTag>
3587  void
3588  writeHDF5(const char* filePath, const char* pathInFile,
3589  MultiArrayView<N, T, StrideTag>const & array);
3590  }
3591  \endcode
3592 
3593  <b> Usage:</b>
3594 
3595  <b>\#include</b> <vigra/hdf5impex.hxx><br>
3596  Namespace: vigra
3597 
3598  \code
3599  MultiArrayShape<3>::type shape(100, 200, 20);
3600  MultiArray<3, int> array(shape);
3601  ... // fill array with data
3602 
3603  writeHDF5("mydata.h5", "/group1/my_dataset", array);
3604  \endcode
3605 */
3606 doxygen_overloaded_function(template <...> void writeHDF5)
3607 
3608 template<unsigned int N, class T, class StrideTag>
3609 inline void writeHDF5(const char* filePath, const char* pathInFile, const MultiArrayView<N, T, StrideTag> & array)
3610 {
3611  HDF5File file(filePath, HDF5File::Open);
3612  file.write(pathInFile, array);
3613 }
3614 
3615 namespace detail
3616 {
3617 struct MaxSizeFnc
3618 {
3619  size_t size;
3620 
3621  MaxSizeFnc()
3622  : size(0)
3623  {}
3624 
3625  void operator()(std::string const & in)
3626  {
3627  size = in.size() > size ?
3628  in.size() :
3629  size;
3630  }
3631 };
3632 }
3633 
3634 
3635 #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 8) || DOXYGEN
3636 /** Write a numeric MultiArray as an attribute with name \a name
3637  of the dataset specified by the handle \a loc.
3638 
3639  <b>\#include</b> <vigra/hdf5impex.hxx><br>
3640  Namespace: vigra
3641 */
3642 template<size_t N, class T, class C>
3643 void writeHDF5Attr(hid_t loc,
3644  const char* name,
3645  MultiArrayView<N, T, C> const & array)
3646 {
3647  if(H5Aexists(loc, name) > 0)
3648  H5Adelete(loc, name);
3649 
3650  ArrayVector<hsize_t> shape(array.shape().begin(),
3651  array.shape().end());
3652  HDF5Handle
3653  dataspace_handle(H5Screate_simple(N, shape.data(), NULL),
3654  &H5Sclose,
3655  "writeToHDF5File(): unable to create dataspace.");
3656 
3657  HDF5Handle attr(H5Acreate(loc,
3658  name,
3659  detail::getH5DataType<T>(),
3660  dataspace_handle,
3661  H5P_DEFAULT ,H5P_DEFAULT ),
3662  &H5Aclose,
3663  "writeHDF5Attr: unable to create Attribute");
3664 
3665  //copy data - since attributes are small - who cares!
3666  ArrayVector<T> buffer;
3667  for(int ii = 0; ii < array.size(); ++ii)
3668  buffer.push_back(array[ii]);
3669  H5Awrite(attr, detail::getH5DataType<T>(), buffer.data());
3670 }
3671 
3672 /** Write a string MultiArray as an attribute with name \a name
3673  of the dataset specified by the handle \a loc.
3674 
3675  <b>\#include</b> <vigra/hdf5impex.hxx><br>
3676  Namespace: vigra
3677 */
3678 template<size_t N, class C>
3679 void writeHDF5Attr(hid_t loc,
3680  const char* name,
3681  MultiArrayView<N, std::string, C> const & array)
3682 {
3683  if(H5Aexists(loc, name) > 0)
3684  H5Adelete(loc, name);
3685 
3686  ArrayVector<hsize_t> shape(array.shape().begin(),
3687  array.shape().end());
3688  HDF5Handle
3689  dataspace_handle(H5Screate_simple(N, shape.data(), NULL),
3690  &H5Sclose,
3691  "writeToHDF5File(): unable to create dataspace.");
3692 
3693  HDF5Handle atype(H5Tcopy (H5T_C_S1),
3694  &H5Tclose,
3695  "writeToHDF5File(): unable to create type.");
3696 
3697  detail::MaxSizeFnc max_size;
3698  max_size = std::for_each(array.data(),array.data()+ array.size(), max_size);
3699  H5Tset_size (atype, max_size.size);
3700 
3701  HDF5Handle attr(H5Acreate(loc,
3702  name,
3703  atype,
3704  dataspace_handle,
3705  H5P_DEFAULT ,H5P_DEFAULT ),
3706  &H5Aclose,
3707  "writeHDF5Attr: unable to create Attribute");
3708 
3709  std::string buf ="";
3710  for(int ii = 0; ii < array.size(); ++ii)
3711  {
3712  buf = buf + array[ii]
3713  + std::string(max_size.size - array[ii].size(), ' ');
3714  }
3715  H5Awrite(attr, atype, buf.c_str());
3716 }
3717 
3718 /** Write a numeric ArrayVectorView as an attribute with name \a name
3719  of the dataset specified by the handle \a loc.
3720 
3721  <b>\#include</b> <vigra/hdf5impex.hxx><br>
3722  Namespace: vigra
3723 */
3724 template<class T>
3725 inline void writeHDF5Attr( hid_t loc,
3726  const char* name,
3727  ArrayVectorView<T> & array)
3728 {
3729  writeHDF5Attr(loc, name,
3731  array.data()));
3732 }
3733 
3734 /** write an Attribute given a file and a path in the file.
3735  the path in the file should have the format
3736  [attribute] or /[subgroups/]dataset.attribute or
3737  /[subgroups/]group.attribute.
3738  The attribute is written to the root group, a dataset or a subgroup
3739  respectively
3740 */
3741 template<class Arr>
3742 inline void writeHDF5Attr( std::string filePath,
3743  std::string pathInFile,
3744  Arr & ar)
3745 {
3746  std::string path_name(pathInFile), group_name, data_set_name, message, attr_name;
3747  std::string::size_type delimiter = path_name.rfind('/');
3748 
3749  //create or open file
3750  HDF5Handle file_id(createFile(filePath), &H5Fclose,
3751  "writeToHDF5File(): unable to open output file.");
3752 
3753  // get the groupname and the filename
3754  if(delimiter == std::string::npos)
3755  {
3756  group_name = "/";
3757  data_set_name = path_name;
3758  }
3759 
3760  else
3761  {
3762  group_name = std::string(path_name.begin(), path_name.begin()+delimiter);
3763  data_set_name = std::string(path_name.begin()+delimiter+1, path_name.end());
3764  }
3765  delimiter = data_set_name.rfind('.');
3766  if(delimiter == std::string::npos)
3767  {
3768  attr_name = path_name;
3769  data_set_name = "/";
3770  }
3771  else
3772  {
3773  attr_name = std::string(data_set_name.begin()+delimiter+1, data_set_name.end());
3774  data_set_name = std::string(data_set_name.begin(), data_set_name.begin()+delimiter);
3775  }
3776 
3777  HDF5Handle group(openGroup(file_id, group_name), &H5Gclose,
3778  "writeToHDF5File(): Unable to create and open group. attr ver");
3779 
3780  if(data_set_name != "/")
3781  {
3782  HDF5Handle dset(H5Dopen(group, data_set_name.c_str(), H5P_DEFAULT), &H5Dclose,
3783  "writeHDF5Attr():unable to open dataset");
3784  writeHDF5Attr(hid_t(dset), attr_name.c_str(), ar);
3785  }
3786  else
3787  {
3788  writeHDF5Attr(hid_t(group), attr_name.c_str(), ar);
3789  }
3790 
3791 }
3792 #endif
3793 
3794 //@}
3795 
3796 } // namespace vigra
3797 
3798 #endif // VIGRA_HDF5IMPEX_HXX
HDF5File & operator=(HDF5File const &other)
Assign a HDF5File object.
Definition: hdf5impex.hxx:1150
void read(const std::string &datasetName, ArrayVectorView< T > array)
Read data into an array vector. If the first character of datasetName is a "/", the path will be inte...
Definition: hdf5impex.hxx:2017
HDF5Handle()
Default constructor. Creates a NULL handle.
Definition: hdf5impex.hxx:224
void write(const std::string &datasetName, const ArrayVectorView< T > &array, int compression=0)
Write array vectors.
Definition: hdf5impex.hxx:1904
bool cd_up(int levels)
Change the current group to its parent group. Returns true if successful, false otherwise. If unsuccessful, the group will not change.
Definition: hdf5impex.hxx:1247
std::vector< std::string > listAttributes(std::string const &group_or_dataset) const
List the attribute names of the given group or dataset.
Definition: hdf5impex.hxx:1575
bool operator==(HDF5Handle const &h) const
Equality comparison of the contained handle.
Definition: hdf5impex.hxx:365
Temporarily disable HDF5's native error output.
Definition: hdf5impex.hxx:130
ArrayVector< hsize_t > getDatasetShape(std::string datasetName) const
Get the shape of each dimension of a certain dataset.
Definition: hdf5impex.hxx:1394
Wrapper for unique hid_t objects.
Definition: hdf5impex.hxx:210
MultiArrayIndex numDimensions() const
bool operator!=(hid_t h) const
Inequality comparison of the contained handle.
Definition: hdf5impex.hxx:636
HDF5HandleShared()
Default constructor. Creates a NULL handle.
Definition: hdf5impex.hxx:446
const difference_type & shape() const
Definition: multi_array.hxx:1648
void readAttribute(std::string object_name, std::string attribute_name, MultiArrayView< N, T, Stride > array)
Read MultiArray Attributes. In contrast to datasets, subarray access is not available.
Definition: hdf5impex.hxx:1698
void cd_mk(std::string groupName)
Change the current group; create it if necessary. If the first character is a "/", the path will be interpreted as absolute path, otherwise it will be interpreted as path relative to the current group.
Definition: hdf5impex.hxx:1285
hssize_t getDatasetDimensions(std::string datasetName) const
Get the number of dimensions of a certain dataset If the first character is a "/", the path will be interpreted as absolute path, otherwise it will be interpreted as path relative to the current group.
Definition: hdf5impex.hxx:1365
Definition: array_vector.hxx:76
const_iterator begin() const
Definition: array_vector.hxx:223
pointer data() const
Definition: multi_array.hxx:1898
HDF5ImportInfo(const char *filePath, const char *pathInFile)
HDF5Handle(hid_t h, Destructor destructor, const char *error_message)
Create a wrapper for a hid_t object.
Definition: hdf5impex.hxx:248
std::string get_absolute_path(std::string path) const
takes any path and converts it into an absolute path in the current file.
Definition: hdf5impex.hxx:2308
void swap(HDF5HandleShared &h)
Swap the contents of two handle wrappers.
Definition: hdf5impex.hxx:586
void reshape(const difference_type &shape)
Definition: multi_array.hxx:2861
HDF5HandleShared getDatasetHandleShared(std::string const &datasetName) const
Obtain a shared HDF5 handle of a dataset.
Definition: hdf5impex.hxx:1527
ArrayVector< hsize_t > getChunkShape(std::string datasetName) const
Get the shape of chunks along each dimension of a certain dataset.
Definition: hdf5impex.hxx:1430
void mkdir(std::string groupName)
Create a new group. If the first character is a "/", the path will be interpreted as absolute path...
Definition: hdf5impex.hxx:1268
bool existsAttribute(std::string object_name, std::string attribute_name)
Test if attribute exists.
Definition: hdf5impex.hxx:1681
const std::string & getFilePath() const
std::ptrdiff_t MultiArrayIndex
Definition: multi_fwd.hxx:60
void writeHDF5Attr(hid_t loc, const char *name, MultiArrayView< N, T, C > const &array)
Definition: hdf5impex.hxx:3643
HDF5HandleShared createDataset(std::string datasetName, TinyVector< MultiArrayIndex, N > const &shape, typename detail::HDF5TypeTraits< T >::value_type init=typename detail::HDF5TypeTraits< T >::value_type(), TinyVector< MultiArrayIndex, N > const &chunkSize=(TinyVector< MultiArrayIndex, N >()), int compressionParameter=0)
Create a new dataset. This function can be used to create a dataset filled with a default value init...
Definition: hdf5impex.hxx:2761
bool operator==(hid_t h) const
Equality comparison of the contained handle.
Definition: hdf5impex.hxx:372
hid_t release()
Return the contained handle and set the wrapper to NULL without calling close().
Definition: hdf5impex.hxx:309
const std::string & getPathInFile() const
herr_t close()
Close the handle if this is the unique (i.e. last) owner.
Definition: hdf5impex.hxx:527
void ls(Container &cont) const
List the contents of the current group into a container-like object via insert(). ...
Definition: hdf5impex.hxx:1332
void write(std::string datasetName, const MultiArrayView< N, T, Stride > &array, typename MultiArrayShape< N >::type chunkSize, int compression=0)
Write multi arrays. Chunks can be activated by providing a MultiArrayShape as chunkSize. chunkSize must have equal dimension as array.
Definition: hdf5impex.hxx:1823
Argument object for the function readHDF5().
Definition: hdf5impex.hxx:682
bool isHDF5(char const *filename)
Check if given filename refers to a HDF5 file.
Definition: hdf5impex.hxx:102
void reset(hid_t h, Destructor destructor, const char *error_message)
Reset the handle to a new value.
Definition: hdf5impex.hxx:550
HDF5File(HDF5File const &other)
Copy a HDF5File object.
Definition: hdf5impex.hxx:1126
HDF5HandleShared & operator=(HDF5HandleShared const &h)
Assignment. Call close() for the present LHS handle and share ownership with the RHS handle (analogou...
Definition: hdf5impex.hxx:498
HDF5HandleShared(HDF5HandleShared const &h)
Copy constructor. Shares ownership with the RHS handle (analogous to std::shared_ptr).
Definition: hdf5impex.hxx:485
bool operator!=(hid_t h) const
Inequality comparison of the contained handle.
Definition: hdf5impex.hxx:386
bool operator!=(HDF5Handle const &h) const
Inequality comparison of the contained handle.
Definition: hdf5impex.hxx:379
difference_type_1 size() const
Definition: multi_array.hxx:1641
Wrapper for shared hid_t objects.
Definition: hdf5impex.hxx:431
std::string filename() const
Get the name of the associated file.
Definition: hdf5impex.hxx:1347
HDF5File()
Default constructor.
Definition: hdf5impex.hxx:1047
HDF5Handle & operator=(HDF5Handle const &h)
Assignment. Calls close() for the LHS handle and hands over ownership of the RHS handle (analogous to...
Definition: hdf5impex.hxx:271
~HDF5File()
The destructor flushes and closes the file.
Definition: hdf5impex.hxx:1137
NumericTraits< V >::Promote prod(TinyVectorBase< V, SIZE, D1, D2 > const &l)
product of the vector's elements
Definition: tinyvector.hxx:2097
bool operator==(HDF5HandleShared const &h) const
Equality comparison of the contained handle.
Definition: hdf5impex.hxx:615
HDF5File(HDF5HandleShared const &fileHandle, const std::string &pathname="", bool read_only=false)
Initialize an HDF5File object from HDF5 file handle.
Definition: hdf5impex.hxx:1098
HDF5Handle getAttributeHandle(std::string dataset_name, std::string attribute_name) const
Obtain the HDF5 handle of a attribute.
Definition: hdf5impex.hxx:1598
void readAndResize(std::string datasetName, MultiArray< N, T, Alloc > &array)
Read data into a MultiArray. Resize MultiArray to the correct size. If the first character of dataset...
Definition: hdf5impex.hxx:1991
bool operator==(hid_t h) const
Equality comparison of the contained handle.
Definition: hdf5impex.hxx:622
HDF5File(char const *filePath, OpenMode mode=ReadOnly, bool track_creation_times=false)
Open or create an HDF5File object.
Definition: hdf5impex.hxx:1076
void readHDF5(...)
Read the data specified by the given vigra::HDF5ImportInfo object and write the into the given 'array...
void readAndResize(std::string datasetName, ArrayVector< T > &array)
Read data into an array vector. Resize the array vector to the correct size. If the first character o...
Definition: hdf5impex.hxx:2030
~HDF5Handle()
Destructor. Calls close() for the contained handle.
Definition: hdf5impex.hxx:286
bool operator!=(HDF5HandleShared const &h) const
Inequality comparison of the contained handle.
Definition: hdf5impex.hxx:629
void read(std::string datasetName, char &data)
Read a single value. Specialization of the read function for simple datatypes.
Definition: hdf5impex.hxx:2162
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
void open(std::string filePath, OpenMode mode)
Open or create the given file in the given mode and set the group to "/". If another file is currentl...
Definition: hdf5impex.hxx:1187
PixelType pixelType() const
OpenMode
Set how a file is opened.
Definition: hdf5impex.hxx:1031
void readAttribute(std::string object_name, std::string attribute_name, char &data)
Read a single value. Specialization of the read function for simple datatypes.
Definition: hdf5impex.hxx:1733
void writeBlock(std::string datasetName, typename MultiArrayShape< N >::type blockOffset, const MultiArrayView< N, T, Stride > &array)
Write a multi array into a larger volume. blockOffset determines the position, where array is written...
Definition: hdf5impex.hxx:1845
void flushToDisk()
Immediately write all data to disk.
Definition: hdf5impex.hxx:2234
HDF5Handle(HDF5Handle const &h)
Copy constructor.
Definition: hdf5impex.hxx:260
void reset(hid_t h, Destructor destructor, const char *error_message)
Reset the wrapper to a new handle.
Definition: hdf5impex.hxx:321
Class for fixed size vectors.This class contains an array of size SIZE of the specified VALUETYPE...
Definition: accessor.hxx:940
void writeHDF5(...)
Store array data in an HDF5 file.
MultiArrayIndex shapeOfDimension(const int dim) const
HDF5Handle getGroupHandle(std::string group_name, std::string function_name="HDF5File::getGroupHandle()")
Obtain the HDF5 handle of a group (create the group if it doesn't exist).
Definition: hdf5impex.hxx:1535
std::string pwd() const
Get the path of the current group.
Definition: hdf5impex.hxx:1340
void read(std::string datasetName, MultiArrayView< N, T, Stride > array)
Read data into a multi array. If the first character of datasetName is a "/", the path will be interp...
Definition: hdf5impex.hxx:1973
void listAttributes(std::string const &group_or_dataset, Container &container) const
Insert the attribute names of the given group or dataset into the given container by calling containe...
Definition: hdf5impex.hxx:1590
bool unique() const
Check if this is the unique owner of the conatined handle.
Definition: hdf5impex.hxx:577
void write(std::string datasetName, char data)
Write a single value. Specialization of the write function for simple datatypes.
Definition: hdf5impex.hxx:1944
const char * getPixelType() const
void writeAttribute(std::string object_name, std::string attribute_name, char data)
Write a single value. Specialization of the write function for simple datatypes.
Definition: hdf5impex.hxx:1646
void writeAttribute(std::string object_name, std::string attribute_name, const MultiArrayView< N, T, Stride > &array)
Write MultiArray Attributes. In contrast to datasets, subarray access, chunks and compression are not...
Definition: hdf5impex.hxx:1611
herr_t close()
Explicitly call the stored destructor (if one has been registered in the constructor) for the contain...
Definition: hdf5impex.hxx:296
void write(std::string datasetName, const MultiArrayView< N, T, Stride > &array, int iChunkSize=0, int compression=0)
Write multi arrays.
Definition: hdf5impex.hxx:1791
image import and export functions
bool isUnstrided(unsigned int dimension=N-1) const
Definition: multi_array.hxx:1286
void close()
Close the current file.
Definition: hdf5impex.hxx:1199
bool cd_up()
Change the current group to its parent group. Returns true if successful, false otherwise. If unsuccessful, the group will not change.
Definition: hdf5impex.hxx:1225
Base class for, and view to, vigra::MultiArray.
Definition: multi_array.hxx:704
std::string getDatasetType(std::string const &datasetName) const
Definition: hdf5impex.hxx:1473
bool existsDataset(std::string datasetName) const
Check if given datasetName exists.
Definition: hdf5impex.hxx:1354
const_iterator end() const
Definition: array_vector.hxx:237
const_pointer data() const
Definition: array_vector.hxx:209
ArrayVector< hsize_t > const & shape() const
size_type size() const
Definition: array_vector.hxx:358
MultiArrayView subarray(difference_type p, difference_type q) const
Definition: multi_array.hxx:1528
void readBlock(std::string datasetName, typename MultiArrayShape< N >::type blockOffset, typename MultiArrayShape< N >::type blockShape, MultiArrayView< N, T, Stride > array)
Read a block of data into a multi array. This function allows to read a small block out of a larger v...
Definition: hdf5impex.hxx:2068
Class for a single RGB value.
Definition: accessor.hxx:938
int ceil(FixedPoint< IntBits, FracBits > v)
rounding up.
Definition: fixedpoint.hxx:675
hid_t getDatasetHandle() const
HDF5HandleShared(hid_t h, Destructor destructor, const char *error_message)
Create a shared wrapper for a plain hid_t object.
Definition: hdf5impex.hxx:471
HDF5File(std::string filePath, OpenMode mode=ReadOnly, bool track_creation_times=false)
Open or create an HDF5File object.
Definition: hdf5impex.hxx:1065
hid_t getH5FileHandle() const
~HDF5HandleShared()
Destructor (calls close())
Definition: hdf5impex.hxx:514
HDF5Handle getDatasetHandle(std::string const &datasetName) const
Obtain the HDF5 handle of a dataset.
Definition: hdf5impex.hxx:1519
void root()
Change current group to "/".
Definition: hdf5impex.hxx:1207
HDF5File(bool track_creation_times)
Construct with time tagging of datasets enabled.
Definition: hdf5impex.hxx:1055
void cd(std::string groupName)
Change the current group. Both absolute and relative group names are allowed.
Definition: hdf5impex.hxx:1216
size_t use_count() const
Get the number of owners of the contained handle.
Definition: hdf5impex.hxx:566
void swap(HDF5Handle &h)
Swap the contents of two handle wrappers.
Definition: hdf5impex.hxx:337
Access to HDF5 files.
Definition: hdf5impex.hxx:974
std::vector< std::string > ls() const
List the contents of the current group. The function returns a vector of strings holding the entries ...
Definition: hdf5impex.hxx:1310

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)