/***************************************************************************** Major portions of this software are copyrighted by the Medical College of Wisconsin, 1994-2000, and are released under the Gnu General Public License, Version 2. See the file README.Copyright for details. ******************************************************************************/ #include "mrilib.h" #include "uthash.h" typedef struct { int id; /* keep it named 'id' to facilitate use of convenience macros in uthash . */ UT_hash_handle hh; /* keep name for same reason */ int index; } INT_HASH_DATUM; /*---------------------------------------------------------------------*/ /*! Make a byte mask from mask dataset: miv = sub-brick of input if( mask_bot <= mask_top ) then only nonzero values in this range will be used else all nonzero values in the mask will be used The input dataset should be byte-, short-, or float-valued. The output is a byte array with 1s in "hit" locations and 0s in other locations. The number of bytes is DSET_NVOX(mask_dset). This array should be free()-d someday. If NULL is returned, some grotesque error transpired. -----------------------------------------------------------------------*/ byte * THD_makemask( THD_3dim_dataset *mask_dset , int miv , float mask_bot , float mask_top ) { float maxval ; /* for computing limits for an empty mask */ byte *mmm = NULL ; int nvox , ii ; int empty = 0 ; /* do we return an empty mask */ if( !ISVALID_DSET(mask_dset) || miv < 0 || miv >= DSET_NVALS(mask_dset) ) return NULL ; nvox = DSET_NVOX(mask_dset) ; DSET_load(mask_dset) ; if( !DSET_LOADED(mask_dset) ) return NULL ; mmm = (byte *) calloc( sizeof(byte) * nvox , 1 ) ; switch( DSET_BRICK_TYPE(mask_dset,miv) ){ default: WARNING_message("makemask: bad brick type %d", DSET_BRICK_TYPE(mask_dset,miv)); free(mmm) ; DSET_unload(mask_dset) ; return NULL ; case MRI_short:{ short mbot , mtop ; short *mar = (short *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top ){ /* maybe this mask is empty, allow for rounding */ maxval = MRI_TYPE_maxval[MRI_short] + 0.5 ; if( mask_bot/mfac >= maxval || mask_top/mfac <= -maxval ) empty=1; mbot = SHORTIZE(mask_bot/mfac) ; mtop = SHORTIZE(mask_top/mfac) ; } else { mbot = (short) -MRI_TYPE_maxval[MRI_short] ; mtop = (short) MRI_TYPE_maxval[MRI_short] ; } if( !empty ) /* 6 Jun 2007 */ for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) mmm[ii]=1; } break ; case MRI_byte:{ byte mbot , mtop ; byte *mar = (byte *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top && mask_top > 0.0 ){ /* maybe this mask is empty, allow for rounding */ /* (top <= 0 is flag for full mask) */ maxval = MRI_TYPE_maxval[MRI_byte] + 0.5 ; if( mask_bot/mfac >= maxval ) empty = 1; mbot = BYTEIZE(mask_bot/mfac) ; mtop = BYTEIZE(mask_top/mfac) ; } else { mbot = 0 ; // [PT: Dec 22, 2020] Thanks for the fix here, C Rorden! mtop = (byte) MRI_TYPE_maxval[MRI_byte] ; } if( !empty ) /* 6 Jun 2007 */ for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) mmm[ii]=1; } break ; case MRI_float:{ float mbot , mtop ; float *mar = (float *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top ){ mbot = (float) (mask_bot/mfac) ; mtop = (float) (mask_top/mfac) ; } else { mbot = -WAY_BIG ; mtop = WAY_BIG ; } for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) mmm[ii]=1; } break ; } return mmm ; } /*----------------------------------------------------------------------------*/ /*! Remove isolated voxels from a byte mask [21 May 2009 - RWCox]. */ int THD_mask_remove_isolas( int nx, int ny, int nz , byte *mmm ) { int ii,jj,kk , ip,im , jp,jm , kp,km , qq,nxy , niso=0 ; if( nx < 1 || ny < 1 || nz < 1 || mmm == NULL ) return 0 ; nxy = nx*ny ; for( qq=kk=0 ; kk < nz ; kk++ ){ /* qq = voxel index in 1D array */ km = kk-1 ; kp = kk+1 ; for( jj=0 ; jj < ny ; jj++ ){ jm = jj-1 ; jp = jj+1 ; for( ii=0 ; ii < nx ; ii++,qq++ ){ if( !mmm[qq] ) continue ; /* already 0 */ im = ii-1 ; ip = ii+1 ; if( im >= 0 && mmm[qq-1] ) continue ; /* -x nbhr */ if( ip < nx && mmm[qq+1] ) continue ; /* +x */ if( jm >= 0 && mmm[qq-nx] ) continue ; /* -y */ if( jp < ny && mmm[qq+nx] ) continue ; /* +y */ if( km >= 0 && mmm[qq-nxy] ) continue ; /* -z */ if( kp < nz && mmm[qq+nxy] ) continue ; /* +z */ mmm[qq] = 0 ; niso++ ; }}} return niso ; } /*----------------------------------------------------------------------------*/ /*! Similar to THD_makemask except that it turns the dset itself to mask values returns (-1) if it fails, number of non-zero voxels if OK */ int THD_makedsetmask( THD_3dim_dataset *mask_dset , int miv , float mask_bot , float mask_top, byte *cmask ) { float maxval ; /* for computing limits for an empty mask */ int nvox , ii, nonzero=-1 , empty = 0 ; if( !ISVALID_DSET(mask_dset) || miv < 0 || miv >= DSET_NVALS(mask_dset) ) return (-1) ; nvox = DSET_NVOX(mask_dset) ; DSET_mallocize(mask_dset); /* do this or else it could be a read only dset! */ DSET_load(mask_dset) ; if( !DSET_LOADED(mask_dset) ) return (-1) ; nonzero = 0; switch( DSET_BRICK_TYPE(mask_dset,miv) ){ default: DSET_unload(mask_dset) ; return (-1) ; case MRI_short:{ short mbot , mtop ; short *mar = (short *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top ){ /* maybe this mask is empty, allow for rounding */ maxval = MRI_TYPE_maxval[MRI_short] + 0.5 ; if( mask_bot/mfac >= maxval || mask_top/mfac <= -maxval ) empty=1; mbot = SHORTIZE(mask_bot/mfac) ; mtop = SHORTIZE(mask_top/mfac) ; } else { mbot = (short) -MRI_TYPE_maxval[MRI_short] ; mtop = (short) MRI_TYPE_maxval[MRI_short] ; } if (empty) { /* if empty, clear result 6 Jun 2007 */ for( ii=0 ; ii < nvox ; ii++ ) mar[ii] = 0; } else if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 && cmask[ii]) { mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } else { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) { mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } } break ; case MRI_byte:{ byte mbot , mtop ; byte *mar = (byte *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top && mask_top > 0.0 ){ /* maybe this mask is empty, allow for rounding */ /* (top <= 0 is flag for full mask) */ maxval = MRI_TYPE_maxval[MRI_byte] + 0.5 ; if( mask_bot/mfac >= maxval ) empty = 1; mbot = BYTEIZE(mask_bot/mfac) ; mtop = BYTEIZE(mask_top/mfac) ; } else { mbot = 0 ; // [PT: Dec 22, 2020] Thanks for the fix here, C Rorden! mtop = (byte) MRI_TYPE_maxval[MRI_byte] ; } if (empty) { /* if empty, clear result 6 Jun 2007 */ for( ii=0 ; ii < nvox ; ii++ ) mar[ii] = 0; } else if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 && cmask[ii]){ mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } else { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) { mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } } break ; case MRI_float:{ float mbot , mtop ; float *mar = (float *) DSET_ARRAY(mask_dset,miv) ; float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ; if( mfac == 0.0 ) mfac = 1.0 ; if( mask_bot <= mask_top ){ mbot = (float) (mask_bot/mfac) ; mtop = (float) (mask_top/mfac) ; } else { mbot = -WAY_BIG ; mtop = WAY_BIG ; } if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 && cmask[ii]) { mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } else { for( ii=0 ; ii < nvox ; ii++ ) if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ) { mar[ii]=1; ++nonzero; } else { mar[ii] = 0; } } } break ; } /* remove any scaling factor ZSS April 24 06*/ EDIT_BRICK_FACTOR(mask_dset,miv , 0.0); return (nonzero) ; } /*---------------------------------------------------------------------------*/ /*! Convert an entire dataset of MRI_byte, and all values to binary (set or not). return 1 on failure, 0 on success 3 Jun, 2014 [rickr] */ int THD_dset_to_mask(THD_3dim_dataset * dset, float mask_bot, float mask_top) { byte * bvol = NULL; int ivol; ENTRY("THD_dset_to_mask"); if( !ISVALID_DSET(dset) ) { ERROR_message("dset_to_mask: dset not valid"); RETURN(1); } DSET_mallocize(dset); DSET_load(dset) ; if( !DSET_LOADED(dset) ) { ERROR_message("dset_to_mask: dset not loaded"); RETURN(1); } for( ivol = 0; ivol < DSET_NVALS(dset); ivol++ ) { bvol = THD_makemask(dset, ivol, mask_bot, mask_top); if( !bvol ) { ERROR_message("dset_to_mask: failed to mask vol %d", ivol); RETURN(1); } EDIT_substitute_brick(dset, ivol, MRI_byte, bvol); EDIT_BRICK_FACTOR(dset, ivol, 0.0); } RETURN(0); } /* Zero out voxels vv in dset where cmask[vv]=0 Returns the number of voxels edited in dset (across all sub-bricks) -1 if dset was null */ int THD_applydsetmask( THD_3dim_dataset *dset , byte *cmask ) { int ss, ii, jj, kk, vv, nedited = -1 ; ENTRY("THD_applydsetmask"); if (!dset) RETURN(nedited); if (!cmask) RETURN(0); DSET_mallocize(dset); DSET_load(dset); for (ss=0; ss= DSET_NVALS(dset) ) { fprintf(stderr,"** Bad dset or sub-brick index.\n"); return (0) ; } if( !DSET_LOADED(dset) ) DSET_load(dset); switch( DSET_BRICK_TYPE(dset,isb) ){ case MRI_short: case MRI_byte: if (check_values) { mfac = DSET_BRICK_FACTOR(dset,isb) ; if (mfac != 0.0f && mfac != 1.0f) return(0); } break; case MRI_double: case MRI_complex: case MRI_float: vv = (void *)DSET_ARRAY(dset,isb); mfac = DSET_BRICK_FACTOR(dset,isb) ; if (mfac != 0.0f && mfac != 1.0f) return(0); if (!vv) { fprintf(stderr,"** NULL array!\n"); return(0); } return(is_integral_data(DSET_NVOX(dset), DSET_BRICK_TYPE(dset,isb), DSET_ARRAY(dset,isb) ) ); break; default: return(0); } return(1); } int is_integral_dset ( THD_3dim_dataset *dset, int check_values) { int i=0; if( !ISVALID_DSET(dset) ) return(0); for (i=0; i= DSET_NVALS(mask_dset) ) { fprintf(stderr,"** Bad mask_dset or sub-brick index.\n"); return (unq) ; } nvox = DSET_NVOX(mask_dset) ; DSET_load(mask_dset) ; if( !DSET_LOADED(mask_dset) ) return (unq) ; if (!is_integral_sub_brick (mask_dset, miv, 0)) { fprintf(stderr,"** Sub-brick %d of %s is not of an integral data type.\n", miv, DSET_PREFIX(mask_dset) ? DSET_PREFIX(mask_dset):"NULL"); return (unq) ; } vals = (int *)malloc(sizeof(int)*nvox); if (!vals) { fprintf(stderr,"** Failed to allocate.\n"); return (unq) ; } switch( DSET_BRICK_TYPE(mask_dset,miv) ){ default: fprintf(stderr,"** Bad dset type for unique operation.\n" "Only integral valued dsets are allowed.\n"); DSET_unload(mask_dset) ; if (vals) free(vals); return (unq) ; case MRI_short:{ short *mar = (short *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; case MRI_byte:{ byte *mar = (byte *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; case MRI_float:{ float *mar = (float *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; } /* unique */ unq = UniqueInt (vals, nvox, n_unique, 0 ); free(vals); vals = NULL; return (unq) ; } /*----------------------------------------------------------------------------*/ /* returns an nvox int array which represents the rank of the voxel value in mask_dset */ int *THD_unique_rank( THD_3dim_dataset *mask_dset , int miv, byte *cmask, char *mapname, int **unqp, int *N_unq) { int nvox , ii, *unq = NULL, *vals=NULL, imax=0; INT_HASH_DATUM *rmap=NULL, *hd=NULL; int n_unique, r; FILE *fout=NULL; n_unique = 0; unq = NULL ; if (unqp && *unqp!=NULL) { fprintf(stderr,"** unqp (%p) not initialized properly to NULL", *unqp); return (vals) ; } if( !ISVALID_DSET(mask_dset) || miv < 0 || miv >= DSET_NVALS(mask_dset) ) { fprintf(stderr,"** Bad mask_dset or sub-brick index.\n"); return (vals) ; } nvox = DSET_NVOX(mask_dset) ; DSET_load(mask_dset) ; if( !DSET_LOADED(mask_dset) ) return (vals) ; if (!is_integral_sub_brick (mask_dset, miv, 0)) { fprintf(stderr,"** Sub-brick %d of %s is not integral valued.\n", miv, DSET_PREFIX(mask_dset) ? DSET_PREFIX(mask_dset):"NULL"); return (vals) ; } vals = (int *)malloc(sizeof(int)*nvox); if (!vals) { fprintf(stderr,"** Failed to allocate.\n"); return (vals) ; } switch( DSET_BRICK_TYPE(mask_dset,miv) ){ default: fprintf( stderr, "** Bad dset type for unique operation.\n" "Only Byte, Short and float dsets are allowed.\n"); DSET_unload(mask_dset) ; if (vals) free(vals); vals = NULL; return (vals) ; case MRI_short:{ short *mar = (short *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; case MRI_byte:{ byte *mar = (byte *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; case MRI_float:{ /* not an integral type but we store ints (from NIFTI) as floats */ float *mar = (float *) DSET_ARRAY(mask_dset,miv) ; if (cmask) { for( ii=0 ; ii < nvox ; ii++ ) if (cmask[ii]) vals[ii] = (int)(mar[ii]); else vals[ii] = 0; } else { for( ii=0 ; ii < nvox ; ii++ ) vals[ii] = (int)(mar[ii]); } } break ; } /* unique */ unq = UniqueInt (vals, nvox, &n_unique, 0 ); /* fprintf(stderr,"-- Have %d unique values\n", n_unique); */ if (!unq) { fprintf(stderr,"** Failed to create unique list\n"); free(vals); return (NULL); } if (mapname && mapname[0]) { /*fprintf(stderr,"-- Writing mapping to >>%s<<\n", mapname);*/ if ((fout = fopen(mapname,"w"))) { fprintf(fout, "#Rank Map (%d unique values)\n", n_unique); fprintf(fout, "#Col. 0: Rank\n"); fprintf(fout, "#Col. 1: Input Dset Value\n"); } } /* now replace by rank */ #if 0 for (r=0; rid = unq[r]; hd->index = r; HASH_ADD_INT(rmap, id, hd); } for (ii=0; iiindex; else { fprintf(stderr, "** Failed to find key %d inhash table\n", vals[ii]); free(vals); while (rmap) { hd=rmap; HASH_DEL(rmap,hd); free(hd); } return (NULL); } } /* destroy hash */ while (rmap) { hd=rmap; HASH_DEL(rmap,hd); if (hd) free(hd); } #endif if (!unqp) free(unq); else *unqp = unq; unq = NULL; if (N_unq) *N_unq = n_unique; if (fout) fclose(fout); fout = NULL; return (vals) ; } /*----------------------------------------------------------------------------*/ /* Same as THD_unique_rank but replaces values in mask_dset with rank */ int THD_unique_rank_edit( THD_3dim_dataset *mask_dset , int miv, byte *cmask, char *mapname, int **unqp, int *N_unq) { int *vals=NULL, nvox, mxval, ii; if (!(vals = THD_unique_rank(mask_dset, miv, cmask, mapname, unqp, N_unq))) { fprintf(stderr,"** Failed to uniquate\n"); return (0); } mxval = -1; nvox = DSET_NVOX(mask_dset) ; for( ii=0 ; ii < nvox ; ii++ ) { if (vals[ii] > mxval) mxval = vals[ii]; } /* fprintf (stderr,"-- Have maxval of %d\n", mxval); */ switch( DSET_BRICK_TYPE(mask_dset,miv) ){ default: fprintf(stderr,"** Bad dset type for unique operation.\n" "Should have been stopped a while ago.\n"); if (vals) free(vals); vals = NULL; return (0) ; case MRI_short:{ short *mar = (short *) DSET_ARRAY(mask_dset,miv) ; if (mxval > MRI_TYPE_maxval[MRI_short]) { fprintf(stderr, "** Have too many unique values (%d) for " "datatype short (limit %f)!\n", mxval, MRI_TYPE_maxval[MRI_short]); if (vals) free(vals); vals = NULL; return (0) ; } EDIT_BRICK_FACTOR(mask_dset,miv,0.0); for( ii=0 ; ii < nvox ; ii++ ) mar[ii] = (short)(vals[ii]); } break ; case MRI_byte:{ byte *mar = (byte *) DSET_ARRAY(mask_dset,miv) ; if (mxval > MRI_TYPE_maxval[MRI_byte]) { fprintf(stderr, "** Have too many unique values (%d) for " "datatype byte (limit %f)!\n", mxval, MRI_TYPE_maxval[MRI_byte]); if (vals) free(vals); vals = NULL; return (0) ; } EDIT_BRICK_FACTOR(mask_dset,miv,0.0); for( ii=0 ; ii < nvox ; ii++ ) mar[ii] = (byte)(vals[ii]); } break ; case MRI_float:{ float *mar = (float *) DSET_ARRAY(mask_dset,miv) ; EDIT_BRICK_FACTOR(mask_dset,miv,0.0); for( ii=0 ; ii < nvox ; ii++ ) mar[ii] = (float)(vals[ii]); } break ; } return (1); } /*---------------------------------------------------------------------*/ /*! Count the number of nonzero voxels in a mask. -----------------------------------------------------------------------*/ int THD_countmask( int nvox , byte *mmm ) { int ii,mc ; if( nvox <= 0 || mmm == NULL ) return 0 ; for( ii=mc=0 ; ii < nvox ; ii++ ) if( mmm[ii] ) mc++ ; return mc ; } /*---------------------------------------------------------------------*/ int THD_parse_boxball( int *boxball_num , float **boxball_dat , char **argv ) { int bnum , narg=0 ; float *bdat ; if( boxball_num == NULL || boxball_dat == NULL || argv == NULL ) return 0 ; bnum = *boxball_num ; if( bnum < 0 ) bnum = 0 ; bdat = *boxball_dat ; if( strcmp(argv[narg]+2,"box") == 0 ){ float xbot,xtop , ybot,ytop , zbot,ztop , btyp ; int nn ; char code = *(argv[narg]+1) ; /* should be 'x', 'd' , 'n', or 'i' */ switch( code ){ case 'x': btyp = BOX_XYZ ; break ; case 'd': btyp = BOX_DIC ; break ; case 'n': btyp = BOX_NEU ; break ; case 'i': btyp = BOX_IJK ; break ; default: WARNING_message("Unknown 'box' option %s\n",argv[narg]) ; return 0 ; } nn = sscanf( argv[narg+1] , "%f:%f" , &xbot , &xtop ) ; if( nn < 1 ){ WARNING_message("Can't decode %s after %s\n",argv[narg+1],argv[narg]); return 0 ; } else if( nn == 1 ) xtop=xbot ; nn = sscanf( argv[narg+2] , "%f:%f" , &ybot , &ytop ) ; if( nn < 1 ){ WARNING_message("Can't decode %s after %s\n",argv[narg+2],argv[narg]); return 0 ; } else if( nn == 1 ) ytop=ybot ; nn = sscanf( argv[narg+3] , "%f:%f" , &zbot , &ztop ) ; if( nn < 1 ){ WARNING_message("Can't decode %s after %s\n",argv[narg+3],argv[narg]); return 0 ; } else if( nn == 1 ) ztop=zbot ; bdat = (float *) realloc( bdat , sizeof(float)*BOXLEN*(bnum+1) ) ; bdat[0+BOXLEN*bnum] = btyp ; bdat[1+BOXLEN*bnum] = xbot ; bdat[2+BOXLEN*bnum] = xtop ; bdat[3+BOXLEN*bnum] = ybot ; bdat[4+BOXLEN*bnum] = ytop ; bdat[5+BOXLEN*bnum] = zbot ; bdat[6+BOXLEN*bnum] = ztop ; bnum++ ; narg = 4 ; } else if( strcmp(argv[narg]+2,"ball") == 0 ){ float xcen,ycen,zcen,rad , btyp ; char code = *(argv[narg]+1) ; /* should be 'x', 'd' , or 'n' */ switch( code ){ case 'x': btyp = BALL_XYZ ; break ; case 'd': btyp = BALL_DIC ; break ; case 'n': btyp = BALL_NEU ; break ; default: WARNING_message("Unknown 'ball' option %s",argv[narg]) ; return 0 ; } xcen = strtod( argv[narg+1] , NULL ) ; ycen = strtod( argv[narg+2] , NULL ) ; zcen = strtod( argv[narg+3] , NULL ) ; rad = strtod( argv[narg+4] , NULL ) ; if( rad <= 0.0f ){ WARNING_message("%s radius=%s !?",argv[narg],argv[narg+4]) ; rad = 0.0f; } bdat = (float *) realloc( bdat , sizeof(float)*BOXLEN*(bnum+1) ) ; bdat[0+BOXLEN*bnum] = btyp ; bdat[1+BOXLEN*bnum] = xcen ; bdat[2+BOXLEN*bnum] = ycen ; bdat[3+BOXLEN*bnum] = zcen ; bdat[4+BOXLEN*bnum] = rad ; bnum++ ; narg = 5 ; } *boxball_num = bnum ; *boxball_dat = bdat ; return narg ; } /*----------------------------------------------------------------------------*/ byte * THD_boxballmask( THD_3dim_dataset *dset , int boxball_num , float *boxball_dat ) { int nx,ny,nz , nxy,nxyz , ii,jj,kk ; byte *bmask ; int bb, ibot,itop, jbot,jtop, kbot,ktop , btyp ; float xbot,xtop, ybot,ytop, zbot,ztop ; float xcen,ycen,zcen , icen,jcen,kcen ; float xmin,xmax , ymin,ymax , zmin,zmax , rad,dist , xx,yy,zz ; THD_fvec3 dv,xv ; ENTRY("THD_boxballmask") ; if( !ISVALID_DSET(dset) || boxball_num <= 0 || boxball_dat == NULL ) RETURN(NULL) ; xmin=dset->daxes->xxmin ; xmax=dset->daxes->xxmax ; ymin=dset->daxes->yymin ; ymax=dset->daxes->yymax ; zmin=dset->daxes->zzmin ; zmax=dset->daxes->zzmax ; nx = DSET_NX(dset) ; ny = DSET_NY(dset) ; nxy = nx*ny ; nz = DSET_NZ(dset) ; nxyz = nxy*nz ; bmask = (byte *)calloc(sizeof(byte),nxyz) ; for( bb=0 ; bb < boxball_num ; bb++ ){ btyp = boxball_dat[0+BOXLEN*bb] ; if( btyp < BALL_XYZ ){ /*---- box ----*/ xbot = boxball_dat[1+BOXLEN*bb]; xtop = boxball_dat[2+BOXLEN*bb]; ybot = boxball_dat[3+BOXLEN*bb]; ytop = boxball_dat[4+BOXLEN*bb]; zbot = boxball_dat[5+BOXLEN*bb]; ztop = boxball_dat[6+BOXLEN*bb]; if( btyp != BOX_IJK ){ /* convert coords to indexes */ if( btyp == BOX_NEU ){ /* coords from Neuroscience to DICOM */ xbot = -xbot; xtop = -xtop; ybot = -ybot; ytop = -ytop; btyp = BOX_DIC; } if( btyp == BOX_DIC ){ /* coords from DICOM to dataset */ LOAD_FVEC3(dv,xbot,ybot,zbot) ; xv = THD_dicomm_to_3dmm( dset , dv ) ; UNLOAD_FVEC3(xv,xbot,ybot,zbot) ; LOAD_FVEC3(dv,xtop,ytop,ztop) ; xv = THD_dicomm_to_3dmm( dset , dv ) ; UNLOAD_FVEC3(xv,xtop,ytop,ztop) ; } if( xbot < xmin && xtop < xmin ) continue ; /* skip box if outside dataset */ if( xbot > xmax && xtop > xmax ) continue ; if( ybot < ymin && ytop < ymin ) continue ; if( ybot > ymax && ytop > ymax ) continue ; if( zbot < zmin && ztop < zmin ) continue ; if( zbot > zmax && ztop > zmax ) continue ; LOAD_FVEC3(dv,xbot,ybot,zbot) ; xv = THD_3dmm_to_3dfind( dset , dv ) ; /* coords from dataset to index */ UNLOAD_FVEC3(xv,xbot,ybot,zbot) ; LOAD_FVEC3(dv,xtop,ytop,ztop) ; xv = THD_3dmm_to_3dfind( dset , dv ) ; UNLOAD_FVEC3(xv,xtop,ytop,ztop) ; } ibot = rint(xbot) ; jbot = rint(ybot) ; kbot = rint(zbot) ; /* round */ itop = rint(xtop) ; jtop = rint(ytop) ; ktop = rint(ztop) ; if( ibot > itop ){ btyp = ibot; ibot = itop; itop = btyp; } /* flip? */ if( jbot > jtop ){ btyp = jbot; jbot = jtop; jtop = btyp; } if( kbot > ktop ){ btyp = kbot; kbot = ktop; ktop = btyp; } /* skip box if outside dataset */ if ( itop < 0 || ibot >= nx ) continue; if ( jtop < 0 || jbot >= ny ) continue; if ( ktop < 0 || kbot >= nz ) continue; /* constrain values to dataset dimensions */ if ( ibot < 0 ) ibot = 0; if ( itop >= nx ) itop = nx-1; if ( jbot < 0 ) jbot = 0; if ( jtop >= ny ) jtop = ny-1; if ( kbot < 0 ) kbot = 0; if ( ktop >= nz ) ktop = nz-1; for( kk=kbot ; kk <= ktop ; kk++ ) for( jj=jbot ; jj <= jtop ; jj++ ) for( ii=ibot ; ii <= itop ; ii++ ) bmask[ii+jj*nx+kk*nxy] = 1 ; } else { /*---- ball ----*/ xcen = boxball_dat[1+BOXLEN*bb] ; ycen = boxball_dat[2+BOXLEN*bb] ; zcen = boxball_dat[3+BOXLEN*bb] ; rad = boxball_dat[4+BOXLEN*bb] ; /* convert center coords to dataset indexes */ if( btyp == BALL_NEU ){ /* coords from Neuroscience to DICOM */ xcen = -xcen; ycen = -ycen; btyp = BALL_DIC; } if( btyp == BALL_DIC ){ /* coords from DICOM to dataset */ LOAD_FVEC3(dv,xcen,ycen,zcen) ; xv = THD_dicomm_to_3dmm( dset , dv ) ; UNLOAD_FVEC3(xv,xcen,ycen,zcen) ; } if( xcen < xmin || xcen > xmax ) continue ; /* skip ball if outside */ if( ycen < ymin || ycen > ymax ) continue ; if( zcen < zmin || zcen > zmax ) continue ; LOAD_FVEC3(dv,xcen,ycen,zcen) ; xv = THD_3dmm_to_3dfind( dset , dv ) ; /* coords from dataset to index */ UNLOAD_FVEC3(xv,icen,jcen,kcen) ; ibot = rint(icen-rad) ; itop = rint(icen+rad) ; /* box around ball */ jbot = rint(jcen-rad) ; jtop = rint(jcen+rad) ; kbot = rint(kcen-rad) ; ktop = rint(kcen+rad) ; rad = rad*rad ; for( kk=kbot ; kk <= ktop ; kk++ ){ for( jj=jbot ; jj <= jtop ; jj++ ){ for( ii=ibot ; ii <= itop ; ii++ ){ LOAD_FVEC3( dv , ii,jj,kk ) ; /* convert to xyz coords */ xv = THD_3dfind_to_3dmm( dset , dv ) ; /* then test distance^2 */ UNLOAD_FVEC3( xv , xx,yy,zz ) ; /* xyz of ball center. */ dist = SQR(xx-xcen) + SQR(yy-ycen) + SQR(zz-zcen) ; if( dist <= rad ) bmask[ii+jj*nx+kk*nxy] = 1 ; }}} } } /*----- end of loop over box/ball list -----*/ RETURN(bmask) ; } /*------- functions moved from vol2surf.c ------------- 13 Nov 2006 [rickr] */ /*---------------------------------------------------------------------- * thd_mask_from_brick - create a mask from a sub-brick and threshold * * return the number of set voxels in the mask *---------------------------------------------------------------------- */ 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); } if( !DSET_LOADED(dset) ) DSET_load(dset); 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: { if (thresh <= (float)MRI_maxbyte) { /* ZSS: Oct 2011 Without this test, a high threshold value might end up equal to MRI_maxbyte when BYTEIZED below, resulting in the highest voxel making it to the mask no matter how much higher the threshold is set. */ 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: { if (thresh <= (float)MRI_maxshort) { /* ZSS: Oct 2011 */ 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); } /*---------------------------------------------------------------------- * thd_multi_mask_from_brick - create a valued mask from a sub-brick * * return 0 on success, else failure 10 Nov 2006 [rickr] *---------------------------------------------------------------------- */ int thd_multi_mask_from_brick(THD_3dim_dataset * dset, int volume, byte ** mask) { float factor; byte * tmask; int nvox, type, c; ENTRY("thd_multi_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,"** tmmfb: sub-brick %d out-of-range\n", volume); RETURN(-1); } if( !DSET_LOADED(dset) ) DSET_load(dset); 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,"** tmmfb: invalid dataset type %s, sorry...\n", MRI_type_name[type]); RETURN(-1); } tmask = (byte *)calloc(nvox, sizeof(byte)); if ( ! tmask ) { fprintf(stderr,"** tmmfb: failed to allocate mask of %d bytes\n", nvox); RETURN(-1); } factor = DSET_BRICK_FACTOR(dset, volume); if( factor == 1.0 ) factor = 0.0; switch( DSET_BRICK_TYPE(dset, volume) ) { case MRI_byte: { byte * dp = DSET_ARRAY(dset, volume); if( factor ) for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)(int)rint(dp[c]*factor); else for ( c = 0; c < nvox; c++ ) tmask[c] = dp[c]; } break; case MRI_short: { short * dp = DSET_ARRAY(dset, volume); if( factor ) for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)(int)rint(dp[c]*factor); else for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)dp[c]; } break; case MRI_int: { int * dp = DSET_ARRAY(dset, volume); if( factor ) for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)(int)rint(dp[c]*factor); else for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)dp[c]; } break; case MRI_float: { float * dp = DSET_ARRAY(dset, volume); if( factor ) for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)(int)rint(dp[c]*factor); else for ( c = 0; c < nvox; c++ ) tmask[c] = (byte)dp[c]; } break; default: /* let's be sure */ { fprintf(stderr,"** tmmfb: invalid dataset type, sorry...\n"); free(tmask); } break; } *mask = tmask; RETURN(0); } /**************************************************************************** ** The functions below are for converting a byte-valued 0/1 mask to/from ** ** an ASCII representation. The ASCII representation is formed like so: ** ** 1. convert it to binary = 8 bits stored in each byte, rather than 1 ** ** - this takes the mask from nvox bytes to 1+(nvox-1)/8 bytes ** ** - this operation is done in function mask_binarize() [below] ** ** 2. compress the binarized array with zlib ** ** - this step is done in function array_to_zzb64(), which uses ** ** function zz_compress_all() [in zfun.c] ** ** 3. express the compressed array into Base64 notation (in ASCII) ** ** - this step is also done in function array_to_zzb64(), which ** ** uses function B64_to_base64() [in niml/niml_b64.c] ** ** 4. attach at the end a string to indicate the number of voxels ** ** in the mask. ** ** + The above steps are done in function mask_to_b64string() [below]. ** ** + The inverse is done in function mask_from_b64string() [below]. ** ** + Function mask_b64string_nvox() [below] can be used to get the voxel ** ** count from the end of the string, which can be used to check if a ** ** mask is compatible with a given dataset for which it is intended. ** **************************************************************************** * See program 3dMaskToASCII.c for sample usage of these functions. * *****************************************************************************/ /*-------------------------------------------------------------------------*/ /*! Convert a byte-value 0/1 mask to an ASCII string in Base64. */ char * mask_to_b64string( int nvox , byte *mful ) { byte *mbin ; char *str ; int nstr ; if( nvox < 1 || mful == NULL ) return NULL ; /* bad inputs */ mbin = mask_binarize( nvox , mful ) ; str = array_to_zzb64( 1+(nvox-1)/8 , (char *)mbin , 72 ) ; free(mbin) ; if( str == NULL ) return NULL ; /* should never happen */ nstr = strlen(str) ; str = (char *)realloc( str , sizeof(char)*(nstr+16) ) ; sprintf( str+nstr-1 , "===%d" , nvox ) ; /* -1 to erase last linefeed */ return str ; } /*-------------------------------------------------------------------------*/ /*! Convert an ASCII string in Base64 to a byte-valued 0/1 mask. */ byte * mask_from_b64string( char *str , int *nvox ) { byte *mful , *mbin=NULL ; int nvvv , nstr , ii,ibot , nbin ; if( str == NULL || nvox == NULL ) return NULL ; /* bad inputs */ nvvv = mask_b64string_nvox(str) ; if( nvvv <= 0 ) return NULL ; /* WTF? */ /* decode string to binarized array */ nbin = zzb64_to_array( str , (char **)&mbin ) ; if( nbin <= 0 || mbin == NULL ) return NULL ; /* bad decode */ /* decode binarized array to byte mask */ mful = mask_unbinarize( nvvv , mbin ) ; free(mbin) ; *nvox = nvvv ; return mful ; } /*-------------------------------------------------------------------------*/ int mask_b64string_nvox( char *str ) { int nstr , ii , ibot ; if( str == NULL ) return 0 ; nstr = strlen(str) ; if( nstr < 7 ) return 0 ; /* too short */ /* find the last '=' at the end of the string */ ibot = nstr-16 ; if( ibot < 3 ) ibot = 3 ; for( ii=nstr-1 ; ii > ibot && str[ii] != '=' ; ii-- ) ; /*nada*/ if( str[ii] != '=' ) return 0 ; /* badly formatted */ ibot = (int)strtod(str+ii+1,NULL) ; /* number of voxels */ return ibot ; } /*-------------------------------------------------------------------------*/ /* Input: 0/1 array of bytes, nvox long. Output: compressed by a factor of 8: 1+(nvox-1)/8 bytes long. *//*-----------------------------------------------------------------------*/ static byte binar[8] = { 1 , 1<<1 , 1<<2 , 1<<3 , 1<<4 , 1<<5 , 1<<6 , 1<<7 } ; byte * mask_binarize( int nvox , byte *mful ) { register byte *mbin ; register int ii ; if( nvox < 1 || mful == NULL ) return NULL ; mbin = (byte *)calloc(sizeof(byte),1+(nvox-1)/8) ; for( ii=0 ; ii < nvox ; ii++ ) if( mful[ii] != 0 ) mbin[ii>>3] |= binar[ii&0x7] ; return mbin ; } /*-------------------------------------------------------------------------*/ byte * mask_unbinarize( int nvox , byte *mbin ) { register byte *mful ; register int ii ; if( nvox < 1 || mbin == NULL ) return NULL ; mful = (byte *)calloc(sizeof(byte),nvox) ; for( ii=0 ; ii < nvox ; ii++ ) mful[ii] = ( mbin[ii>>3] & binar[ii&0x7] ) != 0 ; return mful ; } /*===========================================================================*/ /*! Create a binary byte-valued mask from an input string: - a dataset filename - a Base64 mask string - filename with data containing a Base64 mask string - future editions? *//*-------------------------------------------------------------------------*/ bytevec * THD_create_mask_from_string( char *str ) /* Jul 2010 */ { bytevec *bvec=NULL ; int nstr ; char *buf=NULL ; int ferr=0; ENTRY("THD_create_mask") ; if( str == NULL || *str == '\0' ) RETURN(NULL) ; nstr = strlen(str) ; bvec = (bytevec *)malloc(sizeof(bytevec)) ; /* try to read it as a dataset */ if( nstr < THD_MAX_NAME ){ THD_3dim_dataset *dset = THD_open_one_dataset(str) ; if( dset != NULL ){ bvec->nar = DSET_NVOX(dset) ; bvec->ar = THD_makemask( dset , 0 , 1.0f,0.0f ) ; DSET_delete(dset) ; if( bvec->ar == NULL ){ ERROR_message("Can't make mask from dataset '%s'",str) ; free(bvec) ; bvec = NULL ; } RETURN(bvec) ; } ferr = 1; /* string is short, but failed to open as dataset */ } /* if str is a filename, read that file; otherwise, use the string itself to find the mask */ if( THD_is_file(str) ){ buf = AFNI_suck_file(str) ; if( buf != NULL ) nstr = strlen(buf) ; } else { buf = str ; } /* try to read buf as a Base64 mask string */ if( strrchr(buf,'=') != NULL ){ int nvox ; bvec->ar = mask_from_b64string( buf , &nvox ) ; if( bvec->ar != NULL ){ bvec->nar = nvox ; } else { /* might be a non-existent file 14 Jan 2014 [rickr] */ if( ferr ) ERROR_message("Failed to open mask from '%s'", str); else ERROR_message("Can't make mask from string '%.16s' %s", buf,(nstr<=16)?" ":"...") ; free(bvec) ; bvec = NULL ; } } else { if( ferr ) ERROR_message("Failed to open mask '%s'", str); else ERROR_message("Don't understand mask string '%.16s'", buf,(nstr<=16)?" ":"...") ; free(bvec) ; bvec = NULL ; } if( buf != str && buf != NULL ) free(buf) ; RETURN(bvec) ; }