/***************************************************************************** Major portions of this software are copyrighted by the Medical College of Wisconsin, 1994-2000, and are released under the Gnu General Public License, Version 2. See the file README.Copyright for details. ******************************************************************************/ #include "mrilib.h" void osfilt3_func( int num , float *vec ) ; void median3_func( int num , float *vec ) ; void linear3_func( int num , float *vec ) ; static int Nadapt=9 , Nadhalf=4 ; void adaptN_func ( int num , float *vec ) ; /* 03 Oct 2014 */ /*-- 01 & 03 Mar 2001: linear filtering functions --*/ void linear_filter_extend( int , float * , int , float * ) ; void linear_filter_zero ( int , float * , int , float * ) ; void linear_filter_trend ( int , float * , int , float * ) ; float * hamming_window ( int ) ; float * blackman_window( int ) ; float * custom_filter( int ) ; char custom_file[256] = "\0"; /*KRH 01/03, needed for custom filter */ int custom_ntaps = 0; static float af=0.15 , bf=0.70 , cf=0.15 ; /*--------------------------------------------------------------------------*/ int main( int argc , char *argv[] ) { void (*smth)(int,float *) = linear3_func ; /* default filter */ char prefix[256] = "smooth" ; int new_datum = ILLEGAL_TYPE , old_datum ; int nopt ; THD_3dim_dataset *old_dset , *new_dset ; int ii,kk , nxyz , ntime , use_fac , ityp , nbytes ; void *new_brick ; byte **bptr = NULL ; /* one of these will be the array of */ short **sptr = NULL ; /* pointers to input dataset sub-bricks */ float **fptr = NULL ; /* (depending on input datum type) */ byte **new_bptr = NULL ; /* one of these will be the array of */ short **new_sptr = NULL ; /* pointers to output dataset sub-bricks */ float **new_fptr = NULL ; /* (depending on output datum type) */ float *fxar = NULL ; /* array loaded from input dataset */ float *fac = NULL ; /* array of brick scaling factors */ float *faci = NULL ; #define BLACKMAN 1 #define HAMMING 2 #define CUSTOM 3 #define EXTEND 77 #define ZERO 78 #define TREND 79 int ntap=0 ; /* 01 Mar 2001 */ float *ftap=NULL ; int nwin=0,nfil=EXTEND ; /* 03 Mar 2001 */ void (*lfil)(int,float *,int,float *) = linear_filter_extend ; float * (*lwin)(int) = NULL ; /* start of code */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dTsmooth [options] dataset\n" "Smooths each voxel time series in a 3D+time dataset and produces\n" "as output a new 3D+time dataset (e.g., lowpass filter in time).\n" "\n" "*** Also see program 3dBandpass ***\n" "\n" "General Options:\n" " -prefix ppp = Sets the prefix of the output dataset to be 'ppp'.\n" " [default = 'smooth']\n" " -datum type = Coerce output dataset to be stored as the given type.\n" " [default = input data type]\n" "\n" "Three Point Filtering Options [07 July 1999]\n" "--------------------------------------------\n" "The following options define the smoothing filter to be used.\n" "All these filters use 3 input points to compute one output point:\n" " Let a = input value before the current point\n" " b = input value at the current point\n" " c = input value after the current point\n" " [at the left end, a=b; at the right end, c=b]\n" "\n" " -lin = 3 point linear filter: 0.15*a + 0.70*b + 0.15*c\n" " [This is the default smoother]\n" " -med = 3 point median filter: median(a,b,c)\n" " -osf = 3 point order statistics filter:\n" " 0.15*min(a,b,c) + 0.70*median(a,b,c) + 0.15*max(a,b,c)\n" "\n" " -3lin m = 3 point linear filter: 0.5*(1-m)*a + m*b + 0.5*(1-m)*c\n" " Here, 'm' is a number strictly between 0 and 1.\n" "\n" "General Linear Filtering Options [03 Mar 2001]\n" "----------------------------------------------\n" " -hamming N = Use N point Hamming or Blackman windows.\n" " -blackman N (N must be odd and bigger than 1.)\n" " -custom coeff_filename.1D (odd # of coefficients must be in a \n" " single column in ASCII file)\n" " (-custom added Jan 2003)\n" " WARNING: If you use long filters, you do NOT want to include the\n" " large early images in the program. Do something like\n" " 3dTsmooth -hamming 13 'fred+orig[4..$]'\n" " to eliminate the first 4 images (say).\n" " The following options determine how the general filters treat\n" " time points before the beginning and after the end:\n" " -EXTEND = BEFORE: use the first value; AFTER: use the last value\n" " -ZERO = BEFORE and AFTER: use zero\n" " -TREND = compute a linear trend, and extrapolate BEFORE and AFTER\n" " The default is -EXTEND. These options do NOT affect the operation\n" " of the 3 point filters described above, which always use -EXTEND.\n" "\n" "Adaptive Mean Filtering option [03 Oct 2014]\n" "--------------------------------------------\n" " -adaptive N = use adaptive mean filtering of width N\n" " (where N must be odd and bigger than 3).\n" " * This filter is similar to the 'AdptMean9'\n" " 1D filter in the AFNI GUI, except that the\n" " end points are treated differently.\n" ) ; printf("\n" MASTER_SHORTHELP_STRING ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dTsmooth main"); machdep(); AFNI_logger("3dTsmooth",argc,argv); PRINT_VERSION("3dTsmooth") ; /* parse options */ nopt = 1 ; while( nopt < argc && argv[nopt][0] == '-' ){ if( strcmp(argv[nopt],"-adaptive") == 0 ){ /* 03 Oct 2014 */ if( ++nopt >= argc ) ERROR_exit("need value after '%s'",argv[nopt]-1) ; Nadapt = (int)strtod(argv[nopt],NULL) ; if( Nadapt < 5 || Nadapt%2 != 1 ) ERROR_exit("illegal value after '%s'",argv[nopt-1]) ; Nadhalf = Nadapt/2 ; INFO_message("Adaptive mean filtering (width=%d) will be done",Nadapt) ; smth = adaptN_func ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-EXTEND") == 0 ){ /* 03 Mar 2001 */ nfil = EXTEND ; lfil = linear_filter_extend ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-ZERO") == 0 ){ /* 03 Mar 2001 */ nfil = ZERO ; lfil = linear_filter_zero ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-TREND") == 0 ){ /* 03 Mar 2001 */ nfil = TREND ; lfil = linear_filter_trend ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-hamming") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -hamming: no value!") ; ntap = (int) strtod(argv[nopt],NULL) ; if( ntap < 3 || ntap%2 != 1 ) ERROR_exit("Illegal -hamming: bad value!") ; nwin = HAMMING ; lwin = hamming_window ; INFO_message("Hamming window filter (width=%d) will be done",ntap) ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-blackman") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -blackman: no value!") ; ntap = (int) strtod(argv[nopt],NULL) ; if( ntap < 3 || ntap%2 != 1 ) ERROR_exit("Illegal -blackman: bad value!") ; nwin = BLACKMAN ; lwin = blackman_window ; INFO_message("Blackman window filter (width=%d) will be done",ntap) ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-custom") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -blackman: no filename!") ; strcpy(custom_file, argv[nopt]) ; nwin = CUSTOM ; lwin = custom_filter ; ntap = 1; INFO_message("Custom window filter will be done") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-prefix") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -prefix: no name!") ; strcpy(prefix,argv[nopt]) ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal -prefix: bad name!") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-datum") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -datum: no type name!") ; if( strcmp(argv[nopt],"short") == 0 ){ new_datum = MRI_short ; } else if( strcmp(argv[nopt],"float") == 0 ){ new_datum = MRI_float ; } else if( strcmp(argv[nopt],"byte") == 0 ){ new_datum = MRI_byte ; } else { ERROR_exit("Illegal -datum: bad type name!") ; } nopt++ ; continue ; } if( strcmp(argv[nopt],"-lin") == 0 ){ bf = 0.70 ; af = cf = 0.15 ; smth = linear3_func ; INFO_message("3 point linear filtering will be done") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-med") == 0 ){ smth = median3_func ; INFO_message("3 point median filtering will be done") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-osf") == 0 ){ smth = osfilt3_func ; INFO_message("3 point order-statistic filtering will be done") ; nopt++ ; continue ; } if( strcmp(argv[nopt],"-3lin") == 0 ){ if( ++nopt >= argc ) ERROR_exit("Illegal -3lin: no value!") ; bf = strtod( argv[nopt] , NULL ) ; if( bf <= 0.0 || bf >= 1.0 ) ERROR_exit("Illegal -3lin: bad value") ; af = cf = 0.5*(1.0-bf) ; smth = linear3_func ; INFO_message("3 point linear filtering will be done") ; nopt++ ; continue ; } ERROR_exit("Unknown option: %s",argv[nopt]) ; } /* end of loop over options */ if( nopt >= argc ) ERROR_exit("No input dataset filename!?") ; /*---------- open dataset ----------*/ old_dset = THD_open_dataset( argv[nopt] ) ; if( old_dset == NULL ) ERROR_exit("Can't open dataset %s",argv[nopt]) ; ntime = DSET_NVALS(old_dset) ; nxyz = DSET_NVOX(old_dset) ; if( ntime < 4 ) ERROR_exit(" Can't smooth dataset with less than 4 time points!") ; DSET_load(old_dset) ; CHECK_LOAD_ERROR(old_dset) ; old_datum = DSET_BRICK_TYPE(old_dset,0) ; if( new_datum < 0 ) new_datum = old_datum ; switch( old_datum ){ /* pointer type depends on input datum type */ /** create array of pointers into old dataset sub-bricks **/ /*--------- input is bytes ----------*/ /* voxel #i at time #k is bptr[k][i] */ /* for i=0..nxyz-1 and k=0..ntime-1. */ case MRI_byte: bptr = (byte **) malloc( sizeof(byte *) * ntime ) ; for( kk=0 ; kk < ntime ; kk++ ) bptr[kk] = (byte *) DSET_ARRAY(old_dset,kk) ; break ; /*--------- input is shorts ---------*/ /* voxel #i at time #k is sptr[k][i] */ /* for i=0..nxyz-1 and k=0..ntime-1. */ case MRI_short: sptr = (short **) malloc( sizeof(short *) * ntime ) ; for( kk=0 ; kk < ntime ; kk++ ) sptr[kk] = (short *) DSET_ARRAY(old_dset,kk) ; break ; /*--------- input is floats ---------*/ /* voxel #i at time #k is fptr[k][i] */ /* for i=0..nxyz-1 and k=0..ntime-1. */ case MRI_float: fptr = (float **) malloc( sizeof(float *) * ntime ) ; for( kk=0 ; kk < ntime ; kk++ ) fptr[kk] = (float *) DSET_ARRAY(old_dset,kk) ; break ; } /* end of switch on input type */ /*---- allocate space for 1 voxel timeseries ----*/ fxar = (float *) malloc( sizeof(float) * ntime ) ; /* voxel timeseries */ /*--- get scaling factors for sub-bricks ---*/ fac = (float *) malloc( sizeof(float) * ntime ) ; /* factors */ use_fac = 0 ; for( kk=0 ; kk < ntime ; kk++ ){ fac[kk] = DSET_BRICK_FACTOR(old_dset,kk) ; if( fac[kk] != 0.0 ) use_fac++ ; else fac[kk] = 1.0 ; } if( !use_fac ){ free(fac) ; fac = NULL ; } else { faci = (float *) malloc( sizeof(float) * ntime ) ; for( kk=0 ; kk < ntime ; kk++ ) faci[kk] = 1.0 / fac[kk] ; } /*---------------------- make a new dataset ----------------------*/ new_dset = EDIT_empty_copy( old_dset ) ; tross_Copy_History( old_dset , new_dset ) ; tross_Make_History( "3dTsmooth" , argc,argv , new_dset ) ; /*-- edit some of its internal parameters --*/ EDIT_dset_items( new_dset , ADN_prefix , prefix , ADN_malloc_type , DATABLOCK_MEM_MALLOC , ADN_datum_all , new_datum , ADN_none ) ; /*-- make brick(s) for this dataset --*/ switch( new_datum ){ case MRI_byte: new_bptr = (byte **) malloc( sizeof(byte *) * ntime ) ; break ; case MRI_short: new_sptr = (short **) malloc( sizeof(short *) * ntime ) ; break ; case MRI_float: new_fptr = (float **) malloc( sizeof(float *) * ntime ) ; break ; } for( kk=0 ; kk < ntime ; kk++ ){ ityp = DSET_BRICK_TYPE(new_dset,kk) ; /* type of data */ nbytes = DSET_BRICK_BYTES(new_dset,kk) ; /* how much data */ new_brick = malloc( nbytes ) ; /* make room */ if( new_brick == NULL ) ERROR_exit("Can't allocate memory for output dataset!") ; EDIT_substitute_brick( new_dset , kk , ityp , new_brick ) ; switch( new_datum ){ case MRI_byte: new_bptr[kk] = (byte * ) new_brick ; break ; case MRI_short: new_sptr[kk] = (short *) new_brick ; break ; case MRI_float: new_fptr[kk] = (float *) new_brick ; break ; } } if( lwin != NULL && ntap > 0 ){ /* 03 Mar 2001 */ ftap = lwin(ntap) ; if( lfil == NULL ) lfil = linear_filter_extend ; if( nwin == CUSTOM ) ntap = custom_ntaps ; } /*----------------------------------------------------*/ /*----- Setup has ended. Now do some real work. -----*/ /***** loop over voxels *****/ for( ii=0 ; ii < nxyz ; ii++ ){ /* 1 time series at a time */ /*** load data from input dataset, depending on type ***/ switch( old_datum ){ case MRI_byte: for( kk=0 ; kk < ntime ; kk++ ) fxar[kk] = bptr[kk][ii] ; break ; case MRI_short: for( kk=0 ; kk < ntime ; kk++ ) fxar[kk] = sptr[kk][ii] ; break ; case MRI_float: for( kk=0 ; kk < ntime ; kk++ ) fxar[kk] = fptr[kk][ii] ; break ; } /* end of switch over input type */ if( use_fac ) for( kk=0 ; kk < ntime ; kk++ ) fxar[kk] *= fac[kk] ; /* do smoothing */ if( ftap != NULL ) lfil( ntap,ftap , ntime,fxar ) ; /* 01 Mar 2001 */ else smth( ntime , fxar ) ; /* 3 point smoother */ /*** put data into output dataset ***/ switch( new_datum ){ case MRI_byte: if( use_fac ) for( kk=0 ; kk < ntime ; kk++ ) new_bptr[kk][ii] = (byte)(fxar[kk] * faci[kk]) ; else for( kk=0 ; kk < ntime ; kk++ ) new_bptr[kk][ii] = (byte) fxar[kk] ; break ; case MRI_short: if( use_fac ) for( kk=0 ; kk < ntime ; kk++ ) new_sptr[kk][ii] = (short)(fxar[kk] * faci[kk]) ; else for( kk=0 ; kk < ntime ; kk++ ) new_sptr[kk][ii] = (short) fxar[kk] ; break ; case MRI_float: if( use_fac ) for( kk=0 ; kk < ntime ; kk++ ) new_fptr[kk][ii] = (float)(fxar[kk] * faci[kk]) ; else for( kk=0 ; kk < ntime ; kk++ ) new_fptr[kk][ii] = (float) fxar[kk] ; break ; } } /* end of loop over voxels */ DSET_unload(old_dset) ; free(ftap) ; if (DSET_write(new_dset) != False) { fprintf(stderr,"++ output dataset: %s\n",DSET_BRIKNAME(new_dset)) ; exit(0) ; } else { ERROR_exit("3dTsmooth: Failed to write output dataset :-(" ) ; } } /*--------------- Order Statistics Filter ----------------*/ void osfilt3_func( int num , float *vec ) { int ii ; float aa,bb,cc ; bb = vec[0] ; cc = vec[1] ; vec[0] = OSFILT(bb,bb,cc) ; for( ii=1 ; ii < num-1 ; ii++ ){ aa = bb ; bb = cc ; cc = vec[ii+1] ; vec[ii] = OSFILT(aa,bb,cc) ; /* see mrilib.h */ } vec[num-1] = OSFILT(bb,cc,cc) ; return ; } /*--------------- Median of 3 Filter ----------------*/ void median3_func( int num , float *vec ) { int ii ; float aa,bb,cc ; bb = vec[0] ; cc = vec[1] ; for( ii=1 ; ii < num-1 ; ii++ ){ aa = bb ; bb = cc ; cc = vec[ii+1] ; vec[ii] = MEDIAN(aa,bb,cc) ; /* see mrilib.h */ } return ; } /*--------------- Linear Filter ----------------*/ #define LSUM(a,b,c) af*(a)+bf*(b)+cf*(c) void linear3_func( int num , float *vec ) { int ii ; float aa,bb,cc ; bb = vec[0] ; cc = vec[1] ; vec[0] = LSUM(bb,bb,cc) ; for( ii=1 ; ii < num-1 ; ii++ ){ aa = bb ; bb = cc ; cc = vec[ii+1] ; vec[ii] = LSUM(aa,bb,cc) ; } vec[num-1] = LSUM(bb,cc,cc) ; return ; } /*-------------------------------------------------------------------------*/ void linear_filter_extend( int ntap , float *wt , int npt , float *x ) { int ii , nt2=(ntap-1)/2 , jj ; float sum ; static int nfar=0 ; static float *far=NULL ; if( npt > nfar ){ if(far != NULL) free(far) ; far = (float *)malloc(sizeof(float)*npt) ; nfar = npt ; } #undef XX #define XX(i) ( ((i)<0) ? far[0] : ((i)>npt-1) ? far[npt-1] : far[i] ) memcpy( far , x , sizeof(float)*npt ) ; for( ii=0 ; ii < npt ; ii++ ){ for( sum=0.0,jj=0 ; jj < ntap ; jj++ ) sum += wt[jj] * XX(ii-nt2+jj) ; x[ii] = sum ; } return ; } /*-------------------------------------------------------------------------*/ void linear_filter_zero( int ntap , float *wt , int npt , float *x ) { int ii , nt2=(ntap-1)/2 , jj ; float sum ; static int nfar=0 ; static float *far=NULL ; if( npt > nfar ){ if(far != NULL) free(far) ; far = (float *)malloc(sizeof(float)*npt) ; nfar = npt ; } #undef XX #define XX(i) ( ((i)<0 || (i)>npt-1) ? 0 : far[i] ) memcpy( far , x , sizeof(float)*npt ) ; for( ii=0 ; ii < npt ; ii++ ){ for( sum=0.0,jj=0 ; jj < ntap ; jj++ ) sum += wt[jj] * XX(ii-nt2+jj) ; x[ii] = sum ; } return ; } /*-------------------------------------------------------------------------*/ void linear_filter_trend( int ntap , float *wt , int npt , float *x ) { int ii , nt2=(ntap-1)/2 , jj ; float sum ; static int nfar=0 ; static float *far=NULL ; float a=0.0,b=0.0 ; if( npt > nfar ){ if(far != NULL) free(far) ; far = (float *)malloc(sizeof(float)*npt) ; nfar = npt ; } #undef XX #define XX(i) ( ((i)<0 || (i)>npt-1) ? (a+b*(i)) : far[i] ) memcpy( far , x , sizeof(float)*npt ) ; get_linear_trend( npt,far , &a,&b ) ; /* cf. thd_detrend.c */ for( ii=0 ; ii < npt ; ii++ ){ for( sum=0.0,jj=0 ; jj < ntap ; jj++ ) sum += wt[jj] * XX(ii-nt2+jj) ; x[ii] = sum ; } return ; } /*-------------------------------------------------------------------------*/ float * custom_filter( int dummy ) { MRI_IMAGE * filter_data=NULL; float * filter_coefficients = NULL; filter_data = mri_read_1D(custom_file); if MRI_IS_1D(filter_data) { custom_ntaps = filter_data->nx; if (custom_ntaps % 2) { filter_coefficients = MRI_FLOAT_PTR(filter_data); } else { /*ERROR, EVEN NUMBER OF FILTER TAPS */ } } return filter_coefficients ; } /*-------------------------------------------------------------------------*/ float * hamming_window( int ntap ) { float * wt , tau , t , sum ; int ii , nt2=(ntap-1)/2 ; if( ntap < 3 ) return NULL ; wt = (float *) calloc(sizeof(float),ntap) ; tau = nt2 + 1.0 ; for( sum=0.0,ii=0 ; ii <= 2*nt2 ; ii++ ){ t = PI*(ii-nt2)/tau ; wt[ii] = 0.54 + 0.46*cos(t) ; sum += wt[ii] ; } sum = 1.0 / sum ; for( ii=0 ; ii < ntap ; ii++ ) wt[ii] *= sum ; return wt ; } /*-------------------------------------------------------------------------*/ float * blackman_window( int ntap ) { float * wt , tau , t , sum ; int ii , nt2=(ntap-1)/2 ; if( ntap < 3 ) return NULL ; wt = (float *) calloc(sizeof(float),ntap) ; tau = nt2 + 1.0 ; for( sum=0.0,ii=0 ; ii <= 2*nt2 ; ii++ ){ t = PI*(ii-nt2)/tau ; wt[ii] = 0.42 + 0.5*cos(t) + 0.08*cos(2*t) ; sum += wt[ii] ; } sum = 1.0 / sum ; for( ii=0 ; ii < ntap ; ii++ ) wt[ii] *= sum ; return wt ; } /*--------------------------------------------------------------------------*/ static float adaptive_weighted_mean( int num , float *x ) { float med,mad, wt,wsum, xsum ; int ii ; if( num <= 0 || x == NULL ) return (0.0f) ; else if( num == 1 ) return (x[0]) ; else if( num == 2 ) return (0.5f*(x[0]+x[1])) ; qmedmad_float( num , x , &med , &mad ) ; if( mad <= 0.0f ) return (med) ; wsum = xsum = 0.0f ; mad = 0.4567f / mad ; for( ii=0 ; ii < num ; ii++ ){ wt = mad*fabsf(x[ii]-med); wt = 1.0f / (1.0f+wt*wt*wt); wsum += wt; xsum += wt * x[ii] ; } return (xsum/wsum) ; } /*--------------------------------------------------------------------------*/ void adaptN_func( int num , float *vec ) /* 03 Oct 2014 */ { static float *x=NULL ; float *nv ; int ii,jj,kk,nn , n1=num-1 ; nv = (float *)malloc(sizeof(float)*num) ; if( x == NULL ) x = (float *)malloc(sizeof(float)*Nadapt) ; for( ii=0 ; ii < num ; ii++ ){ for( nn=0,jj=-Nadhalf ; jj <= Nadhalf ; jj++ ){ kk = ii+jj ; if( kk < 0 ) continue ; if( kk > n1 ) break ; x[nn++] = vec[kk] ; } nv[ii] = adaptive_weighted_mean( nn , x ) ; } memcpy(vec,nv,sizeof(float)*num) ; free(nv) ; return ; }