int main( int argc , char *argv[] ) { THD_3dim_dataset *inset=NULL , *outset ; THD_3dim_dataset **insar=NULL ; int nsar=0 ; int iarg=1 , ii,kk , ids ; MCW_cluster *nbhd=NULL ; char *prefix="./localhistog" ; int ntype=0 ; float na=0.0f,nb=0.0f,nc=0.0f ; int verb=1 , do_prob=0 ; int nx=0,ny=0,nz=0,nvox=0, rbot,rtop ; char *labfile=NULL ; NI_element *labnel=NULL ; int nlab=0 , *labval=NULL ; char **lablab=NULL ; char buf[THD_MAX_SBLABEL] ; UINT32 *ohist , *mhist=NULL ; char *ohist_name=NULL ; int ohzadd=0 ; int *rlist , numval ; float mincount=0.0f ; int mcc ; int *exlist=NULL, numex=0 ; int do_excNONLAB=0 ; /*---- for the clueless who wish to become clued-in ----*/ if( argc == 1 ){ usage_3dLocalHistog(1); exit(0); } /* Bob's help shortcut */ /*---- official startup ---*/ #if defined(USING_MCW_MALLOC) && !defined(USE_OMP) enable_mcw_malloc() ; #endif PRINT_VERSION("3dLocalHistog"); mainENTRY("3dLocalHistog main"); machdep(); AFNI_logger("3dLocalHistog",argc,argv); if( getpid()%2 ) AUTHOR("Bilbo Baggins"); else AUTHOR("Thorin Oakenshield"); AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ /*---- loop over options ----*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strcmp(argv[iarg],"-help") == 0 || strcmp(argv[iarg],"-h") == 0){ usage_3dLocalHistog(strlen(argv[iarg])>3 ? 2:1); exit(0); } if( strncmp(argv[iarg],"-qu",3) == 0 ){ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-verb",5) == 0 ){ verb++ ; iarg++ ; continue ; } #ifdef ALLOW_PROB if( strncmp(argv[iarg],"-prob",5) == 0 ){ do_prob = 1 ; iarg++ ; continue ; } #endif if( strcmp(argv[iarg],"-exclude") == 0 ){ int ebot=-6666666,etop=-6666666 , ee ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-exclude'") ; sscanf(argv[iarg],"%d..%d",&ebot,&etop) ; if( ebot >= -TWO15 && ebot <= TWO15 ){ if( etop < -TWO15 || etop > TWO15 || etop < ebot ) etop = ebot ; exlist = (int *)realloc(exlist,sizeof(int)*(etop-ebot+1+numex+1)) ; for( ee=ebot ; ee <= etop ; ee++ ){ if( ee != 0 ) exlist[numex++] = ee ; } } iarg++ ; continue ; } if( strcmp(argv[iarg],"-excNONLAB") == 0 ){ do_excNONLAB = 1 ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-prefix'") ; prefix = strdup(argv[iarg]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Bad -prefix!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-hsave") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-hsave'") ; ohist_name = strdup(argv[iarg]) ; if( !THD_filename_ok(ohist_name) ) ERROR_exit("Bad -hsave!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-mincount") == 0 ){ char *cpt ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-mincount'") ; mincount = (float)strtod(argv[iarg],&cpt) ; #if 0 if( mincount > 0.0f && mincount < 50.0f && *cpt == '%' ) /* percentage */ mincount = -0.01f*mincount ; #endif iarg++ ; continue ; } if( strcmp(argv[iarg],"-nbhd") == 0 ){ char *cpt ; if( ntype > 0 ) ERROR_exit("Can't have 2 '-nbhd' options") ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-nbhd'") ; cpt = argv[iarg] ; if( strncasecmp(cpt,"SPHERE",6) == 0 ){ sscanf( cpt+7 , "%f" , &na ) ; ntype = NTYPE_SPHERE ; } else if( strncasecmp(cpt,"RECT",4) == 0 ){ sscanf( cpt+5 , "%f,%f,%f" , &na,&nb,&nc ) ; if( na == 0.0f && nb == 0.0f && nc == 0.0f ) ERROR_exit("'RECT(0,0,0)' is not a legal neighborhood") ; ntype = NTYPE_RECT ; } else if( strncasecmp(cpt,"RHDD",4) == 0 ){ sscanf( cpt+5 , "%f" , &na ) ; if( na == 0.0f ) ERROR_exit("Can't have a RHDD of radius 0") ; ntype = NTYPE_RHDD ; } else if( strncasecmp(cpt,"TOHD",4) == 0 ){ sscanf( cpt+5 , "%f" , &na ) ; if( na == 0.0f ) ERROR_exit("Can't have a TOHD of radius 0") ; ntype = NTYPE_TOHD ; } else { ERROR_exit("Unknown -nbhd shape: '%s'",cpt) ; } iarg++ ; continue ; } if( strcmp(argv[iarg],"-lab_file") == 0 || strcmp(argv[iarg],"-labfile") == 0 ){ char **labnum ; int nbad=0 ; if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; if( labfile != NULL ) ERROR_exit("Can't use '%s' twice!",argv[iarg-1]) ; labfile = strdup(argv[iarg]) ; labnel = THD_string_table_read(labfile,0) ; if( labnel == NULL || labnel->vec_num < 2 ) ERROR_exit("Can't read label file '%s'",labfile) ; nlab = labnel->vec_len ; labnum = (char **)labnel->vec[0] ; lablab = (char **)labnel->vec[1] ; labval = (int *)calloc(sizeof(int),nlab) ; for( ii=0 ; ii < nlab ; ii++ ){ if( labnum[ii] != NULL ){ labval[ii] = (int)strtod(labnum[ii],NULL) ; if( labval[ii] < -TWO15 || labval[ii] > TWO15 ){ labval[ii] = 0; nbad++; } } } if( nbad > 0 ) ERROR_message("%d label values are outside the range %d..%d :-(" , nbad , -TWO15 , TWO15 ) ; iarg++ ; continue ; } ERROR_message("** 3dLocalHistog: Illegal option: '%s'",argv[iarg]) ; suggest_best_prog_option(argv[0], argv[iarg]); exit(1) ; } /*--- end of loop over options ---*/ /*---- check for stupid user inputs ----*/ if( iarg >= argc ) ERROR_exit("No datasets on command line?") ; if( ohist_name == NULL && strcmp(prefix,"NULL") == 0 ) ERROR_exit("-prefix NULL is only meaningful if you also use -hsave :-(") ; /*------------ scan input datasets, built overall histogram ------------*/ nsar = argc - iarg ; insar = (THD_3dim_dataset **)malloc(sizeof(THD_3dim_dataset *)*nsar) ; if( verb ) fprintf(stderr,"Scanning %d datasets ",nsar) ; ohist = (UINT32 *)calloc(sizeof(UINT32),TWO16) ; for( ids=iarg ; ids < argc ; ids++ ){ /* dataset loop */ insar[ids-iarg] = inset = THD_open_dataset(argv[ids]) ; CHECK_OPEN_ERROR(inset,argv[ids]) ; if( ids == iarg ){ nx = DSET_NX(inset); ny = DSET_NY(inset); nz = DSET_NZ(inset); nvox = nx*ny*nz; } else if( nx != DSET_NX(inset) || ny != DSET_NY(inset) || nz != DSET_NZ(inset) ){ ERROR_exit("Dataset %s grid doesn't match!",argv[ids]) ; } if( !THD_datum_constant(inset->dblk) ) ERROR_exit("Dataset %s doesn't have a fixed data type! :-(",argv[ids]) ; if( THD_need_brick_factor(inset) ) ERROR_exit("Dataset %s has scale factors! :-(",argv[ids]) ; if( DSET_BRICK_TYPE(inset,0) != MRI_byte && DSET_BRICK_TYPE(inset,0) != MRI_short && DSET_BRICK_TYPE(inset,0) != MRI_float ) ERROR_exit("Dataset %s is not byte- or short-valued! :-(",argv[ids]) ; DSET_load(inset) ; CHECK_LOAD_ERROR(inset) ; for( ii=0 ; ii < DSET_NVALS(inset) ; ii++ ){ /* add to overall histogram */ if( verb ) fprintf(stderr,".") ; switch( DSET_BRICK_TYPE(inset,ii) ){ case MRI_short:{ short *sar = (short *)DSET_BRICK_ARRAY(inset,ii) ; for( kk=0 ; kk < nvox ; kk++ ) ohist[ sar[kk]+TWO15 ]++ ; } break ; case MRI_byte:{ byte *bar = (byte *)DSET_BRICK_ARRAY(inset,ii) ; for( kk=0 ; kk < nvox ; kk++ ) ohist[ bar[kk]+TWO15 ]++ ; } break ; case MRI_float:{ float *far = (float *)DSET_BRICK_ARRAY(inset,ii) ; short ss ; for( kk=0 ; kk < nvox ; kk++ ){ ss = SHORTIZE(far[kk]); ohist[ss+TWO15]++; } } break ; } } /* end of sub-brick loop */ DSET_unload(inset) ; /* will re-load later, as needed */ } /* end of dataset loop */ if( verb ) fprintf(stderr,"\n") ; /*-------------- process overall histogram for fun and profit -------------*/ /* if we didn't actually find 0, put it in the histogram now */ if( ohist[0+TWO15] == 0 ){ ohist[0+TWO15] = 1 ; ohzadd = 1 ; } /* excNONLAB? */ if( nlab > 0 && do_excNONLAB ){ byte *klist = (byte *)calloc(sizeof(byte),TWO16) ; int nee ; for( ii=0 ; ii < nlab ; ii++ ){ if( labval[ii] != 0 ) klist[labval[ii]+TWO15] = 1 ; } for( nee=ii=0 ; ii < TWO16 ; ii++ ){ if( !klist[ii] ) nee++ ; } exlist = (int *)realloc(exlist,sizeof(int)*(numex+nee+1)) ; for( ii=0 ; ii < TWO16 ; ii++ ){ if( ii != TWO15 && !klist[ii] ) exlist[numex++] = ii-TWO15 ; } free(klist) ; } /* make a copy of ohist and edit it for mincount, etc */ mhist = (UINT32 *)malloc(sizeof(UINT32)*TWO16) ; memcpy(mhist,ohist,sizeof(UINT32)*TWO16) ; mcc = (mincount < 0.0f) ? (int)(-mincount*nvox) : (int)mincount ; if( mcc > 1 ){ for( ids=ii=0 ; ii < TWO16 ; ii++ ){ if( ii != TWO15 && mhist[ii] > 0 && mhist[ii] < mcc ){ mhist[ii] = 0; ids++; } } if( ids > 0 && verb ) INFO_message("Edited out %d values with overall histogram counts less than %d",ids,mcc) ; } if( numex > 0 ){ int ee ; for( ids=0,ii=0 ; ii < numex ; ii++ ){ ee = exlist[ii] ; if( mhist[ee+TWO15] > 0 ){ mhist[ee+TWO15] = 0; ids++; } } free(exlist) ; if( ids > 0 && verb ) INFO_message("Edited out %d values from the exclude list",ids) ; } /* count number of values with nonzero (edited) counts */ numval = 0 ; for( ii=0 ; ii < TWO16 ; ii++ ) if( mhist[ii] != 0 ) numval++ ; if( numval == 0 ) ERROR_exit("Nothing found! WTF?") ; /* should not happen */ /* make list of all values with nonzero (edited) count */ rlist = (int *)malloc(sizeof(int)*numval) ; if( verb > 1 ) fprintf(stderr,"++ Include list:") ; for( ii=kk=0 ; ii < TWO16 ; ii++ ){ if( mhist[ii] != 0 ){ rlist[kk++] = ii-TWO15 ; if( verb > 1 ) fprintf(stderr," %d[%u]",ii-TWO15,mhist[ii]) ; } } if( verb > 1 ) fprintf(stderr,"\n") ; rbot = rlist[0] ; rtop = rlist[numval-1] ; /* smallest and largest values found */ if( rbot == rtop ) ERROR_exit("Only one value (%d) found in all inputs!",rbot) ; /* if 0 isn't first in rlist, then put it in first place and move negative values up by one spot */ if( rbot < 0 ){ for( kk=0 ; kk < numval && rlist[kk] != 0 ; kk++ ) ; /*nada*/ if( kk < numval ){ /* should always be true */ for( ii=kk-1 ; ii >= 0 ; ii-- ) rlist[ii+1] = rlist[ii] ; rlist[0] = 0 ; } } if( verb ) INFO_message("Value range = %d..%d (%d distinct values)",rbot,rtop,numval ); /* save overall histogram? */ if( ohist_name != NULL ){ FILE *fp = fopen(ohist_name,"w") ; int nl=0 ; if( fp == NULL ) ERROR_exit("Can't open -hsave '%s' for output!",ohist_name) ; if( ohzadd ) ohist[0+TWO15] = 0 ; for( ii=0 ; ii < TWO16 ; ii++ ){ if( ohist[ii] != 0 ){ fprintf(fp,"%6d %u\n",ii-TWO15,ohist[ii]); nl++; } } fclose(fp) ; if( verb ) INFO_message("Wrote %d lines to -hsave file %s",nl,ohist_name) ; } free(ohist) ; free(mhist) ; mhist = ohist = NULL ; /* done with this */ if( strcmp(prefix,"NULL") == 0 ) exit(0) ; /* special case */ /*----------- build the neighborhood mask -----------*/ if( ntype <= 0 ){ /* default neighborhood */ ntype = NTYPE_SPHERE ; na = 0.0f ; if( verb ) INFO_message("Using default neighborhood = self") ; } switch( ntype ){ default: ERROR_exit("WTF? ntype=%d",ntype) ; /* should not happen */ case NTYPE_SPHERE:{ float dx , dy , dz ; if( na < 0.0f ){ dx = dy = dz = 1.0f ; na = -na ; } else { dx = fabsf(DSET_DX(insar[0])) ; dy = fabsf(DSET_DY(insar[0])) ; dz = fabsf(DSET_DZ(insar[0])) ; } nbhd = MCW_spheremask( dx,dy,dz , na ) ; } break ; case NTYPE_RECT:{ float dx , dy , dz ; if( na < 0.0f ){ dx = 1.0f; na = -na; } else dx = fabsf(DSET_DX(insar[0])); if( nb < 0.0f ){ dy = 1.0f; nb = -nb; } else dy = fabsf(DSET_DY(insar[0])); if( nc < 0.0f ){ dz = 1.0f; nc = -nc; } else dz = fabsf(DSET_DZ(insar[0])); nbhd = MCW_rectmask( dx,dy,dz , na,nb,nc ) ; } break ; case NTYPE_RHDD:{ float dx , dy , dz ; if( na < 0.0f ){ dx = dy = dz = 1.0f ; na = -na ; } else { dx = fabsf(DSET_DX(insar[0])) ; dy = fabsf(DSET_DY(insar[0])) ; dz = fabsf(DSET_DZ(insar[0])) ; } nbhd = MCW_rhddmask( dx,dy,dz , na ) ; } break ; case NTYPE_TOHD:{ float dx , dy , dz ; if( na < 0.0f ){ dx = dy = dz = 1.0f ; na = -na ; } else { dx = fabsf(DSET_DX(insar[0])) ; dy = fabsf(DSET_DY(insar[0])) ; dz = fabsf(DSET_DZ(insar[0])) ; } nbhd = MCW_tohdmask( dx,dy,dz , na ) ; } break ; } if( verb ) INFO_message("Neighborhood comprises %d voxels",nbhd->num_pt) ; /*------- actually do some work for a change (is it lunchtime yet?) -------*/ if( verb ) fprintf(stderr,"Voxel-wise histograms ") ; outset = THD_localhistog( nsar,insar , numval,rlist , nbhd , do_prob,verb ) ; if( outset == NULL ) ERROR_exit("Function THD_localhistog() fails?!") ; /*---- save resulting dataset ----*/ EDIT_dset_items( outset , ADN_prefix,prefix , ADN_none ) ; tross_Copy_History( insar[0] , outset ) ; tross_Make_History( "3dLocalHistog" , argc,argv , outset ) ; /* but first attach labels to sub-bricks */ EDIT_BRICK_LABEL(outset,0,"0:Other") ; for( kk=1 ; kk < numval ; kk++ ){ sprintf(buf,"%d:",rlist[kk]) ; for( ii=0 ; ii < nlab ; ii++ ){ if( labval[ii] == rlist[kk] && lablab[ii] != NULL ){ ids = strlen(buf) ; MCW_strncpy(buf+ids,lablab[ii],THD_MAX_SBLABEL-ids) ; break ; } } EDIT_BRICK_LABEL(outset,kk,buf) ; } DSET_write( outset ) ; if( verb ) WROTE_DSET( outset ) ; exit(0) ; }
int main( int argc , char *argv[] ) { int iarg=1 , ii , do_iwarp=0 ; char *prefix = "NwarpCat" ; mat44 wmat , smat , qmat ; THD_3dim_dataset *oset=NULL ; char *cwarp_all=NULL ; int ntot=0 ; AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcasecmp(argv[1],"-help") == 0 ) NWC_help() ; /*-- bureaucracy --*/ mainENTRY("3dNwarpCat"); machdep(); AFNI_logger("3dNwarpCat",argc,argv); PRINT_VERSION("3dNwarpCat"); AUTHOR("Zhark the Warper"); (void)COX_clock_time() ; putenv("AFNI_WSINC5_SILENT=YES") ; /*-- initialization --*/ CW_no_expad = 1 ; /* don't allow automatic padding of input warp */ Hverb = 0 ; /* don't be verbose inside mri_nwarp.c */ for( ii=0 ; ii < NWMAX ; ii++ ) cwarp[ii] = NULL ; /*-- scan args --*/ while( iarg < argc && argv[iarg][0] == '-' ){ /*---------------*/ if( strcasecmp(argv[iarg],"-iwarp") == 0 ){ do_iwarp = 1 ; iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-space") == 0 ){ sname = strdup(argv[++iarg]) ; iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-NN") == 0 || strncasecmp(argv[iarg],"-nearest",6) == 0 ){ WARNING_message("NN interpolation not legal here -- switched to linear") ; interp_code = MRI_LINEAR ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-linear",4)==0 || strncasecmp(argv[iarg],"-trilinear",6)==0 ){ interp_code = MRI_LINEAR ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-cubic",4)==0 || strncasecmp(argv[iarg],"-tricubic",6)==0 ){ WARNING_message("cubic interplation not legal here -- switched to quintic") ; interp_code = MRI_QUINTIC ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-quintic",4)==0 || strncasecmp(argv[iarg],"-triquintic",6)==0 ){ interp_code = MRI_QUINTIC ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-wsinc",5) == 0 ){ interp_code = MRI_WSINC5 ; iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-expad") == 0 ){ int expad ; if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ; expad = (int)strtod(argv[iarg],NULL) ; if( expad < 0 ){ WARNING_message("-expad %d is illegal and is set to zero",expad) ; expad = 0 ; } CW_extra_pad = expad ; /* this is how we force extra padding */ iarg++ ; continue ; } /*---------------*/ if( strncasecmp(argv[iarg],"-interp",5)==0 ){ char *inam ; if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ; inam = argv[iarg] ; if( *inam == '-' ) inam++ ; if( strcasecmp(inam,"NN")==0 || strncasecmp(inam,"nearest",5)==0 ){ WARNING_message("NN interpolation not legal here -- changed to linear") ; interp_code = MRI_LINEAR ; } else if( strncasecmp(inam,"linear",3)==0 || strncasecmp(inam,"trilinear",5)==0 ){ interp_code = MRI_LINEAR ; } else if( strncasecmp(inam,"cubic",3)==0 || strncasecmp(inam,"tricubic",5)==0 ){ WARNING_message("cubic interplation not legal here -- changed to quintic") ; interp_code = MRI_QUINTIC ; } else if( strncasecmp(inam,"quintic",3)==0 || strncasecmp(inam,"triquintic",5)==0 ){ interp_code = MRI_QUINTIC ; } else if( strncasecmp(inam,"wsinc",4)==0 ){ interp_code = MRI_WSINC5 ; } else { ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ; } iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-verb") == 0 ){ verb++ ; NwarpCalcRPN_verb(verb) ; iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ; prefix = argv[iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal name after '%s'",argv[iarg-1]) ; iarg++ ; continue ; } /*---------------*/ if( strncasecmp(argv[iarg],"-warp",5) == 0 ){ int nn ; if( iarg >= argc-1 ) ERROR_exit("no argument after '%s' :-(",argv[iarg]) ; if( !isdigit(argv[iarg][5]) ) ERROR_exit("illegal format for '%s' :-(",argv[iarg]) ; nn = (int)strtod(argv[iarg]+5,NULL) ; if( nn <= 0 || nn > NWMAX ) ERROR_exit("illegal warp index in '%s' :-(",argv[iarg]) ; if( cwarp[nn-1] != NULL ) ERROR_exit("'%s': you can't specify warp #%d more than once :-(",argv[iarg],nn) ; cwarp[nn-1] = strdup(argv[++iarg]) ; if( nn > nwtop ) nwtop = nn ; iarg++ ; continue ; } /*---------------*/ ERROR_message("Confusingly Unknown option '%s' :-(",argv[iarg]) ; suggest_best_prog_option(argv[0],argv[iarg]) ; exit(1) ; } /*-- load any warps left on the command line, after options --*/ for( ; iarg < argc && nwtop < NWMAX-1 ; iarg++ ) cwarp[nwtop++] = strdup(argv[iarg]) ; /*-- check if all warp strings are affine matrices --*/ #undef AFFINE_WARP_STRING #define AFFINE_WARP_STRING(ss) \ ( strstr((ss)," ") == NULL && \ ( strcasestr((ss),".1D") != NULL || strcasestr((ss),".txt") != NULL ) ) for( ntot=ii=0 ; ii < nwtop ; ii++ ){ if( cwarp[ii] == NULL ) continue ; ntot += strlen(cwarp[ii]) ; if( ! AFFINE_WARP_STRING(cwarp[ii]) ) break ; /* not affine */ } if( ntot == 0 ) ERROR_exit("No warps on command line?!") ; if( ii == nwtop ){ /* all are affine (this is for Ziad) */ char *fname = malloc(sizeof(char)*(strlen(prefix)+16)) ; FILE *fp ; float a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34 ; LOAD_IDENT_MAT44(wmat) ; for( ii=0 ; ii < nwtop ; ii++ ){ if( cwarp[ii] == NULL ) continue ; smat = CW_read_affine_warp_OLD(cwarp[ii]) ; qmat = MAT44_MUL(smat,wmat) ; wmat = qmat ; } if( strcmp(prefix,"-") == 0 || strncmp(prefix,"stdout",6) == 0 ){ fp = stdout ; strcpy(fname,"stdout") ; } else { strcpy(fname,prefix) ; if( strstr(fname,".1D") == NULL ) strcat(fname,".aff12.1D") ; fp = fopen(fname,"w") ; if( fp == NULL ) ERROR_exit("Can't open output matrix file %s",fname) ; } if( do_iwarp ){ qmat = MAT44_INV(wmat) ; wmat = qmat ; } UNLOAD_MAT44(wmat,a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34) ; fprintf(fp, " %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g\n", a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34 ) ; if( verb && fp != stdout ) INFO_message("Wrote matrix to %s",fname) ; if( fp != stdout ) fclose(fp) ; exit(0) ; } /*** at least one nonlinear warp ==> cat all strings, use library function to read ***/ cwarp_all = (char *)calloc(sizeof(char),(ntot+NWMAX)*2) ; for( ii=0 ; ii < nwtop ; ii++ ){ if( cwarp[ii] != NULL ){ strcat(cwarp_all,cwarp[ii]) ; strcat(cwarp_all," ") ; } } oset = IW3D_read_catenated_warp( cwarp_all ) ; /* process all of them at once */ if( do_iwarp ){ /* 18 Jul 2014 */ THD_3dim_dataset *qwarp ; if( verb ) fprintf(stderr,"Applying -iwarp option") ; qwarp = THD_nwarp_invert(oset) ; DSET_delete(oset) ; oset = qwarp ; if( verb ) fprintf(stderr,"\n") ; } tross_Make_History( "3dNwarpCat" , argc,argv , oset ) ; if( sname != NULL ) MCW_strncpy( oset->atlas_space , sname , THD_MAX_NAME ) ; EDIT_dset_items( oset , ADN_prefix,prefix , ADN_none ) ; DSET_write(oset) ; WROTE_DSET(oset) ; /*--- run away screaming into the night, never to be seen again ---*/ INFO_message("total CPU time = %.1f sec Elapsed = %.1f\n", COX_cpu_time() , COX_clock_time() ) ; exit(0) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *xset , *cset, *mset=NULL ; int nopt=1 , method=PEARSON , do_autoclip=0 ; int nvox , nvals , ii, jj, kout, kin, polort=1 ; int ix1,jy1,kz1, ix2, jy2, kz2 ; char *prefix = "degree_centrality" ; byte *mask=NULL; int nmask , abuc=1 ; int all_source=0; /* output all source voxels 25 Jun 2010 [rickr] */ char str[32] , *cpt ; int *imap = NULL ; MRI_vectim *xvectim ; float (*corfun)(int,float *,float*) = NULL ; /* djc - add 1d file output for similarity matrix */ FILE *fout1D=NULL; /* CC - we will have two subbricks: binary and weighted centrality */ int nsubbriks = 2; int subbrik = 0; float * bodset; float * wodset; int nb_ctr = 0; /* CC - added flags for thresholding correlations */ double thresh = 0.0; double othresh = 0.0; int dothresh = 0; double sparsity = 0.0; int dosparsity = 0; /* variables for calculating degree centrality */ long * binaryDC = NULL; double * weightedDC = NULL; /* variables for histogram */ hist_node_head* histogram=NULL; hist_node* hptr=NULL; hist_node* pptr=NULL; int bottom_node_idx = 0; int totNumCor = 0; long totPosCor = 0; int ngoal = 0; int nretain = 0; float binwidth = 0.0; int nhistnodes = 50; /*----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: 3dDegreeCentrality [options] dset\n" " Computes voxelwise weighted and binary degree centrality and\n" " stores the result in a new 3D bucket dataset as floats to\n" " preserve their values. Degree centrality reflects the strength and\n" " extent of the correlation of a voxel with every other voxel in\n" " the brain.\n\n" " Conceptually the process involves: \n" " 1. Calculating the correlation between voxel time series for\n" " every pair of voxels in the brain (as determined by masking)\n" " 2. Applying a threshold to the resulting correlations to exclude\n" " those that might have arisen by chance, or to sparsify the\n" " connectivity graph.\n" " 3. At each voxel, summarizing its correlation with other voxels\n" " in the brain, by either counting the number of voxels correlated\n" " with the seed voxel (binary) or by summing the correlation \n" " coefficients (weighted).\n" " Practically the algorithm is ordered differently to optimize for\n" " computational time and memory usage.\n\n" " The threshold can be supplied as a correlation coefficient, \n" " or a sparsity threshold. The sparsity threshold reflects the fraction\n" " of connections that should be retained after the threshold has been\n" " applied. To minimize resource consumption, using a sparsity threshold\n" " involves a two-step procedure. In the first step, a correlation\n" " coefficient threshold is applied to substantially reduce the number\n" " of correlations. Next, the remaining correlations are sorted and a\n" " threshold is calculated so that only the specified fraction of \n" " possible correlations are above threshold. Due to ties between\n" " correlations, the fraction of correlations that pass the sparsity\n" " threshold might be slightly more than the number specified.\n\n" " Regardless of the thresholding procedure employed, negative \n" " correlations are excluded from the calculations.\n" "\n" "Options:\n" " -pearson = Correlation is the normal Pearson (product moment)\n" " correlation coefficient [default].\n" #if 0 " -spearman = Correlation is the Spearman (rank) correlation\n" " coefficient.\n" " -quadrant = Correlation is the quadrant correlation coefficient.\n" #else " -spearman AND -quadrant are disabled at this time :-(\n" #endif "\n" " -thresh r = exclude correlations <= r from calculations\n" " -sparsity s = only use top s percent of correlations in calculations\n" " s should be an integer between 0 and 100. Uses an\n" " an adaptive thresholding procedure to reduce memory.\n" " The speed of determining the adaptive threshold can\n" " be improved by specifying an initial threshold with\n" " the -thresh flag.\n" "\n" " -polort m = Remove polynomical trend of order 'm', for m=-1..3.\n" " [default is m=1; removal is by least squares].\n" " Using m=-1 means no detrending; this is only useful\n" " for data/information that has been pre-processed.\n" "\n" " -autoclip = Clip off low-intensity regions in the dataset,\n" " -automask = so that the correlation is only computed between\n" " high-intensity (presumably brain) voxels. The\n" " mask is determined the same way that 3dAutomask works.\n" "\n" " -mask mmm = Mask to define 'in-brain' voxels. Reducing the number\n" " the number of voxels included in the calculation will\n" " significantly speedup the calculation. Consider using\n" " a mask to constrain the calculations to the grey matter\n" " rather than the whole brain. This is also preferrable\n" " to using -autoclip or -automask.\n" "\n" " -prefix p = Save output into dataset with prefix 'p', this file will\n" " contain bricks for both 'weighted' or 'degree' centrality\n" " [default prefix is 'deg_centrality'].\n" "\n" " -out1D f = Save information about the above threshold correlations to\n" " 1D file 'f'. Each row of this file will contain:\n" " Voxel1 Voxel2 i1 j1 k1 i2 j2 k2 Corr\n" " Where voxel1 and voxel2 are the 1D indices of the pair of\n" " voxels, i j k correspond to their 3D coordinates, and Corr\n" " is the value of the correlation between the voxel time courses.\n" "\n" "Notes:\n" " * The output dataset is a bucket type of floats.\n" " * The program prints out an estimate of its memory used\n" " when it ends. It also prints out a progress 'meter'\n" " to keep you pacified.\n" "\n" "-- RWCox - 31 Jan 2002 and 16 Jul 2010\n" "-- Cameron Craddock - 26 Sept 2015 \n" ) ; PRINT_AFNI_OMP_USAGE("3dDegreeCentrality",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dDegreeCentrality main"); machdep(); PRINT_VERSION("3dDegreeCentrality"); AFNI_logger("3dDegreeCentrality",argc,argv); /*-- option processing --*/ while( nopt < argc && argv[nopt][0] == '-' ){ if( strcmp(argv[nopt],"-time") == 0 ){ abuc = 0 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-autoclip") == 0 || strcmp(argv[nopt],"-automask") == 0 ){ do_autoclip = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-mask") == 0 ){ mset = THD_open_dataset(argv[++nopt]); CHECK_OPEN_ERROR(mset,argv[nopt]); nopt++ ; continue ; } if( strcmp(argv[nopt],"-pearson") == 0 ){ method = PEARSON ; nopt++ ; continue ; } #if 0 if( strcmp(argv[nopt],"-spearman") == 0 ){ method = SPEARMAN ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-quadrant") == 0 ){ method = QUADRANT ; nopt++ ; continue ; } #endif if( strcmp(argv[nopt],"-eta2") == 0 ){ method = ETA2 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-prefix") == 0 ){ prefix = strdup(argv[++nopt]) ; if( !THD_filename_ok(prefix) ){ ERROR_exit("Illegal value after -prefix!") ; } nopt++ ; continue ; } if( strcmp(argv[nopt],"-thresh") == 0 ){ double val = (double)strtod(argv[++nopt],&cpt) ; if( *cpt != '\0' || val >= 1.0 || val < 0.0 ){ ERROR_exit("Illegal value (%f) after -thresh!", val) ; } dothresh = 1; thresh = val ; othresh = val ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-sparsity") == 0 ){ double val = (double)strtod(argv[++nopt],&cpt) ; if( *cpt != '\0' || val > 100 || val <= 0 ){ ERROR_exit("Illegal value (%f) after -sparsity!", val) ; } if( val > 5.0 ) { WARNING_message("Sparsity %3.2f%% is large and will require alot of memory and time, consider using a smaller value. ", val); } dosparsity = 1 ; sparsity = val ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-polort") == 0 ){ int val = (int)strtod(argv[++nopt],&cpt) ; if( *cpt != '\0' || val < -1 || val > 3 ){ ERROR_exit("Illegal value after -polort!") ; } polort = val ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-mem_stat") == 0 ){ MEM_STAT = 1 ; nopt++ ; continue ; } if( strncmp(argv[nopt],"-mem_profile",8) == 0 ){ MEM_PROF = 1 ; nopt++ ; continue ; } /* check for 1d argument */ if ( strcmp(argv[nopt],"-out1D") == 0 ){ if (!(fout1D = fopen(argv[++nopt], "w"))) { ERROR_message("Failed to open %s for writing", argv[nopt]); exit(1); } nopt++ ; continue ; } ERROR_exit("Illegal option: %s",argv[nopt]) ; } /*-- open dataset, check for legality --*/ if( nopt >= argc ) ERROR_exit("Need a dataset on command line!?") ; xset = THD_open_dataset(argv[nopt]); CHECK_OPEN_ERROR(xset,argv[nopt]); if( DSET_NVALS(xset) < 3 ) ERROR_exit("Input dataset %s does not have 3 or more sub-bricks!",argv[nopt]) ; DSET_load(xset) ; CHECK_LOAD_ERROR(xset) ; /*-- compute mask array, if desired --*/ nvox = DSET_NVOX(xset) ; nvals = DSET_NVALS(xset) ; INC_MEM_STATS((nvox * nvals * sizeof(double)), "input dset"); PRINT_MEM_STATS("inset"); /* if a mask was specified make sure it is appropriate */ if( mset ){ if( DSET_NVOX(mset) != nvox ) ERROR_exit("Input and mask dataset differ in number of voxels!") ; mask = THD_makemask(mset, 0, 1.0, 0.0) ; /* update running memory statistics to reflect loading the image */ INC_MEM_STATS( mset->dblk->total_bytes, "mask dset" ); PRINT_MEM_STATS( "mset load" ); nmask = THD_countmask( nvox , mask ) ; INC_MEM_STATS( nmask * sizeof(byte), "mask array" ); PRINT_MEM_STATS( "mask" ); INFO_message("%d voxels in -mask dataset",nmask) ; if( nmask < 2 ) ERROR_exit("Only %d voxels in -mask, exiting...",nmask); /* update running memory statistics to reflect loading the image */ DEC_MEM_STATS( mset->dblk->total_bytes, "mask dset" ); DSET_unload(mset) ; PRINT_MEM_STATS( "mset unload" ); } /* if automasking is requested, handle that now */ else if( do_autoclip ){ mask = THD_automask( xset ) ; nmask = THD_countmask( nvox , mask ) ; INFO_message("%d voxels survive -autoclip",nmask) ; if( nmask < 2 ) ERROR_exit("Only %d voxels in -automask!",nmask); } /* otherwise we use all of the voxels in the image */ else { nmask = nvox ; INFO_message("computing for all %d voxels",nmask) ; } if( method == ETA2 && polort >= 0 ) WARNING_message("Polort for -eta2 should probably be -1..."); /* djc - 1d file out init */ if (fout1D != NULL) { /* define affine matrix */ mat44 affine_mat = xset->daxes->ijk_to_dicom; /* print command line statement */ fprintf(fout1D,"#Similarity matrix from command:\n#"); for(ii=0; ii<argc; ++ii) fprintf(fout1D,"%s ", argv[ii]); /* Print affine matrix */ fprintf(fout1D,"\n"); fprintf(fout1D,"#[ "); int mi, mj; for(mi = 0; mi < 4; mi++) { for(mj = 0; mj < 4; mj++) { fprintf(fout1D, "%.6f ", affine_mat.m[mi][mj]); } } fprintf(fout1D, "]\n"); /* Print image extents*/ THD_dataxes *xset_daxes = xset->daxes; fprintf(fout1D, "#Image dimensions:\n"); fprintf(fout1D, "#[%d, %d, %d]\n", xset_daxes->nxx, xset_daxes->nyy, xset_daxes->nzz); /* Similarity matrix headers */ fprintf(fout1D,"#Voxel1 Voxel2 i1 j1 k1 i2 j2 k2 Corr\n"); } /* CC calculate the total number of possible correlations, will be usefule down the road */ totPosCor = (.5*((float)nmask))*((float)(nmask-1)); /** For the case of Pearson correlation, we make sure the **/ /** data time series have their mean removed (polort >= 0) **/ /** and are normalized, so that correlation = dot product, **/ /** and we can use function zm_THD_pearson_corr for speed. **/ switch( method ){ default: case PEARSON: corfun = zm_THD_pearson_corr ; break ; case ETA2: corfun = my_THD_eta_squared ; break ; } /*-- create vectim from input dataset --*/ INFO_message("vectim-izing input dataset") ; /*-- CC added in mask to reduce the size of xvectim -- */ xvectim = THD_dset_to_vectim( xset , mask , 0 ) ; if( xvectim == NULL ) ERROR_exit("Can't create vectim?!") ; /*-- CC update our memory stats to reflect vectim -- */ INC_MEM_STATS((xvectim->nvec*sizeof(int)) + ((xvectim->nvec)*(xvectim->nvals))*sizeof(float) + sizeof(MRI_vectim), "vectim"); PRINT_MEM_STATS( "vectim" ); /*--- CC the vectim contains a mapping between voxel index and mask index, tap into that here to avoid duplicating memory usage ---*/ if( mask != NULL ) { imap = xvectim->ivec; /* --- CC free the mask */ DEC_MEM_STATS( nmask*sizeof(byte), "mask array" ); free(mask); mask=NULL; PRINT_MEM_STATS( "mask unload" ); } /* -- CC unloading the dataset to reduce memory usage ?? -- */ DEC_MEM_STATS((DSET_NVOX(xset) * DSET_NVALS(xset) * sizeof(double)), "input dset"); DSET_unload(xset) ; PRINT_MEM_STATS("inset unload"); /* -- CC configure detrending --*/ if( polort < 0 && method == PEARSON ){ polort = 0; WARNING_message("Pearson correlation always uses polort >= 0"); } if( polort >= 0 ){ for( ii=0 ; ii < xvectim->nvec ; ii++ ){ /* remove polynomial trend */ DETREND_polort(polort,nvals,VECTIM_PTR(xvectim,ii)) ; } } /* -- this procedure does not change time series that have zero variance -- */ if( method == PEARSON ) THD_vectim_normalize(xvectim) ; /* L2 norm = 1 */ /* -- CC create arrays to hold degree and weighted centrality while they are being calculated -- */ if( dosparsity == 0 ) { if( ( binaryDC = (long*)calloc( nmask, sizeof(long) )) == NULL ) { ERROR_message( "Could not allocate %d byte array for binary DC calculation\n", nmask*sizeof(long)); } /* -- update running memory estimate to reflect memory allocation */ INC_MEM_STATS( nmask*sizeof(long), "binary DC array" ); PRINT_MEM_STATS( "binaryDC" ); if( ( weightedDC = (double*)calloc( nmask, sizeof(double) )) == NULL ) { if (binaryDC){ free(binaryDC); binaryDC = NULL; } ERROR_message( "Could not allocate %d byte array for weighted DC calculation\n", nmask*sizeof(double)); } /* -- update running memory estimate to reflect memory allocation */ INC_MEM_STATS( nmask*sizeof(double), "weighted DC array" ); PRINT_MEM_STATS( "weightedDC" ); } /* -- CC if we are using a sparsity threshold, build a histogram to calculate the threshold */ if (dosparsity == 1) { /* make sure that there is a bin for correlation values that == 1.0 */ binwidth = (1.005-thresh)/nhistnodes; /* calculate the number of correlations we wish to retain */ ngoal = nretain = (int)(((double)totPosCor)*((double)sparsity) / 100.0); /* allocate memory for the histogram bins */ if(( histogram = (hist_node_head*)malloc(nhistnodes*sizeof(hist_node_head))) == NULL ) { /* if the allocation fails, free all memory and exit */ if (binaryDC){ free(binaryDC); binaryDC = NULL; } if (weightedDC){ free(weightedDC); weightedDC = NULL; } ERROR_message( "Could not allocate %d byte array for histogram\n", nhistnodes*sizeof(hist_node_head)); } else { /* -- update running memory estimate to reflect memory allocation */ INC_MEM_STATS( nhistnodes*sizeof(hist_node_head), "hist bins" ); PRINT_MEM_STATS( "hist1" ); } /* initialize history bins */ for( kout = 0; kout < nhistnodes; kout++ ) { histogram[ kout ].bin_low = thresh+kout*binwidth; histogram[ kout ].bin_high = histogram[ kout ].bin_low+binwidth; histogram[ kout ].nbin = 0; histogram[ kout ].nodes = NULL; /*INFO_message("Hist bin %d [%3.3f, %3.3f) [%d, %p]\n", kout, histogram[ kout ].bin_low, histogram[ kout ].bin_high, histogram[ kout ].nbin, histogram[ kout ].nodes );*/ } } /*-- tell the user what we are about to do --*/ if (dosparsity == 0 ) { INFO_message( "Calculating degree centrality with threshold = %f.\n", thresh); } else { INFO_message( "Calculating degree centrality with threshold = %f and sparsity = %3.2f%% (%d)\n", thresh, sparsity, nretain); } /*---------- loop over mask voxels, correlate ----------*/ AFNI_OMP_START ; #pragma omp parallel if( nmask > 999 ) { int lii,ljj,lin,lout,ithr,nthr,vstep,vii ; float *xsar , *ysar ; hist_node* new_node = NULL ; hist_node* tptr = NULL ; hist_node* rptr = NULL ; int new_node_idx = 0; double car = 0.0 ; /*-- get information about who we are --*/ #ifdef USE_OMP ithr = omp_get_thread_num() ; nthr = omp_get_num_threads() ; if( ithr == 0 ) INFO_message("%d OpenMP threads started",nthr) ; #else ithr = 0 ; nthr = 1 ; #endif /*-- For the progress tracker, we want to print out 50 numbers, figure out a number of loop iterations that will make this easy */ vstep = (int)( nmask / (nthr*50.0f) + 0.901f ) ; vii = 0 ; if((MEM_STAT==0) && (ithr == 0 )) fprintf(stderr,"Looping:") ; #pragma omp for schedule(static, 1) for( lout=0 ; lout < xvectim->nvec ; lout++ ){ /*----- outer voxel loop -----*/ if( ithr == 0 && vstep > 2 ) /* allow small dsets 16 Jun 2011 [rickr] */ { vii++ ; if( vii%vstep == vstep/2 && MEM_STAT == 0 ) vstep_print(); } /* get ref time series from this voxel */ xsar = VECTIM_PTR(xvectim,lout) ; /* try to make calculation more efficient by only calculating the unique correlations */ for( lin=(lout+1) ; lin < xvectim->nvec ; lin++ ){ /*----- inner loop over voxels -----*/ /* extract the voxel time series */ ysar = VECTIM_PTR(xvectim,lin) ; /* now correlate the time series */ car = (double)(corfun(nvals,xsar,ysar)) ; if ( car <= thresh ) { continue ; } /* update degree centrality values, hopefully the pragma will handle mutual exclusion */ #pragma omp critical(dataupdate) { /* if the correlation is less than threshold, ignore it */ if ( car > thresh ) { totNumCor += 1; if ( dosparsity == 0 ) { binaryDC[lout] += 1; binaryDC[lin] += 1; weightedDC[lout] += car; weightedDC[lin] += car; /* print correlation out to the 1D file */ if ( fout1D != NULL ) { /* determine the i,j,k coords */ ix1 = DSET_index_to_ix(xset,lii) ; jy1 = DSET_index_to_jy(xset,lii) ; kz1 = DSET_index_to_kz(xset,lii) ; ix2 = DSET_index_to_ix(xset,ljj) ; jy2 = DSET_index_to_jy(xset,ljj) ; kz2 = DSET_index_to_kz(xset,ljj) ; /* add source, dest, correlation to 1D file */ fprintf(fout1D, "%d %d %d %d %d %d %d %d %.6f\n", lii, ljj, ix1, jy1, kz1, ix2, jy2, kz2, car); } } else { /* determine the index in the histogram to add the node */ new_node_idx = (int)floor((double)(car-othresh)/(double)binwidth); if ((new_node_idx > nhistnodes) || (new_node_idx < bottom_node_idx)) { /* this error should indicate a programming error and should not happen */ WARNING_message("Node index %d is out of range [%d,%d)!",new_node_idx, bottom_node_idx, nhistnodes); } else { /* create a node to add to the histogram */ new_node = (hist_node*)calloc(1,sizeof(hist_node)); if( new_node == NULL ) { /* allocate memory for this node, rather than fiddling with error handling here, lets just move on */ WARNING_message("Could not allocate a new node!"); } else { /* populate histogram node */ new_node->i = lout; new_node->j = lin; new_node->corr = car; new_node->next = NULL; /* -- update running memory estimate to reflect memory allocation */ INC_MEM_STATS( sizeof(hist_node), "hist nodes" ); if ((totNumCor % (1024*1024)) == 0) PRINT_MEM_STATS( "hist nodes" ); /* populate histogram */ new_node->next = histogram[new_node_idx].nodes; histogram[new_node_idx].nodes = new_node; histogram[new_node_idx].nbin++; /* see if there are enough correlations in the histogram for the sparsity */ if ((totNumCor - histogram[bottom_node_idx].nbin) > nretain) { /* delete the list of nodes */ rptr = histogram[bottom_node_idx].nodes; while(rptr != NULL) { tptr = rptr; rptr = rptr->next; /* check that the ptr is not null before freeing it*/ if(tptr!= NULL) { DEC_MEM_STATS( sizeof(hist_node), "hist nodes" ); free(tptr); } } PRINT_MEM_STATS( "unloaded hist nodes - thresh increase" ); histogram[bottom_node_idx].nodes = NULL; totNumCor -= histogram[bottom_node_idx].nbin; histogram[bottom_node_idx].nbin=0; /* get the new threshold */ thresh = (double)histogram[++bottom_node_idx].bin_low; if(MEM_STAT == 1) INFO_message("Increasing threshold to %3.2f (%d)\n", thresh,bottom_node_idx); } } /* else, newptr != NULL */ } /* else, new_node_idx in range */ } /* else, do_sparsity == 1 */ } /* car > thresh */ } /* this is the end of the critical section */ } /* end of inner loop over voxels */ } /* end of outer loop over ref voxels */ if( ithr == 0 ) fprintf(stderr,".\n") ; } /* end OpenMP */ AFNI_OMP_END ; /* update the user so that they know what we are up to */ INFO_message ("AFNI_OMP finished\n"); INFO_message ("Found %d (%3.2f%%) correlations above threshold (%f)\n", totNumCor, 100.0*((float)totNumCor)/((float)totPosCor), thresh); /*---------- Finish up ---------*/ /*if( dosparsity == 1 ) { for( kout = 0; kout < nhistnodes; kout++ ) { INFO_message("Hist bin %d [%3.3f, %3.3f) [%d, %p]\n", kout, histogram[ kout ].bin_low, histogram[ kout ].bin_high, histogram[ kout ].nbin, histogram[ kout ].nodes ); } }*/ /*-- create output dataset --*/ cset = EDIT_empty_copy( xset ) ; /*-- configure the output dataset */ if( abuc ){ EDIT_dset_items( cset , ADN_prefix , prefix , ADN_nvals , nsubbriks , /* 2 subbricks, degree and weighted centrality */ ADN_ntt , 0 , /* no time axis */ ADN_type , HEAD_ANAT_TYPE , ADN_func_type , ANAT_BUCK_TYPE , ADN_datum_all , MRI_float , ADN_none ) ; } else { EDIT_dset_items( cset , ADN_prefix , prefix , ADN_nvals , nsubbriks , /* 2 subbricks, degree and weighted centrality */ ADN_ntt , nsubbriks , /* num times */ ADN_ttdel , 1.0 , /* fake TR */ ADN_nsl , 0 , /* no slice offsets */ ADN_type , HEAD_ANAT_TYPE , ADN_func_type , ANAT_EPI_TYPE , ADN_datum_all , MRI_float , ADN_none ) ; } /* add history information to the hearder */ tross_Make_History( "3dDegreeCentrality" , argc,argv , cset ) ; ININFO_message("creating output dataset in memory") ; /* -- Configure the subbriks: Binary Degree Centrality */ subbrik = 0; EDIT_BRICK_TO_NOSTAT(cset,subbrik) ; /* stat params */ /* CC this sets the subbrik scaling factor, which we will probably want to do again after we calculate the voxel values */ EDIT_BRICK_FACTOR(cset,subbrik,1.0) ; /* scale factor */ sprintf(str,"Binary Degree Centrality") ; EDIT_BRICK_LABEL(cset,subbrik,str) ; EDIT_substitute_brick(cset,subbrik,MRI_float,NULL) ; /* make array */ /* copy measure data into the subbrik */ bodset = DSET_ARRAY(cset,subbrik); /* -- Configure the subbriks: Weighted Degree Centrality */ subbrik = 1; EDIT_BRICK_TO_NOSTAT(cset,subbrik) ; /* stat params */ /* CC this sets the subbrik scaling factor, which we will probably want to do again after we calculate the voxel values */ EDIT_BRICK_FACTOR(cset,subbrik,1.0) ; /* scale factor */ sprintf(str,"Weighted Degree Centrality") ; EDIT_BRICK_LABEL(cset,subbrik,str) ; EDIT_substitute_brick(cset,subbrik,MRI_float,NULL) ; /* make array */ /* copy measure data into the subbrik */ wodset = DSET_ARRAY(cset,subbrik); /* increment memory stats */ INC_MEM_STATS( (DSET_NVOX(cset)*DSET_NVALS(cset)*sizeof(float)), "output dset"); PRINT_MEM_STATS( "outset" ); /* pull the values out of the histogram */ if( dosparsity == 0 ) { for( kout = 0; kout < nmask; kout++ ) { if ( imap != NULL ) { ii = imap[kout] ; /* ii= source voxel (we know that ii is in the mask) */ } else { ii = kout ; } if( ii >= DSET_NVOX(cset) ) { WARNING_message("Avoiding bodset, wodset overflow %d > %d (%s,%d)\n", ii,DSET_NVOX(cset),__FILE__,__LINE__ ); } else { bodset[ ii ] = (float)(binaryDC[kout]); wodset[ ii ] = (float)(weightedDC[kout]); } } /* we are done with this memory, and can kill it now*/ if(binaryDC) { free(binaryDC); binaryDC=NULL; /* -- update running memory estimate to reflect memory allocation */ DEC_MEM_STATS( nmask*sizeof(long), "binary DC array" ); PRINT_MEM_STATS( "binaryDC" ); } if(weightedDC) { free(weightedDC); weightedDC=NULL; /* -- update running memory estimate to reflect memory allocation */ DEC_MEM_STATS( nmask*sizeof(double), "weighted DC array" ); PRINT_MEM_STATS( "weightedDC" ); } } else { /* add in the values from the histogram, this is a two stage procedure: at first we add in values a whole bin at the time until we get to a point where we need to add in a partial bin, then we create a new histogram to sort the values in the bin and then add those bins at a time */ kout = nhistnodes - 1; while (( histogram[kout].nbin < nretain ) && ( kout >= 0 )) { hptr = pptr = histogram[kout].nodes; while( hptr != NULL ) { /* determine the indices corresponding to this node */ if ( imap != NULL ) { ii = imap[hptr->i] ; /* ii= source voxel (we know that ii is in the mask) */ } else { ii = hptr->i ; } if ( imap != NULL ) { jj = imap[hptr->j] ; /* ii= source voxel (we know that ii is in the mask) */ } else { jj = hptr->j ; } /* add in the values */ if(( ii >= DSET_NVOX(cset) ) || ( jj >= DSET_NVOX(cset))) { if( ii >= DSET_NVOX(cset)) { WARNING_message("Avoiding bodset, wodset overflow (ii) %d > %d\n (%s,%d)\n", ii,DSET_NVOX(cset),__FILE__,__LINE__ ); } if( jj >= DSET_NVOX(cset)) { WARNING_message("Avoiding bodset, wodset overflow (jj) %d > %d\n (%s,%d)\n", jj,DSET_NVOX(cset),__FILE__,__LINE__ ); } } else { bodset[ ii ] += 1.0 ; wodset[ ii ] += (float)(hptr->corr); bodset[ jj ] += 1.0 ; wodset[ jj ] += (float)(hptr->corr); } if( fout1D != NULL ) { /* add source, dest, correlation to 1D file */ ix1 = DSET_index_to_ix(cset,ii) ; jy1 = DSET_index_to_jy(cset,ii) ; kz1 = DSET_index_to_kz(cset,ii) ; ix2 = DSET_index_to_ix(cset,jj) ; jy2 = DSET_index_to_jy(cset,jj) ; kz2 = DSET_index_to_kz(cset,jj) ; fprintf(fout1D, "%d %d %d %d %d %d %d %d %.6f\n", ii, jj, ix1, jy1, kz1, ix2, jy2, kz2, (float)(hptr->corr)); } /* increment node pointers */ pptr = hptr; hptr = hptr->next; /* delete the node */ if(pptr) { /* -- update running memory estimate to reflect memory allocation */ DEC_MEM_STATS(sizeof( hist_node ), "hist nodes" ); /* free the mem */ free(pptr); pptr=NULL; } } /* decrement the number of correlations we wish to retain */ nretain -= histogram[kout].nbin; histogram[kout].nodes = NULL; /* go on to the next bin */ kout--; } PRINT_MEM_STATS( "hist1 bins free - inc into output" ); /* if we haven't used all of the correlations that are available, go through and add a subset of the voxels from the remaining bin */ if(( nretain > 0 ) && (kout >= 0)) { hist_node_head* histogram2 = NULL; hist_node_head* histogram2_save = NULL; int h2nbins = 100; float h2binwidth = 0.0; int h2ndx=0; h2binwidth = (((1.0+binwidth/((float)h2nbins))*histogram[kout].bin_high) - histogram[kout].bin_low) / ((float)h2nbins); /* allocate the bins */ if(( histogram2 = (hist_node_head*)malloc(h2nbins*sizeof(hist_node_head))) == NULL ) { if (binaryDC){ free(binaryDC); binaryDC = NULL; } if (weightedDC){ free(weightedDC); weightedDC = NULL; } if (histogram){ histogram = free_histogram(histogram, nhistnodes); } ERROR_message( "Could not allocate %d byte array for histogram2\n", h2nbins*sizeof(hist_node_head)); } else { /* -- update running memory estimate to reflect memory allocation */ histogram2_save = histogram2; INC_MEM_STATS(( h2nbins*sizeof(hist_node_head )), "hist bins"); PRINT_MEM_STATS( "hist2" ); } /* initiatize the bins */ for( kin = 0; kin < h2nbins; kin++ ) { histogram2[ kin ].bin_low = histogram[kout].bin_low + kin*h2binwidth; histogram2[ kin ].bin_high = histogram2[ kin ].bin_low + h2binwidth; histogram2[ kin ].nbin = 0; histogram2[ kin ].nodes = NULL; /*INFO_message("Hist2 bin %d [%3.3f, %3.3f) [%d, %p]\n", kin, histogram2[ kin ].bin_low, histogram2[ kin ].bin_high, histogram2[ kin ].nbin, histogram2[ kin ].nodes );*/ } /* move correlations from histogram to histgram2 */ INFO_message ("Adding %d nodes from histogram to histogram2",histogram[kout].nbin); while ( histogram[kout].nodes != NULL ) { hptr = histogram[kout].nodes; h2ndx = (int)floor((double)(hptr->corr - histogram[kout].bin_low)/(double)h2binwidth); if(( h2ndx < h2nbins ) && ( h2ndx >= 0 )) { histogram[kout].nodes = hptr->next; hptr->next = histogram2[h2ndx].nodes; histogram2[h2ndx].nodes = hptr; histogram2[h2ndx].nbin++; histogram[kout].nbin--; } else { WARNING_message("h2ndx %d is not in range [0,%d) :: %.10f,%.10f\n",h2ndx,h2nbins,hptr->corr, histogram[kout].bin_low); } } /* free the remainder of histogram */ { int nbins_rem = 0; for(ii = 0; ii < nhistnodes; ii++) nbins_rem+=histogram[ii].nbin; histogram = free_histogram(histogram, nhistnodes); PRINT_MEM_STATS( "free remainder of histogram1" ); } kin = h2nbins - 1; while (( nretain > 0 ) && ( kin >= 0 )) { hptr = pptr = histogram2[kin].nodes; while( hptr != NULL ) { /* determine the indices corresponding to this node */ if ( imap != NULL ) { ii = imap[hptr->i] ; } else { ii = hptr->i ; } if ( imap != NULL ) { jj = imap[hptr->j] ; } else { jj = hptr->j ; } /* add in the values */ if(( ii >= DSET_NVOX(cset) ) || ( jj >= DSET_NVOX(cset))) { if( ii >= DSET_NVOX(cset)) { WARNING_message("Avoiding bodset, wodset overflow (ii) %d > %d\n (%s,%d)\n", ii,DSET_NVOX(cset),__FILE__,__LINE__ ); } if( jj >= DSET_NVOX(cset)) { WARNING_message("Avoiding bodset, wodset overflow (jj) %d > %d\n (%s,%d)\n", jj,DSET_NVOX(cset),__FILE__,__LINE__ ); } } else { bodset[ ii ] += 1.0 ; wodset[ ii ] += (float)(hptr->corr); bodset[ jj ] += 1.0 ; wodset[ jj ] += (float)(hptr->corr); } if( fout1D != NULL ) { /* add source, dest, correlation to 1D file */ ix1 = DSET_index_to_ix(cset,ii) ; jy1 = DSET_index_to_jy(cset,ii) ; kz1 = DSET_index_to_kz(cset,ii) ; ix2 = DSET_index_to_ix(cset,jj) ; jy2 = DSET_index_to_jy(cset,jj) ; kz2 = DSET_index_to_kz(cset,jj) ; fprintf(fout1D, "%d %d %d %d %d %d %d %d %.6f\n", ii, jj, ix1, jy1, kz1, ix2, jy2, kz2, (float)(hptr->corr)); } /* increment node pointers */ pptr = hptr; hptr = hptr->next; /* delete the node */ if(pptr) { free(pptr); DEC_MEM_STATS(( sizeof(hist_node) ), "hist nodes"); pptr=NULL; } } /* decrement the number of correlations we wish to retain */ nretain -= histogram2[kin].nbin; histogram2[kin].nodes = NULL; /* go on to the next bin */ kin--; } PRINT_MEM_STATS("hist2 nodes free - incorporated into output"); /* we are finished with histogram2 */ { histogram2 = free_histogram(histogram2, h2nbins); /* -- update running memory estimate to reflect memory allocation */ PRINT_MEM_STATS( "free hist2" ); } if (nretain < 0 ) { WARNING_message( "Went over sparsity goal %d by %d, with a resolution of %f", ngoal, -1*nretain, h2binwidth); } } if (nretain > 0 ) { WARNING_message( "Was not able to meet goal of %d (%3.2f%%) correlations, %d (%3.2f%%) correlations passed the threshold of %3.2f, maybe you need to change the threshold or the desired sparsity?", ngoal, 100.0*((float)ngoal)/((float)totPosCor), totNumCor, 100.0*((float)totNumCor)/((float)totPosCor), thresh); } } INFO_message("Done..\n") ; /* update running memory statistics to reflect freeing the vectim */ DEC_MEM_STATS(((xvectim->nvec*sizeof(int)) + ((xvectim->nvec)*(xvectim->nvals))*sizeof(float) + sizeof(MRI_vectim)), "vectim"); /* toss some trash */ VECTIM_destroy(xvectim) ; DSET_delete(xset) ; if(fout1D!=NULL)fclose(fout1D); PRINT_MEM_STATS( "vectim unload" ); if (weightedDC) free(weightedDC) ; weightedDC = NULL; if (binaryDC) free(binaryDC) ; binaryDC = NULL; /* finito */ INFO_message("Writing output dataset to disk [%s bytes]", commaized_integer_string(cset->dblk->total_bytes)) ; /* write the dataset */ DSET_write(cset) ; WROTE_DSET(cset) ; /* increment our memory stats, since we are relying on the header for this information, we update the stats before actually freeing the memory */ DEC_MEM_STATS( (DSET_NVOX(cset)*DSET_NVALS(cset)*sizeof(float)), "output dset"); /* free up the output dataset memory */ DSET_unload(cset) ; DSET_delete(cset) ; /* force a print */ MEM_STAT = 1; PRINT_MEM_STATS( "Fin" ); exit(0) ; }
int main( int argc , char *argv[] ) { int iarg=1 , verb=0 , interp_code=MRI_QUINTIC , ainterp_code=-666 ; char *expr ; int nexpr , narg ; THD_3dim_dataset *oset ; AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcasecmp(argv[1],"-help") == 0 ) NWC_help() ; mainENTRY("3dNwarpCalc"); machdep(); AFNI_logger("3dNwarpCalc",argc,argv); PRINT_VERSION("3dNwarpCalc"); AUTHOR("Bob the Warped"); (void)COX_clock_time() ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strcasecmp(argv[iarg],"-NN") == 0 || strncasecmp(argv[iarg],"-nearest",6) == 0 ){ WARNING_message("NN interpolation not legal here -- switched to linear") ; interp_code = MRI_LINEAR ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-linear",4)==0 || strncasecmp(argv[iarg],"-trilinear",6)==0 ){ interp_code = MRI_LINEAR ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-cubic",4)==0 || strncasecmp(argv[iarg],"-tricubic",6)==0 ){ WARNING_message("cubic interplation not legal here -- switched to quintic") ; interp_code = MRI_QUINTIC ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-quintic",4)==0 || strncasecmp(argv[iarg],"-triquintic",6)==0 ){ interp_code = MRI_QUINTIC ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-wsinc",5) == 0 ){ interp_code = MRI_WSINC5 ; iarg++ ; continue ; } if( strncasecmp(argv[iarg],"-interp",5)==0 ){ char *inam ; if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ; inam = argv[iarg] ; if( *inam == '-' ) inam++ ; if( strcasecmp(inam,"NN")==0 || strncasecmp(inam,"nearest",5)==0 ){ WARNING_message("NN interpolation not legal here -- changed to linear") ; interp_code = MRI_LINEAR ; } else if( strncasecmp(inam,"linear",3)==0 || strncasecmp(inam,"trilinear",5)==0 ){ interp_code = MRI_LINEAR ; } else if( strncasecmp(inam,"cubic",3)==0 || strncasecmp(inam,"tricubic",5)==0 ){ WARNING_message("cubic interplation not legal here -- changed to quintic") ; interp_code = MRI_QUINTIC ; } else if( strncasecmp(inam,"quintic",3)==0 || strncasecmp(inam,"triquintic",5)==0 ){ interp_code = MRI_QUINTIC ; } else if( strncasecmp(inam,"wsinc",4)==0 ){ interp_code = MRI_WSINC5 ; } else { ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ; } iarg++ ; continue ; } /*---------------*/ if( strncasecmp(argv[iarg],"-ainterp",6)==0 ){ char *inam ; if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ; inam = argv[iarg] ; if( *inam == '-' ) inam++ ; if( strcasecmp(inam,"NN")==0 || strncasecmp(inam,"nearest",5)==0 ){ ainterp_code = MRI_NN ; } else if( strncasecmp(inam,"linear",3)==0 || strncasecmp(inam,"trilinear",5)==0 ){ ainterp_code = MRI_LINEAR ; } else if( strncasecmp(inam,"cubic",3)==0 || strncasecmp(inam,"tricubic",5)==0 ){ ainterp_code = MRI_CUBIC ; } else if( strncasecmp(inam,"quintic",3)==0 || strncasecmp(inam,"triquintic",5)==0 ){ ainterp_code = MRI_QUINTIC ; } else if( strncasecmp(inam,"wsinc",4)==0 ){ ainterp_code = MRI_WSINC5 ; } else { ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ; } iarg++ ; continue ; } /*---------------*/ if( strcasecmp(argv[iarg],"-verb") == 0 ){ verb++ ; iarg++ ; continue ; } /*---------------*/ ERROR_message("Bizarre and Unknown option '%s' :-(",argv[iarg]) ; suggest_best_prog_option(argv[0],argv[iarg]) ; exit(1) ; } if( iarg >= argc ) ERROR_exit("No command line expression :-(") ; /*--- Assemble all remaining args into the expression ---*/ expr = strdup(argv[iarg++]) ; for( ; iarg < argc ; iarg++ ){ nexpr = strlen(expr) ; narg = strlen(argv[iarg]) ; if( narg == 0 ) continue ; expr = (char *)realloc( expr , sizeof(char)*(nexpr+narg+4) ) ; strcat(expr," ") ; strcat(expr,argv[iarg]) ; } if( ainterp_code < 0 ) ainterp_code = interp_code ; /*--- All the work is done herein ---*/ NwarpCalcRPN_verb(verb) ; oset = NwarpCalcRPN( expr , NULL , interp_code , ainterp_code ) ; /*--- run away screaming into the night, never to be seen again ---*/ free(expr) ; if( oset != NULL ) DSET_delete(oset) ; INFO_message("total CPU time = %.1f sec Elapsed = %.1f\n", COX_cpu_time() , COX_clock_time() ) ; exit(0) ; }
int main( int argc , char * argv[] ) { int do_norm=0 , qdet=2 , have_freq=0 , do_automask=0 ; float dt=0.0f , fbot=0.0f,ftop=999999.9f , blur=0.0f ; MRI_IMARR *ortar=NULL ; MRI_IMAGE *ortim=NULL ; THD_3dim_dataset **ortset=NULL ; int nortset=0 ; THD_3dim_dataset *inset=NULL , *outset ; char *prefix="bandpass" ; byte *mask=NULL ; int mask_nx=0,mask_ny=0,mask_nz=0,nmask , verb=1 , nx,ny,nz,nvox , nfft=0 , kk ; float **vec , **ort=NULL ; int nort=0 , vv , nopt , ntime ; MRI_vectim *mrv ; float pvrad=0.0f ; int nosat=0 ; int do_despike=0 ; /*-- help? --*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "\n" "** NOTA BENE: For the purpose of preparing resting-state FMRI datasets **\n" "** for analysis (e.g., with 3dGroupInCorr), this program is now mostly **\n" "** superseded by the afni_proc.py script. See the 'afni_proc.py -help' **\n" "** section 'Resting state analysis (modern)' to get our current rs-FMRI **\n" "** pre-processing recommended sequence of steps. -- RW Cox, et alii. **\n" "\n" "Usage: 3dBandpass [options] fbot ftop dataset\n" "\n" "* One function of this program is to prepare datasets for input\n" " to 3dSetupGroupInCorr. Other uses are left to your imagination.\n" "\n" "* 'dataset' is a 3D+time sequence of volumes\n" " ++ This must be a single imaging run -- that is, no discontinuities\n" " in time from 3dTcat-ing multiple datasets together.\n" "\n" "* fbot = lowest frequency in the passband, in Hz\n" " ++ fbot can be 0 if you want to do a lowpass filter only;\n" " HOWEVER, the mean and Nyquist freq are always removed.\n" "\n" "* ftop = highest frequency in the passband (must be > fbot)\n" " ++ if ftop > Nyquist freq, then it's a highpass filter only.\n" "\n" "* Set fbot=0 and ftop=99999 to do an 'allpass' filter.\n" " ++ Except for removal of the 0 and Nyquist frequencies, that is.\n" "\n" "* You cannot construct a 'notch' filter with this program!\n" " ++ You could use 3dBandpass followed by 3dcalc to get the same effect.\n" " ++ If you are understand what you are doing, that is.\n" " ++ Of course, that is the AFNI way -- if you don't want to\n" " understand what you are doing, use Some other PrograM, and\n" " you can still get Fine StatisticaL maps.\n" "\n" "* 3dBandpass will fail if fbot and ftop are too close for comfort.\n" " ++ Which means closer than one frequency grid step df,\n" " where df = 1 / (nfft * dt) [of course]\n" "\n" "* The actual FFT length used will be printed, and may be larger\n" " than the input time series length for the sake of efficiency.\n" " ++ The program will use a power-of-2, possibly multiplied by\n" " a power of 3 and/or 5 (up to and including the 3rd power of\n" " each of these: 3, 9, 27, and 5, 25, 125).\n" "\n" "* Note that the results of combining 3dDetrend and 3dBandpass will\n" " depend on the order in which you run these programs. That's why\n" " 3dBandpass has the '-ort' and '-dsort' options, so that the\n" " time series filtering can be done properly, in one place.\n" "\n" "* The output dataset is stored in float format.\n" "\n" "* The order of processing steps is the following (most are optional):\n" " (0) Check time series for initial transients [does not alter data]\n" " (1) Despiking of each time series\n" " (2) Removal of a constant+linear+quadratic trend in each time series\n" " (3) Bandpass of data time series\n" " (4) Bandpass of -ort time series, then detrending of data\n" " with respect to the -ort time series\n" " (5) Bandpass and de-orting of the -dsort dataset,\n" " then detrending of the data with respect to -dsort\n" " (6) Blurring inside the mask [might be slow]\n" " (7) Local PV calculation [WILL be slow!]\n" " (8) L2 normalization [will be fast.]\n" "\n" "--------\n" "OPTIONS:\n" "--------\n" " -despike = Despike each time series before other processing.\n" " ++ Hopefully, you don't actually need to do this,\n" " which is why it is optional.\n" " -ort f.1D = Also orthogonalize input to columns in f.1D\n" " ++ Multiple '-ort' options are allowed.\n" " -dsort fset = Orthogonalize each voxel to the corresponding\n" " voxel time series in dataset 'fset', which must\n" " have the same spatial and temporal grid structure\n" " as the main input dataset.\n" " ++ At present, only one '-dsort' option is allowed.\n" " -nodetrend = Skip the quadratic detrending of the input that\n" " occurs before the FFT-based bandpassing.\n" " ++ You would only want to do this if the dataset\n" " had been detrended already in some other program.\n" " -dt dd = set time step to 'dd' sec [default=from dataset header]\n" " -nfft N = set the FFT length to 'N' [must be a legal value]\n" " -norm = Make all output time series have L2 norm = 1\n" " ++ i.e., sum of squares = 1\n" " -mask mset = Mask dataset\n" " -automask = Create a mask from the input dataset\n" " -blur fff = Blur (inside the mask only) with a filter\n" " width (FWHM) of 'fff' millimeters.\n" " -localPV rrr = Replace each vector by the local Principal Vector\n" " (AKA first singular vector) from a neighborhood\n" " of radius 'rrr' millimiters.\n" " ++ Note that the PV time series is L2 normalized.\n" " ++ This option is mostly for Bob Cox to have fun with.\n" "\n" " -input dataset = Alternative way to specify input dataset.\n" " -band fbot ftop = Alternative way to specify passband frequencies.\n" "\n" " -prefix ppp = Set prefix name of output dataset.\n" " -quiet = Turn off the fun and informative messages. (Why?)\n" "\n" " -notrans = Don't check for initial positive transients in the data:\n" " *OR* ++ The test is a little slow, so skipping it is OK,\n" " -nosat if you KNOW the data time series are transient-free.\n" " ++ Or set AFNI_SKIP_SATCHECK to YES.\n" " ++ Initial transients won't be handled well by the\n" " bandpassing algorithm, and in addition may seriously\n" " contaminate any further processing, such as inter-voxel\n" " correlations via InstaCorr.\n" " ++ No other tests are made [yet] for non-stationary behavior\n" " in the time series data.\n" ) ; PRINT_AFNI_OMP_USAGE( "3dBandpass" , "* At present, the only part of 3dBandpass that is parallelized is the\n" " '-blur' option, which processes each sub-brick independently.\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } /*-- startup --*/ mainENTRY("3dBandpass"); machdep(); AFNI_logger("3dBandpass",argc,argv); PRINT_VERSION("3dBandpass"); AUTHOR("RW Cox"); nosat = AFNI_yesenv("AFNI_SKIP_SATCHECK") ; nopt = 1 ; while( nopt < argc && argv[nopt][0] == '-' ){ if( strcmp(argv[nopt],"-despike") == 0 ){ /* 08 Oct 2010 */ do_despike++ ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-nfft") == 0 ){ int nnup ; if( ++nopt >= argc ) ERROR_exit("need an argument after -nfft!") ; nfft = (int)strtod(argv[nopt],NULL) ; nnup = csfft_nextup_even(nfft) ; if( nfft < 16 || nfft != nnup ) ERROR_exit("value %d after -nfft is illegal! Next legal value = %d",nfft,nnup) ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-blur") == 0 ){ if( ++nopt >= argc ) ERROR_exit("need an argument after -blur!") ; blur = strtod(argv[nopt],NULL) ; if( blur <= 0.0f ) WARNING_message("non-positive blur?!") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-localPV") == 0 ){ if( ++nopt >= argc ) ERROR_exit("need an argument after -localpv!") ; pvrad = strtod(argv[nopt],NULL) ; if( pvrad <= 0.0f ) WARNING_message("non-positive -localpv?!") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-prefix") == 0 ){ if( ++nopt >= argc ) ERROR_exit("need an argument after -prefix!") ; prefix = strdup(argv[nopt]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("bad -prefix option!") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-automask") == 0 ){ if( mask != NULL ) ERROR_exit("Can't use -mask AND -automask!") ; do_automask = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-mask") == 0 ){ THD_3dim_dataset *mset ; if( ++nopt >= argc ) ERROR_exit("Need argument after '-mask'") ; if( mask != NULL || do_automask ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[nopt] ) ; CHECK_OPEN_ERROR(mset,argv[nopt]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); mask = THD_makemask( mset , 0 , 0.5f, 0.0f ) ; DSET_delete(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%s'",argv[nopt]) ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; if( verb ) INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < 1 ) ERROR_exit("Mask is too small to process") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-norm") == 0 ){ do_norm = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-quiet") == 0 ){ verb = 0 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-notrans") == 0 || strcmp(argv[nopt],"-nosat") == 0 ){ nosat = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-ort") == 0 ){ if( ++nopt >= argc ) ERROR_exit("need an argument after -ort!") ; if( ortar == NULL ) INIT_IMARR(ortar) ; ortim = mri_read_1D( argv[nopt] ) ; if( ortim == NULL ) ERROR_exit("can't read from -ort '%s'",argv[nopt]) ; mri_add_name(argv[nopt],ortim) ; ADDTO_IMARR(ortar,ortim) ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-dsort") == 0 ){ THD_3dim_dataset *qset ; if( ++nopt >= argc ) ERROR_exit("need an argument after -dsort!") ; if( nortset > 0 ) ERROR_exit("only 1 -dsort option is allowed!") ; qset = THD_open_dataset(argv[nopt]) ; CHECK_OPEN_ERROR(qset,argv[nopt]) ; ortset = (THD_3dim_dataset **)realloc(ortset, sizeof(THD_3dim_dataset *)*(nortset+1)) ; ortset[nortset++] = qset ; nopt++ ; continue ; } if( strncmp(argv[nopt],"-nodetrend",6) == 0 ){ qdet = 0 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-dt") == 0 ){ if( ++nopt >= argc ) ERROR_exit("need an argument after -dt!") ; dt = (float)strtod(argv[nopt],NULL) ; if( dt <= 0.0f ) WARNING_message("value after -dt illegal!") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-input") == 0 ){ if( inset != NULL ) ERROR_exit("Can't have 2 -input options!") ; if( ++nopt >= argc ) ERROR_exit("need an argument after -input!") ; inset = THD_open_dataset(argv[nopt]) ; CHECK_OPEN_ERROR(inset,argv[nopt]) ; nopt++ ; continue ; } if( strncmp(argv[nopt],"-band",5) == 0 ){ if( ++nopt >= argc-1 ) ERROR_exit("need 2 arguments after -band!") ; if( have_freq ) WARNING_message("second -band option replaces first one!") ; fbot = strtod(argv[nopt++],NULL) ; ftop = strtod(argv[nopt++],NULL) ; have_freq = 1 ; continue ; } ERROR_exit("Unknown option: '%s'",argv[nopt]) ; } /** check inputs for reasonablositiness **/ if( !have_freq ){ if( nopt+1 >= argc ) ERROR_exit("Need frequencies on command line after options!") ; fbot = (float)strtod(argv[nopt++],NULL) ; ftop = (float)strtod(argv[nopt++],NULL) ; } if( inset == NULL ){ if( nopt >= argc ) ERROR_exit("Need input dataset name on command line after options!") ; inset = THD_open_dataset(argv[nopt]) ; CHECK_OPEN_ERROR(inset,argv[nopt]) ; nopt++ ; } DSET_UNMSEC(inset) ; if( fbot < 0.0f ) ERROR_exit("fbot value can't be negative!") ; if( ftop <= fbot ) ERROR_exit("ftop value %g must be greater than fbot value %g!",ftop,fbot) ; ntime = DSET_NVALS(inset) ; if( ntime < 9 ) ERROR_exit("Input dataset is too short!") ; if( nfft <= 0 ){ nfft = csfft_nextup_even(ntime) ; if( verb ) INFO_message("Data length = %d FFT length = %d",ntime,nfft) ; (void)THD_bandpass_set_nfft(nfft) ; } else if( nfft < ntime ){ ERROR_exit("-nfft %d is less than data length = %d",nfft,ntime) ; } else { kk = THD_bandpass_set_nfft(nfft) ; if( kk != nfft && verb ) INFO_message("Data length = %d FFT length = %d",ntime,kk) ; } if( dt <= 0.0f ){ dt = DSET_TR(inset) ; if( dt <= 0.0f ){ WARNING_message("Setting dt=1.0 since input dataset lacks a time axis!") ; dt = 1.0f ; } } if( !THD_bandpass_OK(ntime,dt,fbot,ftop,1) ) ERROR_exit("Can't continue!") ; nx = DSET_NX(inset); ny = DSET_NY(inset); nz = DSET_NZ(inset); nvox = nx*ny*nz; /* check mask, or create it */ if( verb ) INFO_message("Loading input dataset time series" ) ; DSET_load(inset) ; if( mask != NULL ){ if( mask_nx != nx || mask_ny != ny || mask_nz != nz ) ERROR_exit("-mask dataset grid doesn't match input dataset") ; } else if( do_automask ){ mask = THD_automask( inset ) ; if( mask == NULL ) ERROR_message("Can't create -automask from input dataset?") ; nmask = THD_countmask( DSET_NVOX(inset) , mask ) ; if( verb ) INFO_message("Number of voxels in automask = %d",nmask); if( nmask < 1 ) ERROR_exit("Automask is too small to process") ; } else { mask = (byte *)malloc(sizeof(byte)*nvox) ; nmask = nvox ; memset(mask,1,sizeof(byte)*nvox) ; if( verb ) INFO_message("No mask ==> processing all %d voxels",nvox); } /* A simple check of dataset quality [08 Feb 2010] */ if( !nosat ){ float val ; INFO_message( "Checking dataset for initial transients [use '-notrans' to skip this test]") ; val = THD_saturation_check(inset,mask,0,0) ; kk = (int)(val+0.54321f) ; if( kk > 0 ) ININFO_message( "Looks like there %s %d non-steady-state initial time point%s :-(" , ((kk==1) ? "is" : "are") , kk , ((kk==1) ? " " : "s") ) ; else if( val > 0.3210f ) /* don't ask where this threshold comes from! */ ININFO_message( "MAYBE there's an initial positive transient of 1 point, but it's hard to tell\n") ; else ININFO_message("No widespread initial positive transient detected :-)") ; } /* check -dsort inputs for match to inset */ for( kk=0 ; kk < nortset ; kk++ ){ if( DSET_NX(ortset[kk]) != nx || DSET_NY(ortset[kk]) != ny || DSET_NZ(ortset[kk]) != nz || DSET_NVALS(ortset[kk]) != ntime ) ERROR_exit("-dsort %s doesn't match input dataset grid" , DSET_BRIKNAME(ortset[kk]) ) ; } /* convert input dataset to a vectim, which is more fun */ mrv = THD_dset_to_vectim( inset , mask , 0 ) ; if( mrv == NULL ) ERROR_exit("Can't load time series data!?") ; DSET_unload(inset) ; /* similarly for the ort vectors */ if( ortar != NULL ){ for( kk=0 ; kk < IMARR_COUNT(ortar) ; kk++ ){ ortim = IMARR_SUBIM(ortar,kk) ; if( ortim->nx < ntime ) ERROR_exit("-ort file %s is shorter than input dataset time series", ortim->name ) ; ort = (float **)realloc( ort , sizeof(float *)*(nort+ortim->ny) ) ; for( vv=0 ; vv < ortim->ny ; vv++ ) ort[nort++] = MRI_FLOAT_PTR(ortim) + ortim->nx * vv ; } } /* check whether processing leaves any DoF remaining 18 Mar 2015 [rickr] */ { int nbprem = THD_bandpass_remain_dim(ntime, dt, fbot, ftop, 1); int bpused, nremain; int wlimit; /* warning limit */ bpused = ntime - nbprem; /* #dim lost in bandpass step */ nremain = nbprem - nort; /* #dim left in output */ if( nortset == 1 ) nremain--; nremain -= (qdet+1); if( verb ) INFO_message("%d dimensional data reduced to %d by:\n" " %d (bandpass), %d (-ort), %d (-dsort), %d (detrend)", ntime, nremain, bpused, nort, nortset?1:0, qdet+1); /* possibly warn (if 95% lost) user or fail */ wlimit = ntime/20; if( wlimit < 3 ) wlimit = 3; if( nremain < wlimit && nremain > 0 ) WARNING_message("dimensionality reduced from %d to %d, be careful!", ntime, nremain); if( nremain <= 0 ) /* FAILURE */ ERROR_exit("dimensionality reduced from %d to %d, failing!", ntime, nremain); } /* all the real work now */ if( do_despike ){ int_pair nsp ; if( verb ) INFO_message("Testing data time series for spikes") ; nsp = THD_vectim_despike9( mrv ) ; if( verb ) ININFO_message(" -- Squashed %d spikes from %d voxels",nsp.j,nsp.i) ; } if( verb ) INFO_message("Bandpassing data time series") ; (void)THD_bandpass_vectim( mrv , dt,fbot,ftop , qdet , nort,ort ) ; /* OK, maybe a little more work */ if( nortset == 1 ){ MRI_vectim *orv ; orv = THD_dset_to_vectim( ortset[0] , mask , 0 ) ; if( orv == NULL ){ ERROR_message("Can't load -dsort %s",DSET_BRIKNAME(ortset[0])) ; } else { float *dp , *mvv , *ovv , ff ; if( verb ) INFO_message("Orthogonalizing to bandpassed -dsort") ; (void)THD_bandpass_vectim( orv , dt,fbot,ftop , qdet , nort,ort ) ; THD_vectim_normalize( orv ) ; dp = malloc(sizeof(float)*mrv->nvec) ; THD_vectim_vectim_dot( mrv , orv , dp ) ; for( vv=0 ; vv < mrv->nvec ; vv++ ){ ff = dp[vv] ; if( ff != 0.0f ){ mvv = VECTIM_PTR(mrv,vv) ; ovv = VECTIM_PTR(orv,vv) ; for( kk=0 ; kk < ntime ; kk++ ) mvv[kk] -= ff*ovv[kk] ; } } VECTIM_destroy(orv) ; free(dp) ; } } if( blur > 0.0f ){ if( verb ) INFO_message("Blurring time series data spatially; FWHM=%.2f",blur) ; mri_blur3D_vectim( mrv , blur ) ; } if( pvrad > 0.0f ){ if( verb ) INFO_message("Local PV-ing time series data spatially; radius=%.2f",pvrad) ; THD_vectim_normalize( mrv ) ; THD_vectim_localpv( mrv , pvrad ) ; } if( do_norm && pvrad <= 0.0f ){ if( verb ) INFO_message("L2 normalizing time series data") ; THD_vectim_normalize( mrv ) ; } /* create output dataset, populate it, write it, then quit */ if( verb ) INFO_message("Creating output dataset in memory, then writing it") ; outset = EDIT_empty_copy(inset) ; /* do not copy scalars 11 Sep 2015 [rickr] */ EDIT_dset_items( outset , ADN_prefix,prefix , ADN_brick_fac,NULL , ADN_none ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dBandpass" , argc,argv , outset ) ; for( vv=0 ; vv < ntime ; vv++ ) EDIT_substitute_brick( outset , vv , MRI_float , NULL ) ; #if 1 THD_vectim_to_dset( mrv , outset ) ; #else AFNI_OMP_START ; #pragma omp parallel { float *far , *var ; int *ivec=mrv->ivec ; int vv,kk ; #pragma omp for for( vv=0 ; vv < ntime ; vv++ ){ far = DSET_BRICK_ARRAY(outset,vv) ; var = mrv->fvec + vv ; for( kk=0 ; kk < nmask ; kk++ ) far[ivec[kk]] = var[kk*ntime] ; } } AFNI_OMP_END ; #endif VECTIM_destroy(mrv) ; DSET_write(outset) ; if( verb ) WROTE_DSET(outset) ; exit(0) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *xset=NULL , *cset ; int nopt=1, datum=MRI_float, nvals, ii; MRI_IMAGE *ysim=NULL ; char *prefix = "Tcorr1D", *smethod="pearson"; char *xnam=NULL , *ynam=NULL ; byte *mask=NULL ; int mask_nx,mask_ny,mask_nz , nmask=0 ; int do_atanh = 0 ; /* 12 Jan 2018 */ /*----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dTcorr1D [options] xset y1D\n" "Computes the correlation coefficient between each voxel time series\n" "in the input 3D+time dataset 'xset' and each column in the 1D time\n" "series file 'y1D', and stores the output values in a new dataset.\n" "\n" "OPTIONS:\n" " -pearson = Correlation is the normal Pearson (product moment)\n" " correlation coefficient [this is the default method].\n" " -spearman = Correlation is the Spearman (rank) correlation\n" " coefficient.\n" " -quadrant = Correlation is the quadrant correlation coefficient.\n" " -ktaub = Correlation is Kendall's tau_b coefficient.\n" " ++ For 'continuous' or finely-discretized data, tau_b and\n" " rank correlation are nearly equivalent (but not equal).\n" " -dot = Doesn't actually compute a correlation coefficient; just\n" " calculates the dot product between the y1D vector(s)\n" " and the dataset time series.\n" "\n" " -Fisher = Apply the 'Fisher' (inverse hyperbolic tangent) transformation\n" " to the results.\n" " ++ It does not make sense to use this with '-ktaub', but if\n" " you want to do it, the program will not stop you.\n" " ++ Cannot be used with '-dot'!\n" "\n" " -prefix p = Save output into dataset with prefix 'p'\n" " [default prefix is 'Tcorr1D'].\n" "\n" " -mask mmm = Only process voxels from 'xset' that are nonzero\n" " in the 3D mask dataset 'mmm'.\n" " ++ Other voxels in the output will be set to zero.\n" "\n" " -float = Save results in float format [the default format].\n" " -short = Save results in scaled short format [to save disk space].\n" " ++ Cannot be used with '-dot'!\n" "\n" "NOTES:\n" "* The output dataset is functional bucket type, with one sub-brick\n" " per column of the input y1D file.\n" "* No detrending, blurring, or other pre-processing options are available;\n" " if you want these things, see 3dDetrend or 3dTproject or 3dcalc.\n" " [In other words, this program presumes you know what you are doing!]\n" "* Also see 3dTcorrelate to do voxel-by-voxel correlation of TWO\n" " 3D+time datasets' time series, with similar options.\n" "* You can extract the time series from a single voxel with given\n" " spatial indexes using 3dmaskave, and then run it with 3dTcorr1D:\n" " 3dmaskave -quiet -ibox 40 30 20 epi_r1+orig > r1_40_30_20.1D\n" " 3dTcorr1D -pearson -Fisher -prefix c_40_30_20 epi_r1+orig r1_40_30_20.1D\n" "* http://en.wikipedia.org/wiki/Correlation\n" "* http://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient\n" "* http://en.wikipedia.org/wiki/Spearman%%27s_rank_correlation_coefficient\n" "* http://en.wikipedia.org/wiki/Kendall_tau_rank_correlation_coefficient\n" "\n" "-- RWCox - Apr 2010\n" " - Jun 2010: Multiple y1D columns; OpenMP; -short; -mask.\n" ) ; PRINT_AFNI_OMP_USAGE("3dTcorr1D",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dTcorr1D main"); machdep(); AFNI_logger("3dTcorr1D",argc,argv); PRINT_VERSION("3dTcorr1D") ; THD_check_AFNI_version("3dTcorr1D") ; /*-- option processing --*/ while( nopt < argc && argv[nopt][0] == '-' ){ if( strcmp(argv[nopt],"-mask") == 0 ){ /* 28 Jun 2010 */ THD_3dim_dataset *mset ; if( ++nopt >= argc ) ERROR_exit("Need argument after '-mask'") ; if( mask != NULL ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[nopt] ) ; CHECK_OPEN_ERROR(mset,argv[nopt]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); mask = THD_makemask( mset , 0 , 0.5f, 0.0f ) ; DSET_delete(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%s'",argv[nopt]) ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < 2 ) ERROR_exit("Mask is too small to process") ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-float") == 0 ){ /* 27 Jun 2010 */ datum = MRI_float ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-short") == 0 ){ datum = MRI_short ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-pearson") == 0 ){ smethod = "pearson" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-dot") == 0 ){ smethod = "dot" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-spearman") == 0 || strcasecmp(argv[nopt],"-rank") == 0 ){ smethod = "spearman" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-quadrant") == 0 ){ smethod = "quadrant" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-ktaub") == 0 || strcasecmp(argv[nopt],"-taub") == 0 ){ smethod = "ktaub" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-fisher") == 0 ){ /* 12 Jan 2018 */ do_atanh = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-prefix") == 0 ){ prefix = argv[++nopt] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal value after -prefix!") ; nopt++ ; continue ; } ERROR_exit("Unknown option: %s",argv[nopt]) ; } /*------------ open datasets, check for legality ------------*/ if( nopt+1 >= argc ) ERROR_exit("Need 2 non-option arguments on command line!?") ; /* despite what the help says, if the 1D file is first, that's OK */ if( STRING_HAS_SUFFIX(argv[nopt],"1D") ){ ININFO_message("reading 1D file %s",argv[nopt]) ; ysim = mri_read_1D( argv[nopt] ) ; ynam = argv[nopt] ; if( ysim == NULL ) ERROR_exit("Can't read 1D file %s",argv[nopt]) ; } else { ININFO_message("reading dataset file %s",argv[nopt]) ; xset = THD_open_dataset( argv[nopt] ) ; xnam = argv[nopt] ; if( xset == NULL ) ERROR_exit("Can't open dataset %s",argv[nopt]) ; } /* read whatever type of file (3D or 1D) we don't already have */ nopt++ ; if( xset != NULL ){ ININFO_message("reading 1D file %s",argv[nopt]) ; ysim = mri_read_1D( argv[nopt] ) ; ynam = argv[nopt] ; if( ysim == NULL ) ERROR_exit("Can't read 1D file %s",argv[nopt]) ; } else { ININFO_message("reading dataset file %s",argv[nopt]) ; xset = THD_open_dataset( argv[nopt] ) ; xnam = argv[nopt] ; if( xset == NULL ) ERROR_exit("Can't open dataset %s",argv[nopt]) ; } nvals = DSET_NVALS(xset) ; /* number of time points */ ii = (strcmp(smethod,"dot")==0) ? 2 : 3 ; if( nvals < ii ) ERROR_exit("Input dataset %s length is less than ii?!",xnam,ii) ; if( ysim->nx < nvals ) ERROR_exit("1D file %s has %d time points, but dataset has %d values", ynam,ysim->nx,nvals) ; else if( ysim->nx > nvals ) WARNING_message("1D file %s has %d time points, dataset has %d", ynam,ysim->nx,nvals) ; if( mri_allzero(ysim) ) ERROR_exit("1D file %s is all zero!",ynam) ; if( ysim->ny > 1 ) INFO_message("1D file %s has %d columns: correlating with ALL of them!", ynam,ysim->ny) ; if( strcmp(smethod,"dot") == 0 && do_atanh ){ WARNING_message("'-dot' turns off '-Fisher'") ; do_atanh = 0 ; } if( strcmp(smethod,"dot") == 0 && datum == MRI_short ){ WARNING_message("'-dot' turns off '-short'") ; datum = MRI_float ; } cset = THD_Tcorr1D( xset, mask, nmask, ysim, smethod, prefix, (datum==MRI_short) , do_atanh ); tross_Make_History( "3dTcorr1D" , argc,argv , cset ) ; DSET_unload(xset) ; /* no longer needful */ /* finito */ DSET_write(cset) ; INFO_message("Wrote dataset: %s\n",DSET_BRIKNAME(cset)) ; exit(0) ; }
int main( int argc , char *argv[] ) { int iarg , ct , do_GM=0 ; int do_T2=0 ; float T2_uperc=98.5f ; byte *T2_mask=NULL ; char *prefix = "Unifized" ; THD_3dim_dataset *inset=NULL , *outset=NULL ; MRI_IMAGE *imin , *imout ; float clfrac=0.2f ; AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("\n" "Usage: 3dUnifize [options] inputdataset\n\n" "* The input dataset is supposed to be a T1-weighted volume,\n" " possibly already skull-stripped (e.g., via 3dSkullStrip).\n" " ++ However, this program can be a useful step to take BEFORE\n" " 3dSkullStrip, since the latter program can fail if the input\n" " volume is strongly shaded -- 3dUnifize will (mostly) remove\n" " such shading artifacts.\n" "* The output dataset has the white matter (WM) intensity approximately\n" " uniformized across space, and scaled to peak at about 1000.\n" "* The output dataset is always stored in float format!\n" "* If the input dataset has more than 1 sub-brick, only sub-brick\n" " #0 will be processed!\n" "* Method: Obi-Wan's personal variant of Ziad's sneaky trick.\n" " (If you want to know what his trick is, you'll have to ask him, or\n" " read Obi-Wan's source code, which is a world of ecstasy and exaltation,\n" " or just read all the way to the end of this help output.)\n" "* The principal motive for this program is for use in an image\n" " registration script, and it may or may not be useful otherwise.\n" "\n" "--------\n" "Options:\n" "--------\n" " -prefix pp = Use 'pp' for prefix of output dataset.\n" " -input dd = Alternative way to specify input dataset.\n" " -T2 = Treat the input as if it were T2-weighted, rather than\n" " T1-weighted. This processing is done simply by inverting\n" " the image contrast, processing it as if that result were\n" " T1-weighted, and then re-inverting the results.\n" " ++ This option is NOT guaranteed to be useful for anything!\n" " ++ Of course, nothing in AFNI comes with a guarantee :-)\n" " ++ If you want to be REALLY sneaky, giving this option twice\n" " will skip the second inversion step, so the result will\n" " look like a T1-weighted volume (except at the edges and\n" " near blood vessels).\n" " ++ Might be useful for skull-stripping T2-weighted datasets.\n" " ++ Don't try the '-T2 -T2' trick on FLAIR-T2-weighted datasets.\n" " The results aren't pretty!\n" " -GM = Also scale to unifize 'gray matter' = lower intensity voxels\n" " (to aid in registering images from different scanners).\n" " ++ This option is recommended for use with 3dQwarp when\n" " aligning 2 T1-weighted volumes, in order to make the\n" " WM-GM contrast about the same for the datasets, even\n" " if they don't come from the same scanner/pulse-sequence.\n" " ++ Note that standardizing the contrasts with 3dUnifize will help\n" " 3dQwarp match the source dataset to the base dataset. If you\n" " later want the original source dataset to be warped, you can\n" " do so using the 3dNwarpApply program.\n" " -Urad rr = Sets the radius (in voxels) of the ball used for the sneaky trick.\n" " ++ Default value is %.1f, and should be changed proportionally\n" " if the dataset voxel size differs significantly from 1 mm.\n" " -ssave ss = Save the scale factor used at each voxel into a dataset 'ss'.\n" " ++ This is the white matter scale factor, and does not include\n" " the factor from the '-GM' option (if that was included).\n" " ++ The input dataset is multiplied by the '-ssave' image\n" " (voxel-wise) to get the WM-unifized image.\n" " ++ Another volume (with the same grid dimensions) could be\n" " scaled the same way using 3dcalc, if that is needed.\n" " -quiet = Don't print the fun fun fun progress messages (but whyyyy?).\n" " ++ For the curious, the codes used are:\n" " A = Automask\n" " D = Duplo down (process a half-size volume)\n" " V = Voxel-wise histograms to get local scale factors\n" " U = duplo Up (convert local scale factors to full-size volume)\n" " W = multiply by White matter factors\n" " G = multiply by Gray matter factors [cf the -GM option]\n" " I = contrast inversion [cf the -T2 option]\n" " ++ 'Duplo down' means to scale the input volume to be half the\n" " grid size in each direction for speed when computing the\n" " voxel-wise histograms. The sub-sampling is done using the\n" " median of the central voxel value and its 6 nearest neighbors.\n" "\n" "------------------------------------------\n" "Special options for Jedi AFNI Masters ONLY:\n" "------------------------------------------\n" " -rbt R b t = Specify the 3 parameters for the algorithm, as 3 numbers\n" " following the '-rbt':\n" " R = radius; same as given by option '-Urad' [default=%.1f]\n" " b = bottom percentile of normalizing data range [default=%.1f]\n" " r = top percentile of normalizing data range [default=%.1f]\n" "\n" " -T2up uu = Set the upper percentile point used for T2-T1 inversion.\n" " The default value is 98.5 (for no good reason), and 'uu' is\n" " allowed to be anything between 90 and 100 (inclusive).\n" " ++ The histogram of the data is built, and the uu-th percentile\n" " point value is called 'U'. The contrast inversion is simply\n" " given by output_value = max( 0 , U - input_value ).\n" "\n" " -clfrac cc = Set the automask 'clip level fraction' to 'cc', which\n" " must be a number between 0.1 and 0.9.\n" " A small 'cc' means to make the initial threshold\n" " for clipping (a la 3dClipLevel) smaller, which\n" " will tend to make the mask larger. [default=0.1]\n" " ++ [22 May 2013] The previous version of this program used a\n" " clip level fraction of 0.5, which proved to be too large\n" " for some users, who had images with very strong shading issues.\n" " Thus, the default value for this parameter was lowered to 0.1.\n" " ++ [24 May 2016] The default value for this parameter was\n" " raised to 0.2, since the lower value often left a lot of\n" " noise outside the head on non-3dSkullStrip-ed datasets.\n" " You can still manually set -clfrac to 0.1 if you need to\n" " correct for very large shading artifacts.\n" " ++ If the results of 3dUnifize have a lot of noise outside the head,\n" " then using '-clfrac 0.5' value will probably help.\n" #ifndef USE_ALL_VALS "\n" " -useall = The 'old' way of operating was to use all dataset values\n" " in the local WM histogram. The 'new' way [May 2016] is to\n" " only use positive values. If you want to use the 'old' way,\n" " then this option is what you want.\n" #endif "\n" "-- Feb 2013 - by Obi-Wan Unifobi\n" #ifdef USE_OMP "-- This code uses OpenMP to speed up the slowest part (voxel-wise histograms).\n" #endif , Uprad , Uprad , Upbot , Uptop ) ; printf("\n" "----------------------------------------------------------------------------\n" "HOW IT WORKS (Ziad's sneaky trick is revealed at last! And more.)\n" "----------------------------------------------------------------------------\n" "The basic idea is that white matter in T1-weighted images is reasonably\n" "uniform in intensity, at least when averaged over 'large-ish' regions.\n" "\n" "The first step is to create a local white matter intensity volume.\n" "Around each voxel (inside the volume 'automask'), the ball of values\n" "within a fixed radius (default=18.3 voxels) is extracted and these\n" "numbers are sorted. The values in the high-intensity range of the\n" "histogram (default=70%% to 80%%) are averaged. The result from this\n" "step is a smooth 3D map of the 'white matter intensity' (WMI).\n" "\n" " [The parameters of the above process can be altered with the '-rbt' option.]\n" " [For speed, the WMI map is produced on an image that is half-size in all ]\n" " [directions ('Duplo down'), and then is expanded back to the full-size ]\n" " [volume ('Duplo up'). The automask procedure can be somewhat controlled ]\n" " [via the '-clfrac' option. The default setting is designed to deal with ]\n" " [heavily shaded images, where the WMI varies by a factor of 5 or more over ]\n" " [the image volume. ]\n" "\n" "The second step is to scale the value at every voxel location x in the input\n" "volume by the factor 1000/WMI(x), so that the 'white matter intensity' is\n" "now uniform-ized to be 1000 everywhere. (This is Ziad's 'trick'; it is easy,\n" "works well, and doesn't require fitting some spatial model to the data: the\n" "data provides its own model.)\n" "\n" "If the '-GM' option is used, then this scaled volume is further processed\n" "to make the lower intensity values (presumably gray matter) have a contrast\n" "similar to that from a collection of 3 Tesla MP-RAGE images that were\n" "acquired at the NIH. (This procedure is not Ziad's fault, and should be\n" "blamed on the reclusive Obi-Wan Unifobi.)\n" "\n" "From the WM-uniform-ized volume, the median of all values larger than 1000\n" "is computed; call this value P. P-1000 represents the upward dispersion\n" "of the high-intensity (white matter) voxels in the volume. This value is\n" "'reflected' below 1000 to Q = 1000 - 2*(P-1000), and Q is taken to be the\n" "upper bound for gray matter voxel intensities. A lower bound for gray\n" "matter voxel values is estimated via the 'clip fraction' algorithm as\n" "implemented in program 3dClipLevel; call this lower bound R. The median\n" "of all values between R and Q is computed; call this value G, which is taken\n" "to be a 'typical' gray matter voxel instensity. Then the values z in the\n" "entire volume are linearly scaled by the formula\n" " z_out = (1000-666)/(1000-G) * (z_in-1000) + 1000\n" "so that the WM uniform-ized intensity of 1000 remains at 1000, and the gray\n" "matter median intensity of G is mapped to 666. (Values z_out that end up\n" "negative are set to 0; as a result, some of CSF might end up as 0.)\n" "The value 666 was chosen because it gave results visually comparable to\n" "various NIH-generated 3 Tesla T1-weighted datasets. (Any suggestions that\n" "this value was chosen for other reasons will be treated as 'beastly'.)\n" "\n" "To recap: the WM uniform-ization process provides a linear scaling factor\n" "that varies for each voxel ('local'), while the GM normalization process\n" "uses a global linear scaling. The GM process is optional, and is simply\n" "designed to make the various T1-weighted images look similar.\n" "\n" "-----** CAVEAT **-----\n" "This procedure was primarily developed to aid in 3D registration, especially\n" "when using 3dQwarp, so that the registration algorithms are trying to match\n" "images that are alike. It is *NOT* intended to be used for quantification\n" "purposes, such as Voxel Based Morphometry! That would better be done via\n" "the 3dSeg program, which is far more complicated.\n" "----------------------------------------------------------------------------\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dUnifize main"); machdep(); AFNI_logger("3dUnifize",argc,argv); PRINT_VERSION("3dUnifize") ; ct = NI_clock_time() ; /*-- scan command line --*/ THD_automask_set_clipfrac(0.1f) ; /* 22 May 2013 */ THD_automask_extclip(1) ; /* 19 Dec 2014 */ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strcmp(argv[iarg],"-clfrac") == 0 || strcmp(argv[iarg],"-mfrac") == 0 ){ /* 22 May 2013 */ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; clfrac = (float)strtod( argv[iarg] , NULL ) ; if( clfrac < 0.1f || clfrac > 0.9f ) ERROR_exit("-clfrac value %f is illegal!",clfrac) ; THD_automask_set_clipfrac(clfrac) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; prefix = argv[iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal value after -prefix!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ssave") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; sspref = strdup(argv[iarg]) ; if( !THD_filename_ok(sspref) ) ERROR_exit("Illegal value after -ssave!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-input") == 0 || strcmp(argv[iarg],"-inset") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; if( inset != NULL ) ERROR_exit("Can't use '%s' twice" ,argv[iarg-1]) ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-Urad") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; Uprad = (float)strtod(argv[iarg],NULL) ; if( Uprad < 5.0f || Uprad > 40.0f ) ERROR_exit("Illegal value %f after option -Urad",Uprad) ; iarg++ ; continue ; } #ifndef USE_ALL_VALS if( strcmp(argv[iarg],"-useall") == 0 ){ /* 17 May 2016 */ USE_ALL_VALS = 1 ; iarg++ ; continue ; } #else if( strcmp(argv[iarg],"-useall") == 0 ){ WARNING_message("-useall option is disabled in this version") ; iarg++ ; continue ; } #endif if( strcmp(argv[iarg],"-param") == 0 || /*--- HIDDEN OPTION ---*/ strcmp(argv[iarg],"-rbt" ) == 0 ){ if( ++iarg >= argc-2 ) ERROR_exit("Need 3 arguments (R pb pt) after '%s'",argv[iarg-1]) ; Uprad = (float)strtod(argv[iarg++],NULL) ; Upbot = (float)strtod(argv[iarg++],NULL) ; Uptop = (float)strtod(argv[iarg++],NULL) ; if( Uprad < 5.0f || Uprad > 40.0f || Upbot < 30.0f || Upbot > 80.0f || Uptop <= Upbot || Uptop > 90.0f ) ERROR_exit("Illegal values (R pb pt) after '%s'",argv[iarg-4]) ; continue ; } if( strcasecmp(argv[iarg],"-GM") == 0 ){ do_GM++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-T2") == 0 ){ /* 18 Dec 2014 */ do_T2++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-T2up") == 0 ){ /* 18 Dec 2014 */ T2_uperc = (float)strtod( argv[++iarg] , NULL ) ; if( T2_uperc < 90.0f || T2_uperc > 100.0f ) ERROR_exit("-T2up value is out of range 90..100 :-(") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-quiet") == 0 ){ verb = 0 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-verb") == 0 ){ verb++ ; iarg++ ; continue ; } ERROR_exit("Unknown option: %s\n",argv[iarg]); } /* read input dataset, if not already there */ if( inset == NULL ){ if( iarg >= argc ) ERROR_exit("No dataset name on command line?\n") ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; } if( verb ) fprintf(stderr," + Pre-processing: ") ; /* load input from disk */ DSET_load( inset ) ; CHECK_LOAD_ERROR(inset) ; if( DSET_NVALS(inset) > 1 ) WARNING_message("Only processing sub-brick #0 (out of %d)",DSET_NVALS(inset)) ; /* make a float copy for processing */ imin = mri_to_float( DSET_BRICK(inset,0) ) ; DSET_unload(inset) ; if( imin == NULL ) ERROR_exit("Can't copy input dataset brick?!") ; #if 0 THD_cliplevel_search(imin) ; exit(0) ; /* experimentation only */ #endif THD_automask_set_clipfrac(clfrac) ; /* invert T2? */ if( do_T2 ){ if( verb ) fprintf(stderr,"I") ; T2_mask = mri_automask_image(imin) ; mri_invertcontrast_inplace( imin , T2_uperc , T2_mask ) ; } /* do the actual work */ imout = mri_WMunifize(imin) ; /* local WM scaling */ free(imin) ; if( sspref != NULL && sclim != NULL ){ /* 25 Jun 2013 */ STATUS("output -ssave") ; outset = EDIT_empty_copy( inset ) ; EDIT_dset_items( outset , ADN_prefix , sspref , ADN_nvals , 1 , ADN_ntt , 0 , ADN_none ) ; EDIT_substitute_brick( outset , 0 , MRI_float , MRI_FLOAT_PTR(sclim) ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dUnifize" , argc,argv , outset ) ; DSET_write(outset) ; outset = NULL ; } if( sclim != NULL ){ mri_free(sclim) ; sclim = NULL ; } if( imout == NULL ){ /* this is bad-ositiness */ if( verb ) fprintf(stderr,"\n") ; ERROR_exit("Can't compute Unifize-d dataset for some reason :-(") ; } if( do_GM ) mri_GMunifize(imout) ; /* global GM scaling */ if( do_T2 == 1 ){ /* re-invert T2? */ if( verb ) fprintf(stderr,"I") ; mri_invertcontrast_inplace( imout , T2_uperc , T2_mask ) ; } else if( do_T2 == 2 ){ /* don't re-invert, but clip off bright edges */ mri_clipedges_inplace( imout , PKVAL*1.111f , PKVAL*1.055f ) ; } if( verb ) fprintf(stderr,"\n") ; /* create output dataset, and write it into the historical record */ outset = EDIT_empty_copy( inset ) ; EDIT_dset_items( outset , ADN_prefix , prefix , ADN_nvals , 1 , ADN_ntt , 0 , ADN_none ) ; EDIT_substitute_brick( outset , 0 , MRI_float , MRI_FLOAT_PTR(imout) ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dUnifize" , argc,argv , outset ) ; DSET_write(outset) ; WROTE_DSET(outset) ; DSET_delete(outset) ; DSET_delete(inset) ; /* vamoose the ranch */ if( verb ){ double cput = COX_cpu_time() ; if( cput > 0.05 ) INFO_message("===== CPU time = %.1f sec Elapsed = %.1f\n", COX_cpu_time() , 0.001*(NI_clock_time()-ct) ) ; else INFO_message("===== Elapsed = %.1f sec\n", 0.001*(NI_clock_time()-ct) ) ; } exit(0) ; }
int main(int argc, char *argv[]) { int i,j,k,m,n,mm; int iarg; THD_3dim_dataset *insetTIME = NULL; THD_3dim_dataset *MASK=NULL; THD_3dim_dataset *ROIS=NULL; char *prefix="NETCORR" ; char in_name[300]; char in_mask[300]; char in_rois[300]; char OUT_grid[300]; char OUT_indiv[300]; char OUT_indiv0[300]; // int *SELROI=NULL; // if selecting subset of ROIs // int HAVE_SELROI=0; int NIFTI_OUT = 0; byte ***mskd=NULL; // define mask of where time series are nonzero byte *mskd2=NULL; // not great, but another format of mask int HAVE_MASK=0; int HAVE_ROIS=0; int FISH_OUT=0; int PART_CORR=0; int TS_OUT=0; int TS_LABEL=0; int TS_INDIV=0; int TS_WBCORR_r=0; int TS_WBCORR_Z=0; int *NROI_REF=NULL,*INVROI_REF=NULL; int **ROI_LABELS_REF=NULL, **INV_LABELS_REF=NULL,**ROI_COUNT=NULL; int ***ROI_LISTS=NULL; double ***ROI_AVE_TS=NULL; // double because of GSL float ***Corr_Matr=NULL; float ***PCorr_Matr=NULL, ***PBCorr_Matr=NULL; int Nvox=-1; // tot number vox int *Dim=NULL; int *Nlist=NULL; Dtable *roi_dtable=NULL; char *LabTabStr=NULL; char ***ROI_STR_LABELS=NULL; // for niml.dset -> graph viewing in SUMA char ***gdset_roi_names=NULL; SUMA_DSET *gset=NULL; float ***flat_matr=NULL; float *xyz=NULL; char OUT_gdset[300]; NI_group *GDSET_netngrlink=NULL; char *NAME_gdset=NULL; int Noutmat = 1; // num of matr to output: start with CC for sure char **ParLab=NULL; int FM_ctr = 0; // for counting through flatmatr entries int OLD_LABEL=0; // ooollld style format of regions: Nnumber:Rnumber int IGNORE_LT=0; // ignore label table int idx = 0; int Nmask = 0; FILE *fout1,*fin,*fout2; AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ mainENTRY("3dNetCorr"); machdep(); // **************************************************************** // **************************************************************** // load AFNI stuff // **************************************************************** // **************************************************************** // INFO_message("version: BETA"); /** scan args **/ if (argc == 1) { usage_NetCorr(1); exit(0); } iarg = 1; while( iarg < argc && argv[iarg][0] == '-' ){ if( strcmp(argv[iarg],"-help") == 0 || strcmp(argv[iarg],"-h") == 0 ) { usage_NetCorr(strlen(argv[iarg])>3 ? 2:1); exit(0); } if( strcmp(argv[iarg],"-prefix") == 0 ){ iarg++ ; if( iarg >= argc ) ERROR_exit("Need argument after '-prefix'"); prefix = strdup(argv[iarg]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal name after '-prefix'"); iarg++ ; continue ; } if( strcmp(argv[iarg],"-inset") == 0 ){ iarg++ ; if( iarg >= argc ) ERROR_exit("Need argument after '-input'"); sprintf(in_name,"%s", argv[iarg]); insetTIME = THD_open_dataset(in_name) ; if( (insetTIME == NULL )) ERROR_exit("Can't open time series dataset '%s'.",in_name); // just 0th time point for output... Dim = (int *)calloc(4,sizeof(int)); DSET_load(insetTIME); CHECK_LOAD_ERROR(insetTIME); Nvox = DSET_NVOX(insetTIME) ; Dim[0] = DSET_NX(insetTIME); Dim[1] = DSET_NY(insetTIME); Dim[2] = DSET_NZ(insetTIME); Dim[3]= DSET_NVALS(insetTIME); iarg++ ; continue ; } if( strcmp(argv[iarg],"-mask") == 0 ){ iarg++ ; if( iarg >= argc ) ERROR_exit("Need argument after '-mask'"); HAVE_MASK= 1; sprintf(in_mask,"%s", argv[iarg]); MASK = THD_open_dataset(in_mask) ; if( (MASK == NULL )) ERROR_exit("Can't open time series dataset '%s'.",in_mask); DSET_load(MASK); CHECK_LOAD_ERROR(MASK); iarg++ ; continue ; } if( strcmp(argv[iarg],"-in_rois") == 0 ){ iarg++ ; if( iarg >= argc ) ERROR_exit("Need argument after '-in_rois'"); sprintf(in_rois,"%s", argv[iarg]); ROIS = THD_open_dataset(in_rois) ; if( (ROIS == NULL )) ERROR_exit("Can't open time series dataset '%s'.",in_rois); DSET_load(ROIS); CHECK_LOAD_ERROR(ROIS); HAVE_ROIS=DSET_NVALS(ROIS); //number of subbricks iarg++ ; continue ; } if( strcmp(argv[iarg],"-fish_z") == 0) { FISH_OUT=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-nifti") == 0) { NIFTI_OUT=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-part_corr") == 0) { PART_CORR=2; // because we calculate two matrices here iarg++ ; continue ; } if( strcmp(argv[iarg],"-ts_out") == 0) { TS_OUT=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ts_label") == 0) { TS_LABEL=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ts_indiv") == 0) { TS_INDIV=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ts_wb_corr") == 0) { TS_WBCORR_r=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ts_wb_Z") == 0) { TS_WBCORR_Z=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-old_labels") == 0) { OLD_LABEL=1; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ignore_LT") == 0) { IGNORE_LT=1; iarg++ ; continue ; } /* if( strcmp(argv[iarg],"-sel_roi") == 0 ){ iarg++ ; if( iarg >= argc ) ERROR_exit("Need argument after '-in_rois'"); SELROI = (int *)calloc(MAX_SELROI,sizeof(int)); if( (fin = fopen(argv[iarg], "r")) == NULL) { fprintf(stderr, "Error opening file %s.",argv[iarg]); exit(1); } idx=0; while( !feof(fin) && (idx<MAX_SELROI-1) ){ fscanf(fin, "%d",&SELROI[idx]); fscanf(fin," "); idx++; } HAVE_SELROI=idx; printf("HAVE_SELROI=%d\n",HAVE_SELROI); if(HAVE_SELROI<=0) { ERROR_message("Error reading in `-sel_roi'-- appears to have no ROIs listed.\n"); exit(1); } iarg++ ; continue ; }*/ ERROR_message("Bad option '%s'\n",argv[iarg]) ; suggest_best_prog_option(argv[0], argv[iarg]); exit(1); } INFO_message("Reading in."); if( !TS_OUT && TS_LABEL) { ERROR_message("with '-ts_label', you also need '-ts_out'.\n"); exit(1); } if (iarg < 3) { ERROR_message("Too few options. Try -help for details.\n"); exit(1); } if(!HAVE_ROIS) { ERROR_message("Need to load ROIs with >=1 subbrick...\n"); exit(1); } if(Nvox != DSET_NVOX(ROIS)) { ERROR_message("Data sets of `-inset' and `in_rois' have " "different numbers of voxels per brik!\n"); exit(1); } if( (HAVE_MASK>0) && (Nvox != DSET_NVOX(MASK)) ) { ERROR_message("Data sets of `-inset' and `mask' have " "different numbers of voxels per brik!\n"); exit(1); } // **************************************************************** // **************************************************************** // make storage // **************************************************************** // **************************************************************** Nlist = (int *)calloc(1,sizeof(int)); mskd2 = (byte *)calloc(Nvox,sizeof(byte)); mskd = (byte ***) calloc( Dim[0], sizeof(byte **) ); for ( i = 0 ; i < Dim[0] ; i++ ) mskd[i] = (byte **) calloc( Dim[1], sizeof(byte *) ); for ( i = 0 ; i < Dim[0] ; i++ ) for ( j = 0 ; j < Dim[1] ; j++ ) mskd[i][j] = (byte *) calloc( Dim[2], sizeof(byte) ); if( (mskd == NULL) || (Nlist == NULL) || (mskd2 == NULL)) { fprintf(stderr, "\n\n MemAlloc failure (masks).\n\n"); exit(122); } // ************************************************************* // ************************************************************* // Beginning of main loops // ************************************************************* // ************************************************************* INFO_message("Allocating..."); // go through once: define data vox, and calc rank for each for( k=0 ; k<Dim[2] ; k++ ) for( j=0 ; j<Dim[1] ; j++ ) for( i=0 ; i<Dim[0] ; i++ ) { if( HAVE_MASK ) { if( THD_get_voxel(MASK,idx,0)>0 ) { mskd[i][j][k] = 1; mskd2[idx] = 1; Nmask++; } } else // simple automask attempt if( fabs(THD_get_voxel(insetTIME,idx,0))+ fabs(THD_get_voxel(insetTIME,idx,1))+ fabs(THD_get_voxel(insetTIME,idx,2))+ fabs(THD_get_voxel(insetTIME,idx,3))+ fabs(THD_get_voxel(insetTIME,idx,4)) > EPS_V) { mskd[i][j][k] = 1; mskd2[idx] = 1; Nmask++; } idx+= 1; // skip, and mskd and KW are both still 0 from calloc } if (HAVE_MASK) { DSET_delete(MASK); free(MASK); } // obviously, this should always be TRUE at this point... if(HAVE_ROIS>0) { NROI_REF = (int *)calloc(HAVE_ROIS, sizeof(int)); INVROI_REF = (int *)calloc(HAVE_ROIS, sizeof(int)); if( (NROI_REF == NULL) || (INVROI_REF == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(122); } for( i=0 ; i<HAVE_ROIS ; i++) INVROI_REF[i] = (int) THD_subbrick_max(ROIS, i, 1); ROI_LABELS_REF = calloc( HAVE_ROIS,sizeof(ROI_LABELS_REF)); for(i=0 ; i<HAVE_ROIS ; i++) ROI_LABELS_REF[i] = calloc(INVROI_REF[i]+1,sizeof(int)); INV_LABELS_REF = calloc( HAVE_ROIS,sizeof(INV_LABELS_REF)); for(i=0 ; i<HAVE_ROIS ; i++) INV_LABELS_REF[i] = calloc(INVROI_REF[i]+1,sizeof(int)); if( (ROI_LABELS_REF == NULL) || (INV_LABELS_REF == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } INFO_message("Labelling regions internally."); // Step 3A-2: find out the labels in the ref, organize them // both backwards and forwards. i = ViveLeRoi(ROIS, ROI_LABELS_REF, // ordered list of ROILABEL ints, [1..M]; // maxval is N. INV_LABELS_REF, // ith values at the actual input locs; // maxval is M. NROI_REF, // M: # of ROIs per brik INVROI_REF); // N: max ROI label per brik if( i != 1) ERROR_exit("Problem loading/assigning ROI labels"); ROI_STR_LABELS = (char ***) calloc( HAVE_ROIS, sizeof(char **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) ROI_STR_LABELS[i] = (char **) calloc( NROI_REF[i]+1, sizeof(char *) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i]+1 ; j++ ) ROI_STR_LABELS[i][j] = (char *) calloc( 100 , sizeof(char) ); if( (ROI_STR_LABELS == NULL)) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } // Sept 2014: Labeltable stuff if( IGNORE_LT ) { INFO_message("Ignoring any '-in_rois' label table (if there is one)."); } else{ if ((ROIS->Label_Dtable = DSET_Label_Dtable(ROIS))) { if ((LabTabStr = Dtable_to_nimlstring( DSET_Label_Dtable(ROIS), "VALUE_LABEL_DTABLE"))) { //fprintf(stdout,"%s", LabTabStr); if (!(roi_dtable = Dtable_from_nimlstring(LabTabStr))) { ERROR_exit("Could not parse labeltable."); } } else { INFO_message("No label table from '-in_rois'."); } } } i = Make_ROI_Output_Labels( ROI_STR_LABELS, ROI_LABELS_REF, HAVE_ROIS, NROI_REF, roi_dtable, 1 );//!!!opts.DUMP_with_LABELS ROI_COUNT = calloc( HAVE_ROIS,sizeof(ROI_COUNT)); for(i=0 ; i<HAVE_ROIS ; i++) ROI_COUNT[i] = calloc(NROI_REF[i],sizeof(int)); if( (ROI_COUNT == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } // find num of vox per ROI for( m=0 ; m<HAVE_ROIS ; m++ ) { idx=0; for( k=0 ; k<Dim[2] ; k++ ) for( j=0 ; j<Dim[1] ; j++ ) for( i=0 ; i<Dim[0] ; i++ ) { if( (THD_get_voxel(ROIS,idx,m) > 0 ) && mskd[i][j][k] ) { ROI_COUNT[m][INV_LABELS_REF[m][(int) THD_get_voxel(ROIS,idx,m)]-1]++; } idx++; } } // make list of vox per ROI ROI_LISTS = (int ***) calloc( HAVE_ROIS, sizeof(int **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) ROI_LISTS[i] = (int **) calloc( NROI_REF[i], sizeof(int *) ); for ( i=0 ; i <HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i] ; j++ ) ROI_LISTS[i][j] = (int *) calloc( ROI_COUNT[i][j], sizeof(int) ); // make average time series per voxel ROI_AVE_TS = (double ***) calloc( HAVE_ROIS, sizeof(double **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) ROI_AVE_TS[i] = (double **) calloc( NROI_REF[i], sizeof(double *) ); for ( i=0 ; i <HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i] ; j++ ) ROI_AVE_TS[i][j] = (double *) calloc( Dim[3], sizeof(double) ); // store corr coefs Corr_Matr = (float ***) calloc( HAVE_ROIS, sizeof(float **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) Corr_Matr[i] = (float **) calloc( NROI_REF[i], sizeof(float *) ); for ( i=0 ; i <HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i] ; j++ ) Corr_Matr[i][j] = (float *) calloc( NROI_REF[i], sizeof(float) ); if( (ROI_LISTS == NULL) || (ROI_AVE_TS == NULL) || (Corr_Matr == NULL)) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } if(PART_CORR) { PCorr_Matr = (float ***) calloc( HAVE_ROIS, sizeof(float **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) PCorr_Matr[i] = (float **) calloc( NROI_REF[i], sizeof(float *) ); for ( i=0 ; i <HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i] ; j++ ) PCorr_Matr[i][j] = (float *) calloc( NROI_REF[i], sizeof(float)); PBCorr_Matr = (float ***) calloc( HAVE_ROIS, sizeof(float **) ); for ( i=0 ; i<HAVE_ROIS ; i++ ) PBCorr_Matr[i] = (float **) calloc( NROI_REF[i], sizeof(float *) ); for ( i=0 ; i <HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i] ; j++ ) PBCorr_Matr[i][j] = (float *) calloc( NROI_REF[i], sizeof(float)); if( (PCorr_Matr == NULL) || (PBCorr_Matr == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } } // reuse this to help place list indices for( i=0 ; i<HAVE_ROIS ; i++ ) for( j=0 ; j<NROI_REF[i] ; j++ ) ROI_COUNT[i][j] = 0; INFO_message("Getting volumes."); for( m=0 ; m<HAVE_ROIS ; m++ ) { idx=0; for( k=0 ; k<Dim[2] ; k++ ) for( j=0 ; j<Dim[1] ; j++ ) for( i=0 ; i<Dim[0] ; i++ ) { if( (THD_get_voxel(ROIS,idx,m) > 0) && mskd[i][j][k] ) { mm = INV_LABELS_REF[m][(int) THD_get_voxel(ROIS,idx,m)]-1; ROI_LISTS[m][mm][ROI_COUNT[m][mm]] = idx; ROI_COUNT[m][mm]++; } idx++; } } } // bit of freeing for( i=0 ; i<Dim[0] ; i++) for( j=0 ; j<Dim[1] ; j++) { free(mskd[i][j]); } for( i=0 ; i<Dim[0] ; i++) { free(mskd[i]); } free(mskd); INFO_message("Calculating average time series."); // ROI values for(i=0 ; i<HAVE_ROIS ; i++) for( j=0 ; j<NROI_REF[i] ; j++ ) { Nlist[0]=ROI_COUNT[i][j]; k = CalcAveRTS(ROI_LISTS[i][j], ROI_AVE_TS[i][j], insetTIME, Dim, Nlist); } INFO_message("Calculating correlation matrix."); if(PART_CORR) INFO_message("... and calculating partial correlation matrix."); for(i=0 ; i<HAVE_ROIS ; i++) { for( j=0 ; j<NROI_REF[i] ; j++ ) for( k=j ; k<NROI_REF[i] ; k++ ) { Corr_Matr[i][j][k] = Corr_Matr[i][k][j] = (float) CORR_FUN(ROI_AVE_TS[i][j], ROI_AVE_TS[i][k], Dim[3]); } if(PART_CORR) mm = CalcPartCorrMatr(PCorr_Matr[i], PBCorr_Matr[i], Corr_Matr[i], NROI_REF[i]); } // ************************************************************** // ************************************************************** // Store and output // ************************************************************** // ************************************************************** INFO_message("Writing output: %s ...", prefix); // - - - - - - - - NIML prep - - - - - - - - - - - - - - if(FISH_OUT) Noutmat++; if(PART_CORR) Noutmat+=2; ParLab = (char **)calloc(Noutmat, sizeof(char *)); for (j=0; j<Noutmat; ++j) ParLab[j] = (char *)calloc(32, sizeof(char)); if( (ParLab == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(121); } // NIML output flat_matr = (float ***) calloc( HAVE_ROIS, sizeof(float **) ); for ( i = 0 ; i < HAVE_ROIS ; i++ ) flat_matr[i] = (float **) calloc( Noutmat, sizeof(float *) ); for ( i = 0 ; i < HAVE_ROIS ; i++ ) for ( j = 0 ; j < Noutmat ; j++ ) flat_matr[i][j] = (float *) calloc( NROI_REF[i]*NROI_REF[i], sizeof(float)); gdset_roi_names = (char ***)calloc(HAVE_ROIS, sizeof(char **)); for (i=0; i< HAVE_ROIS ; i++ ) { gdset_roi_names[i] = (char **)calloc(NROI_REF[i], sizeof(char *)); for (j=0; j<NROI_REF[i]; ++j) { gdset_roi_names[i][j] = (char *)calloc(32, sizeof(char)); if( OLD_LABEL ) snprintf(gdset_roi_names[i][j],31,"N%03d:R%d", i, ROI_LABELS_REF[i][j]); else{ snprintf(gdset_roi_names[i][j],31,"%s", ROI_STR_LABELS[i][j+1]); //fprintf(stderr," %s ", // ROI_STR_LABELS[i][j+1]); } } } if( (flat_matr == NULL) || ( gdset_roi_names == NULL) ) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(14); } for( k=0 ; k<HAVE_ROIS ; k++) { // each netw gets own file sprintf(OUT_grid,"%s_%03d.netcc",prefix,k); // zero counting now if( (fout1 = fopen(OUT_grid, "w")) == NULL) { fprintf(stderr, "Error opening file %s.",OUT_grid); exit(19); } // same format as .grid files now fprintf(fout1,"# %d # Number of network ROIs\n",NROI_REF[k]); // NROIs fprintf(fout1,"# %d # Number of netcc matrices\n", FISH_OUT+PART_CORR+1); // Num of params // Sept 2014: label_table stuff // don't need labeltable to make them, can do anyways fprintf(fout1, "# WITH_ROI_LABELS\n"); for( i=1 ; i<NROI_REF[k] ; i++ ) fprintf(fout1," %10s \t",ROI_STR_LABELS[k][i]); fprintf(fout1," %10s\n",ROI_STR_LABELS[k][i]); // THIS IS FOR KNOWING WHICH MATR WE'RE AT // it's always zero for CC; they match one-to-one with later vars FM_ctr = 0; ParLab[FM_ctr] = strdup("CC"); for( i=1 ; i<NROI_REF[k] ; i++ ) // labels of ROIs fprintf(fout1," %10d \t",ROI_LABELS_REF[k][i]);// at =NROI, have '\n' fprintf(fout1," %10d\n# %s\n",ROI_LABELS_REF[k][i],"CC"); for( i=0 ; i<NROI_REF[k] ; i++ ) { for( j=0 ; j<NROI_REF[k]-1 ; j++ ) {// b/c we put '\n' after last one. fprintf(fout1,"%12.4f\t",Corr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = Corr_Matr[k][i][j]; } fprintf(fout1,"%12.4f\n",Corr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = Corr_Matr[k][i][j]; } if(FISH_OUT) { FM_ctr++; ParLab[FM_ctr] = strdup("FZ"); fprintf(fout1,"# %s\n", "FZ"); for( i=0 ; i<NROI_REF[k] ; i++ ) { for( j=0 ; j<NROI_REF[k]-1 ; j++ ) {// b/c we put '\n' after last fprintf(fout1,"%12.4f\t",BOBatanhf(Corr_Matr[k][i][j])); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = BOBatanhf(Corr_Matr[k][i][j]); /* fprintf(fout1,"%12.4f\t",FisherZ(Corr_Matr[k][i][j])); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = FisherZ(Corr_Matr[k][i][j]);*/ } fprintf(fout1,"%12.4f\n",BOBatanhf(Corr_Matr[k][i][j])); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = BOBatanhf(Corr_Matr[k][i][j]); /*fprintf(fout1,"%12.4f\n",FisherZ(Corr_Matr[k][i][j])); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = FisherZ(Corr_Matr[k][i][j]);*/ } } if(PART_CORR) { FM_ctr++; ParLab[FM_ctr] = strdup("PC"); fprintf(fout1,"# %s\n", "PC"); for( i=0 ; i<NROI_REF[k] ; i++ ) { for( j=0 ; j<NROI_REF[k]-1 ; j++ ) {// b/c we put '\n' after last fprintf(fout1,"%12.4f\t",PCorr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = PCorr_Matr[k][i][j]; } fprintf(fout1,"%12.4f\n",PCorr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = PCorr_Matr[k][i][j]; } FM_ctr++; ParLab[FM_ctr] = strdup("PCB"); fprintf(fout1,"# %s\n", "PCB"); for( i=0 ; i<NROI_REF[k] ; i++ ) { for( j=0 ; j<NROI_REF[k]-1 ; j++ ) {// b/c we put '\n' after last fprintf(fout1,"%12.4f\t",PBCorr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = PBCorr_Matr[k][i][j]; } fprintf(fout1,"%12.4f\n",PBCorr_Matr[k][i][j]); flat_matr[k][FM_ctr][i*NROI_REF[k]+j] = PBCorr_Matr[k][i][j]; } } fclose(fout1); // more nimling gset = SUMA_FloatVec_to_GDSET(flat_matr[k], Noutmat, NROI_REF[k]*NROI_REF[k], "full", ParLab, NULL, NULL, NULL); if( xyz = THD_roi_cmass(ROIS, k, ROI_LABELS_REF[k]+1, NROI_REF[k]) ) { if (!(SUMA_AddGDsetNodeListElement(gset, NULL, xyz, NULL, NULL, gdset_roi_names[k], NULL, NULL, NROI_REF[k]))) { ERROR_message("Failed to add node list"); exit(1); } free(xyz); } else { ERROR_message("Failed in THD_roi_cmass"); exit(1); } sprintf(OUT_gdset,"%s_%03d",prefix,k); GDSET_netngrlink = Network_link(SUMA_FnameGet( OUT_gdset, "f",NULL)); NI_add_to_group(gset->ngr, GDSET_netngrlink); NAME_gdset = SUMA_WriteDset_ns( OUT_gdset, gset, SUMA_ASCII_NIML, 1, 0); if (!NAME_gdset && !SUMA_IS_DSET_STDXXX_FORMAT(SUMA_ASCII_NIML)) { ERROR_message("Failed to write dataset."); exit(1); } else { if (NAME_gdset) SUMA_free(NAME_gdset); NAME_gdset = NULL; } SUMA_FreeDset(gset); gset=NULL; } if(TS_OUT) { for( k=0 ; k<HAVE_ROIS ; k++) { // each netw gets own file sprintf(OUT_grid,"%s_%03d.netts",prefix,k); if( (fout1 = fopen(OUT_grid, "w")) == NULL) { fprintf(stderr, "Error opening file %s.",OUT_grid); exit(19); } for( i=0 ; i<NROI_REF[k] ; i++ ) { if(TS_LABEL) fprintf(fout1,"%d\t",ROI_LABELS_REF[k][i+1]); // labels go 1...M for( j=0 ; j<Dim[3]-1 ; j++ ) // b/c we put '\n' after last one. fprintf(fout1,"%.3e\t",ROI_AVE_TS[k][i][j]); fprintf(fout1,"%.3e\n",ROI_AVE_TS[k][i][j]); } fclose(fout1); } } if( TS_INDIV ) { for( k=0 ; k<HAVE_ROIS ; k++) { // each netw gets own file sprintf(OUT_indiv0,"%s_%03d_INDIV", prefix, k); mkdir(OUT_indiv0, 0777); for( i=0 ; i<NROI_REF[k] ; i++ ) { sprintf(OUT_indiv,"%s/ROI_%03d.netts", OUT_indiv0,ROI_LABELS_REF[k][i+1]); if( (fout2 = fopen(OUT_indiv, "w")) == NULL) { fprintf(stderr, "\nError opening file '%s'.\n",OUT_indiv); exit(19); } for( j=0 ; j<Dim[3]-1 ; j++ ) // b/c we put '\n' after last one. fprintf(fout2,"%.3e\t",ROI_AVE_TS[k][i][j]); fprintf(fout2,"%.3e\n",ROI_AVE_TS[k][i][j]); fclose(fout2); } } } if( TS_WBCORR_r || TS_WBCORR_Z ) { INFO_message("Starting whole brain correlations."); i = WB_netw_corr( TS_WBCORR_r, TS_WBCORR_Z, HAVE_ROIS, prefix, NIFTI_OUT, NROI_REF, Dim, ROI_AVE_TS, ROI_LABELS_REF, insetTIME, mskd2, Nmask, argc, argv); } // ************************************************************ // ************************************************************ // Freeing // ************************************************************ // ************************************************************ DSET_delete(ROIS); free(ROIS); for ( i = 0 ; i < HAVE_ROIS ; i++ ) { for (j = 0; j < NROI_REF[i]; ++j) free(gdset_roi_names[i][j]); free(gdset_roi_names[i]); } free(gdset_roi_names); for ( i = 0 ; i < HAVE_ROIS ; i++ ) for ( j = 0 ; j < Noutmat ; j++ ) free(flat_matr[i][j]); for ( i = 0 ; i < HAVE_ROIS ; i++ ) free(flat_matr[i]); free(flat_matr); for( i=0 ; i<Noutmat ; i++) free(ParLab[i]); free(ParLab); if(LabTabStr) free(LabTabStr); if(roi_dtable) free(roi_dtable); for ( i=0 ; i<HAVE_ROIS ; i++ ) for ( j=0 ; j<NROI_REF[i]+1 ; j++ ) free(ROI_STR_LABELS[i][j]); for ( i=0 ; i<HAVE_ROIS ; i++ ) free(ROI_STR_LABELS[i]); free(ROI_STR_LABELS); DSET_delete(insetTIME); free(insetTIME); free(mskd2); free(Nlist); free(Dim); // need to free last because it's used for other arrays... free(prefix); // if(HAVE_SELROI) // free(SELROI); if(HAVE_ROIS >0) { for( i=0 ; i<HAVE_ROIS ; i++) { for( j=0 ; j<NROI_REF[i] ; j++) { free(ROI_LISTS[i][j]); free(ROI_AVE_TS[i][j]); free(Corr_Matr[i][j]); if(PART_CORR) { free(PCorr_Matr[i][j]); free(PBCorr_Matr[i][j]); } } free(ROI_LISTS[i]); free(ROI_AVE_TS[i]); free(Corr_Matr[i]); if(PART_CORR){ free(PCorr_Matr[i]); free(PBCorr_Matr[i]); } free(ROI_LABELS_REF[i]); free(INV_LABELS_REF[i]); free(ROI_COUNT[i]); } free(ROI_LISTS); free(ROI_AVE_TS); free(Corr_Matr); if(PART_CORR) { free(PCorr_Matr); free(PBCorr_Matr); } free(ROI_LABELS_REF); free(INV_LABELS_REF); free(ROI_COUNT); free(NROI_REF); free(INVROI_REF); } return 0; }
int main( int argc , char *argv[] ) { int iarg=1 , ii,nvox , nvals ; THD_3dim_dataset *inset=NULL, *outset=NULL , *mset=NULL ; char *prefix="./blurinmask" ; float fwhm_goal=0.0f ; int fwhm_2D=0 ; byte *mask=NULL ; int mask_nx=0,mask_ny=0,mask_nz=0 , automask=0 , nmask=0 ; float dx,dy,dz=0.0f , *bar , val ; int floatize=0 ; /* 18 May 2009 */ MRI_IMAGE *immask=NULL ; /* 07 Oct 2009 */ short *mmask=NULL ; short *unval_mmask=NULL ; int nuniq_mmask=0 ; int do_preserve=0 , use_qsar ; /* 19 Oct 2009 */ THD_3dim_dataset *fwhmset=NULL ; MRI_IMAGE *fxim=NULL, *fyim=NULL, *fzim=NULL ; /* 13 Jun 2016 */ int niter_fxyz=0 ; float dmax=0.0f , dmin=0.0f ; /*------- help the pitifully ignorant luser? -------*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: ~1~\n" "3dBlurInMask [options]\n" "Blurs a dataset spatially inside a mask. That's all. Experimental.\n" "\n" "OPTIONS ~1~\n" "-------\n" " -input ddd = This required 'option' specifies the dataset\n" " that will be smoothed and output.\n" " -FWHM f = Add 'f' amount of smoothness to the dataset (in mm).\n" " **N.B.: This is also a required 'option'.\n" " -FWHMdset d = Read in dataset 'd' and add the amount of smoothness\n" " given at each voxel -- spatially variable blurring.\n" " ** EXPERIMENTAL EXPERIMENTAL EXPERIMENTAL **\n" " -mask mmm = Mask dataset, if desired. Blurring will\n" " occur only within the mask. Voxels NOT in\n" " the mask will be set to zero in the output.\n" " -Mmask mmm = Multi-mask dataset -- each distinct nonzero\n" " value in dataset 'mmm' will be treated as\n" " a separate mask for blurring purposes.\n" " **N.B.: 'mmm' must be byte- or short-valued!\n" " -automask = Create an automask from the input dataset.\n" " **N.B.: only 1 masking option can be used!\n" " -preserve = Normally, voxels not in the mask will be\n" " set to zero in the output. If you want the\n" " original values in the dataset to be preserved\n" " in the output, use this option.\n" " -prefix ppp = Prefix for output dataset will be 'ppp'.\n" " **N.B.: Output dataset is always in float format.\n" " -quiet = Don't be verbose with the progress reports.\n" " -float = Save dataset as floats, no matter what the\n" " input data type is.\n" " **N.B.: If the input dataset is unscaled shorts, then\n" " the default is to save the output in short\n" " format as well. In EVERY other case, the\n" " program saves the output as floats. Thus,\n" " the ONLY purpose of the '-float' option is to\n" " force an all-shorts input dataset to be saved\n" " as all-floats after blurring.\n" "\n" "NOTES ~1~\n" "-----\n" " * If you don't provide a mask, then all voxels will be included\n" " in the blurring. (But then why are you using this program?)\n" " * Note that voxels inside the mask that are not contiguous with\n" " any other voxels inside the mask will not be modified at all!\n" " * Works iteratively, similarly to 3dBlurToFWHM, but without\n" " the extensive overhead of monitoring the smoothness.\n" " * But this program will be faster than 3dBlurToFWHM, and probably\n" " slower than 3dmerge.\n" " * Since the blurring is done iteratively, rather than all-at-once as\n" " in 3dmerge, the results will be slightly different than 3dmerge's,\n" " even if no mask is used here (3dmerge, of course, doesn't take a mask).\n" " * If the original FWHM of the dataset was 'S' and you input a value\n" " 'F' with the '-FWHM' option, then the output dataset's smoothness\n" " will be about sqrt(S*S+F*F). The number of iterations will be\n" " about (F*F/d*d) where d=grid spacing; this means that a large value\n" " of F might take a lot of CPU time!\n" " * The spatial smoothness of a 3D+time dataset can be estimated with a\n" " command similar to the following:\n" " 3dFWHMx -detrend -mask mmm+orig -input ddd+orig\n" ) ; printf( " * The minimum number of voxels in the mask is %d\n",MASK_MIN) ; printf( " * Isolated voxels will be removed from the mask!\n") ; PRINT_AFNI_OMP_USAGE("3dBlurInMask",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } /*---- official startup ---*/ PRINT_VERSION("3dBlurInMask"); mainENTRY("3dBlurInMask main"); machdep(); AFNI_logger("3dBlurInMask",argc,argv); AUTHOR("RW Cox") ; /*---- loop over options ----*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-preserve",5) == 0 ){ /* 19 Oct 2009 */ do_preserve = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-qui",4) == 0 ){ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-ver",4) == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-input") == 0 || strcmp(argv[iarg],"-dset") == 0 ){ if( inset != NULL ) ERROR_exit("Can't have two -input options") ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-input'") ; inset = THD_open_dataset( argv[iarg] ); CHECK_OPEN_ERROR(inset,argv[iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-prefix'") ; prefix = strdup(argv[iarg]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Bad name after '-prefix'") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-Mmask") == 0 ){ /* 07 Oct 2009 */ if( ++iarg >= argc ) ERROR_exit("Need argument after '-Mmask'") ; if( mmask != NULL || mask != NULL || automask ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(mset,argv[iarg]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); #if 0 if( !MRI_IS_INT_TYPE(DSET_BRICK_TYPE(mset,0)) ) ERROR_exit("-Mmask dataset is not integer type!") ; #endif immask = mri_to_short( 1.0 , DSET_BRICK(mset,0) ) ; mmask = MRI_SHORT_PTR(immask) ; unval_mmask = UniqueShort( mmask, mask_nx*mask_ny*mask_nz, &nuniq_mmask, 0 ) ; if( unval_mmask == NULL || nuniq_mmask == 0 ) ERROR_exit("-Mmask dataset cannot be processed!?") ; if( nuniq_mmask == 1 && unval_mmask[0] == 0 ) ERROR_exit("-Mmask dataset is all zeros!?") ; if( verb ){ int qq , ww ; for( ii=qq=0 ; ii < nuniq_mmask ; ii++ ) if( unval_mmask[ii] != 0 ) qq++ ; for( ii=ww=0 ; ii < immask->nvox ; ii++ ) if( mmask[ii] != 0 ) ww++ ; INFO_message("%d unique nonzero values in -Mmask; %d nonzero voxels",qq,ww) ; } iarg++ ; continue ; } if( strcmp(argv[iarg],"-mask") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-mask'") ; if( mmask != NULL || mask != NULL || automask ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(mset,argv[iarg]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); mask = THD_makemask( mset , 0 , 0.5f, 0.0f ) ; DSET_unload(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%s'",argv[iarg]) ; ii = THD_mask_remove_isolas( mask_nx,mask_ny,mask_nz , mask ) ; if( verb && ii > 0 ) INFO_message("Removed %d isola%s from mask dataset",ii,(ii==1)?"\0":"s") ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; if( verb ) INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < MASK_MIN ) ERROR_exit("Mask is too small to process") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-automask") == 0 ){ if( mmask != NULL || mask != NULL ) ERROR_exit("Can't have 2 mask inputs") ; automask = 1 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-FWHM") == 0 || strcasecmp(argv[iarg],"-FHWM") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); val = (float)strtod(argv[iarg],NULL) ; if( val <= 0.0f ) ERROR_exit("Illegal value after '%s': '%s'", argv[iarg-1],argv[iarg]) ; fwhm_goal = val ; fwhm_2D = 0 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-FWHMdset") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); if( fwhmset != NULL ) ERROR_exit("You can't use option '-FWHMdset' twice :(") ; fwhmset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(fwhmset,argv[iarg]) ; do_preserve = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-float",6) == 0 ){ /* 18 May 2009 */ floatize = 1 ; iarg++ ; continue ; } #if 0 if( strcmp(argv[iarg],"-FWHMxy") == 0 || strcmp(argv[iarg],"-FHWMxy") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); val = (float)strtod(argv[iarg],NULL) ; if( val <= 0.0f ) ERROR_exit("Illegal value after '%s': '%s'", argv[iarg-1],argv[iarg]) ; fwhm_goal = val ; fwhm_2D = 1 ; iarg++ ; continue ; } #endif ERROR_exit("Uknown option '%s'",argv[iarg]) ; } /*--- end of loop over options ---*/ /*----- check for stupid inputs, load datasets, et cetera -----*/ if( fwhmset == NULL && fwhm_goal == 0.0f ) ERROR_exit("No -FWHM option given! What do you want?") ; if( fwhmset != NULL && fwhm_goal > 0.0f ){ WARNING_message("-FWHMdset option replaces -FWHM value") ; fwhm_goal = 0.0f ; } if( fwhmset != NULL && mmask != NULL ) ERROR_exit("Sorry: -FWHMdset and -Mmask don't work together (yet)") ; if( inset == NULL ){ if( iarg >= argc ) ERROR_exit("No input dataset on command line?") ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; } nvox = DSET_NVOX(inset) ; dx = fabs(DSET_DX(inset)) ; if( dx == 0.0f ) dx = 1.0f ; dy = fabs(DSET_DY(inset)) ; if( dy == 0.0f ) dy = 1.0f ; dz = fabs(DSET_DZ(inset)) ; if( dz == 0.0f ) dz = 1.0f ; dmax = MAX(dx,dy) ; if( dmax < dz ) dmax = dz ; /* 13 Jun 2016 */ dmin = MIN(dx,dy) ; if( dmin > dz ) dmin = dz ; if( !floatize ){ /* 18 May 2009 */ if( !THD_datum_constant(inset->dblk) || THD_need_brick_factor(inset) || DSET_BRICK_TYPE(inset,0) != MRI_short ){ if( verb ) INFO_message("forcing output to be stored in float format") ; floatize = 1 ; } else { if( verb ) INFO_message("output dataset will be stored as shorts") ; } } else { if( verb ) INFO_message("output dataset will be stored as floats") ; } #if 0 if( DSET_NZ(inset) == 1 && !fwhm_2D ){ WARNING_message("Dataset is 2D ==> switching from -FWHM to -FWHMxy") ; fwhm_2D = 1 ; } #endif /*--- deal with mask or automask ---*/ if( mask != NULL ){ if( mask_nx != DSET_NX(inset) || mask_ny != DSET_NY(inset) || mask_nz != DSET_NZ(inset) ) ERROR_exit("-mask dataset grid doesn't match input dataset") ; } else if( automask ){ mask = THD_automask( inset ) ; if( mask == NULL ) ERROR_message("Can't create -automask from input dataset?") ; nmask = THD_countmask( DSET_NVOX(inset) , mask ) ; if( verb ) INFO_message("Number of voxels in automask = %d",nmask); if( nmask < MASK_MIN ) ERROR_exit("Automask is too small to process") ; } else if( mmask != NULL ){ if( mask_nx != DSET_NX(inset) || mask_ny != DSET_NY(inset) || mask_nz != DSET_NZ(inset) ) ERROR_exit("-Mmask dataset grid doesn't match input dataset") ; } else { mask = (byte *)malloc(sizeof(byte)*nvox) ; nmask = nvox ; memset(mask,1,sizeof(byte)*nvox) ; if( verb ) INFO_message("No mask ==> processing all %d voxels",nvox); } /*--- process FWHMdset [13 Jun 2016] ---*/ if( fwhmset != NULL ){ float *fxar,*fyar,*fzar , *fwar ; MRI_IMAGE *fwim ; float fwmax=0.0f , fsx,fsy,fsz ; int ii, nfpos=0 ; if( DSET_NX(inset) != DSET_NX(fwhmset) || DSET_NY(inset) != DSET_NY(fwhmset) || DSET_NZ(inset) != DSET_NZ(fwhmset) ) ERROR_exit("grid dimensions for FWHMdset and input dataset do not match :(") ; STATUS("get fwim") ; DSET_load(fwhmset) ; fwim = mri_scale_to_float(DSET_BRICK_FACTOR(fwhmset,0),DSET_BRICK(fwhmset,0)); fwar = MRI_FLOAT_PTR(fwim); DSET_unload(fwhmset) ; STATUS("process fwar") ; for( ii=0 ; ii < nvox ; ii++ ){ if( mask[ii] && fwar[ii] > 0.0f ){ nfpos++ ; if( fwar[ii] > fwmax ) fwmax = fwar[ii] ; } else { fwar[ii] = 0.0f ; mask[ii] = 0 ; } } if( nfpos < 100 ) ERROR_exit("Cannot proceed: too few (%d) voxels are positive in -FWHMdset!",nfpos) ; niter_fxyz = (int)rintf(2.0f*fwmax*fwmax*FFAC/(0.05f*dmin*dmin)) + 1 ; if( verb ) INFO_message("-FWHMdset: niter=%d npos=%d",niter_fxyz,nfpos) ; STATUS("create fxim etc.") ; fxim = mri_new_conforming(fwim,MRI_float); fxar = MRI_FLOAT_PTR(fxim); fyim = mri_new_conforming(fwim,MRI_float); fyar = MRI_FLOAT_PTR(fyim); fzim = mri_new_conforming(fwim,MRI_float); fzar = MRI_FLOAT_PTR(fzim); fsx = FFAC/(dx*dx*niter_fxyz) ; fsy = FFAC/(dy*dy*niter_fxyz) ; fsz = FFAC/(dz*dz*niter_fxyz) ; /** INFO_message("fsx=%g fsy=%g fsz=%g",fsx,fsy,fsz) ; **/ for( ii=0 ; ii < nvox ; ii++ ){ if( fwar[ii] > 0.0f ){ fxar[ii] = fwar[ii]*fwar[ii] * fsx ; fyar[ii] = fwar[ii]*fwar[ii] * fsy ; fzar[ii] = fwar[ii]*fwar[ii] * fsz ; } else { fxar[ii] = fyar[ii] = fzar[ii] = 0.0f ; } } STATUS("free(fwim)") ; mri_free(fwim) ; } /*--- process input dataset ---*/ STATUS("load input") ; DSET_load(inset) ; CHECK_LOAD_ERROR(inset) ; outset = EDIT_empty_copy( inset ) ; /* moved here 04 Jun 2007 */ EDIT_dset_items( outset , ADN_prefix , prefix , ADN_none ) ; EDIT_dset_items( outset , ADN_brick_fac , NULL , ADN_none ) ; /* 11 Sep 2007 */ tross_Copy_History( inset , outset ) ; tross_Make_History( "3dBlurInMask" , argc,argv , outset ) ; nvals = DSET_NVALS(inset) ; use_qsar = (do_preserve || mmask != NULL) ; /* 19 Oct 20090 */ AFNI_OMP_START ; #pragma omp parallel if( nvals > 1 ) { MRI_IMAGE *dsim ; int ids,qit ; byte *qmask=NULL ; register int vv ; MRI_IMAGE *qim=NULL, *qsim=NULL; float *qar=NULL, *dsar, *qsar=NULL; #pragma omp critical (MALLOC) { if( use_qsar ){ qsim = mri_new_conforming(DSET_BRICK(inset,0),MRI_float); qsar = MRI_FLOAT_PTR(qsim); } if( mmask != NULL ){ qmask = (byte *)malloc(sizeof(byte)*nvox) ; qim = mri_new_conforming(immask,MRI_float); qar = MRI_FLOAT_PTR(qim); qim->dx = dx ; qim->dy = dy ; qim->dz = dz ; } } #pragma omp for for( ids=0 ; ids < nvals ; ids++ ){ #pragma omp critical (MALLOC) { dsim = mri_scale_to_float(DSET_BRICK_FACTOR(inset,ids),DSET_BRICK(inset,ids)); DSET_unload_one(inset,ids) ; } dsim->dx = dx ; dsim->dy = dy ; dsim->dz = dz ; dsar = MRI_FLOAT_PTR(dsim) ; /* if needed, initialize qsar with data to be preserved in output */ if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) qsar[vv] = dsar[vv] ; } else if( mmask != NULL ){ for( vv=0 ; vv < nvox ; vv++ ) qsar[vv] = 0.0f ; } if( fwhmset != NULL ){ /* 13 Jun 2016: spatially variable blurring */ for( qit=0 ; qit < niter_fxyz ; qit++ ){ mri_blur3D_variable( dsim , mask , fxim,fyim,fzim ) ; } if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) if( mask[vv] ) qsar[vv] = dsar[vv] ; } } else if( mmask != NULL ){ /* 07 Oct 2009: multiple masks */ int qq ; register short uval ; for( qq=0 ; qq < nuniq_mmask ; qq++ ){ uval = unval_mmask[qq] ; if( uval == 0 ) continue ; for( vv=0 ; vv < nvox ; vv++ ) qmask[vv] = (mmask[vv]==uval) ; /* make mask */ (void)THD_mask_remove_isolas( mask_nx,mask_ny,mask_nz , qmask ) ; nmask = THD_countmask( nvox , qmask ) ; if( verb && ids==0 ) ININFO_message("voxels in Mmask[%d] = %d",uval,nmask) ; if( nmask >= MASK_MIN ){ /* copy data from dataset to qar */ for( vv=0 ; vv < nvox ; vv++ ) if( qmask[vv] ) qar[vv] = dsar[vv] ; /* blur qar (output will be zero where qmask==0) */ mri_blur3D_addfwhm( qim , qmask , fwhm_goal ) ; /** the real work **/ /* copy results back to qsar */ for( vv=0 ; vv < nvox ; vv++ ) if( qmask[vv] ) qsar[vv] = qar[vv] ; } } } else { /* the olden way: 1 mask */ mri_blur3D_addfwhm( dsim , mask , fwhm_goal ) ; /** all the work **/ /* dsim will be zero where mask==0; if we want to preserve the input values, copy dsar into qsar now at all mask!=0 voxels, since qsar contains the original data values */ if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) if( mask[vv] ) qsar[vv] = dsar[vv] ; } } /* if necessary, copy combined results in qsar to dsar for output */ if( use_qsar ){ for( vv=0 ; vv < nvox ; vv++ ) dsar[vv] = qsar[vv] ; } if( floatize ){ EDIT_substitute_brick( outset , ids , MRI_float , dsar ) ; } else { #pragma omp critical (MALLOC) { EDIT_substscale_brick( outset , ids , MRI_float , dsar , MRI_short , 1.0f ) ; mri_free(dsim) ; } } } /* end of loop over sub-bricks */ #pragma omp critical (MALLOC) { if( qsim != NULL ) mri_free(qsim); if( immask != NULL ){ free(qmask); mri_free(qim); } } } /* end OpenMP */ AFNI_OMP_END ; if( mask != NULL ) free( mask) ; if( immask != NULL ) mri_free(immask) ; DSET_unload(inset) ; DSET_write(outset) ; WROTE_DSET(outset) ; exit(0) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *dset , *oset=NULL , *tset=NULL ; int nvals , iv , nxyz , ii,jj,kk , iarg , kz,kzold ; float cut1=2.5,cut2=4.0 , sq2p,sfac , fq ; MRI_IMAGE *flim ; char *prefix="despike" , *tprefix=NULL ; int corder=-1 , nref , ignore=0 , polort=2 , nuse , nomask=0 ; int nspike, nbig, nproc ; float **ref ; float c21,ic21 , pspike,pbig ; short *sar , *qar ; byte *tar , *mask=NULL ; float *zar , *yar ; int datum ; int localedit=0 ; /* 04 Apr 2007 */ int verb=1 ; int do_NEW = 0 ; /* 29 Nov 2013 */ MRI_IMAGE *NEW_psinv=NULL ; int dilate = 4 ; /* 04 Dec 2013 */ int ctim = 0 ; /*----- Read command line -----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dDespike [options] dataset\n" "Removes 'spikes' from the 3D+time input dataset and writes\n" "a new dataset with the spike values replaced by something\n" "more pleasing to the eye.\n" "\n" "Method:\n" " * L1 fit a smooth-ish curve to each voxel time series\n" " [see -corder option for description of the curve]\n" " [see -NEW option for a different & faster fitting method]\n" " * Compute the MAD of the difference between the curve and\n" " the data time series (the residuals).\n" " * Estimate the standard deviation 'sigma' of the residuals\n" " as sqrt(PI/2)*MAD.\n" " * For each voxel value, define s = (value-curve)/sigma.\n" " * Values with s > c1 are replaced with a value that yields\n" " a modified s' = c1+(c2-c1)*tanh((s-c1)/(c2-c1)).\n" " * c1 is the threshold value of s for a 'spike' [default c1=2.5].\n" " * c2 is the upper range of the allowed deviation from the curve:\n" " s=[c1..infinity) is mapped to s'=[c1..c2) [default c2=4].\n" "\n" "Options:\n" " -ignore I = Ignore the first I points in the time series:\n" " these values will just be copied to the\n" " output dataset [default I=0].\n" " -corder L = Set the curve fit order to L:\n" " the curve that is fit to voxel data v(t) is\n" "\n" " k=L [ (2*PI*k*t) (2*PI*k*t) ]\n" " f(t) = a+b*t+c*t*t + SUM [ d * sin(--------) + e * cos(--------) ]\n" " k=1 [ k ( T ) k ( T ) ]\n" "\n" " where T = duration of time series;\n" " the a,b,c,d,e parameters are chosen to minimize\n" " the sum over t of |v(t)-f(t)| (L1 regression);\n" " this type of fitting is is insensitive to large\n" " spikes in the data. The default value of L is\n" " NT/30, where NT = number of time points.\n" "\n" " -cut c1 c2 = Alter default values for the spike cut values\n" " [default c1=2.5, c2=4.0].\n" " -prefix pp = Save de-spiked dataset with prefix 'pp'\n" " [default pp='despike']\n" " -ssave ttt = Save 'spikiness' measure s for each voxel into a\n" " 3D+time dataset with prefix 'ttt' [default=no save]\n" " -nomask = Process all voxels\n" " [default=use a mask of high-intensity voxels, ]\n" " [as created via '3dAutomask -dilate 4 dataset'].\n" " -dilate nd = Dilate 'nd' times (as in 3dAutomask). The default\n" " value of 'nd' is 4.\n" " -q[uiet] = Don't print '++' informational messages.\n" "\n" " -localedit = Change the editing process to the following:\n" " If a voxel |s| value is >= c2, then replace\n" " the voxel value with the average of the two\n" " nearest non-spike (|s| < c2) values; the first\n" " one previous and the first one after.\n" " Note that the c1 cut value is not used here.\n" "\n" " -NEW = Use the 'new' method for computing the fit, which\n" " should be faster than the L1 method for long time\n" " series (200+ time points); however, the results\n" " are similar but NOT identical. [29 Nov 2013]\n" " * You can also make the program use the 'new'\n" " method by setting the environment variable\n" " AFNI_3dDespike_NEW\n" " to the value YES; as in\n" " setenv AFNI_3dDespike_NEW YES (csh)\n" " export AFNI_3dDespike_NEW=YES (bash)\n" " * If this variable is set to YES, you can turn off\n" " the '-NEW' processing by using the '-OLD' option.\n" " -->>* For time series more than 500 points long, the\n" " '-OLD' algorithm is tremendously slow. You should\n" " use the '-NEW' algorith in such cases.\n" " ** At some indeterminate point in the future, the '-NEW'\n" " method will become the default!\n" " -->>* As of 29 Sep 2016, '-NEW' is the default if there\n" " is more than 500 points in the time series dataset.\n" "\n" " -NEW25 = A slightly more aggressive despiking approach than\n" " the '-NEW' method.\n" "\n" "Caveats:\n" "* Despiking may interfere with image registration, since head\n" " movement may produce 'spikes' at the edge of the brain, and\n" " this information would be used in the registration process.\n" " This possibility has not been explored or calibrated.\n" "* [LATER] Actually, it seems like the registration problem\n" " does NOT happen, and in fact, despiking seems to help!\n" "* Check your data visually before and after despiking and\n" " registration!\n" " [Hint: open 2 AFNI controllers, and turn Time Lock on.]\n" ) ; PRINT_AFNI_OMP_USAGE("3dDespike",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } /** AFNI package setup and logging **/ mainENTRY("3dDespike main"); machdep(); AFNI_logger("3dDespike",argc,argv); PRINT_VERSION("3dDespike") ; AUTHOR("RW Cox") ; /** parse options **/ if( AFNI_yesenv("AFNI_3dDespike_NEW") ) do_NEW = 1 ; /* 29 Nov 2013 */ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-q",2) == 0 ){ /* 04 Apr 2007 */ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-v",2) == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW") == 0 ){ /* 29 Nov 2013 */ do_NEW = 1 ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW25") == 0 ){ /* 29 Sep 2016 */ do_NEW = 1 ; use_des25 = 1 ; cut1 = 2.5f ; cut2 = 3.2f ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-OLD") == 0 ){ do_NEW = 0 ; iarg++ ; continue ; } /** -localedit **/ if( strcmp(argv[iarg],"-localedit") == 0 ){ /* 04 Apr 2007 */ localedit = 1 ; iarg++ ; continue ; } /** don't use masking **/ if( strcmp(argv[iarg],"-nomask") == 0 ){ nomask = 1 ; iarg++ ; continue ; } /** dilation count [04 Dec 2013] **/ if( strcmp(argv[iarg],"-dilate") == 0 ){ dilate = (int)strtod(argv[++iarg],NULL) ; if( dilate <= 0 ) dilate = 1 ; else if( dilate > 99 ) dilate = 99 ; iarg++ ; continue ; } /** output dataset prefix **/ if( strcmp(argv[iarg],"-prefix") == 0 ){ prefix = argv[++iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("-prefix is not good"); iarg++ ; continue ; } /** ratio dataset prefix **/ if( strcmp(argv[iarg],"-ssave") == 0 ){ tprefix = argv[++iarg] ; if( !THD_filename_ok(tprefix) ) ERROR_exit("-ssave prefix is not good"); iarg++ ; continue ; } /** trigonometric polynomial order **/ if( strcmp(argv[iarg],"-corder") == 0 ){ corder = strtol( argv[++iarg] , NULL , 10 ) ; if( corder < 0 ) ERROR_exit("Illegal value of -corder"); iarg++ ; continue ; } /** how much to ignore at start **/ if( strcmp(argv[iarg],"-ignore") == 0 ){ ignore = strtol( argv[++iarg] , NULL , 10 ) ; if( ignore < 0 ) ERROR_exit("Illegal value of -ignore"); iarg++ ; continue ; } /** thresholds for s ratio **/ if( strcmp(argv[iarg],"-cut") == 0 ){ cut1 = strtod( argv[++iarg] , NULL ) ; cut2 = strtod( argv[++iarg] , NULL ) ; if( cut1 < 1.0 || cut2 < cut1+0.5 ) ERROR_exit("Illegal values after -cut"); iarg++ ; continue ; } ERROR_exit("Unknown option: %s",argv[iarg]) ; } c21 = cut2-cut1 ; ic21 = 1.0/c21 ; /*----- read input dataset -----*/ if( iarg >= argc ) ERROR_exit("No input dataset!!??"); dset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(dset,argv[iarg]) ; datum = DSET_BRICK_TYPE(dset,0) ; if( (datum != MRI_short && datum != MRI_float) || !DSET_datum_constant(dset) ) ERROR_exit("Can't process non-short, non-float dataset!") ; if( verb ) INFO_message("Input data type = %s\n",MRI_TYPE_name[datum]) ; nvals = DSET_NUM_TIMES(dset) ; nuse = nvals - ignore ; if( nuse < 15 ) ERROR_exit("Can't use dataset with < 15 time points per voxel!") ; if( nuse > 500 && !do_NEW ){ INFO_message("Switching to '-NEW' method since number of time points = %d > 500",nuse) ; do_NEW = 1 ; } if( use_des25 && nuse < 99 ) use_des25 = 0 ; if( verb ) INFO_message("ignoring first %d time points, using last %d",ignore,nuse); if( corder > 0 && 4*corder+2 > nuse ){ ERROR_exit("-corder %d is too big for NT=%d",corder,nvals) ; } else if( corder < 0 ){ corder = rint(nuse/30.0) ; if( corder > 50 && !do_NEW ) corder = 50 ; if( verb ) INFO_message("using %d time points => -corder %d",nuse,corder) ; } else { if( verb ) INFO_message("-corder %d set from command line",corder) ; } nxyz = DSET_NVOX(dset) ; if( verb ) INFO_message("Loading dataset %s",argv[iarg]) ; DSET_load(dset) ; CHECK_LOAD_ERROR(dset) ; /*-- create automask --*/ if( !nomask ){ mask = THD_automask( dset ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } for( ii=0 ; ii < dilate ; ii++ ) THD_mask_dilate( DSET_NX(dset), DSET_NY(dset), DSET_NZ(dset), mask, 3 ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the dilated automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } } else { if( verb ) INFO_message("processing all %d voxels in dataset",DSET_NVOX(dset)) ; } /*-- create empty despiked dataset --*/ oset = EDIT_empty_copy( dset ) ; EDIT_dset_items( oset , ADN_prefix , prefix , ADN_brick_fac , NULL , ADN_datum_all , datum , ADN_none ) ; if( THD_deathcon() && THD_is_file(DSET_HEADNAME(oset)) ) ERROR_exit("output dataset already exists: %s",DSET_HEADNAME(oset)); tross_Copy_History( oset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , oset ) ; /* create bricks (will be filled with zeros) */ for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( oset , iv , datum , NULL ) ; /* copy the ignored bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < ignore ; iv++ ){ sar = DSET_ARRAY(oset,iv) ; qar = DSET_ARRAY(dset,iv) ; memcpy( sar , qar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; case MRI_float: for( iv=0 ; iv < ignore ; iv++ ){ zar = DSET_ARRAY(oset,iv) ; yar = DSET_ARRAY(dset,iv) ; memcpy( zar , yar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; } /*-- setup to save a threshold statistic dataset, if desired --*/ if( tprefix != NULL ){ float *fac ; tset = EDIT_empty_copy( dset ) ; fac = (float *) malloc( sizeof(float) * nvals ) ; for( ii=0 ; ii < nvals ; ii++ ) fac[ii] = TFAC ; EDIT_dset_items( tset , ADN_prefix , tprefix , ADN_brick_fac , fac , ADN_datum_all , MRI_byte , ADN_func_type , FUNC_FIM_TYPE , ADN_none ) ; free(fac) ; tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; #if 0 if( THD_is_file(DSET_HEADNAME(tset)) ) ERROR_exit("-ssave dataset already exists"); #endif tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( tset , iv , MRI_byte , NULL ) ; } /*-- setup to find spikes --*/ sq2p = sqrt(0.5*PI) ; sfac = sq2p / 1.4826f ; /* make ref functions */ nref = 2*corder+3 ; ref = (float **) malloc( sizeof(float *) * nref ) ; for( jj=0 ; jj < nref ; jj++ ) ref[jj] = (float *) malloc( sizeof(float) * nuse ) ; /* r(t) = 1 */ for( iv=0 ; iv < nuse ; iv++ ) ref[0][iv] = 1.0 ; jj = 1 ; /* r(t) = t - tmid */ { float tm = 0.5 * (nuse-1.0) ; float fac = 2.0 / nuse ; for( iv=0 ; iv < nuse ; iv++ ) ref[1][iv] = (iv-tm)*fac ; jj = 2 ; /* r(t) = (t-tmid)**jj */ for( ; jj <= polort ; jj++ ) for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = pow( (iv-tm)*fac , (double)jj ) ; } for( kk=1 ; kk <= corder ; kk++ ){ fq = (2.0*PI*kk)/nuse ; /* r(t) = sin(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = sin(fq*iv) ; jj++ ; /* r(t) = cos(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = cos(fq*iv) ; jj++ ; } /****** setup for the NEW solution method [29 Nov 2013] ******/ if( do_NEW ){ NEW_psinv = DES_get_psinv(nuse,nref,ref) ; INFO_message("Procesing time series with NEW model fit algorithm") ; } else { INFO_message("Procesing time series with OLD model fit algorithm") ; } /*--- loop over voxels and do work ---*/ #define Laplace_t2p(val) ( 1.0 - nifti_stat2cdf( (val), 15, 0.0, 1.4427 , 0.0 ) ) if( verb ){ if( !localedit ){ INFO_message("smash edit thresholds: %.1f .. %.1f MADs",cut1*sq2p,cut2*sq2p) ; ININFO_message(" [ %.3f%% .. %.3f%% of normal distribution]", 200.0*qg(cut1*sfac) , 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% .. %.3f%% of Laplace distribution]" , 100.0*Laplace_t2p(cut1) , 100.0*Laplace_t2p(cut2) ) ; } else { INFO_message("local edit threshold: %.1f MADS",cut2*sq2p) ; ININFO_message(" [ %.3f%% of normal distribution]", 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% of Laplace distribution]", 100.0*Laplace_t2p(cut1) ) ; } INFO_message("%d slices to process",DSET_NZ(dset)) ; } kzold = -1 ; nspike = 0 ; nbig = 0 ; nproc = 0 ; ctim = NI_clock_time() ; AFNI_OMP_START ; #pragma omp parallel if( nxyz > 6666 ) { int ii , iv , iu , id , jj ; float *far , *dar , *var , *fitar , *ssp , *fit , *zar ; short *sar , *qar ; byte *tar ; float fsig , fq , cls , snew , val ; float *NEW_wks=NULL ; #pragma omp critical (DESPIKE_malloc) { far = (float *) malloc( sizeof(float) * nvals ) ; dar = (float *) malloc( sizeof(float) * nvals ) ; var = (float *) malloc( sizeof(float) * nvals ) ; fitar = (float *) malloc( sizeof(float) * nvals ) ; ssp = (float *) malloc( sizeof(float) * nvals ) ; fit = (float *) malloc( sizeof(float) * nref ) ; if( do_NEW ) NEW_wks = (float *)malloc(sizeof(float)*DES_workspace_size(nuse,nref)) ; } #ifdef USE_OMP INFO_message("start OpenMP thread #%d",omp_get_thread_num()) ; #endif #pragma omp for for( ii=0 ; ii < nxyz ; ii++ ){ /* ii = voxel index */ if( mask != NULL && mask[ii] == 0 ) continue ; /* skip this voxel */ #ifndef USE_OMP kz = DSET_index_to_kz(dset,ii) ; /* starting a new slice */ if( kz != kzold ){ if( verb ){ fprintf(stderr, "++ start slice %2d",kz ) ; if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; fprintf(stderr, "; so far %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } fprintf(stderr,"\n") ; } kzold = kz ; } #else if( verb && ii % 2345 == 1234 ) fprintf(stderr,".") ; #endif /*** extract ii-th time series into far[] ***/ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ qar = DSET_ARRAY(dset,iv+ignore) ; /* skip ignored data */ far[iv] = (float)qar[ii] ; } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(dset,iv+ignore) ; far[iv] = zar[ii] ; } break ; } AAmemcpy(dar,far,sizeof(float)*nuse) ; /* copy time series into dar[] */ /*** solve for L1 fit ***/ if( do_NEW ) cls = DES_solve( NEW_psinv , far , fit , NEW_wks ) ; /* 29 Nov 2013 */ else cls = cl1_solve( nuse , nref , far , ref , fit,0 ) ; /* the slow part */ if( cls < 0.0f ){ /* fit failed! */ #if 0 fprintf(stderr,"curve fit fails at voxel %d %d %d\n", DSET_index_to_ix(dset,ii) , DSET_index_to_jy(dset,ii) , DSET_index_to_kz(dset,ii) ) ; #endif continue ; /* skip this voxel */ } for( iv=0 ; iv < nuse ; iv++ ){ /* detrend */ val = fit[0] + fit[1]*ref[1][iv] /* quadratic part of curve fit */ + fit[2]*ref[2][iv] ; for( jj=3 ; jj < nref ; jj++ ) /* rest of curve fit */ val += fit[jj] * ref[jj][iv] ; fitar[iv] = val ; /* save curve fit value */ var[iv] = dar[iv]-val ; /* remove fitted value = resid */ far[iv] = fabsf(var[iv]) ; /* abs value of resid */ } /*** compute estimate standard deviation of detrended data ***/ fsig = sq2p * qmed_float(nuse,far) ; /* also mangles far array */ /*** process time series for spikes, editing data in dar[] ***/ if( fsig > 0.0f ){ /* data wasn't fit perfectly */ /* find spikiness for each point in time */ fq = 1.0f / fsig ; for( iv=0 ; iv < nuse ; iv++ ){ ssp[iv] = fq * var[iv] ; /* spikiness s = how many sigma out */ } /* save spikiness in -ssave datset */ if( tset != NULL ){ for( iv=0 ; iv < nuse ; iv++ ){ tar = DSET_ARRAY(tset,iv+ignore) ; snew = ITFAC*fabsf(ssp[iv]) ; /* scale for byte storage */ tar[ii] = BYTEIZE(snew) ; /* cf. mrilib.h */ } } /* process values of |s| > cut1, editing dar[] */ for( iv=0 ; iv < nuse ; iv++ ){ /* loop over time points */ if( !localedit ){ /** classic 'smash' edit **/ if( ssp[iv] > cut1 ){ snew = cut1 + c21*mytanh((ssp[iv]-cut1)*ic21) ; /* edit s down */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] > cut2 ) nbig++ ; } } else if( ssp[iv] < -cut1 ){ snew = -cut1 + c21*mytanh((ssp[iv]+cut1)*ic21) ; /* edit s up */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] < -cut2 ) nbig++ ; } } } else { /** local edit: 04 Apr 2007 **/ if( ssp[iv] >= cut2 || ssp[iv] <= -cut2 ){ for( iu=iv+1 ; iu < nuse ; iu++ ) /* find non-spike above */ if( ssp[iu] < cut2 && ssp[iu] > -cut2 ) break ; for( id=iv-1 ; id >= 0 ; id-- ) /* find non-spike below */ if( ssp[id] < cut2 && ssp[id] > -cut2 ) break ; switch( (id>=0) + 2*(iu<nuse) ){ /* compute replacement val */ case 3: val = 0.5*(dar[iu]+dar[id]); break; /* iu and id OK */ case 2: val = dar[iu] ; break; /* only iu OK */ case 1: val = dar[id] ; break; /* only id OK */ default: val = fitar[iv] ; break; /* shouldn't be */ } dar[iv] = val ; #pragma omp critical (DESPIKE_counter) { nspike++ ; nbig++ ; } } } } /* end of loop over time points */ #pragma omp atomic nproc += nuse ; /* number data points processed */ } /* end of processing time series when fsig is positive */ /* put dar[] time series (possibly edited above) into output bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ sar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ sar[ii] = (short)dar[iv] ; /* original or mutated data */ } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ zar[ii] = dar[iv] ; /* original or mutated data */ } break ; } } /* end of loop over voxels #ii */ #pragma omp critical (DESPIKE_malloc) { free(fit); free(ssp); free(fitar); free(var); free(dar); free(far); if( do_NEW ) free(NEW_wks) ; } } /* end OpenMP */ AFNI_OMP_END ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif ctim = NI_clock_time() - ctim ; INFO_message( "Elapsed despike time = %s" , nice_time_string(ctim) ) ; if( ctim > 345678 && !do_NEW ) ININFO_message("That was SLOW -- try the '-NEW' option for a speedup") ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif /*--- finish up ---*/ if( do_NEW ) mri_free(NEW_psinv) ; DSET_delete(dset) ; /* delete input dataset */ if( verb ){ if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; INFO_message("FINAL: %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } else { INFO_message("FINAL: no good voxels found to process!!??") ; } } /* write results */ DSET_write(oset) ; if( verb ) WROTE_DSET(oset) ; DSET_delete(oset) ; if( tset != NULL ){ DSET_write(tset) ; if( verb ) WROTE_DSET(tset) ; DSET_delete(tset) ; } exit( THD_get_write_error_count() ) ; }