/* * image-hdf5.c * * Image loading, HDF5 parts * * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2020-2021 Thomas White * 2014-2018 Valerio Mariani * * This file is part of CrystFEL. * * CrystFEL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CrystFEL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CrystFEL. If not, see . * */ #include #include #include #include #include #include #ifdef HAVE_HDF5 #include #include #endif #include "image.h" #include "utils.h" #include "detgeom.h" #include "profile.h" #include "datatemplate.h" #include "datatemplate_priv.h" /* Get the path parts of the event ID * e.g. ev_orig = abc/def/ghi//5/2/7 * -> [abc, def, ghi], with *pn_plvals=3. * * Not part of public API. Not "static" for testing. */ char **read_path_parts(const char *ev_orig, int *pn_plvals) { char **plvals; char *ev; int n_plvals = 0; char *start; plvals = cfmalloc(MAX_PATH_PARTS*sizeof(char *)); if ( plvals == NULL ) return NULL; if ( ev_orig == NULL ) { /* No ev -> no path parts */ *pn_plvals = 0; return plvals; } ev = cfstrdup(ev_orig); if ( ev == NULL ) { cffree(plvals); return NULL; } start = ev; do { char *sep; sep = strchr(start, '/'); if ( sep == NULL ) { /* This would be very strange, because it * must at least have // */ ERROR("Couldn't read path parts ('%s')\n", start); cffree(ev); cffree(plvals); return NULL; } /* Remaining string starts with '/' is end condition */ if ( sep == start ) break; if ( n_plvals == MAX_PATH_PARTS ) { ERROR("Too many path parts: %s\n", ev_orig); cffree(ev); cffree(plvals); return NULL; } sep[0] = '\0'; plvals[n_plvals++] = cfstrdup(start); start = sep+1; } while ( 1 ); cffree(ev); *pn_plvals = n_plvals; return plvals; } /* Get the dimension parts of the event ID * e.g. ev_orig = abc/def/ghi//5/2/7 * -> [5, 2, 7], with *pn_dvals=3 * * Not part of public API. Not "static" for testing. */ int *read_dim_parts(const char *ev_orig, int *pn_dvals) { char *ev; int n_dvals = 0; int *dvals; char *start; int done; if ( ev_orig == NULL ) ev_orig = "//"; /* Valid event ID? (Just the part after //, please) */ ev = strstr(ev_orig, "//"); if ( ev == NULL ) return NULL; dvals = cfmalloc(MAX_DIMS*sizeof(int)); if ( dvals == NULL ) return NULL; if ( ev[2] == '\0' ) { /* No dimension parts - early bailout */ *pn_dvals = 0; return dvals; /* NB Not NULL */ } ev = cfstrdup(ev+2); if ( ev == NULL ) { cffree(dvals); return NULL; } start = ev; done = 0; do { char *sep = strchr(start, '/'); if ( sep != NULL ) { sep[0] = '\0'; } else { done = 1; } if ( n_dvals == MAX_PATH_PARTS ) { ERROR("Too many path parts: %s\n", ev_orig); cffree(ev); cffree(dvals); return NULL; } if ( start[0] == '\0' ) { ERROR("Missing dimension: %s\n", ev_orig); cffree(ev); cffree(dvals); return NULL; } dvals[n_dvals++] = atoi(start); start = sep+1; } while ( !done ); cffree(ev); *pn_dvals = n_dvals; return dvals; } static int imh_num_path_placeholders(const char *pattern) { size_t l, i; int n_pl_exp = 0; l = strlen(pattern); for ( i=0; i n_pl_exp) && !skip_ok ) { ERROR("Event ID has too many path placeholder values: " "event ID '%s' (%i) into pattern '%s' (%i)\n", ev, n_plvals, pattern, n_pl_exp); return NULL; } if ( n_pl_exp == 0 ) { /* No placeholders in path */ for ( i=0; idims[i] == DIM_PLACEHOLDER ) n_pl++; } return n_pl; } static int total_dimensions(const struct panel_template *p) { int i; int n_dim = 0; for ( i=0; idims[i] != DIM_UNDEFINED ) n_dim++; } return n_dim; } static void close_hdf5(hid_t fh) { int n_ids, i; hid_t ids[2048]; n_ids = H5Fget_obj_ids(fh, H5F_OBJ_ALL, 2048, ids); for ( i=0; iname, panel_full_path); profile_end("H5Dopen2"); cffree(panel_full_path); return 1; } profile_end("H5Dopen2"); cffree(panel_full_path); /* Set up dataspace for file * (determine where to read the data from) */ dataspace = H5Dget_space(dh); ndims = H5Sget_simple_extent_ndims(dataspace); if ( ndims < 0 ) { ERROR("Failed to get number of dimensions for panel %s\n", p->name); H5Dclose(dh); return 1; } /* Does the array have the expected number of dimensions? */ total_dt_dims = total_dimensions(p); plh_dt_dims = imh_num_placeholders(p); if ( ndims != total_dt_dims ) { /* If the dimensions match after excluding * placeholders, it's OK - probably a static mask * in a multi-event file. */ if ( skip_placeholders_ok && (ndims == total_dt_dims - plh_dt_dims) ) { make_placeholder_skip(dt_dims, p->dims); n_dt_dims = total_dt_dims - plh_dt_dims; } else { ERROR("Unexpected number of dimensions for " "panel %s (%i, but expected %i or %i)\n", p->name, ndims, total_dt_dims, total_dt_dims - plh_dt_dims); H5Dclose(dh); return 1; } } else { int i; for ( i=0; idims[i]; } n_dt_dims = total_dt_dims; } f_offset = cfmalloc(ndims*sizeof(hsize_t)); f_count = cfmalloc(ndims*sizeof(hsize_t)); if ( (f_offset == NULL) || (f_count == NULL ) ) { ERROR("Failed to allocate offset or count.\n"); cffree(f_offset); cffree(f_count); H5Dclose(dh); return 1; } /* Get those placeholder values from the event ID */ dim_vals = read_dim_parts(event, &n_dim_vals); pl_pos = 0; for ( dim=0; dimorig_min_fs; f_count[dim] = p->orig_max_fs - p->orig_min_fs+1; break; case DIM_SS: f_offset[dim] = p->orig_min_ss; f_count[dim] = p->orig_max_ss - p->orig_min_ss+1; break; case DIM_PLACEHOLDER: f_offset[dim] = dim_vals[pl_pos++]; f_count[dim] = 1; break; case DIM_UNDEFINED: ERROR("Undefined dimension found!\n"); break; default: /* Fixed value */ f_offset[dim] = dt_dims[dim]; f_count[dim] = 1; break; } } cffree(dim_vals); check = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, f_offset, NULL, f_count, NULL); if ( check < 0 ) { ERROR("Error selecting file dataspace for panel %s\n", p->name); cffree(f_offset); cffree(f_count); H5Dclose(dh); return 1; } dims[0] = p->orig_max_ss - p->orig_min_ss + 1; dims[1] = p->orig_max_fs - p->orig_min_fs + 1; memspace = H5Screate_simple(2, dims, NULL); profile_start("H5Dread"); r = H5Dread(dh, el_type, memspace, dataspace, H5P_DEFAULT, data); profile_end("H5Dread"); if ( r < 0 ) { ERROR("Couldn't read data for panel %s\n", p->name); cffree(f_offset); cffree(f_count); cffree(data); H5Dclose(dh); return 1; } cffree(f_offset); cffree(f_count); if ( orig_type != NULL ) { *orig_type = H5Dget_type(dh); } H5Dclose(dh); return 0; } static hid_t open_hdf5_file(const char *filename) { hid_t fh; if ( access(filename, R_OK) == -1 ) { ERROR("File does not exist or cannot be read: %s\n", filename); return -1; } fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if ( fh < 0 ) { ERROR("Couldn't open HDF5 file: %s\n", filename); return -1; } return fh; } static hid_t open_hdf5(struct image *image) { if ( image->data_block == NULL ) { return open_hdf5_file(image->filename); } else { hid_t fh; fh = H5LTopen_file_image(image->data_block, image->data_block_size, H5LT_FILE_IMAGE_DONT_COPY | H5LT_FILE_IMAGE_DONT_RELEASE); if ( fh < 0 ) { ERROR("Couldn't open HDF5 image (%p %lli)\n", image->data_block, image->data_block_size); return -1; } return fh; } } int image_hdf5_read(struct image *image, const DataTemplate *dtempl) { int i; hid_t fh; if ( image->ev == NULL ) { image->ev = "//"; } profile_start("open-hdf5"); fh = open_hdf5(image); profile_end("open-hdf5"); if ( fh < 0 ) { ERROR("Failed to open file\n"); return 1; } for ( i=0; in_panels; i++ ) { long int j; struct panel_template *p = &dtempl->panels[i]; hid_t orig_type; profile_start("load-hdf5-hyperslab"); if ( load_hdf5_hyperslab(p, fh, image->ev, image->dp[i], H5T_NATIVE_FLOAT, sizeof(float), 0, dtempl->panels[i].data, &orig_type) ) { ERROR("Failed to load panel data\n"); profile_end("load-hdf5-hyperslab"); close_hdf5(fh); return 1; } profile_end("load-hdf5-hyperslab"); if ( H5Tget_class(orig_type) == H5T_FLOAT ) { profile_start("nan-inf"); for ( j=0; jdp[i][j]) ) { image->bad[i][j] = 1; } } profile_end("nan-inf"); } H5Tclose(orig_type); } close_hdf5(fh); return 0; } int image_hdf5_read_satmap(struct panel_template *p, const char *filename, const char *event, const char *map_location, float *map_data) { hid_t fh; fh = open_hdf5_file(filename); if ( fh < 0 ) return 1; if ( load_hdf5_hyperslab(p, fh, event, map_data, H5T_NATIVE_FLOAT, sizeof(float), 1, map_location, NULL) ) { ERROR("Failed to load saturation map data\n"); return 1; } close_hdf5(fh); return 0; } int image_hdf5_read_mask(struct panel_template *p, const char *filename, const char *event, int *bad, const char *mask_location, int mask_good, int mask_bad) { int p_w, p_h; int *mask = NULL; long unsigned int j; hid_t fh; p_w = p->orig_max_fs - p->orig_min_fs + 1; p_h = p->orig_max_ss - p->orig_min_ss + 1; fh = open_hdf5_file(filename); if ( fh < 0 ) { ERROR("Failed to open mask '%s'\n", filename); return 1; } mask = cfmalloc(p_w*p_h*sizeof(int)); if ( mask == NULL ) return 1; if ( load_hdf5_hyperslab(p, fh, event, mask, H5T_NATIVE_INT, sizeof(int), 1, mask_location, NULL) ) { ERROR("Failed to load mask data\n"); cffree(mask); return 1; } close_hdf5(fh); for ( j=0; jfilename); return 1; } subst_name = substitute_path(image->ev, name, 1); if ( subst_name == NULL ) { ERROR("Invalid event ID '%s'\n", image->ev); close_hdf5(fh); return 1; } dh = H5Dopen2(fh, subst_name, H5P_DEFAULT); if ( dh < 0 ) { ERROR("No such numeric field '%s'\n", subst_name); cffree(subst_name); close_hdf5(fh); return 1; } type = H5Dget_type(dh); class = H5Tget_class(type); switch ( class ) { /* Acceptable types */ case H5T_FLOAT: case H5T_INTEGER: case H5T_STRING: break; default: ERROR("HDF5 header is not a recognised type (%s).\n", subst_name); close_hdf5(fh); cffree(subst_name); return 1; } /* Get the dimensionality. We have to cope with scalars expressed as * arrays with all dimensions 1, as well as zero-d arrays. */ sh = H5Dget_space(dh); ndims = H5Sget_simple_extent_ndims(sh); if ( ndims > 64 ) { ERROR("Too many dimensions for numeric value\n"); close_hdf5(fh); cffree(subst_name); return 1; } H5Sget_simple_extent_dims(sh, size, NULL); /* We want to read the value as a scalar */ m_offset[0] = 0; m_count[0] = 1; msdims[0] = 1; ms = H5Screate_simple(1, msdims, NULL); if ( ndims == 0 ) { /* Easy case, because value is a scalar */ if ( class == H5T_FLOAT ) { double val; r = H5Dread(dh, H5T_NATIVE_DOUBLE, ms, sh, H5P_DEFAULT, &val); if ( r < 0 ) { ERROR("Couldn't read scalar value from %s.\n", subst_name); cffree(subst_name); close_hdf5(fh); return 1; } image_cache_header_float(image, name, val); cffree(subst_name); return 0; } else if ( class == H5T_INTEGER ) { int val; r = H5Dread(dh, H5T_NATIVE_INT, ms, sh, H5P_DEFAULT, &val); if ( r < 0 ) { ERROR("Couldn't read scalar value from %s.\n", subst_name); cffree(subst_name); close_hdf5(fh); return 1; } image_cache_header_int(image, name, val); cffree(subst_name); return 0; } else if ( class == H5T_STRING ) { htri_t v; hid_t stype; char *val; int rv; stype = H5Dget_type(dh); v = H5Tis_variable_str(stype); H5Tclose(stype); if ( v == 0 ) { val = read_single_fixed_string(dh); } else if ( v > 0 ) { val = read_single_vlen_string(dh); } else { ERROR("Unrecognised string type: %s\n", subst_name); val = NULL; } if ( val != NULL ) { image_cache_header_str(image, name, val); cffree(val); rv = 0; } else { ERROR("Failed to read string '%s'\n", subst_name); rv = 1; } cffree(subst_name); close_hdf5(fh); return rv; } else { /* Should never be reached */ ERROR("Invalid HDF5 class %i\n", class); cffree(subst_name); return 1; } } dim_vals = read_dim_parts(image->ev, &n_dim_vals); if ( dim_vals == NULL ) { ERROR("Couldn't parse event '%s'\n"); close_hdf5(fh); cffree(subst_name); return 1; } f_offset = cfmalloc(ndims*sizeof(hsize_t)); f_count = cfmalloc(ndims*sizeof(hsize_t)); if ( (f_offset == NULL) || (f_count == NULL) ) { ERROR("Couldn't allocate dimension arrays\n"); close_hdf5(fh); cffree(subst_name); return 1; } /* Every dimension of the dataset must either be size 1 or * large enough to contain the next value from the event ID */ dim_val_pos = 0; for ( i=0; i tw-1 ) { H5Sclose(sh); H5Dclose(dh); ERROR("Data block %s does not contain data for required event.\n", path); return 1; } offset[0] = line; count[0] = 1; r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, offset, NULL, count, NULL); if ( r < 0 ) { ERROR("Error selecting file dataspace " "for data block %s\n", path); H5Dclose(dh); H5Sclose(sh); return 1; } m_offset[0] = 0; m_count[0] = 1; dimmh[0] = 1; mh = H5Screate_simple(1, dimmh, NULL); r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, m_offset, NULL, m_count, NULL); if ( r < 0 ) { ERROR("Error selecting memory dataspace " "for data block %s\n", path); H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return 1; } r = H5Dread(dh, H5T_NATIVE_INT, mh, sh, H5P_DEFAULT, num_peaks); if ( r < 0 ) { ERROR("Couldn't read data for block %s, line %i\n", path, line); H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return 1; } H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return 0; } static float *read_peak_line(hid_t fh, char *path, int line, int num_peaks) { hid_t dh, sh, mh; hsize_t size[2]; hsize_t max_size[2]; hsize_t offset[2], count[2]; hsize_t m_offset[2], m_count[2], dimmh[2]; float *buf; int tw, r; dh = H5Dopen2(fh, path, H5P_DEFAULT); if ( dh < 0 ) { ERROR("Data block (%s) not found.\n", path); return NULL; } sh = H5Dget_space(dh); if ( sh < 0 ) { H5Dclose(dh); ERROR("Couldn't get dataspace for data.\n"); return NULL; } if ( H5Sget_simple_extent_ndims(sh) != 2 ) { ERROR("Data block %s has the wrong dimensionality (%i).\n", path, H5Sget_simple_extent_ndims(sh)); H5Sclose(sh); H5Dclose(dh); return NULL; } H5Sget_simple_extent_dims(sh, size, max_size); tw = size[0]; if ( line > tw-1 ) { H5Sclose(sh); H5Dclose(dh); ERROR("Data block %s does not contain data for required event.\n", path); return NULL; } /* NB The array might be bigger - Cheetah allocates in blocks of 2048 */ if ( size[1] < num_peaks ) { ERROR("Data block %s is too small for the specified number of " "peaks (has %i, expected %i)\n", path, size[1], num_peaks); H5Sclose(sh); H5Dclose(dh); return NULL; } offset[0] = line; offset[1] = 0; count[0] = 1; count[1] = size[1]; r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, offset, NULL, count, NULL); if ( r < 0 ) { ERROR("Error selecting file dataspace " "for data block %s\n", path); H5Dclose(dh); H5Sclose(sh); return NULL; } m_offset[0] = 0; m_offset[1] = 0; m_count[0] = 1; m_count[1] = size[1]; dimmh[0] = 1; dimmh[1] = size[1]; mh = H5Screate_simple(2, dimmh, NULL); r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, m_offset, NULL, m_count, NULL); if ( r < 0 ) { ERROR("Error selecting memory dataspace " "for data block %s\n", path); H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return NULL; } buf = cfmalloc(size[1]*sizeof(float)); if ( buf == NULL ) return NULL; r = H5Dread(dh, H5T_NATIVE_FLOAT, mh, sh, H5P_DEFAULT, buf); if ( r < 0 ) { ERROR("Couldn't read data for block %s, line %i\n", path, line); H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return NULL; } H5Dclose(dh); H5Sclose(sh); H5Sclose(mh); return buf; } ImageFeatureList *image_hdf5_read_peaks_cxi(const DataTemplate *dtempl, const char *filename, const char *event, int half_pixel_shift) { ImageFeatureList *features; hid_t fh; char path_n[1024]; char path_x[1024]; char path_y[1024]; char path_i[1024]; int r; int pk; char *subst_name; int line; int num_peaks; float *buf_x; float *buf_y; float *buf_i; int *dim_vals; int n_dim_vals; double peak_offset = half_pixel_shift ? 0.5 : 0.0; if ( access(filename, R_OK) == -1 ) { ERROR("File does not exist or cannot be read: %s\n", filename); return NULL; } subst_name = substitute_path(event, dtempl->peak_list, 0); if ( subst_name == NULL ) { ERROR("Invalid peak path %s\n", subst_name); return NULL; } dim_vals = read_dim_parts(event, &n_dim_vals); if ( dim_vals == NULL ) { ERROR("Couldn't parse event '%s'\n"); cffree(subst_name); return NULL; } if ( n_dim_vals < 1 ) { ERROR("Not enough dimensions in event ID to use CXI " "peak lists (%i)\n", n_dim_vals); cffree(subst_name); return NULL; } line = dim_vals[0]; cffree(dim_vals); snprintf(path_n, 1024, "%s/nPeaks", subst_name); snprintf(path_x, 1024, "%s/peakXPosRaw", subst_name); snprintf(path_y, 1024, "%s/peakYPosRaw", subst_name); snprintf(path_i, 1024, "%s/peakTotalIntensity", subst_name); fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if ( fh < 0 ) { ERROR("Couldn't open file (peaks/cxi): %s\n", filename); cffree(subst_name); return NULL; } r = read_peak_count(fh, path_n, line, &num_peaks); if ( r != 0 ) { close_hdf5(fh); cffree(subst_name); return NULL; } buf_x = read_peak_line(fh, path_x, line, num_peaks); if ( buf_x == NULL ) { close_hdf5(fh); cffree(subst_name); return NULL; } buf_y = read_peak_line(fh, path_y, line, num_peaks); if ( buf_y == NULL ) { cffree(buf_x); cffree(subst_name); close_hdf5(fh); return NULL; } buf_i = read_peak_line(fh, path_i, line, num_peaks); if ( buf_i == NULL ) { cffree(buf_x); cffree(buf_y); cffree(subst_name); close_hdf5(fh); return NULL; } features = image_feature_list_new(); for ( pk=0; pkpeak_list == NULL ) { ERROR("Peak location is not given in geometry file.\n"); return NULL; } if ( access(filename, R_OK) == -1 ) { ERROR("File does not exist or cannot be read: %s\n", filename); return NULL; } fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if ( fh < 0 ) { ERROR("Couldn't open file (peaks/hdf5): %s\n", filename); return NULL; } subst_name = substitute_path(event, dtempl->peak_list, 0); if ( subst_name == NULL ) { ERROR("Invalid peak path: '%s' '%s'\n", event, dtempl->peak_list); close_hdf5(fh); return NULL; } dh = H5Dopen2(fh, subst_name, H5P_DEFAULT); if ( dh < 0 ) { ERROR("Peak list (%s) not found.\n", subst_name); cffree(subst_name); close_hdf5(fh); return NULL; } cffree(subst_name); sh = H5Dget_space(dh); if ( sh < 0 ) { ERROR("Couldn't get dataspace for peak list.\n"); close_hdf5(fh); return NULL; } if ( H5Sget_simple_extent_ndims(sh) != 2 ) { ERROR("Peak list has the wrong dimensionality (%i).\n", H5Sget_simple_extent_ndims(sh)); close_hdf5(fh); return NULL; } H5Sget_simple_extent_dims(sh, size, max_size); H5Sclose(sh); tw = size[1]; if ( (tw != 3) && (tw != 4) ) { ERROR("Peak list has the wrong dimensions.\n"); close_hdf5(fh); return NULL; } buf = cfmalloc(sizeof(float)*size[0]*size[1]); if ( buf == NULL ) { ERROR("Couldn't reserve memory for the peak list.\n"); close_hdf5(fh); return NULL; } r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf); if ( r < 0 ) { ERROR("Couldn't read peak list.\n"); close_hdf5(fh); return NULL; } features = image_feature_list_new(); if ( features == NULL ) { ERROR("Failed to allocate peak list\n"); close_hdf5(fh); return NULL; } for ( i=0; in_events == list->max_events ) { char **new_events = cfrealloc(list->events, (list->max_events+128)*sizeof(char *)); if ( new_events == NULL ) return 1; list->max_events += 128; list->events = new_events; } list->events[list->n_events++] = cfstrdup(ev_str); return 0; } static char *demunge_event(const char *orig) { size_t len = strlen(orig); char *slash; if ( len == 0 ) return cfstrdup("//"); slash = cfmalloc(len+3); if ( slash == NULL ) return NULL; strcpy(slash, orig+1); strcat(slash, "//"); return slash; } static int rec_expand_paths(hid_t gh, struct ev_list *list, const char *ev_str, char **pattern_bits, int n_pattern_bits) { int i; H5G_info_t group_info; if ( H5Gget_info(gh, &group_info) < 0 ) { ERROR("Couldn't get group info\n"); return 1; } for ( i=0; i 20000) ) { ERROR("Couldn't get link name\n"); return 1; } name = cfmalloc(size+1); if ( name == NULL ) { ERROR("Couldn't allocate memory\n"); return 1; } if ( H5Lget_name_by_idx(gh, ".", H5_INDEX_NAME, H5_ITER_INC, i, name, size+1, H5P_DEFAULT) < 0 ) { ERROR("Couldn't get name\n"); return 1; } ev_str_new = matches_pattern(name, pattern_bits[0], ev_str); if ( ev_str_new == NULL ) { cffree(name); continue; } if ( H5Oget_info_by_idx(gh, ".", H5_INDEX_NAME, H5_ITER_INC, i, &obj_info, 0) < 0 ) { ERROR("Couldn't get info\n"); cffree(name); cffree(ev_str_new); return 1; } if ( obj_info.type == H5O_TYPE_GROUP ) { hid_t child_gh; if ( n_pattern_bits == 1 ) { ERROR("Pattern doesn't match file" " (too short)\n"); cffree(name); cffree(ev_str_new); return 1; } child_gh = H5Gopen1(gh, name); if ( child_gh < 0 ) { ERROR("Couldn't open '%s'\n", name); cffree(name); cffree(ev_str_new); return 1; } if ( rec_expand_paths(child_gh, list, ev_str_new, &pattern_bits[1], n_pattern_bits - 1) ) { cffree(name); cffree(ev_str_new); return 1; } cffree(ev_str_new); H5Gclose(child_gh); } else if ( obj_info.type == H5O_TYPE_DATASET ) { char *addme; if ( n_pattern_bits != 1 ) { ERROR("Pattern doesn't match file" " (too long by %i)\n", n_pattern_bits); cffree(name); cffree(ev_str_new); return 1; } addme = demunge_event(ev_str_new); if ( addme != NULL ) { add_ev_to_list(list, addme); cffree(addme); } cffree(ev_str_new); } cffree(name); } return 0; } /* Not "static" so that ev_enumX can test it. * Not part of public API! */ char **expand_paths(hid_t fh, char *pattern, int *n_evs) { int n_sep; size_t len; char **pattern_bits; struct ev_list list; int i; char *start; if ( pattern == NULL ) return NULL; if ( pattern[0] != '/' ) return NULL; /* Chop up the pattern into path bits */ len = strlen(pattern); n_sep = 0; for ( i=0; idims[i] != DIM_UNDEFINED ) n_dims++; } return n_dims; } char **image_hdf5_expand_frames(const DataTemplate *dtempl, const char *filename, int *pn_frames) { char **path_evs; int n_path_evs; hid_t fh; int i; int dims_expected; struct ev_list full_evs; if ( dtempl->n_panels == 0 ) return NULL; full_evs.events = NULL; full_evs.n_events = 0; full_evs.max_events = 0; /* If the DataTemplate already says that one frame will be * found per file, short-circuit this whole affair */ if ( (imh_num_placeholders(&dtempl->panels[0]) == 0) && (imh_num_path_placeholders(dtempl->panels[0].data) == 0) ) { add_ev_to_list(&full_evs, "//"); *pn_frames = full_evs.n_events; return full_evs.events; } if ( !file_exists(filename) ) { ERROR("File not found: %s (hdf5_expand_frames)\n", filename); return NULL; } fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if ( fh < 0 ) { ERROR("Couldn't open file (hdf5_expand_frames): %s\n", filename); return NULL; } /* First, expand placeholders in the HDF5 paths. * * Since we require the number of placeholders to be the same * for all panels, and the placeholders will be substituted * with the same values for each panel (since they come from * the same event ID), this only needs to be done for the * first panel. */ path_evs = expand_paths(fh, dtempl->panels[0].data, &n_path_evs); if ( path_evs == NULL ) { ERROR("Failed to enumerate paths.\n"); close_hdf5(fh); return NULL; } dims_expected = n_dims_expected(&dtempl->panels[0]); /* For each expanded path, enumerate the placeholder * dimensions. Once again, since the number of placeholders * must be the same for each panel, and the substituted values * will be the same, this only needs to be done for one panel. */ for ( i=0; ipanels[0]; path = substitute_path(path_evs[i], p->data, 0); if ( path == NULL ) { ERROR("Path substitution failed during " "expansion of '%s' with partial event " "ID '%s'\n", p->data, path_evs[i]); return NULL; } dh = H5Dopen2(fh, path, H5P_DEFAULT); if ( dh < 0 ) { ERROR("Error opening '%s'\n", path); ERROR("Failed to enumerate events. " "Check your geometry file.\n"); close_hdf5(fh); return NULL; } sh = H5Dget_space(dh); dims = H5Sget_simple_extent_ndims(sh); if ( dims != dims_expected ) { ERROR("Unexpected number of dimensions" "(%s has %i, expected %i)\n", path, dims, dims_expected); close_hdf5(fh); return NULL; } size = cfmalloc(dims*sizeof(hsize_t)); placeholder_sizes = cfmalloc(dims*sizeof(int)); if ( (size == NULL) || (placeholder_sizes == NULL) ) { ERROR("Failed to allocate dimensions\n"); close_hdf5(fh); return NULL; } if ( H5Sget_simple_extent_dims(sh, size, NULL) < 0 ) { ERROR("Failed to get size\n"); close_hdf5(fh); return NULL; } n_placeholder_dims = 0; for ( j=0; jdims[j] == DIM_PLACEHOLDER ) { placeholder_sizes[n_placeholder_dims++] = size[j]; } } cffree(size); /* Path event ID ends with //, but expand_dims will * add a slash. So, remove one slash */ if ( n_placeholder_dims > 0 ) { char **evs_this_path; int n_evs_this_path; path_evs[i][strlen(path_evs[i])-1] = '\0'; evs_this_path = expand_dims(placeholder_sizes, n_placeholder_dims, path_evs[i], &n_evs_this_path); for ( j=0; j