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