int THD_bandpass_OK( int nx , float dt , float fbot , float ftop , int verb ) { int nfft , jbot,jtop ; float df ; if( ftop > ICOR_MAX_FTOP ) return 1 ; /* 26 Feb 2010 */ if( nx < 9 ) return 0 ; if( dt <= 0.0f ) dt = 1.0f ; if( fbot < 0.0f ) fbot = 0.0f ; if( ftop <= fbot ){ ERROR_message("bad bandpass frequencies?"); return 0; } if( bpwrn && dt > 60.0f ){ WARNING_message("Your bandpass timestep (%f) is high.\n" " Make sure units are 'sec', not 'msec'.\n" " This warning will not be repeated." , dt); bpwrn = 0; } nfft = (nfft_fixed >= nx) ? nfft_fixed : csfft_nextup_even(nx) ; df = 1.0f / (nfft * dt) ; /* freq step */ jbot = (int)rint(fbot/df) ; /* band bot index */ jtop = (int)rint(ftop/df) ; /* band top index */ if( jtop >= nfft/2 ) jtop = nfft/2-1 ; if( jbot+1 >= jtop ){ ERROR_message( "bandpass: fbot=%g and ftop=%g too close ==> jbot=%d jtop=%d [nfft=%d dt=%g]", fbot,ftop,jbot,jtop,nfft,dt) ; return 0 ; } if( verb ) ININFO_message( "bandpass: ntime=%d nFFT=%d dt=%.6g dFreq=%.6g Nyquist=%.6g passband indexes=%d..%d", nx, nfft, dt, df, (nfft/2)*df, jbot, jtop) ; return 1 ; }
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 nx,ny,nz , nxyz , ii,kk , num1,num2 , num_tt=0 , iv , piece , fim_offset; float dx,dy,dz , dxyz , num1_inv=0.0 , num2_inv , num1m1_inv=0.0 , num2m1_inv , dof , dd,tt,q1,q2 , f1,f2 , tt_max=0.0 ; THD_3dim_dataset *dset=NULL , *new_dset=NULL ; THD_3dim_dataset * base_dset; float *av1 , *av2 , *sd1 , *sd2 , *ffim , *gfim ; float *base_ary=NULL; void *vsp ; void *vdif ; /* output mean difference */ char cbuf[THD_MAX_NAME] ; float fbuf[MAX_STAT_AUX] , fimfac ; int output_datum ; float npiece , memuse ; float *dofbrik=NULL , *dofar=NULL ; THD_3dim_dataset *dof_dset=NULL ; /*-- read command line arguments --*/ if( argc < 2 || strncmp(argv[1],"-help",5) == 0 ) TT_syntax(NULL) ; /*-- 20 Apr 2001: addto the arglist, if user wants to [RWCox] --*/ mainENTRY("3dttest main"); machdep() ; PRINT_VERSION("3dttest") ; INFO_message("For most purposes, 3dttest++ should be used instead of 3dttest!") ; { int new_argc ; char ** new_argv ; addto_args( argc , argv , &new_argc , &new_argv ) ; if( new_argv != NULL ){ argc = new_argc ; argv = new_argv ; } } AFNI_logger("3dttest",argc,argv) ; TT_read_opts( argc , argv ) ; if( ! TT_be_quiet ) printf("3dttest: t-tests of 3D datasets, by RW Cox\n") ; /*-- read first dataset in set2 to get dimensions, etc. --*/ dset = THD_open_dataset( TT_set2->ar[0] ) ; /* 20 Dec 1999 BDW */ if( ! ISVALID_3DIM_DATASET(dset) ) ERROR_exit("Unable to open dataset file %s",TT_set2->ar[0]); nx = dset->daxes->nxx ; ny = dset->daxes->nyy ; nz = dset->daxes->nzz ; nxyz = nx * ny * nz ; dx = fabs(dset->daxes->xxdel) ; dy = fabs(dset->daxes->yydel) ; dz = fabs(dset->daxes->zzdel) ; dxyz = dx * dy * dz ; #ifdef TTDEBUG printf("*** nx=%d ny=%d nz=%d\n",nx,ny,nz) ; #endif /*-- make an empty copy of this dataset, for eventual output --*/ #ifdef TTDEBUG printf("*** making empty dataset\n") ; #endif new_dset = EDIT_empty_copy( dset ) ; tross_Make_History( "3dttest" , argc,argv , new_dset ) ; strcpy( cbuf , dset->self_name ) ; strcat( cbuf , "+TT" ) ; iv = DSET_PRINCIPAL_VALUE(dset) ; if( TT_datum >= 0 ){ output_datum = TT_datum ; } else { output_datum = DSET_BRICK_TYPE(dset,iv) ; if( output_datum == MRI_byte ) output_datum = MRI_short ; } #ifdef TTDEBUG printf(" ** datum = %s\n",MRI_TYPE_name[output_datum]) ; #endif iv = EDIT_dset_items( new_dset , ADN_prefix , TT_prefix , ADN_label1 , TT_prefix , ADN_directory_name , TT_session , ADN_self_name , cbuf , ADN_type , ISHEAD(dset) ? HEAD_FUNC_TYPE : GEN_FUNC_TYPE , ADN_func_type , FUNC_TT_TYPE , ADN_nvals , FUNC_nvals[FUNC_TT_TYPE] , ADN_ntt , 0 , /* 07 Jun 2007 */ ADN_datum_all , output_datum , ADN_none ) ; if( iv > 0 ) ERROR_exit("%d errors in attempting to create output dataset!",iv ) ; if( THD_deathcon() && THD_is_file(new_dset->dblk->diskptr->header_name) ) ERROR_exit( "Output dataset file %s already exists--cannot continue!\a", new_dset->dblk->diskptr->header_name ) ; #ifdef TTDEBUG printf("*** deleting exemplar dataset\n") ; #endif THD_delete_3dim_dataset( dset , False ) ; dset = NULL ; /** macro to test a malloc-ed pointer for validity **/ #define MTEST(ptr) \ if((ptr)==NULL) \ ( fprintf(stderr,"*** Cannot allocate memory for statistics!\n"), exit(0) ) /*-- make space for the t-test computations --*/ /* (allocate entire volumes) 13 Dec 2005 [rickr] */ npiece = 3.0 ; /* need at least this many */ if( TT_paired ) npiece += 1.0 ; else if( TT_set1 != NULL ) npiece += 2.0 ; npiece += mri_datum_size(output_datum) / (float) sizeof(float) ; npiece += mri_datum_size(output_datum) / (float) sizeof(float) ; #if 0 piece_size = TT_workmem * MEGA / ( npiece * sizeof(float) ) ; if( piece_size > nxyz ) piece_size = nxyz ; #ifdef TTDEBUG printf("*** malloc-ing space for statistics: %g float arrays of length %d\n", npiece,piece_size) ; #endif #endif av2 = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(av2) ; sd2 = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(sd2) ; ffim = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(ffim) ; num2 = TT_set2->num ; if( TT_paired ){ av1 = sd1 = NULL ; gfim = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(gfim) ; num1 = num2 ; } else if( TT_set1 != NULL ){ av1 = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(av1) ; sd1 = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(sd1) ; gfim = NULL ; num1 = TT_set1->num ; } else { av1 = sd1 = NULL ; gfim = NULL ; num1 = 0 ; } vdif = (void *) malloc( mri_datum_size(output_datum) * nxyz ) ; MTEST(vdif) ; vsp = (void *) malloc( mri_datum_size(output_datum) * nxyz ) ; MTEST(vsp) ; /* 27 Dec 2002: make DOF dataset (if prefix is given, and unpooled is on) */ if( TT_pooled == 0 && TT_dof_prefix[0] != '\0' ){ dofbrik = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(dofbrik) ; dof_dset = EDIT_empty_copy( new_dset ) ; tross_Make_History( "3dttest" , argc,argv , dof_dset ) ; EDIT_dset_items( dof_dset , ADN_prefix , TT_dof_prefix , ADN_directory_name , TT_session , ADN_type , ISHEAD(dset) ? HEAD_FUNC_TYPE : GEN_FUNC_TYPE, ADN_func_type , FUNC_BUCK_TYPE , ADN_nvals , 1 , ADN_datum_all , MRI_float , ADN_none ) ; if( THD_is_file(dof_dset->dblk->diskptr->header_name) ) ERROR_exit( "-dof_prefix dataset file %s already exists--cannot continue!\a", dof_dset->dblk->diskptr->header_name ) ; EDIT_substitute_brick( dof_dset , 0 , MRI_float , dofbrik ) ; } /* print out memory usage to edify the user */ if( ! TT_be_quiet ){ memuse = sizeof(float) * nxyz * npiece + ( mri_datum_size(output_datum) + sizeof(short) ) * nxyz ; if( dofbrik != NULL ) memuse += sizeof(float) * nxyz ; /* 27 Dec 2002 */ printf("--- allocated %d Megabytes memory for internal use (%d volumes)\n", (int)(memuse/MEGA), (int)npiece) ; } mri_fix_data_pointer( vdif , DSET_BRICK(new_dset,0) ) ; /* attach bricks */ mri_fix_data_pointer( vsp , DSET_BRICK(new_dset,1) ) ; /* to new dataset */ /** only short and float are allowed for output **/ if( output_datum != MRI_short && output_datum != MRI_float ) ERROR_exit("Illegal output data type %d = %s", output_datum , MRI_TYPE_name[output_datum] ) ; num2_inv = 1.0 / num2 ; num2m1_inv = 1.0 / (num2-1) ; if( num1 > 0 ){ num1_inv = 1.0 / num1 ; num1m1_inv = 1.0 / (num1-1) ; } /*----- loop over pieces to process the input datasets with -----*/ /** macro to open a dataset and make it ready for processing **/ #define DOPEN(ds,name) \ do{ int pv ; (ds) = THD_open_dataset((name)) ; /* 16 Sep 1999 */ \ if( !ISVALID_3DIM_DATASET((ds)) ) \ ERROR_exit("Can't open dataset: %s",(name)) ; \ if( (ds)->daxes->nxx!=nx || (ds)->daxes->nyy!=ny || (ds)->daxes->nzz!=nz ) \ ERROR_exit("Axes size mismatch: %s",(name)) ; \ if( !EQUIV_GRIDS((ds),new_dset) ) \ WARNING_message("Grid mismatch: %s",(name)) ; \ if( DSET_NUM_TIMES((ds)) > 1 ) \ ERROR_exit("Can't use time-dependent data: %s",(name)) ; \ if( TT_use_editor ) EDIT_one_dataset( (ds), &TT_edopt ) ; \ else DSET_load((ds)) ; \ pv = DSET_PRINCIPAL_VALUE((ds)) ; \ if( DSET_ARRAY((ds),pv) == NULL ) \ ERROR_exit("Can't access data: %s",(name)) ; \ if( DSET_BRICK_TYPE((ds),pv) == MRI_complex ) \ ERROR_exit("Can't use complex data: %s",(name)) ; \ break ; } while (0) #if 0 /* can do it directly now (without offsets) 13 Dec 2005 [rickr] */ /** macro to return pointer to correct location in brick for current processing **/ #define SUB_POINTER(ds,vv,ind,ptr) \ do{ switch( DSET_BRICK_TYPE((ds),(vv)) ){ \ default: ERROR_exit("Illegal datum! ***"); \ case MRI_short:{ short * fim = (short *) DSET_ARRAY((ds),(vv)) ; \ (ptr) = (void *)( fim + (ind) ) ; \ } break ; \ case MRI_byte:{ byte * fim = (byte *) DSET_ARRAY((ds),(vv)) ; \ (ptr) = (void *)( fim + (ind) ) ; \ } break ; \ case MRI_float:{ float * fim = (float *) DSET_ARRAY((ds),(vv)) ; \ (ptr) = (void *)( fim + (ind) ) ; \ } break ; } break ; } while(0) #endif /** number of pieces to process **/ /* num_piece = (nxyz + piece_size - 1) / nxyz ; */ #if 0 nice(2) ; /** lower priority a little **/ #endif /* possibly open TT_base_dset now, and convert to floats */ if( TT_base_dname ) { DOPEN(base_dset, TT_base_dname) ; base_ary = (float *) malloc( sizeof(float) * nxyz ) ; MTEST(base_ary) ; EDIT_coerce_scale_type(nxyz , DSET_BRICK_FACTOR(base_dset,0) , DSET_BRICK_TYPE(base_dset,0),DSET_ARRAY(base_dset,0), /* input */ MRI_float ,base_ary ) ; /* output */ THD_delete_3dim_dataset( base_dset , False ) ; base_dset = NULL ; } /* only 1 'piece' now 13 Dec 2005 [rickr] */ for( piece=0 ; piece < 1 ; piece++ ){ fim_offset = 0 ; #ifdef TTDEBUG printf("*** start of piece %d: length=%d offset=%d\n",piece,nxyz,fim_offset) ; #else if( ! TT_be_quiet ){ printf("--- starting piece %d/%d (%d voxels) ",piece+1,1,nxyz) ; fflush(stdout) ; } #endif /** process set2 (and set1, if paired) **/ for( ii=0 ; ii < nxyz ; ii++ ) av2[ii] = 0.0 ; for( ii=0 ; ii < nxyz ; ii++ ) sd2[ii] = 0.0 ; for( kk=0 ; kk < num2 ; kk++ ){ /** read in the data **/ DOPEN(dset,TT_set2->ar[kk]) ; iv = DSET_PRINCIPAL_VALUE(dset) ; #ifndef TTDEBUG if( ! TT_be_quiet ){ printf(".") ; fflush(stdout) ; } /* progress */ #else printf(" ** opened dataset file %s\n",TT_set2->ar[kk]); #endif #if 0 /* fimfac will be compute when the results are ready */ if( piece == 0 && kk == 0 ){ fimfac = DSET_BRICK_FACTOR(dset,iv) ; if( fimfac == 0.0 ) fimfac = 1.0 ; fimfacinv = 1.0 / fimfac ; #ifdef TTDEBUG printf(" ** set fimfac = %g\n",fimfac) ; #endif } #endif /** convert it to floats (in ffim) **/ EDIT_coerce_scale_type(nxyz , DSET_BRICK_FACTOR(dset,iv) , DSET_BRICK_TYPE(dset,iv),DSET_ARRAY(dset,iv), /* input */ MRI_float ,ffim ) ; /* output */ THD_delete_3dim_dataset( dset , False ) ; dset = NULL ; /** get the paired dataset, if present **/ if( TT_paired ){ DOPEN(dset,TT_set1->ar[kk]) ; iv = DSET_PRINCIPAL_VALUE(dset) ; #ifndef TTDEBUG if( ! TT_be_quiet ){ printf(".") ; fflush(stdout) ; } /* progress */ #else printf(" ** opened dataset file %s\n",TT_set1->ar[kk]); #endif EDIT_coerce_scale_type( nxyz , DSET_BRICK_FACTOR(dset,iv) , DSET_BRICK_TYPE(dset,iv),DSET_ARRAY(dset,iv), /* input */ MRI_float ,gfim ) ; /* output */ THD_delete_3dim_dataset( dset , False ) ; dset = NULL ; if( TT_voxel >= 0 ) fprintf(stderr,"-- paired values #%02d: %f, %f\n", kk,ffim[TT_voxel],gfim[TT_voxel]) ; for( ii=0 ; ii < nxyz ; ii++ ) ffim[ii] -= gfim[ii] ; } else if( TT_voxel >= 0 ) fprintf(stderr,"-- set2 value #%02d: %f\n",kk,ffim[TT_voxel]); #ifdef TTDEBUG printf(" * adding into av2 and sd2\n") ; #endif /* accumulate into av2 and sd2 */ for( ii=0 ; ii < nxyz ; ii++ ){ dd = ffim[ii] ; av2[ii] += dd ; sd2[ii] += dd * dd ; } } /* end of loop over set2 datasets */ /** form the mean and stdev of set2 **/ #ifdef TTDEBUG printf(" ** forming mean and sigma of set2\n") ; #endif for( ii=0 ; ii < nxyz ; ii++ ){ av2[ii] *= num2_inv ; dd = (sd2[ii] - num2*av2[ii]*av2[ii]) ; sd2[ii] = (dd > 0.0) ? sqrt( num2m1_inv * dd ) : 0.0 ; } if( TT_voxel >= 0 ) fprintf(stderr,"-- s2 mean = %g, sd = %g\n", av2[TT_voxel],sd2[TT_voxel]) ; /** if set1 exists but is not paired with set2, process it now **/ if( ! TT_paired && TT_set1 != NULL ){ for( ii=0 ; ii < nxyz ; ii++ ) av1[ii] = 0.0 ; for( ii=0 ; ii < nxyz ; ii++ ) sd1[ii] = 0.0 ; for( kk=0 ; kk < num1 ; kk++ ){ DOPEN(dset,TT_set1->ar[kk]) ; iv = DSET_PRINCIPAL_VALUE(dset) ; #ifndef TTDEBUG if( ! TT_be_quiet ){ printf(".") ; fflush(stdout) ; } /* progress */ #else printf(" ** opened dataset file %s\n",TT_set1->ar[kk]); #endif EDIT_coerce_scale_type( nxyz , DSET_BRICK_FACTOR(dset,iv) , DSET_BRICK_TYPE(dset,iv),DSET_ARRAY(dset,iv), /* input */ MRI_float ,ffim ) ; /* output */ THD_delete_3dim_dataset( dset , False ) ; dset = NULL ; #ifdef TTDEBUG printf(" * adding into av1 and sd1\n") ; #endif for( ii=0 ; ii < nxyz ; ii++ ){ dd = ffim[ii] ; av1[ii] += dd ; sd1[ii] += dd * dd ; } if( TT_voxel >= 0 ) fprintf(stderr,"-- set1 value #%02d: %g\n",kk,ffim[TT_voxel]) ; } /* end of loop over set1 datasets */ /** form the mean and stdev of set1 **/ #ifdef TTDEBUG printf(" ** forming mean and sigma of set1\n") ; #endif for( ii=0 ; ii < nxyz ; ii++ ){ av1[ii] *= num1_inv ; dd = (sd1[ii] - num1*av1[ii]*av1[ii]) ; sd1[ii] = (dd > 0.0) ? sqrt( num1m1_inv * dd ) : 0.0 ; } if( TT_voxel >= 0 ) fprintf(stderr,"-- s1 mean = %g, sd = %g\n", av1[TT_voxel], sd1[TT_voxel]) ; } /* end of processing set1 by itself */ /***** now form difference and t-statistic *****/ #ifndef TTDEBUG if( ! TT_be_quiet ){ printf("+") ; fflush(stdout) ; } /* progress */ #else printf(" ** computing t-tests next\n") ; #endif #if 0 /* will do at end using EDIT_convert_dtype 13 Dec 2005 [rickr] */ /** macro to assign difference value to correct type of array **/ #define DIFASS switch( output_datum ){ \ case MRI_short: sdar[ii] = (short) (fimfacinv*dd) ; break ; \ case MRI_float: fdar[ii] = (float) dd ; break ; } #define TOP_SS 32700 #define TOP_TT (32700.0/FUNC_TT_SCALE_SHORT) #endif if( TT_paired || TT_use_bval == 1 ){ /** case 1: paired estimate or 1-sample **/ if( TT_paired || TT_n1 == 0 ){ /* the olde waye: 1 sample test */ f2 = 1.0 / sqrt( (double) num2 ) ; for( ii=0 ; ii < nxyz ; ii++ ){ av2[ii] -= (base_ary ? base_ary[ii] : TT_bval) ; /* final mean */ if( sd2[ii] > 0.0 ){ num_tt++ ; tt = av2[ii] / (f2 * sd2[ii]) ; sd2[ii] = tt; /* final t-stat */ tt = fabs(tt) ; if( tt > tt_max ) tt_max = tt ; } else { sd2[ii] = 0.0; } } if( TT_voxel >= 0 ) fprintf(stderr,"-- paired/bval mean = %g, t = %g\n", av2[TT_voxel], sd2[TT_voxel]) ; } else { /* 10 Oct 2007: -sdn1 was used with -base1: 'two' sample test */ f1 = (TT_n1-1.0) * (1.0/TT_n1 + 1.0/num2) / (TT_n1+num2-2.0) ; f2 = (num2 -1.0) * (1.0/TT_n1 + 1.0/num2) / (TT_n1+num2-2.0) ; for( ii=0 ; ii < nxyz ; ii++ ){ av2[ii] -= (base_ary ? base_ary[ii] : TT_bval) ; /* final mean */ q1 = f1 * TT_sd1*TT_sd1 + f2 * sd2[ii]*sd2[ii] ; if( q1 > 0.0 ){ num_tt++ ; tt = av2[ii] / sqrt(q1) ; sd2[ii] = tt ; /* final t-stat */ tt = fabs(tt) ; if( tt > tt_max ) tt_max = tt ; } else { sd2[ii] = 0.0 ; } } } /* end of -sdn1 special case */ #ifdef TTDEBUG printf(" ** paired or bval test: num_tt = %d\n",num_tt) ; #endif } else if( TT_pooled ){ /** case 2: unpaired 2-sample, pooled variance **/ f1 = (num1-1.0) * (1.0/num1 + 1.0/num2) / (num1+num2-2.0) ; f2 = (num2-1.0) * (1.0/num1 + 1.0/num2) / (num1+num2-2.0) ; for( ii=0 ; ii < nxyz ; ii++ ){ av2[ii] -= av1[ii] ; /* final mean */ q1 = f1 * sd1[ii]*sd1[ii] + f2 * sd2[ii]*sd2[ii] ; if( q1 > 0.0 ){ num_tt++ ; tt = av2[ii] / sqrt(q1) ; sd2[ii] = tt ; /* final t-stat */ tt = fabs(tt) ; if( tt > tt_max ) tt_max = tt ; } else { sd2[ii] = 0.0 ; } } if( TT_voxel >= 0 ) fprintf(stderr,"-- unpaired, pooled mean = %g, t = %g\n", av2[TT_voxel], sd2[TT_voxel]) ; #ifdef TTDEBUG printf(" ** pooled test: num_tt = %d\n",num_tt) ; #endif } else { /** case 3: unpaired 2-sample, unpooled variance **/ /** 27 Dec 2002: modified to save DOF into dofar **/ if( dofbrik != NULL ) dofar = dofbrik + fim_offset ; /* 27 Dec 2002 */ for( ii=0 ; ii < nxyz ; ii++ ){ av2[ii] -= av1[ii] ; q1 = num1_inv * sd1[ii]*sd1[ii] ; q2 = num2_inv * sd2[ii]*sd2[ii] ; if( q1>0.0 && q2>0.0 ){ /* have positive variances? */ num_tt++ ; tt = av2[ii] / sqrt(q1+q2) ; sd2[ii] = tt ; /* final t-stat */ tt = fabs(tt) ; if( tt > tt_max ) tt_max = tt ; if( dofar != NULL ) /* 27 Dec 2002 */ dofar[ii] = (q1+q2)*(q1+q2) / (num1m1_inv*q1*q1 + num2m1_inv*q2*q2) ; } else { sd2[ii] = 0.0 ; if( dofar != NULL ) dofar[ii] = 1.0 ; /* 27 Dec 2002 */ } } if( TT_voxel >= 0 ) fprintf(stderr,"-- unpaired, unpooled mean = %g, t = %g\n", av2[TT_voxel], sd2[TT_voxel]) ; #ifdef TTDEBUG printf(" ** unpooled test: num_tt = %d\n",num_tt) ; #endif } #ifndef TTDEBUG if( ! TT_be_quiet ){ printf("\n") ; fflush(stdout) ; } #endif } /* end of loop over pieces of the input */ if( TT_paired ){ printf("--- Number of degrees of freedom = %d (paired test)\n",num2-1) ; dof = num2 - 1 ; } else if( TT_use_bval == 1 ){ if( TT_n1 == 0 ){ printf("--- Number of degrees of freedom = %d (1-sample test)\n",num2-1) ; dof = num2 - 1 ; } else { dof = TT_n1+num2-2 ; printf("--- Number of degrees of freedom = %d (-sdn1 2-sample test)\n",(int)dof) ; } } else { printf("--- Number of degrees of freedom = %d (2-sample test)\n",num1+num2-2) ; dof = num1+num2-2 ; if( ! TT_pooled ) printf(" (For unpooled variance estimate, this is only approximate!)\n") ; } printf("--- Number of t-tests performed = %d out of %d voxels\n",num_tt,nxyz) ; printf("--- Largest |t| value found = %g\n",tt_max) ; kk = sizeof(ptable) / sizeof(float) ; for( ii=0 ; ii < kk ; ii++ ){ tt = student_p2t( ptable[ii] , dof ) ; printf("--- Double sided tail p = %8f at t = %8f\n" , ptable[ii] , tt ) ; } /**----------------------------------------------------------------------**/ /** now convert data to output format 13 Dec 2005 [rickr] **/ /* first set mean */ fimfac = EDIT_convert_dtype(nxyz , MRI_float,av2 , output_datum,vdif , 0.0) ; DSET_BRICK_FACTOR(new_dset, 0) = (fimfac != 0.0) ? 1.0/fimfac : 0.0 ; dd = fimfac; /* save for debug output */ /* if output is of type short, limit t-stat magnitude to 32.7 */ if( output_datum == MRI_short ){ for( ii=0 ; ii < nxyz ; ii++ ){ if ( sd2[ii] > 32.7 ) sd2[ii] = 32.7 ; else if( sd2[ii] < -32.7 ) sd2[ii] = -32.7 ; } } fimfac = EDIT_convert_dtype(nxyz , MRI_float,sd2 , output_datum,vsp , 0.0) ; DSET_BRICK_FACTOR(new_dset, 1) = (fimfac != 0.0) ? 1.0/fimfac : 0.0 ; #ifdef TTDEBUG printf(" ** fimfac for mean, t-stat = %g, %g\n",dd, fimfac) ; #endif /**----------------------------------------------------------------------**/ INFO_message("Writing combined dataset into %s\n", DSET_BRIKNAME(new_dset) ) ; fbuf[0] = dof ; for( ii=1 ; ii < MAX_STAT_AUX ; ii++ ) fbuf[ii] = 0.0 ; (void) EDIT_dset_items( new_dset , ADN_stat_aux , fbuf , ADN_none ) ; #if 0 /* factors already set */ fbuf[0] = (output_datum == MRI_short && fimfac != 1.0 ) ? fimfac : 0.0 ; fbuf[1] = (output_datum == MRI_short ) ? 1.0 / FUNC_TT_SCALE_SHORT : 0.0 ; (void) EDIT_dset_items( new_dset , ADN_brick_fac , fbuf , ADN_none ) ; #endif if( !AFNI_noenv("AFNI_AUTOMATIC_FDR") ) ii = THD_create_all_fdrcurves(new_dset) ; else ii = 0 ; THD_load_statistics( new_dset ) ; THD_write_3dim_dataset( NULL,NULL , new_dset , True ) ; if( ii > 0 ) ININFO_message("created %d FDR curves in header",ii) ; if( dof_dset != NULL ){ /* 27 Dec 2002 */ DSET_write( dof_dset ) ; WROTE_DSET( dof_dset ) ; } exit(0) ; }
MRI_IMAGE *mri_medianfilter( MRI_IMAGE *imin, float irad, byte *mask, int verb ) { MRI_IMAGE *imout ; float *fin=NULL , *fout , *tmp ; short *sin=NULL ; byte *bin=NULL ; void *vin ; short *di , *dj , *dk ; int nd, ii,jj,kk, ip,jp,kp, nx,ny,nz, nxy, ijk, dd,nt=0,pjk, kd=0 ; MCW_cluster *cl ; float dz ; ENTRY("mri_medianfilter") ; if( imin == NULL || irad <= 0.0f ) RETURN(NULL) ; /** deal with vector-valued images [15 Dec 2008] -- see mrilib.h **/ #undef CALLME #define CALLME(inn,out) (out) = mri_medianfilter( (inn), irad,mask,verb ) if( ISVECTIM(imin) ){ VECTORME(imin,imout) ; RETURN(imout) ; } /** if not a good input data type, floatize and try again **/ if( imin->kind != MRI_float && imin->kind != MRI_short && imin->kind != MRI_byte ){ MRI_IMAGE *qim ; qim = mri_to_float( imin ) ; imout = mri_medianfilter( qim , irad , mask , verb ) ; mri_free( qim ) ; RETURN(imout) ; } /** build cluster of points for median-izing **/ if( !use_dxyz ){ if( irad < 1.01f ) irad = 1.01f ; dz = (imin->nz == 1) ? 6666.0f : 1.0f ; cl = MCW_build_mask( 1.0f,1.0f,dz , irad ) ; } else { float dm ; dz = (imin->nz == 1) ? 6666.0f : imin->dz ; dm = MIN(imin->dx,imin->dy) ; dm = MIN(dm,dz) ; dm *= 1.01f ; if( irad < dm ) irad = dm ; cl = MCW_build_mask( imin->dx,imin->dy,dz , irad ) ; } if( cl == NULL || cl->num_pt < 6 ){ KILL_CLUSTER(cl); RETURN(NULL); } ADDTO_CLUSTER(cl,0,0,0,0) ; di = cl->i ; dj = cl->j ; dk = cl->k ; nd = cl->num_pt ; nx = imin->nx ; ny = imin->ny ; nz = imin->nz ; nxy = nx*ny ; if( verb ){ ININFO_message(" Median filter mask has %d voxels",nd) ; if( mask != NULL ) ININFO_message(" Data mask has %d voxels",THD_countmask(nxy*nz,mask)) ; } imout = mri_new_conforming( imin , MRI_float ) ; fout = MRI_FLOAT_PTR( imout ) ; vin = mri_data_pointer( imin ) ; tmp = (float *) malloc(sizeof(float)*nd) ; switch( imin->kind ){ default: break ; case MRI_float: fin = (float *)vin ; break ; case MRI_short: sin = (short *)vin ; break ; case MRI_byte : bin = (byte *)vin ; break ; } if( verb ){ kd = (int)rint(0.03*nz); if( kd < 1 ) kd = 1; fprintf(stderr," + Median filter loop") ; } for( kk=0 ; kk < nz ; kk++ ){ if( verb && kk%kd == 0 ) fprintf(stderr,".") ; for( jj=0 ; jj < ny ; jj++ ){ for( ii=0 ; ii < nx ; ii++ ){ ijk = ii + jj*nx + kk*nxy ; if( SKIPVOX(ijk) ) continue ; /* extract neighborhood values */ switch( imin->kind ){ default: break ; case MRI_float: for( nt=dd=0 ; dd < nd ; dd++ ){ ip = ii+di[dd] ; if( ip < 0 || ip >= nx ) continue ; jp = jj+dj[dd] ; if( jp < 0 || jp >= ny ) continue ; kp = kk+dk[dd] ; if( kp < 0 || kp >= nz ) continue ; pjk = ip+jp*nx+kp*nxy ; if( SKIPVOX(pjk) ) continue ; tmp[nt++] = fin[pjk] ; } break ; case MRI_short: for( nt=dd=0 ; dd < nd ; dd++ ){ ip = ii+di[dd] ; if( ip < 0 || ip >= nx ) continue ; jp = jj+dj[dd] ; if( jp < 0 || jp >= ny ) continue ; kp = kk+dk[dd] ; if( kp < 0 || kp >= nz ) continue ; pjk = ip+jp*nx+kp*nxy ; if( SKIPVOX(pjk) ) continue ; tmp[nt++] = sin[pjk] ; } break ; case MRI_byte: for( nt=dd=0 ; dd < nd ; dd++ ){ ip = ii+di[dd] ; if( ip < 0 || ip >= nx ) continue ; jp = jj+dj[dd] ; if( jp < 0 || jp >= ny ) continue ; kp = kk+dk[dd] ; if( kp < 0 || kp >= nz ) continue ; pjk = ip+jp*nx+kp*nxy ; if( SKIPVOX(pjk) ) continue ; tmp[nt++] = bin[pjk] ; } break ; } fout[ijk] = qmed_float( nt , tmp ) ; /* the actual median-izing */ }}} if( verb ) fprintf(stderr,"\n") ; KILL_CLUSTER(cl); free((void *)tmp); /* toss the trash */ RETURN(imout) ; }
/*! Put some help like for function thd_polyfit */ int thd_Acluster ( THD_3dim_dataset *in_set, byte *mask, int nmask, THD_3dim_dataset **clust_set, THD_3dim_dataset **dist_set, OPT_KMEANS oc ) { int ii, nl, nc; double **D=NULL, **distmatrix=NULL; /* this double business is a waste of memory, at least for D..*/ int ncol = -1; float *dvec=NULL; int* clusterid = NULL; short *sc = NULL; ENTRY("thd_Acluster"); if (!clust_set || *clust_set) { fprintf(stderr, "ERROR: output volume pointer pointers must point to NULL\n"); RETURN(0); } if (!mask) nmask = DSET_NVOX(in_set); ncol = DSET_NVALS(in_set); if (ncol < DSET_NUM_TIMES(in_set)) ncol = DSET_NUM_TIMES(in_set); if (oc.verb) { ININFO_message("Have %d/%d voxels to process " "with %d dimensions per voxel.\n", nmask, DSET_NVOX(in_set), ncol); } /* Create data matrix */ D = (double **)calloc(sizeof(double*), nmask); for (ii=0;ii<(nmask);++ii) { if (!(D[ii] = (double *)calloc(sizeof(double), ncol))) { fprintf(stderr,"ERROR: Failed while allocating %dx%d double matrix\n", nmask, ncol); RETURN(0); } } dvec = (float * )malloc(sizeof(float)*ncol) ; /* array to hold series */ if (oc.verb) { ININFO_message("Filling D(%dx%d) (mask=%p).\n", nmask, ncol, mask); } ii = 0; for (nl=0; nl<DSET_NVOX(in_set); ++nl) { if (!mask || mask[nl]) { THD_extract_array( nl , in_set , 0 , dvec ) ; for (nc=0; nc<ncol; ++nc) D[ii][nc] = dvec[nc]; ++ii; } } /* allocate for answer arrays */ if (!(clusterid = (int *)calloc(sizeof(int), nmask))) { fprintf(stderr,"ERROR: Failed to allocate for clusterid\n"); RETURN(0); } /* now do the clustering (ANDREJ: I do not know why the counting skipped 1st row and 1st col....) */ if (oc.k > 0) { if (oc.verb) { ININFO_message("Going to cluster: k=%d, r=%d\n" "distmetric %c, jobname %s\n", oc.k, oc.r, oc.distmetric, oc.jobname); } example_kmeans( nmask, ncol, D, oc.k, oc.r, oc.distmetric, oc.jobname, clusterid); } else if (oc.kh > 0) { if (oc.verb) { ININFO_message("Going to h cluster: kh=%d\n" "jobname %s\n", oc.kh, oc.jobname); } if ((distmatrix = example_distance_gene(nmask, ncol, D))) { example_hierarchical( nmask, ncol, D, oc.jobname, oc.kh, distmatrix, clusterid); /* YOU SHOULD FREE distmatrix here ...*/ } else { ERROR_message("Failed to create distmatrix"); RETURN(0); } } else { ERROR_message("Bad option selection"); RETURN(0); } /* create output datasets, if required*/ *clust_set = EDIT_empty_copy(in_set) ; EDIT_dset_items( *clust_set , ADN_nvals , 1 , ADN_ntt , 1 , ADN_datum_all , MRI_short , ADN_brick_fac , NULL , ADN_prefix , "OML!" , ADN_none ) ; /* MRI_float */ if (oc.verb) { ININFO_message("loading results into %s\n", DSET_PREFIX(*clust_set)); } /* transfer ints in clusterid to shorts array */ sc = (short *)calloc(sizeof(short),DSET_NVOX(in_set)); ii = 0; for (nl=0; nl<DSET_NVOX(in_set); ++nl) { if (!mask || mask[nl]) { sc[nl] = (short)clusterid[ii]+1; ++ii; } } free(clusterid); clusterid = NULL; EDIT_substitute_brick( *clust_set , 0 , MRI_short , sc ) ; sc = NULL; /* array now in brick */ if (oc.verb) { ININFO_message("Freedom"); } if (dvec) free(dvec); dvec=NULL; // To free D for (ii=0;ii<nmask;++ii) { if (D[ii]) free(D[ii]); } free(D); D = NULL; RETURN(1); }
THD_3dim_dataset * THD_deghoster( THD_3dim_dataset *inset , THD_3dim_dataset *filset, int pe , int fe , int se ) { MRI_IMAGE *medim=NULL , *tim=NULL , *oim=NULL ; float cval, *mar=NULL , *tar=NULL , *oar=NULL ; float *xzero_t=NULL , *thet1_t=NULL , *dparr_t=NULL , t1med,t1bmv; byte *bmask=NULL , *amask=NULL , sm ; int nvox , nx,ny,nz , dp=0,df=0,ds=0 , np=0,nf=0,ns=0,np2,nf2 ; int pp,ff,ss,nfp , ii , ppg , nsm,ism , vv,nv , iim , sskip ; THD_3dim_dataset *outset=NULL ; float iy,iyn ; float_pair mp ; /* create brain mask (bmask) */ medim = THD_median_brick(inset) ; bmask = DEG_automask_image(medim) ; /* brain mask (we hope) */ nx = medim->nx ; ny = medim->ny ; nz = medim->nz ; nvox = medim->nvox ; nv = DSET_NVALS(inset) ; /* estimate noise level from data outside the mask (crudely) */ mar = MRI_FLOAT_PTR(medim) ; cval = THD_cliplevel(medim,CLFRAC) ; for( noise_estimate=0.0f,iim=ii=0 ; ii < nvox ; ii++ ) { if( !bmask[ii] && mar[ii] < cval ) { noise_estimate += mar[ii] ; iim++ ; } } if( iim < 9 ) { FREEUP; /* should not happen */ return NULL; } noise_estimate /= iim ; /* initial estimate of noise level */ if( verb > 1 ) INFO_message("Global crude noise_estimate = %g",noise_estimate) ; /* chop out all sub-threshold voxels (amask) */ amask = (byte *)malloc(sizeof(byte)*nvox) ; /* clipped brain mask */ memcpy(amask,bmask,sizeof(byte)*nvox) ; for( ii=0 ; ii < nvox ; ii++ ) if( amask[ii] && mar[ii] < cval ) amask[ii] = 0 ; /* setting up slice coordinates f,p,s */ if( pe == 1 ) { dp = 1 ; np = nx ; } else if( pe == 2 ) { dp = nx ; np = ny ; } else if( pe == 3 ) { dp = nx*ny ; np = ns ; } if( fe == 1 ) { df = 1 ; nf = nx ; } else if( fe == 2 ) { df = nx ; nf = ny ; } else if( fe == 3 ) { df = nx*ny ; nf = nz ; } if( se == 1 ) { ds = 1 ; ns = nx ; } else if( se == 2 ) { ds = nx ; ns = ny ; } else if( se == 3 ) { ds = nx*ny ; ns = nz ; } #undef IJK #define IJK(f,p,s) ((f)*df+(p)*dp+(s)*ds) nvim = nfp = nf * np ; np2 = np / 2 ; nf2 = nf / 2 ; smask = (byte * )malloc(sizeof(byte) *nfp) ; bvec = (float *)malloc(sizeof(float)*nfp) ; gvec = (float *)malloc(sizeof(float)*nfp) ; xvec = (float *)malloc(sizeof(float)*nfp) ; yvec = (float *)malloc(sizeof(float)*nfp) ; ctvec = (float *)malloc(sizeof(float)*nfp) ; stvec = (float *)malloc(sizeof(float)*nfp) ; bvim = (float *)malloc(sizeof(float)*nvim) ; gvim = (float *)malloc(sizeof(float)*nvim) ; xvim = (float *)malloc(sizeof(float)*nvim) ; yvim = (float *)malloc(sizeof(float)*nvim) ; ctvim = (float *)malloc(sizeof(float)*nvim) ; stvim = (float *)malloc(sizeof(float)*nvim) ; xzero_t = (float *)malloc(sizeof(float)*nv) ; thet1_t = (float *)malloc(sizeof(float)*nv) ; dparr_t = (float *)malloc(sizeof(float)*nv) ; /* copy input to output (will be ghost edited later) */ outset = EDIT_empty_copy(inset) ; for( vv=0 ; vv < nv ; vv++ ) { oim = THD_extract_float_brick(vv,inset) ; oar = MRI_FLOAT_PTR(oim) ; EDIT_BRICK_FACTOR( outset , vv , 0.0f ) ; EDIT_substitute_brick( outset , vv , MRI_float , oar ) ; mri_clear_and_free(oim) ; } /* loop over slices */ for( ss=0 ; ss < ns ; ss++ ) { /* make copy of brain mask in this slice, then edit it down to voxels in the brain whose N/2 point is outside the brain (smask) */ for( iim=nsm=pp=0 ; pp < np ; pp++ ) { if( pp >= np2 ) ppg = pp-np2 ; else ppg = pp+np2 ; for( ff=0 ; ff < nf ; ff++,iim++ ) { smask[iim] = sm = amask[IJK(ff,pp,ss)] && !bmask[IJK(ff,ppg,ss)] ; xvim[iim] = ff-nf2 ; yvim[iim] = pp-np2 ; if( sm ) { xvec[nsm] = xvim[iim]; yvec[nsm] = yvim[iim]; nsm++; } } } if( nsm < nfp/20 ) { /* skip this slice */ if( verb ) INFO_message("deghost: skipping slice #%d -- too few points in smask",ss) ; continue ; } nvec = nsm ; if( verb ) INFO_message("deghost: processing slice #%d",ss) ; /* smask is now the mask of brain voxels whose Nyquist ghost locations are NOT in the brain mask */ /* loop over time points, estimate the ghost correction parameters */ for( vv=0 ; vv < nv ; vv++ ) { tim = THD_extract_float_brick(vv,filset) ; tar = MRI_FLOAT_PTR(tim) ; /* extract the vector of image values in smask, and the vector of image values at the ghost locations */ for( iim=ism=pp=0 ; pp < np ; pp++ ) { if( pp >= np2 ) ppg = pp-np2 ; else ppg = pp+np2 ; for( ff=0 ; ff < nf ; ff++,iim++ ) { bvim[iim] = tar[IJK(ff,pp,ss)] ; gvim[iim] = tar[IJK(ff,ppg,ss)] ; if( smask[iim] ) { bvec[ism] = bvim[iim]; gvec[ism++] = gvim[iim]; } } } /* fit the theta parameters from the smask region and save them */ optimize_theta() ; xzero_t[vv] = theta_par[0] ; thet1_t[vv] = theta_par[1] ; dparr_t[vv] = d_par ; mri_free(tim) ; tim = NULL ; } /* now check the slice parameters for reasonability */ sskip = 0 ; if( nv > 4 ) { orfilt_len = 3 ; orfilt_vector(nv,xzero_t) ; orfilt_vector(nv,thet1_t) ; orfilt_vector(nv,dparr_t) ; qmedmadbmv_float(nv,thet1_t,&t1med,NULL,&t1bmv) ; if( verb ) ININFO_message(" slice #%d -- median(theta1)=%g stdev=%g ratio=%g", ss,t1med,t1bmv,(t1bmv>0.0f)?t1med/t1bmv:0.0f) ; if( t1med == 0.0f || fabsf(t1med) <= 0.111f*t1bmv ) { sskip = 1 ; ININFO_message(" skipping slice #%d -- theta1 too small",ss) ; } } if( sskip ) continue ; /* skip processing this slice */ /* loop over time points, estimate the un-ghosted image */ for( vv=0 ; vv < nv ; vv++ ) { tim = THD_extract_float_brick(vv,inset) ; /* input data for slice */ tar = MRI_FLOAT_PTR(tim) ; oar = DSET_ARRAY(outset,vv) ; /* output data for volume */ /* compute theta at each voxel */ if( thet1_t[vv] == 0.0f ) continue ; /* ghost amplitude is 0 ==> skip this time point */ if( verb > 1 ) ININFO_message(" slice=%d index=%d theta = %g %g %g", ss,vv,xzero_t[vv],thet1_t[vv],dparr_t[vv]) ; theta_par[0] = xzero_t[vv]; theta_par[1] = thet1_t[vv]; d_par = dparr_t[vv]; compute_thvim() ; /* compute output values at each voxel: (a) inside the smask == voxel in brain, N/2 ghost isn't (b) not in the smask but in the bmask == voxel && N/2 ghost are in brain (c) otherwise == voxel is unimportant effluvium */ for( iim=pp=0 ; pp < np ; pp++ ) { if( pp >= np2 ) ppg = pp-np2 ; else ppg = pp+np2 ; for( ff=0 ; ff < nf ; ff++,iim++ ) { iy = tar[IJK(ff,pp,ss)] ; iyn = tar[IJK(ff,ppg,ss)] ; if( smask[iim] ) { oar[IJK(ff,pp,ss)] = find_mhat( iy,iyn , ctvim[iim],stvim[iim] , d_par ) ; oar[IJK(ff,ppg,ss)] = 0.0f ; } else if( bmask[IJK(ff,pp,ss)] && bmask[IJK(ff,ppg,ss)] ) { if( ppg > pp ) { mp = find_mpair( iy,iyn , ctvim[iim],stvim[iim] , d_par ) ; oar[IJK(ff,pp,ss)] = mp.a ; oar[IJK(ff,ppg,ss)] = mp.b ; } } else { /* nada: output is already a copy of input */ } } } } } /* end of loop over slices */ FREEUP ; return outset ; }
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[] ) { char *drive_afni[128] ; int ndrive=0 , iarg=1 ; char host[1024]="localhost", nsname[2048], *geomstr, *cpt, temp[32] ; int dt=1000 , ctold,ctnew , ctzero ; int verbose=0 , kk,nn , nvox,nval , do_accum=0 ; THD_3dim_dataset *dset ; NI_element *nel ; MRI_IMAGE *fim ; float *far ; char *targname="niml_feedme" ; /*-- help the ignorant user --*/ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: niml_feedme [options] dataset\n" "\n" "* Sends volumes from the dataset to AFNI via the NIML socket interface.\n" "* You must run AFNI with the command 'afni -niml' so that the program\n" " will be listening for the socket connection.\n" "* Inside AFNI, the transmitted dataset will be named 'niml_feedme'.\n" "* For another way to send image data to AFNI, see progam rtfeedme.\n" "* At present, there is no way to attach statistical parameters to\n" " a transmitted volume.\n" "* This program sends all volumes in float format, simply because\n" " that's easy for me. But you can also send byte, short, and\n" " complex valued volumes.\n" "* This program is really just a demo; it has little practical use.\n" "\n" "OPTIONS:\n" " -host sname = Send data, via TCP/IP, to AFNI running on the\n" " computer system 'sname'. By default, uses the\n" " current system (localhost), if you don't use this\n" " option.\n" "\n" " -dt ms = Tries to maintain an inter-transmit interval of 'ms'\n" " milliseconds. The default is 1000 msec per volume.\n" "\n" " -verb = Be (very) talkative about actions.\n" "\n" " -accum = Send sub-bricks so that they accumulate in AFNI.\n" " The default is to create only a 1 volume dataset\n" " inside AFNI, and each sub-brick just replaces\n" " that one volume when it is received.\n" "\n" " -target nam = Change the dataset name transmitted to AFNI from\n" " 'niml_feedme' to 'nam'.\n" "\n" " -drive cmd = Send 'cmd' as a DRIVE_AFNI command.\n" " * If cmd contains blanks, it must be in 'quotes'.\n" " * Multiple -drive options may be used.\n" " * These commands will be sent to AFNI just after\n" " the first volume is transmitted.\n" " * See file README.driver for a list of commands.\n" "\n" "EXAMPLE: Send volumes from a 3D+time dataset to AFNI:\n" "\n" " niml_feedme -dt 1000 -verb -accum -target Elvis \\\n" " -drive 'OPEN_WINDOW axialimage' \\\n" " -drive 'OPEN_WINDOW axialgraph' \\\n" " -drive 'SWITCH_UNDERLAY Elvis' \\\n" " timeseries+orig\n" "\n" "Author: RW Cox -- July 2009\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("niml_feedme") ; /*-- scan arguments --*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-target",5) == 0 ){ targname = strdup(argv[++iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-drive") == 0 ){ drive_afni[ndrive++] = argv[++iarg] ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-host") == 0 ){ strcpy( host , argv[++iarg] ) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-dt") == 0 ){ dt = (int)strtod(argv[++iarg],NULL) ; if( dt < 9 ) dt = 9 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-verbose",4) == 0 ){ verbose = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-accum",4) == 0 ){ do_accum = 1 ; iarg++ ; continue ; } ERROR_exit("Unrecognized option: %s",argv[iarg]) ; } if( iarg >= argc ) ERROR_exit("No dataset on command line?!") ; /*-- read in the dataset --*/ dset = THD_open_dataset( argv[iarg] ) ; if( dset == NULL ) ERROR_exit("Can't open dataset '%s'",argv[iarg]) ; DSET_load(dset) ; if( !DSET_LOADED(dset) ) ERROR_exit("Can't load dataset '%s'",argv[iarg]) ; cpt = EDIT_get_geometry_string(dset) ; geomstr = strdup(cpt) ; /* describes geometry of dataset grid */ if( verbose ) INFO_message("geometry string = '%s'",geomstr) ; nvox = DSET_NVOX(dset); /* number of voxels in dataset */ nval = DSET_NVALS(dset); /* number of sub-bricks in dataset */ /*-- this stuff is one-time-only setup of the I/O to AFNI --*/ atexit(NF_exit) ; /* call this when program ends */ signal(SIGINT ,NF_sigfunc) ; /* setup signal handler */ signal(SIGBUS ,NF_sigfunc) ; /* for fatal errors */ signal(SIGSEGV,NF_sigfunc) ; signal(SIGTERM,NF_sigfunc) ; /* name of NIML stream (socket) to open */ sprintf( nsname , "tcp:%s:%d" , host , get_port_named("AFNI_DEFAULT_LISTEN_NIML")); /* open the socket (i.e., dial the telephone call) */ fprintf(stderr,"opening NIML stream '%s' ",nsname) ; NF_stream = NI_stream_open( nsname , "w" ) ; /* loop until AFNI connects (answers the call), printing a '.' every 1/2 second to keep the user happy */ while(1){ kk = NI_stream_writecheck( NF_stream , 500 ) ; if( kk == 1 ){ fprintf(stderr," connected!\n") ; break ; } if( kk < 0 ){ fprintf(stderr," ** connection fails **\n") ; exit(1) ; } fprintf(stderr,".") ; } /*-- Create VOLUME_DATA NIML element to hold the brick data --*/ nel = NI_new_data_element( "VOLUME_DATA" , nvox ) ; /* add attributes to the element to help AFNI construct the dataset */ /* define the grid of the dataset */ NI_set_attribute( nel , "geometry_string" , geomstr ) ; /* define the name of the dataset */ NI_set_attribute( nel , "target_name" , targname ) ; /* all sub-bricks in the input dataset will be sent to be sub-brick #0 in the dataset inside AFNI -- if you don't want this behavior, and want the dataset inside AFNI to keep growing, then don't set this attribute! */ if( !do_accum ) NI_set_attribute( nel , "index" , "0" ) ; /* +tlrc view? [default in AFNI is +orig view] */ if( dset->view_type == VIEW_TALAIRACH_TYPE ) NI_set_attribute( nel , "view" , "tlrc" ) ; /**-- loop over sub-bricks and send them to AFNI --*/ ctzero = NI_clock_time() ; /* for later reference */ if( verbose ) INFO_message("Starting sub-brick loop") ; for( kk=0 ; kk < nval ; kk++ ){ ctold = NI_clock_time() ; /* clock time at start of work (ms) */ /* get a float copy of the kk-th sub-brick */ fim = THD_extract_float_brick( kk , dset ) ; DSET_unload_one(dset,kk) ; /* unload this sub-brick now */ if( fim == NULL ){ /* should never happen */ ERROR_message("Can't get sub-brick #%d?? -- skipping",kk) ; NI_sleep(dt) ; continue ; } /* copy the float data into the NIML element for transmission */ far = MRI_FLOAT_PTR(fim) ; if( kk == 0 ) /* first time: create data column in element */ NI_add_column( nel , NI_FLOAT , far ) ; else /* later times: overwrite nel data column */ memcpy( nel->vec[0] , far , sizeof(float)*nvox ) ; mri_free(fim) ; /* done with this now [data all copied to nel] */ /* set sub-brick index in AFNI if doing accumulation */ if( do_accum ){ sprintf(temp,"%d",kk) ; NI_set_attribute( nel , "index" , temp ) ; } /*** send the data element to AFNI ***/ nn = NI_write_element( NF_stream , nel , NI_BINARY_MODE ) ; /* if something bad happened in the transmission, report it */ if( nn <= 0 ){ ERROR_message("Can't write sub-brick #%d to AFNI!",kk) ; break ; } /*** first time through ==> do the '-drive' commands now by sending processing instructions ***/ if( kk == 0 && ndrive > 0 ){ int ii ; NI_procins *npi ; if( verbose ) ININFO_message("Sending %d 'drive_afni' elements now",ndrive) ; npi = NI_new_processing_instruction( "DRIVE_AFNI" ) ; NI_sleep(1) ; /* give AFNI a msec to digest the data */ for( ii=0 ; ii < ndrive ; ii++ ){ NI_set_attribute( npi , "cmd" , drive_afni[ii] ) ; (void)NI_write_element( NF_stream , npi , NI_TEXT_MODE ) ; } NI_free_element(npi) ; /* delete this struct from the world! */ } ctnew = NI_clock_time() ; /* clock time now */ if( verbose ) ININFO_message("Sent %d bytes for sub-brick #%d in %d ms", nn , kk , ctnew-ctold ) ; NI_sleep( dt - (ctnew-ctold) ) ; /* sleep so that time delay is right */ } /* end of loop over sub-bricks */ /** summarize, do some cleanup, and exit stage left **/ if( verbose && kk > 0 ){ float dtav = (NI_clock_time()-ctzero) / (float)kk ; INFO_message("Transmission finished: %.1f ms = average time per volume",dtav) ; } NI_free_element(nel) ; /* destroy the data element */ DSET_delete(dset) ; /* destroy the dataset */ exit(0) ; }
int main( int argc , char * argv[] ) { float mrad=0.0f , fwhm=0.0f ; int nrep=1 ; char *prefix = "Polyfit" ; char *resid = NULL ; char *cfnam = NULL ; int iarg , verb=0 , do_automask=0 , nord=3 , meth=2 , do_mclip=0 ; THD_3dim_dataset *inset ; MRI_IMAGE *imout , *imin ; byte *mask=NULL ; int nvmask=0 , nmask=0 , do_mone=0 , do_byslice=0 ; MRI_IMARR *exar=NULL ; floatvec *fvit=NULL ; /* 26 Feb 2019 */ if( argc < 2 || strcasecmp(argv[1],"-help") == 0 ){ printf("\n" "Usage: 3dPolyfit [options] dataset ~1~\n" "\n" "* Fits a polynomial in space to the input dataset and outputs that fitted dataset.\n" "\n" "* You can also add your own basis datasets to the fitting mix, using the\n" " '-base' option.\n" "\n" "* You can get the fit coefficients using the '-1Dcoef' option.\n" "\n" "--------\n" "Options: ~1~\n" "--------\n" "\n" " -nord n = Maximum polynomial order (0..9) [default order=3]\n" " [n=0 is the constant 1]\n" " [n=-1 means only use volumes from '-base']\n" "\n" " -blur f = Gaussian blur input dataset (inside mask) with FWHM='f' (mm)\n" "\n" " -mrad r = Radius (voxels) of preliminary median filter of input\n" " [default is no blurring of either type; you can]\n" " [do both types (Gaussian and median), but why??]\n" " [N.B.: median blur is slower than Gaussian]\n" "\n" " -prefix pp = Use 'pp' for prefix of output dataset (the fit).\n" " [default prefix is 'Polyfit'; use NULL to skip this output]\n" "\n" " -resid rr = Use 'rr' for the prefix of the residual dataset.\n" " [default is not to output residuals]\n" "\n" " -1Dcoef cc = Save coefficients of fit into text file cc.1D.\n" " [default is not to save these coefficients]\n" "\n" " -automask = Create a mask (a la 3dAutomask)\n" " -mask mset = Create a mask from nonzero voxels in 'mset'.\n" " [default is not to use a mask, which is probably a bad idea]\n" "\n" " -mone = Scale the mean value of the fit (inside the mask) to 1.\n" " [probably this option is not useful for anything]\n" "\n" " -mclip = Clip fit values outside the rectilinear box containing the\n" " mask to the edge of that box, to avoid weird artifacts.\n" "\n" " -meth mm = Set 'mm' to 2 for least squares fit;\n" " set it to 1 for L1 fit [default method=2]\n" " [Note that L1 fitting is slower than L2 fitting!]\n" "\n" " -base bb = In addition to the polynomial fit, also use\n" " the volumes in dataset 'bb' as extra basis functions.\n" " [If you use a base dataset, then you can set nord]\n" " [to -1, to skip using any spatial polynomial fit.]\n" "\n" " -verb = Print fun and useful progress reports :-)\n" "\n" "------\n" "Notes: ~1~\n" "------\n" "* Output dataset is always stored in float format.\n" "\n" "* If the input dataset has more than 1 sub-brick, only sub-brick #0\n" " is processed. To fit more than one volume, you'll have to use a script\n" " to loop over the input sub-bricks, and then glue (3dTcat) the results\n" " together to get a final result. A simple example:\n" " #!/bin/tcsh\n" " set base = model.nii\n" " set dset = errts.nii\n" " set nval = `3dnvals $dset`\n" " @ vtop = $nval - 1\n" " foreach vv ( `count 0 $vtop` )\n" " 3dPolyfit -base \"$base\" -nord 0 -mask \"$base\" -1Dcoef QQ.$vv -prefix QQ.$vv.nii $dset\"[$vv]\"\n" " end\n" " 3dTcat -prefix QQall.nii QQ.0*.nii\n" " 1dcat QQ.0*.1D > QQall.1D\n" " \rm QQ.0*\n" " exit 0\n" "\n" "* If the '-base' dataset has multiple sub-bricks, all of them are used.\n" "\n" "* You can use the '-base' option more than once, if desired or needed.\n" "\n" "* The original motivation for this program was to fit a spatial model\n" " to a field map MRI, but that didn't turn out to be useful. Nevertheless,\n" " I make this program available to someone who might find it beguiling.\n" "\n" "* If you really want, I could allow you to put sign constraints on the\n" " fit coefficients (e.g., say that the coefficient for a given base volume\n" " should be non-negative). But you'll have to beg for this.\n" "\n" "-- Emitted by RWCox\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } /*-- startup paperwork --*/ mainENTRY("3dPolyfit main"); machdep(); AFNI_logger("3dPolyfit",argc,argv); PRINT_VERSION("3dPolyfit") ; /*-- scan command line --*/ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strcasecmp(argv[iarg],"-base") == 0 ){ THD_3dim_dataset *bset ; int kk ; MRI_IMAGE *bim ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-base'") ; bset = THD_open_dataset(argv[iarg]) ; CHECK_OPEN_ERROR(bset,argv[iarg]) ; DSET_load(bset) ; CHECK_LOAD_ERROR(bset) ; if( exar == NULL ) INIT_IMARR(exar) ; for( kk=0 ; kk < DSET_NVALS(bset) ; kk++ ){ bim = THD_extract_float_brick(kk,bset) ; if( bim != NULL ) ADDTO_IMARR(exar,bim) ; DSET_unload_one(bset,kk) ; } DSET_delete(bset) ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-verb") == 0 ){ verb++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-hermite") == 0 ){ /* 25 Mar 2013 [New Year's Day] */ mri_polyfit_set_basis("hermite") ; /* HIDDEN */ iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-byslice") == 0 ){ /* 25 Mar 2013 [New Year's Day] */ do_byslice++ ; iarg++ ; continue ; /* HIDDEN */ } if( strcasecmp(argv[iarg],"-mask") == 0 ){ THD_3dim_dataset *mset ; if( ++iarg >= 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[iarg]) ; CHECK_OPEN_ERROR(mset,argv[iarg]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; nvmask = DSET_NVOX(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[iarg]) ; nmask = THD_countmask( nvmask , mask ) ; if( nmask < 99 ) ERROR_exit("Too few voxels in mask (%d)",nmask) ; if( verb ) INFO_message("Number of voxels in mask = %d",nmask) ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-nord") == 0 ){ nord = (int)strtol( argv[++iarg], NULL , 10 ) ; if( nord < -1 || nord > 9 ) ERROR_exit("Illegal value after -nord :(") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-meth") == 0 ){ meth = (int)strtol( argv[++iarg], NULL , 10 ) ; if( meth < 1 || meth > 2 ) ERROR_exit("Illegal value after -meth :(") ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-automask",5) == 0 ){ if( mask != NULL ) ERROR_exit("Can't use -mask and -automask together!") ; do_automask++ ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-mclip",5) == 0 ){ do_mclip++ ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-mone",5) == 0 ){ do_mone++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-mrad") == 0 ){ mrad = strtod( argv[++iarg] , NULL ) ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-blur") == 0 ){ fwhm = strtod( argv[++iarg] , NULL ) ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-prefix") == 0 ){ prefix = argv[++iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal value after -prefix :("); if( strcasecmp(prefix,"NULL") == 0 ) prefix = NULL ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-resid") == 0 ){ resid = argv[++iarg] ; if( !THD_filename_ok(resid) ) ERROR_exit("Illegal value after -resid :("); if( strcasecmp(resid,"NULL") == 0 ) resid = NULL ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-1Dcoef") == 0 ){ /* 26 Feb 2019 */ cfnam = argv[++iarg] ; if( !THD_filename_ok(cfnam) ) ERROR_exit("Illegal value after -1Dcoef :("); if( strcasecmp(cfnam,"NULL") == 0 ) cfnam = NULL ; iarg++ ; continue ; } ERROR_exit("Unknown option: %s\n",argv[iarg]); } /*--- check for blatant errors ---*/ if( iarg >= argc ) ERROR_exit("No input dataset name on command line?"); if( prefix == NULL && resid == NULL && cfnam == NULL ) ERROR_exit("-prefix and -resid and -1Dcoef are all NULL?!") ; if( do_byslice && cfnam != NULL ){ WARNING_message("-byslice does not work with -1Dcoef option :(") ; cfnam = NULL ; } if( nord < 0 && exar == NULL ) ERROR_exit("no polynomial fit AND no -base option ==> nothing to compute :(") ; /*-- read input --*/ if( verb ) INFO_message("Load input dataset") ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; 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) ); /* check input mask or create automask */ if( mask != NULL ){ if( nvmask != DSET_NVOX(inset) ) ERROR_exit("-mask and input datasets don't match in voxel counts :-(") ; } else if( do_automask ){ THD_automask_verbose( (verb > 1) ) ; THD_automask_extclip( 1 ) ; mask = THD_automask( inset ) ; nvmask = DSET_NVOX(inset) ; nmask = THD_countmask( nvmask , mask ) ; if( nmask < 99 ) ERROR_exit("Too few voxels in automask (%d)",nmask) ; if( verb ) ININFO_message("Number of voxels in automask = %d",nmask) ; } else { WARNING_message("3dPolyfit is running without a mask") ; } #undef GOOD #define GOOD(i) (mask == NULL || mask[i]) /* check -base input datasets */ if( exar != NULL ){ int ii,kk , nvbad=0 , nvox=DSET_NVOX(inset),nm ; float *ex , exb ; for( kk=0 ; kk < IMARR_COUNT(exar) ; kk++ ){ if( nvox != IMARR_SUBIM(exar,kk)->nvox ){ if( IMARR_SUBIM(exar,kk)->nvox != nvbad ){ ERROR_message("-base volume (%d voxels) doesn't match input dataset grid size (%d voxels)", IMARR_SUBIM(exar,kk)->nvox , nvox ) ; nvbad = IMARR_SUBIM(exar,kk)->nvox ; } } } if( nvbad != 0 ) ERROR_exit("Cannot continue :-(") ; /* subtract mean from each base input, if is a constant polynomial in the fit */ if( nord >= 0 ){ if( verb ) INFO_message("subtracting spatial mean from '-base'") ; for( kk=0 ; kk < IMARR_COUNT(exar) ; kk++ ){ exb = 0.0f ; ex = MRI_FLOAT_PTR(IMARR_SUBIM(exar,kk)) ; for( nm=ii=0 ; ii < nvox ; ii++ ){ if( GOOD(ii) ){ exb += ex[ii]; nm++; } } exb /= nm ; for( ii=0 ; ii < nvox ; ii++ ) ex[ii] -= exb ; } } } /* if blurring, edit mask a little */ if( mask != NULL && (fwhm > 0.0f || mrad > 0.0f) ){ int ii ; ii = THD_mask_remove_isolas( DSET_NX(inset),DSET_NY(inset),DSET_NZ(inset),mask ) ; if( ii > 0 ){ nmask = THD_countmask( nvmask , mask ) ; if( verb ) ININFO_message("Removed %d isola%s from mask, leaving %d voxels" , ii,(ii==1)?"\0":"s" , nmask ) ; if( nmask < 99 ) ERROR_exit("Too few voxels left in mask after isola removal :-(") ; } } /* convert input to float, which is simpler to deal with */ imin = THD_extract_float_brick(0,inset) ; if( imin == NULL ) ERROR_exit("Can't extract input dataset brick?! :-(") ; DSET_unload(inset) ; if( verb ) INFO_message("Start fitting process") ; /* do the Gaussian blurring */ if( fwhm > 0.0f ){ if( verb ) ININFO_message("Gaussian blur: FWHM=%g mm",fwhm) ; imin->dx = fabsf(DSET_DX(inset)) ; imin->dy = fabsf(DSET_DY(inset)) ; imin->dz = fabsf(DSET_DZ(inset)) ; mri_blur3D_addfwhm( imin , mask , fwhm ) ; } /* do the fitting */ mri_polyfit_verb(verb) ; if( do_byslice ) imout = mri_polyfit_byslice( imin , nord , exar , mask , mrad , meth ) ; else imout = mri_polyfit ( imin , nord , exar , mask , mrad , meth ) ; /* WTF? */ if( imout == NULL ) ERROR_exit("Can't compute polynomial fit :-( !?") ; if( resid == NULL ) mri_free(imin) ; if( ! do_byslice ) fvit = mri_polyfit_get_fitvec() ; /* get coefficients of fit [26 Feb 2019] */ /* scale the fit dataset? */ if( do_mone ){ float sum=0.0f ; int nsum=0 , ii,nvox ; float *par=MRI_FLOAT_PTR(imout) ; nvox = imout->nvox ; for( ii=0 ; ii < nvox ; ii++ ){ if( mask != NULL && mask[ii] == 0 ) continue ; sum += par[ii] ; nsum++ ; } if( nsum > 0 && sum != 0.0f ){ sum = nsum / sum ; if( verb ) ININFO_message("-mone: scaling fit by %g",sum) ; for( ii=0 ; ii < nvox ; ii++ ) par[ii] *= sum ; } } /* if there's a mask, clip values outside of its box */ #undef PF #define PF(i,j,k) par[(i)+(j)*nx+(k)*nxy] if( mask != NULL && do_mclip ){ int xm,xp,ym,yp,zm,zp , ii,jj,kk , nx,ny,nz,nxy ; float *par ; MRI_IMAGE *bim = mri_empty_conforming( imout , MRI_byte ) ; mri_fix_data_pointer(mask,bim) ; if( verb ) ININFO_message("-mclip: polynomial fit to autobox of mask") ; MRI_autobbox( bim , &xm,&xp , &ym,&yp , &zm,&zp ) ; mri_clear_data_pointer(bim) ; mri_free(bim) ; nx = imout->nx ; ny = imout->ny ; nz = imout->nz ; nxy = nx*ny ; par = MRI_FLOAT_PTR(imout) ; for( ii=0 ; ii < xm ; ii++ ) for( kk=0 ; kk < nz ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) PF(ii,jj,kk) = PF(xm,jj,kk) ; for( ii=xp+1 ; ii < nx ; ii++ ) for( kk=0 ; kk < nz ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) PF(ii,jj,kk) = PF(xp,jj,kk) ; for( jj=0 ; jj < ym ; jj++ ) for( kk=0 ; kk < nz ; kk++ ) for( ii=0 ; ii < nx ; ii++ ) PF(ii,jj,kk) = PF(ii,ym,kk) ; for( jj=yp+1 ; jj < ny ; jj++ ) for( kk=0 ; kk < nz ; kk++ ) for( ii=0 ; ii < nx ; ii++ ) PF(ii,jj,kk) = PF(ii,yp,kk) ; for( kk=0 ; kk < zm ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) for( ii=0 ; ii < nx ; ii++ ) PF(ii,jj,kk) = PF(ii,jj,zm) ; for( kk=zp+1 ; kk < nz ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) for( ii=0 ; ii < nx ; ii++ ) PF(ii,jj,kk) = PF(ii,jj,zp) ; } if( mask != NULL ) free(mask) ; /* write outputs */ if( prefix != NULL ){ THD_3dim_dataset *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( "3dPolyfit" , argc,argv , outset ) ; DSET_write(outset) ; WROTE_DSET(outset) ; } if( resid != NULL ){ THD_3dim_dataset *outset = EDIT_empty_copy( inset ) ; float *inar=MRI_FLOAT_PTR(imin) , *outar=MRI_FLOAT_PTR(imout) ; int nx,ny,nz , nxyz , kk ; nx = imout->nx ; ny = imout->ny ; nz = imout->nz ; nxyz = nx*ny*nz ; for( kk=0 ; kk < nxyz ; kk++ ) outar[kk] = inar[kk] - outar[kk] ; mri_free(imin) ; EDIT_dset_items( outset , ADN_prefix , resid , 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( "3dPolyfit" , argc,argv , outset ) ; DSET_write(outset) ; WROTE_DSET(outset) ; } if( cfnam != NULL && fvit != NULL ){ /* won't work with '-byslice' */ char *qn ; qn = STRING_HAS_SUFFIX(cfnam,".1D") ? cfnam : modify_afni_prefix(cfnam,NULL,".1D") ; mri_write_floatvec( qn , fvit ) ; } exit(0) ; }
SEXP R_THD_write_dset(SEXP Sfname, SEXP Sdset, SEXP Opts) { SEXP Rdset, brik, head, names, opt, node_list; int i=0, ip=0, sb, cnt=0, scale = 1, overwrite=0, addFDR=0, kparts=2, *iv=NULL; char *fname = NULL, *head_str, *stmp=NULL, *hist=NULL; NI_group *ngr=NULL; NI_element *nel=NULL; char *listels[3] = {"head","brk","index_list"}; /* the brk is on purpose for backward compatibility */ double *dv=NULL; float *fv=NULL; THD_3dim_dataset *dset = NULL; int debug=0; if (!debug) debug = get_odebug(); /* get the options list, maybe */ PROTECT(Opts = AS_LIST(Opts)); if ((opt = getListElement(Opts,"debug")) != R_NilValue) { debug = (int)INTEGER_VALUE(opt); if (debug>2) set_odebug(debug); if (debug > 1) INFO_message("Debug is %d\n", debug); } /* get the filename */ PROTECT(Sfname = AS_CHARACTER(Sfname)); fname = R_alloc(strlen(CHAR(STRING_ELT(Sfname,0)))+1, sizeof(char)); strcpy(fname, CHAR(STRING_ELT(Sfname,0))); if (debug >1) INFO_message("Output filename %s\n" , fname); /* get the dset structure elements */ PROTECT(Rdset = AS_LIST(Sdset)); if ((head = AS_CHARACTER(getListElement(Rdset,"head"))) == R_NilValue) { ERROR_message("No header found"); UNPROTECT(3); return(R_NilValue); } if (debug > 1) INFO_message("First head element %s\n" , CHAR(STRING_ELT(head,0))); if ((brik = AS_NUMERIC(getListElement(Rdset,"brk"))) == R_NilValue) { ERROR_message("No brick found"); UNPROTECT(3); return(R_NilValue); } dv = NUMERIC_POINTER(brik); if (debug > 1) INFO_message("First brik value %f\n" , dv[0]); ngr = NI_new_group_element(); NI_rename_group(ngr, "AFNI_dataset" ); NI_set_attribute(ngr,"AFNI_prefix", fname); if ((opt = getListElement(Opts,"idcode")) != R_NilValue) { opt = AS_CHARACTER(opt); stmp = (char *)(CHAR(STRING_ELT(opt,0))); if (stmp && !strcmp(stmp,"SET_AT_WRITE_FILENAME")) { stmp = UNIQ_hashcode(fname); NI_set_attribute(ngr, "AFNI_idcode", stmp); free(stmp); } else if (stmp && !strcmp(stmp,"SET_AT_WRITE_RANDOM")) { stmp = UNIQ_idcode() ; NI_set_attribute(ngr, "AFNI_idcode", stmp); free(stmp); } else if (stmp) { NI_set_attribute(ngr, "AFNI_idcode", (char *)(CHAR(STRING_ELT(opt,0)))); } } if ((opt = getListElement(Opts,"scale")) != R_NilValue) { scale = (int)INTEGER_VALUE(opt); if (debug > 1) INFO_message("Scale is %d\n", scale); } if ((opt = getListElement(Opts,"overwrite")) != R_NilValue) { overwrite = (int)INTEGER_VALUE(opt); if (debug > 1) INFO_message("overwrite is %d\n", overwrite); THD_force_ok_overwrite(overwrite) ; if (overwrite) THD_set_quiet_overwrite(1); } if ((opt = getListElement(Opts,"addFDR")) != R_NilValue) { addFDR = (int)INTEGER_VALUE(opt); if (debug > 1) INFO_message("addFDR is %d\n", addFDR); } PROTECT(opt = getListElement(Opts,"hist")); if ( opt != R_NilValue) { opt = AS_CHARACTER(opt); hist = R_alloc(strlen(CHAR(STRING_ELT(opt,0)))+1, sizeof(char)); strcpy(hist, CHAR(STRING_ELT(opt,0))); if (debug > 1) INFO_message("hist is %s\n", hist); } UNPROTECT(1); for (ip=0,i=0; i<length(head); ++i) { head_str = (char *)CHAR(STRING_ELT(head,i)); if (debug > 1) { INFO_message("Adding %s\n", head_str); } nel = NI_read_element_fromstring(head_str); if (!nel->vec) { ERROR_message("Empty attribute vector for\n%s\n" "This is not expected.\n", head_str); UNPROTECT(3); return(R_NilValue); } NI_add_to_group(ngr,nel); } if (debug > 1) INFO_message("Creating dset header\n"); if (!(dset = THD_niml_to_dataset(ngr, 1))) { ERROR_message("Failed to create header"); UNPROTECT(3); return(R_NilValue); } if (debug > 2) { INFO_message("Have header of %d, %d, %d, %d, scale=%d\n", DSET_NX(dset), DSET_NY(dset), DSET_NZ(dset), DSET_NVALS(dset), scale); } for (i=0; i<DSET_NVALS(dset); ++i) { if (debug > 2) { INFO_message("Putting values in sub-brick %d, type %d\n", i, DSET_BRICK_TYPE(dset,i)); } if ( ( DSET_BRICK_TYPE(dset,i) == MRI_byte || DSET_BRICK_TYPE(dset,i) == MRI_short ) ) { EDIT_substscale_brick(dset, i, MRI_double, dv+i*DSET_NVOX(dset), DSET_BRICK_TYPE(dset,i), scale ? -1.0:1.0); } else if ( DSET_BRICK_TYPE(dset,i) == MRI_double ) { EDIT_substitute_brick(dset, i, MRI_double, dv+i*DSET_NVOX(dset)); } else if ( DSET_BRICK_TYPE(dset,i) == MRI_float ) { float *ff=(float*)calloc(DSET_NVOX(dset), sizeof(float)); double *dvi=dv+i*DSET_NVOX(dset); for (ip=0; ip<DSET_NVOX(dset); ++ip) { ff[ip] = dvi[ip]; } EDIT_substitute_brick(dset, i, MRI_float, ff); } } /* THD_update_statistics( dset ) ; */ if (addFDR) { DSET_BRICK_FDRCURVE_ALLKILL(dset) ; DSET_BRICK_MDFCURVE_ALLKILL(dset) ; /* 22 Oct 2008 */ if( addFDR > 0 ){ int nFDRmask=0; /* in the future, perhaps allow for a mask */ byte *FDRmask=NULL; /* to be sent in also, for now, mask is exact */ /* 0 voxels . */ mri_fdr_setmask( (nFDRmask == DSET_NVOX(dset)) ? FDRmask : NULL ) ; ip = THD_create_all_fdrcurves(dset) ; if( ip > 0 ){ if (debug) ININFO_message("created %d FDR curve%s in dataset header", ip,(ip==1)?"\0":"s") ; } else { if (debug) ININFO_message("failed to create FDR curves in dataset header") ; } } } /* Do we have an index_list? */ if ((node_list=AS_INTEGER(getListElement(Rdset,"index_list")))!=R_NilValue) { iv = INTEGER_POINTER(node_list); if (debug > 1) INFO_message("First node index value %d, total (%d)\n", iv[0], length(node_list)); dset->dblk->nnodes = length(node_list); dset->dblk->node_list = (int *)XtMalloc(dset->dblk->nnodes * sizeof(int)); memcpy(dset->dblk->node_list, iv, dset->dblk->nnodes*sizeof(int)); } if (hist) { tross_Append_History(dset, hist); } DSET_write(dset); UNPROTECT(3); return(R_NilValue); }
MRI_IMAGE * mri_multi_threshold_clusterize( MRI_IMAGE *bim , int statcode , float *statpar , MRI_IMAGE *tim , byte *mask , int nthresh , float *pthr , float *cthr , int nnlev , int signmeth ) { int nx,ny,nz , ith,iclu,ptmin,ngood,nadd ; size_t ndar ; float rmm,pval,thr ; MRI_IMAGE *cim ; float *car ; MRI_IMAGE *uim ; float *uar ; MRI_IMAGE *dim ; float *dar ; MRI_IMAGE *eim ; float *ear ; MCW_cluster_array *clar ; MCW_cluster *cl ; ENTRY("mri_multi_threshold_clusterize") ; if( bim == NULL || tim == NULL ) RETURN(NULL) ; if( nthresh < 1 || pthr == NULL || cthr == NULL ) RETURN(NULL) ; nx = bim->nx ; ny = bim->ny ; nz = bim->nz ; if( tim->nx != nx || tim->ny != ny || tim->nz != nz ) RETURN(NULL) ; /* float copy of input volumes */ cim = mri_to_float(bim) ; car = MRI_FLOAT_PTR(cim) ; uim = mri_to_float(tim) ; uar = MRI_FLOAT_PTR(uim) ; /* edit the input volumes as ordered */ if( signmeth == 1 ) mri_threshold( -1.e33 , 0.0 , uim , uim ) ; else if( signmeth == -1 ) mri_threshold( 0.0 , 1.e33 , uim , uim ) ; mri_maskify( uim , mask ) ; mri_maskify( cim , mask ) ; switch( nnlev ){ default: rmm = 1.01f ; break ; /* NN1 */ case 2: rmm = 1.44f ; break ; /* NN2 */ case 3: rmm = 1.75f ; break ; /* NN3 */ } dim = mri_new_vol( nx,ny,nz , MRI_float) ; dar = MRI_FLOAT_PTR(dim) ; ndar = sizeof(float)*nx*ny*nz ; eim = mri_new_vol( nx,ny,nz , MRI_float) ; /* zero filled == output */ ear = MRI_FLOAT_PTR(eim) ; for( ith=0 ; ith < nthresh ; ith++ ){ memcpy( dar , car , ndar ) ; pval = pthr[ith] ; #if 0 if( statcode != FUNC_FT_TYPE && THD_stat_is_2sided(statcode,signmeth) ) pval *= 2.0f ; #endif thr = THD_pval_to_stat(pval,statcode,statpar) ; mri_threshold( -thr , thr , uim , dim ) ; if( signmeth != 66 ) clar = MCW_find_clusters( nx,ny,nz, 1.0f,1.0f,1.0f, MRI_float,dar,rmm ) ; else ERROR_exit("bi_clusterize not implemented yet :-(") ; if( clar == NULL ){ #if 1 ININFO_message("ith=%d pthr=%g thr=%g cthr=%g nclu=0 nclu_good=0 nvox_add=0" , ith,pval,thr,cthr[ith] ) ; #endif continue ; /* pval threshold so high that we got nuthin */ } ptmin = (int)(cthr[ith]+0.951f) ; for( nadd=ngood=iclu=0 ; iclu < clar->num_clu ; iclu++ ){ cl = clar->clar[iclu] ; if( cl->num_pt >= ptmin ){ MCW_cluster_to_vol( nx,ny,nz , MRI_float,ear , cl ) ; ngood++ ; nadd += cl->num_pt ; } } #if 1 ININFO_message("ith=%d pthr=%g thr=%g cthr=%g nclu=%d nclu_good=%d nvox_add=%d" , ith,pval,thr,cthr[ith],(clar==NULL)?0:clar->num_clu,ngood,nadd ) ; #endif } mri_free(dim) ; mri_free(uim) ; mri_free(cim) ; RETURN(eim) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *xset=NULL , *cset ; int nopt=1, datum=MRI_float, nvals, ii; MRI_IMAGE *ysim=NULL ; char *prefix = "Tcorr1D", *smethod="pearson"; char *xnam=NULL , *ynam=NULL ; byte *mask=NULL ; int mask_nx,mask_ny,mask_nz , nmask=0 ; int do_atanh = 0 ; /* 12 Jan 2018 */ /*----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dTcorr1D [options] xset y1D\n" "Computes the correlation coefficient between each voxel time series\n" "in the input 3D+time dataset 'xset' and each column in the 1D time\n" "series file 'y1D', and stores the output values in a new dataset.\n" "\n" "OPTIONS:\n" " -pearson = Correlation is the normal Pearson (product moment)\n" " correlation coefficient [this is the default method].\n" " -spearman = Correlation is the Spearman (rank) correlation\n" " coefficient.\n" " -quadrant = Correlation is the quadrant correlation coefficient.\n" " -ktaub = Correlation is Kendall's tau_b coefficient.\n" " ++ For 'continuous' or finely-discretized data, tau_b and\n" " rank correlation are nearly equivalent (but not equal).\n" " -dot = Doesn't actually compute a correlation coefficient; just\n" " calculates the dot product between the y1D vector(s)\n" " and the dataset time series.\n" "\n" " -Fisher = Apply the 'Fisher' (inverse hyperbolic tangent) transformation\n" " to the results.\n" " ++ It does not make sense to use this with '-ktaub', but if\n" " you want to do it, the program will not stop you.\n" " ++ Cannot be used with '-dot'!\n" "\n" " -prefix p = Save output into dataset with prefix 'p'\n" " [default prefix is 'Tcorr1D'].\n" "\n" " -mask mmm = Only process voxels from 'xset' that are nonzero\n" " in the 3D mask dataset 'mmm'.\n" " ++ Other voxels in the output will be set to zero.\n" "\n" " -float = Save results in float format [the default format].\n" " -short = Save results in scaled short format [to save disk space].\n" " ++ Cannot be used with '-dot'!\n" "\n" "NOTES:\n" "* The output dataset is functional bucket type, with one sub-brick\n" " per column of the input y1D file.\n" "* No detrending, blurring, or other pre-processing options are available;\n" " if you want these things, see 3dDetrend or 3dTproject or 3dcalc.\n" " [In other words, this program presumes you know what you are doing!]\n" "* Also see 3dTcorrelate to do voxel-by-voxel correlation of TWO\n" " 3D+time datasets' time series, with similar options.\n" "* You can extract the time series from a single voxel with given\n" " spatial indexes using 3dmaskave, and then run it with 3dTcorr1D:\n" " 3dmaskave -quiet -ibox 40 30 20 epi_r1+orig > r1_40_30_20.1D\n" " 3dTcorr1D -pearson -Fisher -prefix c_40_30_20 epi_r1+orig r1_40_30_20.1D\n" "* http://en.wikipedia.org/wiki/Correlation\n" "* http://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient\n" "* http://en.wikipedia.org/wiki/Spearman%%27s_rank_correlation_coefficient\n" "* http://en.wikipedia.org/wiki/Kendall_tau_rank_correlation_coefficient\n" "\n" "-- RWCox - Apr 2010\n" " - Jun 2010: Multiple y1D columns; OpenMP; -short; -mask.\n" ) ; PRINT_AFNI_OMP_USAGE("3dTcorr1D",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dTcorr1D main"); machdep(); AFNI_logger("3dTcorr1D",argc,argv); PRINT_VERSION("3dTcorr1D") ; THD_check_AFNI_version("3dTcorr1D") ; /*-- option processing --*/ while( nopt < argc && argv[nopt][0] == '-' ){ if( strcmp(argv[nopt],"-mask") == 0 ){ /* 28 Jun 2010 */ THD_3dim_dataset *mset ; if( ++nopt >= argc ) ERROR_exit("Need argument after '-mask'") ; if( mask != NULL ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[nopt] ) ; CHECK_OPEN_ERROR(mset,argv[nopt]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); mask = THD_makemask( mset , 0 , 0.5f, 0.0f ) ; DSET_delete(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%s'",argv[nopt]) ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < 2 ) ERROR_exit("Mask is too small to process") ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-float") == 0 ){ /* 27 Jun 2010 */ datum = MRI_float ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-short") == 0 ){ datum = MRI_short ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-pearson") == 0 ){ smethod = "pearson" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-dot") == 0 ){ smethod = "dot" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-spearman") == 0 || strcasecmp(argv[nopt],"-rank") == 0 ){ smethod = "spearman" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-quadrant") == 0 ){ smethod = "quadrant" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-ktaub") == 0 || strcasecmp(argv[nopt],"-taub") == 0 ){ smethod = "ktaub" ; nopt++ ; continue ; } if( strcasecmp(argv[nopt],"-fisher") == 0 ){ /* 12 Jan 2018 */ do_atanh = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-prefix") == 0 ){ prefix = argv[++nopt] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal value after -prefix!") ; nopt++ ; continue ; } ERROR_exit("Unknown option: %s",argv[nopt]) ; } /*------------ open datasets, check for legality ------------*/ if( nopt+1 >= argc ) ERROR_exit("Need 2 non-option arguments on command line!?") ; /* despite what the help says, if the 1D file is first, that's OK */ if( STRING_HAS_SUFFIX(argv[nopt],"1D") ){ ININFO_message("reading 1D file %s",argv[nopt]) ; ysim = mri_read_1D( argv[nopt] ) ; ynam = argv[nopt] ; if( ysim == NULL ) ERROR_exit("Can't read 1D file %s",argv[nopt]) ; } else { ININFO_message("reading dataset file %s",argv[nopt]) ; xset = THD_open_dataset( argv[nopt] ) ; xnam = argv[nopt] ; if( xset == NULL ) ERROR_exit("Can't open dataset %s",argv[nopt]) ; } /* read whatever type of file (3D or 1D) we don't already have */ nopt++ ; if( xset != NULL ){ ININFO_message("reading 1D file %s",argv[nopt]) ; ysim = mri_read_1D( argv[nopt] ) ; ynam = argv[nopt] ; if( ysim == NULL ) ERROR_exit("Can't read 1D file %s",argv[nopt]) ; } else { ININFO_message("reading dataset file %s",argv[nopt]) ; xset = THD_open_dataset( argv[nopt] ) ; xnam = argv[nopt] ; if( xset == NULL ) ERROR_exit("Can't open dataset %s",argv[nopt]) ; } nvals = DSET_NVALS(xset) ; /* number of time points */ ii = (strcmp(smethod,"dot")==0) ? 2 : 3 ; if( nvals < ii ) ERROR_exit("Input dataset %s length is less than ii?!",xnam,ii) ; if( ysim->nx < nvals ) ERROR_exit("1D file %s has %d time points, but dataset has %d values", ynam,ysim->nx,nvals) ; else if( ysim->nx > nvals ) WARNING_message("1D file %s has %d time points, dataset has %d", ynam,ysim->nx,nvals) ; if( mri_allzero(ysim) ) ERROR_exit("1D file %s is all zero!",ynam) ; if( ysim->ny > 1 ) INFO_message("1D file %s has %d columns: correlating with ALL of them!", ynam,ysim->ny) ; if( strcmp(smethod,"dot") == 0 && do_atanh ){ WARNING_message("'-dot' turns off '-Fisher'") ; do_atanh = 0 ; } if( strcmp(smethod,"dot") == 0 && datum == MRI_short ){ WARNING_message("'-dot' turns off '-short'") ; datum = MRI_float ; } cset = THD_Tcorr1D( xset, mask, nmask, ysim, smethod, prefix, (datum==MRI_short) , do_atanh ); tross_Make_History( "3dTcorr1D" , argc,argv , cset ) ; DSET_unload(xset) ; /* no longer needful */ /* finito */ DSET_write(cset) ; INFO_message("Wrote dataset: %s\n",DSET_BRIKNAME(cset)) ; exit(0) ; }
int main( int argc , char *argv[] ) { int iarg=1 , ii,nvox , nvals ; THD_3dim_dataset *inset=NULL, *outset=NULL , *mset=NULL ; char *prefix="./blurinmask" ; float fwhm_goal=0.0f ; int fwhm_2D=0 ; byte *mask=NULL ; int mask_nx=0,mask_ny=0,mask_nz=0 , automask=0 , nmask=0 ; float dx,dy,dz=0.0f , *bar , val ; int floatize=0 ; /* 18 May 2009 */ MRI_IMAGE *immask=NULL ; /* 07 Oct 2009 */ short *mmask=NULL ; short *unval_mmask=NULL ; int nuniq_mmask=0 ; int do_preserve=0 , use_qsar ; /* 19 Oct 2009 */ THD_3dim_dataset *fwhmset=NULL ; MRI_IMAGE *fxim=NULL, *fyim=NULL, *fzim=NULL ; /* 13 Jun 2016 */ int niter_fxyz=0 ; float dmax=0.0f , dmin=0.0f ; /*------- help the pitifully ignorant luser? -------*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: ~1~\n" "3dBlurInMask [options]\n" "Blurs a dataset spatially inside a mask. That's all. Experimental.\n" "\n" "OPTIONS ~1~\n" "-------\n" " -input ddd = This required 'option' specifies the dataset\n" " that will be smoothed and output.\n" " -FWHM f = Add 'f' amount of smoothness to the dataset (in mm).\n" " **N.B.: This is also a required 'option'.\n" " -FWHMdset d = Read in dataset 'd' and add the amount of smoothness\n" " given at each voxel -- spatially variable blurring.\n" " ** EXPERIMENTAL EXPERIMENTAL EXPERIMENTAL **\n" " -mask mmm = Mask dataset, if desired. Blurring will\n" " occur only within the mask. Voxels NOT in\n" " the mask will be set to zero in the output.\n" " -Mmask mmm = Multi-mask dataset -- each distinct nonzero\n" " value in dataset 'mmm' will be treated as\n" " a separate mask for blurring purposes.\n" " **N.B.: 'mmm' must be byte- or short-valued!\n" " -automask = Create an automask from the input dataset.\n" " **N.B.: only 1 masking option can be used!\n" " -preserve = Normally, voxels not in the mask will be\n" " set to zero in the output. If you want the\n" " original values in the dataset to be preserved\n" " in the output, use this option.\n" " -prefix ppp = Prefix for output dataset will be 'ppp'.\n" " **N.B.: Output dataset is always in float format.\n" " -quiet = Don't be verbose with the progress reports.\n" " -float = Save dataset as floats, no matter what the\n" " input data type is.\n" " **N.B.: If the input dataset is unscaled shorts, then\n" " the default is to save the output in short\n" " format as well. In EVERY other case, the\n" " program saves the output as floats. Thus,\n" " the ONLY purpose of the '-float' option is to\n" " force an all-shorts input dataset to be saved\n" " as all-floats after blurring.\n" "\n" "NOTES ~1~\n" "-----\n" " * If you don't provide a mask, then all voxels will be included\n" " in the blurring. (But then why are you using this program?)\n" " * Note that voxels inside the mask that are not contiguous with\n" " any other voxels inside the mask will not be modified at all!\n" " * Works iteratively, similarly to 3dBlurToFWHM, but without\n" " the extensive overhead of monitoring the smoothness.\n" " * But this program will be faster than 3dBlurToFWHM, and probably\n" " slower than 3dmerge.\n" " * Since the blurring is done iteratively, rather than all-at-once as\n" " in 3dmerge, the results will be slightly different than 3dmerge's,\n" " even if no mask is used here (3dmerge, of course, doesn't take a mask).\n" " * If the original FWHM of the dataset was 'S' and you input a value\n" " 'F' with the '-FWHM' option, then the output dataset's smoothness\n" " will be about sqrt(S*S+F*F). The number of iterations will be\n" " about (F*F/d*d) where d=grid spacing; this means that a large value\n" " of F might take a lot of CPU time!\n" " * The spatial smoothness of a 3D+time dataset can be estimated with a\n" " command similar to the following:\n" " 3dFWHMx -detrend -mask mmm+orig -input ddd+orig\n" ) ; printf( " * The minimum number of voxels in the mask is %d\n",MASK_MIN) ; printf( " * Isolated voxels will be removed from the mask!\n") ; PRINT_AFNI_OMP_USAGE("3dBlurInMask",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } /*---- official startup ---*/ PRINT_VERSION("3dBlurInMask"); mainENTRY("3dBlurInMask main"); machdep(); AFNI_logger("3dBlurInMask",argc,argv); AUTHOR("RW Cox") ; /*---- loop over options ----*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-preserve",5) == 0 ){ /* 19 Oct 2009 */ do_preserve = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-qui",4) == 0 ){ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-ver",4) == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-input") == 0 || strcmp(argv[iarg],"-dset") == 0 ){ if( inset != NULL ) ERROR_exit("Can't have two -input options") ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-input'") ; inset = THD_open_dataset( argv[iarg] ); CHECK_OPEN_ERROR(inset,argv[iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-prefix'") ; prefix = strdup(argv[iarg]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Bad name after '-prefix'") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-Mmask") == 0 ){ /* 07 Oct 2009 */ if( ++iarg >= argc ) ERROR_exit("Need argument after '-Mmask'") ; if( mmask != NULL || mask != NULL || automask ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(mset,argv[iarg]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); #if 0 if( !MRI_IS_INT_TYPE(DSET_BRICK_TYPE(mset,0)) ) ERROR_exit("-Mmask dataset is not integer type!") ; #endif immask = mri_to_short( 1.0 , DSET_BRICK(mset,0) ) ; mmask = MRI_SHORT_PTR(immask) ; unval_mmask = UniqueShort( mmask, mask_nx*mask_ny*mask_nz, &nuniq_mmask, 0 ) ; if( unval_mmask == NULL || nuniq_mmask == 0 ) ERROR_exit("-Mmask dataset cannot be processed!?") ; if( nuniq_mmask == 1 && unval_mmask[0] == 0 ) ERROR_exit("-Mmask dataset is all zeros!?") ; if( verb ){ int qq , ww ; for( ii=qq=0 ; ii < nuniq_mmask ; ii++ ) if( unval_mmask[ii] != 0 ) qq++ ; for( ii=ww=0 ; ii < immask->nvox ; ii++ ) if( mmask[ii] != 0 ) ww++ ; INFO_message("%d unique nonzero values in -Mmask; %d nonzero voxels",qq,ww) ; } iarg++ ; continue ; } if( strcmp(argv[iarg],"-mask") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '-mask'") ; if( mmask != NULL || mask != NULL || automask ) ERROR_exit("Can't have two mask inputs") ; mset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(mset,argv[iarg]) ; DSET_load(mset) ; CHECK_LOAD_ERROR(mset) ; mask_nx = DSET_NX(mset); mask_ny = DSET_NY(mset); mask_nz = DSET_NZ(mset); mask = THD_makemask( mset , 0 , 0.5f, 0.0f ) ; DSET_unload(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%s'",argv[iarg]) ; ii = THD_mask_remove_isolas( mask_nx,mask_ny,mask_nz , mask ) ; if( verb && ii > 0 ) INFO_message("Removed %d isola%s from mask dataset",ii,(ii==1)?"\0":"s") ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; if( verb ) INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < MASK_MIN ) ERROR_exit("Mask is too small to process") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-automask") == 0 ){ if( mmask != NULL || mask != NULL ) ERROR_exit("Can't have 2 mask inputs") ; automask = 1 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-FWHM") == 0 || strcasecmp(argv[iarg],"-FHWM") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); val = (float)strtod(argv[iarg],NULL) ; if( val <= 0.0f ) ERROR_exit("Illegal value after '%s': '%s'", argv[iarg-1],argv[iarg]) ; fwhm_goal = val ; fwhm_2D = 0 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-FWHMdset") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); if( fwhmset != NULL ) ERROR_exit("You can't use option '-FWHMdset' twice :(") ; fwhmset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(fwhmset,argv[iarg]) ; do_preserve = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-float",6) == 0 ){ /* 18 May 2009 */ floatize = 1 ; iarg++ ; continue ; } #if 0 if( strcmp(argv[iarg],"-FWHMxy") == 0 || strcmp(argv[iarg],"-FHWMxy") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]); val = (float)strtod(argv[iarg],NULL) ; if( val <= 0.0f ) ERROR_exit("Illegal value after '%s': '%s'", argv[iarg-1],argv[iarg]) ; fwhm_goal = val ; fwhm_2D = 1 ; iarg++ ; continue ; } #endif ERROR_exit("Uknown option '%s'",argv[iarg]) ; } /*--- end of loop over options ---*/ /*----- check for stupid inputs, load datasets, et cetera -----*/ if( fwhmset == NULL && fwhm_goal == 0.0f ) ERROR_exit("No -FWHM option given! What do you want?") ; if( fwhmset != NULL && fwhm_goal > 0.0f ){ WARNING_message("-FWHMdset option replaces -FWHM value") ; fwhm_goal = 0.0f ; } if( fwhmset != NULL && mmask != NULL ) ERROR_exit("Sorry: -FWHMdset and -Mmask don't work together (yet)") ; if( inset == NULL ){ if( iarg >= argc ) ERROR_exit("No input dataset on command line?") ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; } nvox = DSET_NVOX(inset) ; dx = fabs(DSET_DX(inset)) ; if( dx == 0.0f ) dx = 1.0f ; dy = fabs(DSET_DY(inset)) ; if( dy == 0.0f ) dy = 1.0f ; dz = fabs(DSET_DZ(inset)) ; if( dz == 0.0f ) dz = 1.0f ; dmax = MAX(dx,dy) ; if( dmax < dz ) dmax = dz ; /* 13 Jun 2016 */ dmin = MIN(dx,dy) ; if( dmin > dz ) dmin = dz ; if( !floatize ){ /* 18 May 2009 */ if( !THD_datum_constant(inset->dblk) || THD_need_brick_factor(inset) || DSET_BRICK_TYPE(inset,0) != MRI_short ){ if( verb ) INFO_message("forcing output to be stored in float format") ; floatize = 1 ; } else { if( verb ) INFO_message("output dataset will be stored as shorts") ; } } else { if( verb ) INFO_message("output dataset will be stored as floats") ; } #if 0 if( DSET_NZ(inset) == 1 && !fwhm_2D ){ WARNING_message("Dataset is 2D ==> switching from -FWHM to -FWHMxy") ; fwhm_2D = 1 ; } #endif /*--- deal with mask or automask ---*/ if( mask != NULL ){ if( mask_nx != DSET_NX(inset) || mask_ny != DSET_NY(inset) || mask_nz != DSET_NZ(inset) ) ERROR_exit("-mask dataset grid doesn't match input dataset") ; } else if( automask ){ mask = THD_automask( inset ) ; if( mask == NULL ) ERROR_message("Can't create -automask from input dataset?") ; nmask = THD_countmask( DSET_NVOX(inset) , mask ) ; if( verb ) INFO_message("Number of voxels in automask = %d",nmask); if( nmask < MASK_MIN ) ERROR_exit("Automask is too small to process") ; } else if( mmask != NULL ){ if( mask_nx != DSET_NX(inset) || mask_ny != DSET_NY(inset) || mask_nz != DSET_NZ(inset) ) ERROR_exit("-Mmask dataset grid doesn't match input dataset") ; } else { mask = (byte *)malloc(sizeof(byte)*nvox) ; nmask = nvox ; memset(mask,1,sizeof(byte)*nvox) ; if( verb ) INFO_message("No mask ==> processing all %d voxels",nvox); } /*--- process FWHMdset [13 Jun 2016] ---*/ if( fwhmset != NULL ){ float *fxar,*fyar,*fzar , *fwar ; MRI_IMAGE *fwim ; float fwmax=0.0f , fsx,fsy,fsz ; int ii, nfpos=0 ; if( DSET_NX(inset) != DSET_NX(fwhmset) || DSET_NY(inset) != DSET_NY(fwhmset) || DSET_NZ(inset) != DSET_NZ(fwhmset) ) ERROR_exit("grid dimensions for FWHMdset and input dataset do not match :(") ; STATUS("get fwim") ; DSET_load(fwhmset) ; fwim = mri_scale_to_float(DSET_BRICK_FACTOR(fwhmset,0),DSET_BRICK(fwhmset,0)); fwar = MRI_FLOAT_PTR(fwim); DSET_unload(fwhmset) ; STATUS("process fwar") ; for( ii=0 ; ii < nvox ; ii++ ){ if( mask[ii] && fwar[ii] > 0.0f ){ nfpos++ ; if( fwar[ii] > fwmax ) fwmax = fwar[ii] ; } else { fwar[ii] = 0.0f ; mask[ii] = 0 ; } } if( nfpos < 100 ) ERROR_exit("Cannot proceed: too few (%d) voxels are positive in -FWHMdset!",nfpos) ; niter_fxyz = (int)rintf(2.0f*fwmax*fwmax*FFAC/(0.05f*dmin*dmin)) + 1 ; if( verb ) INFO_message("-FWHMdset: niter=%d npos=%d",niter_fxyz,nfpos) ; STATUS("create fxim etc.") ; fxim = mri_new_conforming(fwim,MRI_float); fxar = MRI_FLOAT_PTR(fxim); fyim = mri_new_conforming(fwim,MRI_float); fyar = MRI_FLOAT_PTR(fyim); fzim = mri_new_conforming(fwim,MRI_float); fzar = MRI_FLOAT_PTR(fzim); fsx = FFAC/(dx*dx*niter_fxyz) ; fsy = FFAC/(dy*dy*niter_fxyz) ; fsz = FFAC/(dz*dz*niter_fxyz) ; /** INFO_message("fsx=%g fsy=%g fsz=%g",fsx,fsy,fsz) ; **/ for( ii=0 ; ii < nvox ; ii++ ){ if( fwar[ii] > 0.0f ){ fxar[ii] = fwar[ii]*fwar[ii] * fsx ; fyar[ii] = fwar[ii]*fwar[ii] * fsy ; fzar[ii] = fwar[ii]*fwar[ii] * fsz ; } else { fxar[ii] = fyar[ii] = fzar[ii] = 0.0f ; } } STATUS("free(fwim)") ; mri_free(fwim) ; } /*--- process input dataset ---*/ STATUS("load input") ; DSET_load(inset) ; CHECK_LOAD_ERROR(inset) ; outset = EDIT_empty_copy( inset ) ; /* moved here 04 Jun 2007 */ EDIT_dset_items( outset , ADN_prefix , prefix , ADN_none ) ; EDIT_dset_items( outset , ADN_brick_fac , NULL , ADN_none ) ; /* 11 Sep 2007 */ tross_Copy_History( inset , outset ) ; tross_Make_History( "3dBlurInMask" , argc,argv , outset ) ; nvals = DSET_NVALS(inset) ; use_qsar = (do_preserve || mmask != NULL) ; /* 19 Oct 20090 */ AFNI_OMP_START ; #pragma omp parallel if( nvals > 1 ) { MRI_IMAGE *dsim ; int ids,qit ; byte *qmask=NULL ; register int vv ; MRI_IMAGE *qim=NULL, *qsim=NULL; float *qar=NULL, *dsar, *qsar=NULL; #pragma omp critical (MALLOC) { if( use_qsar ){ qsim = mri_new_conforming(DSET_BRICK(inset,0),MRI_float); qsar = MRI_FLOAT_PTR(qsim); } if( mmask != NULL ){ qmask = (byte *)malloc(sizeof(byte)*nvox) ; qim = mri_new_conforming(immask,MRI_float); qar = MRI_FLOAT_PTR(qim); qim->dx = dx ; qim->dy = dy ; qim->dz = dz ; } } #pragma omp for for( ids=0 ; ids < nvals ; ids++ ){ #pragma omp critical (MALLOC) { dsim = mri_scale_to_float(DSET_BRICK_FACTOR(inset,ids),DSET_BRICK(inset,ids)); DSET_unload_one(inset,ids) ; } dsim->dx = dx ; dsim->dy = dy ; dsim->dz = dz ; dsar = MRI_FLOAT_PTR(dsim) ; /* if needed, initialize qsar with data to be preserved in output */ if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) qsar[vv] = dsar[vv] ; } else if( mmask != NULL ){ for( vv=0 ; vv < nvox ; vv++ ) qsar[vv] = 0.0f ; } if( fwhmset != NULL ){ /* 13 Jun 2016: spatially variable blurring */ for( qit=0 ; qit < niter_fxyz ; qit++ ){ mri_blur3D_variable( dsim , mask , fxim,fyim,fzim ) ; } if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) if( mask[vv] ) qsar[vv] = dsar[vv] ; } } else if( mmask != NULL ){ /* 07 Oct 2009: multiple masks */ int qq ; register short uval ; for( qq=0 ; qq < nuniq_mmask ; qq++ ){ uval = unval_mmask[qq] ; if( uval == 0 ) continue ; for( vv=0 ; vv < nvox ; vv++ ) qmask[vv] = (mmask[vv]==uval) ; /* make mask */ (void)THD_mask_remove_isolas( mask_nx,mask_ny,mask_nz , qmask ) ; nmask = THD_countmask( nvox , qmask ) ; if( verb && ids==0 ) ININFO_message("voxels in Mmask[%d] = %d",uval,nmask) ; if( nmask >= MASK_MIN ){ /* copy data from dataset to qar */ for( vv=0 ; vv < nvox ; vv++ ) if( qmask[vv] ) qar[vv] = dsar[vv] ; /* blur qar (output will be zero where qmask==0) */ mri_blur3D_addfwhm( qim , qmask , fwhm_goal ) ; /** the real work **/ /* copy results back to qsar */ for( vv=0 ; vv < nvox ; vv++ ) if( qmask[vv] ) qsar[vv] = qar[vv] ; } } } else { /* the olden way: 1 mask */ mri_blur3D_addfwhm( dsim , mask , fwhm_goal ) ; /** all the work **/ /* dsim will be zero where mask==0; if we want to preserve the input values, copy dsar into qsar now at all mask!=0 voxels, since qsar contains the original data values */ if( do_preserve ){ for( vv=0 ; vv < nvox ; vv++ ) if( mask[vv] ) qsar[vv] = dsar[vv] ; } } /* if necessary, copy combined results in qsar to dsar for output */ if( use_qsar ){ for( vv=0 ; vv < nvox ; vv++ ) dsar[vv] = qsar[vv] ; } if( floatize ){ EDIT_substitute_brick( outset , ids , MRI_float , dsar ) ; } else { #pragma omp critical (MALLOC) { EDIT_substscale_brick( outset , ids , MRI_float , dsar , MRI_short , 1.0f ) ; mri_free(dsim) ; } } } /* end of loop over sub-bricks */ #pragma omp critical (MALLOC) { if( qsim != NULL ) mri_free(qsim); if( immask != NULL ){ free(qmask); mri_free(qim); } } } /* end OpenMP */ AFNI_OMP_END ; if( mask != NULL ) free( mask) ; if( immask != NULL ) mri_free(immask) ; DSET_unload(inset) ; DSET_write(outset) ; WROTE_DSET(outset) ; exit(0) ; }
int main( int argc , char *argv[] ) { double weight[3] , desired[3] , band[6] , h[2048] ; int i , ntap=-666 , nband , iarg=1 ; double fbot=-666.9 , ftop=-999.9 , df,dff , fdel , TR=1.0 , dqq ; /*-- help me if you can ---*/ if( argc < 2 || strcasecmp(argv[1],"-help") == 0 ){ printf( "Usage: FIRdesign [options] fbot ftop ntap\n" "\n" "Uses the Remez algorithm to calculate the FIR filter weights\n" "for a bandpass filter; results are written to stdout in an\n" "unadorned (no header) column of numbers.\n" "Inputs are\n" " fbot = lowest freqency in the pass band.\n" " ftop = highest frequency in the pass band.\n" " * 0 <= fbot < ftop <= 0.5/TR\n" " * Unless the '-TR' option is given, TR=1.\n" " ntap = Number of filter weights (AKA 'taps') to use.\n" " * Define df = 1/(ntap*TR) = frequency resolution:\n" " * Then if fbot < 1.1*df, it will be replaced by 0;\n" " in other words, a pure lowpass filter. This change\n" " is necessary since the duration ntap*TR must be longer\n" " than 1 full cycle of the lowest frequency (1/fbot) in\n" " order to filter out slower frequency components.\n" " * Similarly, if ftop > 0.5/TR-1.1*df, it will be\n" " replaced by 0.5/TR; in other words, a pure\n" " highpass filter.\n" " * If ntap is odd, it will be replaced by ntap+1.\n" " * ntap must be in the range 8..2000 (inclusive).\n" "\n" "OPTIONS:\n" "--------\n" " -TR dd = Set time grid spacing to 'dd' [default is 1.0]\n" " -band fbot ftop = Alternative way to specify the passband\n" " -ntap nnn = Alternative way to specify the number of taps\n" "\n" "EXAMPLES:\n" "---------\n" " FIRdesign 0.01 0.10 180 | 1dplot -stdin\n" " FIRdesign 0.01 0.10 180 | 1dfft -nodetrend -nfft 512 stdin: - \\\n" " | 1dplot -stdin -xaxis 0:0.5:10:10 -dt 0.001953\n" "\n" "The first line plots the filter weights\n" "The second line plots the frequency response (0.001953 = 1/512)\n" "\n" "NOTES:\n" "------\n" "* http://en.wikipedia.org/wiki/Parks-McClellan_filter_design_algorithm\n" "* The Remez algorithm code is written and GPL-ed by Jake Janovetz\n" "* Multiple passbands could be designed this way; let me know if you\n" " need such an option; a Hilbert transform FIR is also possible\n" "* Don't try to be stupidly clever when using this program\n" "* RWCox -- May 2012\n" ) ; exit(0); } /*-- option processing --*/ while( iarg < argc && argv[iarg][0] == '-' ){ /* -TR */ if( strcasecmp(argv[iarg],"-TR") == 0 || strcasecmp(argv[iarg],"-dt") == 0 || strcasecmp(argv[iarg],"-del") == 0 || strcasecmp(argv[iarg],"-dx") == 0 ){ if( ++iarg >= argc ) ERROR_exit("need argument after %s",argv[iarg-1]) ; TR = strtod(argv[iarg],NULL) ; if( TR <= 0.0 ) ERROR_exit("Illegal value after %s",argv[iarg-1]) ; iarg++ ; continue ; } /* -ntap */ if( strcasecmp(argv[iarg],"-ntap") == 0 ){ if( ++iarg >= argc ) ERROR_exit("need argument after %s",argv[iarg-1]) ; ntap = (int)strtod(argv[iarg],NULL) ; if( ntap < 8 || ntap > 2000 ) ERROR_exit("Illegal value after %s",argv[iarg-1]) ; iarg++ ; continue ; } /* -band */ if( strcasecmp(argv[iarg],"-band") == 0 ){ if( ++iarg >= argc-1 ) ERROR_exit("need 2 arguments after %s",argv[iarg-1]) ; fbot = strtod(argv[iarg++],NULL) ; ftop = strtod(argv[iarg++],NULL) ; if( ftop <= fbot ) ERROR_exit("Disorderd values after %s",argv[iarg-3]) ; continue ; } /* ssttooppiidd */ ERROR_exit("Unknown option '%s'",argv[iarg]) ; exit(1) ; } /*-- get fbot ftop if not already present --*/ if( fbot < 0.0f && ftop < 0.0f ){ if( iarg >= argc-1 ) ERROR_exit("Need 2 arguments for fbot ftop") ; fbot = strtod(argv[iarg++],NULL) ; ftop = strtod(argv[iarg++],NULL) ; if( ftop <= fbot ) ERROR_exit("Disorderd fbot ftop values") ; } /*-- get ntap if not already present --*/ if( ntap < 0 ){ if( iarg >= argc ) ERROR_exit("Need argument for ntap") ; ntap = (int)strtod(argv[iarg],NULL) ; if( ntap < 8 || ntap > 2000 ) ERROR_exit("Illegal value after %s",argv[iarg-1]) ; iarg++ ; } /*-- make ntap even, if need be --*/ if( ntap%2 ){ ntap++ ; INFO_message("ntap increased to %d (to be even)",ntap) ; } /*-- scale frequencies by TR to get them into the TR=1 case --*/ fbot *= TR ; ftop *= TR ; df = 1.0/ntap ; fdel = 1.1*df ; dff = 1.0444*df ; /*-- edit frequencies if needed --*/ if( fbot <= fdel && fbot != 0.0 ){ fbot = 0.0 ; INFO_message("fbot re-set to 0") ; } if( ftop >= 0.5-fdel && ftop != 0.5 ){ ftop = 0.5 ; INFO_message("ftop re-set to Nyquist 0.5/TR=%g\n",0.5/TR) ; } if( fbot == 0.0 && ftop == 0.5 ) ERROR_exit("fbot=0 and ftop=Nyquist=0.5/TR=%g ==> nothing to do",0.5/TR) ; /*-- are fbot and ftop too close for comfort? --*/ dqq = 3.0*fdel - (ftop-fbot) ; /* should be negative */ if( dqq > 0.0 ){ dqq *= 0.5 ; INFO_message("fbot=%g and ftop=%g are too close: adjusting",fbot/TR,ftop/TR) ; fbot -= dqq ; ftop += dqq ; ININFO_message("adjusted fbot=%g ftop=%g",fbot/TR,ftop/TR) ; if( fbot <= fdel && fbot != 0.0 ){ fbot = 0.0 ; ININFO_message("and now fbot re-set to 0") ; } if( ftop >= 0.5-fdel && ftop != 0.5 ){ ftop = 0.5 ; ININFO_message("and now ftop re-set to Nyquist 0.5/TR=%g\n",0.5/TR) ; } } /*-- initialize number of bands (might end up as 2 or 3) --*/ nband = 0 ; /*-- reject below fbot, if fbot > 0 --*/ if( fbot > 0.0 ){ weight[nband] = 1.0 ; desired[nband] = 0 ; band[2*nband] = 0.0 ; band[2*nband+1] = fbot-dff ; nband++ ; } /*-- pass between fbot and ftop --*/ weight[nband] = 1.0 ; desired[nband] = 1 ; if( fbot > 0.0 ){ band[2*nband] = fbot+dff ; band[2*nband+1] = (ftop < 0.5) ? ftop-dff : 0.5 ; } else { band[2*nband] = 0.0 ; band[2*nband+1] = ftop-dff ; } nband++ ; /*-- reject above ftop, if ftop < Nyquist --*/ if( ftop < 0.5 ){ weight[nband] = 1.0 ; desired[nband] = 0 ; band[2*nband] = ftop+dff ; band[2*nband+1] = 0.5 ; nband++ ; } /*-- compute FIR weights --*/ remez( h, ntap, nband, band, desired, weight, BANDPASS ) ; /*-- print --*/ for( i=0 ; i < ntap ; i++ ) printf("%23.20f\n", h[i]) ; exit(0) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *dset , *oset=NULL , *tset=NULL ; int nvals , iv , nxyz , ii,jj,kk , iarg , kz,kzold ; float cut1=2.5,cut2=4.0 , sq2p,sfac , fq ; MRI_IMAGE *flim ; char *prefix="despike" , *tprefix=NULL ; int corder=-1 , nref , ignore=0 , polort=2 , nuse , nomask=0 ; int nspike, nbig, nproc ; float **ref ; float c21,ic21 , pspike,pbig ; short *sar , *qar ; byte *tar , *mask=NULL ; float *zar , *yar ; int datum ; int localedit=0 ; /* 04 Apr 2007 */ int verb=1 ; int do_NEW = 0 ; /* 29 Nov 2013 */ MRI_IMAGE *NEW_psinv=NULL ; int dilate = 4 ; /* 04 Dec 2013 */ int ctim = 0 ; /*----- Read command line -----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dDespike [options] dataset\n" "Removes 'spikes' from the 3D+time input dataset and writes\n" "a new dataset with the spike values replaced by something\n" "more pleasing to the eye.\n" "\n" "Method:\n" " * L1 fit a smooth-ish curve to each voxel time series\n" " [see -corder option for description of the curve]\n" " [see -NEW option for a different & faster fitting method]\n" " * Compute the MAD of the difference between the curve and\n" " the data time series (the residuals).\n" " * Estimate the standard deviation 'sigma' of the residuals\n" " as sqrt(PI/2)*MAD.\n" " * For each voxel value, define s = (value-curve)/sigma.\n" " * Values with s > c1 are replaced with a value that yields\n" " a modified s' = c1+(c2-c1)*tanh((s-c1)/(c2-c1)).\n" " * c1 is the threshold value of s for a 'spike' [default c1=2.5].\n" " * c2 is the upper range of the allowed deviation from the curve:\n" " s=[c1..infinity) is mapped to s'=[c1..c2) [default c2=4].\n" "\n" "Options:\n" " -ignore I = Ignore the first I points in the time series:\n" " these values will just be copied to the\n" " output dataset [default I=0].\n" " -corder L = Set the curve fit order to L:\n" " the curve that is fit to voxel data v(t) is\n" "\n" " k=L [ (2*PI*k*t) (2*PI*k*t) ]\n" " f(t) = a+b*t+c*t*t + SUM [ d * sin(--------) + e * cos(--------) ]\n" " k=1 [ k ( T ) k ( T ) ]\n" "\n" " where T = duration of time series;\n" " the a,b,c,d,e parameters are chosen to minimize\n" " the sum over t of |v(t)-f(t)| (L1 regression);\n" " this type of fitting is is insensitive to large\n" " spikes in the data. The default value of L is\n" " NT/30, where NT = number of time points.\n" "\n" " -cut c1 c2 = Alter default values for the spike cut values\n" " [default c1=2.5, c2=4.0].\n" " -prefix pp = Save de-spiked dataset with prefix 'pp'\n" " [default pp='despike']\n" " -ssave ttt = Save 'spikiness' measure s for each voxel into a\n" " 3D+time dataset with prefix 'ttt' [default=no save]\n" " -nomask = Process all voxels\n" " [default=use a mask of high-intensity voxels, ]\n" " [as created via '3dAutomask -dilate 4 dataset'].\n" " -dilate nd = Dilate 'nd' times (as in 3dAutomask). The default\n" " value of 'nd' is 4.\n" " -q[uiet] = Don't print '++' informational messages.\n" "\n" " -localedit = Change the editing process to the following:\n" " If a voxel |s| value is >= c2, then replace\n" " the voxel value with the average of the two\n" " nearest non-spike (|s| < c2) values; the first\n" " one previous and the first one after.\n" " Note that the c1 cut value is not used here.\n" "\n" " -NEW = Use the 'new' method for computing the fit, which\n" " should be faster than the L1 method for long time\n" " series (200+ time points); however, the results\n" " are similar but NOT identical. [29 Nov 2013]\n" " * You can also make the program use the 'new'\n" " method by setting the environment variable\n" " AFNI_3dDespike_NEW\n" " to the value YES; as in\n" " setenv AFNI_3dDespike_NEW YES (csh)\n" " export AFNI_3dDespike_NEW=YES (bash)\n" " * If this variable is set to YES, you can turn off\n" " the '-NEW' processing by using the '-OLD' option.\n" " -->>* For time series more than 500 points long, the\n" " '-OLD' algorithm is tremendously slow. You should\n" " use the '-NEW' algorith in such cases.\n" " ** At some indeterminate point in the future, the '-NEW'\n" " method will become the default!\n" " -->>* As of 29 Sep 2016, '-NEW' is the default if there\n" " is more than 500 points in the time series dataset.\n" "\n" " -NEW25 = A slightly more aggressive despiking approach than\n" " the '-NEW' method.\n" "\n" "Caveats:\n" "* Despiking may interfere with image registration, since head\n" " movement may produce 'spikes' at the edge of the brain, and\n" " this information would be used in the registration process.\n" " This possibility has not been explored or calibrated.\n" "* [LATER] Actually, it seems like the registration problem\n" " does NOT happen, and in fact, despiking seems to help!\n" "* Check your data visually before and after despiking and\n" " registration!\n" " [Hint: open 2 AFNI controllers, and turn Time Lock on.]\n" ) ; PRINT_AFNI_OMP_USAGE("3dDespike",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } /** AFNI package setup and logging **/ mainENTRY("3dDespike main"); machdep(); AFNI_logger("3dDespike",argc,argv); PRINT_VERSION("3dDespike") ; AUTHOR("RW Cox") ; /** parse options **/ if( AFNI_yesenv("AFNI_3dDespike_NEW") ) do_NEW = 1 ; /* 29 Nov 2013 */ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-q",2) == 0 ){ /* 04 Apr 2007 */ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-v",2) == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW") == 0 ){ /* 29 Nov 2013 */ do_NEW = 1 ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW25") == 0 ){ /* 29 Sep 2016 */ do_NEW = 1 ; use_des25 = 1 ; cut1 = 2.5f ; cut2 = 3.2f ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-OLD") == 0 ){ do_NEW = 0 ; iarg++ ; continue ; } /** -localedit **/ if( strcmp(argv[iarg],"-localedit") == 0 ){ /* 04 Apr 2007 */ localedit = 1 ; iarg++ ; continue ; } /** don't use masking **/ if( strcmp(argv[iarg],"-nomask") == 0 ){ nomask = 1 ; iarg++ ; continue ; } /** dilation count [04 Dec 2013] **/ if( strcmp(argv[iarg],"-dilate") == 0 ){ dilate = (int)strtod(argv[++iarg],NULL) ; if( dilate <= 0 ) dilate = 1 ; else if( dilate > 99 ) dilate = 99 ; iarg++ ; continue ; } /** output dataset prefix **/ if( strcmp(argv[iarg],"-prefix") == 0 ){ prefix = argv[++iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("-prefix is not good"); iarg++ ; continue ; } /** ratio dataset prefix **/ if( strcmp(argv[iarg],"-ssave") == 0 ){ tprefix = argv[++iarg] ; if( !THD_filename_ok(tprefix) ) ERROR_exit("-ssave prefix is not good"); iarg++ ; continue ; } /** trigonometric polynomial order **/ if( strcmp(argv[iarg],"-corder") == 0 ){ corder = strtol( argv[++iarg] , NULL , 10 ) ; if( corder < 0 ) ERROR_exit("Illegal value of -corder"); iarg++ ; continue ; } /** how much to ignore at start **/ if( strcmp(argv[iarg],"-ignore") == 0 ){ ignore = strtol( argv[++iarg] , NULL , 10 ) ; if( ignore < 0 ) ERROR_exit("Illegal value of -ignore"); iarg++ ; continue ; } /** thresholds for s ratio **/ if( strcmp(argv[iarg],"-cut") == 0 ){ cut1 = strtod( argv[++iarg] , NULL ) ; cut2 = strtod( argv[++iarg] , NULL ) ; if( cut1 < 1.0 || cut2 < cut1+0.5 ) ERROR_exit("Illegal values after -cut"); iarg++ ; continue ; } ERROR_exit("Unknown option: %s",argv[iarg]) ; } c21 = cut2-cut1 ; ic21 = 1.0/c21 ; /*----- read input dataset -----*/ if( iarg >= argc ) ERROR_exit("No input dataset!!??"); dset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(dset,argv[iarg]) ; datum = DSET_BRICK_TYPE(dset,0) ; if( (datum != MRI_short && datum != MRI_float) || !DSET_datum_constant(dset) ) ERROR_exit("Can't process non-short, non-float dataset!") ; if( verb ) INFO_message("Input data type = %s\n",MRI_TYPE_name[datum]) ; nvals = DSET_NUM_TIMES(dset) ; nuse = nvals - ignore ; if( nuse < 15 ) ERROR_exit("Can't use dataset with < 15 time points per voxel!") ; if( nuse > 500 && !do_NEW ){ INFO_message("Switching to '-NEW' method since number of time points = %d > 500",nuse) ; do_NEW = 1 ; } if( use_des25 && nuse < 99 ) use_des25 = 0 ; if( verb ) INFO_message("ignoring first %d time points, using last %d",ignore,nuse); if( corder > 0 && 4*corder+2 > nuse ){ ERROR_exit("-corder %d is too big for NT=%d",corder,nvals) ; } else if( corder < 0 ){ corder = rint(nuse/30.0) ; if( corder > 50 && !do_NEW ) corder = 50 ; if( verb ) INFO_message("using %d time points => -corder %d",nuse,corder) ; } else { if( verb ) INFO_message("-corder %d set from command line",corder) ; } nxyz = DSET_NVOX(dset) ; if( verb ) INFO_message("Loading dataset %s",argv[iarg]) ; DSET_load(dset) ; CHECK_LOAD_ERROR(dset) ; /*-- create automask --*/ if( !nomask ){ mask = THD_automask( dset ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } for( ii=0 ; ii < dilate ; ii++ ) THD_mask_dilate( DSET_NX(dset), DSET_NY(dset), DSET_NZ(dset), mask, 3 ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the dilated automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } } else { if( verb ) INFO_message("processing all %d voxels in dataset",DSET_NVOX(dset)) ; } /*-- create empty despiked dataset --*/ oset = EDIT_empty_copy( dset ) ; EDIT_dset_items( oset , ADN_prefix , prefix , ADN_brick_fac , NULL , ADN_datum_all , datum , ADN_none ) ; if( THD_deathcon() && THD_is_file(DSET_HEADNAME(oset)) ) ERROR_exit("output dataset already exists: %s",DSET_HEADNAME(oset)); tross_Copy_History( oset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , oset ) ; /* create bricks (will be filled with zeros) */ for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( oset , iv , datum , NULL ) ; /* copy the ignored bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < ignore ; iv++ ){ sar = DSET_ARRAY(oset,iv) ; qar = DSET_ARRAY(dset,iv) ; memcpy( sar , qar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; case MRI_float: for( iv=0 ; iv < ignore ; iv++ ){ zar = DSET_ARRAY(oset,iv) ; yar = DSET_ARRAY(dset,iv) ; memcpy( zar , yar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; } /*-- setup to save a threshold statistic dataset, if desired --*/ if( tprefix != NULL ){ float *fac ; tset = EDIT_empty_copy( dset ) ; fac = (float *) malloc( sizeof(float) * nvals ) ; for( ii=0 ; ii < nvals ; ii++ ) fac[ii] = TFAC ; EDIT_dset_items( tset , ADN_prefix , tprefix , ADN_brick_fac , fac , ADN_datum_all , MRI_byte , ADN_func_type , FUNC_FIM_TYPE , ADN_none ) ; free(fac) ; tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; #if 0 if( THD_is_file(DSET_HEADNAME(tset)) ) ERROR_exit("-ssave dataset already exists"); #endif tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( tset , iv , MRI_byte , NULL ) ; } /*-- setup to find spikes --*/ sq2p = sqrt(0.5*PI) ; sfac = sq2p / 1.4826f ; /* make ref functions */ nref = 2*corder+3 ; ref = (float **) malloc( sizeof(float *) * nref ) ; for( jj=0 ; jj < nref ; jj++ ) ref[jj] = (float *) malloc( sizeof(float) * nuse ) ; /* r(t) = 1 */ for( iv=0 ; iv < nuse ; iv++ ) ref[0][iv] = 1.0 ; jj = 1 ; /* r(t) = t - tmid */ { float tm = 0.5 * (nuse-1.0) ; float fac = 2.0 / nuse ; for( iv=0 ; iv < nuse ; iv++ ) ref[1][iv] = (iv-tm)*fac ; jj = 2 ; /* r(t) = (t-tmid)**jj */ for( ; jj <= polort ; jj++ ) for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = pow( (iv-tm)*fac , (double)jj ) ; } for( kk=1 ; kk <= corder ; kk++ ){ fq = (2.0*PI*kk)/nuse ; /* r(t) = sin(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = sin(fq*iv) ; jj++ ; /* r(t) = cos(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = cos(fq*iv) ; jj++ ; } /****** setup for the NEW solution method [29 Nov 2013] ******/ if( do_NEW ){ NEW_psinv = DES_get_psinv(nuse,nref,ref) ; INFO_message("Procesing time series with NEW model fit algorithm") ; } else { INFO_message("Procesing time series with OLD model fit algorithm") ; } /*--- loop over voxels and do work ---*/ #define Laplace_t2p(val) ( 1.0 - nifti_stat2cdf( (val), 15, 0.0, 1.4427 , 0.0 ) ) if( verb ){ if( !localedit ){ INFO_message("smash edit thresholds: %.1f .. %.1f MADs",cut1*sq2p,cut2*sq2p) ; ININFO_message(" [ %.3f%% .. %.3f%% of normal distribution]", 200.0*qg(cut1*sfac) , 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% .. %.3f%% of Laplace distribution]" , 100.0*Laplace_t2p(cut1) , 100.0*Laplace_t2p(cut2) ) ; } else { INFO_message("local edit threshold: %.1f MADS",cut2*sq2p) ; ININFO_message(" [ %.3f%% of normal distribution]", 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% of Laplace distribution]", 100.0*Laplace_t2p(cut1) ) ; } INFO_message("%d slices to process",DSET_NZ(dset)) ; } kzold = -1 ; nspike = 0 ; nbig = 0 ; nproc = 0 ; ctim = NI_clock_time() ; AFNI_OMP_START ; #pragma omp parallel if( nxyz > 6666 ) { int ii , iv , iu , id , jj ; float *far , *dar , *var , *fitar , *ssp , *fit , *zar ; short *sar , *qar ; byte *tar ; float fsig , fq , cls , snew , val ; float *NEW_wks=NULL ; #pragma omp critical (DESPIKE_malloc) { far = (float *) malloc( sizeof(float) * nvals ) ; dar = (float *) malloc( sizeof(float) * nvals ) ; var = (float *) malloc( sizeof(float) * nvals ) ; fitar = (float *) malloc( sizeof(float) * nvals ) ; ssp = (float *) malloc( sizeof(float) * nvals ) ; fit = (float *) malloc( sizeof(float) * nref ) ; if( do_NEW ) NEW_wks = (float *)malloc(sizeof(float)*DES_workspace_size(nuse,nref)) ; } #ifdef USE_OMP INFO_message("start OpenMP thread #%d",omp_get_thread_num()) ; #endif #pragma omp for for( ii=0 ; ii < nxyz ; ii++ ){ /* ii = voxel index */ if( mask != NULL && mask[ii] == 0 ) continue ; /* skip this voxel */ #ifndef USE_OMP kz = DSET_index_to_kz(dset,ii) ; /* starting a new slice */ if( kz != kzold ){ if( verb ){ fprintf(stderr, "++ start slice %2d",kz ) ; if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; fprintf(stderr, "; so far %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } fprintf(stderr,"\n") ; } kzold = kz ; } #else if( verb && ii % 2345 == 1234 ) fprintf(stderr,".") ; #endif /*** extract ii-th time series into far[] ***/ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ qar = DSET_ARRAY(dset,iv+ignore) ; /* skip ignored data */ far[iv] = (float)qar[ii] ; } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(dset,iv+ignore) ; far[iv] = zar[ii] ; } break ; } AAmemcpy(dar,far,sizeof(float)*nuse) ; /* copy time series into dar[] */ /*** solve for L1 fit ***/ if( do_NEW ) cls = DES_solve( NEW_psinv , far , fit , NEW_wks ) ; /* 29 Nov 2013 */ else cls = cl1_solve( nuse , nref , far , ref , fit,0 ) ; /* the slow part */ if( cls < 0.0f ){ /* fit failed! */ #if 0 fprintf(stderr,"curve fit fails at voxel %d %d %d\n", DSET_index_to_ix(dset,ii) , DSET_index_to_jy(dset,ii) , DSET_index_to_kz(dset,ii) ) ; #endif continue ; /* skip this voxel */ } for( iv=0 ; iv < nuse ; iv++ ){ /* detrend */ val = fit[0] + fit[1]*ref[1][iv] /* quadratic part of curve fit */ + fit[2]*ref[2][iv] ; for( jj=3 ; jj < nref ; jj++ ) /* rest of curve fit */ val += fit[jj] * ref[jj][iv] ; fitar[iv] = val ; /* save curve fit value */ var[iv] = dar[iv]-val ; /* remove fitted value = resid */ far[iv] = fabsf(var[iv]) ; /* abs value of resid */ } /*** compute estimate standard deviation of detrended data ***/ fsig = sq2p * qmed_float(nuse,far) ; /* also mangles far array */ /*** process time series for spikes, editing data in dar[] ***/ if( fsig > 0.0f ){ /* data wasn't fit perfectly */ /* find spikiness for each point in time */ fq = 1.0f / fsig ; for( iv=0 ; iv < nuse ; iv++ ){ ssp[iv] = fq * var[iv] ; /* spikiness s = how many sigma out */ } /* save spikiness in -ssave datset */ if( tset != NULL ){ for( iv=0 ; iv < nuse ; iv++ ){ tar = DSET_ARRAY(tset,iv+ignore) ; snew = ITFAC*fabsf(ssp[iv]) ; /* scale for byte storage */ tar[ii] = BYTEIZE(snew) ; /* cf. mrilib.h */ } } /* process values of |s| > cut1, editing dar[] */ for( iv=0 ; iv < nuse ; iv++ ){ /* loop over time points */ if( !localedit ){ /** classic 'smash' edit **/ if( ssp[iv] > cut1 ){ snew = cut1 + c21*mytanh((ssp[iv]-cut1)*ic21) ; /* edit s down */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] > cut2 ) nbig++ ; } } else if( ssp[iv] < -cut1 ){ snew = -cut1 + c21*mytanh((ssp[iv]+cut1)*ic21) ; /* edit s up */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] < -cut2 ) nbig++ ; } } } else { /** local edit: 04 Apr 2007 **/ if( ssp[iv] >= cut2 || ssp[iv] <= -cut2 ){ for( iu=iv+1 ; iu < nuse ; iu++ ) /* find non-spike above */ if( ssp[iu] < cut2 && ssp[iu] > -cut2 ) break ; for( id=iv-1 ; id >= 0 ; id-- ) /* find non-spike below */ if( ssp[id] < cut2 && ssp[id] > -cut2 ) break ; switch( (id>=0) + 2*(iu<nuse) ){ /* compute replacement val */ case 3: val = 0.5*(dar[iu]+dar[id]); break; /* iu and id OK */ case 2: val = dar[iu] ; break; /* only iu OK */ case 1: val = dar[id] ; break; /* only id OK */ default: val = fitar[iv] ; break; /* shouldn't be */ } dar[iv] = val ; #pragma omp critical (DESPIKE_counter) { nspike++ ; nbig++ ; } } } } /* end of loop over time points */ #pragma omp atomic nproc += nuse ; /* number data points processed */ } /* end of processing time series when fsig is positive */ /* put dar[] time series (possibly edited above) into output bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ sar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ sar[ii] = (short)dar[iv] ; /* original or mutated data */ } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ zar[ii] = dar[iv] ; /* original or mutated data */ } break ; } } /* end of loop over voxels #ii */ #pragma omp critical (DESPIKE_malloc) { free(fit); free(ssp); free(fitar); free(var); free(dar); free(far); if( do_NEW ) free(NEW_wks) ; } } /* end OpenMP */ AFNI_OMP_END ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif ctim = NI_clock_time() - ctim ; INFO_message( "Elapsed despike time = %s" , nice_time_string(ctim) ) ; if( ctim > 345678 && !do_NEW ) ININFO_message("That was SLOW -- try the '-NEW' option for a speedup") ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif /*--- finish up ---*/ if( do_NEW ) mri_free(NEW_psinv) ; DSET_delete(dset) ; /* delete input dataset */ if( verb ){ if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; INFO_message("FINAL: %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } else { INFO_message("FINAL: no good voxels found to process!!??") ; } } /* write results */ DSET_write(oset) ; if( verb ) WROTE_DSET(oset) ; DSET_delete(oset) ; if( tset != NULL ){ DSET_write(tset) ; if( verb ) WROTE_DSET(tset) ; DSET_delete(tset) ; } exit( THD_get_write_error_count() ) ; }
int powell_newuoa_constrained( int ndim, double *x, double *cost , double *xbot, double *xtop , int nrand, int nkeep, int ntry , double rstart , double rend , int maxcall , double (*ufunc)(int,double *) ) { integer n , npt , icode , maxfun ; doublereal rhobeg , rhoend , *w ; int ii,tt , tbest , ntot=0 , nx01 ; double **x01 , *x01val , vbest ; /*--- check inputs for stupidity ---*/ if( ndim < 1 ) return -2 ; if( x == NULL ) return -3 ; if( ufunc == NULL ) return -5 ; if( xbot == NULL || xtop == NULL ) return -6 ; /*--- edit inputs for idiocy ---*/ if( rstart <= rend || rstart <= 1.e-4 || rstart > 0.333 ){ rstart = 0.1 ; rend = 1.e-4 ; } /* if this call is totally vanilla, then call the older function */ if( nrand == 0 && nkeep == 0 && ntry < 2 ) return powell_newuoa_con( ndim , x,xbot,xtop , 0 , rstart,rend , maxcall , ufunc ) ; if( maxcall < 10+5*ndim ) maxcall = 10+5*ndim ; if( ntry < 1 ) ntry = 1 ; if( nkeep < 1 && nrand > 0 ) nkeep = 1 ; if( nkeep > 0 && nrand <= 2*nkeep ) nrand = 2*nkeep+1 ; /*--- set up newuoa parameters and workspace ---*/ n = ndim ; npt = (int)(mfac*n+afac) ; if( npt < n+2 ) npt = n+2 ; icode = (n+1)*(n+2)/2 ; if( npt > icode ) npt = icode ; maxfun = maxcall ; rhobeg = (doublereal)rstart ; rhoend = (doublereal)rend ; icode = (npt+14)*(npt+n) + 3*n*(n+3)/2 + 666 ; w = (doublereal *)malloc(sizeof(doublereal)*icode) ; /* workspace */ userfun = ufunc ; /* store pointer to user's function in global variable */ /*-- To enforce constraints: (a) scale each variable to be in the range 0..1, in x01[] array; (b) in calfun_(), if an input variable drifts outside the 0..1 range, bring it back into that range with the PRED01() macro; (c) then scale that 0..1 value back to the 'true' value before calling ufunc() to evaluate objective function. -------*/ scalx = 1 ; /* signal to calfun_() to apply scaling */ sxmin = (double *)malloc(sizeof(double)*ndim) ; /* copy xbot for calfun_ */ sxsiz = (double *)malloc(sizeof(double)*ndim) ; /* = xtop - xbot */ sx = (double *)malloc(sizeof(double)*ndim) ; /* workspace for calfun_ */ for( ii=0 ; ii < ndim ; ii++ ){ sxmin[ii] = xbot[ii] ; sxsiz[ii] = xtop[ii] - xbot[ii]; if( sxsiz[ii] <= 0.0 ) sxsiz[ii] = 1.0 ; } /*-- set up the first starting vector from x[] array --*/ nx01 = nkeep+2 ; x01 = (double **)malloc(sizeof(double *)*nx01) ; /* array of vectors */ for( tt=0 ; tt < nx01 ; tt++ ) x01[tt] = (double *)malloc(sizeof(double)*ndim) ; /* tt-th keeper vector */ x01val = (double *)malloc(sizeof(double)*nx01) ; /* cost func values */ for( tt=0 ; tt < nx01 ; tt++ ) x01val[tt] = BIGVAL; /* mark as unready */ /* copy x[] into x01[0], scaling to 0..1 range */ for( ii=0 ; ii < ndim ; ii++ ){ x01[0][ii] = (x[ii] - sxmin[ii]) / sxsiz[ii] ; x01[0][ii] = PRED01(x01[0][ii]) ; /* make sure is in range 0..1 */ } (void)calfun_(&n,x01[0],x01val+0) ; /* value of keeper #0 = input vector */ ntot++ ; /* number of times calfun_() is called */ /*-- do a random search for the best starting vector? --*/ if( nrand > 0 ){ double *xtest , *qpar,*cpar , ftest , rb,re , dist ; int qq,jj ; integer mf ; static int seed=1 ; if( seed ){ srand48((long)time(NULL)+(long)getpid()); seed=0; } xtest = (double *)malloc(sizeof(double)*ndim) ; /* Step 1: search nrand start vectors, keeping the nkeep-th best values we find on the way. N.B.: we do NOT displace the input vector in x01[0]; therefore, there are nkeep+1 vectors being kept */ if( verb ) INFO_message("Powell: random search of %d vectors in %d-dim space",nrand,ndim); for( qq=0 ; qq < nrand ; qq++ ){ for( ii=0 ; ii < ndim ; ii++ ) xtest[ii] = drand48() ; /* random pt */ (void)calfun_(&n,xtest,&ftest) ; ntot++ ; /* eval cost func */ for( tt=1 ; tt <= nkeep ; tt++ ){ /* is this better than what */ if( ftest < x01val[tt] ){ /* we've seen thus far? */ for( jj=nkeep-1 ; jj >= tt ; jj-- ){ /* push those above #tt up */ memcpy( x01[jj+1] , x01[jj] , sizeof(double)*ndim ) ; /* in list */ x01val[jj+1] = x01val[jj] ; } memcpy( x01[tt] , xtest , sizeof(double)*ndim ) ; /* save in list */ x01val[tt] = ftest ; break ; /* breaking out of 'tt' loop, having put xtest in list */ } } } /* end of random search loop */ free((void *)xtest) ; /* don't need this no more */ /* count number that have valid cost function results */ for( tt=0 ; tt <= nkeep && x01val[tt] < BIGVAL ; tt++ ) ; /* nada */ nkeep = tt ; /** from now on, nkeep = actual number of keepers **/ /* Step 2a: do a little first round optimization on each of the keepers */ if( verb ) INFO_message("Powell: 1st round optimization on %d vectors",nkeep); rb = 0.05 ; re = 0.005 ; mf = 9*ndim+7 ; tbest = 0 ; vbest = BIGVAL ; for( tt=0 ; tt < nkeep ; tt++ ){ (void)newuoa_( &n, &npt, (doublereal *)x01[tt], &rb,&re,&mf,w,&icode ) ; for( ii=0 ; ii < ndim ; ii++ ) x01[tt][ii] = PRED01(x01[tt][ii]) ; (void)calfun_(&n,x01[tt],x01val+tt) ; ntot += icode+1 ; if( x01val[tt] < vbest ){ vbest = x01val[tt]; tbest = tt; } if( verb > 1 ) ININFO_message("%2d: cost = %g %c nfunc=%d",tt,x01val[tt],(tbest==tt)?'*':' ',icode) ; } /* Step 2b: sort results by new x01val costs */ qsort_doublestuff( nkeep , x01val , (void **)x01 ) ; /* Step 2c: cast out those that are too close to better vectors in the max-norm (we always keep the best vector in x01[0]) */ #undef DTHRESH #define DTHRESH 0.05 /* max-norm distance threshold for vector reject */ for( tt=1 ; tt < nkeep ; tt++ ){ qpar = x01[tt] ; /* do we keep this vector? */ for( jj=0 ; jj < tt ; jj++ ){ /* loop over previous keepers */ if( x01val[jj] >= BIGVAL ) continue ; /* already rejected */ cpar = x01[jj] ; /* compare qpar to cpar */ for( dist=0.0,ii=0 ; ii < ndim ; ii++ ){ re = fabs(cpar[ii]-qpar[ii]) ; dist = MAX(dist,re) ; } if( dist < DTHRESH ){ /* qpar is too close to cpar */ x01val[tt] = BIGVAL ; break ; /* reject qpar vector */ } } } /* Step 2d: sort again (so that the rejected ones rise to the top) */ qsort_doublestuff( nkeep , x01val , (void **)x01 ) ; for( tt=0 ; tt <= nkeep && x01val[tt] < BIGVAL ; tt++ ) ; /* nada */ nkeep = tt ; /* number of keepers that weren't rejected above */ if( ntry > nkeep ) ntry = nkeep ; } else { /*------ didn't do random search -----*/ ntry = 1 ; /* can only try the input x[] vector! */ } /****** fully optimize each of the first ntry-th vectors in x01[] ******/ if( verb ) INFO_message("Powell: 2nd round optimization on %d vectors",ntry) ; tbest = 0 ; vbest = BIGVAL ; for( tt=0 ; tt < ntry ; tt++ ){ (void)newuoa_( &n , &npt , (doublereal *)x01[tt] , &rhobeg , &rhoend , &maxfun , w , &icode ) ; for( ii=0 ; ii < ndim ; ii++ ) x01[tt][ii] = PRED01(x01[tt][ii]) ; (void)calfun_(&n,x01[tt],x01val+tt) ; ntot += icode+1 ; if( x01val[tt] < vbest ){ vbest = x01val[tt]; tbest = tt; } if( verb > 1 ) ININFO_message("%2d: cost = %g %c nfunc=%d",tt,x01val[tt],(tbest==tt)?'*':' ',icode) ; } /*-- Rescale bestest output vector back to 'true' range --*/ for( ii=0 ; ii < ndim ; ii++ ) x[ii] = sxmin[ii] + x01[tbest][ii] * sxsiz[ii] ; if( cost != NULL ) *cost = vbest ; /* save cost func */ /*-- toss the trash, and vamoose the ranch --*/ free((void *)sx); free((void *)sxsiz); free((void *)sxmin); sx = sxmin = sxsiz = NULL ; scalx = 0 ; for( tt=0 ; tt < nx01 ; tt++ ) free((void *)x01[tt]) ; free((void *)x01val); free((void *)x01); free((void *)w) ; return ntot ; }
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=NULL; char *prefix="RSFC" ; 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 ; // @@ non-BP variables float fbotALL=0.0f, ftopALL=999999.9f; // do full range version int NumDen = 0; // switch for doing numerator or denom THD_3dim_dataset *outsetALL=NULL ; int m, mm; float delf; // harmonics int ind_low,ind_high,N_ny, ctr; float sqnt,nt_fac; gsl_fft_real_wavetable *real1, *real2; // GSL stuff gsl_fft_real_workspace *work; double *series1, *series2; double *xx1,*xx2; float numer,denom,val; float *alff=NULL,*malff=NULL,*falff=NULL, *rsfa=NULL,*mrsfa=NULL,*frsfa=NULL; // values float meanALFF=0.0f,meanRSFA=0.0f; // will be for mean in brain region THD_3dim_dataset *outsetALFF=NULL; THD_3dim_dataset *outsetmALFF=NULL; THD_3dim_dataset *outsetfALFF=NULL; THD_3dim_dataset *outsetRSFA=NULL; THD_3dim_dataset *outsetmRSFA=NULL; THD_3dim_dataset *outsetfRSFA=NULL; char out_lff[300]; char out_alff[300]; char out_malff[300]; char out_falff[300]; char out_rsfa[300]; char out_mrsfa[300]; char out_frsfa[300]; char out_unBP[300]; int SERIES_OUT = 1; int UNBP_OUT = 0; int DO_RSFA = 1; int BP_LAST = 0; // option for only doing filter to LFFs at very end of proc float de_rsfa=0.0f,nu_rsfa=0.0f; double pow1=0.0,pow2=0.0; /*-- help? --*/ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "\n Program to calculate common resting state functional connectivity (RSFC)\n" " parameters (ALFF, mALFF, fALFF, RSFA, etc.) for resting state time\n" " series. This program is **heavily** based on the existing\n" " 3dBandPass by RW Cox, with the amendments to calculate RSFC\n" " parameters written by PA Taylor (July, 2012).\n" " This program is part of FATCAT (Taylor & Saad, 2013) in AFNI. Importantly,\n" " its functionality can be included in the `afni_proc.py' processing-script \n" " generator; see that program's help file for an example including RSFC\n" " and spectral parameter calculation via the `-regress_RSFC' option.\n" "\n" "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" "\n" " All options of 3dBandPass may be used here (with a couple other\n" " parameter options, as well): essentially, the motivation of this\n" " program is to produce ALFF, etc. values of the actual RSFC time\n" " series that you calculate. Therefore, all the 3dBandPass processing\n" " you normally do en route to making your final `resting state time\n" " series' is done here to generate your LFFs, from which the\n" " amplitudes in the LFF band are calculated at the end. In order to\n" " calculate fALFF, the same initial time series are put through the\n" " same processing steps which you have chosen but *without* the\n" " bandpass part; the spectrum of this second time series is used to\n" " calculate the fALFF denominator.\n" " \n" " For more information about each RSFC parameter, see, e.g.: \n" " ALFF/mALFF -- Zang et al. (2007),\n" " fALFF -- Zou et al. (2008),\n" " RSFA -- Kannurpatti & Biswal (2008).\n" "\n" "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" "\n" " + USAGE: 3dRSFC [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 3dRSFC 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" "* 3dRSFC 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 3dRSFC will\n" " depend on the order in which you run these programs. That's why\n" " 3dRSFC 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), and\n" " for the LFFs, the bandpass is done between the specified fbot and ftop,\n" " while for the `whole spectrum' (i.e., fALFF denominator) the bandpass is:\n" " done only to exclude the time series mean and the Nyquist frequency:\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" " (9) Calculate spectrum and amplitudes, for RSFC parameters.\n" "\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. Name of filtered time\n" " series would be, e.g., ppp_LFF+orig.*, and the parameter\n" " outputs are named with obvious suffices.\n" " -quiet = Turn off the fun and informative messages. (Why?)\n" " -no_rs_out = Don't output processed time series-- just output\n" " parameters (not recommended, since the point of\n" " calculating RSFC params here is to have them be quite\n" " related to the time series themselves which are used for\n" " further analysis)." " -un_bp_out = Output the un-bandpassed series as well (default is not \n" " to). Name would be, e.g., ppp_unBP+orig.* .\n" " with suffix `_unBP'.\n" " -no_rsfa = If you don't want RSFA output (default is to do so).\n" " -bp_at_end = A (probably unnecessary) switch to have bandpassing be \n" " the very last processing step that is done in the\n" " sequence of steps listed above; at Step 3 above, only \n" " the time series mean and nyquist are BP'ed out, and then\n" " the LFF series is created only after Step 9. NB: this \n" " probably makes only very small changes for most\n" " processing sequences (but maybe not, depending usage).\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-\n" " voxel correlations via InstaCorr.\n" " ++ No other tests are made [yet] for non-stationary \n" " behavior in the time series data.\n" "\n" "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" "\n" " If you use this program, please reference the introductory/description\n" " paper for the FATCAT toolbox:\n" " Taylor PA, Saad ZS (2013). FATCAT: (An Efficient) Functional\n" " And Tractographic Connectivity Analysis Toolbox. Brain \n" " Connectivity 3(5):523-535.\n" "____________________________________________________________________________\n" ); PRINT_AFNI_OMP_USAGE( " 3dRSFC" , " * At present, the only part of 3dRSFC that is parallelized is the\n" " '-blur' option, which processes each sub-brick independently.\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } /*-- startup --*/ mainENTRY("3dRSFC"); machdep(); AFNI_logger("3dRSFC",argc,argv); PRINT_VERSION("3dRSFC (from 3dBandpass by RW Cox): version THETA"); AUTHOR("PA Taylor"); 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],"-no_rs_out") == 0 ){ // @@ SERIES_OUT = 0 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-un_bp_out") == 0 ){ // @@ UNBP_OUT = 1 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-no_rsfa") == 0 ){ // @@ DO_RSFA = 0 ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-bp_at_end") == 0 ){ // @@ BP_LAST = 1 ; 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 ; } } ftopALL = 1./dt ;// Aug,2016: should solve problem of a too-large // value for THD_bandpass_vectors(), while still // being >f_{Nyquist} 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 ) // @@ alert if aaaalllllll vox are going to be analyzed! 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 */ // @@ convert BP'ing ftop/bot into indices for the DFT (below) delf = 1.0/(ntime*dt); ind_low = (int) rint(fbot/delf); ind_high = (int) rint(ftop/delf); if( ntime % 2 ) // nyquist number N_ny = (ntime-1)/2; else N_ny = ntime/2; sqnt = sqrt(ntime); nt_fac = sqrt(ntime*(ntime-1)); // @@ if BP_LAST==0: // now we go through twice, doing LFF bandpass for NumDen==0 and // `full spectrum' processing for NumDen==1. // if BP_LAST==1: // now we go through once, doing only `full spectrum' processing for( NumDen=0 ; NumDen<2 ; NumDen++) { //if( NumDen==1 ){ // full spectrum // fbot = fbotALL; // ftop = ftopALL; //} // essentially, just doesn't BP here, and the perfect filtering at end // is used for both still; this makes the final output spectrum // contain only frequencies in range of 0.01-0.08 if( BP_LAST==1 ) INFO_message("Only doing filtering to LFFs at end!"); mrv = THD_dset_to_vectim( inset , mask , 0 ) ; if( mrv == NULL ) ERROR_exit("Can't load time series data!?") ; if( NumDen==1 ) DSET_unload(inset) ; // @@ only unload on 2nd pass /* 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 ; } } /* 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") ; if( (BP_LAST==0) && (NumDen==0) ) (void)THD_bandpass_vectim( mrv , dt,fbot,ftop , qdet , nort,ort ) ; else (void)THD_bandpass_vectim( mrv , dt,fbotALL,ftopALL, 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 ) ; //@@ if( (BP_LAST==0) && (NumDen==0) ) (void)THD_bandpass_vectim(orv,dt,fbot,ftop,qdet,nort,ort); else (void)THD_bandpass_vectim(orv,dt,fbotALL,ftopALL,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( (NumDen==0) ) { // @@ BP'ed version; will do filt if BP_LAST if(BP_LAST) // do bandpass here for BP_LAST (void)THD_bandpass_vectim(mrv,dt,fbot,ftop,qdet,0,NULL); if( verb ) INFO_message("Creating output dataset in memory, then writing it") ; outset = EDIT_empty_copy(inset) ; if(SERIES_OUT){ sprintf(out_lff,"%s_LFF",prefix); EDIT_dset_items( outset , ADN_prefix,out_lff , 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) ; if(SERIES_OUT){ // @@ DSET_write(outset) ; if( verb ) WROTE_DSET(outset) ; } } else{ // @@ non-BP'ed version if( verb ) INFO_message("Creating output dataset 2 in memory") ; // do this here because LFF version was also BP'ed at end. if(BP_LAST) // do bandpass here for BP_LAST (void)THD_bandpass_vectim(mrv,dt,fbotALL,ftopALL,qdet,0,NULL); outsetALL = EDIT_empty_copy(inset) ; if(UNBP_OUT){ sprintf(out_unBP,"%s_unBP",prefix); EDIT_dset_items( outsetALL, ADN_prefix, out_unBP, ADN_none ); tross_Copy_History( inset , outsetALL ) ; tross_Make_History( "3dRSFC" , argc,argv , outsetALL ) ; } for( vv=0 ; vv < ntime ; vv++ ) EDIT_substitute_brick( outsetALL , vv , MRI_float , NULL ) ; #if 1 THD_vectim_to_dset( mrv , outsetALL ) ; #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(outsetALL,vv) ; var = mrv->fvec + vv ; for( kk=0 ; kk < nmask ; kk++ ) far[ivec[kk]] = var[kk*ntime] ; } } AFNI_OMP_END ; #endif VECTIM_destroy(mrv) ; if(UNBP_OUT){ DSET_write(outsetALL) ; if( verb ) WROTE_DSET(outsetALL) ; } } }// end of NumDen loop // @@ INFO_message("Starting the (f)ALaFFel calcs") ; // allocations series1 = (double *)calloc(ntime,sizeof(double)); series2 = (double *)calloc(ntime,sizeof(double)); xx1 = (double *)calloc(2*ntime,sizeof(double)); xx2 = (double *)calloc(2*ntime,sizeof(double)); alff = (float *)calloc(nvox,sizeof(float)); malff = (float *)calloc(nvox,sizeof(float)); falff = (float *)calloc(nvox,sizeof(float)); if( (series1 == NULL) || (series2 == NULL) || (xx1 == NULL) || (xx2 == NULL) || (alff == NULL) || (malff == NULL) || (falff == NULL)) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(122); } if(DO_RSFA) { rsfa = (float *)calloc(nvox,sizeof(float)); mrsfa = (float *)calloc(nvox,sizeof(float)); frsfa = (float *)calloc(nvox,sizeof(float)); if( (rsfa == NULL) || (mrsfa == NULL) || (frsfa == NULL)) { fprintf(stderr, "\n\n MemAlloc failure.\n\n"); exit(123); } } work = gsl_fft_real_workspace_alloc (ntime); real1 = gsl_fft_real_wavetable_alloc (ntime); real2 = gsl_fft_real_wavetable_alloc (ntime); gsl_complex_packed_array compl_freqs1 = xx1; gsl_complex_packed_array compl_freqs2 = xx2; // ********************************************************************* // ********************************************************************* // ************** Falafelling = ALFF/fALFF calcs ***************** // ********************************************************************* // ********************************************************************* // Be now have the BP'ed data set (outset) and the non-BP'ed one // (outsetALL). now we'll FFT both, get amplitudes in appropriate // ranges, and calculate: ALFF, mALFF, fALFF, ctr = 0; for( kk=0; kk<nvox ; kk++) { if(mask[kk]) { // BP one, and unBP one, either for BP_LAST or !BP_LAST for( m=0 ; m<ntime ; m++ ) { series1[m] = THD_get_voxel(outset,kk,m); series2[m] = THD_get_voxel(outsetALL,kk,m); } mm = gsl_fft_real_transform(series1, 1, ntime, real1, work); mm = gsl_fft_halfcomplex_unpack(series1, compl_freqs1, 1, ntime); mm = gsl_fft_real_transform(series2, 1, ntime, real2, work); mm = gsl_fft_halfcomplex_unpack(series2, compl_freqs2, 1, ntime); numer = 0.0f; denom = 0.0f; de_rsfa = 0.0f; nu_rsfa = 0.0f; for( m=1 ; m<N_ny ; m++ ) { mm = 2*m; pow2 = compl_freqs2[mm]*compl_freqs2[mm] + compl_freqs2[mm+1]*compl_freqs2[mm+1]; // power //pow2*=2;// factor of 2 since ampls are even funcs denom+= (float) sqrt(pow2); // amplitude de_rsfa+= (float) pow2; if( ( m>=ind_low ) && ( m<=ind_high ) ){ pow1 = compl_freqs1[mm]*compl_freqs1[mm]+ compl_freqs1[mm+1]*compl_freqs1[mm+1]; //pow1*=2; numer+= (float) sqrt(pow1); nu_rsfa+= (float) pow1; } } if( denom>0.000001 ) falff[kk] = numer/denom; else falff[kk] = 0.; alff[kk] = 2*numer/sqnt;// factor of 2 since ampl is even funct meanALFF+= alff[kk]; if(DO_RSFA){ nu_rsfa = sqrt(2*nu_rsfa); // factor of 2 since ampls de_rsfa = sqrt(2*de_rsfa); // are even funcs if( de_rsfa>0.000001 ) frsfa[kk] = nu_rsfa/de_rsfa; else frsfa[kk]=0.; rsfa[kk] = nu_rsfa/nt_fac; meanRSFA+= rsfa[kk]; } ctr+=1; } } meanALFF/= ctr; meanRSFA/= ctr; gsl_fft_real_wavetable_free(real1); gsl_fft_real_wavetable_free(real2); gsl_fft_real_workspace_free(work); // ALFFs divided by mean of brain value for( kk=0 ; kk<nvox ; kk++ ) if(mask[kk]){ malff[kk] = alff[kk]/meanALFF; if(DO_RSFA) mrsfa[kk] = rsfa[kk]/meanRSFA; } // ************************************************************** // ************************************************************** // Store and output // ************************************************************** // ************************************************************** outsetALFF = EDIT_empty_copy( inset ) ; sprintf(out_alff,"%s_ALFF",prefix); EDIT_dset_items( outsetALFF, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_alff, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetALFF)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetALFF)); EDIT_substitute_brick(outsetALFF, 0, MRI_float, alff); alff=NULL; THD_load_statistics(outsetALFF); tross_Make_History("3dRSFC", argc, argv, outsetALFF); THD_write_3dim_dataset(NULL, NULL, outsetALFF, True); outsetfALFF = EDIT_empty_copy( inset ) ; sprintf(out_falff,"%s_fALFF",prefix); EDIT_dset_items( outsetfALFF, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_falff, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetfALFF)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetfALFF)); EDIT_substitute_brick(outsetfALFF, 0, MRI_float, falff); falff=NULL; THD_load_statistics(outsetfALFF); tross_Make_History("3dRSFC", argc, argv, outsetfALFF); THD_write_3dim_dataset(NULL, NULL, outsetfALFF, True); outsetmALFF = EDIT_empty_copy( inset ) ; sprintf(out_malff,"%s_mALFF",prefix); EDIT_dset_items( outsetmALFF, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_malff, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetmALFF)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetmALFF)); EDIT_substitute_brick(outsetmALFF, 0, MRI_float, malff); malff=NULL; THD_load_statistics(outsetmALFF); tross_Make_History("3dRSFC", argc, argv, outsetmALFF); THD_write_3dim_dataset(NULL, NULL, outsetmALFF, True); if(DO_RSFA){ outsetRSFA = EDIT_empty_copy( inset ) ; sprintf(out_rsfa,"%s_RSFA",prefix); EDIT_dset_items( outsetRSFA, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_rsfa, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetRSFA)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetRSFA)); EDIT_substitute_brick(outsetRSFA, 0, MRI_float, rsfa); rsfa=NULL; THD_load_statistics(outsetRSFA); tross_Make_History("3dRSFC", argc, argv, outsetRSFA); THD_write_3dim_dataset(NULL, NULL, outsetRSFA, True); outsetfRSFA = EDIT_empty_copy( inset ) ; sprintf(out_frsfa,"%s_fRSFA",prefix); EDIT_dset_items( outsetfRSFA, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_frsfa, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetfRSFA)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetfRSFA)); EDIT_substitute_brick(outsetfRSFA, 0, MRI_float, frsfa); frsfa=NULL; THD_load_statistics(outsetfRSFA); tross_Make_History("3dRSFC", argc, argv, outsetfRSFA); THD_write_3dim_dataset(NULL, NULL, outsetfRSFA, True); outsetmRSFA = EDIT_empty_copy( inset ) ; sprintf(out_mrsfa,"%s_mRSFA",prefix); EDIT_dset_items( outsetmRSFA, ADN_nvals, 1, ADN_datum_all , MRI_float , ADN_prefix , out_mrsfa, ADN_none ) ; if( !THD_ok_overwrite() && THD_is_ondisk(DSET_HEADNAME(outsetmRSFA)) ) ERROR_exit("Can't overwrite existing dataset '%s'", DSET_HEADNAME(outsetmRSFA)); EDIT_substitute_brick(outsetmRSFA, 0, MRI_float, mrsfa); mrsfa=NULL; THD_load_statistics(outsetmRSFA); tross_Make_History("3dRSFC", argc, argv, outsetmRSFA); THD_write_3dim_dataset(NULL, NULL, outsetmRSFA, True); } // ************************************************************ // ************************************************************ // Freeing // ************************************************************ // ************************************************************ DSET_delete(inset); DSET_delete(outsetALL); DSET_delete(outset); DSET_delete(outsetALFF); DSET_delete(outsetmALFF); DSET_delete(outsetfALFF); DSET_delete(outsetRSFA); DSET_delete(outsetmRSFA); DSET_delete(outsetfRSFA); free(inset); free(outsetALL); free(outset); free(outsetALFF); free(outsetmALFF); free(outsetfALFF); free(outsetRSFA); free(outsetmRSFA); free(outsetfRSFA); free(rsfa); free(mrsfa); free(frsfa); free(alff); free(malff); free(falff); free(mask); free(series1); free(series2); free(xx1); free(xx2); exit(0) ; }
int main( int argc , char *argv[] ) { int iarg , nerr=0 , nvals,nvox , nx,ny,nz , ii,jj,kk ; char *prefix="LSSout" , *save1D=NULL , nbuf[256] ; THD_3dim_dataset *inset=NULL , *outset ; MRI_vectim *inset_mrv=NULL ; byte *mask=NULL ; int mask_nx=0,mask_ny=0,mask_nz=0, automask=0, nmask=0 ; NI_element *nelmat=NULL ; char *matname=NULL ; char *cgl ; int Ngoodlist,*goodlist=NULL , nfull , ncmat,ntime ; NI_int_array *giar ; NI_str_array *gsar ; NI_float_array *gfar ; MRI_IMAGE *imX, *imA, *imC, *imS ; float *Xar, *Sar ; MRI_IMARR *imar ; int nS ; float *ss , *oo , *fv , sum ; int nvec , iv ; int nbstim , nst=0 , jst_bot,jst_top ; char *stlab="LSS" ; int nodata=1 ; /*--- help me if you can ---*/ if( argc < 2 || strcasecmp(argv[1],"-HELP") == 0 ) LSS_help() ; /*--- bureaucratic startup ---*/ PRINT_VERSION("3dLSS"); mainENTRY("3dLSS main"); machdep(); AFNI_logger("3dLSS",argc,argv); AUTHOR("RWCox"); (void)COX_clock_time() ; /**------- scan command line --------**/ iarg = 1 ; while( iarg < argc ){ if( strcmp(argv[iarg],"-verb") == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-VERB") == 0 ){ verb+=2 ; iarg++ ; continue ; } /**========== -mask ==========**/ if( strcasecmp(argv[iarg],"-mask") == 0 ){ THD_3dim_dataset *mset ; if( ++iarg >= argc ) ERROR_exit("Need argument after '-mask'") ; if( 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_delete(mset) ; if( mask == NULL ) ERROR_exit("Can't make mask from dataset '%.33s'",argv[iarg]) ; nmask = THD_countmask( mask_nx*mask_ny*mask_nz , mask ) ; if( verb || nmask < 1 ) INFO_message("Number of voxels in mask = %d",nmask) ; if( nmask < 1 ) ERROR_exit("Mask is too small to process") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-automask") == 0 ){ if( mask != NULL ) ERROR_exit("Can't have -automask and -mask") ; automask = 1 ; iarg++ ; continue ; } /**========== -matrix ==========**/ if( strcasecmp(argv[iarg],"-matrix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; if( nelmat != NULL ) ERROR_exit("More than 1 -matrix option!?"); nelmat = NI_read_element_fromfile( argv[iarg] ) ; /* read NIML file */ matname = argv[iarg]; if( nelmat == NULL || nelmat->type != NI_ELEMENT_TYPE ) ERROR_exit("Can't process -matrix file '%s'!?",matname) ; iarg++ ; continue ; } /**========== -nodata ===========**/ if( strcasecmp(argv[iarg],"-nodata") == 0 ){ nodata = 1 ; iarg++ ; continue ; } /**========== -input ==========**/ if( strcasecmp(argv[iarg],"-input") == 0 ){ if( inset != NULL ) ERROR_exit("Can't have two -input options!?") ; if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; nodata = 0 ; iarg++ ; continue ; } /**========== -prefix =========**/ if( strcasecmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; prefix = strdup(argv[iarg]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal string after %s",argv[iarg-1]) ; if( verb && strcmp(prefix,"NULL") == 0 ) INFO_message("-prefix NULL ==> no dataset will be written") ; iarg++ ; continue ; } /**========== -save1D =========**/ if( strcasecmp(argv[iarg],"-save1D") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; save1D = strdup(argv[iarg]) ; if( !THD_filename_ok(save1D) ) ERROR_exit("Illegal string after %s",argv[iarg-1]) ; iarg++ ; continue ; } /***** Loser User *****/ ERROR_message("Unknown option: %s",argv[iarg]) ; suggest_best_prog_option(argv[0], argv[iarg]); exit(1); } /* end of loop over options */ /*----- check for errors -----*/ if( nelmat == NULL ){ ERROR_message("No -matrix option!?") ; nerr++ ; } if( nerr > 0 ) ERROR_exit("Can't continue without these inputs!") ; if( inset != NULL ){ nvals = DSET_NVALS(inset) ; nvox = DSET_NVOX(inset) ; nx = DSET_NX(inset) ; ny = DSET_NY(inset) ; nz = DSET_NZ(inset) ; } else { automask = nvals = 0 ; nvox = nx = ny = nz = nodata = 1 ; /* nodata */ mask = NULL ; } /*----- masque -----*/ if( mask != NULL ){ /* check -mask option for compatibility */ if( mask_nx != nx || mask_ny != ny || mask_nz != nz ) ERROR_exit("-mask dataset grid doesn't match input dataset :-(") ; } else if( automask ){ /* create a mask from input dataset */ mask = THD_automask( inset ) ; if( mask == NULL ) ERROR_message("Can't create -automask from input dataset :-(") ; nmask = THD_countmask( nvox , mask ) ; if( verb || nmask < 1 ) INFO_message("Number of voxels in automask = %d (out of %d = %.1f%%)", nmask, nvox, (100.0f*nmask)/nvox ) ; if( nmask < 1 ) ERROR_exit("Automask is too small to process") ; } else if( !nodata ) { /* create a 'mask' for all voxels */ if( verb ) INFO_message("No mask ==> computing for all %d voxels",nvox) ; mask = (byte *)malloc(sizeof(byte)*nvox) ; nmask = nvox ; memset( mask , 1 , sizeof(byte)*nvox ) ; } /*----- get matrix info from the NIML element -----*/ if( verb ) INFO_message("extracting matrix info") ; ncmat = nelmat->vec_num ; /* number of columns */ ntime = nelmat->vec_len ; /* number of rows */ if( ntime < ncmat+2 ) ERROR_exit("Matrix has too many columns (%d) for number of rows (%d)",ncmat,ntime) ; /*--- number of rows in the full matrix (without censoring) ---*/ cgl = NI_get_attribute( nelmat , "NRowFull" ) ; if( cgl == NULL ) ERROR_exit("Matrix is missing 'NRowFull' attribute!") ; nfull = (int)strtod(cgl,NULL) ; if( nodata ){ nvals = nfull ; } else if( nvals != nfull ){ ERROR_exit("-input dataset has %d time points, but matrix indicates %d", nvals , nfull ) ; } /*--- the goodlist = mapping from matrix row index to time index (which allows for possible time point censoring) ---*/ cgl = NI_get_attribute( nelmat , "GoodList" ) ; if( cgl == NULL ) ERROR_exit("Matrix is missing 'GoodList' attribute!") ; giar = NI_decode_int_list( cgl , ";," ) ; if( giar == NULL || giar->num < ntime ) ERROR_exit("Matrix 'GoodList' badly formatted?!") ; Ngoodlist = giar->num ; goodlist = giar->ar ; if( Ngoodlist != ntime ) ERROR_exit("Matrix 'GoodList' incorrect length?!") ; else if( verb > 1 && Ngoodlist < nfull ) ININFO_message("censoring reduces time series length from %d to %d",nfull,Ngoodlist) ; /*--- extract the matrix from the NIML element ---*/ imX = mri_new( ntime , ncmat , MRI_float ) ; Xar = MRI_FLOAT_PTR(imX) ; if( nelmat->vec_typ[0] == NI_FLOAT ){ /* from 3dDeconvolve_f */ float *cd ; for( jj=0 ; jj < ncmat ; jj++ ){ cd = (float *)nelmat->vec[jj] ; for( ii=0 ; ii < ntime ; ii++ ) Xar[ii+jj*ntime] = cd[ii] ; } } else if( nelmat->vec_typ[0] == NI_DOUBLE ){ /* from 3dDeconvolve */ double *cd ; for( jj=0 ; jj < ncmat ; jj++ ){ cd = (double *)nelmat->vec[jj] ; for( ii=0 ; ii < ntime ; ii++ ) Xar[ii+jj*ntime] = cd[ii] ; } } else { ERROR_exit("Matrix file stored with illegal data type!?") ; } /*--- find the stim_times_IM option ---*/ cgl = NI_get_attribute( nelmat , "BasisNstim") ; if( cgl == NULL ) ERROR_exit("Matrix doesn't have 'BasisNstim' attribute!") ; nbstim = (int)strtod(cgl,NULL) ; if( nbstim <= 0 ) ERROR_exit("Matrix 'BasisNstim' attribute is %d",nbstim) ; for( jj=1 ; jj <= nbstim ; jj++ ){ sprintf(nbuf,"BasisOption_%06d",jj) ; cgl = NI_get_attribute( nelmat , nbuf ) ; if( cgl == NULL || strcmp(cgl,"-stim_times_IM") != 0 ) continue ; if( nst > 0 ) ERROR_exit("More than one -stim_times_IM option was found in the matrix") ; nst = jj ; sprintf(nbuf,"BasisColumns_%06d",jj) ; cgl = NI_get_attribute( nelmat , nbuf ) ; if( cgl == NULL ) ERROR_exit("Matrix doesn't have %s attribute!",nbuf) ; jst_bot = jst_top = -1 ; sscanf(cgl,"%d:%d",&jst_bot,&jst_top) ; if( jst_bot < 0 || jst_top < 0 ) ERROR_exit("Can't decode matrix attribute %s",nbuf) ; if( jst_bot == jst_top ) ERROR_exit("Matrix attribute %s shows only 1 column for -stim_time_IM:\n" " -->> 3dLSS is meant to be used when more than one stimulus\n" " time was given, and then it computes the response beta\n" " for each stim time separately. If you have only one\n" " stim time with -stim_times_IM, you can use the output\n" " dataset from 3dDeconvolve (or 3dREMLfit) to get that\n" " single beta directly.\n" , nbuf ) ; if( jst_bot >= jst_top || jst_top >= ncmat ) ERROR_exit("Matrix attribute %s has illegal value: %d:%d (ncmat=%d)",nbuf,jst_bot,jst_top,ncmat) ; sprintf(nbuf,"BasisName_%06d",jj) ; cgl = NI_get_attribute( nelmat , nbuf ) ; if( cgl != NULL ) stlab = strdup(cgl) ; if( verb > 1 ) ININFO_message("-stim_times_IM at stim #%d; cols %d..%d",jj,jst_bot,jst_top) ; } if( nst == 0 ) ERROR_exit("Matrix doesn't have any -stim_times_IM options inside :-(") ; /*--- mangle matrix to segregate IM regressors from the rest ---*/ if( verb ) INFO_message("setting up LSS vectors") ; imar = LSS_mangle_matrix( imX , jst_bot , jst_top ) ; if( imar == NULL ) ERROR_exit("Can't compute LSS 'mangled' matrix :-(") ; /*--- setup for LSS computations ---*/ imA = IMARR_SUBIM(imar,0) ; imC = IMARR_SUBIM(imar,1) ; imS = LSS_setup( imA , imC ) ; DESTROY_IMARR(imar) ; if( imS == NULL ) ERROR_exit("Can't complete LSS setup :-((") ; nS = imS->ny ; Sar = MRI_FLOAT_PTR(imS) ; if( save1D != NULL ){ mri_write_1D( save1D , imS ) ; if( verb ) ININFO_message("saved LSS vectors into file %s",save1D) ; } else if( nodata ){ WARNING_message("-nodata used but -save1D not used ==> you get no output!") ; } if( nodata || strcmp(prefix,"NULL") == 0 ){ INFO_message("3dLSS ends since prefix is 'NULL' or -nodata was used") ; exit(0) ; } /*----- create output dataset -----*/ if( verb ) INFO_message("creating output datset in memory") ; outset = EDIT_empty_copy(inset) ; EDIT_dset_items( outset , ADN_prefix , prefix , ADN_datum_all , MRI_float , ADN_brick_fac , NULL , ADN_nvals , nS , ADN_ntt , nS , ADN_none ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dLSS" , argc,argv , outset ) ; for( kk=0 ; kk < nS ; kk++ ){ EDIT_substitute_brick( outset , kk , MRI_float , NULL ) ; sprintf(nbuf,"%s#%03d",stlab,kk) ; EDIT_BRICK_LABEL( outset , kk , nbuf ) ; } /*----- convert input dataset to vectim -----*/ if( verb ) INFO_message("loading input dataset into memory") ; DSET_load(inset) ; CHECK_LOAD_ERROR(inset) ; inset_mrv = THD_dset_to_vectim( inset , mask , 0 ) ; DSET_unload(inset) ; /*----- compute dot products, store results -----*/ if( verb ) INFO_message("computing away, me buckos!") ; nvec = inset_mrv->nvec ; for( kk=0 ; kk < nS ; kk++ ){ ss = Sar + kk*ntime ; oo = DSET_ARRAY(outset,kk) ; for( iv=0 ; iv < nvec ; iv++ ){ fv = VECTIM_PTR(inset_mrv,iv) ; for( sum=0.0f,ii=0 ; ii < ntime ; ii++ ) sum += ss[ii] * fv[goodlist[ii]] ; oo[inset_mrv->ivec[iv]] = sum ; } } DSET_write(outset) ; WROTE_DSET(outset) ; /*-------- Hasta la vista, baby --------*/ if( verb ) INFO_message("3dLSS finished: total CPU=%.2f Elapsed=%.2f", COX_cpu_time() , COX_clock_time() ) ; exit(0) ; }