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) ;
}
Exemple #3
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) ;
}
Exemple #5
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) ;
}
Exemple #7
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) ;
}
Exemple #8
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) ;
}
Exemple #10
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() ) ;
}