#include "mrilib.h" PARAMS_euclid_dist set_euclid_dist_defaults(void) { PARAMS_euclid_dist defopt; defopt.input_name = NULL; defopt.mask_name = NULL; defopt.prefix = NULL; defopt.zeros_are_zeroed = 0; defopt.zero_region_sign = 1; defopt.nz_region_sign = 1; defopt.bounds_are_zero = 1; defopt.ignore_voxdims = 0; defopt.dist_sq = 0; defopt.rimify = 0.0; defopt.edims[0] = 0.0; defopt.edims[1] = 0.0; defopt.edims[2] = 0.0; defopt.shape[0] = 0; defopt.shape[1] = 0; defopt.shape[2] = 0; defopt.verb = 1; defopt.only2D = NULL; defopt.axes_to_proc[0] = 1; defopt.axes_to_proc[1] = 1; defopt.axes_to_proc[2] = 1; defopt.binary_only = 0; return defopt; }; // --------------------------------------------------------------------------- /* Minor helper function: we want to go through the axes in the order of descending voxel size. Ledge : (input) array of 3 vox edge lengths ord : (output) array of 3 indices, describing decreasing vox size */ int sort_vox_ord_desc(int N, float *Ledge, int *ord) { int i; float far[N]; int iar[N]; ENTRY("sort_vox_ord_desc"); for( i=0 ; idaxes, ostr); if( strcmp(which_slice, "cor") == 0 ){ // LR and IS, not AP for( i=0 ; i<3 ; i++ ) if( !strncmp(ostr+i,"A", 1) || !strncmp(ostr+i,"P", 1) ){ onoff_arr[i] = 0; break; } } else if( strcmp(which_slice, "axi") == 0 ){ // LR and AP, not IS for( i=0 ; i<3 ; i++ ) if( !strncmp(ostr+i,"I", 1) || !strncmp(ostr+i,"S", 1) ){ onoff_arr[i] = 0; break; } } else if( strcmp(which_slice, "sag") == 0 ){ // AP and IS, not LR for( i=0 ; i<3 ; i++ ) if( !strncmp(ostr+i,"R", 1) || !strncmp(ostr+i,"L", 1) ){ onoff_arr[i] = 0; break; } } if( verb ) { THD_fill_orient_str_3(dset->daxes, ostr); INFO_message("Do 2D calc in '%s' plane, and dset orient=%s,\n" " so the ON/OFF of axes for the EDT calc is: %d%d%d.", which_slice, ostr, onoff_arr[0], onoff_arr[1], onoff_arr[2]); } return 0; }; // --------------------------------------- /* Primary function to calculate rim per ROIs. Will pass along any labeltable from the original dset_rim : dset that gets filled in with values here---basically, a subset from the dset_roi at the boundary of each ROI dset_edt : the depthmap of each ROI, which is thresholded to determine how deep the rim for each ROI is dset_roi : the map of ROIs that was input rim_thr : float, the depth of the rim (can be in terms of mm or number of voxels, depending on what the distance units in dset_edt are); negative rim_thr inverts the selection: now keep parts with dist >=RIM_THR copy_lt : flag for whether to copy any labeltable that dset_roi has to dset_rim */ int calc_EDT_rim(THD_3dim_dataset *dset_rim, THD_3dim_dataset *dset_edt, THD_3dim_dataset *dset_roi, float rim_thr, int copy_lt) { int idx, nn; int nvox, nvals; int roi_datum; int sign = 1; // control for when rim_thr<0 byte *btmp_arr = NULL; short *stmp_arr = NULL; float *ftmp_arr = NULL; nvox = DSET_NVOX(dset_rim); nvals = DSET_NVALS(dset_rim); if( rim_thr < 0 ) sign = -1; roi_datum = DSET_BRICK_TYPE(dset_roi, 0) ; for( nn=0 ; nndblk , dset_roi->dblk )) { WARNING_message("Failed to copy labletable attributes"); } } return 0; }; // ========================================================================= /* ***This function treats treats in the input "ROI map" as a binary mask*** dset_edt : dset that is filled in here with EDT values opts : struct containing default/user options dset_roi : the ROI map (dataset, basically 'im' in lib_EDT.py) dset_mask : a mask dset to be applied at end (could be NULL) ival : index value of the subbrick/subvolume to analyze */ int calc_EDT_3D_BIN( THD_3dim_dataset *dset_edt, PARAMS_euclid_dist opts, THD_3dim_dataset *dset_roi, THD_3dim_dataset *dset_mask, int ival) { int i, j, k, idx; int ii, jj, kk; float minmax[2] = {0, 0}; int nx, ny, nz, nxy, nvox; int nx2, ny2, nz2; float delta = 1.0; float Ledge[3]; // voxel edge lengths ('edims' in lib_EDT.py) int vox_ord_rev[3]; float fldim[3]; // just want max arr len int dim_ord_rev[3] = {0,1,2}; int dim_max = -1; float *flarr = NULL; // store distances along one dim float *workarr = NULL; // has len of flarr, plus 2 int *maparr = NULL; // store ROI map along one dim float ***arr_dist = NULL; // array that will hold dist values float ***arr_distZ = NULL; // array that will hold dist values in zeros float *tmp_arr = NULL; ENTRY("calc_EDT_3D_BIN"); if( opts.verb ) INFO_message("Binary-mask input version"); // check if a subbrick is const; there are a couple cases where we // would be done at this stage. i = THD_subbrick_minmax( dset_roi, ival, 1, &minmax[0], &minmax[1] ); if ( minmax[0] == minmax[1] ){ if ( minmax[1] == 0.0 ){ WARNING_message("Constant (zero) subbrick: %d\n" "\t-> This volume will have all zero values", ival); return 0; } else if( !opts.bounds_are_zero ){ WARNING_message("Constant (nonzero) subbrick, " "and no bounds_are_zero: %d\n" "\t-> This volume will have all zero values", ival); return 0; } } // dset properties we need to know nx = DSET_NX(dset_roi); ny = DSET_NY(dset_roi); nz = DSET_NZ(dset_roi); nxy = nx*ny; nvox = DSET_NVOX(dset_roi); Ledge[0] = fabs(DSET_DX(dset_roi)); Ledge[1] = fabs(DSET_DY(dset_roi)); Ledge[2] = fabs(DSET_DZ(dset_roi)); // dims of array we proc nx2 = nx+2; ny2 = ny+2; nz2 = nz+2; // create arrays to be used tmp_arr = (float *) calloc( nvox, sizeof(float) ); arr_dist = (float ***) calloc( nx2, sizeof(float **) ); for ( i=0 ; i=nx ) ? nx-1 : ii; for ( j=-1 ; j=ny ) ? ny-1 : jj; for ( k=-1 ; k=nz ) ? nz-1 : kk; idx = THREE_TO_IJK(ii, jj, kk, nx, nxy); if( THD_get_voxel(dset_roi, idx, ival)) arr_dist[i+1][j+1][k+1] = EUCLID_BIG; }}} } else { // boundaries remain all zero if( opts.verb && ival==0 ) INFO_message("Copying values WITHOUT extensions for mask boundary."); for( i=0 ; i= EUCLID_BIG ) arr_dist[i][j][k] = 0.0; } if( !opts.zeros_are_zeroed ){ for ( i=1 ; i<=nx ; i++ ) for ( j=1 ; j<=ny ; j++ ) for ( k=1 ; k<=nz ; k++ ){ if( arr_distZ[i][j][k] >= EUCLID_BIG ) arr_distZ[i][j][k] = 0.0; } } } // Output distance-squared, or just distance (sqrt of what we have // so-far calc'ed); padded edge values don't matter here if( !opts.dist_sq ) { for ( i=1 ; i<=nx ; i++ ) for ( j=1 ; j<=ny ; j++ ) for ( k=1 ; k<=nz ; k++ ){ if( arr_dist[i][j][k] ) arr_dist[i][j][k] = (float) sqrt(arr_dist[i][j][k]); } if( !opts.zeros_are_zeroed ){ for ( i=1 ; i<=nx ; i++ ) for ( j=1 ; j<=ny ; j++ ) for ( k=1 ; k<=nz ; k++ ){ if( arr_distZ[i][j][k] ) arr_distZ[i][j][k] = (float) sqrt(arr_distZ[i][j][k]); } } } // negative where input was zero? if( opts.nz_region_sign==-1 ){ for ( i=1 ; i<=nx ; i++ ) for ( j=1 ; j<=ny ; j++ ) for ( k=1 ; k<=nz ; k++ ){ if ( arr_dist[i][j][k] ) arr_dist[i][j][k]*= -1; } } // combine two sets of data, if zeroes weren't zeroed: they should // be perfectly complementary (where dist is zero in one, it should // be nonzero in the other, and vice versa); depending on whether // only2D was used, some extra values may be zeroed out, but that // should be fine. // This step also handles the sign of the zero-region distances if( !opts.zeros_are_zeroed ){ for ( i=1 ; i<=nx ; i++ ) for ( j=1 ; j<=ny ; j++ ) for ( k=1 ; k<=nz ; k++ ){ if( arr_distZ[i][j][k] ) arr_dist[i][j][k] = zeros_sign * arr_distZ[i][j][k]; } } return 0; } // --------------------------------------------------------------------------- /* dset_edt : dset that is filled in here with EDT values opts : struct containing default/user options dset_roi : the ROI map (dataset, basically 'im' in lib_EDT.py) dset_mask : a mask dset to be applied at end (could be NULL) ival : index value of the subbrick/subvolume to analyze */ int calc_EDT_3D_GEN( THD_3dim_dataset *dset_edt, PARAMS_euclid_dist opts, THD_3dim_dataset *dset_roi, THD_3dim_dataset *dset_mask, int ival) { int i, j, k, idx; float minmax[2] = {0, 0}; int nx, ny, nz, nxy, nvox; float Ledge[3]; // voxel edge lengths ('edims' in lib_EDT.py) int vox_ord_rev[3]; float fldim[3]; // just want max arr len int dim_ord_rev[3] = {0,1,2}; int dim_max = -1; float *flarr = NULL; // store distances along one dim float *workarr = NULL; // has len of flarr, plus 2 int *maparr = NULL; // store ROI map along one dim float ***arr_dist = NULL; // array that will hold dist values float *tmp_arr = NULL; ENTRY("calc_EDT_3D_GEN"); // check if a subbrick is const; there are a couple cases where we // would be done at this stage. i = THD_subbrick_minmax( dset_roi, ival, 1, &minmax[0], &minmax[1] ); if ( minmax[0] == minmax[1] ){ if ( minmax[1] == 0.0 ){ WARNING_message("Constant (zero) subbrick: %d\n" "\t-> This volume will have all zero values", ival); return 0; } else if( !opts.bounds_are_zero ){ WARNING_message("Constant (nonzero) subbrick, " "and no bounds_are_zero: %d\n" "\t-> This volume will have all zero values", ival); return 0; } } // dset properties we need to know nx = DSET_NX(dset_roi); ny = DSET_NY(dset_roi); nz = DSET_NZ(dset_roi); nxy = nx*ny; nvox = DSET_NVOX(dset_roi); Ledge[0] = fabs(DSET_DX(dset_roi)); Ledge[1] = fabs(DSET_DY(dset_roi)); Ledge[2] = fabs(DSET_DZ(dset_roi)); // create arrays to be used tmp_arr = (float *) calloc( nvox, sizeof(float) ); arr_dist = (float ***) calloc( nx, sizeof(float **) ); for ( i=0 ; i=2+Na roi_line :int array of 'Na' ROI labels (unchanged) Na :length of 1D arrays used here delta :voxel dim along this 1D array bounds_are_zero :option for how to treat FOV boundaries for nonzero ROIs binary_only :if we treat the ROI map as a binary map only */ int run_EDTD_GEN_per_line( float *dist2_line, float *warr, int *roi_line, int Na, float delta, int bounds_are_zero, int binary_only ) { int idx = 0; int i, m, n; float *line_out = NULL; int start, stop, inc, roi; int npts; float *Df = NULL; int limit = Na-1; Df = (float *) calloc( Na, sizeof(float) ); while (idx < Na){ // get interval of line with current ROI value roi = roi_line[idx]; n = idx; while (n < Na){ if (roi_line[n] != roi){ break; } n += 1; } n -= 1; /* Now, n stores the index of last matching element; idx stores the index of first matching element. Here and below, pay careful attention to indices and offsets (offsets used so we can apply boundary conditions to each segment of the line). */ npts = n - idx + 1; // bc line interval of line is [n, idx] // copy dist values in this interval; note warr index is offset // by 1 here for( m=idx ; m<=n ; m++ ) warr[m+1] = dist2_line[m]; // left bound if( idx==0 ){ // on the FOV edge if(roi != 0 && bounds_are_zero) warr[idx] = 0; // a change of ROI else warr[idx] = EUCLID_BIG; // pretend like ROI keeps going } else // inside FOV warr[idx] = 0; // a change of ROI // right bound if( n==limit ){ // on the FOV edge if(roi != 0 && bounds_are_zero) warr[n+2] = 0; // a change of ROI else warr[n+2] = EUCLID_BIG; // pretend like ROI keeps going } else // inside FOV warr[n+2] = 0; // a change of ROI // now calc EDT starting from appropriate spot in warr Df = Euclidean_DT_delta(warr+idx, npts+2, delta); // copy dist values in this interval for( m=0 ; m what has been filled in here opts : struct containing default/user options dset_roi : the ROI map (dataset, basically 'im' in lib_EDT.py) ival : index value of the subbrick/subvolume to analyze */ int apply_opts_to_edt_arr_GEN( float ***arr_dist, PARAMS_euclid_dist opts, THD_3dim_dataset *dset_roi, int ival) { int i, j, k, idx; int nx, ny, nz, nxy; ENTRY("apply_opts_to_edt_arr_GEN"); // dset properties we need to know nx = DSET_NX(dset_roi); ny = DSET_NY(dset_roi); nz = DSET_NZ(dset_roi); nxy = nx*ny; // in case user calcs EDT in 2D, replace any remaining EUCLID_BIG values // (from initialization) with 0; do this before sqrt of dist**2 or // any negating of dists values, for simplicity if( opts.only2D ){ for ( i=0 ; i= EUCLID_BIG ) arr_dist[i][j][k] = 0.0; } } // Zero out EDT values in "zero" ROI? if( opts.zeros_are_zeroed ) { for ( i=0 ; i