1 /** \file volume.c
2 * \brief MINC 2.0 Volume Functions
3 * \author Leila Baghdadi, Bert Vincent
4 *
5 * Functions to create, open, and close MINC volume objects.
6 ************************************************************************/
7 #include <stdlib.h>
8 #include <hdf5.h>
9 
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif /*HAVE_CONFIG_H*/
13 
14 #ifdef HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif //HAVE_SYS_TYPES_H
17 
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif //HAVE_UNISTD_H
21 
22 #ifdef HAVE_MINC1
23 #include "minc.h"
24 #endif //HAVE_MINC1
25 
26 #include <limits.h>
27 #include <float.h>
28 #include <time.h>
29 #include <math.h>
30 
31 #include "minc_config.h"
32 #include "minc2.h"
33 #include "minc2_private.h"
34 
35 /* Build with 1.8.x support if using 1.10.x */
36 #if (H5_VERS_MAJOR==1)&&(H5_VERS_MINOR<10)
37 #define H5F_LIBVER_V18 H5F_LIBVER_LATEST
38 #elif (H5_VERS_MAJOR==1)&&(H5_VERS_MINOR==10)&&(H5_VERS_RELEASE<2)
39 #error The selected version of HDF5 library does not support setting backwards compatibility at run-time.\
40   Please use a different version of HDF5
41 #endif
42 
43 /*Used to optimize chunking size for faster MINC1 API access*/
44 #define _MI1_MAX_VAR_BUFFER_SIZE 1000000
45 
46 
47 /**
48 * \defgroup mi2Vol MINC 2.0 Volume Functions
49 */
50 
51 /* Forward declarations */
52 
53 static void miread_valid_range(mihandle_t volume, double *valid_max,
54                                double *valid_min);
55 
56 static int _miset_volume_class(mihandle_t volume, miclass_t volclass);
57 static int _miget_volume_class(mihandle_t volume, miclass_t *volclass);
58 
59 /**
60  * Creates a (hopefully) unique identifier to associate with a
61  *              MINC file, by concatenating various information about the
62  *              system, process, etc.
63  * returns the length of identifier
64  */
_generate_ident(char * id_str,size_t length)65 static int _generate_ident( char * id_str, size_t length )
66 {
67   static int identx = 1;      /* Static ID counter */
68   time_t now;
69   struct tm tm_buf;
70   char host_str[128];
71   char user_str[128];
72   char *temp_ptr;
73   char time_str[26];
74   int result;
75 
76 // Linking in ws2_32  for gethostname is problematic with static libraries.
77 #ifdef _WIN32
78   strcpy(host_str, "unknown");
79 #else
80   if (gethostname(host_str, sizeof(host_str)) != 0) {
81     strcpy(host_str, "unknown");
82   }
83 #endif
84 
85   temp_ptr = getenv("LOGNAME");
86   if (temp_ptr != NULL) {
87     strncpy(user_str, temp_ptr, sizeof(user_str) - 1);
88   }
89   else {
90     strcpy(user_str, "nobody");
91   }
92 
93 
94   time(&now);
95 #ifdef _WIN32
96   memcpy(&tm_buf, localtime(&now), sizeof(tm_buf));
97 #else
98   localtime_r(&now, &tm_buf);
99 #endif
100   strftime(time_str, sizeof(time_str), "%Y.%m.%d.%H.%M.%S", &tm_buf);
101 
102   result = snprintf(id_str, length, "%s:%s:%s:%u:%u",
103                     user_str,
104                     host_str,
105                     time_str,
106                     getpid(),
107                     identx++);
108   return result;
109 }
110 
111 /**
112  * open HDF5 file
113  */
_hdf_open(const char * path,int mode)114 static hid_t _hdf_open(const char *path, int mode)
115 {
116   hid_t fd;
117   hid_t prp_id;
118 /*  hid_t grp_id;
119   hid_t dset_id;
120   int ndims;*/
121 
122   prp_id = H5Pcreate(H5P_FILE_ACCESS);
123   H5Pset_libver_bounds(prp_id, H5F_LIBVER_V18, H5F_LIBVER_V18);
124   H5Pset_cache(prp_id, 0, 2503, miget_cfg_present(MICFG_MINC_FILE_CACHE)?miget_cfg_int(MICFG_MINC_FILE_CACHE)*100000:_MI1_MAX_VAR_BUFFER_SIZE*10, 1.0);
125 
126   H5E_BEGIN_TRY {
127 #ifdef HDF5_MMAP_TEST
128     if (mode & 0x8000) {
129 
130       H5Pset_fapl_mmap(prp_id, 8192, 1);
131       fd = H5Fopen(path, mode & 0x7FFF, prp_id);
132     } else {
133       fd = H5Fopen(path, mode, prp_id);
134     }
135 #else
136     fd = H5Fopen(path, mode, prp_id);
137 #endif
138   } H5E_END_TRY;
139 
140   H5Pclose(prp_id);
141 
142 
143   /* Open the image variables.
144    */
145   //TODO: convert
146 
147 //   H5E_BEGIN_TRY
148 //   {
149 //     dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image");
150 //     if (dset_id >= 0) {
151 //       hid_t type_id;
152 //       int is_compound = 0;
153 //
154 //       hdf_get_diminfo(dset_id, &ndims, dims);
155 //
156 //       #ifndef NO_EMULATE_VECTOR_DIMENSION
157 //       /* See if a vector_dimension needs to be emulated.
158 //        */
159 //       type_id = H5Dget_type(dset_id);
160 //       if (type_id >= 0) {
161 //         if (H5Tget_class(type_id) == H5T_COMPOUND) {
162 //           /* OK, it's compound type. */
163 //           struct m2_dim *dim = hdf_dim_add(file, MIvector_dimension,
164 //                                            H5Tget_nmembers(type_id));
165 //           dim->is_fake = 1;
166 //           dims[ndims++] = H5Tget_nmembers(type_id);
167 //           is_compound = 1;
168 //         }
169 //         H5Tclose(type_id);
170 //       }
171 //       #endif /* NO_EMULATE_VECTOR_DIMENSION */
172 //
173 //       var = hdf_var_add(file, MIimage, "/minc-2.0/image/0/image",
174 //                         ndims, dims);
175 //       var->is_cmpd = is_compound;
176 //
177 //       H5Dclose(dset_id);
178 //     }
179 //
180 //     dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-min");
181 //     if (dset_id >= 0) {
182 //       hdf_get_diminfo(dset_id, &ndims, dims);
183 //       hdf_var_add(file, MIimagemin, "/minc-2.0/image/0/image-min",
184 //                   ndims, dims);
185 //       H5Dclose(dset_id);
186 //     }
187 //
188 //     dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-max");
189 //     if (dset_id >= 0) {
190 //       hdf_get_diminfo(dset_id, &ndims, dims);
191 //       hdf_var_add(file, MIimagemax, "/minc-2.0/image/0/image-max",
192 //                   ndims, dims);
193 //       H5Dclose(dset_id);
194 //     }
195 //   } H5E_END_TRY;
196 //
197 //   /* Open all of the datasets in the "dimensions" category.
198 //    */
199 //   grp_id = H5Gopen2(fd, "/minc-2.0/dimensions", H5P_DEFAULT);
200 //   hdf_open_dsets(file, grp_id, "/minc-2.0/dimensions/", 1);
201 //   H5Gclose(grp_id);
202 //
203 //   /* Open all of the datasets in the "info" category.
204 //    */
205 //   grp_id = H5Gopen2(fd, "/minc-2.0/info", H5P_DEFAULT);
206 //   hdf_open_dsets(file, grp_id, "/minc-2.0/info/", 0);
207 //   H5Gclose(grp_id);
208   return fd;
209 }
210 
211 
212 /**
213  * Create an HDF5 file.
214  */
_hdf_create(const char * path,int cmode)215 static hid_t _hdf_create(const char *path, int cmode)
216 {
217   hid_t grp_id;
218   hid_t fd;
219   hid_t tmp_id;
220   hid_t hdf_gpid;
221   hid_t fpid;
222 
223   fpid = H5Pcreate (H5P_FILE_ACCESS);
224 
225   /* Limit filetype to 1.8.x */
226   H5Pset_libver_bounds(fpid, H5F_LIBVER_V18, H5F_LIBVER_V18);
227 
228   H5Pset_cache(fpid, 0, 2503, miget_cfg_present(MICFG_MINC_FILE_CACHE)?miget_cfg_int(MICFG_MINC_FILE_CACHE)*100000:_MI1_MAX_VAR_BUFFER_SIZE*100, 1.0);
229 
230   H5E_BEGIN_TRY {
231     fd = H5Fcreate(path, cmode, H5P_DEFAULT, fpid);
232   } H5E_END_TRY;
233 
234   if (fd < 0) {
235     /*TODO: report error properly*/
236     return MI_LOG_ERROR(MI2_MSG_CREATEFILE,path);
237   }
238 
239   /* Create the default groups.
240    * Should we use a non-zero value for size_hint (parameter 3)???
241    */
242 
243   hdf_gpid = H5Pcreate (H5P_GROUP_CREATE);
244   H5Pset_attr_phase_change (hdf_gpid, 0, 0);
245 
246   MI_CHECK_HDF_CALL_RET(grp_id = H5Gcreate2(fd, MI_ROOT_PATH , H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2")
247 
248   MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "dimensions", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2")
249   H5Gclose(tmp_id);
250 
251   MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "info", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2")
252   H5Gclose(tmp_id);
253 
254   MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "image", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2")
255 
256   H5Gclose(tmp_id);
257   MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "image/0", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2")
258 
259   H5Pclose ( hdf_gpid );
260   H5Gclose(tmp_id);
261   H5Gclose(grp_id);
262 
263   return fd;
264 }
265 
_hdf_close(hid_t fd)266 static int _hdf_close(hid_t fd)
267 {
268   //TODO: make sure we save all that is needed
269 
270   MI_CHECK_HDF_CALL_RET(H5Fclose(fd),"H5Fclose")
271   return MI_NOERROR;
272 }
273 
274 
275 /** Create the actual image for the volume.
276   * Note that the image dataset muct be created in the hierarchy
277   * before the image data can be added.
278   * \ingroup mi2Vol
279 */
micreate_volume_image(mihandle_t volume)280 int micreate_volume_image(mihandle_t volume)
281 {
282   char dimorder[MI2_CHAR_LENGTH];
283   int i;
284   int dimorder_len=0;
285   hid_t dataspace_id;
286   hid_t dset_id;
287   hsize_t hdf_size[MI2_MAX_VAR_DIMS];
288 
289   /* Try creating IMAGE dataset i.e. /minc-2.0/image/0/image
290   */
291 
292   dimorder[0] = '\0'; /* Set string to empty */
293 
294   for (i = 0; i < volume->number_of_dims; i++) {
295     hdf_size[i] = volume->dim_handles[i]->length;
296 
297     /* Create the dimorder string, ordered comma-separated
298       list of dimension names.
299     */
300     strncat(dimorder, volume->dim_handles[i]->name, MI2_CHAR_LENGTH - 1 - strlen(dimorder)); /*as a replacement for strlcat*/
301     if (i != volume->number_of_dims - 1) {
302       strncat(dimorder, ",", MI2_CHAR_LENGTH - 1);
303     }
304   }
305 
306 
307   /* Create a SIMPLE dataspace  */
308   dataspace_id = H5Screate_simple(volume->number_of_dims, hdf_size, NULL);
309   if (dataspace_id < 0) {
310     return MI_ERROR;
311   }
312 
313   MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate2(volume->hdf_id, MI_ROOT_PATH "/image/0/image",
314                                              volume->ftype_id,
315                                              dataspace_id, H5P_DEFAULT,
316                                              volume->plist_id,H5P_DEFAULT),"H5Dcreate2")
317 
318   volume->image_id = dset_id;
319 
320   add_standard_minc_attributes(volume->hdf_id,volume->image_id);
321 
322   /* Create the dimorder attribute, ordered comma-separated
323     list of dimension names.
324   */
325   miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING,
326                     strlen(dimorder), dimorder);
327 
328   H5Sclose(dataspace_id);
329 
330   if (volume->volume_class == MI_CLASS_REAL) {
331     int ndims;
332     hid_t dcpl_id;
333     double dtmp;
334 
335     MI_CHECK_HDF_CALL_RET(dcpl_id = H5Pcreate(H5P_DATASET_CREATE),"H5Pcreate")
336 
337     if (volume->has_slice_scaling && (volume->number_of_dims > 2) ) {
338       /* TODO: Find the slowest-varying spatial dimension; that forms
339       * the basis for the image-min and image-max variables.  Right
340       * now this is an oversimplification!
341       */
342       ndims = volume->number_of_dims - 2;
343       MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate_simple(ndims, hdf_size, NULL),"H5Screate_simple")
344     } else {
345       ndims = 0;
346       MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate(H5S_SCALAR),"H5Screate")
347     }
348 
349     if (ndims != 0) {
350       dimorder[0] = '\0'; /* Set string to empty */
351       for (i = 0; i < ndims; i++) {
352         /* Create the dimorder string, ordered comma-separated
353           list of dimension names.
354         */
355         strncat(dimorder, volume->dim_handles[i]->name, MI2_CHAR_LENGTH - 1 - strlen(dimorder));
356         if (i != ndims - 1) {
357           strncat(dimorder, ",", MI2_CHAR_LENGTH - 1 - strlen(dimorder));
358         }
359       }
360     }
361 
362     /* Create the image minimum dataset for FULL-RESOLUTION storage of data
363     */
364     dtmp = 0.0;
365     H5Pset_fill_value(dcpl_id, H5T_NATIVE_DOUBLE, &dtmp);
366 
367     MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate2(volume->hdf_id, MI_ROOT_PATH "/image/0/image-min",
368                          H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT,dcpl_id,H5P_DEFAULT),"H5Dcreate2")
369     if (ndims != 0) {
370       miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING,
371                         strlen(dimorder), dimorder);
372     }
373     volume->imin_id = dset_id;
374     add_standard_minc_attributes(volume->hdf_id,volume->imin_id);
375 
376     /* Create the image maximum dataset for FULL-RESOLUTION storage of data
377     */
378     dtmp = 1.0;
379     H5Pset_fill_value(dcpl_id, H5T_NATIVE_DOUBLE, &dtmp);
380 
381     MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate2(volume->hdf_id, MI_ROOT_PATH "/image/0/image-max",
382                          H5T_IEEE_F64LE, dataspace_id,H5P_DEFAULT, dcpl_id, H5P_DEFAULT),"H5Dcreate2")
383     if (ndims != 0) {
384       miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING,
385                         strlen(dimorder), dimorder);
386     }
387     volume->imax_id = dset_id;
388     add_standard_minc_attributes(volume->hdf_id,volume->imax_id);
389     H5Sclose(dataspace_id);
390     H5Pclose(dcpl_id);
391   }
392 
393   return (MI_NOERROR);
394 }
395 
396 /** Set up the array of conversions from voxel to world coordinate order.
397 */
miset_volume_world_indices(mihandle_t hvol)398 static int miset_volume_world_indices(mihandle_t hvol)
399 {
400   int i;
401 
402   for (i = 0; i < hvol->number_of_dims; i++) {
403     midimhandle_t hdim = hvol->dim_handles[i];
404 
405     hdim->world_index = -1;
406     if (hdim->dim_class == MI_DIMCLASS_SPATIAL) {
407       if (!strcmp(hdim->name, MIxspace)) {
408         hdim->world_index = MI2_X;
409       } else if (!strcmp(hdim->name, MIyspace)) {
410         hdim->world_index = MI2_Y;
411       } else if (!strcmp(hdim->name, MIzspace)) {
412         hdim->world_index = MI2_Z;
413       }
414     } else if (hdim->dim_class == MI_DIMCLASS_SFREQUENCY) {
415       if (!strcmp(hdim->name, MIxfrequency)) {
416         hdim->world_index = MI2_X;
417       } else if (!strcmp(hdim->name, MIyfrequency)) {
418         hdim->world_index = MI2_Y;
419       } else if (!strcmp(hdim->name, MIzfrequency)) {
420         hdim->world_index = MI2_Z;
421       }
422     }
423   }
424   return (MI_NOERROR);
425 }
426 
427 /** Create and initialize a MINC 2.0 volume structure.
428 */
mialloc_volume_handle(void)429 static mihandle_t mialloc_volume_handle(void)
430 {
431   mihandle_t handle = (mihandle_t) malloc(sizeof(struct mivolume));
432 
433   if (handle != NULL) {
434     /* Clear the memory by default. */
435     memset(handle, 0, sizeof(struct mivolume));
436 
437     /* Set the defaults for the data structure */
438     handle->scale_min = 0.0;
439     handle->scale_max = 1.0;
440     handle->image_id = -1;
441     handle->imax_id = -1;
442     handle->imin_id = -1;
443     handle->plist_id = -1;
444     handle->has_slice_scaling = FALSE;
445     handle->is_dirty = FALSE;
446     handle->dim_indices = NULL;
447     handle->selected_resolution = 0;
448   }
449   return (handle);
450 }
451 
452 /** Create a volume with the specified name, dimensions,
453     type, class, volume properties and retrieve the volume handle.
454     \ingroup mi2Vol
455 */
micreate_volume(const char * filename,int number_of_dimensions,midimhandle_t dimensions[],mitype_t volume_type,miclass_t volume_class,mivolumeprops_t create_props,mihandle_t * volume)456 int micreate_volume(const char *filename, int number_of_dimensions,
457                 midimhandle_t dimensions[], mitype_t volume_type,
458                 miclass_t volume_class, mivolumeprops_t create_props,
459                 mihandle_t *volume)
460 {
461   int i;
462   int stat;
463   hid_t file_id;
464   hid_t hdf_type;
465   hid_t hdf_plist;
466   hid_t fspc_id;
467   hsize_t dim[1];
468   hid_t grp_id;
469   herr_t status;
470   hid_t dataset_id = -1;
471   hid_t dataset_width = -1;
472   hid_t dataspace_id = -1;
473   char *name;
474   size_t size;
475   hsize_t hdf_size[MI2_MAX_VAR_DIMS];
476   mihandle_t handle;
477   mivolumeprops_t props_handle;
478   char ident_str[128];
479   hid_t tmp_type;
480   int   dimension_is_vector = 0;
481 
482   /* Initialization.
483     For the actual body of this function look at m2utils.c
484   */
485   miinit();
486 
487   /* Validate the parameters.
488   */
489   if (filename == NULL) {
490     return MI_LOG_ERROR(MI2_MSG_CREATEFILE," (NULL) ");
491   }
492 
493   if (dimensions == NULL && number_of_dimensions != 0) {
494     return MI_LOG_ERROR(MI2_MSG_GENERIC," Can't create volume with undefined dimensions");
495   }
496 
497   /* Allocate space for the volume handle
498   */
499   handle = mialloc_volume_handle();
500   if (handle == NULL) {
501     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,sizeof(struct mivolume));
502   }
503 
504   /* Initialize some of the variables associated with the volume handle.
505   */
506   handle->mode = MI2_OPEN_RDWR;
507   handle->number_of_dims = number_of_dimensions;
508 
509   /* convert minc type to hdf type
510   */
511   hdf_type = mitype_to_hdftype(volume_type, FALSE);
512 
513   /* Setting up volume type_id
514   */
515   switch (volume_class) {
516   case MI_CLASS_REAL:
517   case MI_CLASS_INT:
518     handle->ftype_id = hdf_type;
519     handle->mtype_id = H5Tget_native_type(handle->ftype_id,
520                                           H5T_DIR_ASCEND);
521     break;
522 
523   case MI_CLASS_LABEL:
524     /* A volume of class LABEL must have an integer type (positive).
525     */
526     switch (volume_type) {
527     case MI_TYPE_UBYTE:
528     case MI_TYPE_USHORT:
529     case MI_TYPE_UINT:
530     case MI_TYPE_BYTE:
531     case MI_TYPE_SHORT:
532     case MI_TYPE_INT:
533       MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Tenum_create(hdf_type),"H5Tenum_create")
534 
535       tmp_type = H5Tget_native_type(hdf_type, H5T_DIR_ASCEND);
536       H5Tclose(hdf_type);
537       hdf_type = tmp_type;
538 
539       /* Create an enumerated type with the native type as it's base.
540       */
541       MI_CHECK_HDF_CALL_RET(handle->mtype_id = H5Tenum_create(hdf_type),"H5Tenum_create")
542 
543       H5Tclose(hdf_type);
544 
545       miinit_enum(handle->ftype_id);
546       miinit_enum(handle->mtype_id);
547       break;
548     default:
549       free(handle);
550       return (MI_ERROR);
551     }
552     break;
553 
554   case MI_CLASS_COMPLEX:
555     switch (volume_type) {
556     case MI_TYPE_SCOMPLEX:
557     case MI_TYPE_ICOMPLEX:
558     case MI_TYPE_FCOMPLEX:
559     case MI_TYPE_DCOMPLEX:
560       handle->ftype_id = hdf_type;
561       handle->mtype_id = mitype_to_hdftype(volume_type, TRUE);
562       break;
563     default:
564       free(handle);
565       return MI_LOG_ERROR(MI2_MSG_BADTYPE,volume_type);
566     }
567     break;
568 
569   case MI_CLASS_UNIFORM_RECORD:
570     MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Tcreate(H5T_COMPOUND, H5Tget_size(hdf_type)),"H5Tcreate")
571     MI_CHECK_HDF_CALL_RET(handle->mtype_id = H5Tcreate(H5T_COMPOUND, H5Tget_size(hdf_type)),"H5Tcreate")
572     H5Tclose(hdf_type);
573     break;
574 
575   default:
576     free(handle);
577     return (MI_ERROR);
578   }
579 
580   handle->volume_class = volume_class;
581 
582   /* Create file in HDF5 with the given filename and
583     H5F_ACC_TRUNC: Truncate file, if it already exists,
584                   erasing all data previously stored in the file.
585     and create ID and ID access as default.
586   */
587 
588   file_id = _hdf_create(filename, H5F_ACC_TRUNC);
589   if (file_id < 0) {
590     free(handle);
591     return (MI_ERROR);
592   }
593 
594   handle->hdf_id = file_id;
595 
596   _generate_ident(ident_str, sizeof(ident_str));
597   miset_attribute(handle, MI_ROOT_PATH, "ident", MI_TYPE_STRING,
598                   strlen(ident_str), ident_str);
599   miset_attribute(handle, MI_ROOT_PATH, "minc_version", MI_TYPE_STRING,
600                   strlen(MINC_VERSION), MINC_VERSION);
601 
602   _miset_volume_class(handle, handle->volume_class);
603 
604   /* Create a new property list for the volume
605   */
606   MI_CHECK_HDF_CALL_RET(hdf_plist = H5Pcreate(H5P_DATASET_CREATE),"H5Pcreate")
607 
608   handle->plist_id = hdf_plist;
609 
610   /* Set fill value to guarantee valid data on incomplete datasets.
611   */
612   if (volume_class != MI_CLASS_LABEL &&
613       volume_class != MI_CLASS_UNIFORM_RECORD) {
614     size_t siz = H5Tget_size(handle->ftype_id);
615     char *tmp = calloc(1, siz);
616     H5Pset_fill_value(hdf_plist, handle->ftype_id, tmp);
617     free(tmp);
618   }
619 
620   /* See if chunking and/or compression should be enabled
621     and if yes set the type of storage used to store the
622     raw data for a dataset.
623   */
624 
625   if (create_props != NULL  &&
626       ( create_props->compression_type == MI_COMPRESS_ZLIB ||
627         create_props->edge_count != 0 )
628       )
629   {
630     /* Set the storage to CHUNKED */
631     MI_CHECK_HDF_CALL_RET( stat = H5Pset_layout(hdf_plist, H5D_CHUNKED),"H5Pset_layout")
632 
633 
634     if(create_props->edge_count != 0) {
635       /* Create an array, hdf_size, containing the size of each chunk
636       */
637       for ( i=0; i < number_of_dimensions; i++) {
638         hdf_size[i] = create_props->edge_lengths[i];
639         /* If the size of each chunk is greater than the size of
640           the corresponding dimension, set the chunk size to the
641           dimension size
642         */
643         if (hdf_size[i] > dimensions[i]->length) {
644             hdf_size[i] = dimensions[i]->length;
645         }
646       }
647     } else {
648       hsize_t val = 1;
649       size_t unit_size = H5Tget_size(handle->ftype_id);
650       /*adopted code from hdf_convenience.c:1360 to match behaviour of MINC1 API*/
651       for( i = number_of_dimensions-1; i >= 0; i-- ) {
652           if( _MI1_MAX_VAR_BUFFER_SIZE > dimensions[i]->length * val * unit_size ) {
653               hdf_size[i] = dimensions[i]->length;
654           } else {
655             if ( dimensions[i]->length < (hsize_t)( _MI1_MAX_VAR_BUFFER_SIZE / ( val * unit_size ) ) )
656               hdf_size[i] = dimensions[i]->length;
657             else
658               hdf_size[i] = (hsize_t)( _MI1_MAX_VAR_BUFFER_SIZE / ( val * unit_size ));
659           }
660           val *= hdf_size[i];
661       }
662     }
663 
664     /* Sets the size of the chunks used to store a chunked layout dataset */
665     MI_CHECK_HDF_CALL_RET(stat = H5Pset_chunk(hdf_plist, number_of_dimensions, hdf_size),"H5Pset_chunk")
666 
667     /* Sets compression method and compression level */
668     MI_CHECK_HDF_CALL_RET(stat = H5Pset_deflate(hdf_plist, create_props->zlib_level),"H5Pset_deflate")
669 
670 
671     if (create_props->checksum )
672     {
673       MI_CHECK_HDF_CALL_RET(H5Pset_fletcher32(hdf_plist),"H5Pset_fletcher32")
674     }
675 
676 
677   } else { /* No COMPRESSION or CHUNKING is enabled */
678 
679     MI_CHECK_HDF_CALL_RET(stat = H5Pset_layout(hdf_plist, H5D_CONTIGUOUS),"H5Pset_layout") /*  CONTIGUOUS data */
680   }
681 
682   /* See if Multi-res is set to a level above 0 and if yes create subgroups
683     i.e., /minc-2.0/image/1/..
684           /minc-2.0/image/2/..  etc
685   */
686   // must add some code to make sure that the res level is possible
687   if (create_props != NULL && create_props->depth > 0) {
688     for (i=0; i < create_props->depth ; i++) {
689       if (minc_create_thumbnail(handle, i+1) < 0) {
690         free(handle);
691         return (MI_ERROR);
692       }
693     }
694   }
695 
696 
697   /* Try creating DIMENSIONS GROUP i.e. /minc-2.0/dimensions
698   */
699   MI_CHECK_HDF_CALL_RET(grp_id = H5Gopen1(file_id, MI_FULLDIMENSIONS_PATH),"H5Gopen1")
700   /* Once the DIMENSIONS GROUP is opened, create each dimension.
701   */
702 
703   for (i=0; i < number_of_dimensions ; i++) {
704     /* First create the dataspace required to create a
705       dimension variable (dataset)
706     */
707     if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) {
708       dim[0] = dimensions[i]->length;
709       MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate_simple(1, dim, NULL),"H5Screate_simple")
710     } else {
711       MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate(H5S_SCALAR),"H5Screate")
712     }
713 
714     if (dataspace_id < 0) {
715       free(handle);
716       return (MI_ERROR);
717     }
718 
719     dimension_is_vector= (strcmp ( dimensions[i]->name, MIvector_dimension ) == 0 );
720 
721 
722     /* Create a dataset(dimension variable name) in DIMENSIONS GROUP */
723     MI_CHECK_HDF_CALL_RET(dataset_id = H5Dcreate2(grp_id, dimensions[i]->name,
724                             H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT,  H5P_DEFAULT, H5P_DEFAULT),"H5Dcreate2")
725 
726     /* Dimension variable for a regular dimension contains
727       no meaningful data. Whereas, Dimension variable for
728       an irregular dimension contains a vector with the lengths
729       equal to the sampled points along the dimension.
730       Also, create a variable named "<dimension>-width" and
731       write the dimension->widths.
732     */
733 
734     if(!dimension_is_vector )
735       add_standard_minc_attributes(file_id,dataset_id);
736       /*vector dimension is a record (?)*/
737 
738     /* Check for irregular dimension and make sure
739       offset values are provided for this dimension
740     */
741     if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) {
742       if (dimensions[i]->offsets == NULL) {
743         free(handle);
744         return (MI_ERROR);
745       } else {
746 
747         /* If dimension is regularly sampled */
748         MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dataset_id),"H5Dget_space")
749 
750         /* Write the raw data from buffer (dimensions[i]->offsets)
751           to the dataset.
752         */
753         MI_CHECK_HDF_CALL_RET(status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, dataspace_id,
754                           fspc_id, H5P_DEFAULT, dimensions[i]->offsets),"H5Dwrite")
755 
756         /* Write the raw data from buffer (dimensions[i]->offsets)
757           to the dataset.
758         */
759         size = strlen(dimensions[i]->name) + 6 + 1;
760         name = malloc(size);
761         strcpy(name, dimensions[i]->name);
762         strcat(name, "-width");
763 
764         /* Create dataset dimension_name-width */
765         dataset_width = H5Dcreate2(grp_id, name, H5T_IEEE_F64LE,
766                                    dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
767         /* Return an Id for the dataspace of the dataset dataset_width */
768         MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dataset_width),"H5Dget_space")
769 
770         /* Write the raw data from buffer (dimensions[i]->widths)
771           to the dataset.
772         */
773         MI_CHECK_HDF_CALL_RET(status = H5Dwrite(dataset_width, H5T_NATIVE_DOUBLE, dataspace_id, fspc_id, H5P_DEFAULT, dimensions[i]->widths),"H5Dwrite")
774 
775         miset_attr_at_loc(dataset_id, "dimorder", MI_TYPE_STRING,
776                           strlen(dimensions[i]->name), dimensions[i]->name);
777 
778         miset_attr_at_loc(dataset_width, "dimorder", MI_TYPE_STRING,
779                           strlen(dimensions[i]->name), dimensions[i]->name);
780 
781         /* Create new attribute "length", with appropriate
782           type (to hdf5) conversion.
783           miset_attr_at_loc(..) is implemented at m2utils.c
784         */
785         miset_attr_at_loc(dataset_width, "length", MI_TYPE_INT,
786                           1, &dimensions[i]->length);
787         /* Close the specified datatset */
788         H5Dclose(dataset_width);
789         free(name);
790       }
791     }
792 
793     if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) {
794       name = "irregular";
795     } else {
796       name = "regular__";
797     }
798     /* Create attribute "spacing" and set its value to
799       "regular__" or "irregular"
800     */
801 
802     if(!dimension_is_vector)
803       miset_attr_at_loc(dataset_id, "spacing", MI_TYPE_STRING,
804                       strlen(name), name);
805 
806     switch (dimensions[i]->dim_class) {
807     case MI_DIMCLASS_SPATIAL:
808       name = "spatial";
809       break;
810     case MI_DIMCLASS_TIME:
811       name = "time___";
812       break;
813     case MI_DIMCLASS_SFREQUENCY:
814       name = "sfreq__";
815       break;
816     case MI_DIMCLASS_TFREQUENCY:
817       name = "tfreq__";
818       break;
819     case MI_DIMCLASS_USER:
820       name = "user___";
821       break;
822     case MI_DIMCLASS_RECORD:
823       name = "record_";
824       break;
825     case MI_DIMCLASS_ANY:
826     default:
827       /* These should not be seen in this context!!!
828       */
829       return (MI_ERROR);
830     }
831 
832     /* Save dimension length */
833     miset_attr_at_loc(dataset_id, "length", MI_TYPE_INT,
834                       1, &dimensions[i]->length);
835 
836     /* Create Dimension attribute "direction_cosines"  */
837     if(dimensions[i]->dim_class == MI_DIMCLASS_SPATIAL)
838       miset_attr_at_loc(dataset_id, "direction_cosines", MI_TYPE_DOUBLE,
839                       3, dimensions[i]->direction_cosines);
840 
841     if(!dimension_is_vector)
842     {
843       const char *align_str;
844 
845       miset_attr_at_loc(dataset_id, "class", MI_TYPE_STRING, strlen(name),
846                       name);
847 
848 
849       /* Save step value. */
850       miset_attr_at_loc(dataset_id, "step", MI_TYPE_DOUBLE,
851                       1, &dimensions[i]->step);
852 
853       /* Save start value. */
854       miset_attr_at_loc(dataset_id, "start", MI_TYPE_DOUBLE,
855                       1, &dimensions[i]->start);
856 
857       if (dimensions[i]->align == MI_DIMALIGN_END)
858         align_str = "end___";
859       else if (dimensions[i]->align == MI_DIMALIGN_START)
860         align_str = "start_";
861       else
862         align_str = "centre";
863       miset_attr_at_loc(dataset_id, "alignment", MI_TYPE_STRING,
864                         strlen(align_str), align_str);
865 
866       /* Save units. */
867       miset_attr_at_loc(dataset_id, "units", MI_TYPE_STRING,
868                       strlen(dimensions[i]->units), dimensions[i]->units);
869 
870       /* Save sample width. */
871       miset_attr_at_loc(dataset_id, "width", MI_TYPE_DOUBLE,
872                       1,  &dimensions[i]->width);
873     }
874 
875     /* Save comments. If user has not specified
876       any comments, do not add this attribute
877     */
878 
879     if (dimensions[i]->comments != NULL) {
880       miset_attr_at_loc(dataset_id, "comments", MI_TYPE_STRING,
881                         strlen(dimensions[i]->comments),
882                         dimensions[i]->comments);
883     }
884 
885     /* Close the dataset with the specified Id
886     */
887     H5Dclose(dataset_id);
888 
889 
890   } //for (i=0; i < number_of_dimensions ; i++)
891 
892   /* Close the group with the specified Id
893   */
894   H5Gclose(grp_id);
895 
896   /* Allocate space for all the dimension handles
897     Note, each volume handle is associated with an array of
898     dimension handles in the order that they were create (i.e, file order)
899   */
900   handle->dim_handles = (midimhandle_t *)malloc(number_of_dimensions *
901                         sizeof(midimhandle_t));
902 
903   if (handle->dim_handles == NULL) {
904     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,number_of_dimensions * sizeof(midimhandle_t));
905   }
906 
907   /* Once the space for all dimension handles is created
908     fill the dimension handle array with the appropriate
909     dimension handle.
910   */
911   for (i = 0; i < number_of_dimensions; i++) {
912     handle->dim_handles[i] = dimensions[i];
913     dimensions[i]->volume_handle = handle;
914   }
915 
916   miset_volume_world_indices(handle);
917 
918   /* Verify the volume type.
919   */
920   switch (volume_type) {
921   case MI_TYPE_BYTE:
922   case MI_TYPE_SHORT:
923   case MI_TYPE_INT:
924   case MI_TYPE_FLOAT:
925   case MI_TYPE_DOUBLE:
926   case MI_TYPE_STRING:
927   case MI_TYPE_UBYTE:
928   case MI_TYPE_USHORT:
929   case MI_TYPE_UINT:
930   case MI_TYPE_SCOMPLEX:
931   case MI_TYPE_ICOMPLEX:
932   case MI_TYPE_FCOMPLEX:
933   case MI_TYPE_DCOMPLEX:
934   case MI_TYPE_UNKNOWN:
935     break;
936   default:
937     return MI_LOG_ERROR(MI2_MSG_BADTYPE,volume_type);
938   }
939 
940   handle->volume_type = volume_type;
941 
942   /* Set the initial value of the valid-range
943   */
944   miinit_default_range(handle->volume_type,
945                        &handle->valid_max,
946                        &handle->valid_min);
947 
948   /* Get the voxel to world transform for the volume
949   */
950   miget_voxel_to_world(handle, handle->v2w_transform);
951 
952   /* Calculate the inverse transform */
953   miinvert_transform(handle->v2w_transform, handle->w2v_transform);
954 
955   /* Allocated space for the volume properties */
956   props_handle = (mivolumeprops_t)malloc(sizeof(struct mivolprops));
957   /* Initialize volume properties with zero */
958   memset(props_handle, 0, sizeof (struct mivolprops));
959   /* If volume properties is specified by the user
960     set all the properties of the volume handle
961   */
962   if (create_props != NULL) {
963     /* Set the enable_flag for multi-resolution */
964     props_handle->enable_flag = create_props->enable_flag;
965     /* Set the depth of multi-resolution, i.e., how many
966     levels of resolution is specified maximum is 16.
967     */
968     props_handle->depth = create_props->depth;
969     /* Set compression type, currently two values
970     either no compression or zlib is applicable.
971     */
972     switch (create_props->compression_type) {
973     case MI_COMPRESS_NONE:
974       props_handle->compression_type = MI_COMPRESS_NONE;
975       break;
976     case MI_COMPRESS_ZLIB:
977       props_handle->compression_type = MI_COMPRESS_ZLIB;
978       break;
979     default:
980       free(props_handle);
981       return MI_LOG_ERROR(MI2_MSG_BADTYPE,create_props->compression_type);
982     }
983     /* Note that setting compression on (i.e., MI_COMPRESS_ZLIB)
984     turns chunking on by default. Need to set the number of chunks
985     (edge_count)
986     */
987     props_handle->zlib_level = create_props->zlib_level;
988     props_handle->edge_count = create_props->edge_count;
989     /* Allocate space for an array which holds the size of each chunk
990     and fill the array with the appropriiate chunk sizes.
991     */
992     props_handle->edge_lengths = (int *)malloc(create_props->max_lengths*sizeof(int));
993     for (i=0; i<create_props->max_lengths; i++) {
994       props_handle->edge_lengths[i] = create_props->edge_lengths[i];
995     }
996 
997     props_handle->max_lengths = create_props->max_lengths;
998     props_handle->record_length = create_props->record_length;
999 
1000     /* Explicitly allocate storage for name
1001     */
1002     if (create_props->record_name != NULL) {
1003       props_handle->record_name =malloc(strlen(create_props->record_name) + 1 );
1004       strcpy(props_handle->record_name, create_props->record_name);
1005     }
1006     props_handle->template_flag = create_props->template_flag;
1007   }
1008   /* Set the handle to volume properties */
1009   handle->create_props = props_handle;
1010   /* Return volume handle */
1011   *volume = handle;
1012 
1013   return (MI_NOERROR);
1014 }
1015 
1016 /** Return the number of dimensions associated with this volume.
1017   * \ingroup mi2Vol
1018 */
miget_volume_dimension_count(mihandle_t volume,midimclass_t cls,midimattr_t attr,int * number_of_dimensions)1019 int miget_volume_dimension_count(mihandle_t volume, midimclass_t cls,
1020                              midimattr_t attr, int *number_of_dimensions)
1021 {
1022   int i, count=0;
1023   /* Validate the parameters */
1024   if (volume == NULL || number_of_dimensions == NULL) {
1025     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get dimension count with null volume or null variable");
1026   }
1027   /* For each dimension check to make sure that dimension class and
1028     attribute match with the specified parameters and if yes
1029     increment the dimension count
1030   */
1031   for (i=0; i< volume->number_of_dims; i++) {
1032     if ((cls == MI_DIMCLASS_ANY || volume->dim_handles[i]->dim_class == cls) &&
1033         (attr == MI_DIMATTR_ALL || volume->dim_handles[i]->attr == attr)) {
1034       count++;
1035     }
1036   }
1037 
1038   *number_of_dimensions = count;
1039   return (MI_NOERROR);
1040 }
1041 
1042 /** Returns the number of voxels in the volume.
1043   * \ingroup mi2Vol
1044 */
miget_volume_voxel_count(mihandle_t volume,misize_t * number_of_voxels)1045 int miget_volume_voxel_count(mihandle_t volume, misize_t *number_of_voxels)
1046 {
1047   char path[MI2_MAX_PATH];
1048   hid_t dset_id;
1049   hid_t fspc_id;
1050 
1051   /* Validate parameters */
1052   if (volume == NULL || number_of_voxels == NULL) {
1053     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get voxel count with null volume or null variable");
1054   }
1055 
1056   /* Quickest way to do this is with the dataspace identifier of the
1057   * volume. Use the volume's current resolution.
1058   */
1059   sprintf(path, MI_ROOT_PATH "/image/%d/image", volume->selected_resolution);
1060   /* Open the dataset with the specified path
1061   */
1062   MI_CHECK_HDF_CALL_RET(dset_id = H5Dopen1(volume->hdf_id, path),"H5Dopen1");
1063 
1064   /* Get an Id to the copy of the dataspace */
1065   MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dset_id),"H5Dget_space");
1066   /* Determines the number of elements in the dataspace and
1067     cast the result to an integer.
1068   */
1069   *number_of_voxels = (misize_t) H5Sget_simple_extent_npoints(fspc_id);
1070   /* Close the dataspace */
1071   H5Sclose(fspc_id);
1072   /* Close the dataset */
1073   H5Dclose(dset_id);
1074   return (MI_NOERROR);
1075 }
1076 
1077 /* Get the number of dimensions in the file */
_miget_file_dimension_count(hid_t file_id)1078 static int _miget_file_dimension_count(hid_t file_id)
1079 {
1080   hid_t dset_id;
1081   hid_t space_id;
1082   int result = -1;
1083   /* hdf5 macro can temporarily disable the automatic error printing */
1084   H5E_BEGIN_TRY {
1085     dset_id = midescend_path(file_id, MI_ROOT_PATH "/image/0/image");
1086   } H5E_END_TRY;
1087 
1088   if (dset_id >= 0) {
1089     /* Get an Id to the copy of the dataspace */
1090     MI_CHECK_HDF_CALL(space_id = H5Dget_space(dset_id),"H5Dget_space");
1091 
1092     if (space_id > 0) {
1093       /* Determine the dimensionality of the dataspace */
1094       MI_CHECK_HDF_CALL(result = H5Sget_simple_extent_ndims(space_id),"H5Sget_simple_extent_ndims");
1095       /* Close the dataspace */
1096       H5Sclose(space_id);
1097     }
1098     /* Close the dataset */
1099     H5Dclose(dset_id);
1100   }
1101   return (result);
1102 }
1103 
1104 /* Get dimension variable attributes for the given dimension name */
_miset_volume_class(mihandle_t volume,miclass_t volume_class)1105 static int _miset_volume_class(mihandle_t volume, miclass_t volume_class)
1106 {
1107   const char *class_ptr;
1108 
1109   switch (volume_class) {
1110   case MI_CLASS_REAL:
1111     class_ptr = "real___";
1112     break;
1113   case MI_CLASS_INT:
1114     class_ptr = "integer";
1115     break;
1116   case MI_CLASS_LABEL:
1117     class_ptr = "label__";
1118     break;
1119   case MI_CLASS_COMPLEX:
1120     class_ptr = "complex";
1121     break;
1122   case MI_CLASS_UNIFORM_RECORD:
1123     class_ptr = "array__";
1124     break;
1125   default:
1126     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Unknown volume class");
1127   }
1128   miset_attribute(volume, MI_ROOT_PATH, "class", MI_TYPE_STRING,
1129                   strlen(class_ptr), class_ptr);
1130   return (MI_NOERROR);
1131 }
1132 
_miget_volume_class(mihandle_t volume,miclass_t * volume_class)1133 static int _miget_volume_class(mihandle_t volume, miclass_t *volume_class)
1134 {
1135   char class_buf[MI2_CHAR_LENGTH];
1136 
1137   if( miget_attribute(volume, MI_ROOT_PATH, "class", MI_TYPE_STRING,
1138                   MI2_CHAR_LENGTH, class_buf) == MI_NOERROR )
1139   {
1140     if (!strcmp(class_buf, "label__")) {
1141       *volume_class = MI_CLASS_LABEL;
1142     } else if (!strcmp(class_buf, "integer")) {
1143       *volume_class = MI_CLASS_INT;
1144     } else if (!strcmp(class_buf, "complex")) {
1145       *volume_class = MI_CLASS_COMPLEX;
1146     } else if (!strcmp(class_buf, "array__")) {
1147       *volume_class = MI_CLASS_UNIFORM_RECORD;
1148     } else {
1149       *volume_class = MI_CLASS_REAL;
1150     }
1151   } else {
1152     /*probably volume doesn't have this attribute*/
1153     *volume_class = MI_CLASS_REAL;
1154   }
1155 
1156   return (MI_NOERROR);
1157 }
1158 
1159 /* Read the irregular spacing information from a file.
1160  */
_miget_irregular_spacing(mihandle_t hvol,midimhandle_t hdim)1161 static int _miget_irregular_spacing(mihandle_t hvol, midimhandle_t hdim)
1162 {
1163   herr_t status;
1164   hid_t dset_id;
1165   hid_t dspc_id;
1166   char path[MI2_CHAR_LENGTH];
1167   hssize_t n_points;
1168 
1169   sprintf(path, MI_ROOT_PATH "/dimensions/%s", hdim->name);
1170   MI_CHECK_HDF_CALL_RET(dset_id = H5Dopen1(hvol->hdf_id, path),"H5Dopen1");
1171   MI_CHECK_HDF_CALL_RET(dspc_id = H5Dget_space(dset_id), "H5Dget_space");
1172 
1173   n_points = H5Sget_simple_extent_npoints(dspc_id);
1174 
1175   hdim->offsets = malloc(n_points * sizeof(double));
1176   if (hdim->offsets == NULL)
1177     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM, n_points * sizeof(double));
1178 
1179   /* Read the raw data to buffer (dimensions[i]->offsets)
1180      from the dataset.
1181   */
1182   MI_CHECK_HDF_CALL_RET(status = H5Dread(dset_id, H5T_NATIVE_DOUBLE,
1183                                          H5S_ALL, H5S_ALL, H5P_DEFAULT,
1184                                          hdim->offsets), "H5Dread")
1185 
1186   H5Dclose(dset_id);
1187   sprintf(path, MI_ROOT_PATH "/dimensions/%s-width", hdim->name);
1188   dset_id = H5Dopen1(hvol->hdf_id, path);
1189   if (dset_id < 0) {
1190     /* Unfortunately, the emulation library in MINC1 puts this variable
1191      * in the wrong place.
1192      */
1193     sprintf(path, MI_ROOT_PATH "/info/%s-width", hdim->name);
1194     dset_id = H5Dopen1(hvol->hdf_id, path);
1195     if (dset_id < 0) {
1196       return 0;
1197     }
1198   }
1199   hdim->widths = malloc(n_points * sizeof(double));
1200   if (hdim->widths == NULL)
1201     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM, n_points * sizeof(double));
1202 
1203   MI_CHECK_HDF_CALL_RET(status = H5Dread(dset_id, H5T_NATIVE_DOUBLE,
1204                                          H5S_ALL, H5S_ALL, H5P_DEFAULT,
1205                                          hdim->widths), "H5Dread")
1206   H5Dclose(dset_id);
1207   return 0;
1208 }
1209 
1210 /* Get dimension variable attributes for the given dimension name */
_miget_file_dimension(mihandle_t volume,const char * dimname,midimhandle_t * hdim_ptr)1211 static int _miget_file_dimension(mihandle_t volume, const char *dimname,
1212                       midimhandle_t *hdim_ptr)
1213 {
1214   char path[MI2_CHAR_LENGTH];
1215   char temp[MI2_CHAR_LENGTH];
1216   midimhandle_t hdim;
1217   unsigned int len;
1218 
1219   /* Create a path with the dimension name */
1220   sprintf(path, MI_ROOT_PATH "/dimensions/%s", dimname);
1221   /* Allocate space for the dimension handle */
1222   hdim = (midimhandle_t) malloc(sizeof (*hdim));
1223   /* Initialize everything to zero */
1224   memset(hdim, 0, sizeof (*hdim));
1225 
1226   hdim->name = strdup(dimname);
1227 
1228   /* hdf5 macro can temporarily disable the automatic error printing */
1229   H5E_BEGIN_TRY {
1230     int r;
1231     /* Get the attribute (spacing) from a minc file */
1232     r = miget_attribute(volume, path, "spacing", MI_TYPE_STRING, MI2_CHAR_LENGTH, temp);
1233 
1234     if (r==MI_NOERROR && !strcmp(temp, "irregular")) {
1235       hdim->attr |= MI_DIMATTR_NOT_REGULARLY_SAMPLED;
1236       _miget_irregular_spacing(volume, hdim);
1237     } else {
1238       hdim->attr |= MI_DIMATTR_REGULARLY_SAMPLED;
1239     }
1240 
1241     /* Get the attribute (class) from a minc file */
1242     r = miget_attribute(volume, path, "class", MI_TYPE_STRING,  MI2_CHAR_LENGTH, temp);
1243     if (r < 0) {
1244       /* Get the default class. */
1245       if (!strcmp(dimname, MItime)) {
1246         hdim->dim_class = MI_DIMCLASS_TIME;
1247       } else if (!strcmp(dimname, MIvector_dimension)) {
1248         hdim->dim_class = MI_DIMCLASS_RECORD;
1249         hdim->step = 0.0;
1250       } else {
1251         hdim->dim_class =  MI_DIMCLASS_SPATIAL;
1252       }
1253     } else {
1254       if (!strcmp(temp, "spatial")) {
1255         hdim->dim_class = MI_DIMCLASS_SPATIAL;
1256       } else if (!strcmp(temp, "time___")) {
1257         hdim->dim_class = MI_DIMCLASS_TIME;
1258       } else if (!strcmp(temp, "sfreq__")) {
1259         hdim->dim_class = MI_DIMCLASS_SFREQUENCY;
1260       } else if (!strcmp(temp, "tfreq__")) {
1261         hdim->dim_class = MI_DIMCLASS_TFREQUENCY;
1262       } else if (!strcmp(temp, "user___")) {
1263         hdim->dim_class = MI_DIMCLASS_USER;
1264       } else if (!strcmp(temp, "record_")) {
1265         hdim->dim_class = MI_DIMCLASS_RECORD;
1266       } else {
1267         MI_LOG_ERROR(MI2_MSG_GENERIC,"Unknown dimension type");
1268       }
1269     }
1270     /* Get the attribute (length) from a minc file. We have to do this in
1271      * two steps, as MI_TYPE_UINT is not necessarily the same size as
1272      * hsize_t/misize_t, so we have to read the value into a variable of
1273      * the right type, then assign it to the structure member, to guarantee
1274      * proper promotion.
1275      */
1276     r = miget_attribute(volume, path, "length", MI_TYPE_UINT, 1, &len);
1277     if (r < 0) {
1278       MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine dimension length");
1279     }
1280     hdim->length = len;         /* Will promote unsigned int to misize_t. */
1281 
1282     /* Get the attribute (start) from a minc file for NON vector_dimension only */
1283     if (strcmp(dimname, "vector_dimension")) {
1284       r = miget_attribute(volume, path, MIstart, MI_TYPE_DOUBLE, 1, &hdim->start);
1285       if (r < 0) {
1286         hdim->start = 0.0;
1287       }
1288       /* Get the attribute (step) from a minc file */
1289       r = miget_attribute(volume, path, MIstep, MI_TYPE_DOUBLE, 1, &hdim->step);
1290       if (r < 0) {
1291         hdim->step = 1.0;
1292       }
1293     }
1294     /* Get the attribute (direction_cosines) from a minc file */
1295     r = miget_attribute(volume, path, MIdirection_cosines, MI_TYPE_DOUBLE, 3,
1296                         hdim->direction_cosines);
1297     if (r < 0) {
1298       hdim->direction_cosines[MI2_X] = 0.0;
1299       hdim->direction_cosines[MI2_Y] = 0.0;
1300       hdim->direction_cosines[MI2_Z] = 0.0;
1301       if (!strcmp(dimname, MIxspace)) {
1302         hdim->direction_cosines[MI2_X] = 1.0;
1303       } else if (!strcmp(dimname, MIyspace)) {
1304         hdim->direction_cosines[MI2_Y] = 1.0;
1305       } else if (!strcmp(dimname, MIzspace)) {
1306         hdim->direction_cosines[MI2_Z] = 1.0;
1307       }
1308     }
1309 
1310     r = miget_attribute(volume, path, "units", MI_TYPE_STRING,
1311                         MI2_CHAR_LENGTH, temp);
1312     if (r < 0) {
1313       hdim->units = strdup("");
1314     } else {
1315       hdim->units = strdup(temp);
1316     }
1317 
1318   } H5E_END_TRY;
1319   /* Return the dimension handle */
1320   *hdim_ptr = hdim;
1321   hdim->volume_handle = volume;
1322   return (MI_NOERROR);
1323 }
1324 
1325 
1326 /** Opens an existing MINC volume for read-only access if mode argument is
1327   * MI2_OPEN_READ, or read-write access if mode argument is MI2_OPEN_RDWR.
1328   * \ingroup mi2Vol
1329 */
miopen_volume(const char * filename,int mode,mihandle_t * volume)1330 int miopen_volume(const char *filename, int mode, mihandle_t *volume)
1331 {
1332   hid_t file_id;
1333   hid_t dset_id;
1334   hid_t space_id;
1335   mihandle_t handle;
1336   int hdf_mode;
1337   char dimorder[MI2_CHAR_LENGTH];
1338   int i,r;
1339   char *p1, *p2;
1340   H5T_class_t hdf_class;
1341   size_t nbytes;
1342   int is_signed;
1343   int n_dimensions;
1344 
1345   /* Initialization.
1346     For the actual body of this function look at m2utils.c
1347   */
1348   miinit();
1349   /* Convert the specified mode to hdf mode */
1350   if (mode == MI2_OPEN_READ) {
1351     hdf_mode = H5F_ACC_RDONLY;
1352   } else if (mode == MI2_OPEN_RDWR) {
1353     hdf_mode = H5F_ACC_RDWR;
1354   } else {
1355     return (MI_ERROR);
1356   }
1357   /* Allocate space for the volume handle */
1358   handle = mialloc_volume_handle();
1359   if (handle == NULL) {
1360     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,sizeof(struct mivolume));
1361   }
1362 
1363   /* Open the hdf file using the given filename and mode */
1364   file_id = _hdf_open(filename, hdf_mode);
1365 
1366   if (file_id < 0) {
1367     /*try to convert MINC1 file*/
1368 #ifdef HAVE_MINC1
1369     char * temp_file=NULL;
1370 
1371     if ( mode == MI2_OPEN_READ )
1372     {
1373       if( (temp_file=micreate_tempfile()))
1374       {
1375          if( minc_format_convert(filename,temp_file) == MI_NOERROR )
1376          {
1377            if( (file_id = _hdf_open(temp_file, hdf_mode) ) >0)
1378            {
1379             unlink( temp_file ); /*file will be deleted immedeately after closing...*/
1380             free( temp_file );
1381            } else {
1382             unlink( temp_file );
1383             free( temp_file );
1384             free( handle );
1385             return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename);
1386            }
1387          } else {
1388            free( temp_file );
1389            free( handle );
1390            return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename);
1391          }
1392       } else {
1393          free( temp_file );
1394          free( handle );
1395          return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename);
1396       }
1397     } else {
1398       free( handle );
1399       return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename);
1400     }
1401 #else
1402     free( handle );
1403     return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename);
1404 #endif
1405   }
1406   /* Set some varibales associated with the volume handle */
1407   handle->hdf_id = file_id;
1408   handle->mode = mode;
1409 
1410   /* Get the volume class.
1411   */
1412   _miget_volume_class(handle, &handle->volume_class);
1413 
1414   /* GET THE DIMENSION COUNT
1415   */
1416   n_dimensions = handle->number_of_dims = _miget_file_dimension_count(file_id);
1417 
1418   if( n_dimensions <= 0 ) {
1419     free(handle);
1420     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to open minc file without image variable");
1421   }
1422 
1423   /* READ EACH OF THE DIMENSIONS
1424   */
1425   handle->dim_handles = (midimhandle_t *)malloc(n_dimensions *
1426                         sizeof(midimhandle_t));
1427 
1428   if(handle->dim_handles == NULL) {
1429     free(handle);
1430     return MI_LOG_ERROR(MI2_MSG_OUTOFMEM, n_dimensions * sizeof(midimhandle_t));
1431   }
1432 
1433   /* Get the attribute (dimorder) from the image dataset */
1434   r =  miget_attribute(handle, MI_ROOT_PATH "/image/0/image", "dimorder",
1435                        MI_TYPE_STRING, sizeof(dimorder), dimorder);
1436 
1437   if ( r < 0) {
1438     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine dimension order");
1439   }
1440   p1 = dimorder;
1441   /* Break the ordered, comma-separated list of dimension names
1442     to get each individual dimension name */
1443   for (i = 0; i < handle->number_of_dims; i++) {
1444     p2 = strchr(p1, ',');
1445     if (p2 != NULL) {
1446       *p2 = '\0';
1447     }
1448     /* Get dimension variable attributes for each dimension */
1449     _miget_file_dimension(handle, p1, &handle->dim_handles[i]);
1450     p1 = p2 + 1;
1451   }
1452 
1453   if( miset_volume_world_indices(handle) < 0 ) {
1454     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine world indices");
1455   }
1456 
1457   /* SEE IF SLICE SCALING IS ENABLED
1458   */
1459   handle->has_slice_scaling = FALSE;
1460   /* hdf5 macro can temporarily disable the automatic error printing */
1461   H5E_BEGIN_TRY {
1462     /* Open the dataset image-max at the specified path*/
1463     dset_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-max");
1464   } H5E_END_TRY;
1465 
1466   if (dset_id >= 0) {
1467     /* Get the Id of the copy of the dataspace of the dataset */
1468     space_id = H5Dget_space(dset_id);
1469     if (space_id >= 0) {
1470 
1471       /* If the dimensionality of the image-max variable is one or
1472       * greater, we consider this volume to have slice-scaling enabled.
1473       */
1474       if ( H5Sget_simple_extent_ndims(space_id) >= 1) {
1475         handle->has_slice_scaling = TRUE;
1476       }
1477       H5Sclose(space_id);	/* Close the dataspace handle */
1478     }
1479     H5Dclose(dset_id);	/* Close the dataset handle */
1480   }
1481 
1482   if (!handle->has_slice_scaling) {
1483     /* Read the minimum scalar of the given type at the specified path */
1484     miget_scalar(handle->hdf_id, H5T_NATIVE_DOUBLE,
1485                  MI_ROOT_PATH "/image/0/image-min", &handle->scale_min);
1486     /* Read the maximum scalar of the given type at the specified path */
1487     miget_scalar(handle->hdf_id, H5T_NATIVE_DOUBLE,
1488                  MI_ROOT_PATH "/image/0/image-max", &handle->scale_max);
1489   }
1490 
1491   /* Read the current voxel-to-world transform */
1492   miget_voxel_to_world(handle, handle->v2w_transform);
1493 
1494   /* Calculate the inverse transform */
1495   miinvert_transform(handle->v2w_transform, handle->w2v_transform);
1496 
1497   /* Open the image dataset */
1498   MI_CHECK_HDF_CALL_RET(handle->image_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image"),"H5Dopen1");
1499   /* Get the Id for the copy of the datatype for the dataset */
1500   MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Dget_type(handle->image_id),"H5Dget_type");
1501 
1502   switch (H5Tget_class(handle->ftype_id)) {
1503   case H5T_INTEGER:
1504   case H5T_FLOAT:
1505     handle->mtype_id = H5Tget_native_type(handle->ftype_id,
1506                                           H5T_DIR_ASCEND);
1507     break;
1508 
1509   case H5T_COMPOUND:
1510     handle->mtype_id = H5Tcreate(H5T_COMPOUND,
1511                                  H5Tget_size(handle->ftype_id));
1512     for (i = 0; i < H5Tget_nmembers(handle->ftype_id); i++) {
1513       hid_t tmp_id = H5Tget_member_type(handle->ftype_id, i);
1514       size_t tmp_off = H5Tget_member_offset(handle->ftype_id, i);
1515       char *tmp_nm = H5Tget_member_name(handle->ftype_id, i);
1516       hid_t tmp2_id = H5Tget_native_type(tmp_id, H5T_DIR_ASCEND);
1517       H5Tinsert(handle->mtype_id, tmp_nm, tmp_off, tmp2_id);
1518 
1519       free(tmp_nm);
1520       H5Tclose(tmp_id);
1521       H5Tclose(tmp2_id);
1522     }
1523     break;
1524 
1525   case H5T_ENUM:
1526     handle->mtype_id = H5Tget_native_type(handle->ftype_id, H5T_DIR_ASCEND);
1527     miinit_enum(handle->ftype_id);
1528     miinit_enum(handle->mtype_id);
1529     break;
1530 
1531   default:
1532     return (MI_ERROR);
1533   }
1534 
1535   /* hdf5 macro can temporarily disable the automatic error printing */
1536   H5E_BEGIN_TRY {
1537     /* Open both image-min and image-max datasets */
1538     handle->imax_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-max");
1539     handle->imin_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-min");
1540   } H5E_END_TRY;
1541 
1542   /* Convert the type to a MINC type.
1543   */
1544   /* Get the class Id for the datatype */
1545   hdf_class = H5Tget_class(handle->ftype_id);
1546   /* Get the size of the datatype */
1547   nbytes = H5Tget_size(handle->ftype_id);
1548 
1549   switch (hdf_class) {
1550   case H5T_INTEGER:
1551   case H5T_ENUM:              /* label images */
1552     is_signed = (H5Tget_sign(handle->ftype_id) == H5T_SGN_2);
1553 
1554     switch (nbytes) {
1555     case 1:
1556       handle->volume_type = (is_signed ? MI_TYPE_BYTE : MI_TYPE_UBYTE);
1557       break;
1558     case 2:
1559       handle->volume_type = (is_signed ? MI_TYPE_SHORT : MI_TYPE_USHORT);
1560       break;
1561     case 4:
1562       handle->volume_type = (is_signed ? MI_TYPE_INT : MI_TYPE_UINT);
1563       break;
1564     default:
1565       return MI_LOG_ERROR(MI2_MSG_BADTYPE,hdf_class);
1566     }
1567     break;
1568   case H5T_FLOAT:
1569     handle->volume_type = (nbytes == 4) ? MI_TYPE_FLOAT : MI_TYPE_DOUBLE;
1570     break;
1571   case H5T_STRING:
1572     handle->volume_type = MI_TYPE_STRING;
1573     break;
1574   case H5T_ARRAY:
1575     /* TODO: handle this case for uniform records (arrays)? */
1576     break;
1577   case H5T_COMPOUND:
1578     /* TODO: handle this case for non-uniform records? */
1579     break;
1580   default:
1581     return MI_LOG_ERROR(MI2_MSG_BADTYPE,hdf_class);
1582   }
1583 
1584   /* Read the current settings for valid-range */
1585   miread_valid_range(handle, &handle->valid_max, &handle->valid_min);
1586 
1587   *volume = handle;
1588   return (MI_NOERROR);
1589 }
1590 
1591 /** Writes any changes associated with the volume to disk.
1592     \ingroup mi2Vol
1593 */
miflush_volume(mihandle_t volume)1594 static int miflush_volume(mihandle_t volume)
1595 {
1596   if ((volume->mode & MI2_OPEN_RDWR) != 0) {
1597     H5Fflush(volume->hdf_id, H5F_SCOPE_GLOBAL);
1598     misave_valid_range(volume);
1599   }
1600   return (MI_NOERROR);
1601 }
1602 
1603 /** Close an existing MINC volume. If the volume was newly created,
1604   *  all changes will be written to disk. In all cases this function closes
1605   *  the open volume and frees memory associated with the volume handle.
1606   *  \ingroup mi2Vol
1607 */
miclose_volume(mihandle_t volume)1608 int miclose_volume(mihandle_t volume)
1609 {
1610   int i;
1611 
1612   if (volume == NULL) {
1613     return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to close null volume");
1614   }
1615 
1616   if (volume->is_dirty) {
1617     minc_update_thumbnails(volume);
1618     volume->is_dirty = FALSE;
1619   }
1620 
1621   miflush_volume(volume);
1622 
1623   if (volume->image_id > 0) {
1624     H5Dclose(volume->image_id);
1625   }
1626   if (volume->imax_id > 0) {
1627     H5Dclose(volume->imax_id);
1628   }
1629   if (volume->imin_id > 0) {
1630     H5Dclose(volume->imin_id);
1631   }
1632   if (volume->ftype_id > 0) {
1633     H5Tclose(volume->ftype_id);
1634   }
1635   if (volume->mtype_id > 0) {
1636     H5Tclose(volume->mtype_id);
1637   }
1638   if (volume->plist_id > 0) {
1639     H5Pclose(volume->plist_id);
1640   }
1641   if (_hdf_close(volume->hdf_id) < 0) {
1642     return (MI_ERROR);
1643   }
1644   if (volume->dim_handles != NULL) {
1645 
1646     for(i=0;i<volume->number_of_dims;i++)
1647     {
1648       mifree_dimension_handle(volume->dim_handles[i]);
1649     }
1650 
1651     free(volume->dim_handles);
1652   }
1653   if (volume->dim_indices != NULL) {
1654     free(volume->dim_indices);
1655   }
1656   if (volume->create_props != NULL) {
1657     mifree_volume_props(volume->create_props);
1658   }
1659 
1660   free(volume);
1661 
1662   return (MI_NOERROR);
1663 }
1664 
1665 
1666 
1667 /** \internal
1668 */
miinit_default_range(mitype_t mitype,double * valid_max,double * valid_min)1669 void miinit_default_range(mitype_t mitype, double *valid_max, double *valid_min)
1670 {
1671   switch (mitype) {
1672   case MI_TYPE_BYTE:
1673     *valid_min = (double)CHAR_MIN;
1674     *valid_max = (double)CHAR_MAX;
1675     break;
1676   case MI_TYPE_SHORT:
1677     *valid_min = (double)SHRT_MIN;
1678     *valid_max = (double)SHRT_MAX;
1679     break;
1680   case MI_TYPE_INT:
1681     *valid_min = (double)INT_MIN;
1682     *valid_max = (double)INT_MAX;
1683     break;
1684   case MI_TYPE_UBYTE:
1685     *valid_min = 0.0;
1686     *valid_max = (double)UCHAR_MAX;
1687     break;
1688   case MI_TYPE_USHORT:
1689     *valid_min = 0.0;
1690     *valid_max = (double)USHRT_MAX;
1691     break;
1692   case MI_TYPE_UINT:
1693     *valid_min = 0.0;
1694     *valid_max = (double)UINT_MAX;
1695     break;
1696   case MI_TYPE_FLOAT:
1697     *valid_min = (double)-FLT_MAX;
1698     *valid_max = (double)FLT_MAX;
1699     break;
1700   case MI_TYPE_DOUBLE:
1701     *valid_min = -DBL_MAX;
1702     *valid_max = DBL_MAX;
1703     break;
1704   case MI_TYPE_DCOMPLEX:
1705     *valid_min = -DBL_MAX;
1706     *valid_max = DBL_MAX;
1707     break;
1708   case MI_TYPE_FCOMPLEX:
1709     *valid_min = (double)-FLT_MAX;
1710     *valid_max = (double)FLT_MAX;
1711     break;
1712   default:
1713     *valid_min = 0.0;
1714     *valid_max = 1.0;
1715     MI_LOG_ERROR(MI2_MSG_BADTYPE,mitype);
1716     break;
1717   }
1718 }
1719 
1720 /** \internal
1721  */
miread_valid_range(mihandle_t volume,double * valid_max,double * valid_min)1722 static void miread_valid_range(mihandle_t volume, double *valid_max, double *valid_min)
1723 {
1724   int r;
1725   double range[2];
1726 
1727   H5E_BEGIN_TRY {
1728     r = miget_attribute(volume, MI_ROOT_PATH "/image/0/image", "valid_range", MI_TYPE_DOUBLE, 2, range);
1729   } H5E_END_TRY;
1730   if (r == MI_NOERROR) {
1731     if (range[0] < range[1]) {
1732       *valid_min = range[0];
1733       *valid_max = range[1];
1734     } else {
1735       *valid_min = range[1];
1736       *valid_max = range[0];
1737     }
1738   } else {
1739     /* Didn't find the attribute, so assign default values. */
1740     miinit_default_range(volume->volume_type, valid_max, valid_min);
1741   }
1742 }
1743 
1744 /** \internal
1745 * This function saves the current valid range set for a MINC file.
1746 */
misave_valid_range(mihandle_t volume)1747 void misave_valid_range(mihandle_t volume)
1748 {
1749   double range[2];
1750   range[0] = volume->valid_min;
1751   range[1] = volume->valid_max;
1752 
1753   miset_attribute(volume, MI_ROOT_PATH "/image/0/image", "valid_range",
1754                   MI_TYPE_DOUBLE, 2, range);
1755 }
1756 
1757 
miget_slice_dimension_count(mihandle_t volume,midimclass_t dimclass,midimattr_t attr,int * number_of_dimensions)1758 int miget_slice_dimension_count(mihandle_t volume, midimclass_t dimclass,
1759                                 midimattr_t attr, int *number_of_dimensions)
1760 {
1761   int number_of_volume_dimensions=-1;
1762   hid_t image_max_fspc_id;
1763   int slice_ndims;
1764   int result=-1;
1765   if( miget_volume_dimension_count(volume,dimclass,attr, &number_of_volume_dimensions) <0 )
1766   {
1767     return -1;
1768   }
1769 
1770   if(!volume->has_slice_scaling)
1771   {
1772     *number_of_dimensions=number_of_volume_dimensions;
1773     return MI_NOERROR;
1774   }
1775 
1776   image_max_fspc_id=H5Dget_space(volume->imax_id);
1777   slice_ndims = H5Sget_simple_extent_ndims ( image_max_fspc_id );
1778   if(slice_ndims>=0)
1779   {
1780     result=MI_NOERROR;
1781     *number_of_dimensions=number_of_volume_dimensions-slice_ndims;
1782   }
1783   H5Sclose(image_max_fspc_id);
1784   return result;
1785 }
1786 
1787 
1788 /* kate: indent-mode cstyle; indent-width 2; replace-tabs on; */
1789