00001 #include "mrilib.h"
00002
00003
00004
00005
00006
00007
00008
00009
00010 static void rank_order_float( int n , float * a )
00011 {
00012 register int ii , ns , n1 , ib ;
00013 static int nb = 0 ;
00014 static int * b = NULL ;
00015 static float * c = NULL ;
00016 float cs ;
00017
00018
00019
00020 if( a == NULL ){
00021 if( b != NULL ){ free(b); free(c); b=NULL ; c=NULL; nb=0; }
00022 return ;
00023 }
00024
00025 if( n < 1 ) return ;
00026 if( n == 1 ){ a[0] = 0.0 ; return ; }
00027
00028
00029
00030 if( nb < n ){
00031 if( b != NULL ){ free(b); free(c); }
00032 b = (int *) malloc(sizeof(int )*n) ;
00033 c = (float *) malloc(sizeof(float)*n) ;
00034 nb = n ;
00035 }
00036
00037 for( ii=0 ; ii < n ; ii++ ) c[ii] = b[ii] = ii ;
00038
00039
00040
00041 qsort_floatint( n , a , b ) ;
00042
00043
00044
00045 n1 = n-1 ;
00046 for( ii=0 ; ii < n1 ; ii++ ){
00047 if( a[ii] == a[ii+1] ){
00048 cs = 2*ii+1 ; ns = 2 ; ib=ii ; ii++ ;
00049 while( ii < n1 && a[ii] == a[ii+1] ){ ii++ ; ns++ ; cs += ii ; }
00050 for( cs/=ns ; ib <= ii ; ib++ ) c[ib] = cs ;
00051 }
00052 }
00053
00054 for( ii=0 ; ii < n ; ii++ ) a[b[ii]] = c[ii] ;
00055
00056 return ;
00057 }
00058
00059
00060
00061
00062
00063 static float spearman_rank_prepare( int n , float * a )
00064 {
00065 register int ii ;
00066 register float rb , rs ;
00067
00068 rank_order_float( n , a ) ;
00069
00070 rb = 0.5*(n-1) ; rs=0.0 ;
00071 for( ii=0 ; ii < n ; ii++ ){
00072 a[ii] -= rb ;
00073 rs += a[ii]*a[ii] ;
00074 }
00075
00076 return rs ;
00077 }
00078
00079
00080
00081 static float quadrant_corr_prepare( int n , float * a )
00082 {
00083 register int ii ;
00084 register float rb , rs ;
00085
00086 rank_order_float( n , a ) ;
00087
00088 rb = 0.5*(n-1) ; rs=0.0 ;
00089 for( ii=0 ; ii < n ; ii++ ){
00090 a[ii] = (a[ii] > rb) ? 1.0
00091 : (a[ii] < rb) ? -1.0 : 0.0 ;
00092 rs += a[ii]*a[ii] ;
00093 }
00094
00095 return rs ;
00096 }
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 static float spearman_rank_corr( int n , float * x , float rv , float * r )
00107 {
00108 register int ii ;
00109 register float ss ; float xv ;
00110
00111 xv = spearman_rank_prepare( n , x ) ; if( xv <= 0.0 ) return 0.0 ;
00112
00113 for( ii=0,ss=0.0 ; ii < n ; ii++ ) ss += x[ii] * r[ii] ;
00114
00115 return ( ss/sqrt(rv*xv) ) ;
00116 }
00117
00118
00119
00120 static float quadrant_corr( int n , float * x , float rv , float * r )
00121 {
00122 register int ii ;
00123 register float ss ; float xv ;
00124
00125 xv = quadrant_corr_prepare( n , x ) ; if( xv <= 0.0 ) return 0.0 ;
00126
00127 for( ii=0,ss=0.0 ; ii < n ; ii++ ) ss += x[ii] * r[ii] ;
00128
00129 return ( ss/sqrt(rv*xv) ) ;
00130 }
00131
00132
00133
00134 #define SPEARMAN 1
00135 #define QUADRANT 2
00136
00137 int main( int argc , char *argv[] )
00138 {
00139 THD_3dim_dataset *dset ;
00140 int nopt=1 , method=SPEARMAN , do_range=0 , do_autoclip=0 ;
00141 int nvox , nvals , ii,iv,jj ;
00142 float *medar, *var , rv , *corr , cmed,cmad,cbot,ctop , clip_val=0.0 ;
00143 MRI_IMAGE *tsim , *medim ;
00144 int nkeep , *keep ;
00145 float *mkeep , *tkeep ;
00146
00147
00148
00149 if( argc < 2 || strcmp(argv[1],"-help") == 0 ){
00150 printf("Usage: 3dTqual [options] dataset\n"
00151 "Computes a `quality index' for each sub-brick in a 3D+time dataset.\n"
00152 "The output is a 1D time series with the index for each sub-brick.\n"
00153 "The results are written to stdout.\n"
00154 "\n"
00155 "Note that small values of the index are 'good', indicating that\n"
00156 "the sub-brick is not very different from the norm. The purpose\n"
00157 "of this program is to provide a crude way of screening FMRI\n"
00158 "time series for sporadic abnormal images, such as might be\n"
00159 "caused by large subject head motion or scanner glitches.\n"
00160 "\n"
00161 "Do not take the results of this program too literally. It\n"
00162 "is intended as a GUIDE to help you find data problems, and no\n"
00163 "more. It is not an assurance that the dataset is good, and\n"
00164 "it may indicate problems where nothing is wrong.\n"
00165 "\n"
00166 "Sub-bricks with index values much higher than others should be\n"
00167 "examined for problems. How you determine what 'much higher' means\n"
00168 "is mostly up to you. I suggest graphical inspection of the indexes\n"
00169 "(cf. EXAMPLE, infra). As a guide, the program will print (stderr)\n"
00170 "the median quality index and the range median-3.5*MAD .. median+3.5*MAD\n"
00171 "(MAD=Median Absolute Deviation). Values well outside this range might\n"
00172 "be considered suspect; if the quality index were normally distributed,\n"
00173 "then values outside this range would occur only about 1%% of the time.\n"
00174 "\n"
00175 "OPTIONS:\n"
00176 " -spearman = Quality index is 1 minus the Spearman (rank)\n"
00177 " correlation coefficient of each sub-brick\n"
00178 " with the median sub-brick.\n"
00179 " [This is the default method.]\n"
00180 " -quadrant = Similar to -spearman, but using 1 minus the\n"
00181 " quadrant correlation coefficient as the\n"
00182 " quality index.\n"
00183 "\n"
00184 " -autoclip = Clip off low-intensity regions in the median sub-brick,\n"
00185 " -automask = so that the correlation is only computed between\n"
00186 " high-intensity (presumably brain) voxels. The\n"
00187 " intensity level is determined the same way that\n"
00188 " 3dClipLevel works. This prevents the vast number\n"
00189 " of nearly 0 voxels outside the brain from biasing\n"
00190 " the correlation coefficient calculations.\n"
00191 #if 1
00192 "\n"
00193 " -clip val = Clip off values below 'val' in the median sub-brick.\n"
00194 #endif
00195 "\n"
00196 " -range = Print the median-3.5*MAD and median+3.5*MAD values\n"
00197 " out with EACH quality index, so that they\n"
00198 " can be plotted (cf. Example, infra).\n"
00199 " Notes: * These values are printed to stderr in any case.\n"
00200 " * This is only useful for plotting with 1dplot.\n"
00201 " * The lower value median-3.5*MAD is never allowed\n"
00202 " to go below 0.\n"
00203 "\n"
00204 "EXAMPLE:\n"
00205 " 3dTqual -range -automask fred+orig | 1dplot -one -stdin\n"
00206 "will calculate the time series of quality indexes and plot them\n"
00207 "to an X11 window, along with the median+/-3.5*MAD bands.\n"
00208 "\n"
00209 "NOTE: cf. program 3dToutcount for a somewhat different quality check.\n"
00210 "\n"
00211 "-- RWCox - Aug 2001\n"
00212 ) ;
00213 exit(0) ;
00214 }
00215
00216 mainENTRY("3dTqual main"); machdep(); AFNI_logger("3dTqual",argc,argv);
00217 PRINT_VERSION("3dTqual");
00218
00219
00220
00221 while( nopt < argc && argv[nopt][0] == '-' ){
00222
00223 if( strcmp(argv[nopt],"-autoclip") == 0 ||
00224 strcmp(argv[nopt],"-automask") == 0 ){
00225
00226 do_autoclip = 1 ; nopt++ ; continue ;
00227 }
00228
00229 #if 1
00230 if( strcmp(argv[nopt],"-clip") == 0 ){
00231 do_autoclip = 0 ;
00232 clip_val = strtod(argv[++nopt],NULL) ;
00233 if( clip_val <= 0.0 ){
00234 fprintf(stderr,"** value after -clip is illegal!\n"); exit(1);
00235 }
00236 nopt++ ;continue ;
00237 }
00238 #endif
00239
00240 if( strcmp(argv[nopt],"-range") == 0 ){
00241 do_range = 1 ; nopt++ ; continue ;
00242 }
00243
00244 if( strcmp(argv[nopt],"-spearman") == 0 ){
00245 method = SPEARMAN ; nopt++ ; continue ;
00246 }
00247
00248 if( strcmp(argv[nopt],"-quadrant") == 0 ){
00249 method = QUADRANT ; nopt++ ; continue ;
00250 }
00251
00252 fprintf(stderr,"*** Unknown option: %s\n",argv[nopt]); exit(1);
00253 }
00254
00255
00256
00257 if( nopt >= argc ){
00258 fprintf(stderr,"*** No dataset on command line!?\n"); exit(1);
00259 }
00260
00261 dset = THD_open_dataset( argv[nopt] ) ;
00262 if( dset == NULL ){
00263 fprintf(stderr,"*** Can't open dataset %s\n",argv[nopt]); exit(1);
00264 }
00265 if( DSET_NUM_TIMES(dset) < 2 ){
00266 fprintf(stderr,"*** Input dataset is not 3D+time\n"); exit(1);
00267 }
00268 DSET_load(dset) ;
00269 if( !DSET_LOADED(dset) ){
00270 fprintf(stderr,"*** Can't read dataset bricks\n"); exit(1);
00271 }
00272
00273
00274
00275 nvox = DSET_NVOX(dset) ;
00276 nvals = DSET_NVALS(dset) ;
00277
00278 medim = THD_median_brick( dset ) ; if( medim == NULL ) exit(1) ;
00279 medar = MRI_FLOAT_PTR(medim) ;
00280
00281 if( do_autoclip ){
00282 clip_val = THD_cliplevel( medim , 0.5 ) ;
00283 fprintf(stderr,"++ Autoclip value = %g\n",clip_val) ;
00284 }
00285
00286 if( clip_val > 0.0 ){
00287 nkeep = 0 ;
00288 for( ii=0 ; ii < nvox ; ii++ )
00289 if( medar[ii] >= clip_val ) nkeep++ ;
00290 if( nkeep < 9 ){
00291 fprintf(stderr,"*** %d voxels survive the clip: can't continue\n",nkeep) ;
00292 exit(1) ;
00293 } else if( nkeep == nvox ){
00294 fprintf(stderr,"++ All %d voxels survive the clip\n",nvox) ;
00295 mkeep = medar ;
00296 } else {
00297 keep = (int *) malloc( sizeof(int) *nkeep ) ;
00298 mkeep = (float *) malloc( sizeof(float)*nkeep ) ;
00299 tkeep = (float *) malloc( sizeof(float)*nkeep ) ;
00300 for( jj=ii=0 ; ii < nvox ; ii++ )
00301 if( medar[ii] >= clip_val ){
00302 keep[jj] = ii ; mkeep[jj] = medar[ii] ; jj++ ;
00303 }
00304 mri_free(medim) ;
00305 fprintf(stderr,"++ %d out of %d voxels survive the clip\n",nkeep,nvox) ;
00306 }
00307 } else {
00308 nkeep = nvox ; mkeep = medar ;
00309 }
00310
00311 switch( method ){
00312 default:
00313 case SPEARMAN:
00314 rv = spearman_rank_prepare( nkeep , mkeep ) ; break ;
00315
00316 case QUADRANT:
00317 rv = quadrant_corr_prepare( nkeep , mkeep ) ; break ;
00318 }
00319
00320
00321
00322 corr = (float *) malloc( sizeof(float)*nvals ) ;
00323
00324 for( iv=0 ; iv < nvals ; iv++ ){
00325
00326
00327
00328 tsim = THD_extract_float_brick( iv , dset ) ;
00329 if( tsim == NULL ) exit(1) ;
00330 var = MRI_FLOAT_PTR(tsim) ;
00331
00332 if( nkeep < nvox ){
00333 for( jj=0 ; jj < nkeep ; jj++ ) tkeep[jj] = var[keep[jj]] ;
00334 } else {
00335 tkeep = var ;
00336 }
00337
00338
00339
00340 switch( method ){
00341 default:
00342 case SPEARMAN:
00343 corr[iv] = 1.0-spearman_rank_corr(nkeep,tkeep,rv,mkeep) ; break ;
00344
00345 case QUADRANT:
00346 corr[iv] = 1.0-quadrant_corr(nkeep,tkeep,rv,mkeep) ; break ;
00347 }
00348
00349 mri_free(tsim) ; DSET_unload_one(dset,iv) ;
00350
00351 if( !do_range ) printf( "%g\n" , corr[iv] ) ;
00352
00353 }
00354
00355
00356
00357 qmedmad_float( nvals,corr , &cmed,&cmad ) ;
00358
00359 cbot = cmed - 3.5*cmad ;
00360 ctop = cmed + 3.5*cmad ; if( cbot < 0.0 ) cbot = 0.0 ;
00361
00362
00363
00364 if( do_range ){
00365 for( iv=0 ; iv < nvals ; iv++ )
00366 printf( "%g %g %g\n", corr[iv] , cbot,ctop ) ;
00367 }
00368
00369 fprintf(stderr,"++ Median=%g Median-3.5*MAD=%g Median+3.5*MAD=%g\n",
00370 cmed,cbot,ctop) ;
00371 exit(0) ;
00372 }