1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #include "../mrilib.h"
6 #include "cluster_floatNOMASK.h"
7 #include "thd_segtools_fNM.h"
8 
9 
10 
display_help(int detail)11 static void display_help(int detail)
12 { printf ("3d+t Clustering segmentation, command-line version.\n");
13   printf ("    Based on The C clustering library.\n");
14   printf ("    Copyright (C) 2002 Michiel Jan Laurens de Hoon.\n");
15   printf ("USAGE: 3dkmeans [options]\n");
16   printf ("options:\n");
17   printf ("  -v, --version Version information\n");
18   printf ("  -f filename: Input data to be clustered.   \n");
19   printf ("                You can specify multiple filenames in sequence\n"
20           "                and they will be catenated internally.\n"
21           "         e.g: -f F1+orig F2+orig F3+orig ...\n"
22           "           or -f F1+orig -f F2+orig -f F3+orig ...\n" );
23   printf ("  -input filename: Same as -f\n");
24   printf(
25  " -mask mset   Means to use the dataset 'mset' as a mask:\n"
26  "                 Only voxels with nonzero values in 'mset'\n"
27  "                 will be printed from 'dataset'.  Note\n"
28  "                 that the mask dataset and the input dataset\n"
29  "                 must have the same number of voxels.\n"
30  " -mrange a b  Means to further restrict the voxels from\n"
31  "                 'mset' so that only those mask values\n"
32  "                 between 'a' and 'b' (inclusive) will\n"
33  "                 be used.  If this option is not given,\n"
34  "                 all nonzero values from 'mset' are used.\n"
35  "                 Note that if a voxel is zero in 'mset', then\n"
36  "                 it won't be included, even if a < 0 < b.\n"
37  " -cmask 'opts' Means to execute the options enclosed in single\n"
38  "                  quotes as a 3dcalc-like program, and produce\n"
39  "                  produce a mask from the resulting 3D brick.\n"
40  "       Examples:\n"
41  "        -cmask '-a fred+orig[7] -b zork+orig[3] -expr step(a-b)'\n"
42  "                  produces a mask that is nonzero only where\n"
43  "                  the 7th sub-brick of fred+orig is larger than\n"
44  "                  the 3rd sub-brick of zork+orig.\n"
45  "        -cmask '-a fred+orig -expr 1-bool(k-7)'\n"
46  "                  produces a mask that is nonzero only in the\n"
47  "                  7th slice (k=7); combined with -mask, you\n"
48  "                  could use this to extract just selected voxels\n"
49  "                  from particular slice(s).\n"
50  "       Notes: * You can use both -mask and -cmask in the same\n"
51  "                  run - in this case, only voxels present in\n"
52  "                  both masks will be dumped.\n"
53  "              * Only single sub-brick calculations can be\n"
54  "                  used in the 3dcalc-like calculations -\n"
55  "                  if you input a multi-brick dataset here,\n"
56  "                  without using a sub-brick index, then only\n"
57  "                  its 0th sub-brick will be used.\n"
58  "              * Do not use quotes inside the 'opts' string!\n"
59  "\n");
60 #if 0
61    /* Not implemented */
62   printf ("  -cg a|m       Specifies whether to center each row\n"
63           "                in the data\n"
64           "                a: Subtract the mean of each row\n"
65           "                m: Subtract the median of each row\n"
66           "                (default is no centering)\n");
67   printf ("  -ng           Specifies to normalize each row in the data\n"
68           "                (default is no normalization)\n");
69   printf ("  -ca a|m       Specifies whether to center each column \n"
70           "                in the data\n"
71           "                a: Subtract the mean of each column\n"
72           "                m: Subtract the median of each column\n"
73           "                (default is no centering)\n");
74   printf ("  -na           Specifies to normalize each column in the data\n"
75           "                (default is no normalization)\n");
76 #endif
77   printf ("  -u jobname    Allows you to specify a different name for the \n"
78           "                output files.\n"
79           "                (default is derived from the input file name)\n");
80   printf ("  -prefix PREFIX Allows you to specify a prefix for the output \n"
81           "                 volumes. Default is the same as jobname\n"
82           "                 There are two output volumes, one for the cluster\n"
83           "                 membership and one with distance measures.\n"
84           "                 The distance dataset, mostly for debugging puposes\n"
85           "                 is formatted as follows:\n"
86           "                 Sub-brick 0: Dc = 100*(1-Ci)+100*Di/(Dmax)\n"
87           "                 with Ci the cluster number for voxel i, Di the \n"
88           "                 distance of voxel i to the centroid of its \n"
89           "                 assigned cluster, Dmax is the maximum distance in\n"
90           "                 cluster Ci.\n"
91           "                 Sub-bricks 1..k: Dc0k contains the distance of a\n"
92           "                 voxel's data to the centroid of cluster k.\n"
93           "                 Sub-brick k+1: Dc_norm = (1.0-Di/Ei)*100.0, where \n"
94           "                 Ei is the smallest distance of voxel i to \n"
95           "                 the remaining clusters that is larger than Di.\n");
96   printf ("  -g [0..8]     Specifies distance measure for clustering\n" );
97   printf ("                Note: Weight is a vector as long as the signatures\n"
98           "                and used when computing distances. However for the\n"
99           "                moment, all weights are set to 1\n"
100           "                0: No clustering\n"
101           "                1: Uncentered correlation distance\n"
102           "                    Same as Pearson distance, except\n"
103           "                    the means of v and s are not removed\n"
104           "                    when computing correlation.\n"
105           "                2: Pearson distance\n"
106           "                    = (1-Weighted_Pearson_Correlation(v,s))\n"
107           "                3: Uncentered correlation distance, absolute value\n"
108           "                    Same as abs(Pearson distance), except\n"
109           "                    the means of v and s are not removed\n"
110           "                    when computing correlation.\n"
111           "                4: Pearson distance, absolute value\n"
112           "                    = (1-abs(Weighted_Pearson_Correlation(v,s)))\n"
113           "                5: Spearman's rank distance\n"
114           "                    = (1-Spearman_Rank_Correlation(v,s))\n"
115           "                   No weighting is used\n"
116           "                6: Kendall's distance\n"
117           "                    = (1-Kendall_Tau(v,s))\n"
118           "                   No weighting is used\n"
119           "                7: Euclidean distance between v and s\n"
120           "                    = 1/sum(weight) * sum(weight[i]*(v[i]-s[i])^2)\n"
121           "                8: City-block distance\n"
122           "                    = 1/sum(weight) * sum(weight[i]*abs(v[i]-s[i]))\n"
123           "\n"
124           "       (default for -g is 1, 7 if input has one value per voxel)\n"
125           "\n");
126 #if 0
127   printf ("  -k number     Specifies whether to run k-means clustering\n"
128           "                instead of hierarchical clustering, and the number\n"
129           "                of clusters k to use. \n"
130           "                Default is kmeans with k = 3 clusters\n");
131 #endif
132   printf ("  -k number     Specify number of clusters\n");
133   printf ("  -remap  METH  Reassign clusters numbers based on METH:\n"
134           "                   NONE: No remapping (default)\n"
135           "                   COUNT: based on cluster size ascending\n"
136           "                  iCOUNT: COUNT, descending\n"
137           "                   MAG:  based on ascending magnitude of centroid\n"
138           "                  iMAG: MAG, descending\n");
139   printf ("  -labeltable LTFILE: Attach labeltable LTFILE to clustering\n"
140           "                      output. This labeltable will overwrite\n"
141           "                      a table that is taken from CLUST_INIT\n"
142           "                      should you use -clust_init option.\n");
143   printf ("  -clabels LAB1 LAB2 ...: Provide a label for each cluster.\n"
144           "                          Labels cannot start with '-'.\n");
145   printf ("  -clust_init CLUST_INIT: Specify a dataset to initialize \n"
146           "                          clustering. This option sets -r 0 .\n"
147           "                          If CLUST_INIT has a labeltable and \n"
148           "                          you do not specify one then CLUST_INIT's\n"
149           "                          table is used for the output\n");
150 #if 0
151   printf ("  -c number     Force the program to do hierarchical clsutering\n"
152           "                and specifies the number of clusters for tree\n"
153           "                cutting after hierarchical clustering.\n"
154           "       Options -c and -k are mutually exclusive\n");
155 #endif
156   printf ("  -r number     For k-means clustering, the number of times the\n"
157           "                k-means clustering algorithm is run\n"
158           "                (default: 0 with -clust_init, 1 otherwise)\n");
159 /*  printf ("  -m [msca]     Specifies which hierarchical clustering method to\n"
160           "                use:\n"
161           "                m: Pairwise complete-linkage\n"
162           "                s: Pairwise single-linkage\n"
163           "                c: Pairwise centroid-linkage\n"
164           "                a: Pairwise average-linkage\n"
165           "                (default: m)\n"); */
166   printf ("  -rsigs SIGS   Calculate distances from each voxel's signature\n"
167           "                to the signatures in SIGS. \n"
168           "                SIGS is a multi-column 1D file with each column\n"
169           "                being a signature.\n"
170           "                The output is a dset the same size as the input\n"
171           "                with as many sub-bricks as there are columns in \n"
172           "                SIGS.\n"
173           "                With this option, no clustering is done.\n");
174   printf ("  -verb         verbose \n");
175   printf ("  -write_dists  Output text files containing various measures.\n"
176           "                FILE.kgg.1D : Cluster assignments \n"
177           "                FILE.dis.1D : Distance between clusters\n"
178           "                FILE.cen.1D : Cluster centroids\n"
179           "                FILE.info1.1D: Within cluster sum of distances\n"
180           "                FILE.info2.1D: Maximum distance within each cluster\n"
181           "                FILE.vcd.1D: Distance from voxel to its centroid\n");
182   printf ("  -voxdbg I J K Output debugging info for voxel I J K\n");
183   printf ("  -seed SEED    Seed for the random number generator.\n"
184           "                Default is 1234567\n");
185   EXRETURN;
186 }
187 
188 
189 
190 /* ========================================================================= */
191 
main(int argc,char ** argv)192 int main(int argc, char **argv)
193 {
194    int ii=0, ncol=0, nrow=0, nl=0, nc=0;
195    int i = 1;
196    char* filename[256];
197    int l = 0;
198    char method = 'm';
199    char cg = '\0';
200    char ca = '\0';
201    int ng = 0;
202    int na = 0;
203    char *prefix = NULL;
204    char *signame=NULL;
205    THD_3dim_dataset *in_set=NULL, *clust_set=NULL, *clust_init=NULL;
206    THD_3dim_dataset *mask_dset=NULL, *dist_set=NULL;
207    byte *cmask=NULL ; int ncmask=0 ;
208    byte *mask=NULL;
209    int nmask=-1, mnx=-1, mny=-1, mnz=-1, iset=0, N_iset=0, mnxyz=-1;
210    float mask_bot=666.0 , mask_top=-666.0 ;
211    OPT_KMEANS oc;
212    float *dvec=NULL, **D=NULL;
213    int n = 0, Ncoltot=0, nc0=0, nx=0, ny=0, nz=0;
214    char *prefixvcd = NULL, *clust_init_name=NULL;
215 
216 
217    mainENTRY("3dkmeans"); machdep();/* Used to be called 3dAclustering_fNM */
218    PRINT_VERSION("3dkmeans"); AUTHOR("avovk") ;
219 
220    oc = new_kmeans_oc();
221 
222    oc.r = -1;
223    oc.k = 0;
224    oc.kh = 0;
225    oc.jobname = NULL;
226    oc.distmetric = '*';
227    oc.verb = 0;
228    oc.rand_seed = 1234567;
229    oc.remap = NONE;
230    oc.user_labeltable=NULL;
231    oc.nclabels=0;
232 
233    for (i=0; i<4; ++i) oc.voxdebug[i] = -1;
234    N_iset = 0;
235    filename[N_iset] = NULL;
236 
237 
238    i = 1;
239    while (i < argc)
240    { const char* const argument = argv[i];
241     i++;
242     if (strlen(argument)<2)
243     { printf("ERROR: missing argument\n");
244       RETURN(1);
245     }
246     if (argument[0]!='-')
247     { printf("ERROR: unknown argument %s\n", argument);
248       RETURN(1);
249     }
250     if(!strcmp(argument,"--version") || !strcmp(argument,"-v"))
251     { clusterlib_display_version();
252       RETURN(0);
253     }
254     if(     !strcmp(argument,"--help")
255          || !strcmp(argument,"-h")
256          || !strcmp(argument,"-help") )
257     { display_help(strlen(argument) > 3 ? 2:1);
258       RETURN(0);
259     }
260     if(     !strcmp(argument,"--verb")
261          || !strcmp(argument,"-verb") )
262     { oc.verb +=1;
263       continue;
264     }
265     if(     !strcmp(argument,"--write_dists")
266          || !strcmp(argument,"-write_dists") )
267     { oc.writedists=1;
268       continue;
269     }
270     if(     !strcmp(argument,"--remap")
271          || !strcmp(argument,"-remap") )
272     {
273       if (i<argc) {
274        if (!strcmp("COUNT",argv[i])) {
275          oc.remap = COUNT;
276        } else if (!strcmp("iCOUNT",argv[i])) {
277          oc.remap = iCOUNT;
278        } else if (!strcmp("MAG",argv[i])) {
279          oc.remap = MAG;
280        } else if (!strcmp("iMAG",argv[i])) {
281          oc.remap = iMAG;
282        } else if (!strcmp("NONE",argv[i])) {
283          oc.remap = NONE;
284        } else { printf ("Error reading command line argument for -remap\n");
285         RETURN(1);
286        }
287       } else {
288          printf ("Need parameter after -remap\n");
289          RETURN(1);
290       }
291       i++;
292       continue;
293     }
294     if(!strcmp(argument,"-cg"))
295     { if (i==argc || strlen(argv[i])>1 || !strchr("am",argv[i][0]))
296       { printf ("Error reading command line argument cg\n");
297         RETURN(1);
298       }
299       cg = argv[i][0];
300       i++;
301       continue;
302     }
303     if(!strcmp(argument,"-ca"))
304     { if (i==argc || strlen(argv[i])>1 || !strchr("am",argv[i][0]))
305       { printf ("Error reading command line argument ca\n");
306         RETURN(1);
307       }
308       ca = argv[i][0];
309       i++;
310       continue;
311     }
312     if(!strcmp(argument,"-labeltable"))
313     { if (i==argc)
314       { printf ("Error: Need filename after -labeltable\n");
315         RETURN(1);
316       }
317       oc.user_labeltable = argv[i];
318       i++;
319       continue;
320     }
321     if(!strcmp(argument,"-prefix"))
322     { if (i==argc)
323       { printf ("Error: Need name after -prefix\n");
324         RETURN(1);
325       }
326       prefix = strdup(argv[i]);
327       i++;
328       continue;
329     }
330     if(!strcmp(argument,"-clust_init"))
331     { if (i==argc)
332       { printf ("Error: Need dset after -clust_init\n");
333         RETURN(1);
334       }
335       clust_init_name = argv[i];
336       i++;
337       continue;
338     }
339     if(!strcmp(argument,"-voxdbg"))
340     { if (i+2==argc)
341       { printf ("Error: Need 3 integers after -voxedbg\n");
342         RETURN(1);
343       }
344       oc.voxdebug[0] = atoi(argv[i]);i++;
345       oc.voxdebug[1] = atoi(argv[i]);i++;
346       oc.voxdebug[2] = atoi(argv[i]);i++;
347       continue;
348     }
349 
350     if(!strcmp(argument,"-rsigs"))
351     { if (i==argc)
352       { printf ("Error: Need name after -rsigs\n");
353         RETURN(1);
354       }
355       signame = argv[i];
356       i++;
357       continue;
358     }
359 
360     if(!strcmp(argument,"-seed"))
361     { if (i==argc)
362       { printf ("Error: Need a +ve integer after -seed\n");
363         RETURN(1);
364       }
365       oc.rand_seed = atoi(argv[i]);
366       if (oc.rand_seed <=0) {
367         printf ("Error: seed must be > 0\n");
368         RETURN(1);
369       }
370       i++;
371       continue;
372     }
373 
374     if( strncmp(argument,"-mask",5) == 0 ){
375        if( mask_dset != NULL )
376          ERROR_exit("Cannot have two -mask options!\n") ;
377        if( i >= argc )
378          ERROR_exit("-mask option requires a following argument!\n");
379        mask_dset = THD_open_dataset( argv[i] ) ;
380        if( mask_dset == NULL )
381          ERROR_exit("Cannot open mask dataset!\n") ;
382        if( DSET_BRICK_TYPE(mask_dset,0) == MRI_complex )
383          ERROR_exit("Cannot deal with complex-valued mask dataset!\n");
384        i++ ; continue ;
385     }
386 
387     if( strncmp(argument,"-mrange",5) == 0 ){
388       if( i+1 >= argc )
389         ERROR_exit("-mrange option requires 2 following arguments!\n");
390        mask_bot = strtod( argv[  i] , NULL ) ;
391        mask_top = strtod( argv[++i] , NULL ) ;
392        if( mask_top < mask_top )
393          ERROR_exit("-mrange inputs are illegal!\n") ;
394        i++ ; continue ;
395     }
396 
397     if( strcmp(argument,"-cmask") == 0 ){  /* 16 Mar 2000 */
398        if( i >= argc )
399           ERROR_exit("-cmask option requires a following argument!\n");
400        cmask = EDT_calcmask( argv[i] , &ncmask, 0 ) ;
401        if( cmask == NULL ) ERROR_exit("Can't compute -cmask!\n");
402        i++ ; continue ;
403     }
404 
405     if(!strcmp(argument,"-ng"))
406     { ng = 1;
407       continue;
408     }
409     if(!strcmp(argument,"-na"))
410     { na = 1;
411       continue;
412     }
413 
414     if(!strcmp(argument,"-f") || !strcmp(argument,"-input")) {
415          if (i==argc)
416         { printf ("Error reading command line argument -f (or -input): "
417                   "no file name specified\n");
418           RETURN(1);
419         }
420         do {
421          filename[N_iset] = argv[i];
422          if (N_iset > 100) {
423             printf ("Error: Too many input files!\n");
424             RETURN(1);
425          }
426          ++N_iset; filename[N_iset] = NULL;
427          i++;
428         } while (i< argc && argv[i][0] != '-');
429         continue;
430     }
431     if(!strcmp(argument,"-clabels")) {
432          if (i==argc)
433         { printf ("Error reading command line argument -clabels: "
434                   "no labels specified\n");
435           RETURN(1);
436         }
437         do {
438          oc.clabels[oc.nclabels] = argv[i];
439          if (oc.nclabels > 400) {
440             printf ("Error: Too many labels!\n");
441             RETURN(1);
442          }
443          ++oc.nclabels;
444          i++;
445         } while (i< argc && argv[i][0] != '-');
446         continue;
447     }
448 
449     switch (argument[1])
450     { case 'l': l=1; break;
451       case 'u':
452       { if (i==argc)
453         { printf ("Error reading command line argument u: "
454                   "no job name specified\n");
455           RETURN(1);
456         }
457         oc.jobname = clusterlib_setjobname(argv[i],0);
458         i++;
459         break;
460       }
461       #if 0
462       case 'f':
463       { if (i==argc)
464         { printf ("Error reading command line argument f: "
465                   "no file name specified\n");
466           RETURN(1);
467         }
468         do {
469          filename[N_iset] = argv[i];
470          if (N_iset > 100) {
471             printf ("Error: Too many input files!\n");
472             RETURN(1);
473          }
474          ++N_iset; filename[N_iset] = NULL;
475          i++;
476         } while (i< argc && argv[i][0] != '-');
477         break;
478       }
479       #endif
480       case 'g':
481       { int g;
482         if (i==argc)
483         { printf ("Error reading command line argument g: parameter missing\n");
484           RETURN(1);
485         }
486         g = clusterlib_readnumber(argv[i]);
487         if (g < 0 || g > 9)
488         { printf ("Error reading command line argument g: "
489                   "should be between 0 and 9 inclusive\n");
490           RETURN(1);
491         }
492         i++;
493         oc.distmetric = clusterlib_getmetric(g);
494         break;
495       }
496 
497       case 'k':
498       { if (i==argc)
499         { printf ("Error reading command line argument k: "
500                   "parameter missing\n");
501           RETURN(1);
502         }
503         if (oc.kh > 0) {
504             ERROR_message("-k and -c options are mutually exclusive\n");
505             RETURN(1);
506         }
507         oc.k = clusterlib_readnumber(argv[i]);
508         if (oc.k < 1)
509         { printf ("Error reading command line argument k: "
510                   "a positive integer is required\n");
511           RETURN(1);
512         }
513         i++;
514         break;
515       }
516     case 'c':
517       { if (i==argc)
518         { printf ("Error reading command line argument c: parameter missing\n");
519           RETURN(1);
520         }
521         if (oc.k > 0) {
522             ERROR_message("-k and -c options are mutually exclusive\n");
523             RETURN(1);
524         }
525         oc.kh = clusterlib_readnumber(argv[i]);
526         if (oc.kh < 1)
527         { printf ("Error reading command line argument c: "
528                   "a positive integer is required\n");
529           RETURN(1);
530         }
531         i++;
532         break;
533       }
534    case 'r':
535       { if (i==argc)
536         { printf ("Error reading command line argument r: parameter missing\n");
537           RETURN(1);
538         }
539         oc.r = clusterlib_readnumber(argv[i]);
540         if (oc.r < 0)
541         { printf ("Error reading command line argument r: "
542                   "a >= 0 integer is required\n");
543           RETURN(1);
544         }
545         i++;
546         break;
547       }
548    case 'm':
549       { if (i==argc || strlen(argv[i])>1 || !strchr("msca",argv[i][0]))
550 	  { printf ("Error reading command line argument m: "
551                "should be 'm', 's', 'c', or 'a'\n");
552 	    RETURN(1);
553 	  }
554         method = argv[i][0];
555         i++;
556         break;
557       }
558       default:
559          printf ("Unknown option %s\n", argv[i-1]);
560          suggest_best_prog_option(argv[0], argv[i-1]);
561          RETURN(1);
562     }
563 
564    }
565    if (argc < 2) {
566       printf ("Too few options!");
567       display_help(0);
568       RETURN(1);
569    }
570 
571    if (oc.k <= 0 && oc.kh <= 0) oc.k = 3;
572 
573    if (clust_init_name) {
574       if (oc.r > 0) {
575          ERROR_message("Can't use -clust_init and -r > 0");
576          RETURN(1);
577       } else {
578          oc.r = 0;
579       }
580    }
581 
582    if (oc.r == -1) oc.r = 1;
583 
584 
585 
586    if(oc.jobname == NULL) oc.jobname = clusterlib_setjobname(filename[0],1);
587 
588    if (oc.nclabels && oc.nclabels != oc.k && oc.nclabels != oc.kh) {
589       ERROR_message("Have %d labels, but %d clusters\n",
590                oc.nclabels, oc.k > 0 ? oc.k : oc.kh);
591       RETURN(1);
592    }
593 
594    /* load dsets and prepare array data for sending to clustering functions */
595 
596    if (!prefix) {
597       prefix = (char *)calloc(strlen(oc.jobname)+15,sizeof(char));
598       sprintf(prefix,"%s.k%d",oc.jobname, oc.k);
599       THD_force_ok_overwrite(1) ;   /* don't worry about overwriting */
600    }
601 
602    /* ------------- Mask business -----------------*/
603    if( mask_dset == NULL ){
604       mask = NULL ;
605       if( oc.verb )
606          INFO_message("Using all voxels in the entire dataset (no mask)\n") ;
607    } else {
608       mnx = DSET_NX(mask_dset);
609       mny = DSET_NY(mask_dset);
610       mnz = DSET_NZ(mask_dset);
611       mnxyz = mnx*mny*mnz;
612       mask = THD_makemask( mask_dset , 0 , mask_bot, mask_top ) ;
613       if( mask == NULL ) ERROR_exit("Can't make mask") ;
614       nmask = THD_countmask( mnx*mny*mnz  , mask ) ;
615       if( oc.verb )
616          INFO_message("%d voxels in the [%dx%dx%d] mask",nmask, mnx, mny, mnz) ;
617       if( nmask <= 0 ) ERROR_exit("No voxels in the mask!\n") ;
618       DSET_delete(mask_dset) ;
619    }
620 
621    if( cmask != NULL ){
622       if( mask != NULL ){
623          if (mnxyz != ncmask) ERROR_exit("Mask and cmask dimension mismatch") ;
624          for( ii=0 ; ii < mnxyz ; ii++ )
625             mask[ii] = (mask[ii] && cmask[ii]) ;
626          free(cmask) ;
627          nmask = THD_countmask( mnxyz , mask ) ;
628          if( nmask <= 0 ) ERROR_exit("No voxels in the mask+cmask!\n") ;
629          if( oc.verb ) INFO_message("%d voxels in the mask+cmask\n",nmask) ;
630       } else {
631          mnx = -1; mny = -1; mnz = -1; /* unknown */
632          mnxyz = ncmask;
633          mask = cmask ;
634          nmask = THD_countmask( mnxyz , mask ) ;
635          if( nmask <= 0 ) ERROR_exit("No voxels in the cmask!\n") ;
636          if( oc.verb ) INFO_message("%d voxels in the cmask\n",nmask) ;
637       }
638    }
639 
640    if (signame) {
641       MRI_IMAGE *im = NULL;
642       float *far = NULL;
643 
644       /* catenate all input dsets */
645       if (N_iset == 1) {
646          in_set = THD_open_dataset(filename[0]);
647          CHECK_OPEN_ERROR(in_set,filename[0]) ;
648          if (oc.voxdebug[0] >= 0) {
649             /* setup for debugging */
650             oc.voxdebug[3] = oc.voxdebug[0] + oc.voxdebug[1]*DSET_NX(in_set) +
651                               oc.voxdebug[2]*DSET_NX(in_set)*DSET_NY(in_set);
652          } else oc.voxdebug[3] = -1;
653       } else {
654          /* you'll need to read and catenate on the fly ... */
655          ERROR_exit( "Not ready to deal with more than one input.\n"
656                      "Consdier catenating the input externally.\n"
657                      "Let me know if it becomes annoying ...\n");
658       }
659 
660       /* load the set of distance files */
661       im = mri_read_1D (signame);
662       far = MRI_FLOAT_PTR(im);
663       /* Now call distance function */
664       if (!thd_Adist ( in_set,
665                        mask,
666                        far, im->ny,
667                        &dist_set ,
668                        oc)) {
669          ERROR_exit("Failed in thd_Acluster");
670       }
671       if (im) mri_free(im); im = NULL; far = NULL;
672       /* add history to output data and write them  out */
673       if( oc.verb && dist_set)
674             ININFO_message("\nWriting datasets: %s",prefix) ;
675       if (dist_set) {
676          EDIT_dset_items(  dist_set , ADN_prefix  , prefix, ADN_none);
677          tross_Copy_History( in_set , dist_set ) ;
678          tross_Make_History( "3dkmeans" , argc, argv , dist_set ) ;
679          DSET_write(dist_set); DSET_unload(dist_set);
680          DSET_delete(dist_set); dist_set = NULL;
681       }
682    } else {
683       /* Doing clustering function */
684       Ncoltot=0;
685       /* Read in dset(s) and create D */
686       for (iset = 0; iset < N_iset; ++iset) {
687          if (oc.verb) fprintf(stderr,"Reading %s's header (%d/%d), ",
688                                     filename[iset], iset+1, N_iset);
689          in_set = THD_open_dataset(filename[iset]);
690          CHECK_OPEN_ERROR(in_set,filename[iset]) ;
691          if (oc.voxdebug[0] >= 0) {
692             /* setup for debugging */
693             oc.voxdebug[3] = oc.voxdebug[0] + oc.voxdebug[1]*DSET_NX(in_set) +
694                               oc.voxdebug[2]*DSET_NX(in_set)*DSET_NY(in_set);
695          } else oc.voxdebug[3] = -1;
696          if (iset == 0) {
697             ncol = DSET_NVALS(in_set);
698             nrow = DSET_NVOX(in_set);
699             nx = DSET_NX(in_set); ny = DSET_NY(in_set); nz = DSET_NZ(in_set);
700             if (  mask                                              &&
701                   ( ( (mnx >= 0 && mnx != DSET_NX(in_set)) ||
702                       (mny >= 0 && mny != DSET_NY(in_set)) ||
703                       (mnz >= 0 && mnz != DSET_NZ(in_set))  )    ||
704                     ( mnxyz != nx*ny*nz ) ) ) {
705                ERROR_exit("Dimension mismatch between mask (%d=%dx%dx%d)\n"
706                           "                 and input dset (%d=%dx%dx%d)",
707                           mnxyz, mnx, mny,mnz, nx*ny*nz, nx,ny,nz);
708             }
709             if (!mask) nmask = DSET_NVOX(in_set);
710          } else { /* check for consistency with previous input */
711             if (  (nx != DSET_NX(in_set) ||
712                    ny != DSET_NY(in_set) ||
713                    nz != DSET_NZ(in_set) ) ) {
714                ERROR_exit( "Dimension mismatch between input dset"
715                            " %s and preceding ones", filename[iset]);
716             }
717             ncol = DSET_NVALS(in_set);
718          }
719          if (oc.verb) fprintf(stderr," %d cols\n ",
720                                     ncol);
721          Ncoltot += ncol;
722          /* get rid of dset */
723          DSET_delete (in_set);
724       }
725 
726       if (oc.distmetric == '*') {
727          if (Ncoltot == 1) oc.distmetric = 'e';
728          else oc.distmetric = 'u';
729       }
730 
731       /* Now allocate for D */
732       D = (float **)calloc(sizeof(float*), nmask);
733       for (ii=0;ii<(nmask);++ii) {
734          if (!(D[ii] = (float *)calloc(sizeof(float), Ncoltot))) {
735             fprintf(stderr,"ERROR: Failed while allocating %dx%d float matrix\n",
736                            nmask, Ncoltot);
737             RETURN(1);
738          }
739       }
740 
741       dvec = (float * )malloc(sizeof(float)*Ncoltot) ;  /* array to hold series
742                                                          longer than needed, but
743                                                          less hassle*/
744       nc0 = 0;
745       for (iset = 0; iset < N_iset; ++iset) {
746          if (oc.verb)
747             fprintf(stderr,"Patience, rereading %s...\n", filename[iset]);
748          in_set = THD_open_dataset(filename[iset]);
749          DSET_load(in_set) ; ncol = DSET_NVALS(in_set);
750 
751          if (oc.verb) {
752             ININFO_message("Filling cols [%d..%d] of D(%dx%d) (mask=%p).\n",
753                               nc0,nc0+ncol-1, nmask, Ncoltot, mask);
754          }
755          ii = 0;
756          for (nl=0; nl<DSET_NVOX(in_set); ++nl) {
757             if (!mask || mask[nl]) {
758                THD_extract_array( nl , in_set , 0 , dvec ) ;
759                for (nc=0; nc<ncol; ++nc) D[ii][nc0+nc] = dvec[nc];
760                ++ii;
761             }
762          }
763          nc0 += ncol;
764          if (iset != N_iset-1) DSET_delete(in_set);
765          else DSET_unload(in_set);
766       }
767       free(dvec); dvec = NULL;
768 
769       /* Load initialization */
770       if (clust_init_name) {
771          if (oc.verb) fprintf(stderr,"Reading %s's header, ",
772                                     clust_init_name);
773          if (!(clust_init = THD_open_dataset(clust_init_name))) {
774             ERROR_exit("Failed to read initialization dset %s\n",
775                         clust_init_name);
776          }
777          DSET_load(clust_init) ;
778          CHECK_OPEN_ERROR(clust_init,clust_init_name) ;
779       }
780 
781       /* Now call clustering function */
782       if (!thd_Acluster ( in_set,
783                         mask, nmask,
784                         &clust_set,
785                         &dist_set ,
786                         clust_init,
787                         oc, D, Ncoltot)) {
788          ERROR_exit("Failed in thd_Acluster");
789       }
790 
791       /* freedom */
792       if (D) {
793          for (ii=0; ii<nmask; ++ii) if (D[ii]) free(D[ii]);
794          free(D); D = NULL;
795       }
796       /* avovk; make prefix for other datasets, based on input prefix */
797 
798       n = 1 + strlen(prefix) + strlen("_vcd");
799       prefixvcd = (char *)malloc(n*sizeof(char));
800       sprintf (prefixvcd, "%s_vcd", prefix);
801 
802 
803       /* add history to output data and write them  out */
804       if( oc.verb &&
805           (clust_set || dist_set))
806         ININFO_message("\nWriting dataset: %s %s",prefix, prefixvcd) ;
807       if (clust_set) {
808          EDIT_dset_items(  clust_set , ADN_prefix  , prefix, ADN_none);
809          tross_Copy_History( in_set , clust_set ) ;
810          tross_Make_History( "3dkmeans" , argc, argv , clust_set ) ;
811          DSET_write(clust_set); DSET_unload(clust_set);
812          DSET_delete(clust_set); clust_set = NULL;
813       }
814       ININFO_message("\nWriting dataset: %s", prefixvcd) ;
815       if (dist_set) {
816          EDIT_dset_items(  dist_set , ADN_prefix  , prefixvcd, ADN_none);
817          tross_Copy_History( in_set , dist_set ) ;
818          tross_Make_History( "3dkmeans" , argc, argv , dist_set ) ;
819          DSET_write(dist_set);
820          DSET_unload(dist_set);
821          DSET_delete(dist_set);
822          dist_set = NULL;
823       }
824 
825    }
826 
827    if (prefix) free(prefix); prefix=NULL;
828    if (mask) free(mask); mask = NULL;
829    if (oc.jobname) free(oc.jobname); oc.jobname = NULL;
830 
831    fprintf (stderr,"\n");
832    RETURN(0);
833 }
834 
835