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