/*---------------------------------------------------------------------- * main functions for afni (and others): * * v2s_results * afni_vol2surf - create surface data * int free_v2s_results - free surface data * * main interface routing: * * v2s_results * vol2surf - create surface data * (assumes valid parameters) * * display functions: * * int disp_mri_imarr ( char * info, MRI_IMARR * dp ); * int disp_v2s_opts_t ( char * info, v2s_opts_t * sopt ); * int disp_v2s_param_t ( char * info, v2s_param_t * p ); * int disp_v2s_plugin_opts ( char * info, v2s_plugin_opts * d ); * int disp_v2s_results ( char * mesg, v2s_results * d ); * * Author: R Reynolds *---------------------------------------------------------------------- */ #define _VOL2SURF_C_ /* so the header files know */ char gv2s_history[] = "----------------------------------------------------------------------\n" "vol2surf library history:\n" "\n" "September 01, 2004 [rickr]\n" " - initial install into afni\n" "\n" "September 02, 2004 [rickr]\n" " - moved gv2s_map_names here (from SUMA_3dVol2Surf.c)\n" " - moved v2s_map_type (and test) here (from SUMA_3dVol2Surf.c)\n" " - define _VOL2SURF_C_, to allow extern defines in vol2surf.h\n" "\n" "September 09, 2004 [rickr]\n" " - in afni_vol2surf(), print v2s options when debug > 1\n" " - allow (first_node > last_node) if (last == 0), then change to n-1\n" "\n" "September 16, 2004 [rickr]\n" " - added support for -gp_index, computing over a single sub-brick\n" " - altered subs in dump_surf_3dt(), max_index in set_surf_results(),\n" " set brick_index in v2s_apply_filter(), mem in alloc_output_mem(),\n" " and added an index check in validate_v2s_inputs()\n" " - add gp_index as a parameter of afni_vol2surf()\n" " - changed keep_norm_dir to norm_dir, allowing default/keep/reverse\n" "\n" "September 29, 2004 [rickr]\n" " - added thd_mask_from_brick() (to make byte mask from sub-brick)\n" " - added compact_results(), in case nalloc > nused\n" " - added realloc_ints() and realloc_vals_list() (for compact_results())\n" " - in afni_vol2surf(), if 1 surf and no norms, set steps to 1\n" " - in set_surf_results(), pass gp_index to v2s_apply_filter\n" " - segment oob only if both nodes are\n" " - move dset bounds to range_3dmm struct\n" " - in segment_imarr()\n" " - changed THD_3dmm_to_3dind() to new THD_3dmm_to_3dind_no_wod()\n" " - verify success of THD_extract_series()\n" " - keep track of repeated and oob nodes\n" " - in init_seg_endpoints(), nuke p1, pn; save dicom_to_mm until end\n" " - changed THD_3dmm_to_3dind() to new THD_3dmm_to_3dind_no_wod()\n" " - added function v2s_is_good_map_index()\n" "\n" "October 08, 2004 [rickr]\n" " - added disp_v2s_plugin_opts()\n" " - dealt with default v2s mapping of surface pairs\n" " - added fill_sopt_default()\n" " - moved v2s_write_outfile_*() here, with print_header()\n" " - in afni_vol2surf(), actually write output files\n" "\n" "October 25, 2004 [rickr]\n" " - apply debug and dnode, even for defaults\n" " - if the user sets dnode, then skip any (debug > 0) tests for it\n" " - check for out of bounds, even if an endpoint is in (e.g. midpoint)\n" " - in thd_mask_from_brick(), allow for absolute thresholding\n" "\n" "March 22, 2005 [rickr]\n" " - remove tabs\n" "---------------------------------------------------------------------\n"; #include "mrilib.h" #include "vol2surf.h" /*----------------------------------------------------------------------*/ /* local typedefs */ typedef struct { int nused; int nalloc; float * list; } float_list_t; typedef struct { THD_3dim_dataset * dset; /* for data and geometry */ THD_fvec3 p1; /* segment endpoints */ THD_fvec3 pn; THD_fvec3 dset_min; /* bounds on the dataset */ THD_fvec3 dset_max; int oob_check; /* should we check for oob? */ int debug; /* for local control */ } range_3dmm; typedef struct { MRI_IMARR ims; /* the image array struct */ int repeats; /* number of repeated nodes */ int masked; /* number of masked points */ int oob; /* number of oob points */ int ifirst; /* 1D index of first point */ THD_ivec3 i3first; /* i3ind index of first point */ THD_ivec3 * i3arr; /* i3ind index array */ } range_3dmm_res; /*----------------------------------------------------------------------*/ /* local prototypes */ static v2s_results * alloc_output_mem (v2s_opts_t *sopt, v2s_param_t *p); static int alloc_ints(int ** ptr, int length, char * dstr, int debug); static int alloc_vals_list(float *** ptr, int length, int width, int debug); static int check_SUMA_surface( SUMA_surface * s ); static int compact_results(v2s_results * sd, int debug); static float directed_dist(float * pnew, float * pold, float *dir, float dist); static float dist_f3mm( THD_fvec3 * p1, THD_fvec3 * p2 ); static int disp_range_3dmm( char * info, range_3dmm * dp ); static int disp_range_3dmm_res( char * info, range_3dmm_res * dp ); static int disp_surf_vals( char * mesg, v2s_results * sd, int node ); static int dump_surf_3dt(v2s_opts_t *sopt, v2s_param_t *p, v2s_results *sd); static int f3mm_out_of_bounds(THD_fvec3 *cp, THD_fvec3 *min, THD_fvec3 *max); static int fill_sopt_default(v2s_opts_t * sopt, int nsurf ); static int float_list_alloc(float_list_t *f,int **ilist,int size,int trunc); static int float_list_comp_mode(float_list_t *f, float *mode, int *nvals, int *index); static int float_list_slow_sort(float_list_t * f, int * ilist); static int init_seg_endpoints(v2s_opts_t * sopt, v2s_param_t * p, range_3dmm * R, int node ); static int init_range_structs( range_3dmm * r3, range_3dmm_res * res3 ); static double magnitude_f( float * p, int length ); static int print_header(FILE *outfp, char *surf, char *map, v2s_results *sd); static int realloc_ints( int ** ptr, int length, char * dstr, int debug ); static int realloc_vals_list(float ** ptr, int length, int width, int debug); static int set_3dmm_bounds(THD_3dim_dataset *dset, THD_fvec3 *min, THD_fvec3 *max); static int set_all_surf_vals (v2s_results * sd, int node_ind, int vind, int i, int j, int k, float fval); static int set_surf_results(v2s_param_t *p, v2s_opts_t *sopt,v2s_results *sd, range_3dmm_res * r3res, int node, int findex); static int segment_imarr(range_3dmm_res *res,range_3dmm *R,v2s_opts_t *sopt, byte * cmask); static int v2s_adjust_endpts(v2s_opts_t *sopt, THD_fvec3 *p1, THD_fvec3 *pn); static float v2s_apply_filter(range_3dmm_res *rr, v2s_opts_t *sopt, int index, int * findex); static int v2s_map_needs_sort(int map); static int validate_v2s_inputs(v2s_opts_t * sopt, v2s_param_t * p); /*----------------------------------------------------------------------*/ /* globals to be accessed by plugin and in afni_suma.c */ v2s_plugin_opts gv2s_plug_opts = {0, 0, 0, -1, -1, -1, -1}; /* this must match v2s_map_nums enum */ char * gv2s_map_names[] = { "none", "mask", "midpoint", "mask2", "ave", "count", "min", "max", "max_abs", "seg_vals", "median", "mode" }; /*---------------------------------------------------------------------- * afni_vol2surf - create v2s_results from gv2s_* afni globals * * input: gpar : AFNI dataset to be used as the grid parent * gp_index : sub-brick selector * sA : surface A structure * sB : surface B structure * mask : thresholding mask * use_defaults : use default sopt structure * * output: sd : allocated v2s_results struct, with requested data * * This function is used to map data from an AFNI volume to a surface. * These structures are expected to be complete. *---------------------------------------------------------------------- */ v2s_results * afni_vol2surf ( THD_3dim_dataset * gpar, int gp_index, SUMA_surface * sA, SUMA_surface * sB, byte * mask, int use_defaults ) { static v2s_param_t P; v2s_opts_t * sopt, sopt_def; v2s_results * res; ENTRY("afni_vol2surf"); if ( !gpar ) RETURN(NULL); if ( check_SUMA_surface(sA) ) RETURN(NULL); if ( sB && check_SUMA_surface(sB) ) RETURN(NULL); if ( use_defaults ) { sopt = &sopt_def; fill_sopt_default(sopt, sB ? 2 : 1); /* 1 or 2 surfaces */ /* but apply any debug options */ sopt->debug = gv2s_plug_opts.sopt.debug; sopt->dnode = gv2s_plug_opts.sopt.dnode; } else sopt = &gv2s_plug_opts.sopt; sopt->gp_index = gp_index; /* now fill the param struct based on the inputs */ memset(&P, 0, sizeof(P)); P.gpar = gpar; P.cmask = mask; P.nvox = DSET_NVOX(gpar); P.over_steps = v2s_vals_over_steps(sopt->map); P.nsurf = sB ? 2 : 1; P.surf[0] = *sA; /* verify steps, in case the user has not selected 2 surfaces */ if ( P.nsurf == 1 && ! sopt->use_norms ) sopt->f_steps = 1; if ( sB ) P.surf[1] = *sB; if ( gv2s_plug_opts.sopt.debug > 1 ) disp_v2s_opts_t(" surf options: ", sopt); /* fire it up */ res = vol2surf(sopt, &P); /* if the user wants output files, here they are (don't error check) */ if (res && sopt->outfile_1D ) { if ( THD_is_file(sopt->outfile_1D) ) fprintf(stderr,"** over-writing 1D output file '%s'\n", sopt->outfile_1D); v2s_write_outfile_1D(sopt, res, P.surf[0].label); } if (res && sopt->outfile_niml ) { if ( THD_is_file(sopt->outfile_niml) ) fprintf(stderr,"** over-writing niml output file '%s'\n", sopt->outfile_niml); v2s_write_outfile_niml(sopt, res, 0); } RETURN(res); } /*---------------------------------------------------------------------- * vol2surf - produce a v2s_results surface dataset * * input: sopt : volume to surface options struct * p : volume to surface parameter struct * * output: sd : allocated v2s_results struct, with requested data * * This function is used to map data from an AFNI volume to a surface. * These structures are expected to be complete. *---------------------------------------------------------------------- */ v2s_results * vol2surf ( v2s_opts_t * sopt, v2s_param_t * p ) { v2s_results * sd; int rv; ENTRY("vol2surf"); if ( sopt == NULL || p == NULL ) { fprintf( stderr, "** smd_wo - bad params (%p,%p)\n", sopt, p ); RETURN(NULL); } if ( validate_v2s_inputs(sopt, p) ) RETURN(NULL); if ( sopt->map == E_SMAP_INVALID ) { fprintf(stderr,"** v2s wo: invalid map %d\n", sopt->map); RETURN(NULL); } sd = alloc_output_mem( sopt, p ); if ( !sd ) RETURN(NULL); if ( sopt->debug > 1 ) disp_v2s_param_t( "-d post alloc_output_mem : ", p ); rv = dump_surf_3dt( sopt, p, sd ); if ( compact_results(sd, sopt->debug) ) { free_v2s_results(sd); /* free whatever didn't get burned */ RETURN(NULL); } if ( sopt->debug > 1 ) disp_v2s_results( "-d post surf creation : ", sd); RETURN(sd); } /* compact_results - if nused < nalloc, realloc all pointers */ static int compact_results(v2s_results * sd, int debug) { int rv = 0, mem = 0; ENTRY("compact_results"); if ( sd->nused > sd->nalloc ) /* should not happen, of course */ { fprintf(stderr,"** cr: nused (%d) > nalloc (%d) !!\n", sd->nused, sd->nalloc); RETURN(-1); } if ( sd->nused == sd->nalloc ) RETURN(0); /* we're good */ /* otherwise, realloc everthing */ sd->nalloc = sd->nused; if ( sd->nodes ) { mem += sizeof(int); rv = realloc_ints(&sd->nodes, sd->nalloc, "nodes", debug); } if ( ! rv && sd->volind ) { mem += sizeof(int); rv = realloc_ints(&sd->volind, sd->nalloc, "volind", debug); } if ( ! rv && sd->i ) { mem += sizeof(int); rv = realloc_ints(&sd->i, sd->nalloc, "i", debug); } if ( ! rv && sd->j ) { mem += sizeof(int); rv = realloc_ints(&sd->j, sd->nalloc, "j", debug); } if ( ! rv && sd->k ) { mem += sizeof(int); rv = realloc_ints(&sd->k, sd->nalloc, "k", debug); } if ( ! rv && sd->nvals ) { mem += sizeof(int); rv = realloc_ints(&sd->nvals, sd->nalloc, "nvals", debug); } if ( ! rv ) { mem += (sizeof(float) * sd->max_vals); rv = realloc_vals_list(sd->vals, sd->nalloc, sd->max_vals, debug); } if ( rv ) RETURN(rv); /* if there was a failure, just leave */ mem *= sd->nalloc; /* now multiply be the array length */ if ( debug > 1 ) fprintf(stderr,"+d compact results: reallocated %d bytes down to %d\n", sd->memory, mem); sd->memory = mem; RETURN(rv); } /*---------------------------------------------------------------------- * dump_surf_3dt - for each node index, get an appropriate node sampling, * and compute and output results across sub-bricks *---------------------------------------------------------------------- */ static int dump_surf_3dt( v2s_opts_t * sopt, v2s_param_t * p, v2s_results * sd ) { range_3dmm_res r3mm_res; range_3dmm r3mm; float dist, min_dist, max_dist; int sub, subs, nindex, findex = 0; int oobc, oomc, max_index; int oob1, oob2; ENTRY("dump_surf_3dt"); if ( ! sopt || ! p || ! sd ) { fprintf(stderr, "** ds3 : bad params (%p,%p,%p)\n", sopt, p, sd ); RETURN(-1); } /* note the number of sub-bricks, unless the user has given just one */ subs = sopt->gp_index >= 0 ? 1 : DSET_NVALS(p->gpar); init_range_structs( &r3mm, &r3mm_res ); /* to empty */ r3mm.dset = p->gpar; set_3dmm_bounds( p->gpar, &r3mm.dset_min, &r3mm.dset_max ); if ( sopt->debug > 1 ) fprintf(stderr, "-d dset bounding box: (%f, %f, %f)\n" " (%f, %f, %f)\n", r3mm.dset_min.xyz[0],r3mm.dset_min.xyz[1],r3mm.dset_min.xyz[2], r3mm.dset_max.xyz[0],r3mm.dset_max.xyz[1],r3mm.dset_max.xyz[2]); min_dist = 9999.9; /* v2.3 */ max_dist = -1.0; oobc = 0; /* init out-of-bounds counter */ oomc = 0; /* init out-of-mask counter */ /* note, NodeList elements are in dicomm mm orientation */ for ( nindex = sopt->first_node; nindex <= sopt->last_node; nindex++ ) { /* init default max for oob and oom cases */ max_index = p->over_steps ? sopt->f_steps : subs; init_seg_endpoints(sopt, p, &r3mm, nindex); /* segment endpoints */ v2s_adjust_endpts( sopt, &r3mm.p1, &r3mm.pn ); if ( r3mm.debug ) r3mm.debug = 0; if ( nindex == sopt->dnode ) /* if we have dnode, forget debug */ r3mm.debug = sopt->debug > 0 ? sopt->debug : 1; /* if both points are outside our dataset, skip the pair v2.3 */ oob1 = f3mm_out_of_bounds( &r3mm.p1, &r3mm.dset_min, &r3mm.dset_max ); oob2 = f3mm_out_of_bounds( &r3mm.pn, &r3mm.dset_min, &r3mm.dset_max ); if ( oob1 && oob2 ) { oobc++; if ( sopt->oob.show ) if ( set_all_surf_vals( sd, nindex, sopt->oob.index, sopt->oob.index, sopt->oob.index, sopt->oob.index, sopt->oob.value) ) RETURN(1); if ( nindex == sopt->dnode ) { disp_surf_vals("-d debug node, out-of-bounds : ", sd, -1); fprintf(stderr,"-d dnode coords: (%f, %f, %f)\n", r3mm.p1.xyz[0], r3mm.p1.xyz[1], r3mm.p1.xyz[2]); } continue; } else r3mm.oob_check = oob1 || oob2; dist = dist_f3mm( &r3mm.p1, &r3mm.pn ); if ( dist < min_dist ) min_dist = dist; if ( dist > max_dist ) max_dist = dist; if ( segment_imarr( &r3mm_res, &r3mm, sopt, p->cmask ) != 0 ) continue; if ( r3mm_res.ims.num == 0 ) /* any good voxels in the bunch? */ { /* oob or oom? */ if ( r3mm_res.oob == sopt->f_steps ) /* out of bounds */ { oobc++; if ( sopt->oob.show ) if ( set_all_surf_vals( sd, nindex, sopt->oob.index, sopt->oob.index, sopt->oob.index, sopt->oob.index, sopt->oob.value) ) RETURN(1); if ( nindex == sopt->dnode ) disp_surf_vals("-d debug node, out-of-bounds : ", sd, -1); } else /* then we consider it out of mask */ { oomc++; if ( sopt->oom.show ) if ( set_all_surf_vals( sd, nindex, r3mm_res.ifirst, r3mm_res.i3first.ijk[0], r3mm_res.i3first.ijk[1], r3mm_res.i3first.ijk[2], sopt->oom.value ) ) RETURN(1); if ( nindex == sopt->dnode ) disp_surf_vals("-d debug node, out-of-mask : ", sd, -1); } if ( nindex == sopt->dnode ) fprintf(stderr,"-d dnode coords: (%f, %f, %f)\n", r3mm.p1.xyz[0], r3mm.p1.xyz[1], r3mm.p1.xyz[2]); continue; /* in any case */ } /* get element 0, just for the findex */ (void)v2s_apply_filter(&r3mm_res, sopt, 0, &findex); if ( set_surf_results(p, sopt, sd, &r3mm_res, nindex, findex) ) RETURN(-1); /* clean up the MRI_IMARR struct, but don't free imarr */ for ( sub = 0; sub < r3mm_res.ims.num; sub++ ) { mri_free(r3mm_res.ims.imarr[sub]); r3mm_res.ims.imarr[sub] = NULL; } r3mm_res.ims.num = 0; } if ( sopt->debug > 0 ) /* v2.3 */ { fprintf( stderr, "-d node pair dist (min,max) = (%f,%f)\n", min_dist, max_dist ); fprintf( stderr, "-d out-of-bounds, o-o-mask counts : %d, %d (of %d)\n", oobc, oomc, sopt->last_node - sopt->first_node + 1); } /* now we can free the imarr and voxel lists */ if ( r3mm_res.ims.nall > 0 ) { free(r3mm_res.ims.imarr); free(r3mm_res.i3arr); r3mm_res.ims.nall = 0; } RETURN(0); } /*********************************************************************** * set_surf_results *********************************************************************** */ static int set_surf_results(v2s_param_t *p, v2s_opts_t * sopt, v2s_results * sd, range_3dmm_res * r3res, int node, int findex) { int i, j, k, volind; int c, max_index; ENTRY("set_surf_results"); if ( sd->nused >= sd->nalloc ) { fprintf(stderr,"** ssr: nused (%d) >= nalloc (%d)!\n", sd->nused, sd->nalloc); RETURN(1); } i = r3res->i3arr[findex].ijk[0]; j = r3res->i3arr[findex].ijk[1]; k = r3res->i3arr[findex].ijk[2]; /* now get 3D and 1D coordinates */ volind = i + DSET_NX(p->gpar) * (j + DSET_NY(p->gpar) * k ); /* set everything but the values */ if (sd->nodes ) sd->nodes[sd->nused] = node; if (sd->volind) sd->volind[sd->nused] = volind; if (sd->i ) sd->i[sd->nused] = i; if (sd->j ) sd->j[sd->nused] = j; if (sd->k ) sd->k[sd->nused] = k; if (sd->nvals ) sd->nvals[sd->nused] = r3res->ims.num; /* set max_index, and adjust in case max_vals has been restricted */ max_index = p->over_steps ? r3res->ims.num : DSET_NVALS(p->gpar); if ( max_index > sd->max_vals ) max_index = sd->max_vals; if ( sopt->gp_index >= 0 ) sd->vals[0][sd->nused] = v2s_apply_filter(r3res, sopt, sopt->gp_index, NULL); else for ( c = 0; c < max_index; c++ ) sd->vals[c][sd->nused] = v2s_apply_filter(r3res, sopt, c, NULL); /* possibly fill line with default if by steps and short */ if ( max_index < sd->max_vals ) for ( c = max_index; c < sd->max_vals; c++ ) sd->vals[c][sd->nused] = 0.0; /* rcr : should I nuke the MRI images, and just copy what is needed? */ if ( node == sopt->dnode ) { fprintf(stderr, "--------------------------------------------------\n"); if ( ! p->over_steps && sopt->gp_index >= 0 ) fprintf(stderr,"+d dnode %d gets %f from gp_index %d\n", node, sd->vals[0][sd->nused], sopt->gp_index); if ( sopt->debug > 1 ) fprintf(stderr, "-d debug: node %d, findex %d, vol_index %d\n", node, findex, volind ); if ( sopt->use_norms ) { float * fp = p->surf[0].norm[node].xyz; fprintf(stderr,"-d normal %f, %f, %f\n", fp[0], fp[1], fp[2]); } disp_mri_imarr( "+d raw data: ", &r3res->ims ); } sd->nused++; RETURN(0); } /*********************************************************************** * set_all_surf_vals * return 0 on success *********************************************************************** */ static int set_all_surf_vals( v2s_results * sd, int node_ind, int vind, int i, int j, int k, float fval ) { int c, nused; ENTRY("set_all_surf_vals"); nused = sd->nused; if ( nused >= sd->nalloc ) { fprintf(stderr,"** sasv: nused=%d >= nalloc=%d!\n", nused, sd->nalloc); RETURN(1); } if ( sd->nodes ) sd->nodes[nused] = node_ind; if ( sd->volind ) sd->volind[nused] = vind; if ( sd->i ) sd->i[nused] = i; if ( sd->j ) sd->j[nused] = j; if ( sd->k ) sd->k[nused] = k; if ( sd->nvals ) sd->nvals[nused] = sd->max_vals; for ( c = 0; c < sd->max_vals; c++ ) sd->vals[c][nused] = fval; sd->nused++; RETURN(0); } /*********************************************************************** * segment_imarr - get MRI_IMARR for steps over a segment * * The res->ims structure should be empty, except that it may * optionally contain space for pointers in imarr. Note that nall * should be accurate. * * return 0 on success *********************************************************************** */ static int segment_imarr( range_3dmm_res *res, range_3dmm *R, v2s_opts_t *sopt, byte * cmask ) { THD_fvec3 f3mm; THD_ivec3 i3ind; float rat1, ratn; int nx, ny; int step, vindex, prev_ind; ENTRY("segment_imarr"); /* check params for validity */ if ( !R || !sopt || !res || !R->dset ) { fprintf(stderr, "** seg_imarr: invalid params (%p,%p,%p)\n",R,sopt,res); if ( R ) disp_range_3dmm("segment_imarr: bad inputs:", R ); RETURN(-1); } if ( R->debug > 1 ) disp_range_3dmm("-d segment_imarr: ", R ); /* handle this as an acceptable, trivial case */ if ( sopt->f_steps < 1 ) { res->ims.num = 0; RETURN(0); } nx = DSET_NX(R->dset); ny = DSET_NY(R->dset); /* if we don't have enough memory for results, (re)allocate some */ if ( res->ims.nall < sopt->f_steps ) { if ( R->debug > 1 ) fprintf(stderr,"+d realloc of imarr (from %d to %d pointers)\n", res->ims.nall,sopt->f_steps); res->ims.nall = sopt->f_steps; res->ims.imarr = realloc(res->ims.imarr, sopt->f_steps*sizeof(MRI_IMAGE *)); res->i3arr = realloc(res->i3arr, sopt->f_steps*sizeof(THD_ivec3)); if ( !res->ims.imarr || !res->i3arr ) { fprintf(stderr,"** seg_imarr: no memory for %d MRI_IMAGE ptrs\n", sopt->f_steps); res->ims.nall = res->ims.num = 0; /* one might be good */ if ( res->ims.imarr ) free(res->ims.imarr); if ( res->i3arr ) free(res->i3arr); RETURN(-1); } } /* init return structure */ res->ims.num = 0; res->repeats = 0; res->masked = 0; res->oob = 0; res->i3first = THD_3dmm_to_3dind_no_wod( R->dset, R->p1 ); res->ifirst = res->i3first.ijk[0] + nx * (res->i3first.ijk[1] + ny * res->i3first.ijk[2] ); prev_ind = -1; /* in case we want unique voxels */ for ( step = 0; step < sopt->f_steps; step++ ) { /* set our endpoint ratios */ if ( sopt->f_steps < 2 ) /* if this is based on a single point */ ratn = 0.0; else ratn = (float)step / (sopt->f_steps - 1); rat1 = 1.0 - ratn; f3mm.xyz[0] = rat1 * R->p1.xyz[0] + ratn * R->pn.xyz[0]; f3mm.xyz[1] = rat1 * R->p1.xyz[1] + ratn * R->pn.xyz[1]; f3mm.xyz[2] = rat1 * R->p1.xyz[2] + ratn * R->pn.xyz[2]; /* accept part being oob 30 Sep 2004 [rickr] */ if ( R->oob_check && f3mm_out_of_bounds( &f3mm, &R->dset_min, &R->dset_max ) ) { res->oob++; continue; } i3ind = THD_3dmm_to_3dind_no_wod( R->dset, f3mm ); vindex = i3ind.ijk[0] + nx * (i3ind.ijk[1] + ny * i3ind.ijk[2] ); /* is this voxel masked out? */ if ( cmask && !cmask[vindex] ) { res->masked++; continue; } /* is this voxel repeated, and if so, do we skip it? */ if ( sopt->f_index == V2S_INDEX_VOXEL && vindex == prev_ind ) { res->repeats++; continue; } /* Huston, we have a good voxel... */ prev_ind = vindex; /* note this for next time */ /* now for the big finish, get and insert the actual data */ res->i3arr [res->ims.num] = i3ind; /* store the 3-D indices */ res->ims.imarr[res->ims.num] = THD_extract_series( vindex, R->dset, 0 ); res->ims.num++; if ( ! res->ims.imarr[res->ims.num-1] ) { fprintf(stderr,"** failed THD_extract_series, vox %d\n", vindex); RETURN(-1); } if ( R->debug > 2 ) fprintf(stderr, "-d seg step %2d, vindex %d, coords %f %f %f\n", step,vindex,f3mm.xyz[0],f3mm.xyz[1],f3mm.xyz[2]); } if ( R->debug > 1 ) disp_range_3dmm_res( "+d i3mm_seg_imarr results: ", res ); RETURN(0); } /*---------------------------------------------------------------------- * f3mm_out_of_bounds - check wether cp is between min and max * * - v2.3 [rickr] *---------------------------------------------------------------------- */ static int f3mm_out_of_bounds( THD_fvec3 * cp, THD_fvec3 * min, THD_fvec3 * max) { int c; if ( !cp || !min || !max ) return(-1); for ( c = 0; c < 3; c++ ) { if ( ( cp->xyz[c] < min->xyz[c] ) || ( cp->xyz[c] > max->xyz[c] ) ) return(-1); } return(0); } /*---------------------------------------------------------------------- * v2s_adjust_endpoints - adjust endpoints for map and options * * return 0 on success * < 0 on error *---------------------------------------------------------------------- */ static int v2s_adjust_endpts( v2s_opts_t * sopt, THD_fvec3 * p1, THD_fvec3 * pn) { THD_fvec3 f3_diff; float dist, factor; ENTRY("v2s_adjust_endpts"); if ( !sopt || !p1 || !pn ) { fprintf(stderr,"** v2s_ae: invalid params (%p,%p,%p)\n", sopt, p1, pn); RETURN(-1); } /* first, get the difference, and distance */ f3_diff.xyz[0] = pn->xyz[0] - p1->xyz[0]; f3_diff.xyz[1] = pn->xyz[1] - p1->xyz[1]; f3_diff.xyz[2] = pn->xyz[2] - p1->xyz[2]; dist = dist_f3mm( p1, pn ); if ( (sopt->f_p1_fr != 0.0) || (sopt->f_p1_mm != 0.0) ) { if ( sopt->f_p1_fr != 0.0 ) /* what the heck, choose fr if both */ factor = sopt->f_p1_fr; else factor = (dist == 0.0) ? 0.0 : sopt->f_p1_mm / dist; p1->xyz[0] += factor * f3_diff.xyz[0]; p1->xyz[1] += factor * f3_diff.xyz[1]; p1->xyz[2] += factor * f3_diff.xyz[2]; } if ( (sopt->f_pn_fr != 0.0) || (sopt->f_pn_mm != 0.0) ) { if ( sopt->f_pn_fr != 0.0 ) factor = sopt->f_pn_fr; else factor = (dist == 0.0) ? 0.0 : sopt->f_pn_mm / dist; pn->xyz[0] += factor * f3_diff.xyz[0]; pn->xyz[1] += factor * f3_diff.xyz[1]; pn->xyz[2] += factor * f3_diff.xyz[2]; } switch ( sopt->map ) { default: fprintf(stderr,"** v2s_ae: mapping %d not ready\n", sopt->map ); RETURN(-1); case E_SMAP_AVE: case E_SMAP_MAX: case E_SMAP_MAX_ABS: case E_SMAP_MIN: case E_SMAP_MASK: case E_SMAP_SEG_VALS: case E_SMAP_MEDIAN: case E_SMAP_MODE: break; case E_SMAP_MIDPT: /* set the first point to be the average of the two */ p1->xyz[0] = (p1->xyz[0] + pn->xyz[0]) / 2.0; p1->xyz[1] = (p1->xyz[1] + pn->xyz[1]) / 2.0; p1->xyz[2] = (p1->xyz[2] + pn->xyz[2]) / 2.0; break; } RETURN(0); } /*--------------------------------------------------------------------------- * v2s_apply_filter - compute results for the given function and index * * As a side step, return any filter result index. *--------------------------------------------------------------------------- */ static float v2s_apply_filter( range_3dmm_res * rr, v2s_opts_t * sopt, int index, int * findex ) { static float_list_t flist = { 0, 0, NULL }; /* for sorting results */ static int * ind_list = NULL; /* track index sources */ double tmp, comp = 0.0; float fval; int count, source; int brick_index = 0; ENTRY("v2s_apply_filter"); if ( !rr || !sopt || index < 0 ) { fprintf(stderr,"** v2s_cm2: invalid params (%p,%p,%d)\n", rr, sopt, index); RETURN(0.0); } if ( rr->ims.num <= 0 ) RETURN(0.0); /* if sorting is required for resutls, do it now */ if ( v2s_map_needs_sort( sopt->map ) ) { if ( float_list_alloc( &flist, &ind_list, rr->ims.num, 0 ) != 0 ) RETURN(0.0); for ( count = 0; count < rr->ims.num; count++ ) { flist.list[count] = MRI_FLOAT_PTR(rr->ims.imarr[count])[index]; ind_list [count] = count; /* init index sources */ } flist.nused = rr->ims.num; float_list_slow_sort( &flist, ind_list ); } switch ( sopt->map ) { default: if ( findex ) *findex = 0; RETURN(0.0); case E_SMAP_AVE: if ( findex ) *findex = 0; for ( count = 0; count < rr->ims.num; count++ ) comp += MRI_FLOAT_PTR(rr->ims.imarr[count])[index]; comp = comp / rr->ims.num; break; case E_SMAP_MASK: case E_SMAP_MIDPT: if ( findex ) *findex = 0; /* we have only the one point */ comp = MRI_FLOAT_PTR(rr->ims.imarr[0])[index]; break; case E_SMAP_MAX: comp = MRI_FLOAT_PTR(rr->ims.imarr[0])[index]; if ( findex ) *findex = 0; for ( count = 1; count < rr->ims.num; count++ ) { tmp = MRI_FLOAT_PTR(rr->ims.imarr[count])[index]; if ( tmp > comp ) { if ( findex ) *findex = count; comp = tmp; } } break; case E_SMAP_MAX_ABS: comp = MRI_FLOAT_PTR(rr->ims.imarr[0])[index]; if ( findex ) *findex = 0; for ( count = 1; count < rr->ims.num; count++ ) { tmp = MRI_FLOAT_PTR(rr->ims.imarr[count])[index]; if ( fabs(tmp) > fabs(comp) ) { if ( findex ) *findex = count; comp = tmp; } } break; case E_SMAP_MIN: comp = MRI_FLOAT_PTR(rr->ims.imarr[0])[index]; if ( findex ) *findex = 0; for ( count = 1; count < rr->ims.num; count++ ) { tmp = MRI_FLOAT_PTR(rr->ims.imarr[count])[index]; if ( tmp < comp ) { if ( findex ) *findex = count; comp = tmp; } } break; case E_SMAP_SEG_VALS: /* if the user has specified a brick index, use it */ if ( sopt->gp_index >= 0 ) brick_index = sopt->gp_index; if ( findex ) *findex = 0; comp = MRI_FLOAT_PTR(rr->ims.imarr[index])[brick_index]; break; case E_SMAP_MEDIAN: count = flist.nused >> 1; if ( (flist.nused & 1) || (count == 0) ) { comp = flist.list[count]; if ( findex ) *findex = ind_list[count]; } else { comp = (flist.list[count-1] + flist.list[count]) / 2; if ( findex ) *findex = ind_list[count-1]; /* take first */ } break; case E_SMAP_MODE: float_list_comp_mode(&flist, &fval, &count, &source); comp = fval; if ( findex ) *findex = ind_list[source]; break; } RETURN((float)comp); } /*---------------------------------------------------------------------- * v2s_map_needs_sort - does this map function require sorting? * * return 1 on true * 0 on false *---------------------------------------------------------------------- */ static int v2s_map_needs_sort( int map ) { if ( (map == E_SMAP_MEDIAN) || (map == E_SMAP_MODE) ) return 1; return 0; } /*---------------------------------------------------------------------- * float_list_comp_mode - compute the mode of the list * * return 0 : on success * -1 : on error *---------------------------------------------------------------------- */ static int float_list_comp_mode( float_list_t *f, float *mode, int *nvals, int *index ) { float fcur; int ncur, c; ENTRY("float_list_comp_mode"); /* init default results */ *nvals = ncur = 1; *mode = fcur = f->list[0]; *index = 0; for ( c = 1; c < f->nused; c++ ) { if ( f->list[c] == fcur ) ncur ++; else /* found a new entry to count */ { if ( ncur > *nvals ) /* keep track of any new winner */ { *mode = fcur; *nvals = ncur; *index = c; } fcur = f->list[c]; ncur = 1; } } if ( ncur > *nvals ) /* keep track of any new winner */ { *mode = fcur; *nvals = ncur; *index = c; } RETURN(0); } /*---------------------------------------------------------------------- * float_list_slow_sort - sort (small) float list * * If ilist, track index sources. * * return 0 on success * < 0 on error *---------------------------------------------------------------------- */ static int float_list_slow_sort( float_list_t * f, int * ilist ) { float * list, save; int c0, c1, sindex; ENTRY("float_list_slow_sort"); list = f->list; /* for any little speed gain */ for ( c0 = 0; c0 < f->nused-1; c0++ ) { sindex = c0; save = list[c0]; /* find smallest remaining */ for ( c1 = c0+1; c1 < f->nused; c1++ ) if ( list[c1] < save ) { sindex = c1; save = list[sindex]; } /* swap if smaller was found */ if ( sindex > c0 ) { list[sindex] = list[c0]; list[c0] = save; if ( ilist ) /* make same swap of indices */ { c1 = ilist[sindex]; ilist[sindex] = ilist[c0]; ilist[c0] = c1; } } } RETURN(0); } /*---------------------------------------------------------------------- * float_list_alloc - verify float list memory * * If trunc(ate), realloc down to necessary size. * If ilist, make space for accompanying int list. * * return 0 on success * < 0 on error *---------------------------------------------------------------------- */ static int float_list_alloc(float_list_t *f, int **ilist, int size, int trunc) { ENTRY("float_list_alloc"); if ( (f->nalloc < size) || (trunc && (f->nalloc > size)) ) { f->list = (float *)realloc(f->list, size * sizeof(float)); if ( ! f->list ) { fprintf(stderr,"** float_list_alloc: failed for %d floats\n", size); RETURN(-2); } f->nalloc = size; if ( ilist ) /* then allocate accompanying ilist */ { *ilist = (int *)realloc(*ilist, size * sizeof(int)); if ( ! *ilist ) { fprintf(stderr,"** float_list_alloc: failed for %d ints\n", size); RETURN(-2); } } if ( trunc && (f->nused > f->nalloc) ) f->nused = f->nalloc; } RETURN(0); } /*--------------------------------------------------------------------------- * directed_dist - travel from pold, along dir, a distance of dist *--------------------------------------------------------------------------- */ static float directed_dist(float * pnew, float * pold, float * dir, float dist) { double mag, ratio; int c; ENTRY("directed_dist"); mag = magnitude_f(dir, 3); if ( mag < V2S_EPSILON ) /* can't be negative */ ratio = 0.0; else ratio = dist/mag; for ( c = 0; c < 3; c++ ) pnew[c] = pold[c] + dir[c]*ratio; RETURN(dist); } /*---------------------------------------------------------------------- * init_seg_endpoints - initialize segment endpoints *---------------------------------------------------------------------- */ static int init_seg_endpoints ( v2s_opts_t * sopt, v2s_param_t * p, range_3dmm * R, int node ) { SUMA_surface * sp; ENTRY("init_seg_endpoints"); /* get node from first surface */ sp = p->surf; R->p1.xyz[0] = sp->ixyz[node].x; R->p1.xyz[1] = sp->ixyz[node].y; R->p1.xyz[2] = sp->ixyz[node].z; /* set pn based on other parameters */ if ( sopt->use_norms ) directed_dist(R->pn.xyz, R->p1.xyz, sp->norm[node].xyz, sopt->norm_len); else if ( p->nsurf > 1 ) { /* get node from second surface */ sp = p->surf + 1; R->pn.xyz[0] = sp->ixyz[node].x; R->pn.xyz[1] = sp->ixyz[node].y; R->pn.xyz[2] = sp->ixyz[node].z; } else R->pn = R->p1; R->p1 = THD_dicomm_to_3dmm(R->dset, R->p1); R->pn = THD_dicomm_to_3dmm(R->dset, R->pn); RETURN(0); } /*---------------------------------------------------------------------- * init_range_structs *---------------------------------------------------------------------- */ static int init_range_structs( range_3dmm * r3, range_3dmm_res * res3 ) { ENTRY("init_range_structs"); r3->dset = NULL; r3->debug = 0; res3->ims.num = 0; res3->ims.nall = 0; res3->ims.imarr = NULL; res3->i3arr = NULL; RETURN(0); } /*---------------------------------------------------------------------- * set_3dmm_bounds - note 3dmm bounding values * * This is an outer bounding box, like FOV, not SLAB. *---------------------------------------------------------------------- */ int set_3dmm_bounds ( THD_3dim_dataset *dset, THD_fvec3 *min, THD_fvec3 *max) { float tmp; int c; ENTRY("set_3dmm_bounds"); if ( !dset || !min || !max ) { fprintf(stderr, "** invalid params to set_3dmm_bounds: (%p,%p,%p)\n", dset, min, max ); RETURN(-1); } /* get undirected bounds */ min->xyz[0] = DSET_XORG(dset) - 0.5 * DSET_DX(dset); max->xyz[0] = min->xyz[0] + DSET_NX(dset) * DSET_DX(dset); min->xyz[1] = DSET_YORG(dset) - 0.5 * DSET_DY(dset); max->xyz[1] = min->xyz[1] + DSET_NY(dset) * DSET_DY(dset); min->xyz[2] = DSET_ZORG(dset) - 0.5 * DSET_DZ(dset); max->xyz[2] = min->xyz[2] + DSET_NZ(dset) * DSET_DZ(dset); for ( c = 0; c < 3; c++ ) if ( min->xyz[c] > max->xyz[c] ) { tmp = min->xyz[c]; min->xyz[c] = max->xyz[c]; max->xyz[c] = tmp; } RETURN(0); } /*---------------------------------------------------------------------- * alloc_output_mem - for output surface dataset *---------------------------------------------------------------------- */ static v2s_results * alloc_output_mem(v2s_opts_t *sopt, v2s_param_t *p) { v2s_results * sd; int rv = 0, mem, nnodes; ENTRY("allocate_output_mem"); /* if last <= 0, it will be set to nodes-1 */ if ( sopt->first_node > sopt->last_node && sopt->last_node > 0 ) { fprintf(stderr,"** error : first_node (%d) > last_node (%d)\n", sopt->first_node, sopt->last_node); RETURN(NULL); } nnodes = p->surf[0].num_ixyz; /* just for typing ease */ sd = calloc(1, sizeof(v2s_results)); if ( ! sd ) { fprintf(stderr,"** aom: failed to allocate v2s_results struct\n"); RETURN(NULL); } /* explicitly initialize all pointers with NULL */ sd->nodes = sd->volind = sd->i = sd->j = sd->k = sd->nvals = NULL; sd->vals = NULL; /* rcr - eventually, this may not apply */ if ( sopt->first_node < 0 ) sopt->first_node = 0; if ( sopt->first_node >= nnodes ) sopt->first_node = nnodes-1; if ( sopt->last_node <= 0 ) sopt->last_node = nnodes-1; if ( sopt->last_node >= nnodes ) sopt->last_node = nnodes-1; sd->nalloc = sopt->last_node - sopt->first_node + 1; sd->nused = 0; /* decide the maximum number of entries per row */ if ( p->over_steps ) sd->max_vals = sopt->f_steps; else if ( sopt->gp_index >= 0 ) sd->max_vals = 1; else sd->max_vals = DSET_NVALS(p->gpar); if ( sopt->skip_cols & V2S_SKIP_VALS ) sd->max_vals = 1; if ( p->over_steps && (sopt->skip_cols & V2S_SKIP_VALS) ) { fprintf(stderr,"** if SKIP_VALS, cannot compute results over steps\n"); free(sd); RETURN(NULL); } sd->memory = 0; /* first, compute the memory needed for one row */ mem = 0; if ( !(sopt->skip_cols & V2S_SKIP_NODES ) ) mem += sizeof(int); if ( !(sopt->skip_cols & V2S_SKIP_VOLIND) ) mem += sizeof(int); if ( !(sopt->skip_cols & V2S_SKIP_I ) ) mem += sizeof(int); if ( !(sopt->skip_cols & V2S_SKIP_J ) ) mem += sizeof(int); if ( !(sopt->skip_cols & V2S_SKIP_K ) ) mem += sizeof(int); if ( !(sopt->skip_cols & V2S_SKIP_NVALS ) ) mem += sizeof(int); /* now note the actual output values */ if ( !(sopt->skip_cols & V2S_SKIP_VALS ) ) mem += sizeof(float); else mem += sd->max_vals * sizeof(float); mem *= sd->nalloc; /* and multiply by the height of each column */ sd->memory = mem; if ( sopt->debug > 1 ) fprintf(stderr,"+d alloc result mem: %d bytes, max_vals = %d\n", mem, sd->max_vals); /* okay, this time let's allocate something... */ if ( ! (sopt->skip_cols & V2S_SKIP_NODES) ) rv = alloc_ints(&sd->nodes, sd->nalloc, "nodes", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_VOLIND) ) rv = alloc_ints(&sd->volind, sd->nalloc, "volind", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_I) ) rv = alloc_ints(&sd->i, sd->nalloc, "i", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_J) ) rv = alloc_ints(&sd->j, sd->nalloc, "j", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_K) ) rv = alloc_ints(&sd->k, sd->nalloc, "k", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_NVALS) ) rv = alloc_ints(&sd->nvals, sd->nalloc, "nvals", sopt->debug); if ( ! rv && ! (sopt->skip_cols & V2S_SKIP_VALS) ) rv = alloc_vals_list(&sd->vals, sd->nalloc, sd->max_vals, sopt->debug); else if ( ! rv ) rv = alloc_vals_list(&sd->vals, sd->nalloc, 1, sopt->debug); if ( rv ) { fprintf(stderr,"** failed v2s_results allocation\n"); free_v2s_results(sd); sd = NULL; } RETURN(sd); } /*---------------------------------------------------------------------- * alloc_vals_list - allocate 2D array for surface data values *---------------------------------------------------------------------- */ static int alloc_vals_list(float *** ptr, int length, int width, int debug) { int c; ENTRY("alloc_vals_list"); *ptr = (float **)malloc(width * sizeof(float *)); if ( !*ptr ) fprintf(stderr,"** avl: failed to alloc %d floats pointers\n", width); for ( c = 0; c < width; c++ ) { (*ptr)[c] = (float *)malloc(length * sizeof(float)); if ( (*ptr)[c] == NULL ) fprintf(stderr,"** avl: failed to alloc %d floats (# %d of %d)\n", length, c, width); } if ( debug > 1 ) fprintf(stderr,"-d alloc'd %d x %d floats for surf data\n", width, length); RETURN(0); } /*---------------------------------------------------------------------- * realloc_vals_list - reallocate 2D arrays for surface data values *---------------------------------------------------------------------- */ static int realloc_vals_list(float ** ptr, int length, int width, int debug) { int c, count; ENTRY("realloc_vals_list"); count = 0; for ( c = 0; c < width; c++ ) { if ( ptr[c] ) { ptr[c] = (float *)realloc(ptr[c], length * sizeof(float)); if ( ptr[c] == NULL ) fprintf(stderr,"** rvl: fail to realloc %d floats (%d of %d)\n", length, c, width); count++; } } if ( debug > 1 ) fprintf(stderr,"-d realloc'd %d x %d floats for surf data\n", count, length); RETURN(0); } /*---------------------------------------------------------------------- * alloc_ints - allocate 1D array of ints *---------------------------------------------------------------------- */ static int alloc_ints( int ** ptr, int length, char * dstr, int debug ) { ENTRY("alloc_ints"); *ptr = (int *)malloc(length * sizeof(int)); if ( ! *ptr ) { fprintf(stderr,"** ai: failed to alloc %d ints for '%s'\n",length,dstr); RETURN(1); } if ( debug > 1 ) fprintf(stderr,"-d ai: alloc'd %d ints for '%s'\n", length, dstr); RETURN(0); } /*---------------------------------------------------------------------- * realloc_ints - reallocate 1D array of ints (could replace alloc_ints) *---------------------------------------------------------------------- */ static int realloc_ints( int ** ptr, int length, char * dstr, int debug ) { ENTRY("realloc_ints"); *ptr = (int *)realloc(*ptr, length * sizeof(int)); if ( ! *ptr ) { fprintf(stderr,"** ri: failed to alloc %d ints for '%s'\n",length,dstr); RETURN(1); } if ( debug > 1 ) fprintf(stderr,"-d ri: alloc'd %d ints for '%s'\n", length, dstr); RETURN(0); } /*---------------------------------------------------------------------- * disp_v2s_param_t - display the contents of the v2s_param_t struct *---------------------------------------------------------------------- */ int disp_v2s_param_t ( char * info, v2s_param_t * p ) { ENTRY("disp_v2s_param_t"); if ( info ) fputs( info, stderr ); if ( p == NULL ) { fprintf(stderr, "disp_v2s_param_t: p == NULL\n" ); RETURN(-1); } fprintf(stderr, "v2s_param_t struct at %p :\n" " gpar : vcheck = %p : %s\n" " cmask = %p\n" " nvox, over_steps = %d, %d\n" " nsurf = %d\n" , p, p->gpar, ISVALID_DSET(p->gpar) ? "valid" : "invalid", p->cmask, p->nvox, p->over_steps, p->nsurf ); RETURN(0); } /*---------------------------------------------------------------------- * disp_v2s_opts_t - display the contents of the v2s_opts_t struct *---------------------------------------------------------------------- */ int disp_v2s_opts_t ( char * info, v2s_opts_t * sopt ) { ENTRY("disp_v2s_opts_t"); if ( info ) fputs( info, stderr ); if ( sopt == NULL ) { fprintf(stderr, "disp_v2s_opts_t: sopt == NULL\n"); RETURN(-1); } fprintf(stderr, "v2s_opts_t struct at %p :\n" " map, gp_index = %d, %d\n" " debug, dnode = %d, %d\n" " no_head, skip_cols = %d, %d\n" " first_node, last_node = %d, %d\n" " use_norms, norm_len = %d, %f\n" " norm_dir = %d\n" " f_index, f_steps = %d, %d\n" " f_p1_fr, f_pn_fr = %f, %f\n" " f_p1_mm, f_pn_mm = %f, %f\n" " outfile_1D = %s\n" " outfile_niml = %s\n" , sopt, sopt->map, sopt->gp_index, sopt->debug, sopt->dnode, sopt->no_head, sopt->skip_cols, sopt->first_node, sopt->last_node, sopt->use_norms, sopt->norm_len, sopt->norm_dir, sopt->f_index, sopt->f_steps, sopt->f_p1_fr, sopt->f_pn_fr, sopt->f_p1_mm, sopt->f_pn_mm, CHECK_NULL_STR(sopt->outfile_1D), CHECK_NULL_STR(sopt->outfile_niml) ); RETURN(0); } /*---------------------------------------------------------------------- * magnitude_f - return magnitude of float vector *---------------------------------------------------------------------- */ static double magnitude_f( float * p, int length ) { int c; double sums = 0.0; for ( c = 0; c < length; c++, p++ ) sums += (*p) * (*p); return(sqrt(sums)); } /*---------------------------------------------------------------------- * dist_f3mm - return Euclidean distance between the points *---------------------------------------------------------------------- */ static float dist_f3mm( THD_fvec3 * p1, THD_fvec3 * p2 ) { double d0, d1, d2; if ( p1 == NULL || p2 == NULL ) { fprintf( stderr, "** dist_f3mm: invalid params (%p,%p)\n", p1, p2 ); return(0.0); } d0 = p1->xyz[0] - p2->xyz[0]; d1 = p1->xyz[1] - p2->xyz[1]; d2 = p1->xyz[2] - p2->xyz[2]; return(sqrt(d0*d0 + d1*d1 + d2*d2)); } /*--------------------------------------------------------------------------- * disp_range_3dmm - display the contents of the range_3dmm struct *--------------------------------------------------------------------------- */ static int disp_range_3dmm ( char * info, range_3dmm * dp ) { ENTRY("disp_range_3dmm"); if ( info ) fputs( info, stderr ); if ( dp == NULL ) { fprintf(stderr, "disp_range_3dmm: dp == NULL\n"); RETURN(-1); } fprintf(stderr, "range_3dmm struct at %p :\n" " dset = %p : %s\n" " p1 = (%f, %f, %f)\n" " pn = (%f, %f, %f)\n" " dset_min = (%f, %f, %f)\n" " dset_max = (%f, %f, %f)\n" " oob_check, debug = (%d, %d)\n", dp, dp->dset, ISVALID_DSET(dp->dset) ? "valid" : "invalid", dp->p1.xyz[0], dp->p1.xyz[1], dp->p1.xyz[2], dp->pn.xyz[0], dp->pn.xyz[1], dp->pn.xyz[2], dp->dset_min.xyz[0], dp->dset_min.xyz[1], dp->dset_min.xyz[2], dp->dset_max.xyz[0], dp->dset_max.xyz[1], dp->dset_max.xyz[2], dp->oob_check, dp->debug ); RETURN(0); } /*--------------------------------------------------------------------------- * disp_range_3dmm_res - display the contents of the range_3dmm_res struct *--------------------------------------------------------------------------- */ static int disp_range_3dmm_res ( char * info, range_3dmm_res * dp ) { ENTRY("disp_range_3dmm_res"); if ( info ) fputs( info, stderr ); if ( dp == NULL ) { fprintf(stderr, "disp_range_3dmm: dp == NULL\n"); RETURN(-1); } fprintf(stderr, "range_3dmm_res struct at %p :\n" " ims.imarr = %p\n" " ims.num, ims.nall = %d, %d\n" " repeats, masked = %d, %d\n" " oob, ifirst = %d, %d\n" " i3first[0,1,2] = %d, %d, %d\n" " i3arr = %p\n" , dp, dp->ims.imarr, dp->ims.num, dp->ims.nall, dp->repeats, dp->masked, dp->oob, dp->ifirst, dp->i3first.ijk[0], dp->i3first.ijk[1], dp->i3first.ijk[2], dp->i3arr ); if ( dp->i3arr ) fprintf(stderr, " i3arr[0].ijk = %d, %d, %d\n", dp->i3arr[0].ijk[0], dp->i3arr[0].ijk[1], dp->i3arr[0].ijk[2] ); RETURN(0); } /*--------------------------------------------------------------------------- * disp_mri_imarr - display the contents of the MRI_IMARR struct *--------------------------------------------------------------------------- */ int disp_mri_imarr ( char * info, MRI_IMARR * dp ) { float * fp; int cr, cc; ENTRY("disp_mri_imarr"); if ( info ) fputs( info, stderr ); if ( dp == NULL ) { fprintf(stderr, "disp_mri_imarr: dp == NULL\n"); RETURN(-1); } fprintf(stderr, "mri_imarr struct at %p :\n" " num, nall = %d, %d\n", dp, dp->num, dp->nall ); for ( cr = 0; cr < dp->num; cr++ ) { fp = MRI_FLOAT_PTR(dp->imarr[cr]); fprintf(stderr, " %3d: ", cr); for ( cc = 0; cc < dp->imarr[cr]->nx; cc++, fp++ ) fprintf(stderr, "%f ", *fp ); fputc( '\n', stderr ); } RETURN(0); } /*********************************************************************** * disp_surf_vals *********************************************************************** */ static int disp_surf_vals( char * mesg, v2s_results * sd, int node ) { int index, c; ENTRY("disp_surf_vals"); fprintf(stderr, "-------------------------------------------------\n"); if ( mesg ) fputs( mesg, stderr ); if ( sd->nused < 1 ) { fprintf(stderr,"** no surf nodes defined\n"); RETURN(-1); } index = (node >= 0) ? node : sd->nused - 1; fprintf(stderr, "v2s_results vals for sd_index %d, node %d :\n" " volind, (i, j, k) = %d, (%d, %d, %d)\n" " nvals: values... = %d: ", index, sd->nodes ? sd->nodes[index] : 0, sd->volind ? sd->volind[index] : 0, sd->i ? sd->i[index] : 0, sd->j ? sd->j[index] : 0, sd->k ? sd->k[index] : 0, sd->nvals ? sd->nvals[index] : 0 ); for ( c = 0; c < sd->max_vals; c++ ) fprintf(stderr,"%s ", MV_format_fval(sd->vals[c][index])); fputc('\n', stderr); RETURN(0); } /*********************************************************************** * disp_v2s_results *********************************************************************** */ int disp_v2s_results( char * mesg, v2s_results * d ) { ENTRY("disp_v2s_results"); if ( mesg ) fputs( mesg, stderr ); fprintf(stderr, "v2s_results @ %p\n" " nalloc, nused = %d, %d\n" " max_vals, memory = %d, %d\n" " nodes, volind = %p, %p\n" " i, j, k = %p, %p, %p\n" " nvals, vals = %p, %p\n", d, d->nalloc, d->nused, d->max_vals, d->memory, d->nodes, d->volind, d->i, d->j, d->k, d->nvals, d->vals); RETURN(0); } /*********************************************************************** * disp_v2s_plugin_opts *********************************************************************** */ int disp_v2s_plugin_opts( char * mesg, v2s_plugin_opts * d ) { ENTRY("disp_v2s_plugin_opts"); if ( mesg ) fputs( mesg, stderr ); fprintf(stderr, "v2s_plugin_opts @ %p\n" " ready = %d\n" " use0, use1 = %d, %d\n" " s0A, s0B = %d, %d\n" " s1A, s1B = %d, %d\n", d, d->ready, d->use0, d->use1, d->s0A, d->s0B, d->s1A, d->s1B ); RETURN(0); } /*---------------------------------------------------------------------- * free_v2s_results - free contents of v2s_results struct *---------------------------------------------------------------------- */ int free_v2s_results( v2s_results * sd ) { int c; ENTRY("free_v2s_results"); if( ! sd ) RETURN(0); if (sd->nodes) { free(sd->nodes); sd->nodes = NULL; } if (sd->volind) { free(sd->volind); sd->volind = NULL; } if (sd->i) { free(sd->i); sd->i = NULL; } if (sd->j) { free(sd->j); sd->j = NULL; } if (sd->k) { free(sd->k); sd->k = NULL; } if (sd->nvals) { free(sd->nvals); sd->nvals = NULL; } if (sd->vals) { for ( c = 0; c < sd->max_vals; c++ ) if ( sd->vals[c] ) { free(sd->vals[c]); sd->vals[c] = NULL; } free(sd->vals); sd->vals = NULL; } free(sd); RETURN(0); } /*---------------------------------------------------------------------- * validate structure contents *---------------------------------------------------------------------- */ static int validate_v2s_inputs ( v2s_opts_t * sopt, v2s_param_t * p ) { int c; ENTRY("validate_v2s_inputs"); if ( !sopt || !p ) { fprintf(stderr,"** validate_v2s_inputs: bad params (%p,%p)\n", sopt, p); RETURN(-1); } /* validate surface options structure */ if ( sopt->map <= E_SMAP_INVALID || sopt->map >= E_SMAP_FINAL ) { fprintf(stderr,"** invalid mapping index %d\n", sopt->map); RETURN(1); } else if ( v2s_map_type(gv2s_map_names[sopt->map]) != sopt->map ) { fprintf(stderr,"** mapping index failure for %d\n", sopt->map); RETURN(1); } if ( sopt->f_steps <= 0 || sopt->f_steps >= V2S_STEPS_TOOOOO_BIG ) { fprintf(stderr,"** invalid f_steps = %d\n", sopt->f_steps); RETURN(1); } /* validate the contents of p, proper */ if ( !p->gpar ) {fprintf(stderr,"** vv2si: no dset?\n"); RETURN(2);} if ( p->nvox != DSET_NVOX(p->gpar) ) { fprintf(stderr,"** invalid voxel count (%d) for grid_parent\n",p->nvox); RETURN(2); } if ( sopt->gp_index >= DSET_NVALS(p->gpar) ) { fprintf(stderr,"** gp_index (%d) > max grid_parent index (%d)\n", sopt->gp_index, DSET_NVALS(p->gpar) - 1); RETURN(2); } if ( p->nsurf < 1 || p->nsurf > 2 ) /* see V2S_MAX_SURFS */ { fprintf(stderr,"** invalid: nsurf = %d must be in [%d,%d]\n", p->nsurf, 1, 2); RETURN(2); } /* validate individual SUMA_surface structs */ for ( c = 0; c < p->nsurf; c++ ) if ( check_SUMA_surface(p->surf + c) ) RETURN(3); /* now look for consistency */ if ( p->nsurf > 1 ) { if ( p->surf[0].num_ixyz != p->surf[1].num_ixyz ) { fprintf(stderr,"** invalid surfaces, different # nodes (%d,%d)\n", p->surf[0].num_ixyz, p->surf[1].num_ixyz); RETURN(4); } } else if ( sopt->use_norms && !p->surf[0].norm ) { fprintf(stderr,"** missing surface normals for surface '%s'\n", CHECK_EMPTY_STR(p->surf[0].label)); RETURN(4); } RETURN(0); } /* return 0 if valid, > 0 otherwise */ static int check_SUMA_surface( SUMA_surface * s ) { int rv = 0; ENTRY("is_valid_SUMA_surface"); if ( !s ) { fprintf(stderr,"** ivSs: no surface\n"); RETURN(0); } if ( s->type != SUMA_SURFACE_TYPE ) { fprintf(stderr,"** surf '%s': invalid type %d\n", CHECK_EMPTY_STR(s->label), s->type); rv++; } if ( s->num_ixyz < 0 || s->nall_ixyz < 0 || s->num_ixyz < s->nall_ixyz ) { fprintf(stderr,"** surf '%s': invalid num_ixyz = %d, nall_ixyz = %d\n", CHECK_EMPTY_STR(s->label), s->num_ixyz, s->nall_ixyz); rv++; } if ( s->seq == 0 || s->sorted == 0 || s->seqbase != 0 ) { fprintf(stderr,"** surf '%s': invalid seq, sort or base (%d, %d, %d)\n", CHECK_EMPTY_STR(s->label), s->seq, s->sorted, s->seqbase); rv++; } if ( !s->ixyz ) { fprintf(stderr,"** surf '%s': invalid, missing nodes\n", CHECK_EMPTY_STR(s->label)); rv++; } RETURN(rv); } /*--------------------------------------------------------------------------- * v2s_vals_over_steps - return whether a function is displayed over steps * * Most function results are output per sub-brick. These functions will * have results displayed over the segment steps. *--------------------------------------------------------------------------- */ int v2s_vals_over_steps( int map ) { if ( map == E_SMAP_SEG_VALS ) return 1; return 0; } /*---------------------------------------------------------------------- * v2s_map_type - return an E_SMAP_XXX code * * on failure, return -1 (E_SMAP_INVALID) * else return >0 (a valid map code) *---------------------------------------------------------------------- */ int v2s_map_type ( char * map_str ) { v2s_map_nums map; ENTRY("v2s_map_type"); if ( map_str == NULL ) { fprintf( stderr, "** v2s_map_type: missing map_str parameter\n" ); RETURN((int)E_SMAP_INVALID); } if ( sizeof(gv2s_map_names) / sizeof(char *) != (int)E_SMAP_FINAL ) { fprintf( stderr, "** error: gv2s_map_names/v2s_map_num mis-match\n"); RETURN((int)E_SMAP_INVALID); } /* not ready for E_SMAP_COUNT yet (until someone wants it) */ if ( !strcmp( map_str, gv2s_map_names[E_SMAP_COUNT] ) ) RETURN((int)E_SMAP_INVALID); for ( map = E_SMAP_INVALID; map < E_SMAP_FINAL; map++ ) if ( !strcmp( map_str, gv2s_map_names[map] ) ) RETURN((int)map); RETURN((int)E_SMAP_INVALID); } /*---------------------------------------------------------------------- * thd_mask_from_brick - create a mask from a sub-brick and threshold * * return a pointer to a new mask, or NULL on failure *---------------------------------------------------------------------- */ int thd_mask_from_brick(THD_3dim_dataset * dset, int volume, float thresh, byte ** mask, int absolute) { float factor; byte * tmask; int nvox, type, c, size = 0; ENTRY("thd_mask_from_brick"); if ( mask ) *mask = NULL; /* to be sure */ if ( !ISVALID_DSET(dset) || ! mask || volume < 0 ) RETURN(-1); if ( volume >= DSET_NVALS(dset) ) { fprintf(stderr,"** tmfb: sub-brick %d out-of-range\n", volume); RETURN(-1); } nvox = DSET_NVOX(dset); type = DSET_BRICK_TYPE(dset, volume); if ( type != MRI_byte && type != MRI_short && type != MRI_int && type != MRI_float ) { fprintf(stderr,"** tmfb: invalid dataset type %s, sorry...\n", MRI_type_name[type]); RETURN(-1); } tmask = (byte *)calloc(nvox, sizeof(byte)); if ( ! tmask ) { fprintf(stderr,"** tmfb: failed to allocate mask of %d bytes\n", nvox); RETURN(-1); } factor = DSET_BRICK_FACTOR(dset, volume); /* cheat: adjust threshold, not data */ if ( factor != 0.0 ) thresh /= factor; switch( DSET_BRICK_TYPE(dset, volume) ) { case MRI_byte: { byte * dp = DSET_ARRAY(dset, volume); byte thr = BYTEIZE(thresh + 0.99999); /* ceiling */ for ( c = 0; c < nvox; c++ ) if ( dp[c] != 0 && ( dp[c] >= thr ) ) { size++; tmask[c] = 1; } } break; case MRI_short: { short * dp = DSET_ARRAY(dset, volume); short thr = SHORTIZE(thresh + 0.99999); /* ceiling */ for ( c = 0; c < nvox; c++, dp++ ) if ( *dp != 0 && ( *dp >= thr || (absolute && *dp <= -thr) ) ) { size++; tmask[c] = 1; } } break; case MRI_int: { int * dp = DSET_ARRAY(dset, volume); int thr = (int)(thresh + 0.99999); /* ceiling */ for ( c = 0; c < nvox; c++, dp++ ) if ( *dp != 0 && ( *dp >= thr || (absolute && *dp <= -thr) ) ) { size++; tmask[c] = 1; } } break; case MRI_float: { float * dp = DSET_ARRAY(dset, volume); for ( c = 0; c < nvox; c++, dp++ ) if (*dp != 0 && (*dp >= thresh || (absolute && *dp <= -thresh))) { size++; tmask[c] = 1; } } break; default: /* let's be sure */ { fprintf(stderr,"** tmfb: invalid dataset type, sorry...\n"); free(tmask); } break; } *mask = tmask; RETURN(size); } /*---------------------------------------------------------------------- * check for a map index that we consider valid * * from anywhere, E_SMAP_MASK2 and E_SMAP_COUNT are not yet implemented * from afni, E_SMAP_SEG_VALS is not acceptable (only allow 1 output) *---------------------------------------------------------------------- */ int v2s_is_good_map( int map, int from_afni ) { ENTRY("v2s_good_map_index"); if ( map <= E_SMAP_INVALID || map >= E_SMAP_FINAL ) RETURN(0); /* these have not (yet? do we care?) been implemented */ if ( map == E_SMAP_MASK2 || map == E_SMAP_COUNT ) RETURN(0); if ( from_afni && map == E_SMAP_SEG_VALS ) RETURN(0); RETURN(1); } /*---------------------------------------------------------------------- * check for a map index that we consider valid * * from anywhere, E_SMAP_MASK2 and E_SMAP_COUNT are not yet implemented * from afni, E_SMAP_SEG_VALS is not acceptable (only allow 1 output) *---------------------------------------------------------------------- */ static int fill_sopt_default(v2s_opts_t * sopt, int nsurf ) { ENTRY("fill_sopt_default"); if ( !sopt || nsurf < 1 || nsurf > 2 ) { fprintf(stderr,"** fill_sopt_default: bad params (%p,%d)\n",sopt,nsurf); RETURN(1); } /* first set any zeros */ memset(sopt, 0, sizeof(*sopt)); if ( nsurf == 2 ) sopt->map = E_SMAP_MIDPT; else sopt->map = E_SMAP_MASK; sopt->gp_index = -1; sopt->no_head = 1; sopt->skip_cols = V2S_SKIP_ALL ^ V2S_SKIP_NODES; /* nodes and 1 val */ sopt->f_steps = 1; sopt->outfile_1D = NULL; sopt->outfile_niml = NULL; RETURN(0); } /*---------------------------------------------------------------------- * v2s_write_outfile_1D - write results to 1D file *---------------------------------------------------------------------- */ int v2s_write_outfile_1D ( v2s_opts_t * sopt, v2s_results * sd, char * label ) { FILE * fp; int c, c2; ENTRY("v2s_write_outfile_1D"); fp = fopen( sopt->outfile_1D, "w" ); if ( fp == NULL ) { fprintf( stderr, "** failure to open '%s' for writing\n", sopt->outfile_1D ); RETURN(-1); } if ( ! sopt->no_head ) print_header(fp, label, gv2s_map_names[sopt->map], sd); for ( c = 0; c < sd->nused; c++ ) { /* keep old spacing */ fputc(' ', fp); if ( sd->nodes ) fprintf(fp, " %8d", sd->nodes[c]); if ( sd->volind ) fprintf(fp, " %8d ", sd->volind[c]); if ( sd->i ) fprintf(fp, " %3d", sd->i[c]); if ( sd->j ) fprintf(fp, " %3d", sd->j[c]); if ( sd->k ) fprintf(fp, " %3d", sd->k[c]); if ( sd->nvals ) fprintf(fp, " %3d", sd->nvals[c]); for ( c2 = 0; c2 < sd->max_vals; c2++ ) fprintf(fp, " %10s", MV_format_fval(sd->vals[c2][c])); fputc('\n', fp); } fclose(fp); RETURN(0); } /*---------------------------------------------------------------------- * v2s_write_outfile_niml - write results to niml file * - free data pointers as we go *---------------------------------------------------------------------- */ int v2s_write_outfile_niml ( v2s_opts_t * sopt, v2s_results * sd, int free_vals){ static char v2s_name[] = "3dVol2Surf_dataset"; NI_element * nel = NULL; NI_stream ns; char * ni_name; int c; ENTRY("v2s_write_outfile_niml"); if ( !sopt->outfile_niml ) RETURN(0); nel = NI_new_data_element( v2s_name, sd->nused ); if ( !nel ) { fprintf(stderr,"** file NI_new_data_element, n = '%s', len = %d\n", v2s_name, sd->nused); RETURN(1); } ni_name = (char *)calloc(strlen(sopt->outfile_niml)+6, sizeof(char)); if ( !ni_name ) { fprintf(stderr,"** ni_name failed\n"); RETURN(1); } sprintf(ni_name, "file:%s", sopt->outfile_niml); ns = NI_stream_open(ni_name, "w"); NI_add_column(nel,NI_INT,sd->nodes); for ( c = 0; c < sd->max_vals; c++ ) { NI_add_column(nel, NI_FLOAT, sd->vals[c]); if ( free_vals ) { free(sd->vals[c]); sd->vals[c] = NULL; } } if ( free_vals ) { free(sd->vals); sd->vals = NULL; } if ( NI_write_element(ns, nel, NI_BINARY_MODE) < 0 ) { fprintf(stderr,"** NI_write_element failed for: '%s'\n", ni_name); RETURN(1); } NI_free_element( nel ); NI_stream_close( ns ); free(ni_name); RETURN(0); } /*---------------------------------------------------------------------- * print_header - dump standard header for node output - v2.4 *---------------------------------------------------------------------- */ static int print_header(FILE * outfp, char * surf, char * map, v2s_results * sd) { int val; ENTRY("print_header"); fprintf( outfp, "# --------------------------------------------------\n" ); fprintf( outfp, "# surface '%s', '%s' :\n", surf, map ); fprintf( outfp, "#\n" ); /* keep old style, but don't presume all columns get used (v 6.0) : * fprintf( outfp, "# node 1dindex i j k vals" ); * fprintf( outfp, "# ------ ------- --- --- --- ----" ); */ /* output column headers */ fputc( '#', outfp ); /* still comment line */ if ( sd->nodes ) fprintf(outfp, " node "); if ( sd->volind ) fprintf(outfp, " 1dindex "); if ( sd->i ) fprintf(outfp, " i "); if ( sd->j ) fprintf(outfp, " j "); if ( sd->k ) fprintf(outfp, " k "); if ( sd->nvals ) fprintf(outfp, " vals"); for ( val = 0; val < sd->max_vals; val++ ) fprintf( outfp, " v%-2d ", val ); fputc( '\n', outfp ); fputc( '#', outfp ); /* underline the column headers */ if ( sd->nodes ) fprintf(outfp, " ------"); if ( sd->volind ) fprintf(outfp, " ------- "); if ( sd->i ) fprintf(outfp, " ---"); if ( sd->j ) fprintf(outfp, " ---"); if ( sd->k ) fprintf(outfp, " ---"); if ( sd->nvals ) fprintf(outfp, " ----"); fputs( " ", outfp ); for ( val = 0; val < sd->max_vals; val++ ) fprintf( outfp, " -------- " ); fputc( '\n', outfp ); RETURN(0); }