#include "mrilib.h" #undef EM #define EM(s) ERROR_message("InstaCorr setup bad: %s",(s)) /*---------------------------------------------------------------------------*/ /* Return 1 if methods uses timeseries normalization, 0 otherwise */ int THD_instacorr_cmeth_needs_norm(int cmeth) { switch (cmeth) { case NBISTAT_EUCLIDIAN_DIST: case NBISTAT_CITYBLOCK_DIST: return(0); default: return(1); } return(1); } /*---------------------------------------------------------------------------*/ /* Read and pre-process time series for InstaCorr [moved here 10 Nov 2011]. */ static MRI_vectim * THD_instacorr_tsprep( ICOR_setup *iset , THD_3dim_dataset *dset ) { MRI_vectim *mv ; int iv , nmmm , ntime ; float **dvec , **gvec=NULL ; int ngvec=0 ; int ngext=0 ; float *uvec=NULL ; ENTRY("THD_instacorr_tsprep") ; if( iset == NULL || dset == NULL ) RETURN(NULL) ; /*--- Extract time series for analysis ---*/ ININFO_message("Extracting dataset time series: %s",DSET_BRIKNAME(dset)) ; mv = THD_dset_to_vectim_stend( dset , iset->mmm , iset->start , iset->end ) ; if( mv == NULL ){ EM("Can't extract dataset time series!") ; RETURN(NULL) ; } nmmm = mv->nvec ; ntime = mv->nvals ; /* #dataset time points to process */ mv->ignore = 0 ; /* 12 Jul 2024 */ if( iset->despike ){ int_pair ip ; ININFO_message("- Testing time series for spikes") ; ip = THD_vectim_despike9( mv ) ; if( ip.i > 0 ) ININFO_message(" -- Removed %d spikes from %d time series",ip.j,ip.i) ; else ININFO_message(" -- No spikes were found") ; } /*--- Filter time series ---*/ ININFO_message("- Filtering %d dataset time series",nmmm) ; if( iset->fbot < 0.0f ) iset->fbot = 0.0f ; if( iset->ftop <= iset->fbot ) iset->ftop = 999999.9f ; /* infinity */ dvec = (float **)malloc(sizeof(float *)*nmmm) ; for( iv=0 ; iv < nmmm ; iv++ ) dvec[iv] = VECTIM_PTR(mv,iv) ; if( iset->gortim != NULL ){ /** set up the extra orts **/ if( iset->gortim->nx < ntime ){ /* too short to be useful */ ERROR_message("Global Ort time series length=%d is too short!",iset->gortim->nx); ERROR_message(" ==> ignoring Global Ort file") ; } else if( iset->gortim->nx >= ntime && iset->gortim->nx < ntime+iset->start ){ /* too short to ignore initial points */ if( iset->start > 0 ) ININFO_message("Global Ort time series length=%d: not ignoring initial points", iset->gortim->nx ) ; ngvec = iset->gortim->ny ; gvec = (float **)malloc(sizeof(float *)*ngvec) ; for( iv=0 ; iv < ngvec ; iv++ ) gvec[iv] = MRI_FLOAT_PTR(iset->gortim) + iv*iset->gortim->nx ; } else { /* long enough to ignore initial points */ if( iset->start > 0 ) ININFO_message("Global Ort time series length=%d: ignoring first %d points", iset->gortim->nx , iset->start ) ; ngvec = iset->gortim->ny ; gvec = (float **)malloc(sizeof(float *)*ngvec) ; for( iv=0 ; iv < ngvec ; iv++ ) gvec[iv] = MRI_FLOAT_PTR(iset->gortim) + iv*iset->gortim->nx + iset->start ; } } ngext = iset->gortnpc ; if( ngext > 0 ){ MRI_IMAGE *qim ; MRI_IMARR *qimar ; float *qar , *svec ; INIT_IMARR(qimar) ; for( iv=0 ; iv < nmmm ; iv++ ){ qim = mri_new(ntime,1,MRI_float) ; qar = MRI_FLOAT_PTR(qim) ; memcpy(qar,dvec[iv],sizeof(float)*ntime) ; if( iset->polort >= 0 ) THD_cubic_detrend(ntime,qar) ; ADDTO_IMARR(qimar,qim) ; } uvec = (float *)malloc(sizeof(float)*ntime*ngext) ; svec = (float *)malloc(sizeof(float)*ngext) ; iv = mri_principal_vectors( qimar , ngext , svec , uvec ) ; DESTROY_IMARR(qimar) ; if( iv < ngext ){ WARNING_message("Could not compute PC vectors -- skipping :-(((") ; free(uvec) ; } else { gvec = (float **)realloc(gvec,sizeof(float *)*(ngvec+ngext)) ; for( iv=0 ; iv < ngext ; iv++ ) gvec[iv+ngvec] = uvec + iv*ntime ; fprintf(stderr," + singular values:") ; for( iv=0 ; iv < ngext ; iv++ ) fprintf(stderr," %.6g",svec[iv]) ; fprintf(stderr,"\n") ; free(svec) ; } } /* despite the 'bandpass' name, this is the filtering step */ (void)THD_bandpass_OK( ntime , mv->dt , iset->fbot,iset->ftop , 1 ) ; iset->ndet = THD_bandpass_vectors( ntime, nmmm, dvec, mv->dt, iset->fbot, iset->ftop, iset->polort, ngvec+ngext, gvec ) ; /** ININFO_message("Filtering removed %d DOF",iset->ndet) ; **/ free(dvec) ; if( gvec != NULL ) free(gvec) ; if( uvec != NULL ) free(uvec) ; /*--- Blur time series ---*/ if( iset->blur > 0.0f ){ int nrep ; float fx,fy,fz ; ININFO_message("- Starting %.1f mm blur %s" , iset->blur , (iset->mmm==NULL) ? "\0" : "(in mask)" ) ; mri_blur3D_getfac( iset->blur , mv->dx , mv->dy , mv->dz , &nrep , &fx , &fy , &fz ) ; ININFO_message("- Spatially blurring %d dataset volumes: %d iterations",mv->nvals,nrep) ; mri_blur3D_vectim( mv , iset->blur ) ; } /*-- normalize --*/ if( THD_instacorr_cmeth_needs_norm(iset->cmeth) ){ ININFO_message("- Normalizing dataset time series") ; THD_vectim_normalize( mv ) ; } RETURN(mv) ; } /*---------------------------------------------------------------------------*/ /*! Return value is 0 if an error occurs, or number of voxels prepared. */ int THD_instacorr_prepare( ICOR_setup *iset ) { int nmmm=0 ; byte *mmm=NULL ; ENTRY("THD_instacorr_prepare") ; /*-- check inputs for a reasonable amount of usefulness --*/ if( iset == NULL || !ISVALID_DSET(iset->dset) ){ EM("no dataset"); RETURN(0); } if( DSET_NVALS(iset->dset) < 4 ) { EM("nvals < 4") ; RETURN(0); } if( ISVALID_DSET(iset->eset) && ( DSET_NVOX (iset->eset) != DSET_NVOX (iset->dset) || DSET_NVALS(iset->eset) != DSET_NVALS(iset->dset) ) ){ EM("eset <> dset"); RETURN(0); } /*-- create mask --*/ if( iset->mmm != NULL ){ free(iset->mmm) ; iset->mmm = NULL ; } INFO_message("InstaCorr preparations:") ; /*-- automask? --*/ if( iset->automask ){ ININFO_message("Computing automask") ; mmm = THD_automask(iset->dset) ; if( mmm == NULL ){ ERROR_message("Can't create automask from '%s'?!",DSET_BRIKNAME(iset->dset)) ; RETURN(0) ; } nmmm = THD_countmask( DSET_NVOX(iset->dset) , mmm ) ; if( nmmm < 9 ){ ERROR_message("Automask from '%s' has %d voxels!" , DSET_BRIKNAME(iset->dset) , nmmm ) ; free(mmm) ; RETURN(0) ; } ININFO_message("Automask from '%s' has %d voxels",DSET_BRIKNAME(iset->dset),nmmm) ; iset->mmm = mmm ; } /*-- or read a mask dataset? ---*/ if( iset->mmm == NULL && iset->mset != NULL ){ if( DSET_NVOX(iset->mset) != DSET_NVOX(iset->dset) ){ ERROR_message("Mask dataset '%s' doesn't match input dataset '%s'", DSET_BRIKNAME(iset->mset) , DSET_BRIKNAME(iset->dset) ) ; RETURN(0) ; } ININFO_message("Extracting mask from mask dataset sub-brick #%d",iset->mindex) ; mmm = THD_makemask( iset->mset , iset->mindex , 1.0f,-1.0f ) ; if( mmm == NULL ){ ERROR_message("Can't make mask from '%s[%d]'" , DSET_BRIKNAME(iset->mset) , iset->mindex ) ; RETURN(0) ; } nmmm = THD_countmask( DSET_NVOX(iset->mset) , mmm ) ; if( nmmm < 9 ){ ERROR_message("Mask from '%s[%d]' has %d voxels!" , DSET_BRIKNAME(iset->mset) , iset->mindex , nmmm ) ; free(mmm) ; RETURN(0) ; } ININFO_message("Mask from '%s[%d]' has %d voxels!" , DSET_BRIKNAME(iset->mset) , iset->mindex , nmmm ) ; iset->mmm = mmm ; } if( iset->mmm == NULL ){ ININFO_message("No mask for InstaCorr") ; nmmm = DSET_NVOX(iset->dset) ; } /*--- time series preparation ---*/ iset->mv = THD_instacorr_tsprep( iset , iset->dset ) ; if( iset->mv == NULL ) RETURN(0) ; iset->mv_is_new = (iset->mv != NULL) ; /* mark as a new timeseries vectim [12 Jul 2024] */ iset->ev = THD_instacorr_tsprep( iset , iset->eset ) ; iset->ev_is_new = (iset->ev != NULL) ; /* 12 Jul 2024 */ RETURN(nmmm) ; } /*---------------------------------------------------------------------------*/ /* Get the seed timeseries [moved to a function 07 Oct 2014] */ float * THD_instacorr_getseed( ICOR_setup *iset , int ijk ) { float *tsar ; int kk ; float sblur ; ENTRY("THD_instacorr_getseed") ; if( iset == NULL || iset->mv == NULL || ijk < 0 ) RETURN(NULL) ; /** extract reference time series **/ tsar = (float *)malloc(sizeof(float)*(iset->mv->nvals+iset->start)) ; kk = THD_vectim_ifind( ijk , iset->mv ) ; if( kk >= 0 ){ /* direct from vectim, if available */ memcpy( tsar , VECTIM_PTR(iset->mv,kk) , sizeof(float)*iset->mv->nvals ) ; } else { /* non-vectim voxel in dataset ==> must be processed */ free(tsar) ; RETURN(NULL) ; /* don't allow this */ } /** blur the ref time series, if ordered [15 May 2009] **/ sblur = iset->sblur ; if( sblur != 0.0f ){ int gblur = AFNI_yesenv("AFNI_INSTACORR_SEEDBLUR") ; int grad = (gblur) ? 1.2345f*sblur : 1.0001f*sblur ; MCW_cluster *smask ; float wtsum=1.0f , fac , *qar ; float *sar=(float *)malloc(sizeof(float)*iset->mv->nvals) ; int qi,qj,qk , ii,ij,ik , qjk,qq , nx,ny,nz,nxy ; register int tt ; if( grad > 0.0f ) smask = MCW_spheremask( iset->mv->dx, iset->mv->dy, iset->mv->dz, grad ) ; else smask = MCW_spheremask( 1.0f, 1.0f, 1.0f, -grad ) ; nx = iset->mv->nx ; ny = iset->mv->ny ; nz = iset->mv->nz ; nxy = nx*ny ; ii = ijk % nx ; ik = ijk / nxy ; ij = (ijk-ik*nxy) / nx ; fac = FWHM_TO_SIGMA(sblur); fac = 1.0 / (2.0f * fac*fac); /* for gblur */ memcpy(sar,tsar,sizeof(float)*iset->mv->nvals) ; /* copy of seed */ for( kk=1 ; kk < smask->num_pt ; kk++ ){ /* add stuff to this copy */ qi = ii + smask->i[kk] ; if( qi < 0 || qi >= nx ) continue ; qj = ij + smask->j[kk] ; if( qj < 0 || qj >= ny ) continue ; qk = ik + smask->k[kk] ; if( qk < 0 || qk >= nz ) continue ; qjk = qi + qj*nx + qk*nxy ; qq = THD_vectim_ifind( qjk , iset->mv ) ; if( qq >= 0 ){ register float wt ; if( gblur ){ float rad=smask->mag[kk]; wt = exp(-fac*rad*rad); } else wt = 1.0f; wtsum += wt ; qar = VECTIM_PTR(iset->mv,qq) ; for( tt=0 ; tt < iset->mv->nvals ; tt++ ) sar[tt] += wt * qar[tt] ; } } if( wtsum > 1.0f ){ /* usually true, but not always */ fac = 1.0f / wtsum ; for( tt=0 ; tt < iset->mv->nvals ; tt++ ) tsar[tt] = fac * sar[tt] ; } free(sar) ; KILL_CLUSTER(smask) ; THD_normalize( iset->mv->nvals , tsar ) ; } /* end of seed blur */ RETURN(tsar) ; } /*---------------------------------------------------------------------------*/ /*! Compute the instant correlation from spatial voxel index ijk -- mri_free() this image when you are done with it! *//*-------------------------------------------------------------------------*/ MRI_IMAGE * THD_instacorr( ICOR_setup *iset , int ijk ) { int kk ; MRI_IMAGE *qim ; float *qar , *dar , *tsar ; int *ivar ; float sblur=0.0f ; MRI_vectim *mv ; int iter_count , iter , nvals ; float iter_thresh , *nsar=NULL ; ENTRY("THD_instacorr") ; #if 0 INFO_message("THD_instacorr: ijk=%d iset=%p",ijk,(void *)iset) ; if( iset != NULL ) ININFO_message(" iset->mv=%p",(void *)iset->mv) ; #endif if( iset == NULL || iset->mv == NULL || ijk < 0 ) RETURN(NULL) ; /** extract reference time series **/ tsar = THD_instacorr_getseed( iset , ijk ) ; if( tsar == NULL ) RETURN(NULL) ; /* bad */ /** save seed in iset struct [15 May 2009] **/ nvals = iset->mv->nvals ; iset->tseed = (float *)realloc( iset->tseed , sizeof(float)*nvals ) ; memcpy( iset->tseed , tsar , sizeof(float)*nvals ) ; /** do the correlations **/ sblur = iset->sblur ; iter_count = iset->iter_count ; if( iter_count <= 0 ) iter_count = 1 ; iter_thresh = iset->iter_thresh ; if( iter_count > 1 ) nsar = (float *)malloc(sizeof(float)*nvals) ; mv = (iset->ev == NULL) ? iset->mv : iset->ev ; dar = (float *)malloc(sizeof(float)*iset->mv->nvec) ; for( iter=0 ; iter < iter_count ; iter++ ){ /* iteration: 05 Feb 2015 */ switch( iset->cmeth ){ /* the actual work */ default: case NBISTAT_PEARSON_CORR: THD_vectim_dotprod ( mv , tsar , dar , 0 ) ; break ; case NBISTAT_SPEARMAN_CORR: THD_vectim_spearman( mv , tsar , dar ) ; break ; case NBISTAT_QUADRANT_CORR: THD_vectim_quadrant( mv , tsar , dar ) ; break ; case NBISTAT_TICTACTOE_CORR: THD_vectim_tictactoe( mv , tsar , dar ) ; break ; case NBISTAT_KENDALL_TAUB: THD_vectim_ktaub( mv , tsar , dar ) ; break ; /* 29 Apr 2010 */ case NBISTAT_BC_PEARSON_M: THD_vectim_pearsonBC( mv,sblur,ijk,0,dar ); break; /* 07 Mar 2011 */ case NBISTAT_BC_PEARSON_V: THD_vectim_pearsonBC( mv,sblur,ijk,1,dar ); break; /* 07 Mar 2011 */ case NBISTAT_EUCLIDIAN_DIST:/* 04 Apr 2012, ZSS*/ THD_vectim_distance( mv , tsar , dar , 0, "inv;n_scale") ; break ; case NBISTAT_CITYBLOCK_DIST:/* 04 Apr 2012, ZSS*/ THD_vectim_distance( mv , tsar , dar , 1, "inv;n_scale") ; break ; case NBISTAT_QUANTILE_CORR: /* 11 May 2012 */ THD_vectim_quantile( mv , tsar , dar ) ; break ; } if( iter+1 < iter_count ){ /* threshold, compute new seed [05 Feb 2015] */ int ii , ngood=0 ; float *vv ; for( ii=0 ; ii < nvals ; ii++ ) nsar[ii] = 0.0f ; for( kk=0 ; kk < mv->nvec ; kk++ ){ vv = VECTIM_PTR(mv,kk) ; if( dar[kk] >= iter_thresh ){ ngood++ ; for( ii=0 ; ii < nvals ; ii++ ) nsar[ii] += vv[ii] ; } else if( dar[kk] <= -iter_thresh ){ ngood++ ; for( ii=0 ; ii < nvals ; ii++ ) nsar[ii] -= vv[ii] ; } } if( ngood == 0 ){ /* nothing above threshold?! */ WARNING_message("InstaCorr -- nothing above threshold %g on iteration #%d", iter_thresh , iter+1 ) ; break ; } #if 0 ININFO_message("InstaCorr: iteration #%d has %d supra-threshold",iter+1,ngood) ; #endif THD_normalize( nvals , nsar ) ; memcpy( tsar , nsar , sizeof(float)*nvals ) ; } else if( nsar != NULL ){ free(nsar) ; nsar = NULL ; } } /* end of iterations */ /** put them into the output image **/ qim = mri_new_vol( mv->nx , mv->ny , mv->nz , MRI_float ) ; qar = MRI_FLOAT_PTR(qim) ; ivar = mv->ivec ; for( kk=0 ; kk < mv->nvec ; kk++ ) qar[ivar[kk]] = dar[kk] ; /** e finito **/ free(dar) ; free(tsar) ; RETURN(qim) ; } /*---------------------------------------------------------------------------*/ /*! Compute the instant correlation from tsar as the seed series, from time indexes ibot to itop-1 -- mri_free() result when you are done with it *//*-------------------------------------------------------------------------*/ MRI_IMAGE * THD_instacorr_section( ICOR_setup *iset, float *tsar, int ibot, int itop ) { int kk ; MRI_IMAGE *qim ; float *qar , *dar ; int *ivar ; MRI_vectim *mv ; ENTRY("THD_instacorr_section") ; if( iset == NULL || iset->mv == NULL || tsar == NULL ) RETURN(NULL) ; if( ibot < 0 ) ibot = 0 ; if( itop > iset->mv->nvals-1 ) itop = iset->mv->nvals-1 ; if( itop-ibot < 5 ) RETURN(NULL) ; /* too short to correlate */ #if 0 ININFO_message("section: ibot=%d itop=%d",ibot,itop) ; #endif /** do the correlations **/ dar = (float *)malloc(sizeof(float)*iset->mv->nvec) ; mv = (iset->ev == NULL) ? iset->mv : iset->ev ; switch( iset->cmeth ){ default: case NBISTAT_PEARSON_CORR: THD_vectim_pearson_section( mv , tsar , dar , ibot,itop ) ; break ; #if 0 case NBISTAT_SPEARMAN_CORR: THD_vectim_spearman( mv , tsar , dar ) ; break ; case NBISTAT_QUADRANT_CORR: THD_vectim_quadrant( mv , tsar , dar ) ; break ; case NBISTAT_TICTACTOE_CORR: THD_vectim_tictactoe( mv , tsar , dar ) ; break ; case NBISTAT_KENDALL_TAUB: THD_vectim_ktaub( mv , tsar , dar ) ; break ; /* 29 Apr 2010 */ case NBISTAT_BC_PEARSON_M: THD_vectim_pearsonBC( mv,sblur,ijk,0,dar ); break; /* 07 Mar 2011 */ case NBISTAT_BC_PEARSON_V: THD_vectim_pearsonBC( mv,sblur,ijk,1,dar ); break; /* 07 Mar 2011 */ case NBISTAT_EUCLIDIAN_DIST:/* 04 Apr 2012, ZSS*/ THD_vectim_distance( mv , tsar , dar , 0, "inv;n_scale") ; break ; case NBISTAT_CITYBLOCK_DIST:/* 04 Apr 2012, ZSS*/ THD_vectim_distance( mv , tsar , dar , 1, "inv;n_scale") ; break ; case NBISTAT_QUANTILE_CORR: /* 11 May 2012 */ THD_vectim_quantile( mv , tsar , dar ) ; break ; #endif } /** put them into the output image **/ qim = mri_new_vol( mv->nx , mv->ny , mv->nz , MRI_float ) ; qar = MRI_FLOAT_PTR(qim) ; ivar = mv->ivec ; for( kk=0 ; kk < mv->nvec ; kk++ ) qar[ivar[kk]] = dar[kk] ; /** e finito **/ free(dar) ; RETURN(qim) ; } /*---------------------------------------------------------------------------*/ MRI_IMARR * THD_instacorr_collection( ICOR_setup *iset , int ijk ) { int ibot , itop , nii , kk ; float *tsar ; MRI_IMARR *outar ; MRI_IMAGE *outim ; /** extract reference time series **/ tsar = THD_instacorr_getseed( iset , ijk ) ; if( tsar == NULL ) RETURN(NULL) ; /* bad */ INIT_IMARR(outar) ; for( nii=0,ibot=0 ; nii < iset->cnum ; ibot+=iset->cstep,nii++ ){ itop = ibot + iset->clen-1 ; outim = THD_instacorr_section( iset , tsar , ibot,itop ) ; ADDTO_IMARR(outar,outim) ; } free(tsar) ; RETURN(outar) ; }