void mri_GMunifize( MRI_IMAGE *gim ) { float *gar=MRI_FLOAT_PTR(gim) , *pval , pupper,plower,pmid,pfac ; int ii,jj , npval , nvox=gim->nvox ; ENTRY("mri_GMunifize") ; /* extract all values above the WM-unifized peak value */ for( npval=ii=0 ; ii < nvox ; ii++ ) if( gar[ii] > PKVAL ) npval++ ; if( npval < 111 ) EXRETURN ; /* 1/6 of being beastly bad */ pval = (float *)malloc(sizeof(float)*npval) ; for( ii=jj=0 ; ii < nvox ; ii++ ) if( gar[ii] > PKVAL ) pval[jj++] = gar[ii] ; /* get the median of these large values */ pupper = qmed_float(npval,pval) ; free(pval) ; /* reflect below the peak value to get the upper cutoff for GM */ pupper = PKVAL - 1.987654321f * (pupper-PKVAL) ; /* set the lower cutoff for GM from the AFNI auto-clip level */ plower = THD_cliplevel(gim,0.4321f) ; /* extract all values between these 2 cutoffs */ for( npval=ii=0 ; ii < nvox ; ii++ ) if( gar[ii] >= plower && gar[ii] <= pupper) npval++ ; if( npval < 111 ) EXRETURN ; /* badly bad */ pval = (float *)malloc(sizeof(float)*npval) ; for( ii=jj=0 ; ii < nvox ; ii++ ) if( gar[ii] >= plower && gar[ii] <= pupper) pval[jj++] = gar[ii] ; /* compute the median of these intermediate-value 'GM' voxels */ pmid = qmed_float(npval,pval) ; free(pval) ; /* scale globally to put this pmid value at a standard value for GM */ pfac = (PKVAL-PKMID) / (PKVAL-pmid) ; for( ii=0 ; ii < nvox ; ii++ ){ if( gar[ii] > 0.0f ){ gar[ii] = pfac * (gar[ii]-PKVAL) + PKVAL ; if( gar[ii] < 0.0f ) gar[ii] = 0.0f ; } else { gar[ii] = 0.0f ; } } if( verb ) fprintf(stderr,"G") ; EXRETURN ; }
MRI_IMAGE * THD_median_brick( THD_3dim_dataset *dset ) { int nvox , nvals , ii ; MRI_IMAGE *tsim , *medim ; float *medar ; float *tsar ; /* 05 Nov 2001 */ ENTRY("THD_median_brick") ; if( !ISVALID_DSET(dset) ) RETURN(NULL) ; DSET_load(dset) ; if( !DSET_LOADED(dset) ) RETURN(NULL) ; nvals = DSET_NVALS(dset) ; tsim = DSET_BRICK(dset,0) ; if( nvals == 1 ){ medim = mri_scale_to_float( DSET_BRICK_FACTOR(dset,0), tsim ) ; RETURN(medim) ; } medim = mri_new_conforming( tsim , MRI_float ) ; medar = MRI_FLOAT_PTR(medim) ; nvox = DSET_NVOX(dset) ; tsar = (float *) calloc( sizeof(float),nvals+1 ) ; /* 05 Nov 2001 */ for( ii=0 ; ii < nvox ; ii++ ){ THD_extract_array( ii , dset , 0 , tsar ) ; /* 05 Nov 2001 */ medar[ii] = qmed_float( nvals , tsar ) ; } free(tsar) ; RETURN(medim) ; }
static int DES_despike9( int num , float *vec , float *wks ) { int ii , nsp ; float *zma,*zme , med,mad,val ; if( num < 9 || vec == NULL ) return 0 ; if( wks != NULL ) zme = wks ; else zme = (float *)malloc(sizeof(float)*(2*num)) ; zma = zme + num ; DES9_init ; /* 18 Apr 2019 */ for( ii=0 ; ii < num ; ii++ ){ mead9(ii) ; zme[ii] = med ; zma[ii] = mad ; } mad = qmed_float(num,zma) ; if( mad <= 0.0f ){ if( wks == NULL ) free(zme); return 0; } /* should not happen */ mad *= 6.789f ; /* threshold value */ for( nsp=ii=0 ; ii < num ; ii++ ) if( fabsf(vec[ii]-zme[ii]) > mad ){ vec[ii] = zme[ii]; nsp++; } if( wks == NULL ) free(zme) ; return nsp ; }
float extreme_proj( int n , float *ar ) /* 02 Feb 2002 */ { float vv,ww , med=qmed_float(n,ar) ; int ii , jj ; jj = 0 ; vv = fabs(ar[0]-med) ; /* Find the value */ for( ii=1 ; ii < n ; ii++ ){ /* furthest from */ ww = fabs(ar[ii]-med) ; /* the median. */ if( ww > vv ){ vv=ww; jj=ii; } } return ar[jj] ; }
void median21_box_func( int nx , int ny , double dx, double dy, float *ar ) { int ii , jj , nxy , joff ; float aa[21] ; float *ajj , *ajm , *ajp , *ajmm , *ajpp ; if( nx < 5 || ny < 5 ) return ; /** make space and copy input into it **/ nxy = nx * ny ; MAKE_ATEMP(nxy) ; if( atemp == NULL ) return ; memcpy(atemp,ar,sizeof(float)*nxy) ; /** process copy of input back into the input array **/ for( jj=1 ; jj < ny-1 ; jj++ ){ joff = jj * nx ; /* offset into this row */ ajj = atemp + joff ; /* pointer to this row */ ajm = ajj-nx ; /* pointer to last row */ ajp = ajj+nx ; /* pointer to next row */ ajmm = (jj == 1 ) ? ajm : ajm-nx ; /* to last last row */ ajpp = (jj ==ny-2) ? ajp : ajp+nx ; /* to next next row */ /* do interior points of this row */ for( ii=2 ; ii < nx-2 ; ii++ ){ aa[0]=ajmm[ii-1]; aa[1]=ajmm[ii]; aa[2]=ajmm[ii+1]; aa[ 3]=ajm[ii-2]; aa[ 4]=ajm[ii-1]; aa[ 5]=ajm[ii]; aa[ 6]=ajm[ii+1]; aa[ 7]=ajm[ii+2]; aa[ 8]=ajj[ii-2]; aa[ 9]=ajj[ii-1]; aa[10]=ajj[ii]; aa[11]=ajj[ii+1]; aa[12]=ajj[ii+2]; aa[13]=ajp[ii-2]; aa[14]=ajp[ii-1]; aa[15]=ajp[ii]; aa[16]=ajp[ii+1]; aa[17]=ajp[ii+2]; aa[18]=ajpp[ii-1]; aa[19]=ajpp[ii]; aa[20]=ajpp[ii+1]; #if 0 isort_float( 21 , aa ) ; ar[ii+joff] = aa[10] ; #else ar[ii+joff] = qmed_float(21,aa) ; /* 25 Oct 2000 */ #endif } } return ; }
MRI_IMAGE * mri_sharpness( MRI_IMAGE *inim ) { int nx,ny , ii,ip,im , jj,jp,jm ; float *innar , *outar , avv,qvv ; MRI_IMAGE *outim , *innim ; if( inim == NULL ) return NULL ; innim = mri_to_float(inim) ; innar = MRI_FLOAT_PTR(innim) ; nx = innim->nx ; ny = innim->ny ; outim = mri_new_conforming( innim , MRI_float ) ; /* all zero */ outar = MRI_FLOAT_PTR(outim) ; { float *qar = (float *)malloc(sizeof(float)*nx*ny) ; int nq=0 ; for( ii=0 ; ii < nx*ny ; ii++ ) if( innar[ii] != 0.0f ) qar[nq++] = fabsf(innar[ii]) ; if( nq < 32 ) qvv = 0.0f ; else qvv = 0.18f * qmed_float(nq,qar) ; free(qar) ; } if( qvv == 0.0f ){ mri_free(innim) ; return outim ; } /* input is all 0! */ for( jj=0 ; jj < ny ; jj++ ){ jm = jj-1 ; if( jm < 0 ) jm++ ; jp = jj+1 ; if( jp >= ny ) jp-- ; for( ii=0 ; ii < nx ; ii++ ){ im = ii-1 ; if( im < 0 ) im++ ; ip = ii+1 ; if( ip >= nx ) ip-- ; avv = fabsf(IAR(im,jm)) + fabsf(IAR(im,jp)) + fabsf(IAR(ip,jm)) + fabsf(IAR(ip,jp)) + fabsf(IAR(ii,jm)) + fabsf(IAR(ii,jp)) + fabsf(IAR(im,jj)) + fabsf(IAR(ip,jj)) + fabsf(IAR(ii,jj)) ; if( avv < qvv ) avv = qvv ; OAR(ii,jj) = fabsf( IAR(im,jm) + IAR(im,jp) + IAR(ip,jm) + IAR(ip,jp) + 4.0f * ( IAR(ii,jm) + IAR(ii,jp) + IAR(im,jj) + IAR(ip,jj) ) - 20.0f * IAR(ii,jj) ) / avv ; } } innim = mri_median21(outim) ; mri_free(outim) ; return innim ; }
void despike9_func( int num , double to,double dt , float *vec ) { int ii ; float *zma,*zme , med,mad,val ; if( num < 9 ) return ; zme = (float *)malloc(sizeof(float)*num) ; zma = (float *)malloc(sizeof(float)*num) ; for( ii=0 ; ii < num ; ii++ ){ mmm9(ii) ; zme[ii] = med ; zma[ii] = mad ; } mad = qmed_float(num,zma) ; free(zma) ; if( mad <= 0.0f ){ free(zme) ; return ; } /* should not happen */ mad *= MTHR ; /* threshold value */ for( ii=0 ; ii < num ; ii++ ) if( fabsf(vec[ii]-zme[ii]) > mad ) vec[ii] = zme[ii] ; free(zme) ; return ; }
int DES_despike9( int num , float *vec , float *wks ) { int ii , nsp ; float *zma,*zme , med,mad,val ; if( num < 9 || vec == NULL ) return 0 ; zme = wks ; zma = zme + num ; for( ii=0 ; ii < num ; ii++ ){ mead9(ii) ; zme[ii] = med ; zma[ii] = mad ; } mad = qmed_float(num,zma) ; if( mad <= 0.0f ){ if( wks == NULL ) free(zme); return 0; } /* should not happen */ mad *= 6.789f ; /* threshold value */ for( nsp=ii=0 ; ii < num ; ii++ ) if( fabsf(vec[ii]-zme[ii]) > mad ){ vec[ii] = zme[ii]; nsp++; } return nsp ; }
int THD_despike9( int num , float *vec ) { int ii , nsp ; float *zma,*zme , med,mad,val ; if( num < 9 || vec == NULL ) return 0 ; zme = (float *)malloc(sizeof(float)*num) ; zma = (float *)malloc(sizeof(float)*num) ; for( ii=0 ; ii < num ; ii++ ){ mead9(ii) ; zme[ii] = med ; zma[ii] = mad ; } mad = qmed_float(num,zma) ; free(zma) ; if( mad <= 0.0f ){ free(zme); return 0; } /* should not happen */ mad *= 6.789f ; /* threshold value */ for( nsp=ii=0 ; ii < num ; ii++ ) if( fabsf(vec[ii]-zme[ii]) > mad ){ vec[ii] = zme[ii]; nsp++; } free(zme) ; return nsp ; }
void DES_despike9( int num , float *vec ) { int ii , nsp ; float *zma,*zme , med,mad,val ; if( num < 9 || vec == NULL ) return ; if( deswks == NULL ) deswks = (float *)malloc(sizeof(float *)*(4*num)) ; zme = deswks ; zma = zme + num ; for( ii=0 ; ii < num ; ii++ ){ mead9(ii) ; zme[ii] = med ; zma[ii] = mad ; } mad = qmed_float(num,zma) ; if( mad <= 0.0f ) return ; mad *= 6.789f ; /* threshold value */ for( nsp=ii=0 ; ii < num ; ii++ ) if( fabsf(vec[ii]-zme[ii]) > mad ){ vec[ii] = zme[ii]; nsp++; } return ; }
MRI_IMAGE * mri_median21( MRI_IMAGE *innim ) { float *innar , *outar , qar[21] ; MRI_IMAGE *outim ; int ii,ip,iq,im,in , jj,jp,jq,jm,jn , kk , nx,ny ; if( innim == NULL || innim->kind != MRI_float ) return NULL ; innar = MRI_FLOAT_PTR(innim) ; nx = innim->nx ; ny = innim->ny ; outim = mri_new_conforming( innim , MRI_float ) ; outar = MRI_FLOAT_PTR(outim) ; for( jj=0 ; jj < ny ; jj++ ){ jm = jj-1 ; if( jm < 0 ) jm++ ; jn = jm-1 ; if( jn < 0 ) jn++ ; jp = jj+1 ; if( jp >= ny ) jp-- ; jq = jp+1 ; if( jq >= ny ) jq-- ; for( ii=0 ; ii < nx ; ii++ ){ im = ii-1 ; if( im < 0 ) im++ ; in = im-1 ; if( in < 0 ) in++ ; ip = ii+1 ; if( ip >= nx ) ip-- ; iq = ip+1 ; if( iq >= nx ) iq-- ; kk = 0 ; qar[kk++] = IAR(im,jn); qar[kk++] = IAR(ii,jn); qar[kk++] = IAR(ip,jn); qar[kk++] = IAR(in,jm); qar[kk++] = IAR(im,jm); qar[kk++] = IAR(ii,jm); qar[kk++] = IAR(ip,jm); qar[kk++] = IAR(iq,jm); qar[kk++] = IAR(in,jj); qar[kk++] = IAR(im,jj); qar[kk++] = IAR(ii,jj); qar[kk++] = IAR(ip,jj); qar[kk++] = IAR(iq,jj); qar[kk++] = IAR(in,jp); qar[kk++] = IAR(im,jp); qar[kk++] = IAR(ii,jp); qar[kk++] = IAR(ip,jp); qar[kk++] = IAR(iq,jp); qar[kk++] = IAR(im,jq); qar[kk++] = IAR(ii,jq); qar[kk++] = IAR(ip,jq); OAR(ii,jj) = qmed_float(21,qar) ; } } return outim ; }
void median9_box_func( int nx , int ny , double dx, double dy, float *ar ) { int ii , jj , nxy , joff ; float aa[9] ; float *ajj , *ajm , *ajp ; if( nx < 3 || ny < 3 ) return ; /** make space and copy input into it **/ nxy = nx * ny ; MAKE_ATEMP(nxy) ; if( atemp == NULL ) return ; memcpy(atemp,ar,sizeof(float)*nxy) ; /** process copy of input back into the input array **/ for( jj=0 ; jj < ny ; jj++ ){ joff = jj * nx ; /* offset into this row */ ajj = atemp + joff ; /* pointer to this row */ ajm = (jj==0 ) ? ajj : ajj-nx ; /* pointer to last row */ ajp = (jj==ny-1) ? ajj : ajj+nx ; /* pointer to next row */ /* do interior points of this row */ for( ii=1 ; ii < nx-1 ; ii++ ){ aa[0] = ajm[ii-1] ; aa[1] = ajm[ii] ; aa[2] = ajm[ii+1] ; aa[3] = ajj[ii-1] ; aa[4] = ajj[ii] ; aa[5] = ajj[ii+1] ; aa[6] = ajp[ii-1] ; aa[7] = ajp[ii] ; aa[8] = ajp[ii+1] ; #if 0 isort_float( 9 , aa ) ; ar[ii+joff] = aa[4] ; #else ar[ii+joff] = qmed_float(9,aa) ; /* 25 Oct 2000 */ #endif } /* do leading edge point (ii=0) */ aa[0] = ajm[0] ; aa[1] = ajm[0] ; aa[2] = ajm[1] ; aa[3] = ajj[0] ; aa[4] = ajj[0] ; aa[5] = ajj[1] ; aa[6] = ajp[0] ; aa[7] = ajp[0] ; aa[8] = ajp[1] ; #if 0 isort_float( 9 , aa ) ; ar[joff] = aa[4] ; #else ar[joff] = qmed_float(9,aa) ; /* 25 Oct 2000 */ #endif /* do trailing edge point (ii=nx-1) */ aa[0] = ajm[nx-2] ; aa[1] = ajm[nx-1] ; aa[2] = ajm[nx-1] ; aa[3] = ajj[nx-2] ; aa[4] = ajj[nx-1] ; aa[5] = ajj[nx-1] ; aa[6] = ajp[nx-2] ; aa[7] = ajp[nx-1] ; aa[8] = ajp[nx-1] ; #if 0 isort_float( 9 , aa ) ; ar[nx-1+joff] = aa[4] ; #else ar[nx-1+joff] = qmed_float(9,aa) ; /* 25 Oct 2000 */ #endif } return ; }
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) ; }
MRI_IMARR * THD_time_fit_dataset( THD_3dim_dataset *dset , int nref , float **ref , int meth , byte *mask ) { int ii , nvox,nval , qq,tt ; float *far , *fit , *var , val ; MRI_IMARR *imar ; MRI_IMAGE *qim ; float **fitar ; ENTRY("THD_time_fit_dataset") ; if( !ISVALID_DSET(dset) || nref < 1 || nref >= DSET_NVALS(dset) || ref == NULL ) RETURN(NULL) ; DSET_load(dset) ; if( !DSET_LOADED(dset) ) RETURN(NULL) ; /* construct output images */ INIT_IMARR(imar) ; fitar = (float **)malloc(sizeof(float *)*nref) ; for( qq=0 ; qq < nref ; qq++ ){ qim = mri_new_conforming( DSET_BRICK(dset,0) , MRI_float ) ; fitar[qq] = MRI_FLOAT_PTR(qim) ; ADDTO_IMARR(imar,qim) ; } qim = mri_new_conforming( DSET_BRICK(dset,0) , MRI_float ) ; var = MRI_FLOAT_PTR(qim) ; ADDTO_IMARR(imar,qim) ; nvox = DSET_NVOX(dset) ; nval = DSET_NVALS(dset) ; far = (float *)malloc(sizeof(float)*nval) ; fit = (float *)malloc(sizeof(float)*nref) ; for( ii=0 ; ii < nvox ; ii++ ){ if( !INMASK(ii) ) continue ; qq = THD_extract_array( ii , dset , 0 , far ) ; /* get data */ if( qq == 0 ){ switch(meth){ /* get fit */ default: case 2: THD_generic_detrend_LSQ( nval, far, -1, nref,ref, fit ); break; case 1: THD_generic_detrend_L1 ( nval, far, -1, nref,ref, fit ); break; } for( qq=0 ; qq < nref ; qq++ ) fitar[qq][ii] = fit[qq] ; /* save fit */ /* at this point, far[] contains the residuals */ switch(meth){ /* get stdev or MAD */ default: case 2:{ float mm,vv ; for( mm=0.0,tt=0 ; tt < nval ; tt++ ) mm += far[tt] ; mm /= nval ; for( vv=0.0,tt=0 ; tt < nval ; tt++ ) vv += (far[tt]-mm)*(far[tt]-mm) ; var[ii] = sqrtf( vv/(nval-1.0) ) ; } break ; case 1:{ for( tt=0 ; tt < nval ; tt++ ) far[tt] = fabsf(far[tt]) ; var[ii] = qmed_float( nval , far ) ; } break ; } } } free(fit); free(far); free(fitar); RETURN(imar); }
static void STATS_tsfunc( double tzero, double tdelta , int npts, float *ts , double ts_mean, double ts_slope, void *ud, int nbriks, float *val ) { static int ncall ; int meth_index, ii , out_index, nzpts, onset, offset, duration; float *ts_det, *ts_dif=NULL ; /** is this a "notification"? **/ if( val == NULL ){ if( npts > 0 ){ /* the "start notification" */ ncall = 0 ; /* number of calls */ } else { /* the "end notification" */ /* nothing to do here */ } return ; } /* RWC: first difference here [25 May 2011] */ if( do_tdiff ){ float tsm , tss ; ts_dif = (float*)calloc(npts, sizeof(float)) ; for( ii=1 ; ii < npts ; ii++ ) ts_dif[ii-1] = ts[ii]-ts[ii-1] ; get_linear_trend( npts-1 , ts_dif , &tsm , &tss ) ; ts_mean = (double)tsm ; ts_slope = (double)tss ; npts-- ; ts = ts_dif ; } /* KRH make copy and detrend it right here. Use ts_mean and ts_slope for detrend-ization. */ ts_det = (float*)calloc(npts, sizeof(float)); memcpy( ts_det, ts, npts * sizeof(float)); for( ii = 0; ii < npts; ii++) ts_det[ii] -= (ts_mean - (ts_slope * (npts - 1) * tdelta/2) + ts_slope * tdelta * ii) ; /** OK, actually do some work **/ /* This main loop now uses meth_index AND out_index as loop variables, */ /* mainly to avoid rewriting code that worked. */ /* meth_index is an index into the static method array, which contains the */ /* list of methods to be executed (and will also contain an integer */ /* parameter specifying the number of return values if -autocorr n and/or */ /* -autoreg p have been requested). */ /* out_index is an index into the output array. */ for(meth_index=out_index=0 ; meth_index < nmeths; meth_index++,out_index++){ switch( meth[meth_index] ){ default: case METH_MEAN: val[out_index] = ts_mean ; break ; case METH_SUM: val[out_index] = ts_mean * npts; break; /* 24 Apr 2006 */ case METH_ABSSUM:{ /* 18 Jun 2006 */ register int ii ; register float sum ; sum = 0.0; for(ii=0; ii< npts; ii++) sum += fabs(ts[ii]); val[out_index] = sum; } break; case METH_L2_NORM: /* 07 Jan 2013 [rickr] */ case METH_SUM_SQUARES:{ /* 18 Dec 2008 */ register int ii ; register float sum ; sum = 0.0; for(ii=0; ii< npts; ii++) sum += ts[ii]*ts[ii]; /* check multiple methods here */ if ( meth[meth_index] == METH_SUM_SQUARES ) val[out_index] = sum; else if ( meth[meth_index] == METH_L2_NORM ) { /* theory and practice do not always seem to agree, so... */ if( sum >= 0.0 ) val[out_index] = sqrt(sum); else val[out_index] = 0.0; } } break; case METH_SLOPE: val[out_index] = ts_slope ; break ; case METH_CVAR_NOD: /* methods that depend on the mean and stdev */ case METH_SIGMA_NOD: case METH_CVARINVNOD: case METH_CVAR: case METH_CVARINV: case METH_SIGMA:{ register int ii ; register double sum ; int mm = meth[meth_index] ; int nod = (mm == METH_CVAR_NOD) || /* no detrend flag */ (mm == METH_SIGMA_NOD) || (mm == METH_CVARINVNOD) ; sum = 0.0 ; if( !nod ){ /* not no detrend ==> use detrended data */ for( ii=0 ; ii < npts ; ii++ ) sum += ts_det[ii] * ts_det[ii] ; } else { /* use data as God gave it to us */ for( ii=0 ; ii < npts ; ii++ ) sum += (ts[ii]-ts_mean) *(ts[ii]-ts_mean) ; } sum = sqrt( sum/(npts-1.0) ) ; /* stdev */ if( mm == METH_SIGMA || mm == METH_SIGMA_NOD ) val[out_index] = sum ; else if( mm == METH_CVAR || mm == METH_CVAR_NOD ) val[out_index] = (ts_mean != 0.0) ? sum/fabs(ts_mean) : 0.0 ; else val[out_index] = (sum != 0.0) ? fabs(ts_mean)/sum : 0.0 ; } break ; /* 14 Feb 2000: these 2 new methods disturb the array ts[] */ /* 18 Dec 2002: these 2 methods no longer disturb the array ts[] */ case METH_MEDIAN:{ float* ts_copy; ts_copy = (float*)calloc(npts, sizeof(float)); memcpy( ts_copy, ts, npts * sizeof(float)); val[out_index] = qmed_float( npts , ts_copy ) ; free(ts_copy); } break ; case METH_NZMEDIAN:{ /* 27 Jun 2012 [rickr] */ float* ts_copy; int lind, lnzcount; ts_copy = (float*)calloc(npts, sizeof(float)); /* replace memcpy with non-zero copy */ lnzcount=0; for (lind=0; lind < npts; lind++) if( ts[lind] ) ts_copy[lnzcount++] = ts[lind]; /* and get the result from the possibly shortened array */ if( lnzcount > 0 ) val[out_index] = qmed_float( lnzcount , ts_copy ) ; else val[out_index] = 0.0 ; free(ts_copy); } break ; case METH_CENTROMEAN:{ /* 01 Nov 2010 */ float* ts_copy; ts_copy = (float*)calloc(npts, sizeof(float)); memcpy( ts_copy, ts, npts * sizeof(float)); val[out_index] = centromean_float( npts , ts_copy ) ; free(ts_copy); } break ; case METH_MAD:{ float* ts_copy; register int ii ; register float vm ; ts_copy = (float*)calloc(npts, sizeof(float)); memcpy( ts_copy, ts, npts * sizeof(float)); vm = qmed_float( npts , ts_copy ) ; for( ii=0 ; ii < npts ; ii++ ) ts_copy[ii] = fabs(ts_copy[ii]-vm) ; val[out_index] = qmed_float( npts , ts_copy ) ; free(ts_copy); } break ; case METH_BMV:{ /* 16 Oct 2009 */ float bmv ; qmedmadbmv_float( npts , ts , NULL,NULL , &bmv ) ; val[out_index] = bmv ; } break ; case METH_ARGMIN: case METH_ARGMIN1: case METH_MIN:{ register int ii,outdex=0 ; register float vm=ts[0] ; for( ii=1 ; ii < npts ; ii++ ) if( ts[ii] < vm ) { vm = ts[ii] ; outdex = ii ; } if (meth[meth_index] == METH_MIN) { val[out_index] = vm ; } else if (meth[meth_index] == METH_ARGMIN) { val[out_index] = outdex ; } else { val[out_index] = outdex +1; } } break ; case METH_ZCOUNT:{ /* 05 Apr 2012 */ int ii , zc ; for( ii=zc=0 ; ii < npts ; ii++ ) if( ts[ii] == 0.0f ) zc++ ; val[out_index] = zc ; } break ; case METH_NZCOUNT:{ /* Turkey 2014 */ int ii , zc ; for( ii=zc=0 ; ii < npts ; ii++ ) if( ts[ii] == 0.0f ) zc++ ; val[out_index] = npts-zc ; } break ; case METH_DW:{ register int ii ; register float den=ts[0]*ts[0] ; register float num=0 ; for( ii=1 ; ii < npts ; ii++ ) { num = num + (ts_det[ii] - ts_det[ii-1]) *(ts_det[ii] - ts_det[ii-1]); den = den + ts_det[ii] * ts_det[ii]; } if (den == 0) { val[out_index] = 0 ; } else { val[out_index] = num/den ; } } break ; case METH_ONSET: case METH_OFFSET: case METH_DURATION: case METH_ABSMAX: case METH_SIGNED_ABSMAX: case METH_ARGMAX: case METH_ARGMAX1: case METH_ARGABSMAX: case METH_ARGABSMAX1: case METH_MAX: case METH_CENTDURATION: case METH_CENTROID:{ register int ii, outdex=0 ; register float vm=ts[0]; if ( (meth[meth_index] == METH_ABSMAX) || (meth[meth_index] == METH_ARGABSMAX) || (meth[meth_index] == METH_ARGABSMAX1) ) { vm = fabs(vm) ; for( ii=1 ; ii < npts ; ii++ ) { if( fabs(ts[ii]) > vm ) { vm = fabs(ts[ii]) ; outdex = ii ; } } } else if ( meth[meth_index] == METH_SIGNED_ABSMAX ) { /* for P Hamilton 31 Aug 2012 [rickr] */ register float avm=fabs(vm); for( ii=1 ; ii < npts ; ii++ ) { if( fabs(ts[ii]) > avm ) { vm = ts[ii] ; avm = fabs(vm) ; outdex = ii ; } } } else { for( ii=1 ; ii < npts ; ii++ ) { if( ts[ii] > vm ) { vm = ts[ii] ; outdex = ii ; } } } switch( meth[meth_index] ){ default: case METH_ABSMAX: case METH_SIGNED_ABSMAX: case METH_MAX: val[out_index] = vm ; break; case METH_ONSET: case METH_OFFSET: case METH_DURATION: duration = Calc_duration(ts, npts, vm, outdex, &onset,&offset); switch(meth[meth_index]) { case METH_ONSET: val[out_index] = onset; break; case METH_OFFSET: val[out_index] = offset; break; case METH_DURATION: val[out_index] = duration; break; } break; case METH_ARGMAX: case METH_ARGABSMAX: val[out_index] = outdex ; break; case METH_ARGMAX1: case METH_ARGABSMAX1: val[out_index] = outdex +1; break; case METH_CENTROID: case METH_CENTDURATION: { float cm; cm = Calc_centroid(ts, npts); if(meth[meth_index]== METH_CENTDURATION) val[out_index] = Calc_duration(ts, npts, vm, (int) cm, &onset,&offset); else val[out_index] = cm; } break; } } break ; case METH_NZMEAN: { register int ii ; register float sum ; sum = 0.0; nzpts = 0; for( ii=0 ; ii < npts ; ii++ ) { if( ts[ii] != 0.0 ) { sum += ts[ii] ; nzpts++; } } if(npts>0) val[out_index] = sum / nzpts; else val[out_index] = 0.0; } break; case METH_AUTOCORR:{ int numVals; float *ts_corr; /* for these new methods, the extra, needed integer */ /* parameter is stored in the static array "meth", */ /* in the element right after the indicator for */ /* this method. This parameter indicates the number*/ /* of values to return, or 0 for the same length as */ /* the input array. */ numVals = meth[meth_index + 1]; if (numVals == 0) numVals = npts - 1; ts_corr = (float*)calloc(numVals,sizeof(float)); /* Call the autocorrelation function, in this file. */ autocorr(npts,ts,numVals,ts_corr); /* Copy the values into the output array val, which */ /* will be returned to the fbuc MAKER caller to */ /* populate the appropriate BRIKs with the data. */ for( ii = 0; ii < numVals; ii++) { val[out_index + ii] = ts_corr[ii]; } /* Although meth_index will be incremented by the */ /* for loop, it needs to be incremented an extra */ /* time to account for the extra parameter we */ /* pulled from the meth array. */ meth_index++; /* Similarly, though the out_index will be incremented */ /* by the for loop, we have added potentially several */ /* values to the output array, and we need to account */ /* for that here. */ out_index+=(numVals - 1); free(ts_corr); } break ; case METH_AUTOREGP:{ int numVals,kk,mm; float *ts_corr, *y, *z; float alpha, beta, tmp; /* For these new methods, the extra, needed integer */ /* parameter is stored in the static array "meth", */ /* in the element right after the indicator for */ /* this method. This parameter indicates the number*/ /* of values to return, or 0 for the same length as */ /* the input array. */ numVals = meth[meth_index + 1]; if (numVals == 0) numVals = npts - 1; /* Allocate space for the autocorrelation coefficients, */ /* result, and a temp array for calculations. */ /* Correlation coeff array is larger, because we must */ /* disregard the r0, which is identically 1. */ /* Might fix this in autocorr function */ ts_corr = (float*)malloc((numVals) * sizeof(float)); y = (float*)malloc(numVals * sizeof(float)); z = (float*)malloc(numVals * sizeof(float)); /* Call autocorr function to generate the autocorrelation */ /* coefficients. */ autocorr(npts,ts,numVals,ts_corr); /* Durbin algorithm for solving Yule-Walker equations. */ /* The Yule-Walker equations specify the autoregressive */ /* coefficients in terms of the autocorrelation coefficients. */ /* R*Phi = r, where r is vector of correlation coefficients, */ /* R is Toeplitz matrix formed from r, and Phi is a vector of */ /* the autoregression coefficients. */ /* In this implementation, 'y' is 'Phi' above and 'ts_corr' is 'r' */ y[0] = -ts_corr[0]; alpha = -ts_corr[0]; beta = 1; for (kk = 0 ; kk < (numVals - 1) ; kk++ ) { beta = (1 - alpha * alpha ) * beta ; tmp = 0; for ( mm = 0 ; mm <= kk ; mm++) { tmp = tmp + ts_corr[kk - mm] * y[mm]; } alpha = - ( ts_corr[kk+1] + tmp ) / beta ; for ( mm = 0 ; mm <= kk ; mm++ ) { z[mm] = y[mm] + alpha * y[kk - mm]; } for ( mm = 0 ; mm <= kk ; mm++ ) { y[mm] = z[mm]; } y[kk+1] = alpha ; } /* Copy the values into the output array val, which */ /* will be returned to the fbuc MAKER caller to */ /* populate the appropriate BRIKs with the data. */ for( ii = 0; ii < numVals; ii++) { val[out_index + ii] = y[ii]; if (!finite(y[ii])){ WARNING_message("BAD FLOAT y[%d]=%f; Call#%d\n",ii,y[ii],ncall); val[out_index + ii] = 0.0f ; } } /* Although meth_index will be incremented by the */ /* for loop, it needs to be incremented an extra */ /* time to account for the extra parameter we */ /* pulled from the meth array. */ meth_index++; /* Similarly, though the out_index will be incremented */ /* by the for loop, we have added potentially several */ /* values to the output array, and we need to account */ /* for that here. */ out_index+=(numVals - 1); free(ts_corr); free(y); free(z); } break ; case METH_ACCUMULATE:{ register double sum = 0.0 ; register int ii ; meth_index++; /* go past dummy zero */ for( ii = 0; ii < npts; ii++) { sum += ts[ii]; /* keep track as double */ val[out_index + ii] = sum; } out_index+=(npts - 1); } break ; } } free(ts_det); if( ts_dif == ts ) free(ts_dif) ; ncall++ ; return ; }
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() ) ; }