1 /*****************************************************************************
2 Major portions of this software are copyrighted by the Medical College
3 of Wisconsin, 1994-2000, and are released under the Gnu General Public
4 License, Version 2. See the file README.Copyright for details.
5 ******************************************************************************/
6
7 #include "mrilib.h"
8 #include <string.h>
9
10 double DSET_cor( THD_3dim_dataset *, THD_3dim_dataset *, byte *, int ,
11 double *,double *,double *,double *,double *,int * ) ;
12 double DSET_eta2(THD_3dim_dataset *, THD_3dim_dataset *, byte *, int * ) ;
13 double FPTR_cor( float *, float *, int , byte *, int ,
14 double *,double *,double *,double *,double *,int * ) ;
15 double FPTR_eta2(float *, float *, int , byte *, int * ) ;
16 double FPTR_dice(float *, float *, int , byte *, int * ) ;
17 float * get_float_dset_data_pointer( THD_3dim_dataset * , int , int * , int) ;
18
usage_3ddot(int detail)19 void usage_3ddot(int detail) {
20 printf(
21 "Usage: 3ddot [options] dset1 [dset2 dset3 ...]\n"
22 "Output = correlation coefficient between sub-brick pairs\n"
23 " All datasets on the command line will get catenated\n"
24 " at loading time and should all be on the same grid.\n"
25 " - you can use sub-brick selectors on the dsets\n"
26 " - the result is a number printed to stdout\n"
27 "Options:\n"
28 " -mask mset Means to use the dataset 'mset' as a mask:\n"
29 " Only voxels with nonzero values in 'mset'\n"
30 " will be averaged from 'dataset'. Note\n"
31 " that the mask dataset and the input dataset\n"
32 " must have the same number of voxels.\n"
33 " -mrange a b Means to further restrict the voxels from\n"
34 " 'mset' so that only those mask values\n"
35 " between 'a' and 'b' (inclusive) will\n"
36 " be used. If this option is not given,\n"
37 " all nonzero values from 'mset' are used.\n"
38 " Note that if a voxel is zero in 'mset', then\n"
39 " it won't be included, even if a < 0 < b.\n"
40 " -demean Means to remove the mean from each volume\n"
41 " prior to computing the correlation.\n"
42 " -docor Return the correlation coefficient (default).\n"
43 " -dodot Return the dot product (unscaled).\n"
44 " -docoef Return the least square fit coefficients\n"
45 " {a,b} so that dset2 is approximately a + b*dset1\n"
46 " -dosums Return the 6 numbers xbar=<x> ybar=<y>\n"
47 " <(x-xbar)^2> <(y-ybar)^2> <(x-xbar)(y-ybar)> \n"
48 " and the correlation coefficient.\n"
49 " -doeta2 Return eta-squared (Cohen, NeuroImage 2008).\n"
50 " -dodice Return the Dice coefficient (the Sorensen-Dice index).\n"
51 " -show_labels Print sub-brick labels to help identify what \n"
52 " is being correlated. This option is useful when\n"
53 " you have more than 2 sub-bricks at input.\n"
54 " -upper Compute upper triangular matrix\n"
55 " -full Compute the whole matrix. A waste of time, but handy\n"
56 " for parsing.\n"
57 " -1D Comment headings in order to read in 1D format.\n"
58 " This is only useful with -full.\n"
59 " -NIML Write output in NIML 1D format. Nicer for plotting.\n"
60 " -full and -show_labels are automatically turned on with -NIML.\n"
61 " For example: \n"
62 " 3ddot -NIML anat.001.sc7z.sigset+orig\"[0,1,2,3,4]\" \\\n"
63 " > corrmat.1D\n"
64 " 1dRplot corrmat.1D \n"
65 " or\n"
66 " 1dRplot -save somecorr.jpg -i corrmat.1D\n"
67 "\n"
68 " Note: This program is not efficient when more than two subbricks are input.\n"
69 "\n" ) ;
70
71 printf("\n" MASTER_SHORTHELP_STRING ) ;
72
73 PRINT_COMPILE_DATE ;
74 return;
75 }
76
main(int argc,char * argv[])77 int main( int argc , char * argv[] )
78 {
79 double dxy , xbar,ybar,xxbar,yybar,xybar ;
80 int narg , ndset , nvox , demean=0 , mode=0 , nnn ,
81 xar_new, yar_new, nsub, iii, jjj, mxlen=0, ShowLabels = 0,
82 modelen = 0, OneD=0, iii1, jjj0, half = 1, ils = 1;
83 THD_3dim_dataset * mask_dset=NULL , *cset=NULL;
84 float mask_bot=666.0 , mask_top=-666.0, *xar=NULL, *yar=NULL;
85 byte * mmm=NULL ;
86 char *catname=NULL, form[256], val[256], *modelabel=NULL;
87
88 mainENTRY("3dLocalstat main"); machdep();
89
90 /*-- read command line arguments --*/
91
92 if( argc == 1){ usage_3ddot(1); exit(0); } /* Bob's help shortcut */
93
94 narg = 1 ;
95 while( narg < argc && argv[narg][0] == '-' ){
96 if( strcmp(argv[narg],"-help") == 0 || strcmp(argv[narg],"-h") == 0){
97 usage_3ddot(strlen(argv[narg])>3 ? 2:1);
98 exit(0);
99 }
100
101 if( strcmp(argv[narg],"-show_labels") == 0 ){
102 ShowLabels = 1 ; narg++ ; continue ;
103 }
104 if( strcmp(argv[narg],"-1D") == 0 ){
105 OneD = 1 ; narg++ ; continue ;
106 }
107 if( strcmp(argv[narg],"-NIML") == 0 ){
108 half = 0 ; ShowLabels = 1 ;
109 OneD = 2 ; narg++ ; continue ;
110 }
111 if( strcmp(argv[narg],"-full") == 0 ){
112 half = 0 ; narg++ ; continue ;
113 }
114 if( strcmp(argv[narg],"-upper") == 0 ){
115 half = 1 ; narg++ ; continue ;
116 }
117 if( strncmp(argv[narg],"-demean",5) == 0 ){
118 demean++ ; narg++ ; continue ;
119 }
120 if( strcmp(argv[narg],"-dodot") == 0 ){
121 mode = 1 ; narg++ ; continue ;
122 }
123 if( strcmp(argv[narg],"-docor") == 0 ){
124 mode = 0 ; narg++ ; continue ;
125 }
126 if( strcmp(argv[narg],"-docoef") == 0 ){
127 mode = 2 ; narg++ ; continue ;
128 }
129 if( strcmp(argv[narg],"-dosums") == 0 ){
130 mode = 3 ; narg++ ; continue ;
131 }
132 if( strcmp(argv[narg],"-doeta2") == 0 ){
133 mode = 4 ; narg++ ; continue ;
134 }
135 if( strcmp(argv[narg],"-dodice") == 0 ){
136 mode = 5 ; narg++ ; continue ;
137 }
138 if( strncmp(argv[narg],"-mask",5) == 0 ){
139 if( mask_dset != NULL ){
140 fprintf(stderr,"*** Cannot have two -mask options!\n") ; exit(1) ;
141 }
142 if( narg+1 >= argc ){
143 fprintf(stderr,"*** -mask option requires a following argument!\n");
144 exit(1) ;
145 }
146 mask_dset = THD_open_dataset( argv[++narg] ) ;
147 if( mask_dset == NULL ){
148 fprintf(stderr,"*** Cannot open mask dataset!\n") ; exit(1) ;
149 }
150 if( DSET_BRICK_TYPE(mask_dset,0) == MRI_complex ){
151 fprintf(stderr,
152 "*** Cannot deal with complex-valued mask dataset!\n");
153 exit(1) ;
154 }
155 narg++ ; continue ;
156 }
157
158 if( strncmp(argv[narg],"-mrange",5) == 0 ){
159 if( narg+2 >= argc ){
160 fprintf(stderr,
161 "*** -mrange option requires 2 following arguments!\n") ;
162 exit(1) ;
163 }
164 mask_bot = strtod( argv[++narg] , NULL ) ;
165 mask_top = strtod( argv[++narg] , NULL ) ;
166 if( mask_top < mask_top ){
167 fprintf(stderr,"*** -mrange inputs are illegal!\n") ; exit(1) ;
168 }
169 narg++ ; continue ;
170 }
171
172 fprintf(stderr,"*** Unknown option: %s\n",argv[narg]) ;
173 suggest_best_prog_option(argv[0], argv[narg]);
174 exit(1) ;
175 }
176
177 if( argc < 3 ){
178 ERROR_message("Too few options, try -help for details");
179 exit(1);
180 }
181
182 if( mode >= 2 ) demean = 1 ;
183
184 for (jjj=0, iii=narg; iii<argc; ++iii) {
185 jjj += (2+strlen(argv[iii]));
186 }
187 nsub = 0;
188 catname = (char *)calloc(jjj, sizeof(char));
189 for (iii=narg; iii<argc; ++iii) {
190 strcat(catname, argv[iii]);
191 strcat(catname, " ");
192 }
193 if (!(cset = THD_open_dataset( catname ))) {
194 fprintf(stderr,"*** Failed to read catenation of %s\n", catname); exit(1) ;
195 }
196 nsub = DSET_NVALS(cset);
197 if (nsub < 2) {
198 fprintf(stderr,"*** Need at least two sub-bricks in input!\n") ; exit(1) ;
199 }
200 nvox = DSET_NVOX(cset) ;
201
202 /* make a byte mask from mask dataset */
203
204 if( mask_dset != NULL ){
205 int mcount ;
206 if( DSET_NVOX(mask_dset) != nvox ){
207 fprintf(stderr,
208 "*** Input and mask datasets are not same dimensions!\n");
209 exit(1) ;
210 }
211 mmm = THD_makemask( mask_dset , 0 , mask_bot,mask_top ) ;
212 mcount = THD_countmask( nvox , mmm ) ;
213 fprintf(stderr,"+++ %d voxels in the mask\n",mcount) ;
214 if( mcount <= 5 ){
215 fprintf(stderr,"*** Mask is too small!\n");exit(1);
216 }
217 DSET_delete(mask_dset) ;
218 }
219
220 /* compute output string lengths */
221 switch( mode ){
222 default: modelen = 12; modelabel = "correlation"; break;
223 case 1: modelen = 12; modelabel = "dot product"; break;
224 case 2: modelen = 12*2; modelabel = "a+by"; break;
225 case 3: modelen = 12*6;
226 modelabel = "<x> <y> <(x-<x>)^2> <(y-<y>)^2> <(x-<x>)(y-<y>)> cor";
227 break;
228 case 4: modelen = 12; modelabel = "Eta2"; break;
229 }
230
231 if (ShowLabels) {
232 DSET_load(cset); CHECK_LOAD_ERROR(cset);
233 if (half) ils = 1;
234 else ils = 0;
235 mxlen = strlen(DSET_BRICK_LABEL(cset, 0));
236 if (mxlen < modelen) mxlen = modelen;
237 for (iii=ils; iii<nsub; ++iii) {
238 if (iii==ils && OneD) jjj = strlen(DSET_BRICK_LABEL(cset, iii))+1;
239 else jjj = strlen(DSET_BRICK_LABEL(cset, iii));
240 if (jjj > mxlen)
241 mxlen = jjj;
242 }
243 sprintf(form,"%%%ds%c", mxlen, '\t');
244 if (OneD == 1 || OneD == 0) {
245 if (OneD) printf("#");
246 for (iii=ils; iii<nsub; ++iii) {
247 printf(form, DSET_BRICK_LABEL(cset, iii));
248 }
249 sprintf(val,":%s:", modelabel);
250 printf(form, val);
251 printf("\n");
252 } else if (OneD == 2) {
253 printf("#<3ddot\n# ni_type = \"%d*double\"\n# ni_dime = \"%d\"\n",
254 nsub, nsub);
255 printf("# Measure = \"%s\"\n",modelabel);
256 printf("# ColumnLabels = \"");
257 for (iii=ils; iii<nsub; ++iii) {
258 if (iii > ils) printf( " ; ");
259 printf("%s", DSET_BRICK_LABEL(cset, iii));
260 }
261 printf("%s", "\"");
262 printf("\n");
263 printf("# CommandLine = \"");
264 for (iii=0; iii<argc; ++iii) {
265 printf("%s ", argv[iii]);
266 }
267 printf("\"\n# >\n");
268 }
269 } else {
270 if (nsub == 2) {
271 sprintf(form,"%%s%c",'\t');
272 } else {
273 sprintf(form,"%%%ds%c",modelen, '\t');
274 }
275 }
276 if (half) {
277 iii1 = nsub-1;
278 } else {
279 iii1 = nsub;
280 }
281 for (iii=0; iii<iii1; ++iii) {
282 xar = get_float_dset_data_pointer(cset, iii, &xar_new, 0);
283 if (half) {
284 jjj0 = iii+1;
285 for (jjj=0; jjj<iii; ++jjj) printf(form," ");
286 } else {
287 jjj0 = 0;
288 }
289 for (jjj=jjj0; jjj<nsub; ++jjj) {
290 yar = get_float_dset_data_pointer(cset, jjj, &yar_new, 0);
291 /* mode 4 is special: eta^2 16 Jun 2011 [rickr] */
292 if ( mode == 4 ) dxy = FPTR_eta2( xar , yar , nvox, mmm , &nnn ) ;
293 else if ( mode == 5 ) dxy = FPTR_dice( xar , yar , nvox, mmm , &nnn ) ;
294 else dxy = FPTR_cor ( xar , yar , nvox, mmm , demean,
295 &xbar,&ybar,&xxbar,&yybar,&xybar, &nnn );
296
297 if( nnn == 0 ) ERROR_exit("Can't compute for some reason!") ;
298 if( nnn == 1 ) fprintf(stderr,"** only 1 masked voxel?\n") ;
299 switch( mode ){
300 default: snprintf(val, 255, "%g",dxy) ; break ;
301
302 case 1: snprintf(val, 255, "%g",xybar*nnn) ; break ;
303
304 case 2:{
305 double a,b ;
306 b = xybar / xxbar ;
307 a = ybar - b*xbar ;
308 snprintf(val, 255, "%g %g",a,b) ;
309 }
310 break ;
311
312 case 3:
313 /* the old version had no break for case 3 so it printed also dxy
314 from case 4. I will mimic this behavior here too to keep things
315 consistent. Help will be updated to match */
316 snprintf(val, 255,"%g %g %g %g %g %g",xbar,ybar,xxbar,yybar,xybar,dxy);
317 break ;
318 }
319 if (OneD == 0 || OneD == 1) {
320 printf(form,val);
321 } else {
322 printf("%s ", val);
323 }
324 if (yar_new) free(yar); yar=NULL;
325 }/* jjj */
326
327 if (ShowLabels) {
328 if (OneD == 1) {
329 printf("#");
330 } else if (OneD == 0) {
331 printf(form, DSET_BRICK_LABEL(cset, iii));
332 }
333 }
334 printf("\n");
335 if (xar_new) free(xar); xar=NULL;
336 }/* iii */
337 if (OneD == 2) {
338 printf("# </matrix>\n");
339 }
340 exit(0) ;
341 }
342
343 /*------------------------------------------------------------------*/
344
345 #undef ASSIF
346 #define ASSIF(p,v) do{ if( (p)!=NULL ) *(p)=(v) ; } while(0)
347
DSET_eta2(THD_3dim_dataset * xset,THD_3dim_dataset * yset,byte * mmm,int * npt)348 double DSET_eta2( THD_3dim_dataset *xset, THD_3dim_dataset *yset,
349 byte *mmm , int *npt )
350 {
351 double e2 ;
352 float *fxar , *fyar;
353 int fxar_new, fyar_new, nxyz;
354
355 nxyz = DSET_NVOX(xset) ;
356
357 /* load bricks */
358
359 fxar = get_float_dset_data_pointer(xset, 0, &fxar_new, 1);
360 fyar = get_float_dset_data_pointer(yset, 0, &fyar_new, 1);
361 if ( ! fxar || ! fyar ) ERROR_exit("Cannot get float pointers!") ;
362
363 e2 = FPTR_eta2(fxar, fyar, nxyz, mmm, npt);
364
365 if( fxar_new ) free(fxar) ;
366 if( fyar_new ) free(fyar) ;
367
368 return(e2) ;
369 }
370
FPTR_eta2(float * fxar,float * fyar,int nxyz,byte * mmm,int * npt)371 double FPTR_eta2( float *fxar, float *fyar, int nxyz, byte *mmm , int *npt )
372 {
373 double e2 ;
374 int ii , nnn ;
375
376 ASSIF(npt,0) ;
377
378 if ( ! fxar || ! fyar ) ERROR_exit("Cannot get float pointers!") ;
379
380 if( npt ) { /* then count applied voxels (masked or all) */
381 if( mmm ) {
382 for( nnn=ii=0 ; ii < nxyz ; ii++ ) if( mmm[ii] ) nnn++;
383 } else nnn = nxyz ;
384 ASSIF(npt, nnn) ;
385 }
386
387 /* actual work: get eta^2 */
388 e2 = THD_eta_squared_masked(nxyz, fxar, fyar, mmm);
389
390 return e2 ;
391 }
392
393 /* dice: one word diff from FPTR_eta2 28 Oct, 2015 [rickr] */
FPTR_dice(float * fxar,float * fyar,int nxyz,byte * mmm,int * npt)394 double FPTR_dice( float *fxar, float *fyar, int nxyz, byte *mmm , int *npt )
395 {
396 double e2 ;
397 int ii , nnn ;
398
399 ASSIF(npt,0) ;
400
401 if ( ! fxar || ! fyar ) ERROR_exit("Cannot get float pointers!") ;
402
403 if( npt ) { /* then count applied voxels (masked or all) */
404 if( mmm ) {
405 for( nnn=ii=0 ; ii < nxyz ; ii++ ) if( mmm[ii] ) nnn++;
406 } else nnn = nxyz ;
407 ASSIF(npt, nnn) ;
408 }
409
410 /* actual work: get eta^2 */
411 e2 = THD_dice_coef_f_masked(nxyz, fxar, fyar, mmm);
412
413 return e2 ;
414 }
415
DSET_cor(THD_3dim_dataset * xset,THD_3dim_dataset * yset,byte * mmm,int dm,double * xbar,double * ybar,double * xxbar,double * yybar,double * xybar,int * npt)416 double DSET_cor( THD_3dim_dataset *xset,
417 THD_3dim_dataset *yset, byte *mmm , int dm,
418 double *xbar, double *ybar,
419 double *xxbar, double *yybar, double *xybar , int *npt )
420 {
421 double dxy ;
422 float *fxar , *fyar ;
423 int nxyz , fxar_new, fyar_new ;
424
425 nxyz = DSET_NVOX(xset) ;
426
427 /* load bricks */
428
429 fxar = get_float_dset_data_pointer(xset, 0, &fxar_new, 1);
430 fyar = get_float_dset_data_pointer(yset, 0, &fyar_new, 1);
431 if ( ! fxar || ! fyar ) ERROR_exit("Cannot get float pointers!") ;
432
433 dxy = FPTR_cor(fxar, fyar, nxyz, mmm, dm,
434 xbar, ybar, xxbar, yybar, xybar, npt);
435
436 /* toss trash */
437
438 if( fxar_new ) free(fxar) ;
439 if( fyar_new ) free(fyar) ;
440
441 return(dxy);
442 }
443
FPTR_cor(float * fxar,float * fyar,int nxyz,byte * mmm,int dm,double * xbar,double * ybar,double * xxbar,double * yybar,double * xybar,int * npt)444 double FPTR_cor( float *fxar,
445 float *fyar, int nxyz, byte *mmm , int dm,
446 double *xbar, double *ybar,
447 double *xxbar, double *yybar, double *xybar , int *npt )
448 {
449 double sumxx , sumyy , sumxy , tx,ty , dxy ;
450 double meanx=0.0 , meany=0.0 ;
451 void *xar , *yar ;
452 int ii , ivx,ivy , itypx,itypy , nnn ;
453
454
455 ASSIF(npt,0) ; ASSIF(xbar,0.0) ; ASSIF(ybar,0.0) ;
456 ASSIF(xxbar,0.0) ; ASSIF(yybar,0.0) ; ASSIF(xybar,0.0) ;
457
458 if ( ! fxar || ! fyar ) ERROR_exit("Cannot get float pointers!") ;
459
460 /* 29 Feb 2000: remove mean? */
461
462 sumxx = sumyy = 0.0 ;
463 for( nnn=ii=0 ; ii < nxyz ; ii++ ){
464 if( mmm == NULL || mmm[ii] ){sumxx += fxar[ii]; sumyy += fyar[ii]; nnn++;}
465 }
466 if( nnn < 5 ) return 0.0 ; /* ERROR */
467 sumxx /= nnn ; sumyy /= nnn ;
468 ASSIF(xbar,sumxx) ; ASSIF(ybar,sumyy) ; ASSIF(npt,nnn) ;
469 meanx = sumxx ; meany = sumyy ; /* save for later */
470
471 /* modifying dset data causes a segmentation fault based on permission
472 (seen on Fedora Core 5 and FreeBSD 7.2):
473 Process terminating with default action of signal 11 (SIGSEGV)
474 Bad permissions for mapped region at address 0x5055B74
475 at 0x407642: DSET_cor (3ddot.c:238)
476 by 0x4068D9: main (3ddot.c:146)
477
478 --> leave the data as is; apply means upon read 16 Sep 2009 [rickr] */
479 #if 0
480 if( dm ){
481 for( ii=0 ; ii < nxyz ; ii++ ){
482 if( mmm == NULL || mmm[ii] ){ fxar[ii] -= sumxx; fyar[ii] -= sumyy; }
483 }
484 }
485 #endif
486
487 /* compute sums */
488
489 sumxx = sumyy = sumxy = 0.0 ;
490 for( ii=0 ; ii < nxyz ; ii++ ){
491 if( mmm == NULL || mmm[ii] ){
492 if( dm ) { tx = fxar[ii]-meanx ; ty = fyar[ii]-meany ; }
493 else { tx = fxar[ii] ; ty = fyar[ii] ; }
494 sumxx += tx * tx ; sumyy += ty * ty ; sumxy += tx * ty ;
495 }
496 }
497 sumxx /= nnn ; ASSIF(xxbar,sumxx) ;
498 sumyy /= nnn ; ASSIF(yybar,sumyy) ;
499 sumxy /= nnn ; ASSIF(xybar,sumxy) ;
500
501 /* compute result */
502
503 dxy = sumxx * sumyy ; if( dxy <= 0.0 ) return 0.0 ;
504
505 dxy = sumxy / sqrt(dxy) ; return dxy ;
506 }
507
508 /* return the data pointer (if float) or a pointer to converted data,
509 * with a flag to specify whether conversion to float happened
510 * - index is the sub-brick to read 16 Jun 2011 [rickr]
511 */
get_float_dset_data_pointer(THD_3dim_dataset * dset,int index,int * dnew,int purge)512 float * get_float_dset_data_pointer( THD_3dim_dataset * dset,
513 int index, int * dnew, int purge )
514 {
515 int nxyz, dtype ;
516 void * dar;
517 float * fdar;
518
519 nxyz = DSET_NVOX(dset) ;
520
521 DSET_load(dset); CHECK_LOAD_ERROR(dset);
522 dtype = DSET_BRICK_TYPE(dset,index) ;
523 dar = DSET_ARRAY(dset,index) ; if( dar == NULL ) return NULL ;
524 if( dtype == MRI_float ){
525 fdar = (float *) dar ; ASSIF(dnew, 0) ;
526 } else {
527 fdar = (float *) malloc( sizeof(float) * nxyz ) ; ASSIF(dnew, 1) ;
528 EDIT_coerce_type( nxyz , dtype,dar , MRI_float,fdar ) ;
529 if (purge) PURGE_DSET( dset ) ;
530 }
531
532 return fdar;
533 }
534
535