1 #include "SUMA_suma.h"
2 #include "../avovk/thd_segtools_fNM.h"
3 #include "SUMA_SegOpts.h"
4 #include "SUMA_SegFunc.h"
5 
6 #ifdef USE_OMP
7 #include <omp.h>
8 #endif
9 
10 static int VN = 0;
11 
12 #if 0
13 static void vstep_print(void)
14 {
15    static char xx[10] = "0123456789" ;
16    fprintf(stderr , "%c" , xx[VN%10] ) ;
17    if( VN%10 == 9) fprintf(stderr,".") ;
18    VN++ ;
19 }
20 #endif
21 
22 static int debug = 0;
23 static int VoxDbg3[3];
24 static int VoxDbg = -1;
25 static FILE *VoxDbgOut=NULL;
26 
SUMA_hist_variable(SUMA_HIST * hh)27 char *SUMA_hist_variable(SUMA_HIST *hh)
28 {
29    if (!hh || !hh->label) return(NULL);
30 
31    return(SUMA_label_variable(hh->label, 'h'));
32 }
SUMA_dist_variable(SUMA_FEAT_DIST * hh)33 char *SUMA_dist_variable(SUMA_FEAT_DIST *hh)
34 {
35    if (!hh || !hh->label) return(NULL);
36 
37    return(SUMA_label_variable(hh->label, 'd'));
38 }
SUMA_hist_conditional(SUMA_HIST * hh)39 char *SUMA_hist_conditional(SUMA_HIST *hh)
40 {
41    if (!hh || !hh->label) return(NULL);
42 
43    return(SUMA_label_conditional(hh->label, 'h'));
44 }
SUMA_dist_conditional(SUMA_FEAT_DIST * hh)45 char *SUMA_dist_conditional(SUMA_FEAT_DIST *hh)
46 {
47    if (!hh || !hh->label) return(NULL);
48 
49    return(SUMA_label_conditional(hh->label, 'd'));
50 }
51 
52 /*
53    infer class from label, this is particular to the way you label these dudes
54    Do NOT free what is returned
55 */
SUMA_label_variable(char * label,char c)56 char *SUMA_label_variable(char *label, char c)
57 {
58    static char feats[10][256];
59    static int ii=0;
60    int j,k;
61    if (!label) return(NULL);
62    if (label[0]!=c || label[1]!='(') return(NULL);
63    ++ii; if (ii>9) ii = 0;
64    feats[ii][0]='\0'; feats[ii][255]='\0';
65    j=2; k=0;
66    while (label[j]!='\0' && label[j]!='|' && label[j]!=')' && k<255)
67    {
68       feats[ii][k]=label[j]; ++k; ++j;
69    }
70    feats[ii][k]='\0';
71    return(feats[ii]);
72 }
73 
74 /*
75    infer class from label, this is particular to the way you label these dudes
76    Do NOT free what is returned
77 */
SUMA_label_conditional(char * label,char c)78 char *SUMA_label_conditional(char *label, char c)
79 {
80    static char cls[10][256];
81    static int ii=0;
82    int j,k;
83    if (!label) return(NULL);
84    if (label[0]!=c || label[1]!='(') return(NULL);
85    ++ii; if (ii>9) ii = 0;
86    cls[ii][0]='\0'; cls[ii][255]='\0';
87    j=2; k=0;
88    while (label[j]!='\0' && label[j]!='|' && label[j]!=')')
89    {
90       ++j;
91    }
92    if (label[j]!='\0') {
93       ++j;
94       while (label[j]!='\0' && label[j]!=')' && k<255)
95       {
96          cls[ii][k]=label[j]; ++k; ++j;
97       }
98       cls[ii][k]='\0';
99    }
100    return(cls[ii]);
101 }
102 
103 /*
104    return histogram filename for a certain variable and conditional
105 */
SUMA_hist_fname(char * proot,char * variable,char * conditional,int withext)106 char *SUMA_hist_fname(char *proot, char *variable, char *conditional,
107                       int withext)
108 {
109    static char cls[10][256];
110    static int ii=0;
111    int j,k;
112 
113    if (!proot || !variable) return(NULL);
114    ++ii; if (ii>9) ii = 0;
115    cls[ii][0]='\0'; cls[ii][255]='\0';
116    if (conditional) {
117       snprintf(cls[ii], 255, "%s/h.%s-G-%s",
118                proot, variable, conditional);
119    } else {
120       snprintf(cls[ii], 255, "%s/h.%s",
121                proot, variable);
122    }
123    if (withext) {
124       SUMA_strncat(cls[ii],".niml.hist", 255);
125    }
126    return(cls[ii]);
127 }
128 
SUMA_corrmat_fname(char * proot,char * conditional,int withext)129 char *SUMA_corrmat_fname(char *proot, char *conditional, int withext)
130 {
131    static char cls[10][256];
132    static int ii=0;
133    int j,k;
134 
135    if (!proot || !conditional) return(NULL);
136    ++ii; if (ii>9) ii = 0;
137    cls[ii][0]='\0'; cls[ii][255]='\0';
138    snprintf(cls[ii], 255, "%s/C.%s",
139                proot, conditional);
140    if (withext) {
141       SUMA_strncat(cls[ii],".niml.cormat", 255);
142    }
143    return(cls[ii]);
144 }
145 
SUMA_find_feature_dist(SUMA_FEAT_DISTS * FDV,char * label,char * feature,char * class,int * ifind)146 SUMA_FEAT_DIST *SUMA_find_feature_dist(SUMA_FEAT_DISTS *FDV,
147                                        char *label, char *feature, char *class,
148                                        int *ifind)
149 {
150    static char FuncName[]={"SUMA_find_feature_dist"};
151    int ff=-1;
152    char sbuf[256]={""}, *skey=NULL;
153    SUMA_Boolean LocalHead = NOPE;
154 
155    SUMA_ENTRY;
156 
157    if (!FDV || (!label && !feature) || FDV->N_FD < 1) SUMA_RETURN(NULL);
158    if (ifind) *ifind = -1;
159 
160    if (label) {
161       if (feature || class) {
162          SUMA_S_Err("Can't use label with feature or class");
163          SUMA_RETURN(NULL);
164       }
165       skey = label;
166    } else {
167       if (class)
168          snprintf(sbuf, 255, "d(%s|%s)",feature, class);
169       else
170          snprintf(sbuf, 255, "d(%s)",feature);
171       skey = sbuf;
172    }
173    ff = 0;
174    while (ff < FDV->N_FD) {
175       /* SUMA_LHv("%d: skey=%s, label=%s\n", ff, skey, FDV->FD[ff]->label);*/
176       if (!strcmp(skey, FDV->FD[ff]->label)) {
177          if (ifind) *ifind = ff;
178          SUMA_RETURN(FDV->FD[ff]);
179       }
180       ++ff;
181    }
182 
183    SUMA_RETURN(NULL);
184 }
185 
SUMA_dists_featureset(SUMA_FEAT_DISTS * FDV)186 NI_str_array * SUMA_dists_featureset(SUMA_FEAT_DISTS *FDV)
187 {
188    static char FuncName[]={"SUMA_dists_featureset"};
189    NI_str_array *sar=NULL;
190    int i=0;
191 
192    SUMA_ENTRY;
193 
194    if (!FDV) SUMA_RETURN(sar);
195 
196    for (i=0; i<FDV->N_FD; ++i) {
197       sar = SUMA_NI_str_array(sar, SUMA_dist_variable(FDV->FD[i]), "A");
198    }
199 
200    SUMA_RETURN(sar);
201 }
202 
SUMA_dists_classset(SUMA_FEAT_DISTS * FDV)203 NI_str_array * SUMA_dists_classset(SUMA_FEAT_DISTS *FDV)
204 {
205    static char FuncName[]={"SUMA_dists_classset"};
206    NI_str_array *sar=NULL;
207    int i=0;
208 
209    SUMA_ENTRY;
210 
211    if (!FDV) SUMA_RETURN(sar);
212 
213    for (i=0; i<FDV->N_FD; ++i) {
214       sar = SUMA_NI_str_array(sar, SUMA_dist_conditional(FDV->FD[i]), "A");
215    }
216 
217    SUMA_RETURN(sar);
218 }
219 
SUMA_grow_feature_dists(SUMA_FEAT_DISTS * FDV)220 SUMA_FEAT_DISTS *SUMA_grow_feature_dists(SUMA_FEAT_DISTS *FDV)
221 {
222    static char FuncName[]={"SUMA_grow_feature_dists"};
223 
224    SUMA_ENTRY;
225 
226    if (!FDV) {
227       FDV = (SUMA_FEAT_DISTS *)SUMA_calloc(1, sizeof(SUMA_FEAT_DISTS));
228       FDV->N_FD = 0;
229    }
230    FDV->N_alloc += 50;
231    FDV->FD = (SUMA_FEAT_DIST **)SUMA_realloc(FDV->FD, FDV->N_alloc*
232                                                       sizeof(SUMA_FEAT_DIST*));
233 
234    SUMA_RETURN(FDV);
235 }
236 
SUMA_free_dist(SUMA_FEAT_DIST * FD)237 SUMA_FEAT_DIST *SUMA_free_dist(SUMA_FEAT_DIST *FD)
238 {
239    static char FuncName[]={"SUMA_free_dist"};
240 
241    SUMA_ENTRY;
242 
243    if (FD) {
244       if (FD->label) SUMA_free(FD->label); FD->label = NULL;
245       if (FD->hh) FD->hh = SUMA_Free_hist(FD->hh);
246       SUMA_free(FD);
247    }
248 
249    SUMA_RETURN(NULL);
250 }
251 
SUMA_free_dists(SUMA_FEAT_DISTS * FDV)252 SUMA_FEAT_DISTS *SUMA_free_dists(SUMA_FEAT_DISTS *FDV)
253 {
254    static char FuncName[]={"SUMA_free_dists"};
255    int i=0;
256 
257    SUMA_ENTRY;
258 
259    if (!FDV) SUMA_RETURN(NULL);
260    for (i=0; i<FDV->N_FD; ++i) {
261       if (FDV->FD[i]) FDV->FD[i] = SUMA_free_dist(FDV->FD[i]);
262    }
263    if (FDV->FD) SUMA_free(FDV->FD);
264 
265    SUMA_free(FDV);
266 
267    SUMA_RETURN(NULL);
268 }
269 
SUMA_add_feature_dist(SUMA_FEAT_DISTS * FDV,SUMA_FEAT_DIST ** FDp,int append)270 SUMA_FEAT_DISTS *SUMA_add_feature_dist(SUMA_FEAT_DISTS *FDV,
271                                        SUMA_FEAT_DIST **FDp,
272                                        int append)
273 {
274    static char FuncName[]={"SUMA_add_feature_dist"};
275    int ff=-1;
276    char sbuf[256]={""};
277    SUMA_FEAT_DIST *FD=NULL, *FDo=NULL;
278 
279    SUMA_ENTRY;
280 
281    if (!FDp) SUMA_RETURN(FDV);
282    if (!(FD = *FDp)) {
283       SUMA_RETURN(FDV);
284    }
285 
286    if (!FD->label) {
287       SUMA_S_Err("Failed to add FD, no label");
288       SUMA_RETURN(FDV);
289    }
290    if (!FDV || FDV->N_FD>=FDV->N_alloc-1) {
291       FDV = SUMA_grow_feature_dists(FDV);
292    }
293    if (append) { /* no replacing */
294       FDV->FD[FDV->N_FD] = FD;
295       FDV->N_FD += 1;
296    } else {
297       FDo = SUMA_find_feature_dist(FDV, FD->label, NULL, NULL, &ff);
298       if (!FDo) {
299          FDV->FD[FDV->N_FD] = FD;
300          FDV->N_FD += 1;
301       } else {
302          FDo = SUMA_free_dist(FDo);
303          FDV->FD[ff]=FD;
304       }
305    }
306    *FDp = NULL; /* to keep user from freeing by mistake */
307 
308    SUMA_RETURN(FDV);
309 }
310 
SUMA_hist_To_dist(SUMA_HIST ** hhp,char * thislabel)311 SUMA_FEAT_DIST *SUMA_hist_To_dist(SUMA_HIST **hhp, char *thislabel)
312 {
313    static char FuncName[]={"SUMA_hist_To_dist"};
314    SUMA_FEAT_DIST *FD=NULL;
315    SUMA_HIST *hh=NULL;
316    char *var=NULL, *cond=NULL;
317 
318    SUMA_ENTRY;
319 
320    if (!hhp) SUMA_RETURN(FD);
321    hh = *hhp;
322    if (!hh->label && !thislabel) {
323       SUMA_S_Err("No histogram label");
324       SUMA_RETURN(FD);
325    }
326    FD = (SUMA_FEAT_DIST *) SUMA_calloc(1,sizeof(SUMA_FEAT_DIST));
327    FD->tp = SUMA_FEAT_NP;
328    FD->hh = hh; *hhp = NULL;
329    if (thislabel) FD->label = SUMA_copy_string(thislabel);
330    else {
331       var = SUMA_hist_variable(FD->hh);
332       cond = SUMA_hist_conditional(FD->hh);
333       if (!cond || cond[0]=='\0') {
334          FD->label = SUMA_append_replace_string("d(",")",var,0);
335       } else {
336          FD->label = SUMA_append_replace_string("d(","|",var,0);
337          FD->label = SUMA_append_replace_string(FD->label,")",cond,1);
338       }
339    }
340 
341    SUMA_RETURN(FD);
342 }
343 
SUMA_TRAIN_DISTS_To_dists(SUMA_FEAT_DISTS * FDV,NI_element * ndist)344 SUMA_FEAT_DISTS *SUMA_TRAIN_DISTS_To_dists(SUMA_FEAT_DISTS *FDV,
345                                            NI_element *ndist)
346 {
347    static char FuncName[]={"SUMA_TRAIN_DISTS_To_dists"};
348    SUMA_FEAT_DIST *FD = NULL;
349    char **clsv=NULL, **featv=NULL;
350    float *shapev=NULL, *ratev=NULL;
351    int i = 0;
352    char *atr=NULL, atname[256]={""};
353    NI_str_array *atrs=NULL;
354 
355    SUMA_ENTRY;
356 
357    if (!ndist) {
358       SUMA_S_Err("NULL ndist");
359       SUMA_RETURN(FDV);
360    }
361    if (strcmp(ndist->name,"TRAIN_DISTS")) {
362       SUMA_S_Errv("nel %s is no good here sir.\n", ndist->name);
363       SUMA_RETURN(FDV);
364    }
365    if (!(atr=NI_get_attribute(ndist,"Dist")) ||
366        strcmp(atr, "gamma")) {
367       SUMA_S_Err("Dunno what to do with this element");
368       SUMA_RETURN(FDV);
369    } else { /* have gamma */
370       featv = (char **)ndist->vec[0];
371       clsv  = (char **)ndist->vec[1];
372       shapev= (float *)ndist->vec[2];
373       ratev = (float *)ndist->vec[3];
374       for (i=0; i<ndist->vec_len; ++i) {
375          sprintf(atname,"%s_Scale+Shift", featv[i]);
376 
377          if (!(atr = NI_get_attribute(ndist, atname))) {
378             SUMA_S_Errv("Failed to find attribute %s\n", atname);
379             RETURN(FDV);
380          }
381          if (!(atrs = NI_decode_string_list(atr,",")) || atrs->num != 2) {
382             SUMA_S_Errv("Failed to find shift+scale on %s\n",atname);
383             RETURN(FDV);
384          }
385          FD = (SUMA_FEAT_DIST *) SUMA_calloc(1,sizeof(SUMA_FEAT_DIST));
386          FD->label = SUMA_append_replace_string("d(","|",featv[i],0);
387          FD->label = SUMA_append_replace_string(FD->label,")",clsv[i],1);
388          FD->tp = SUMA_FEAT_GAMMA;
389          FD->scpar[0] = strtod(atrs->str[0], NULL);
390          FD->scpar[1] = strtod(atrs->str[1], NULL);
391          FD->par[0] = (double)shapev[i];
392          FD->par[1] = (double)ratev[i];
393 
394          FDV = SUMA_add_feature_dist(FDV, &FD, 0);
395       }
396    }
397 
398    SUMA_RETURN(FDV);
399 }
400 
SUMA_get_all_dists(char * where)401 SUMA_FEAT_DISTS *SUMA_get_all_dists(char *where)
402 {
403    static char FuncName[]={"SUMA_get_all_dists"};
404    int nfile ;
405    char **flist=NULL;
406    char *wilds[]={"*.niml.hist", "*.niml.td", NULL},
407          *wild=NULL, *allwild=NULL;
408    int i;
409    NI_element *nel=NULL;
410    SUMA_FEAT_DISTS *FDV=NULL;
411    SUMA_FEAT_DIST *FD=NULL;
412    SUMA_HIST *hh=NULL;
413    SUMA_Boolean LocalHead = NOPE;
414 
415    SUMA_ENTRY;
416 
417    if (!where) SUMA_RETURN(NULL);
418 
419    if (THD_is_directory(where)) {
420       i = 0;
421       while (wilds[i] != NULL) {
422          wild = SUMA_append_replace_string(where,wilds[i],"/",0);
423          allwild = SUMA_append_replace_string(allwild,wild, " ",1);
424          SUMA_free(wild); wild = NULL;
425          ++i;
426       }
427    } else {
428       allwild = SUMA_copy_string(where);
429    }
430    if (!allwild) { SUMA_S_Err("No wildness"); SUMA_RETURN(NULL); }
431 
432    MCW_wildcards( allwild , &nfile , &flist ) ;
433    if (nfile <=0) {
434       SUMA_S_Errv("No training material under %s \n%s\n", where, allwild);
435    } else {
436       for (i=0; i<nfile; ++i) {
437          if (SUMA_isExtension(flist[i],"niml.td")) {
438             SUMA_LHv("Adding TRAIN_DISTS %s\n", flist[i]);
439             nel = (NI_element*) Seg_NI_read_file(flist[i]);
440             if( !nel || strcmp(nel->name,"TRAIN_DISTS")){
441               SUMA_S_Warnv("can't open  %s, or bad type. Ignoring\n",
442                             flist[i]) ;
443             } else {
444                FDV = SUMA_TRAIN_DISTS_To_dists(FDV, nel);
445             }
446             if (nel) NI_free_element(nel); nel = NULL;
447          } else if (SUMA_isExtension(flist[i],"niml.hist")) {
448             SUMA_LHv("Adding hist %s\n", flist[i]);
449             hh = SUMA_read_hist(flist[i]);
450             FD = SUMA_hist_To_dist(&hh,NULL);
451             FDV = SUMA_add_feature_dist(FDV, &FD, 0);
452          } else {
453             SUMA_LHv("Unknow extension for %s, ignoring it.\n",
454                   flist[i]);
455          }
456       }
457    }
458    MCW_free_wildcards( nfile , flist ) ;
459    if (allwild) { SUMA_free(allwild); allwild = NULL;}
460 
461    if (LocalHead) {
462       SUMA_Show_dists(FDV, NULL, 1);
463    }
464    SUMA_RETURN(FDV);
465 }
466 
SUMA_hist_To_NIhist(SUMA_HIST * hh)467 NI_group *SUMA_hist_To_NIhist(SUMA_HIST *hh)
468 {
469    static char FuncName[]={"SUMA_hist_To_NIhist"};
470    NI_group *ngr=NULL;
471    NI_element *nel=NULL;
472    char *feat=NULL, *class=NULL;
473 
474    SUMA_ENTRY;
475 
476    if (!hh) SUMA_RETURN(ngr);
477 
478 
479    ngr = NI_new_group_element();
480    NI_rename_group(ngr, hh->label?hh->label:"MrEd");
481    #if 0 /* don't add this junk unless necessary */
482    if ((feat = SUMA_hist_variable(hh))) {
483       NI_set_attribute(ngr,"feature", feat);
484    }
485    if ((class = SUMA_hist_conditional(hh))) {
486       NI_set_attribute(ngr,"class", class);
487    }
488    #endif
489    nel = NI_new_data_element("seg_histogram", hh->K);
490    NI_add_to_group(ngr, nel);
491    NI_SET_FLOAT(nel,"window",hh->W);
492    NI_SET_FLOAT(nel,"min", hh->min);
493    NI_SET_FLOAT(nel,"max", hh->max);
494    NI_SET_INT(nel,"N_samp", hh->n);
495    NI_SET_INT(nel,"N_ignored", hh->N_ignored);
496    NI_add_column(nel, NI_FLOAT, hh->b);
497    NI_add_column(nel, NI_INT, hh->c);
498    NI_add_column(nel, NI_FLOAT, hh->cn);
499       /* forgive the redundancy, need it to facilitate plot in R
500          without much group handling */
501    NI_set_attribute(nel, "xlabel", hh->label?hh->label:"MrEd");
502    SUMA_RETURN(ngr);
503 }
504 
SUMA_NIhist_To_hist(NI_group * ngr)505 SUMA_HIST *SUMA_NIhist_To_hist(NI_group *ngr)
506 {
507    static char FuncName[]={"SUMA_NIhist_To_hist"};
508    NI_element *nel=NULL;
509    SUMA_HIST *hh=NULL;
510 
511    SUMA_ENTRY;
512 
513    if (!ngr) SUMA_RETURN(hh);
514    nel = SUMA_FindNgrNamedElement(ngr,"seg_histogram");
515    if (!nel) {
516       /* try the old name */
517       nel = SUMA_FindNgrNamedElement(ngr,"histogram");
518    }
519    if (!nel) SUMA_RETURN(hh);
520 
521    hh = (SUMA_HIST *)SUMA_calloc(1,sizeof(SUMA_HIST));
522    hh->label = SUMA_copy_string(ngr->name);
523    hh->K = nel->vec_len;
524    NI_GET_FLOAT(nel,"window",hh->W);
525    NI_GET_FLOAT(nel,"min", hh->min);
526    NI_GET_FLOAT(nel,"max", hh->max);
527    NI_GET_INT(nel,"N_samp", hh->n);
528    NI_GET_INT(nel,"N_ignored", hh->N_ignored);
529    hh->b = (float *)SUMA_calloc(hh->K,sizeof(float));
530    hh->c = (int *)SUMA_calloc(hh->K,sizeof(int));
531    hh->cn = (float *)SUMA_calloc(hh->K,sizeof(float));
532    memcpy(hh->b, nel->vec[0], sizeof(float)*hh->K);
533    memcpy(hh->c, nel->vec[1], sizeof(int)*hh->K);
534    memcpy(hh->cn, nel->vec[2], sizeof(float)*hh->K);
535 
536    SUMA_RETURN(hh);
537 }
538 
SUMA_write_hist(SUMA_HIST * hh,char * name)539 int SUMA_write_hist(SUMA_HIST *hh, char *name)
540 {
541    static char FuncName[]={"SUMA_write_hist"};
542    char *ff=NULL;
543    NI_stream m_ns = NULL;
544    NI_group *ngr = NULL;
545 
546    SUMA_ENTRY;
547 
548    if (!hh) SUMA_RETURN(NOPE);
549    if (!(ngr = SUMA_hist_To_NIhist(hh))) {
550       SUMA_S_Err("Failed to go NII");
551       SUMA_RETURN(NOPE);
552    }
553 
554    if (!name) name = ngr->name;
555 
556    ff = SUMA_Extension(name, ".niml.hist", NOPE);
557    ff = SUMA_append_replace_string("file:",ff,"",2);
558 
559    m_ns = NI_stream_open( ff , "w" ) ;
560    if( m_ns == NULL ) {
561       SUMA_S_Errv ("Failed to open stream %s\n", ff);
562       SUMA_free(ff); ff=NULL;
563       SUMA_RETURN(NOPE);
564    } else {
565       /* write out the element */
566       if (NI_write_element( m_ns , ngr ,
567                             NI_TEXT_MODE ) < 0) {
568          SUMA_S_Err ("Failed to write element");
569          SUMA_free(ff); ff=NULL;
570          NI_free_element(ngr); ngr=NULL;
571          NI_stream_close( m_ns ) ; m_ns = NULL;
572          SUMA_RETURN(NOPE);
573       }
574    }
575 
576    if (ff) SUMA_free(ff); ff = NULL;
577    /* close the stream */
578    NI_stream_close( m_ns ) ; m_ns = NULL;
579    if (ngr) NI_free_element(ngr); ngr=NULL;
580    SUMA_RETURN(YUP);
581 }
582 
SUMA_read_hist(char * name)583 SUMA_HIST *SUMA_read_hist(char *name)
584 {
585 
586    static char FuncName[]={"SUMA_read_hist"};
587    char *ff=NULL;
588    NI_stream m_ns = NULL;
589    NI_group *ngr = NULL;
590    SUMA_HIST *hh = NULL;
591 
592    SUMA_ENTRY;
593 
594    if (!name) SUMA_RETURN(hh);
595    ff = SUMA_Extension(name, ".niml.hist", NOPE);
596    ff = SUMA_append_replace_string("file:",ff,"",2);
597 
598    m_ns = NI_stream_open( ff , "r" ) ;
599    if( m_ns == NULL ) {
600       SUMA_S_Errv ("Failed to open stream %s for reading\n", ff);
601       SUMA_free(ff); ff=NULL;
602       SUMA_RETURN(hh);
603    } else {
604       /* read out the element */
605       if (!(ngr = NI_read_element( m_ns , 1 ))) {
606          SUMA_S_Err ("Failed to read element");
607          SUMA_free(ff); ff=NULL;
608          NI_stream_close( m_ns ) ; m_ns = NULL;
609          SUMA_RETURN(hh);
610       }
611    }
612    /* close the stream */
613    NI_stream_close( m_ns ) ; m_ns = NULL;
614    if (!(hh = SUMA_NIhist_To_hist(ngr))) {
615       SUMA_S_Err("Failed to get hist from NI");
616    }
617    if (ff) SUMA_free(ff); ff = NULL;
618    if (ngr) NI_free_element(ngr); ngr=NULL;
619    SUMA_RETURN(hh);
620 }
621 
622 
SUMA_set_SegFunc_debug(int dbg,int vdbg,int * vdbg3,FILE * out)623 void SUMA_set_SegFunc_debug(int dbg, int vdbg, int *vdbg3, FILE *out) {
624    debug = dbg;
625    VoxDbg = vdbg;
626    if (vdbg3) {
627       VoxDbg3[0] = vdbg3[0]; VoxDbg3[1] = vdbg3[1]; VoxDbg3[2] = vdbg3[2];
628    }
629    if (out) { VoxDbgOut=out; }
630    else {VoxDbgOut= SUMA_STDERR;}
631 }
632 
SUMA_Seg_Write_Dset(char * proot,char * prefi,THD_3dim_dataset * dset,int iter,char * hh)633 int SUMA_Seg_Write_Dset(char *proot, char *prefi, THD_3dim_dataset *dset,
634                         int iter, char *hh)
635 {
636    static char FuncName[]={"SUMA_Seg_Write_Dset"};
637    char pref[512];
638    int ovw;
639    char *opref = NULL, *oid = NULL, *ohist = NULL;
640 
641    SUMA_ENTRY;
642 
643    opref = SUMA_copy_string(DSET_PREFIX(dset));
644    oid = SUMA_copy_string(DSET_IDCODE_STR(dset));
645    ohist = tross_Get_History(dset);
646    if (proot != NULL) {
647       if (iter >=0) { snprintf(pref, 500, "%s/%s.%d",
648                                        proot, prefi, iter); }
649       else { snprintf(pref, 500, "%s/%s", proot, prefi); }
650    } else {
651       if (iter >=0) snprintf(pref, 500, "%s.%d",  prefi, iter);
652       else snprintf(pref, 500, "%s", prefi);
653    }
654 
655    if (debug) SUMA_S_Notev("Writing %s\n", pref);
656 
657    EDIT_dset_items(  dset , ADN_prefix  , pref, ADN_none);
658    UNIQ_idcode_fill(DSET_IDCODE_STR(dset));/* new id */
659    if (hh) tross_Append_History(dset, hh);/*add history*/
660 
661    DSET_quiet_overwrite(dset);
662    /* dset might get purged at write time, such as when
663       AFNI_BYTEORDER is not the same as the native order (Isaac's shenanigans)
664       So reload dset since I may still need it */
665       DSET_mallocize(dset); DSET_load(dset);
666 
667    EDIT_dset_items(  dset , ADN_prefix  , opref, ADN_none);
668    strcpy(DSET_IDCODE_STR(dset), oid);
669 
670    if (ohist) tross_Replace_History(dset, ohist);
671    SUMA_free(opref); SUMA_free(oid); free(ohist); ohist=NULL;
672 
673    SUMA_RETURN(1);
674 }
675 
SUMA_KeyofLabel_Dtable(Dtable * vl_dtable,char * label)676 int SUMA_KeyofLabel_Dtable(Dtable *vl_dtable, char *label) {
677    static char FuncName[]={"SUMA_KeyofLabel_Dtable"};
678    int kk;
679    char *str_key=NULL;
680 
681    SUMA_ENTRY;
682    if (!(str_key = findin_Dtable_b(label, vl_dtable))){
683       SUMA_S_Errv("Could not find entry in label table for class %s\n",
684                  label);
685       SUMA_RETURN(-1);
686    }
687    kk = strtol(str_key,NULL, 10);
688    SUMA_RETURN(kk);
689 }
690 
SUMA_LabelsKeys2Dtable(char ** str,int num,int * keys)691 Dtable *SUMA_LabelsKeys2Dtable (char **str, int num, int *keys)
692 {
693    static char FuncName[]={"SUMA_LabelsKeys2Dtable"};
694    char sval[256];
695    int i;
696    Dtable *vl_dtable=NULL;
697 
698    SUMA_ENTRY;
699 
700    /* make a labeltable */
701    vl_dtable = new_Dtable(5);
702    for (i=0; i<num; ++i) {
703       if (keys) sprintf(sval,"%d", keys[i]);
704       else sprintf(sval,"%d", i+1);
705       addto_Dtable( sval , str[i] , vl_dtable ) ;
706    }
707 
708    SUMA_RETURN(vl_dtable);
709 }
710 
SUMA_SetDsetLabeltable(THD_3dim_dataset * dset,char ** labels,int N_labels,int * keys)711 int SUMA_SetDsetLabeltable(THD_3dim_dataset *dset, char **labels,
712                            int N_labels, int *keys)
713 {
714    static char FuncName[]={"SUMA_SetDsetLabeltable"};
715    char *labeltable_str=NULL;
716    SUMA_ENTRY;
717 
718    labeltable_str = SUMA_LabelsKeys2labeltable_str(labels, N_labels, keys);
719    THD_set_string_atr( dset->dblk , "VALUE_LABEL_DTABLE" , labeltable_str );
720    free(labeltable_str); labeltable_str=NULL;
721 
722    SUMA_RETURN(1);
723 }
724 
SUMA_LabelsKeys2labeltable_str(char ** str,int num,int * keys)725 char *SUMA_LabelsKeys2labeltable_str(char **str, int num, int *keys)
726 {
727    static char FuncName[]={"SUMA_LabelsKeys2labeltable_str"};
728    char *labeltable_str=NULL;
729    Dtable *vl_dtable=SUMA_LabelsKeys2Dtable(str, num, keys);
730 
731    SUMA_ENTRY;
732 
733    labeltable_str = Dtable_to_nimlstring(vl_dtable,
734                                              "VALUE_LABEL_DTABLE");
735    destroy_Dtable(vl_dtable); vl_dtable=NULL;
736 
737    SUMA_RETURN(labeltable_str);
738 }
739 
SUMA_ShowClssKeys(char ** str,int num,int * keys)740 void SUMA_ShowClssKeys(char **str, int num, int *keys)
741 {
742    static char FuncName[]={"SUMA_ShowClssKeys"};
743    int i;
744 
745    SUMA_ENTRY;
746 
747    for (i=0; i<num; ++i) {
748       if (keys) fprintf(SUMA_STDERR, "  %s --> %d\n", str[i], keys[i]);
749       else fprintf(SUMA_STDERR, "  %s --> %d (assumed)\n", str[i], i+1);
750    }
751 
752    SUMA_RETURNe;
753 }
754 
755 #if 0
756 int get_train_pdist_old(SEG_OPTS *Opt, char *feat, char *cls,
757                      double *par, double *scpar)
758 {
759    char **clsv=NULL, **featv=NULL;
760    float *shapev=NULL, *ratev=NULL;
761    int i = 0;
762    char *atr=NULL, atname[256]={""};
763    NI_str_array *atrs=NULL;
764 
765    ENTRY("get_train_pdist_old");
766 
767    if (!Opt->ndist) RETURN(0);
768    featv = (char **)Opt->ndist->vec[0];
769    clsv  = (char **)Opt->ndist->vec[1];
770    shapev= (float *)Opt->ndist->vec[2];
771    ratev = (float *)Opt->ndist->vec[3];
772    scpar[0]=1.0; scpar[1]=0.0;
773    sprintf(atname,"%s_Scale+Shift", feat);
774    atr = NI_get_attribute(Opt->ndist, atname);
775    if (!atr) {
776       ERROR_message("Failed to find attribute %s", atname);
777       RETURN(0);
778    }
779    atrs = NI_decode_string_list(atr,",") ;
780    if (atrs->num == 2) {
781       scpar[0] = strtod(atrs->str[0], NULL);
782       scpar[1] = strtod(atrs->str[1], NULL);
783       NI_delete_str_array(atrs);
784    } else {
785       ERROR_message("Failed to find scale and shift in %s", atname);
786       RETURN(0);
787    }
788    for (i=0; i<Opt->ndist->vec_len; ++i) {
789       /*fprintf(stderr,"%d/%d, %s %s , %f %f\n",
790             i,Opt->ndist->vec_len, featv[i], clsv[i], shapev[i], ratev[i]);*/
791       if (!strcmp(featv[i],feat) && !strcmp(clsv[i],cls)) {
792          par[0] = (double)shapev[i]; par[1] = (double)ratev[i];
793          RETURN(2);
794       }
795    }
796 
797    RETURN(0);
798 }
799 #endif
800 
pdfgam(double x,double ash,double brt)801 double pdfgam(double x,double ash, double brt)
802 {
803    #ifdef UNS  /* unstable version */
804       double an = pow(brt,ash)/gamma(ash)*pow(x,ash-1)*exp(-brt*x);
805       return(an);
806    #else
807       double an = ash*log(brt) - lgamma(ash) + (ash-1)*log(x) - brt*x;
808       return(exp(an));
809    #endif
810 }
811 #define PDFGAM_UNS(x,ash,brt) (pow(brt,ash)/gamma(ash)*pow(x,ash-1)*exp(-brt*x))
812 #define PDFGAM(x,ash,brt) exp((ash*log(brt) - lgamma(ash) + (ash-1)*log(x) - brt*x))
813 
814 /*!
815    Estimate the probability of a particular feature's amplitude given a  class
816 */
p_a_GIV_cvfu(SEG_OPTS * Opt,char * feat,char * cls,THD_3dim_dataset * pout)817 int p_a_GIV_cvfu(SEG_OPTS *Opt, char *feat, char *cls,
818                   THD_3dim_dataset *pout)
819 {
820    static char FuncName[]={"p_a_GIV_cvfu"};
821    static int icomp_note = 0, ifound_note = 0;
822    char fpref[256+IDCODE_LEN+32]={""}, bl[256]={""};
823    char fsave[256+IDCODE_LEN+32]={""};
824    THD_3dim_dataset *pload=NULL;
825    short *a=NULL, *p=NULL;
826    int ia = 0, i=0;
827    float af=0.0;
828    double dp=0.0, da=0.0, hbw = 0.0, pfhbw, pf=0.0;
829    SUMA_FEAT_DIST *FD = NULL;
830 
831    SUMA_ENTRY;
832 
833    if (!pout || DSET_BRICK_TYPE(pout,0) != MRI_short) SUMA_RETURN(0);
834 
835    /* form the temp filename */
836    if (Opt->UseTmp) {
837       sprintf(fpref, "/tmp/%s.a_GIV_cvfu-%s-%s",
838                   Opt->uid, feat, cls);
839       sprintf(fsave, "%s+orig.HEAD", fpref);
840    }
841    if (Opt->UseTmp && (pload = THD_open_dataset( fsave ))) {
842       if (Opt->debug > 1) {
843          if (Opt->debug > 2 || !ifound_note) {
844             SUMA_S_Notev("Found %s %s\n",
845                fsave, Opt->debug <= 2 ? "(further message will be muted)":"");
846             ++ifound_note;
847          }
848       }
849       if (DSET_BRICK_TYPE(pload,0) != MRI_short) SUMA_RETURN(0);
850       DSET_mallocize(pload)   ; DSET_load(pload);
851       /* swap column and factor */
852       SWAP_COL(pload, pout,0)
853       /* erase pload and get out */
854       DSET_delete(pload);
855       SUMA_RETURN(1);
856    } else {
857       if (Opt->debug > 1) {
858          if (Opt->debug > 2 || !icomp_note) {
859             SUMA_S_Notev("Must compute %s, %s %s\n",
860               feat, cls, Opt->debug <= 2 ? "(further message will be muted)":"");
861             ++icomp_note;
862          }
863       }
864       SB_LABEL(Opt->sig,feat, ia);
865       if (ia<0) {
866          SUMA_S_Errv("Failed to find %s", feat); SUMA_RETURN(0);
867       }
868       a = (short *)DSET_ARRAY(Opt->sig, ia);
869       af = DSET_BRICK_FACTOR(Opt->sig, ia);
870       if (!af) af = 1;
871 
872       p = (short *)DSET_ARRAY(pout, 0);
873       pf = 32767.0; /* max p is 1, so stick with this */
874 
875       if (!(FD = SUMA_find_feature_dist(Opt->FDV, NULL, feat, cls, NULL))) {
876          SUMA_S_Errv("Failed to find dist struct for %s %s\n",
877                         feat, cls);
878          SUMA_RETURN(0);
879       }
880       switch(FD->tp){
881          case SUMA_FEAT_GAMMA:
882             /* compute probs */
883             af = af * FD->scpar[0];
884             hbw = Opt->binwidth / 2.0;
885             pfhbw = pf * hbw ;
886             for (i=0; i<DSET_NVOX(Opt->sig); ++i) {
887                if (IN_MASK(Opt->cmask,i)) {
888                   da = (double)((a[i]*af)+FD->scpar[1]);
889                   #if 0
890                   /* gold standard see area.gam*/
891                   dp = ( gamma_t2p( da-hbw, FD->par[0] , FD->par[1] ) -
892                          gamma_t2p( da+hbw, FD->par[0] , FD->par[1] ) ) * pf;
893                   #else
894                   dp = (PDFGAM((da-hbw), FD->par[0], FD->par[1]) +
895                         PDFGAM((da+hbw), FD->par[0], FD->par[1]) ) *  pfhbw;
896                   #endif
897                   if (i == Opt->VoxDbg) {
898                      fprintf(Opt->VoxDbgOut,"      a = %d, a_sc = %f\n"
899                                            "p(a(%s)=%f|c=%s)=%f\n",
900                                            a[i], da, feat, a[i]*af, cls, dp/pf);
901                   }
902                } else {
903                   if (i == Opt->VoxDbg) fprintf(Opt->VoxDbgOut,
904                                                 " SFG Vox Masked\n");
905                   dp = 0.0;
906                }
907                p[i] = (short)dp;
908             }
909             break;
910          case SUMA_FEAT_NP:
911             /* get prob from histogram */
912             for (i=0; i<DSET_NVOX(Opt->sig); ++i) {
913                if (IN_MASK(Opt->cmask,i)) {
914                   p[i] = (short)(SUMA_hist_freq(FD->hh, (a[i]*af))*pf);
915                   if (i == Opt->VoxDbg) {
916                      fprintf(Opt->VoxDbgOut, "h(a(%s)=%f|c=%s)=%f\n",
917                                            feat, a[i]*af, cls, (float)p[i]/pf);
918                   }
919                } else {
920                   if (i == Opt->VoxDbg) fprintf(Opt->VoxDbgOut,
921                                                 " SFNP Vox Masked\n");
922                   p[i] = 0;
923                }
924             }
925             break;
926          default:
927             SUMA_S_Errv(
928                "Don't much about dist type %d, but I do know that I love you.\n",
929                FD->tp);
930             break;
931       }
932 
933       EDIT_BRICK_FACTOR(pout,0,1.0/pf);
934       if (Opt->UseTmp) {
935          if (Opt->debug > 1) INFO_message("Writing %s", fsave);
936          UNIQ_idcode_fill(DSET_IDCODE_STR(pout));/* new id */
937          EDIT_dset_items( pout, ADN_prefix, fpref, ADN_none );
938          sprintf(bl, "p(a(%s)|c=%s)",feat, cls);
939          EDIT_BRICK_LABEL(pout,0,bl);
940          DSET_quiet_overwrite(pout) ;
941       }
942       SUMA_RETURN(1);
943    }
944 
945    SUMA_RETURN(0);
946 }
947 
948 /*!
949 Estimate the probability of a class, given a particular feature
950 */
p_cv_GIV_afu(SEG_OPTS * Opt,char * feat,char * cls,double * d)951 int p_cv_GIV_afu (SEG_OPTS *Opt, char *feat,
952                   char *cls, double *d)
953 {
954    static char FuncName[]={"p_cv_GIV_afu"};
955    static THD_3dim_dataset *pb=NULL;
956    static double *dd=NULL;
957    static long long init=0;
958    int i,j, ifeat = -1;
959    short *a=NULL;
960    float af =0.0, pf = 0.0;
961    double bb=0.0;
962 
963    SUMA_ENTRY;
964 
965    if (cls==NULL) {
966       if (!init) {/* init */
967          if (pb) { ERROR_message("Non null pb"); SUMA_RETURN(0); }
968          NEW_SHORTY(Opt->sig,1,"p_cv_GIV_afu",pb);
969          if (!pb) SUMA_RETURN(0);
970          dd = (double *)calloc(DSET_NVOX(Opt->sig), sizeof(double));
971          if (!dd) SUMA_RETURN(0);
972          init = 1;
973       } else { /* clean */
974          DSET_delete(pb); pb=NULL;
975          free(dd); dd=NULL; init=0;
976       }
977       SUMA_RETURN(1);
978    }
979 
980    if (!pb || init==0) { ERROR_message("Not initialized"); SUMA_RETURN(0); }
981    if (!d) { ERROR_message("NULL d"); SUMA_RETURN(0); }
982    SB_LABEL(Opt->sig,feat, ifeat);
983    if (ifeat < 0) {
984       SUMA_S_Errv("Failed to find feature %s\n", feat); SUMA_RETURN(0);
985    }
986    memset(d, 0, DSET_NVOX(Opt->sig)*sizeof(double));
987    for (i=0; i<Opt->clss->num;++i) {
988       if (Opt->debug > 2)
989          SUMA_S_Notev(" Calling p_a_GIV_cvfu %d/%d\n", i,Opt->clss->num);
990       if (!(p_a_GIV_cvfu(Opt, feat, Opt->clss->str[i],pb))) {
991          SUMA_S_Err("Failed in p_a_GIV_cvfu"); SUMA_RETURN(0);
992       }
993       a = (short *)DSET_ARRAY(pb,0);
994       af = DSET_BRICK_FACTOR(pb,0); if (af==0.0) af=1.0;
995       if (!strcmp(Opt->clss->str[i], cls)) {
996          for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
997             if (IN_MASK(Opt->cmask, j)) {
998                bb = Opt->mixfrac[i] * a[j];/* skip af scaling here */
999                dd[j] = bb;
1000                d[j] += bb;
1001             } else {
1002                dd[j] = 0.0;
1003             }
1004          }
1005       } else {
1006          for (j=0; j<DSET_NVOX(Opt->sig); ++j)  {
1007             if (IN_MASK(Opt->cmask, j)) {
1008                d[j] += Opt->mixfrac[i] * a[j];/* skip af scaling here */
1009             } else {
1010                /* nothing needed */
1011             }
1012          }
1013       }
1014    }
1015 
1016    for (j=0; j<DSET_NVOX(Opt->sig); ++j) {/* compute ratio, scaling not needed */
1017       if (IN_MASK(Opt->cmask, j)) {
1018          d[j] = dd[j]/d[j];
1019          if (j == Opt->VoxDbg) {
1020             fprintf(Opt->VoxDbgOut,"   p(c=%s|%s=%f)=%f\n",
1021                                   cls, feat, THD_get_voxel(Opt->sig, j, ifeat),
1022                                   d[j]);
1023          }
1024          if (isnan(d[j])) d[j] = 0.0;
1025       } else {
1026          d[j] = 0.0;
1027       }
1028    }
1029 
1030    if (Opt->debug > 2) {
1031       char ff[256];
1032       FILE *fout=NULL;
1033       sprintf(ff,"p_cv_GIV_afu.%lld.1D",init);
1034       fout = fopen(ff,"w");
1035       SUMA_S_Notev("Writing %s", ff);
1036       for (j=0; j<DSET_NVOX(Opt->sig); ++j)
1037          fprintf(fout,"%f\n",d[j]);
1038       fclose(fout);
1039    }
1040 
1041    ++init;
1042    SUMA_RETURN(1);
1043 }
1044 
1045 /*!
1046    Estimate the probability of a class, given all features
1047 */
p_cv_GIV_A(SEG_OPTS * Opt,char * cls,double * dr)1048 int p_cv_GIV_A (SEG_OPTS *Opt, char *cls, double *dr)
1049 {
1050    static char FuncName[]={"p_cv_GIV_A"};
1051    char fpref[256]={""};
1052    double pf= 32767.0; /* max p is 1, so stick with this */
1053    static double *d=NULL, ddd=0.0, wfeat;
1054    static int init=0;
1055    int i, j, icls;
1056    short *a=NULL;
1057    static THD_3dim_dataset *pcgrec=NULL;
1058    SUMA_Boolean LocalHead = NOPE;
1059 
1060    SUMA_ENTRY;
1061 
1062 
1063    if (cls==NULL) {
1064       if (!init) {/* init */
1065          SUMA_LH("Initialization");
1066          if (d) { SUMA_S_Err("Non null d"); SUMA_RETURN(0); }
1067          d = (double *)calloc(DSET_NVOX(Opt->sig), sizeof(double));
1068          if (!d) SUMA_RETURN(0);
1069          if (!p_cv_GIV_afu (Opt, NULL, NULL, d) ) SUMA_RETURN(0);
1070          if (Opt->Writepcg_G_au) {
1071             NEW_SHORTY(Opt->sig, Opt->feats->num,"p_cv_GIV_A_debug",pcgrec);
1072             if (!pcgrec) SUMA_RETURN(0);
1073          }
1074          init = 1;
1075       } else { /* clean */
1076          SUMA_LH("Cleanup");
1077          if (!p_cv_GIV_afu (Opt, NULL, NULL, d) ) SUMA_RETURN(0);
1078          if (pcgrec) DSET_delete(pcgrec); pcgrec=NULL;
1079          free(d); d=NULL;
1080          init=0;
1081       }
1082       SUMA_RETURN(1);
1083    }
1084 
1085    if (!dr) SUMA_RETURN(0);
1086    if (init!=1) { SUMA_S_Err("Not initialized"); SUMA_RETURN(0); }
1087    if (Opt->Writepcg_G_au && !pcgrec) {
1088       SUMA_S_Err("Want Writepcg_G_au, but no pcgrec. Bad init.");
1089       SUMA_RETURN(0);
1090    }
1091    memset(dr, 0, DSET_NVOX(Opt->sig)*sizeof(double));
1092    if ((icls = SUMA_NI_str_array_find(cls, Opt->clss, 0, 0)) < 0) {
1093       SUMA_S_Errv("Failed to find class %s !!!\n", cls);
1094       SUMA_RETURN(0);
1095    }
1096    SUMA_LH("Looping over features");
1097    for (i=0; i<Opt->feats->num; ++i) {
1098       if (Opt->feat_exp) wfeat = Opt->feat_exp[icls][i];
1099       else wfeat= 0.0;
1100       if (Opt->debug > 1)
1101          SUMA_S_Notev("Calling p_cv_GIV_afu %d/%d\n", i,Opt->feats->num);
1102       if (!(p_cv_GIV_afu(Opt, Opt->feats->str[i], cls, d))) {
1103          SUMA_S_Err("Failed in p_cv_GIV_afu"); SUMA_RETURN(0);
1104       }
1105 
1106       if (Opt->Writepcg_G_au) {
1107          /* stick the results in pcgrec and write it out */
1108          a = (short *)DSET_ARRAY(pcgrec,i);
1109          for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
1110             ddd = d[j]+MINP;
1111             if (wfeat > 0) ddd = pow(ddd,wfeat);
1112             if (ddd>1.0) a[j]=(short)pf;
1113             else a[j] = (short)(pf*(ddd));
1114          }
1115          EDIT_BRICK_FACTOR(pcgrec, i,1.0/pf);
1116          sprintf(fpref, "p(c=%s|a(%s))", cls, Opt->feats->str[i]);
1117          EDIT_BRICK_LABEL(pcgrec,i,fpref);
1118       }
1119 
1120       for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
1121          if (IN_MASK(Opt->cmask, j)) {
1122             #if 0
1123             if (1) {
1124                if (d[j] > MINP) dr[j] = dr[j] + log(d[j]);
1125                else dr[j] = dr[j] + log(MINP);
1126                /* if (!(j%1000))
1127                   fprintf(stderr,"dr %f exp(dr) %f \n",  dr[j], exp(dr[j])); */
1128             } else {
1129                 dr[j] = dr[j] + log(d[j]);
1130             }
1131             #else
1132                /* better just add MINP to all probs Aug. 2012*/
1133                d[j] += MINP;
1134                if (d[j]>1.0) d[j] = 1.0; /* This is not proper,
1135                                              proper scaling should be done
1136                                              once all d[j] are computed across
1137                                              classes */
1138                if (wfeat>0) dr[j] = dr[j] + wfeat*log(d[j]);
1139                else dr[j] = dr[j] + log(d[j]);
1140             #endif
1141          } else {
1142             dr[j] = 0.0;
1143          }
1144       }
1145    }
1146 
1147    if (Opt->Writepcg_G_au) {
1148       UNIQ_idcode_fill(DSET_IDCODE_STR(pcgrec));/* new id */
1149       sprintf(fpref,"%s.p_c%s_G_each_feature", Opt->prefix, cls);
1150       EDIT_dset_items( pcgrec, ADN_prefix, fpref, ADN_none );
1151       DSET_quiet_overwrite(pcgrec) ;
1152    }
1153 
1154    if (!Opt->logp) {
1155       /* undo log and return */
1156       for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
1157          if (IN_MASK(Opt->cmask,j)) dr[j] = exp(dr[j]);
1158       }
1159    }
1160 
1161    if (Opt->VoxDbg >= 0) {
1162       fprintf(Opt->VoxDbgOut,"      %sp(c=%s|a,ALL f)=%f\n",
1163                             Opt->logp ? "LOG" : "",
1164                             cls, dr[Opt->VoxDbg]);
1165    }
1166    SUMA_RETURN(1);
1167 }
1168 
normalize_p(SEG_OPTS * Opt,THD_3dim_dataset * pout)1169 int normalize_p(SEG_OPTS *Opt, THD_3dim_dataset *pout) {
1170    int i, ii, j;
1171    float bfi[DSET_NVALS(pout)];
1172    float bfo[DSET_NVALS(pout)];
1173    double dv[DSET_NVALS(pout)], ddf, sdv;
1174    double dvo[DSET_NVALS(pout)];
1175 
1176    ENTRY("normalize_p");
1177 
1178    for (i=0; i<DSET_NVALS(pout); ++i) bfo[i]=1/32767.0f;
1179 
1180    GET_BFs(pout, bfi);
1181    for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
1182       if (IN_MASK(Opt->cmask, j)) {
1183          GET_VEC_AT_VOX(pout, j, dv, bfi);
1184          if (Opt->logp) {
1185             for (i=0; i<DSET_NVALS(pout); ++i) {
1186                ddf = 1.0;
1187                for (ii=0; ii<DSET_NVALS(pout); ++ii) {
1188                   if (ii!=i) ddf += exp(dv[ii]-dv[i]);
1189                }
1190                dvo[i] = 1.0f / ddf;
1191             }
1192             PUT_VEC_AT_VOX(pout,j,dvo,bfo);
1193          } else {
1194             sdv=0.0;
1195             for (i=0; i<DSET_NVALS(pout); ++i) sdv +=dv[i];
1196             for (i=0; i<DSET_NVALS(pout); ++i) dvo[i] = dv[i]/sdv;
1197             PUT_VEC_AT_VOX(pout,j,dvo,bfo);
1198          }
1199       }
1200    }
1201    PUT_BFs(pout, bfo);
1202 
1203    RETURN(1);
1204 }
1205 
1206 
is_shorty(THD_3dim_dataset * pset)1207 int is_shorty(THD_3dim_dataset *pset)
1208 {
1209    int ii;
1210    if (!pset) return(0);
1211    for (ii=0; ii<DSET_NVALS(pset); ++ii) {
1212       if (DSET_BRICK_TYPE(pset,ii) != MRI_short) return(0);
1213    }
1214    return(1);
1215 }
1216 
1217 /*!
1218    Set the floor of mixing fractions in class stat struct
1219 */
SUMA_set_Stat_mix_floor(SUMA_CLASS_STAT * cs,float floor)1220 SUMA_Boolean SUMA_set_Stat_mix_floor(SUMA_CLASS_STAT *cs, float floor)
1221 {
1222    static char FuncName[]={"SUMA_set_Stat_mix_floor"};
1223    int i, N_tot = 0;
1224    double d, m;
1225    SUMA_Boolean LocalHead = NOPE;
1226 
1227    SUMA_ENTRY;
1228 
1229    if (!cs || cs->N_label < 2) SUMA_RETURN(NOPE);
1230 
1231    if (floor > 1.0/cs->N_label) floor = 1.0/cs->N_label;
1232    if (floor == 0.0) SUMA_RETURN(YUP); /* nothing to do */
1233    if (floor == -1.0f) floor = 0.000001;
1234 
1235    d = floor/(1.0-floor*cs->N_label);
1236    for (i=0; i<cs->N_label; ++i) {
1237       N_tot += SUMA_get_Stat(cs, cs->label[i], "num");
1238    }
1239    for (i=0; i<cs->N_label; ++i) {
1240       m = SUMA_get_Stat(cs, cs->label[i], "mix");
1241       m = (m+d)/(1.0+cs->N_label*d);
1242       SUMA_set_Stat(cs, cs->label[i], "mix", m);
1243       SUMA_set_Stat(cs, cs->label[i], "num", (int)(m*N_tot));
1244    }
1245 
1246    SUMA_RETURN(YUP);
1247 
1248 }
1249 
1250 
1251 /*!
1252    Set the floor of the probabilities in a dataset
1253 */
set_p_floor(THD_3dim_dataset * pset,float pfl,byte * cmask)1254 int set_p_floor(THD_3dim_dataset *pset, float pfl, byte *cmask)
1255 {
1256    static char FuncName[]={"set_p_floor"};
1257    int i, ii, j;
1258    float bfi[DSET_NVALS(pset)];
1259    float bfo[DSET_NVALS(pset)];
1260    double dv[DSET_NVALS(pset)];
1261    double dvo[DSET_NVALS(pset)];
1262    double d = 0.0, N = 0.0;
1263 
1264    SUMA_ENTRY;
1265 
1266    if (!is_shorty(pset)) {
1267       SUMA_S_Errv("Dset %s not all shorts\n", DSET_PREFIX(pset));
1268       SUMA_RETURN(0);
1269    }
1270 
1271    N = DSET_NVALS(pset);
1272    d = pfl/(1.0-pfl*N);
1273 
1274    for (i=0; i<DSET_NVALS(pset); ++i) bfo[i]=1/32767.0f;
1275 
1276    GET_BFs(pset, bfi);
1277    for (j=0; j<DSET_NVOX(pset); ++j) {
1278       if (IN_MASK(cmask, j)) {
1279          GET_VEC_AT_VOX(pset, j, dv, bfi);
1280          for (i=0; i<DSET_NVALS(pset); ++i) dvo[i] = (dv[i]+d)/(1.0+N*d);
1281          PUT_VEC_AT_VOX(pset,j,dvo,bfo);
1282       }
1283    }
1284    PUT_BFs(pset, bfo);
1285    SUMA_RETURN(1);
1286 }
1287 
1288 /*!
1289    Estimate the probability of each class, given all features
1290 */
p_C_GIV_A(SEG_OPTS * Opt)1291 THD_3dim_dataset *p_C_GIV_A (SEG_OPTS *Opt)
1292 {
1293    static char FuncName[]={"p_C_GIV_A"};
1294    char bl[256]={""};
1295    int i,j, ii;
1296    double *d=NULL;
1297    THD_3dim_dataset *pout=NULL;
1298    short *p=NULL;
1299    float pf=0.0;
1300 
1301    SUMA_ENTRY;
1302 
1303 
1304    /* init */
1305    d = (double *)calloc(DSET_NVOX(Opt->sig), sizeof(double));
1306    if (!d) SUMA_RETURN(NULL);
1307    if (!p_cv_GIV_A (Opt, NULL, d)) SUMA_RETURN(NULL);
1308    NEW_SHORTY(Opt->sig, Opt->clss->num, Opt->prefix, pout);
1309    if (!pout) SUMA_RETURN(NULL);
1310    if( !THD_ok_overwrite() && THD_is_file( DSET_HEADNAME(pout) ) ){
1311       ERROR_exit("Output file %s already exists -- cannot continue!\n",
1312                   DSET_HEADNAME(pout) ) ;
1313    }
1314 
1315    /* process */
1316    for (i=0; i<Opt->clss->num; ++i) {
1317       if (Opt->debug > -1000)
1318          SUMA_S_Notev("Calling p_cv_GIV_A %d/%d\n", i,Opt->clss->num);
1319       if (!(p_cv_GIV_A (Opt, Opt->clss->str[i],d))) {
1320          SUMA_S_Err("Failed in p_cv_GIV_A"); SUMA_RETURN(NULL);
1321       }
1322 
1323       /* and store in output */
1324       if (!Opt->logp) {
1325          p = (short *)DSET_ARRAY(pout, i);
1326          pf = 32767.0f; /* max p is 1, so stick with this */
1327          for (j=0; j<DSET_NVOX(Opt->sig); ++j) {
1328             p[j]=(short)(d[j]*pf);
1329          }
1330          EDIT_BRICK_FACTOR(pout,i,1.0/pf);
1331          sprintf(bl, "p(c=%s|A)",Opt->clss->str[i]);
1332       } else {
1333          sprintf(bl, "LOGp(c=%s|A)",Opt->clss->str[i]);
1334          EDIT_substscale_brick(pout, i, MRI_double, d, MRI_short, -1.0);
1335       }
1336       EDIT_BRICK_LABEL(pout,i,bl);
1337    }
1338 
1339 
1340    if (Opt->rescale_p) {
1341       /* Now rescale probs so that sum is 1 */
1342       if (!normalize_p(Opt, pout)) {
1343          ERROR_exit("Failed to normalize_p!\n",
1344                   DSET_HEADNAME(pout) ) ;
1345       }
1346       /* and redo labels */
1347       for (i=0; i<Opt->clss->num; ++i) {
1348          sprintf(bl, "P(c=%s|A)",Opt->clss->str[i]);
1349          EDIT_BRICK_LABEL(pout,i,bl);
1350       }
1351    } else {
1352       /* labels OK from above*/
1353    }
1354 
1355    /* clean */
1356    if (!p_cv_GIV_A (Opt, NULL, d)) {
1357       SUMA_S_Err("Failed in p_cv_GIV_A cleanup but will proceed");
1358    }
1359    free(d); d= NULL;
1360 
1361    SUMA_RETURN(pout);
1362 }
1363 
1364 /*!
1365    Estimate the probability of each class, given all features, faster
1366 */
p_C_GIV_A_omp(SEG_OPTS * Opt)1367 THD_3dim_dataset *p_C_GIV_A_omp (SEG_OPTS *Opt)
1368 {
1369    static char FuncName[]={"p_C_GIV_A_omp"};
1370    char bl[256]={""};
1371    int N_ijk=0, N_f=0, N_c=0, *iff=NULL, ia, ff, cc;
1372    THD_3dim_dataset *pout=NULL;
1373    float *afv=NULL, pf = 32767.0f;
1374    SUMA_FEAT_DIST **FDv=NULL, *FD=NULL;
1375    int *imask=NULL, N_imask=0, ijk;
1376    float minp;
1377 
1378    SUMA_ENTRY;
1379 
1380    if (Opt->rescale_p && Opt->logp) {
1381       SUMA_S_Err("Not ready to handle both Opt->rescale_p && Opt->logp");
1382       SUMA_RETURN(NULL);
1383    }
1384 
1385    /* init convenience vars */
1386    N_ijk=DSET_NVOX(Opt->sig);
1387    N_f=Opt->feats->num;
1388    N_c=Opt->clss->num;
1389 
1390    /* Get your self a nicely sorted array of distributions */
1391    FDv = (SUMA_FEAT_DIST **)SUMA_calloc(Opt->feats->num*Opt->clss->num,
1392                                         sizeof(SUMA_FEAT_DIST *));
1393    for (ff=0; ff<N_f; ++ff) {
1394       for (cc=0; cc<N_c; ++cc) {
1395          if (!(FD = SUMA_find_feature_dist(Opt->FDV, NULL,
1396                         Opt->feats->str[ff], Opt->clss->str[cc], NULL)) ) {
1397             SUMA_S_Errv("Failed to find dist struct for %s %s\n",
1398                         Opt->feats->str[ff], Opt->clss->str[cc]);
1399             SUMA_free(FDv);
1400             SUMA_RETURN(NULL);
1401          }
1402          if (FD->tp != SUMA_FEAT_NP) {
1403             SUMA_S_Warnv("Dist for %s %s is not NP.\n"
1404                          "Will revert to old function",
1405                          Opt->feats->str[ff], Opt->clss->str[cc]);
1406             SUMA_free(FDv);
1407             SUMA_RETURN(p_C_GIV_A(Opt));
1408          }
1409          FDv[ff*N_c+cc] = FD; FD=NULL;
1410       }
1411    }
1412    /* and the indices of sub-bricks corresponding to the classes */
1413    iff = (int *)SUMA_calloc(Opt->feats->num, sizeof(int));
1414    afv = (float *)SUMA_calloc(Opt->feats->num, sizeof(float));
1415    for (ff=0; ff<N_f; ++ff) {
1416       SB_LABEL(Opt->sig,Opt->feats->str[ff], ia);
1417       if (ia<0) {
1418          SUMA_S_Err("Failed to find %s. Are sub-brick labels set properly?",
1419                      Opt->feats->str[ff]);
1420          SUMA_free(FDv); SUMA_free(afv); SUMA_free(iff);
1421          SUMA_RETURN(NULL);
1422       }
1423       iff[ff]=ia;
1424       afv[ff] = DSET_BRICK_FACTOR(Opt->sig, ia);
1425       if (!afv[ff]) afv[ff] = 1.0;
1426    }
1427 
1428    /* and a mask indexing array to balance cpu loads */
1429    for (ijk=0,N_imask=0; ijk<N_ijk; ++ijk) {
1430       if (IN_MASK(Opt->cmask,ijk)) ++N_imask;
1431    }
1432    imask = (int *)SUMA_calloc(N_imask, sizeof(int));
1433    for (ijk=0,N_imask=0; ijk<N_ijk; ++ijk) {
1434       if (IN_MASK(Opt->cmask,ijk)) {
1435          imask[N_imask++]=ijk;
1436       }
1437    }
1438    /* init output volumes*/
1439    NEW_SHORTY(Opt->sig, Opt->clss->num, Opt->prefix, pout);
1440    if (!pout) SUMA_RETURN(NULL);
1441    if( !THD_ok_overwrite() && THD_is_file( DSET_HEADNAME(pout) ) ){
1442       ERROR_exit("Output file %s already exists -- cannot continue!\n",
1443                   DSET_HEADNAME(pout) ) ;
1444    }
1445    minp = 1.0/(float)N_c; /* instead of constant MINP */
1446    if (Opt->DO_o) {
1447       char fpref[256]={""};
1448       sprintf(fpref,"%s.centrality", Opt->proot);
1449       NEW_SHORTY(Opt->sig, Opt->clss->num, fpref, Opt->outl);
1450       if (!Opt->outl) SUMA_RETURN(NULL);
1451       if( !THD_ok_overwrite() && THD_is_file( DSET_HEADNAME(Opt->outl) ) ){
1452          ERROR_exit("Output file %s already exists -- cannot continue!\n",
1453                      DSET_HEADNAME(Opt->outl) ) ;
1454       }
1455    }
1456 
1457 AFNI_OMP_START ;
1458 #pragma omp parallel if( N_imask > 10000 )
1459 { /* OMP start */
1460    int   ijk, cc, ff, iii, m_i0;
1461    double *pvGa=NULL, *P = NULL, *pp=NULL, d=0.0, A2, ps, *O=NULL;
1462    float a, m_a;
1463    short *bb=NULL;
1464 
1465 #pragma omp critical (MALLOC)
1466    {
1467       pvGa = (double *)calloc(N_c*N_f, sizeof(double));
1468       P = (double *)calloc(N_c, sizeof (double));
1469       if (Opt->outl) O = (double *)calloc(N_c, sizeof(double));
1470    }
1471 #pragma omp for
1472    for (iii=0; iii < N_imask; ++iii) { /* voxel loop */
1473       ijk = imask[iii];
1474       { /* mask cond. */
1475          if (O) memset(O, 0, N_c*sizeof(double));
1476          for (ff=0; ff<N_f; ++ff) { /* feature loop */
1477             A2 = 0.0;
1478             pp = pvGa+ff*N_c;
1479             bb = (short *)DSET_ARRAY(Opt->sig, iff[ff]);
1480             a = afv[ff]*bb[ijk]; /* feature amplitude */
1481             for (cc=0; cc<N_c; ++cc) { /* class loop */
1482                #if 0
1483                   d = SUMA_hist_freq((FDv[ff*N_c+cc])->hh, a);
1484                #else
1485                   /* shaves off a few seconds relative to SUMA_hist_freq()*/
1486                   SUMA_HIST_FREQ((FDv[ff*N_c+cc])->hh, a, d);
1487                #endif
1488                pp[cc] = Opt->mixfrac[cc]*d;
1489                A2 += pp[cc];
1490                         /* Add outlierness of feature amplitude given class */
1491                if (O) O[cc] += SUMA_hist_value((FDv[ff*N_c+cc])->hh, a, "outl");
1492             } /* class loop */
1493             for (cc=0; cc<N_c; ++cc) {
1494                pp[cc] /= A2;
1495                if (ijk == Opt->VoxDbg) {
1496                   fprintf(Opt->VoxDbgOut,"   p(c=%s|%s=%f)=%f\n",
1497                             Opt->clss->str[cc], Opt->feats->str[ff], a, pp[cc]);
1498                }
1499             } /* unit sum */
1500             if (ijk == Opt->VoxDbg) fprintf(Opt->VoxDbgOut,"\n");
1501 
1502          } /* feature loop */
1503          /* Compute P(class|all features) */
1504          for (cc=0; cc<N_c; ++cc) { /* class loop 2 */
1505             for (ff=0, P[cc]=0.0; ff<N_f; ++ ff) {
1506                if (Opt->feat_exp) {  /* feature loop 2 */
1507                   P[cc] += (Opt->feat_exp[cc][ff]*log(pvGa[ff*N_c+cc]+minp));
1508                } else {
1509                   P[cc] += (log(pvGa[ff*N_c+cc]+minp));
1510                }
1511             } /* feature loop 2 */
1512          } /* class loop 2 */
1513          /* take exp of P and scale so that all probs sum to 1.
1514             There may be precision problems here, consider
1515             summing of log(p)s */
1516          for (cc=0, ps = 0.0; cc<N_c; ++cc) { /* class loop 3 */
1517             P[cc] = exp(P[cc]); ps += P[cc];
1518          } /* class loop 3 */
1519          if (ijk == Opt->VoxDbg) fprintf(Opt->VoxDbgOut,"\n");
1520          for (cc=0; cc<N_c; ++cc) { /* class loop 4 */
1521             if (Opt->rescale_p) P[cc] /= ps;
1522             /* store in output */
1523             bb = (short *)DSET_ARRAY(pout, cc);
1524             if (!Opt->logp) {
1525                bb[ijk] = (short)(P[cc]*pf);
1526             } else {
1527                /* SUMA_S_Err("Not ready to write out logp, sticking with p"); */
1528                bb[ijk] = (short)(P[cc]*pf);
1529             }
1530             /* store 1.0-outlierness, call it centrality. Easier to look at */
1531             if (Opt->outl) {
1532                bb = (short *)DSET_ARRAY(Opt->outl, cc);
1533                bb[ijk] = (short)((1.0-O[cc]/N_f)*pf);
1534             }
1535             if (ijk == Opt->VoxDbg) {
1536                   fprintf(Opt->VoxDbgOut,"      p(c=%s|a, ALL f)=%f\n",
1537                             Opt->clss->str[cc], P[cc]);
1538                if (O) {
1539                   fprintf(Opt->VoxDbgOut,"      c(A|c=%s)=%f\n",
1540                             Opt->clss->str[cc], 1.0-O[cc]);
1541                }
1542             }
1543          } /* class loop 4 */
1544       } /* mask cond. */
1545    } /* voxel loop */
1546 
1547 #pragma omp critical (FREE)
1548 {
1549    if (pvGa) free(pvGa); pvGa = NULL;
1550    if (P) free(P); P = NULL;
1551    if (O) free(O); O = NULL;
1552 }
1553 
1554 } /* OMP end */
1555 AFNI_OMP_END ;
1556 
1557    /* fix up output set */
1558    for (cc=0; cc<N_c; ++cc) {
1559       if (!Opt->logp) {
1560          EDIT_BRICK_FACTOR(pout,cc,1.0/pf);
1561          sprintf(bl, "%c(c=%s|A)",Opt->rescale_p ? 'P':'p', Opt->clss->str[cc]);
1562       } else {
1563          SUMA_S_Warn("Not ready to write out logp, sticking with p");
1564          sprintf(bl, "%c(c=%s|A)",Opt->rescale_p ? 'P':'p',Opt->clss->str[cc]);
1565       }
1566       EDIT_BRICK_LABEL(pout,cc,bl);
1567       if (Opt->outl) {
1568          EDIT_BRICK_FACTOR(Opt->outl,cc,1.0/pf);
1569          sprintf(bl, "%c(A|c=%s)",'C', Opt->clss->str[cc]);
1570          EDIT_BRICK_LABEL(Opt->outl,cc,bl);
1571       }
1572    }
1573 
1574    /* clean */
1575 
1576    if (FDv) SUMA_free(FDv); FDv = NULL;
1577    if (afv) SUMA_free(afv); afv = NULL;
1578    if (iff) SUMA_free(iff); iff = NULL;
1579    if (imask) SUMA_free(imask); imask = NULL;
1580 
1581    SUMA_RETURN(pout);
1582 }
1583 
SUMA_LabelToGroupedIndex(char * cls_str,char ** group_lbls,int N_group_lbls)1584 int SUMA_LabelToGroupedIndex(char *cls_str, char **group_lbls, int N_group_lbls)
1585 {
1586    static char FuncName[]={"SUMA_LabelToGroupedKey"};
1587    int mtch=0, j,  ng=0, jmatch=-1;
1588 
1589    SUMA_ENTRY;
1590 
1591    mtch = 0;
1592    for (j=0; j<N_group_lbls; ++j) {
1593       ng = strlen(group_lbls[j]);
1594       if (strlen(cls_str) >= ng) {
1595          if (!strcmp(cls_str, group_lbls[j])) {
1596                /* ININFO_message("%s --> %s (%d)",
1597                                  cls_str, group_lbls[j], j); */
1598                jmatch = j;
1599                mtch += 1;
1600          } else if (!strncmp(cls_str, group_lbls[j],
1601                                     strlen(group_lbls[j])) &&
1602                         ( cls_str[ng] == ',' ||
1603                           cls_str[ng] == '.' ||
1604                           cls_str[ng] == '-' ||
1605                           cls_str[ng] == '_') ) {
1606                /* ININFO_message("%s --> %s (%d)",
1607                               cls_str, group_lbls[j], j); */
1608                jmatch = j;
1609                mtch += 1;
1610          }
1611       }
1612    }
1613    if (mtch == 0) {
1614       /* ININFO_message("Could not find match for %s\n", cls_str); */
1615       SUMA_RETURN(-1);
1616    }
1617    if (mtch > 1) {
1618       /* ERROR_message("Found more than one match"); */
1619       SUMA_RETURN(-mtch);
1620    }
1621 
1622    SUMA_RETURN(jmatch);
1623 }
1624 
SUMA_LabelToGroupedKey(char * cls_str,char ** group_lbls,int N_group_lbls,int * group_keys)1625 int SUMA_LabelToGroupedKey(char *cls_str, char **group_lbls, int N_group_lbls,
1626                       int *group_keys) {
1627    int j = SUMA_LabelToGroupedIndex(cls_str,  group_lbls, N_group_lbls);
1628 
1629    if (j<0) return(j);
1630    else return(group_keys[j]);
1631 }
1632 
1633 
SUMA_GroupLabelMapping(char ** clss_lbls,int N_clss_lbls,char ** grpclss_lbls,int N_grpclss_lbls,int * map,int verb)1634 int SUMA_GroupLabelMapping (char **clss_lbls , int N_clss_lbls,
1635                         char **grpclss_lbls, int N_grpclss_lbls,
1636                         int *map, int verb)
1637 {
1638    static char FuncName[]={"SUMA_GroupLabelMapping"};
1639    int j, i;
1640 
1641    SUMA_ENTRY;
1642 
1643    /* make sure you can map one to the other */
1644    for (i=0; i<N_clss_lbls; ++i) map[i] = -1;
1645    {
1646       for (i=0; i<N_clss_lbls; ++i) {
1647          j = SUMA_LabelToGroupedIndex(clss_lbls[i], grpclss_lbls,
1648                                        N_grpclss_lbls);
1649          if (j >= 0) { map[i] = j; }
1650 
1651       }
1652    }
1653    if (verb) {
1654       for (i=0; i<N_clss_lbls; ++i) {
1655          if (map[i]>=0) {
1656             fprintf(stderr,"%s --> %s\n", clss_lbls[i] , grpclss_lbls[map[i]]);
1657          } else {
1658             fprintf(stderr,"%s --> NO MATCH\n", clss_lbls[i]);
1659          }
1660       }
1661    }
1662    SUMA_RETURN(1);
1663 }
1664 
1665 /*!
1666    Regroup classes.
1667 */
SUMA_Regroup_classes(SEG_OPTS * Opt,char ** clss_lbls,int N_clss_lbls,int * keys,char ** group_clss_lbls,int N_group_clss_lbls,int * ugroup_keys,byte * cmask,THD_3dim_dataset * pset,THD_3dim_dataset * cset,THD_3dim_dataset ** gpsetp,THD_3dim_dataset ** gcsetp)1668 int SUMA_Regroup_classes (SEG_OPTS *Opt,
1669                      char **clss_lbls,
1670                      int N_clss_lbls,
1671                      int *keys,
1672                      char **group_clss_lbls,
1673                      int N_group_clss_lbls,
1674                      int  * ugroup_keys,
1675                      byte *cmask,
1676                      THD_3dim_dataset *pset,
1677                      THD_3dim_dataset *cset,
1678                      THD_3dim_dataset **gpsetp,
1679                      THD_3dim_dataset **gcsetp)
1680 {
1681    static char FuncName[]={"SUMA_Regroup_classes"};
1682    int i,c, v,gc, imax=0, dtable_key[1024], ckey=0, gkey = 0;
1683    short *p=NULL, *pg=NULL;
1684    double max=0.0;
1685    int igrp[1024], mapverb=0;
1686    int group_keys[1024];
1687    THD_3dim_dataset *gpset=NULL,*gcset=NULL;
1688    char sval[256], *group_labeltable_str=NULL;
1689    Dtable *vl_dtable=NULL;
1690    SUMA_Boolean LocalHead = NOPE;
1691 
1692    SUMA_ENTRY;
1693    /* checks */
1694    if (group_clss_lbls==NULL) {
1695       SUMA_S_Errv("Bad input %p \n",
1696                   group_clss_lbls);
1697       SUMA_RETURN(0);
1698    }
1699 
1700    mapverb=0;
1701    if (!cset && !pset) {
1702       mapverb = 1;
1703    }
1704 
1705    /* figure out the mapping between one and the other */
1706    if (!SUMA_GroupLabelMapping (clss_lbls, N_clss_lbls,
1707                                 group_clss_lbls, N_group_clss_lbls,
1708                                 igrp, mapverb)) {
1709       ERROR_message("Failed to group map");
1710       SUMA_RETURN(0);
1711    }
1712 
1713    if (!cset && !pset) {
1714       /* just called to be sure conversion is OK */
1715       SUMA_RETURN(1);
1716    }
1717 
1718    if (!gcsetp || *gcsetp) {
1719       ERROR_message("You must send the address of a null pointer for gcsetp");
1720       SUMA_RETURN(0);
1721    }
1722    if (gpsetp && *gpsetp) {
1723       ERROR_message("If you send gpsetp it must be the address to null pointer");
1724       SUMA_RETURN(0);
1725    }
1726 
1727    if (pset && !gpsetp) {
1728       ERROR_message("Nothing to return grouped probs in");
1729       SUMA_RETURN(0);
1730    }
1731    gcset = *gcsetp;
1732    NEW_SHORTY(cset, DSET_NVALS(cset), Opt->cgrefix, gcset);
1733 
1734    /* get the key of each group_clss, and form dtable */
1735    vl_dtable = new_Dtable(5);
1736    for (i=0; i<N_group_clss_lbls; ++i) {
1737       if (ugroup_keys) group_keys[i] = ugroup_keys[i];
1738       else group_keys[i] = i+1;
1739       sprintf(sval,"%d", group_keys[i]);
1740       addto_Dtable( sval , group_clss_lbls[i] , vl_dtable ) ;
1741    }
1742 
1743    p = (short *)DSET_ARRAY(cset,0);
1744    pg = (short *)DSET_ARRAY(gcset,0);
1745 
1746    for (i=0; i<DSET_NVOX(cset); ++i) { pg[i] = p[i]; } /* init */
1747    for (c=0; c<N_clss_lbls; ++c) {
1748       ckey = keys[c];
1749       if ((gkey = SUMA_LabelToGroupedKey(clss_lbls[c],
1750                         group_clss_lbls, N_group_clss_lbls,group_keys)) < 0) {
1751 
1752          /* ERROR_message("Failed to get group key" ); */
1753          /* that's OK, mask it */
1754          gkey = 0; /* mask entry */
1755       }
1756       {
1757          for (i=0; i<DSET_NVOX(cset); ++i) {
1758             if (p[i] == ckey) {
1759                pg[i] = gkey;
1760             }
1761          }
1762       }
1763    }
1764 
1765    EDIT_BRICK_LABEL(gcset,0,"maxprob_labels");
1766    group_labeltable_str = Dtable_to_nimlstring(vl_dtable,
1767                                                 "VALUE_LABEL_DTABLE");
1768    THD_set_string_atr(gcset->dblk ,
1769                         "VALUE_LABEL_DTABLE" , group_labeltable_str );
1770    free(group_labeltable_str);
1771    *gcsetp = gcset; gcset=NULL;
1772 
1773    /* if we have probabilities, need to group those too */
1774    if (pset && gpsetp) {
1775       double dv[N_clss_lbls], gdv[N_group_clss_lbls], sgdv;
1776       float bfi[N_clss_lbls];
1777       gpset = *gpsetp;
1778       NEW_SHORTY(pset, N_group_clss_lbls, Opt->pgrefix, gpset);
1779       GET_BFs(pset, bfi);
1780 
1781       for (v=0; v<DSET_NVOX(pset); ++v) {
1782          if (IN_MASK(cmask, v)) {
1783             GET_VEC_AT_VOX(pset, v, dv, bfi);
1784             sgdv=0.0;
1785             for (gc=0; gc<N_group_clss_lbls; ++gc) {
1786                gdv[gc] = 0.0;
1787                for (c=0; c<N_clss_lbls; ++c) {
1788                   if (igrp[c] == gc) {
1789                      if (dv[c] > gdv[gc]) {
1790                         gdv[gc] = dv[c];
1791                      }
1792                   }
1793                }
1794                sgdv += gdv[gc];
1795             }
1796             if (sgdv) {
1797                for (gc=0; gc<N_group_clss_lbls; ++gc) {
1798                   gdv[gc] /= sgdv;
1799                }
1800             }
1801             /* can use the same factor from other dset,
1802                all values between 0 and 1*/
1803             PUT_VEC_AT_VOX(gpset, v, gdv, bfi);
1804          }
1805       }
1806 
1807       PUT_BFs(gpset, bfi);
1808       for (gc=0; gc<N_group_clss_lbls; ++gc) {
1809          sprintf(sval,"p.%s",group_clss_lbls[gc]);
1810          EDIT_BRICK_LABEL(gpset,gc,sval);
1811       }
1812 
1813       *gpsetp = gpset; gpset=NULL;
1814    }
1815 
1816    SUMA_RETURN(1);
1817 }
1818 
SUMA_SplitClass_ind(int ig,int ks,int N_Glbls,int * Split)1819 int SUMA_SplitClass_ind(int ig, int ks, int N_Glbls, int *Split)
1820 {
1821    int i, k, l=0;
1822    for (i=0; i<N_Glbls; ++i) {
1823       for (k=0; k<Split[i]; ++k) {
1824          if (i==ig && k==ks) return(l);
1825          ++l;
1826       }
1827    }
1828    return(-1);
1829 }
1830 
SUMA_Split_Classes(char ** Glbls,int N_Glbls,int * Gkeys,int * Split,THD_3dim_dataset * aset,THD_3dim_dataset * Gcset,byte * cmask,THD_3dim_dataset ** Scsetp,SUMA_CLASS_STAT ** Scs,SEG_OPTS * Opt)1831 int SUMA_Split_Classes(char **Glbls, int N_Glbls, int *Gkeys, int *Split,
1832                        THD_3dim_dataset *aset, THD_3dim_dataset *Gcset,
1833                        byte *cmask,
1834                        THD_3dim_dataset **Scsetp, SUMA_CLASS_STAT **Scs,
1835                        SEG_OPTS *Opt)
1836 {
1837    static char FuncName[]={"SUMA_Split_Classes"};
1838    char **Slbls, snum[64];
1839    int N_Slbls, *Skeys, *GRkey;
1840    int i, j, k, l, ijk, smask_count, N_submax;
1841    short *c=NULL, *ctmp=NULL, *sc=NULL;
1842    byte *smask=NULL;
1843    THD_3dim_dataset *Scset=NULL;
1844    OPT_KMEANS oc;
1845    SUMA_Boolean LocalHead = NOPE;
1846 
1847    SUMA_ENTRY;
1848 
1849    /* total number of resultant classes */
1850    N_Slbls = 0; N_submax=0;
1851    for (i=0; i<N_Glbls; ++i) {
1852       N_Slbls += Split[i];
1853       if (Split[i]>N_submax) N_submax = Split[i];
1854    }
1855 
1856    /* create output */
1857          /* Using N_Slbls for Slbls was causing a
1858             MCW_malloc post-corruption error at free
1859             time. This would happen even if nothing
1860             was done before freeing Slbls (search for BUG below).
1861             Valgrind found nothing.
1862             Not sure what to do, but adding a +1 seems to
1863             fix the problem */
1864    Slbls = (char **)SUMA_calloc(N_Slbls+1, sizeof(char*));
1865    Skeys = (int   *)SUMA_calloc(N_Slbls, sizeof(int)  );
1866    GRkey = (int   *)SUMA_calloc(N_Slbls, sizeof(int)  );
1867 
1868    /* Fill the keys */
1869    l = 0;
1870    for (i=0; i<N_Glbls; ++i) {
1871       for (k=0; k<Split[i]; ++k) {
1872          sprintf(snum,"%02d", k);
1873          Skeys[l] = 10*(i+(N_submax+1)/10)+k+1;
1874          Slbls[l] = SUMA_append_replace_string(Glbls[i],snum,".",0);
1875          GRkey[l] = Gkeys[i];
1876          ++l;
1877       }
1878    }
1879    #if 0 /* BUG See BUG above  */
1880       SUMA_LHv("%d, %d\n", l, N_Slbls);
1881       for (l=0; l<N_Slbls; ++l) SUMA_ifree(Slbls[l]);
1882       SUMA_ifree(Slbls);
1883       exit(1);
1884    #endif
1885 
1886    if (LocalHead) {
1887       for (l=0; l<N_Slbls; ++l) {
1888          fprintf(SUMA_STDERR,"Slbls[%03d]=%s --> %d (parent %d)\n",
1889                            l, Slbls[l], Skeys[l], GRkey[l]);
1890       }
1891    }
1892    /* a new class stats */
1893    *Scs =  SUMA_New_Class_Stat(Slbls, N_Slbls, Skeys, 3, NULL);
1894 
1895    /* Add GRkey */
1896    for (l=0; l<N_Slbls; ++l) {
1897       SUMA_set_Stat(*Scs, Slbls[l], "GRkey",(double)GRkey[l]);
1898    }
1899 
1900 
1901    /* Here is where you split the actual classes */
1902    if (Gcset) {
1903       SUMA_LH("Working Gcset");
1904       if (*Scsetp == NULL) {
1905          NEW_SHORTY(Gcset,1,"split.classes",Scset); *Scsetp = Scset;
1906       } else {
1907          Scset = *Scsetp;
1908       }
1909       smask = (byte *)SUMA_calloc(sizeof(byte), DSET_NVOX(aset));
1910       c = (short *)DSET_ARRAY(Gcset,0);
1911       sc = (short *)SUMA_calloc(sizeof(short), DSET_NVOX(aset));
1912       oc = new_kmeans_oc();
1913       oc.remap = MAG; oc.verb = 0; oc.distmetric = 'e';
1914       oc.r = 3;
1915       for (i=0; i<N_Glbls; ++i) {
1916          oc.k = Split[i];
1917          snprintf(snum,60,"Split.%s.%02d",Glbls[i],oc.k);
1918          oc.jobname = SUMA_copy_string(snum);
1919          for (k=0; k<Split[i]; ++k) {
1920             l = SUMA_SplitClass_ind(i,k, N_Glbls, Split);
1921             oc.clabels[k] = Slbls[l];
1922          }
1923          smask_count = 0;
1924          for (ijk=0; ijk<DSET_NVOX(aset); ++ijk) {
1925             if (IN_MASK(cmask, ijk) && c[ijk] == Gkeys[i]) {
1926                smask[ijk] = 1; ++smask_count;
1927             } else smask[ijk] = 0;
1928          }
1929          SUMA_LHv("Splitting class %s (%d voxels) into %d\n",
1930                   Glbls[i], smask_count, Split[i]);
1931          if (!(thd_Acluster1 (aset,
1932                smask, smask_count,
1933                &Scset,
1934                NULL,
1935                NULL,
1936                oc))) {
1937             SUMA_S_Err("Failed to split cluster");
1938             SUMA_RETURN(0);
1939          }
1940          SUMA_ifree(oc.jobname);
1941          /* Now collect new clusters in new array sc*/
1942          l = SUMA_SplitClass_ind(i, 0, N_Glbls, Split);
1943          ctmp = (short *)DSET_ARRAY(Scset,0);
1944          for (ijk=0; ijk<DSET_NVOX(aset); ++ijk) {
1945             if (smask[ijk]) {
1946                sc[ijk] = Skeys[l+ctmp[ijk]-1];
1947             }
1948          }
1949       } /* for each grouped class i */
1950       SUMA_ifree(smask);
1951       /* Now put the new array in Scset */
1952       EDIT_substitute_brick(Scset,0,MRI_short,sc);
1953 
1954 
1955       /* And a proper labeltable */
1956       if (!SUMA_SetDsetLabeltable(Scset, Slbls, N_Slbls, Skeys)) {
1957          SUMA_S_Err("Failed to set labeltable");
1958          SUMA_RETURN(0);
1959       }
1960 
1961       if (LocalHead) {
1962          SUMA_Seg_Write_Dset(Opt->proot, "Splitted",
1963                           Scset, -1, Opt->hist);
1964       }
1965    }
1966 
1967    SUMA_LH("Free temps");
1968    for (l=0;l<N_Slbls; ++l) SUMA_ifree(Slbls[l]);
1969    SUMA_ifree(Slbls); SUMA_ifree(Skeys); SUMA_ifree(GRkey);
1970 
1971    SUMA_RETURN(1);
1972 }
1973 
SUMA_assign_classes(THD_3dim_dataset * pset,SUMA_CLASS_STAT * cs,byte * cmask,THD_3dim_dataset ** csetp)1974 int SUMA_assign_classes (THD_3dim_dataset *pset,
1975                          SUMA_CLASS_STAT *cs,
1976                          byte *cmask,
1977                          THD_3dim_dataset **csetp)
1978 {
1979    return(SUMA_assign_classes_eng (pset, cs->label, cs->N_label,
1980                                    cs->keys, cmask, csetp));
1981 }
1982 /*!
1983    Assign a class given likelihoods.
1984    Only tested if normalize_p was called
1985 */
SUMA_assign_classes_eng(THD_3dim_dataset * pset,char ** label,int N_label,int * keys,byte * cmask,THD_3dim_dataset ** csetp)1986 int SUMA_assign_classes_eng (THD_3dim_dataset *pset,
1987                          char **label, int N_label, int *keys,
1988                          byte *cmask,
1989                          THD_3dim_dataset **csetp)
1990 {
1991    static char FuncName[]={"SUMA_assign_classes_eng"};
1992    int i,j, imax=0, uneven;
1993    double *d=NULL;
1994    THD_3dim_dataset *cset=*csetp;
1995    short *p=NULL;
1996    double max=0.0;
1997    SUMA_Boolean LocalHead = NOPE;
1998 
1999    SUMA_ENTRY;
2000 
2001    /* checks */
2002    if (!pset || !keys ) {
2003       SUMA_S_Errv("Bad input %p %p\n", pset, keys);
2004       SUMA_RETURN(0);
2005    }
2006 
2007    /* init */
2008    if (!cset) {
2009       NEW_SHORTY(pset, 1, "assign_classes", cset);
2010       *csetp = cset;
2011    }
2012 
2013    if (!cset) RETURN(0);
2014 
2015    /* make sure all factors are the same */
2016    max = DSET_BRICK_FACTOR(pset,0);
2017    uneven = 0;
2018    for (j=1; j<DSET_NVALS(pset); ++j) {
2019       if (max != DSET_BRICK_FACTOR(pset,j)) {
2020          uneven=1; break;
2021       }
2022    }
2023    /* process */
2024    p = (short *)DSET_ARRAY(cset,0);
2025    if (!uneven) {
2026       for (j=0; j<DSET_NVOX(pset); ++j) {
2027          if (IN_MASK(cmask, j)) {
2028             MAX_AT_VOX(pset, j, imax, max);
2029             p[j] = keys[imax];
2030          } else {
2031             p[j]=0;
2032          }
2033       }
2034    }else {
2035       for (j=0; j<DSET_NVOX(pset); ++j) {
2036          if (IN_MASK(cmask, j)) {
2037             MAX_SC_AT_VOX(pset, j, imax, max);
2038             p[j] = keys[imax];
2039          } else {
2040             p[j]=0;
2041          }
2042       }
2043    }
2044 
2045    if (!SUMA_SetDsetLabeltable(cset, label, N_label, keys)) {
2046       SUMA_S_Err("Failed to set labeltable");
2047       SUMA_RETURN(0);
2048    }
2049    EDIT_BRICK_LABEL(cset,0,"maxprob_labels");
2050 
2051    SUMA_RETURN(1);
2052 }
2053 
2054 
2055 /*!
2056    Calculate group mean
2057 */
group_mean(SEG_OPTS * Opt,THD_3dim_dataset * aset,byte * mm,THD_3dim_dataset * pset,int N_cl,double * M_v,int scl)2058 int  group_mean (SEG_OPTS *Opt, THD_3dim_dataset *aset,
2059                  byte *mm, THD_3dim_dataset *pset, int N_cl,
2060                  double *M_v, int scl) {
2061    int i=0, g= 0;
2062    short *a=NULL, *p=NULL;
2063    float bf = 1.0;
2064    char srep[512]={""}, sbuf[64]={""};
2065    double w=0.0;
2066 
2067    ENTRY("group_mean");
2068 
2069    if (pset) p=(short*)DSET_ARRAY(pset,0);
2070    a = (short *)DSET_ARRAY(aset,0);
2071    bf=DSET_BRICK_FACTOR(aset,0);
2072    if (bf == 0.0) bf = 1.0;
2073 
2074    if (!p) {
2075       for (g=0; g<N_cl; ++g) {
2076          M_v[g] = 0.0; w=0.0;
2077          for (i=0; i<DSET_NVOX(aset); ++i) {
2078             if (mm[i] == g+1) {
2079                M_v[g] += a[i]; ++w;
2080             }
2081          }
2082          M_v[g] = M_v[g]/w;
2083          if (scl) M_v[g] = bf * M_v[g];
2084       }
2085    } else {/* weighted avg */
2086       for (g=0; g<N_cl; ++g) {
2087          M_v[g] = 0.0; w=0.0;
2088          for (i=0; i<DSET_NVOX(aset); ++i) {
2089             if (mm[i] == g+1) {
2090                M_v[g] += p[i]*a[i]; w=w+p[i];
2091             }
2092          }
2093          M_v[g] = M_v[g]/w;
2094          if (scl) M_v[g] = bf * M_v[g];
2095       }
2096    }
2097 
2098    if (Opt->debug > 1) {
2099       for (g=0; g<N_cl; ++g) {
2100          if (scl) sprintf(sbuf,"%d -- %f , (%f)  ",
2101                            g+1, M_v[g], M_v[g]/bf);
2102          else sprintf(sbuf,"%d -- %f , (%f)  ",
2103                            g+1, M_v[g]*bf, M_v[g]);
2104 
2105          SUMA_strncat(srep, sbuf, 510);
2106       }
2107       INFO_message("%s group means brick scaled , (unscaled): %s\n",
2108                   p ? "p-weighted" : "uniform-weight",
2109                   srep);
2110    }
2111 
2112    RETURN(1);
2113 }
2114 
2115 
SUMA_ijk_to1(int i,int j,int k,int ni,int nij)2116 int SUMA_ijk_to1 (int i, int j, int k, int ni, int nij) {
2117    return(i+j*ni+k*nij);
2118 }
SUMA_jik_to1(int j,int i,int k,int ni,int nij)2119 int SUMA_jik_to1 (int j, int i, int k, int ni, int nij) {
2120    return(i+j*ni+k*nij);
2121 }
SUMA_kji_to1(int k,int j,int i,int ni,int nij)2122 int SUMA_kji_to1 (int k, int j, int i, int ni, int nij) {
2123    return(i+j*ni+k*nij);
2124 }
2125 
2126 /*! Find and fill holes in volume.
2127 The holes are defined as zero voxels that are
2128 surrounded with non-zero ones in at least one of the
2129 three directions.
2130 This function is relatively slow, use SUMA_mri_volume_infill_zoom instead.
2131 */
SUMA_mri_volume_infill(MRI_IMAGE * imin)2132 int SUMA_mri_volume_infill(MRI_IMAGE *imin)
2133 {
2134    static char FuncName[]={"SUMA_mri_volume_infill"};
2135    int Ni, Nj, Nk, Nij, Nijk, i, j, k, v, iter;
2136    int d, d1b, d1e, d1, d2b, d2e, d2, d3b, d3e, d3;
2137    int Nfills, trg, cnd;
2138    float cndval, aprev;
2139    byte *ba=NULL;
2140    float *fa=NULL, *sum=NULL;
2141    byte *hits = NULL;
2142    int minhits = 0;
2143    int maxiter=500;
2144    int (*I3_to_1)(int, int, int, int, int);
2145 
2146    SUMA_Boolean LocalHead = NOPE;
2147 
2148    SUMA_ENTRY;
2149 
2150    Ni = imin->nx; Nj = imin->ny; Nk = imin->nz;
2151    Nij = Ni*Nj; Nijk = Nij*Nk;
2152 
2153    fa = MRI_FLOAT_PTR(imin);
2154 
2155    /* make byte mask */
2156    ba = (byte *)SUMA_calloc(Nijk, sizeof(byte));
2157    for (v=0; v<Nijk; ++v) {
2158       if (SUMA_ABS(fa[v]-0.0f)>0.00001) ba[v] = 1;
2159    }
2160    sum = (float *)SUMA_calloc(Nijk, sizeof(float));
2161    hits = (byte *)SUMA_calloc(Nijk, sizeof(byte));
2162 
2163    iter = 0;
2164    do {
2165       for (d=0; d<6; ++d) { /* for each direction */
2166          switch (d) {
2167             case 0:
2168                d1b =  0;  d1e = Ni;  d1 = +1;
2169                d2b =  0;  d2e = Nj;  d2 = +1;
2170                d3b =  0;  d3e = Nk;  d3 = +1;
2171                I3_to_1 = &SUMA_ijk_to1;
2172                break;
2173             case 1:
2174                d1b = Ni;  d1e =  0;  d1 = -1;
2175                d2b =  0;  d2e = Nj;  d2 = +1;
2176                d3b =  0;  d3e = Nk;  d3 = +1;
2177                I3_to_1 = &SUMA_ijk_to1;
2178                break;
2179             case 2:
2180                d1b =  0;  d1e = Nj;  d1 = +1;
2181                d2b =  0;  d2e = Ni;  d2 = +1;
2182                d3b =  0;  d3e = Nk;  d3 = +1;
2183                I3_to_1 = &SUMA_jik_to1;
2184                break;
2185             case 3:
2186                d1b = Nj;  d1e =  0;  d1 = -1;
2187                d2b =  0;  d2e = Ni;  d2 = +1;
2188                d3b =  0;  d3e = Nk;  d3 = +1;
2189                I3_to_1 = &SUMA_jik_to1;
2190                break;
2191             case 4:
2192                d1b =  0;  d1e = Nk;  d1 = +1;
2193                d2b =  0;  d2e = Nj;  d2 = +1;
2194                d3b =  0;  d3e = Ni;  d3 = +1;
2195                I3_to_1 = &SUMA_kji_to1;
2196                break;
2197             case 5:
2198                d1b = Nk;  d1e =  0;  d1 = -1;
2199                d2b =  0;  d2e = Nj;  d2 = +1;
2200                d3b =  0;  d3e = Ni;  d3 = +1;
2201                I3_to_1 = &SUMA_kji_to1;
2202                break;
2203             default:
2204                SUMA_S_Err("palinmoment");
2205                SUMA_RETURN(0);
2206 
2207          }
2208          for (k=d3b; k<d3e; k = k+d3) {
2209             for (j=d2b; j<d2e; j = j+d2) {
2210                trg = -1; cnd = -1; cndval = 0; aprev = 0;
2211                for (i=d1b; i<d1e; i = i+d1) {
2212                   v = I3_to_1(i,j,k, Ni, Nij);
2213                   if (trg < 0 && ba[v]) {
2214                      trg = 0; /* you're in, target holes*/
2215                   } else if (trg == 0 && !ba[v]) {
2216                      /* found a candidate on the edge */
2217                      if (cnd >= 0) { SUMA_S_Err("SHOULD NOT BE"); };
2218                      cnd = v; cndval = aprev; trg = 1; /* next seek non zero */
2219                   } else if (trg == 1 && ba[v]) {
2220                      if (cnd >= 0) { /* have candidate with data after it */
2221                         sum[cnd] += cndval; /* add candidate value */
2222                         hits[cnd] += 1;
2223                      }
2224                      cnd = -1; cndval = 0;
2225                      trg = 0; /* next we seek another hole */
2226                   }
2227                   aprev = fa[v];
2228                }
2229             }
2230          }
2231 
2232       }/* for each direction */
2233       /* Now put the candidates back into fa */
2234       Nfills = 0;
2235       for (v=0; v<Nijk; ++v) {
2236          if (hits[v]) {
2237                /* the next three statements used to be conditioned
2238                on hits[v] >= minhits, but minhits can not be used
2239                to control which kind of holes get filled. So it
2240                is kind of useless */
2241                fa[v] = sum[v]/(float)hits[v];
2242                ba[v] = 1;
2243                ++Nfills;
2244             /* reset for next loop */
2245             sum[v] = 0.0; hits[v] = 0;
2246          }
2247       }
2248       ++iter;
2249       SUMA_LHv("Iter %d, N_fills  = %d\n", iter, Nfills);
2250    } while (Nfills > 0 && iter < maxiter);
2251 
2252    if (Nfills > 0) {
2253       SUMA_S_Warnv("Function stopped because of maximum iter limit of %d. "
2254                    "Holes may still exist.", maxiter);
2255    }
2256    SUMA_ifree(hits); SUMA_ifree(sum); SUMA_ifree(ba);
2257 
2258    SUMA_RETURN(1);
2259 }
2260 
2261 /*!
2262    Trace rays from voxel n1D in +i and -i directions.
2263    Return the first non zero value (ta) encountered and the number of
2264    voxels traveled (da) to get to it.
2265    Results are stored in ta[0] and da[0] for +ve direction
2266    and ta[1] and da[1] for -ve direction.
2267 */
SUMA_ray_i(int n1D,int ni,int nij,float * av,byte * ba,float ta[],int da[])2268 int SUMA_ray_i(int n1D, int ni, int nij, float *av, byte *ba,
2269                float ta[], int da[])
2270 {
2271    static char FuncName[]={"SUMA_ray_i"};
2272    int IJK[3], ii, t1D;
2273    int hitcode;
2274 
2275    SUMA_ENTRY;
2276 
2277    ta[0] = ta[1] = 0.0;
2278    da[0] = da[1] = 0;
2279    hitcode = 0;
2280 
2281    Vox1D2Vox3D(n1D, ni, nij, IJK)
2282 
2283    /* shoot ray in +ve direction */
2284    ii = IJK[0]; t1D = n1D;
2285    if (ii < ni && !ba[t1D]) {
2286       do {
2287          ++ii; ++t1D;
2288       } while (ii < ni && !ba[t1D]);
2289       if (ii < ni) {
2290          ta[0] = av[t1D];  /* The value at the non zero in +ve i*/
2291          da[0] = ii-IJK[0];/* The number of voxels needed to get there*/
2292          hitcode = hitcode + SUMA_IposBOUND;
2293       }
2294    }
2295 
2296    /* shoot ray in -ve direction */
2297    ii = IJK[0]; t1D = n1D;
2298    if (-1 < ii && !ba[t1D]) {
2299       do {
2300          --ii; --t1D;
2301       } while (-1 < ii && !ba[t1D]);
2302       if (-1 < ii) {
2303          ta[1] = av[t1D];  /* The value at the non zero in -ve i*/
2304          da[1] = IJK[0]-ii;/* The number of voxels needed to get there*/
2305          hitcode = hitcode + SUMA_InegBOUND;
2306       }
2307    }
2308 
2309    SUMA_RETURN(hitcode);
2310 }
2311 
2312 /*!
2313    Trace rays from voxel n1D in +j and -j directions.
2314    Return the first non zero value (ta) encountered and the number of
2315    voxels traveled (da) to get to it.
2316    Results are stored in ta[0] and da[0] for +ve direction
2317    and ta[1] and da[1] for -ve direction.
2318 */
SUMA_ray_j(int n1D,int ni,int nij,int nj,float * av,byte * ba,float ta[],int da[])2319 int SUMA_ray_j(int n1D, int ni, int nij, int nj, float *av, byte *ba,
2320                float ta[], int da[])
2321 {
2322    static char FuncName[]={"SUMA_ray_j"};
2323    int IJK[3], jj, t1D;
2324    int hitcode;
2325 
2326    SUMA_ENTRY;
2327 
2328    ta[0] = ta[1] = 0.0;
2329    da[0] = da[1] = 0;
2330    hitcode = 0;
2331 
2332    Vox1D2Vox3D(n1D, ni, nij, IJK)
2333 
2334    /* shoot ray in +ve direction */
2335    jj = IJK[1]; t1D = n1D;
2336    if (jj < nj && !ba[t1D]) {
2337       do {
2338          ++jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2339       } while (jj < nj && !ba[t1D]);
2340       if (jj < nj) {
2341          ta[0] = av[t1D];  /* The value at the non zero in +ve j*/
2342          da[0] = jj-IJK[1];/* The number of voxels needed to get there*/
2343          hitcode = hitcode + SUMA_JposBOUND;
2344       }
2345    }
2346 
2347    /* shoot ray in -ve direction */
2348    jj = IJK[1]; t1D = n1D;
2349    if (-1 < jj && !ba[t1D]) {
2350       do {
2351          --jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2352       } while (-1 < jj && !ba[t1D]);
2353       if (-1 < jj) {
2354          ta[1] = av[t1D];  /* The value at the non zero in -ve j*/
2355          da[1] = IJK[1]-jj;/* The number of voxels needed to get there*/
2356          hitcode = hitcode + SUMA_JnegBOUND;
2357       }
2358    }
2359 
2360    SUMA_RETURN(hitcode);
2361 }
2362 
2363 /*!
2364    Trace rays from voxel n1D in +k and -k directions.
2365    Return the first non zero value (ta) encountered and the number of
2366    voxels traveled (da) to get to it.
2367    Results are stored in ta[0] and da[0] for +ve direction
2368    and ta[1] and da[1] for -ve direction.
2369 */
SUMA_ray_k(int n1D,int ni,int nij,int nk,float * av,byte * ba,float ta[],int da[])2370 int SUMA_ray_k(int n1D, int ni, int nij, int nk, float *av, byte *ba,
2371                float ta[], int da[])
2372 {
2373    static char FuncName[]={"SUMA_ray_k"};
2374    int IJK[3], kk, t1D;
2375    int hitcode;
2376 
2377    SUMA_ENTRY;
2378 
2379    ta[0] = ta[1] = 0.0;
2380    da[0] = da[1] = 0;
2381    hitcode = 0;
2382 
2383    Vox1D2Vox3D(n1D, ni, nij, IJK);
2384 
2385    /* shoot ray in +ve direction */
2386    kk = IJK[2]; t1D = n1D;
2387    if (kk < nk && !ba[t1D]) {
2388       do {
2389          ++kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2390       } while (kk < nk && !ba[t1D]);
2391       if (kk < nk) {
2392          ta[0] = av[t1D];  /* The value at the non zero in +ve k*/
2393          da[0] = kk-IJK[2];/* The number of voxels needed to get there*/
2394          hitcode = hitcode + SUMA_kposBOUND;
2395       }
2396    }
2397 
2398    /* shoot ray in -ve direction */
2399    kk = IJK[2]; t1D = n1D;
2400    if (-1 < kk && !ba[t1D]) {
2401       do {
2402          --kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2403       } while (-1 < kk && !ba[t1D]);
2404       if (-1 < kk) {
2405          ta[1] = av[t1D];  /* The value at the non zero in -ve j*/
2406          da[1] = IJK[2]-kk;/* The number of voxels needed to get there*/
2407          hitcode = hitcode + SUMA_knegBOUND;
2408       }
2409    }
2410 
2411    SUMA_RETURN(hitcode);
2412 }
2413 
2414 /*!
2415    Trace rays from voxel n1D in +i and -i directions.
2416    Count the number of non-zero hits on either side
2417    Starting voxel must be zero
2418    if (side == -1) zero out side with lesser number of non-zeros
2419    if (side == 0) zero out IposBound side
2420    if (side == 1) zero out InegBound side
2421    if (side == 2) zero out both sides
2422 */
SUMA_ray_unplug_i(int n1D,int ni,int nij,float * av,byte * ba,int side)2423 int SUMA_ray_unplug_i(int n1D, int ni, int nij,
2424                       float *av, byte *ba, int side)
2425 {
2426    static char FuncName[]={"SUMA_ray_unplug_i"};
2427    int IJK[3], ii, t1D, nzpos=0,nzneg=0, nrm=0;
2428 
2429    SUMA_ENTRY;
2430 
2431    Vox1D2Vox3D(n1D, ni, nij, IJK)
2432 
2433    if (side != 1) {
2434       /* shoot ray in +ve direction */
2435       ii = IJK[0]; t1D = n1D;
2436       if (ii < ni && !ba[t1D]) {
2437          do {
2438             if (ba[t1D]) ++nzpos;
2439             ++ii; ++t1D;
2440          } while (ii < ni);
2441       }
2442    }
2443 
2444    if (side != 0) {
2445       /* shoot ray in -ve direction */
2446       ii = IJK[0]; t1D = n1D;
2447       if (-1 < ii && !ba[t1D]) {
2448          do {
2449             if (ba[t1D]) ++nzneg;
2450             --ii; --t1D;
2451          } while (-1 < ii);
2452       }
2453    }
2454 
2455    /* unplug */
2456    if (side == -1) {
2457       if (nzpos >= nzneg) side = 1;
2458       else side = 0;
2459    }
2460    if ((side == 0 || side == 2) && nzpos) {
2461       ii = IJK[0]; t1D = n1D;
2462       while (ii < ni) {
2463          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2464          ++ii; ++t1D;
2465       }
2466    }
2467    if ((side == 1 || side == 2) && nzneg) {
2468       ii = IJK[0]; t1D = n1D;
2469       while (-1 < ii) {
2470          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2471          --ii; --t1D;
2472       }
2473    }
2474 
2475    SUMA_RETURN(nrm);
2476 }
2477 
2478 /*
2479    See SUMA_ray_unplug_i
2480 */
SUMA_ray_unplug_j(int n1D,int ni,int nij,int nj,float * av,byte * ba,int side)2481 int SUMA_ray_unplug_j(int n1D, int ni, int nij, int nj,
2482                       float *av, byte *ba, int side)
2483 {
2484    static char FuncName[]={"SUMA_ray_unplug_j"};
2485    int IJK[3], jj, t1D, nzpos=0,nzneg=0,nrm=0;
2486    SUMA_Boolean LocalHead = NOPE;
2487 
2488    SUMA_ENTRY;
2489 
2490    Vox1D2Vox3D(n1D, ni, nij, IJK)
2491    SUMA_LH("Vox %d [%d %d %d], side %d",
2492            n1D, IJK[0], IJK[1], IJK[2], side);
2493 
2494    if (side != 1) {
2495       /* shoot ray in +ve direction */
2496       jj = IJK[1]; t1D = n1D;
2497       if (jj < nj && !ba[t1D]) {
2498          do {
2499             if (ba[t1D]) ++nzpos;
2500             ++jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2501          } while (jj < nj);
2502       }
2503    }
2504 
2505    if (side != 0) {
2506       /* shoot ray in -ve direction */
2507       jj = IJK[1]; t1D = n1D;
2508       if (-1 < jj && !ba[t1D]) {
2509          do {
2510             if (ba[t1D]) ++nzneg;
2511             --jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2512          } while (-1 < jj);
2513       }
2514    }
2515 
2516    /* unplug */
2517    if (side == -1) {
2518       if (nzpos >= nzneg) side = 1;
2519       else side = 0;
2520    }
2521    SUMA_LH("nzpos=%d, nzneg=%d, side now %d",
2522             nzpos, nzneg, side);
2523 
2524    if ((side == 0 || side == 2) && nzpos) {
2525       jj = IJK[1]; t1D = n1D;
2526       while (jj < nj) {
2527          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2528          ++jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2529       }
2530    }
2531    if ((side == 1 || side == 2) && nzneg) {
2532       jj = IJK[1]; t1D = n1D;
2533       while (-1 < jj) {
2534          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2535          --jj; t1D = IJK[0]+jj*ni+IJK[2]*nij;
2536       }
2537    }
2538 
2539    SUMA_RETURN(nrm);
2540 }
2541 /*
2542    See SUMA_ray_unplug_i
2543 */
SUMA_ray_unplug_k(int n1D,int ni,int nij,int nk,float * av,byte * ba,int side)2544 int SUMA_ray_unplug_k(int n1D, int ni, int nij, int nk,
2545                       float *av, byte *ba, int side)
2546 {
2547    static char FuncName[]={"SUMA_ray_unplug_k"};
2548    int IJK[3], kk, t1D, nzpos=0, nzneg=0, nrm=0;
2549 
2550    SUMA_ENTRY;
2551 
2552    Vox1D2Vox3D(n1D, ni, nij, IJK)
2553 
2554    if (side != 1) {
2555       /* shoot ray in +ve direction */
2556       kk = IJK[2]; t1D = n1D;
2557       if (kk < nk && !ba[t1D]) {
2558          do {
2559             if (ba[t1D]) ++nzpos;
2560             ++kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2561          } while (kk < nk);
2562       }
2563    }
2564 
2565    if (side != 0) {
2566       /* shoot ray in -ve direction */
2567       kk = IJK[2]; t1D = n1D;
2568       if (-1 < kk && !ba[t1D]) {
2569          do {
2570             if (ba[t1D]) ++nzneg;
2571             --kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2572          } while (-1 < kk);
2573       }
2574    }
2575 
2576    /* unplug */
2577    if (side == -1) {
2578       if (nzpos >= nzneg) side = 1;
2579       else side = 0;
2580    }
2581    if ((side == 0 || side == 2) && nzpos) {
2582       kk = IJK[2]; t1D = n1D;
2583       while (kk < nk) {
2584          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2585          ++kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2586       }
2587    }
2588    if ((side == 1 || side == 2) && nzneg) {
2589       kk = IJK[2]; t1D = n1D;
2590       while (-1 < kk) {
2591          if (ba[t1D]) { av[t1D] = 0.0f; ++nrm; }
2592          --kk; t1D = IJK[0]+IJK[1]*ni+kk*nij;
2593       }
2594    }
2595 
2596    SUMA_RETURN(nrm);
2597 }
2598 
2599 /*! Find a vole in a volume. A hole voxel is a voxel
2600     with 0 value that is surrounded in at least one
2601     dimension by non zero voxels. */
SUMA_find_hole_voxels(int Ni,int Nj,int Nk,float * fa,byte * ba,int * holeat)2602 int SUMA_find_hole_voxels( int Ni, int Nj, int Nk,
2603                            float *fa, byte *ba, int *holeat)
2604 {
2605    static char FuncName[]={"SUMA_find_hole_voxels"};
2606    int nh=0, v, Nij, Nijk;
2607    int da[2];
2608    float ta[2];
2609 
2610    SUMA_ENTRY;
2611 
2612    Nij = Ni*Nj; Nijk = Nij*Nk;
2613 
2614    nh = 0;
2615    for (v=0; v<Nijk; ++v) {
2616       if (ba[v]) continue; /* not a hole! */
2617       if (SUMA_ray_i(v, Ni, Nij, fa, ba, ta, da)
2618                      == SUMA_I_HOLE) {
2619          holeat[nh] = v; ++nh;
2620          continue;
2621       } else if (SUMA_ray_j(v, Ni, Nij, Nj, fa, ba, ta, da)
2622                      == SUMA_J_HOLE) {
2623          holeat[nh] = v; ++nh;
2624          continue;
2625       } else if (SUMA_ray_k(v, Ni, Nij, Nk, fa, ba, ta, da)
2626                      == SUMA_K_HOLE) {
2627          holeat[nh] = v; ++nh;
2628          continue;
2629       }
2630    }
2631 
2632    SUMA_RETURN(nh);
2633 }
2634 
2635 /*!
2636    Record or return number of hits.
2637    a (int) action flag.
2638       0- Free temps.
2639       1- Add hit key
2640       2- Return hits of key
2641       3- Return key of max hits
2642       4- Reset hit record
2643 
2644 */
hits_rec(int a,int key)2645 int hits_rec(int a, int key)
2646 {
2647    static int *keys=NULL;
2648    static int *hits=NULL;
2649    static int n_alloc=0, n=0;
2650    int mk = 0, mh = 0, i=0;
2651 
2652    if (a==1) { /* add hit */
2653       if (n>=n_alloc) {
2654          n_alloc += 100;
2655          keys = (int *)realloc(keys, n_alloc*sizeof(int));
2656          hits = (int *)realloc(hits, n_alloc*sizeof(int));
2657       }
2658       while (i<n && keys[i]!=key) ++i;
2659       if (i==n) { keys[i]=key; hits[i]=1; ++n;}
2660       else { ++hits[i]; }
2661    } else if (a==2) { /* return hits of key */
2662       for (i=0; i<n; ++i) {
2663          if (keys[i] == key) { return(hits[i]); }
2664       }
2665       return(-1);
2666    } else if (a==3 && n) { /* return key of max hits */
2667       mk = keys[0]; mh = hits[0];
2668       for (i=0; i<n; ++i) {
2669          if (hits[i] > mh) { mh = hits[i]; mk = keys[i]; }
2670       }
2671       return(mk);
2672    } else if (a==4) { /* reset */
2673       n = 0;
2674    } else if (a==0) { /* free */
2675       if (keys) free(keys); keys=NULL;
2676       if (hits) free(hits); hits=NULL;
2677       n = 0;
2678       n_alloc = 0;
2679    }
2680 
2681    return(1);
2682 
2683 }
2684 /*!   A faster version of SUMA_mri_volume_infill
2685       linfill == 1 rescans volume for holes with each new iteration
2686                    Cautious approach, but wastes a lot of time.
2687               == 0 rescans volume for holes once all existing
2688                    holes are filled. Faster, but slightly
2689                    different model.
2690          Relatively few voxels are affected by linfill
2691 */
2692 
SUMA_mri_volume_infill_zoom(MRI_IMAGE * imin,byte linfill,int integ,int umaxiter)2693 int SUMA_mri_volume_infill_zoom(MRI_IMAGE *imin, byte linfill,
2694                                 int integ, int umaxiter)
2695 {
2696    static char FuncName[]={"SUMA_mri_volume_infill_zoom"};
2697    int Ni, Nj, Nk, Nij, Nijk, v, iter;
2698    int Nfills, h, nh, hitcode, hitsum, da[2];
2699    byte *ba=NULL, *nhits=NULL;
2700    float *fa=NULL, ta[2];
2701    float *sum=NULL;
2702    int *holeat = NULL, *intar=NULL;
2703    int maxiter= 500;
2704    SUMA_Boolean LocalHead = NOPE;
2705 
2706    SUMA_ENTRY;
2707 
2708    if (umaxiter > 0) maxiter = umaxiter;
2709 
2710    if (integ != 0 && integ != 1) {
2711       SUMA_S_Warnv("Function only accepts integ of 0 or 1. \n"
2712                    "Received %d but will proceed with 0.\n",
2713                    integ);
2714       integ = 0;
2715    }
2716 
2717    Ni = imin->nx; Nj = imin->ny; Nk = imin->nz;
2718    Nij = Ni*Nj; Nijk = Nij*Nk;
2719 
2720    fa = MRI_FLOAT_PTR(imin);
2721 
2722    /* make byte mask */
2723    ba = (byte *)SUMA_calloc(Nijk, sizeof(byte));
2724    for (v=0; v<Nijk; ++v) {
2725       if (SUMA_ABS(fa[v]-0.0f)>0.00001) ba[v] = 1;
2726    }
2727    holeat = (int *)SUMA_calloc(Nijk, sizeof(int));
2728    sum = (float *)SUMA_calloc(Nijk, sizeof(float));
2729    nhits = (byte *)SUMA_calloc(Nijk, sizeof(byte));
2730    if (integ) intar  = (int *)SUMA_calloc(Nijk, sizeof(int));
2731 
2732    if (integ) hits_rec(0, 0); /* clean start */
2733 
2734    iter = 0; nh = 0;
2735    do {
2736       if (linfill || !iter) {
2737                /* To ensure that the final volume has no holes in it,
2738                you must rescan the whole volume, as opposed to the one
2739                pass scan performed only at iteration 0.
2740                Two ways to handle this problem. Either repeat a full scan
2741                with each iteration (linfill = 1) and waste a lot of time.
2742                With linfill = 1, the function is as slow as the older
2743                version SUMA_mri_volume_infill.
2744                Alternately (linfill = 0), we do a new scan once the first set
2745                of holes are filled. That's much faster.
2746                The two approaches are not equivalent,
2747                but very few voxels are affected */
2748 
2749          /* find all hole points */
2750          nh = SUMA_find_hole_voxels(Ni, Nj, Nk, fa, ba, holeat);
2751       }
2752 
2753       Nfills = 0;
2754       for (h=0; h<nh; ++h) {
2755          hitcode = 0; sum[holeat[h]] = 0.0; nhits[holeat[h]]=0;
2756          hitsum=0;
2757          if ( (hitcode = SUMA_ray_i(holeat[h], Ni, Nij, fa, ba, ta, da)) ==
2758                SUMA_I_HOLE) {
2759             hitsum += hitcode;
2760             if (da[0] == 1) { /* only process holes at edge */
2761                sum[holeat[h]] += ta[0]; /* add value at edge voxel */
2762                if (integ) hits_rec(1, (int)ta[0]);
2763                ++nhits[holeat[h]];
2764             }
2765             if (da[1] == 1) { /* only process holes at edge */
2766                sum[holeat[h]] += ta[1]; /* add value at edge voxel */
2767                if (integ) hits_rec(1, (int)ta[1]);
2768                ++nhits[holeat[h]];
2769             }
2770          }
2771          #if 0
2772             if (holeat[h] == 1007828) {
2773                SUMA_S_Notev("Vox %d, iter %d, nhit=%d, hitsum=%d\n"
2774                             "iRay da=[%d %d], ta=[%f %f]\n",
2775                      holeat[h], iter, nhits[holeat[h]], hitcode,
2776                      da[0], da[1], ta[0], ta[1]);
2777             }
2778          #endif
2779          if ( (hitcode = SUMA_ray_j(holeat[h], Ni, Nij, Nj, fa, ba, ta, da)) ==
2780                SUMA_J_HOLE) {
2781             hitsum += hitcode;
2782             if (da[0] == 1) { /* only process holes at edge */
2783                sum[holeat[h]] += ta[0]; /* add value at edge voxel */
2784                if (integ) hits_rec(1, (int)ta[0]);
2785                ++nhits[holeat[h]];
2786             }
2787             if (da[1] == 1) { /* only process holes at edge */
2788                sum[holeat[h]] += ta[1]; /* add value at edge voxel */
2789                if (integ) hits_rec(1, (int)ta[1]);
2790                ++nhits[holeat[h]];
2791             }
2792          }
2793          if ( (hitcode = SUMA_ray_k(holeat[h], Ni, Nij, Nk, fa, ba, ta, da)) ==
2794                SUMA_K_HOLE) {
2795             hitsum += hitcode;
2796             if (da[0] == 1) { /* only process holes at edge */
2797                sum[holeat[h]] += ta[0]; /* add value at edge voxel */
2798                if (integ) hits_rec(1, (int)ta[0]);
2799                ++nhits[holeat[h]];
2800             }
2801             if (da[1] == 1) { /* only process holes at edge */
2802                sum[holeat[h]] += ta[1]; /* add value at edge voxel */
2803                if (integ) hits_rec(1, (int)ta[1]);
2804                ++nhits[holeat[h]];
2805             }
2806          }
2807          if (integ) {
2808             if (nhits[holeat[h]]) intar[holeat[h]] = hits_rec(3,0);
2809             hits_rec(4,0); /* reset */
2810          }
2811 
2812       }
2813       /* now update holeat array */
2814       h = 0;
2815       while (nh > 0 && h<nh) {
2816          if (nhits[holeat[h]]) {
2817             if (integ) {
2818                fa[holeat[h]] = (float)intar[holeat[h]]; /* get most freq. key */
2819             } else {
2820                fa[holeat[h]] =
2821                   sum[holeat[h]]/(float)nhits[holeat[h]]; /* assign new value */
2822             }
2823             ba[holeat[h]] = 1;   /* mark as filled */
2824 
2825             nhits[holeat[h]] = 0;   /* reset */
2826             sum[holeat[h]] = 0;
2827 
2828             ++Nfills;   /* keep track of numbers filled  */
2829 
2830             holeat[h] = holeat[nh-1]; /* reduce list of holes */
2831             --nh;
2832          } else {
2833             ++h;
2834          }
2835       }
2836       ++iter;
2837       SUMA_LHv("Iter %d, N_fills = %d, %d holes remain\n", iter, Nfills, nh);
2838       if (nh == 0 && iter < maxiter && !linfill) {
2839          /* if nh, rerun full scan to ensure no new voxels qualify as holes.
2840             This is not necessary if linfill is used because a rescan is carried
2841             out with each iteration */
2842          nh = SUMA_find_hole_voxels(Ni, Nj, Nk, fa, ba, holeat);
2843          if (nh) {
2844             SUMA_LHv("%d more holes in patched version, one more pass\n", nh);
2845          }
2846       }
2847    } while (nh > 0 && iter < maxiter);
2848 
2849    if (nh > 0) {
2850       SUMA_S_Warnv("Function stopped because of maximum iter limit of %d. "
2851                    "%d holes still exist." , maxiter ,nh);
2852    }
2853 
2854    SUMA_ifree(holeat); SUMA_ifree(ba);
2855    hits_rec(0,0); /* cleanup */
2856 
2857    SUMA_RETURN(1);
2858 }
2859 
2860 /*!
2861    A brutish filler function, filling interpolation is crude
2862 
2863    imin is the volume to be filled
2864    minhits is the minimum number of non zero hits along outward going
2865            cardinal rays from the zero voxel.
2866            1 is the most liberal == sandwiched along one direction
2867            2 is the middle of the road == sandwiched between slices
2868            3 is the most conservative == sandwiched from all sides
2869            -1 == 1
2870    Nitermax: 1 --> Go through once, don't verify that nothing else can
2871              still be filled. You can't tell that nothing else can
2872              be filled until you iterate again and fail to fill anything.
2873              -1 --> 10
2874    unholize: Edit the volume after initial convergence so that no holes
2875              with nhits < minhits remain. Not a very useful thing to do
2876              unless made iterative also. Keeping it here for the record
2877              but best not use it.
2878    mask: If not null, only consider voxels to fill if they fall within
2879          this mask
2880 */
2881 
SUMA_mri_volume_infill_solid(MRI_IMAGE * imin,int minhits,int Nitermax,int unholize,byte * mask)2882 int SUMA_mri_volume_infill_solid(MRI_IMAGE *imin, int minhits,
2883                                  int Nitermax, int unholize,
2884                                  byte *mask)
2885 {
2886    static char FuncName[]={"SUMA_mri_volume_infill_solid"};
2887    int Ni, Nj, Nk, Nij, Nijk, v, niter=0, N_filled=0;
2888    int hitcode, hitsum, da[2];
2889    byte *ba=NULL, *lesserhole = NULL;
2890    float *fa=NULL, *fan=NULL, ta[2];
2891    float  sI, sK, sJ, nhits=0.0;
2892    SUMA_Boolean LocalHead = NOPE;
2893 
2894    SUMA_ENTRY;
2895 
2896    SUMA_LH("minhits = %d, Nitermax = %d, Unholize = %d\n",
2897            minhits, Nitermax, unholize);
2898 
2899    if (minhits <= 0) minhits = 1;
2900    if (Nitermax < 0) Nitermax = 10;
2901    Ni = imin->nx; Nj = imin->ny; Nk = imin->nz;
2902    Nij = Ni*Nj; Nijk = Nij*Nk;
2903 
2904    fa = MRI_FLOAT_PTR(imin);
2905    fan = (float *)SUMA_calloc(Nijk, sizeof(float));
2906 
2907    /* make byte mask, 1 where there is value, 0 otherwise */
2908    ba = (byte *)SUMA_calloc(Nijk, sizeof(byte));
2909    for (v=0; v<Nijk; ++v) {
2910       if (SUMA_ABS(fa[v]-0.0f)>0.00001) ba[v] = 1;
2911    }
2912    if (unholize) lesserhole = (byte *)SUMA_malloc(Nijk*sizeof(byte));
2913 
2914    do {
2915       SUMA_LH("iteration %d",niter);
2916       if (unholize) memset(lesserhole, 0, Nijk*sizeof(byte));
2917       for (v=0; v<Nijk; ++v) {
2918          if (ba[v]) continue; /* not a hole */
2919          if (mask && !mask[v]) continue; /* do not consider */
2920          hitcode = 0; nhits=0.0;
2921          hitsum=0; sI=0.0; sJ=0.0; sK=0.0;
2922          if ( (hitcode = SUMA_ray_i(v, Ni, Nij, fa, ba, ta, da)) ==
2923                SUMA_I_HOLE) {
2924             hitsum += hitcode;
2925             sI = (ta[0]*da[1]+ta[1]*da[0])/(da[1]+da[0]);
2926             ++nhits;
2927             SUMA_LH("Voxel %d I hole", v);
2928          }
2929          if ( (hitcode = SUMA_ray_j(v, Ni, Nij, Nj, fa, ba, ta, da)) ==
2930                SUMA_J_HOLE) {
2931             hitsum += hitcode;
2932             sJ = (ta[0]*da[1]+ta[1]*da[0])/(da[1]+da[0]);
2933             ++nhits;
2934             SUMA_LH("Voxel %d J hole", v);
2935          }
2936          if ( (hitcode = SUMA_ray_k(v, Ni, Nij, Nk, fa, ba, ta, da)) ==
2937                SUMA_K_HOLE) {
2938             hitsum += hitcode;
2939             sK = (ta[0]*da[1]+ta[1]*da[0])/(da[1]+da[0]);
2940             SUMA_LH("Voxel %d K hole", v);
2941             ++nhits;
2942          }
2943          if (nhits >= minhits) {
2944             fan[v] = (sI + sK + sJ) / nhits;
2945          } else if (unholize && nhits > 0.0) {
2946             lesserhole[v] = hitsum;
2947          }
2948       }
2949 
2950       for (N_filled=0, v=0; v<Nijk; ++v) {
2951          if (!ba[v]) {/* Was hole, fill it? */
2952             if (fan[v] != 0.0f) {
2953                fa[v] = fan[v];
2954                ba[v] = 1;
2955                ++N_filled;
2956             }
2957          }
2958       }
2959 
2960       ++niter;
2961    } while (N_filled && niter < Nitermax);
2962 
2963    if (N_filled && niter == Nitermax) {
2964       SUMA_S_Note("Leaving after %d iterations without ensuring "
2965                   "no fillable holes remain",
2966                   niter);
2967    }
2968 
2969    if (unholize) {
2970       SUMA_LH("Unholizing");
2971       for (N_filled = 0, v=0; v<Nijk; ++v) {
2972          if (lesserhole[v] & SUMA_I_HOLE) {
2973             N_filled += SUMA_ray_unplug_i(v, Ni, Nij, fa, ba, -1);
2974             SUMA_LH("Voxel %d I Hole, %d zeroed out", v,N_filled);
2975          } else if (lesserhole[v] & SUMA_J_HOLE) {
2976             N_filled += SUMA_ray_unplug_j(v, Ni, Nij, Nj, fa, ba, -1);
2977             SUMA_LH("Voxel %d J Hole, %d zeroed out", v,N_filled);
2978          } else if (lesserhole[v] & SUMA_K_HOLE) {
2979             N_filled += SUMA_ray_unplug_k(v, Ni, Nij, Nk, fa, ba, -1);
2980             SUMA_LH("Voxel %d K Hole, %d zeroed out", v,N_filled);
2981          } else if (lesserhole[v]) {
2982             SUMA_LH("lesserhole[%d]=%d\n", v, lesserhole[v]);
2983          }
2984       }
2985       SUMA_S_Note("%d hanging voxels removed", N_filled);
2986    }
2987 
2988    SUMA_ifree(ba); SUMA_ifree(fan); SUMA_ifree(lesserhole);
2989 
2990    SUMA_RETURN(1);
2991 }
2992 
SUMA_VolumeInFill(THD_3dim_dataset * aset,THD_3dim_dataset ** filledp,int method,int integ,int MxIter,int minhits,int erode,int dilate,float val,byte * mask)2993 int SUMA_VolumeInFill(THD_3dim_dataset *aset,
2994                       THD_3dim_dataset **filledp,
2995                       int method, int integ,
2996                       int MxIter, int minhits,
2997                       int erode, int dilate, float val,
2998                       byte *mask)
2999 {
3000    static char FuncName[]={"SUMA_VolumeInFill"};
3001    float *fa=NULL;
3002    int ii=0;
3003    THD_3dim_dataset *filled = *filledp;
3004    MRI_IMAGE *imin=NULL;
3005    SUMA_Boolean LocalHead = NOPE;
3006 
3007    SUMA_ENTRY;
3008 
3009    if (minhits > 0 && method != 2 && method != 3 ) {
3010       SUMA_S_Err("minhits is only useful with method = 2 or 3.\n");
3011    }
3012 
3013    if (integ < 0 && method != 2 && method != 3) { /* figure it out */
3014       if (is_integral_dset(aset,1)) integ = 1;
3015       else integ = 0;
3016    }
3017 
3018    /* get data into float image */
3019    imin = THD_extract_float_brick(0,aset) ;
3020    if (method == 0) { /* slow */
3021       if (!SUMA_mri_volume_infill(imin)) {
3022          SUMA_S_Err("Failed to fill volume");
3023          SUMA_RETURN(0);
3024       }
3025    } else if (method == 1) { /* faster */
3026       if (!SUMA_mri_volume_infill_zoom(imin, 0, integ, MxIter)) {
3027          SUMA_S_Err("Failed to fill volume");
3028          SUMA_RETURN(0);
3029       }
3030    } else if (method == 2 || method == 3){ /* solid */
3031       SUMA_LH("method is %d\n", method);
3032       if (!SUMA_mri_volume_infill_solid(imin, minhits,
3033                                         MxIter, method == 3 ? 1:0,
3034                                         mask)) {
3035          SUMA_S_Err("Failed to fill volume");
3036          SUMA_RETURN(0);
3037       }
3038    }
3039 
3040    /* put results in dset */
3041    fa = MRI_FLOAT_PTR(imin);
3042 
3043    if (erode || dilate) {
3044       byte *b = NULL;
3045       int tt;
3046       if (!(b = (byte *)SUMA_calloc(DSET_NVOX(aset), sizeof(byte)))) {
3047          SUMA_S_Err("Failed to allocate for %d vox!", DSET_NVOX(aset));
3048          SUMA_RETURN(0);
3049       }
3050       for (ii=0; ii<DSET_NVOX(aset); ++ii) if (fa[ii] != 0.0f) b[ii]=1;
3051       if (erode) {
3052          SUMA_S_Note("Eroding %d", erode);
3053          for (tt=0; tt<erode; ++tt) {
3054             THD_mask_erode_sym(DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
3055                                b, 1, 2);
3056          }
3057       }
3058       if (dilate) {
3059          SUMA_S_Note("Dilating %d", dilate);
3060          for (tt=0; tt<dilate; ++tt) {
3061             THD_mask_dilate(DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
3062                                b, 1, 2);
3063          }
3064       }
3065       SUMA_S_Note("Filling with %f", val);
3066       for (ii=0; ii<DSET_NVOX(aset); ++ii)
3067          if (!b[ii] && fa[ii] != 0.0f) fa[ii] = 0.0;
3068          else if (b[ii] && fa[ii] == 0.0f) fa[ii] = val;
3069 
3070 
3071       SUMA_ifree(b);
3072    }
3073 
3074    /* Put result in output dset */
3075    if (!filled) {
3076       filled = EDIT_full_copy(aset, FuncName);
3077       *filledp = filled;
3078    }
3079    EDIT_substscale_brick(  filled, 0, MRI_float, fa,
3080                            DSET_BRICK_TYPE(filled,0), -1.0);
3081    EDIT_BRICK_LABEL(filled,0,"HolesFilled");
3082    if (DSET_BRICK_TYPE(filled,0) == MRI_float) {
3083       mri_clear_and_free(imin);  /* pointer was recycled */
3084    } else {
3085      mri_free(imin);
3086    }
3087    imin = NULL; fa = NULL;
3088    if (integ) { /* copy attributes, if any */
3089       THD_copy_labeltable_atr( filled->dblk,  aset->dblk);
3090    }
3091 
3092    SUMA_RETURN(1);
3093 }
3094 
3095 /*
3096    Extrapolate radially to fill region outside of mask.
3097    For each voxel on dataset perimeter, draw trace to the
3098    center of mass and sample voxels from CM to perimeter voxel.
3099    Search from perimeter down until at least nplug consecutive
3100    voxels are in the mask, then keep sampling until you have
3101    nlin voxels in the mask. Fit a straight line to the nlin
3102    voxels and extrapolate to area outside the mask.
3103 
3104    aset: Volume providing grid, and data at times
3105    ufv: if NULL, use values in 1st sub-brick of aset
3106         otherwise, use values in hfv
3107    ucmask: if NULL, create mask from non zero values in aset
3108            (or ufv), otherwise use values in ucmask to define
3109            data voxels.
3110    ucm: User supplied xyz (RAI) mm coords of center of mass.
3111         If NULL, function computes one from aset
3112    filledp: if NULL, ufv contains results. Otherwise a dataset
3113             is created and returned in filledp
3114    nplug:Minimum number of consecutive voxels that are in the mask.
3115    nlin: Use no more than nlin voxels in mask for the linear fit
3116    smooth: Amount of smoothing done to final result
3117    N_off: Number of centroid offsets. Choose from 1, 5, or 9
3118           The larger the number, the less likely you are to
3119           end up with holes. Usually 9 will fill everything up
3120           except for holes inside the mask. The smoothing function
3121           would paper over the few holes that remain.
3122           The function can be modified in the future to have an
3123           aggressive fill, but that is not necessary at this point.
3124           Holes inside the mask are not handled yet.
3125 */
SUMA_Volume_RadFill_killme(THD_3dim_dataset * aset,float * ufv,byte * ucmask,float * ucm,THD_3dim_dataset ** filledp,int nplug,int nlin,float smooth,int N_off)3126 int SUMA_Volume_RadFill_killme(THD_3dim_dataset *aset, float *ufv, byte *ucmask,
3127                       float *ucm, THD_3dim_dataset **filledp,
3128                       int nplug, int nlin, float smooth, int N_off)
3129 {
3130    static char FuncName[]={"SUMA_Volume_RadFill_killme"};
3131    float *fv=NULL, cm[3], cmo[3], xyz_ijk[3], *vals=NULL, mid,
3132          *wt=NULL, **ref=NULL, *wref=NULL, *wtls, *data, *fvn=NULL;
3133    THD_fvec3 ccc, ncoord;
3134    int ii, jj, kk, nsamp, vv, nn, mm, *ivals=NULL,
3135        off[10][3], ioff, nstrt,
3136        found=0, ijk[3], nref, nok, veclen, ncand, niter,
3137        *Nv=NULL, iim, iiM, jjm, jjM, kkm, kkM, pl;
3138    byte *cmask=NULL, *holi=NULL;
3139    THD_3dim_dataset *filled = *filledp;
3140    MRI_IMAGE *imin=NULL;
3141    SUMA_Boolean LocalHead = NOPE;
3142 
3143    SUMA_ENTRY;
3144 
3145    if (!aset) {
3146       SUMA_S_Err("Need input dataset");
3147       SUMA_RETURN(NOPE);
3148    }
3149    if (!ufv && !filledp) {
3150       SUMA_S_Err("No way to return results");
3151       SUMA_RETURN(NOPE);
3152    }
3153    if (N_off == -1) N_off = 9;
3154    if (N_off != 1 && N_off != 5 && N_off != 9) {
3155       SUMA_S_Errv("N_off (%d) must be one of 1,5, or 9\n", N_off);
3156       SUMA_RETURN(NOPE);
3157    }
3158 
3159    /* If no float vector given get one */
3160    if (!ufv) {
3161       if (!(fv = THD_extract_to_float(0,aset))) {
3162          SUMA_S_Err("Failed to extract float");
3163          SUMA_RETURN(0);
3164       }
3165    } else {
3166       fv = ufv;
3167    }
3168 
3169    fvn = (float *)SUMA_malloc(sizeof(float)*DSET_NVOX(aset));
3170    memcpy(fvn, fv, sizeof(float)*DSET_NVOX(aset));
3171 
3172    /* If no mask is given make one */
3173    if (!ucmask) {
3174       cmask = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
3175       for (vv=0; vv<DSET_NVOX(aset); ++vv) {
3176          if (!fv[vv]) cmask[vv]=0; else cmask[vv]=1;
3177       }
3178    } else {
3179       cmask = ucmask;
3180    }
3181    /* a vector to flag the holiness of a voxel */
3182    holi = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
3183    for (vv=0; vv<DSET_NVOX(aset); ++vv) {
3184       if (cmask[vv]) holi[vv] = 1; /* in mask */
3185       else holi[vv] = 0;
3186    }
3187 
3188    /* center of mass */
3189    if (!ucm) {
3190       ccc = THD_cmass(aset, 0, cmask, 0);
3191       cm[0] = ccc.xyz[0];
3192       cm[1] = ccc.xyz[1];
3193       cm[2] = ccc.xyz[2];
3194    } else {
3195       cm[0] = ucm[0];
3196       cm[1] = ucm[1];
3197       cm[2] = ucm[2];
3198    }
3199    /* change cm to index coords */
3200    ccc.xyz[0]=cm[0]; ccc.xyz[1]=cm[1]; ccc.xyz[2]=cm[2];
3201    ncoord = THD_dicomm_to_3dmm(aset, ccc);
3202    ccc = THD_3dmm_to_3dfind(aset, ncoord);
3203    cm[0] = ccc.xyz[0];
3204    cm[1] = ccc.xyz[1];
3205    cm[2] = ccc.xyz[2];
3206 
3207    SUMA_S_Notev("Center of mass in ijk is %d %d %d\n",
3208              (int)cm[0], (int)cm[1], (int) cm[2]);
3209 
3210    /* Overkill buffer but won't hurt */
3211    vals = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3212                                sizeof(float));
3213    wt = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3214                                sizeof(float));
3215    ref = (float **)SUMA_calloc(2, sizeof(float*));
3216    ref[0] = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3217                                sizeof(float));
3218    ref[1] = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3219                                sizeof(float));
3220    ivals = (int *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3221                                sizeof(int));
3222 
3223 
3224    nref = 2;
3225    for (nn=0; nn<(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset)); ++nn)
3226       ref[0][nn]=1.0;
3227 
3228    /* identify holes */
3229    Nv = (int *)SUMA_calloc(DSET_NVOX(aset), sizeof(int));
3230    /* Because we're traversing by a constant offset
3231    on an irregular grid. Some voxels may never get
3232    hit, even if we start from them. Using multiple
3233    center of masses, via the offsets, will help
3234    increase the coverage and smooth the result */
3235    off[0][0] = 0; off[0][1] = 0; off[0][2] = 0;
3236    for (pl=0; pl<6; ++pl) {
3237       switch (pl) {
3238          case 0:
3239             iim=              0; iiM=DSET_NX(aset);
3240             jjm=              0; jjM=DSET_NY(aset);
3241             kkm=              0; kkM=            1;
3242             off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;
3243             off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;
3244             off[3][0] =  0; off[3][1] =  1; off[3][2] =  0;
3245             off[4][0] =  0; off[4][1] = -1; off[4][2] =  0;
3246             off[5][0] =  1; off[5][1] =  1; off[5][2] =  0;
3247             off[6][0] =  1; off[6][1] = -1; off[6][2] =  0;
3248             off[7][0] = -1; off[7][1] =  1; off[7][2] =  0;
3249             off[8][0] = -1; off[8][1] = -1; off[8][2] =  0;
3250             break;
3251          case 1:
3252             iim=              0; iiM=DSET_NX(aset);
3253             jjm=              0; jjM=DSET_NY(aset);
3254             kkm=DSET_NZ(aset)-1; kkM=DSET_NZ(aset);
3255             off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;
3256             off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;
3257             off[3][0] =  0; off[3][1] =  1; off[3][2] =  0;
3258             off[4][0] =  0; off[4][1] = -1; off[4][2] =  0;
3259             off[5][0] =  1; off[5][1] =  1; off[5][2] =  0;
3260             off[6][0] =  1; off[6][1] = -1; off[6][2] =  0;
3261             off[7][0] = -1; off[7][1] =  1; off[7][2] =  0;
3262             off[8][0] = -1; off[8][1] = -1; off[8][2] =  0;
3263            break;
3264          case 2:
3265             iim=              0; iiM=DSET_NX(aset);
3266             jjm=              0; jjM=            1;
3267             kkm=              0; kkM=DSET_NZ(aset);
3268             off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;
3269             off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;
3270             off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;
3271             off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;
3272             off[5][0] =  1; off[5][1] =  0; off[5][2] =  1;
3273             off[6][0] =  1; off[6][1] =  0; off[6][2] = -1;
3274             off[7][0] = -1; off[7][1] =  0; off[7][2] =  1;
3275             off[8][0] = -1; off[8][1] =  0; off[8][2] = -1;
3276             break;
3277          case 3:
3278             iim=              0; iiM=DSET_NX(aset);
3279             jjm=DSET_NY(aset)-1; jjM=DSET_NY(aset);
3280             kkm=              0; kkM=DSET_NZ(aset);
3281             off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;
3282             off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;
3283             off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;
3284             off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;
3285             off[5][0] =  1; off[5][1] =  0; off[5][2] =  1;
3286             off[6][0] =  1; off[6][1] =  0; off[6][2] = -1;
3287             off[7][0] = -1; off[7][1] =  0; off[7][2] =  1;
3288             off[8][0] = -1; off[8][1] =  0; off[8][2] = -1;
3289             break;
3290          case 4:
3291             iim=              0; iiM=            1;
3292             jjm=              0; jjM=DSET_NY(aset);
3293             kkm=              0; kkM=DSET_NZ(aset);
3294             off[1][0] =  0; off[1][1] =  1; off[1][2] =  0;
3295             off[2][0] =  0; off[2][1] = -1; off[2][2] =  0;
3296             off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;
3297             off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;
3298             off[5][0] =  0; off[5][1] =  1; off[5][2] =  1;
3299             off[6][0] =  0; off[6][1] = -1; off[6][2] =  1;
3300             off[7][0] =  0; off[7][1] =  1; off[7][2] = -1;
3301             off[8][0] =  0; off[8][1] = -1; off[8][2] = -1;
3302             break;
3303          case 5:
3304             iim=DSET_NX(aset)-1; iiM=DSET_NX(aset);
3305             jjm=              0; jjM=DSET_NY(aset);
3306             kkm=              0; kkM=DSET_NZ(aset);
3307             off[1][0] =  0; off[1][1] =  1; off[1][2] =  0;
3308             off[2][0] =  0; off[2][1] = -1; off[2][2] =  0;
3309             off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;
3310             off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;
3311             off[5][0] =  0; off[5][1] =  1; off[5][2] =  1;
3312             off[6][0] =  0; off[6][1] = -1; off[6][2] =  1;
3313             off[7][0] =  0; off[7][1] =  1; off[7][2] = -1;
3314             off[8][0] =  0; off[8][1] = -1; off[8][2] = -1;
3315             break;
3316          default:
3317             SUMA_S_Err("Rats");
3318             SUMA_RETURN(0);
3319       }
3320       /* When you pick a voxel to debug, make sure you select
3321          it from one of the perimeter voxels */
3322       VoxDbg = -1;
3323       for (kk=kkm; kk<kkM; ++kk) {
3324       for (jj=jjm; jj<jjM; ++jj) {
3325       for (ii=iim; ii<iiM; ++ii) {
3326          vv = ii+jj*DSET_NX(aset)+kk*DSET_NX(aset)*DSET_NY(aset);
3327          if (cmask[vv] == 0) {
3328          for (ioff=0; ioff<N_off; ++ioff) {
3329             xyz_ijk[0]= ii; xyz_ijk[1]= jj; xyz_ijk[2]= kk;
3330             /* offset center of mass */
3331             cmo[0]=cm[0]+off[ioff][0];
3332             cmo[1]=cm[1]+off[ioff][1];
3333             cmo[2]=cm[2]+off[ioff][2];
3334             if (!(nsamp = SUMA_Vox_Radial_Samples(fv,
3335                      DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
3336                      xyz_ijk, cmo, vals, ivals))) {
3337                SUMA_S_Errv("Failed at voxel %d %d %d\n",
3338                            ii, jj, kk);
3339                SUMA_RETURN(NOPE);
3340             }
3341             /* search from outside in until you hit plug */
3342             nn=nsamp-1; found = 0;
3343             while (nn - nplug > 0 && !found) {
3344                found = nn;
3345                for (mm=0; mm<nplug && found; ++mm) {
3346                   if (!cmask[ivals[nn-mm]]) found=0;
3347                }
3348                if (!found) --nn;
3349             }
3350             if (found) {
3351                /* just for debugging, fillup the ray, note that
3352                   some voxels will get revisited and the order
3353                   of the visitation might affect the outcome
3354                   in the modified holi array */
3355                nn = found+1;
3356                while (nn<nsamp) {
3357                   holi[ivals[nn]] = 11; /* exterior (over the plug)
3358                                         overwrite clumps thinner than nplug*/
3359                   ++nn;
3360                }
3361                nn=0;
3362                while (nn<=found) {
3363                   if (!holi[ivals[nn]])
3364                      holi[ivals[nn]] = 21; /* interior (under the plug) */
3365                   ++nn;
3366                }
3367                veclen = -1;
3368                if (found < nlin) { /* work with what you've got, if possible */
3369                   /* count the number of values in the mask */
3370                   /* This for loop is not needed except
3371                      for clarity while writing debugging trace
3372                   for (nn=0; nn<nsamp; ++nn) wt[nn]=0.0; */
3373                   for (nok=0, nn=0; nn <= found; ++nn) {
3374                      if (cmask[ivals[nn]]) {
3375                         wt[nn] = 1.0;
3376                         ++nok;
3377                      } else {
3378                         wt[nn] = 0.0;
3379                      }
3380                   }
3381                   if (nok > nplug && nok > 2) { /* attempt a fit */
3382                      veclen = found+1;
3383                      data = vals;
3384                      wtls = wt;
3385                      mid = (float)veclen/2.0;
3386                      nstrt=0;
3387                      for (nok=0; nok<veclen; ++nok)
3388                         ref[1][nok] = (float)(nok-mid);
3389                   } else {
3390                      SUMA_S_Warnv(
3391                         "No fit for voxel %d %d %d (found=%d, nok=%d)\n",
3392                               ii, jj, kk, found, nok);
3393                   }
3394                } else { /* more points than we need */
3395                   /* This for loop is not needed except
3396                      for clarity while writing debugging trace
3397                   for (nn=0; nn<nsamp; ++nn) wt[nn]=0.0; */
3398                   /* found a healthy amount of unmasked voxels */
3399                   for (nok=0, nn=found; nn>=0 && nok<=nlin; --nn) {
3400                      if (cmask[ivals[nn]]) {
3401                         ++nok;
3402                         wt[nn] = 1.0;
3403                      } else {
3404                         wt[nn] = 0.0;
3405                      }
3406                   }
3407                   ++nn;
3408                   veclen = found - nn+1;
3409                   data = vals+nn;
3410                   wtls = wt+nn;
3411                   mid = (float)veclen/2.0;
3412                   nstrt=nn;
3413                   for (nok=0; nok<veclen; ++nok) ref[1][nok] = (float)(nok-mid);
3414                }
3415                if (veclen > 0) {
3416                   for (nn=0; vv == VoxDbg && nn<veclen; ++nn) {
3417                      fprintf(stdout,"%f %f %f\n",
3418                              ref[1][nn], data[nn], wtls[nn]);
3419                   }
3420                   /* Now we fit using the lower points */
3421                   if (!(wref = lsqfit( veclen , data , wtls , nref , ref ))) {
3422                      SUMA_S_Err("Failed in lsqfit");
3423                      SUMA_RETURN(NOPE);
3424                   }
3425 
3426                   /* and then fill up the output */
3427                   /* At some point, consider blending the
3428                      interpolation results with the data
3429                      in a manner reflecting the uncertainty
3430                      of shallow values. Basically, begin
3431                      blending at the bottom of the
3432                      fitted region with 1 weight of 1
3433                      for the data, and 0 for the fit and
3434                      increase the weight for the fit (sigmoid)
3435                      so that as you leave the data region the
3436                      last voxel has a very low weight from
3437                      the data. This would remove transition edges
3438                      in the image. For now, just smooth the result
3439                      a little */
3440                   for (nn=found+1; nn<nsamp; ++nn) {
3441                      if (!cmask[ivals[nn]]) {
3442                         fvn[ivals[nn]] += wref[0]+wref[1]*(nn-mid-nstrt);
3443                         Nv[ivals[nn]] += 1;
3444                      }
3445                   }
3446                   if (vv == VoxDbg) {
3447                    SUMA_LHv(
3448                      "Trace at ijk %d %d %d, nsamp=%d, nlin=%d, nok=%d\n",
3449                               ii, jj, kk, nsamp, nlin, nok);
3450                 fprintf(stdout,
3451                         "#i  j  k  hol      vals    wtls   n<=f msk  fit\n");
3452                      for (nn=0; nn<nsamp; ++nn) {
3453                         Vox1D2Vox3D(ivals[nn],DSET_NX(aset),
3454                                     DSET_NX(aset)*DSET_NY(aset), ijk);
3455                         fprintf(stdout,"%d %d %d %d    %f %f   %d  %d    %f\n",
3456                               ijk[0],  ijk[1],
3457                               ijk[2],  holi[ivals[nn]],
3458                               vals[nn], wt[nn],
3459                               nn<=found ? 1:0, cmask[ivals[nn]],
3460                               wref[0]+wref[1]*(nn-mid-nstrt));
3461                      }
3462                 fprintf(stdout,
3463                         "#i  j  k  hol      vals    wtls   n<f msk  fit\n");
3464                   }
3465                   free(wref); wref=NULL;
3466                }
3467             } else if (0) {
3468                nn=0;
3469                while (nn<nsamp) {
3470                   if (!holi[ivals[nn]])
3471                      holi[ivals[nn]] = 21; /* interior (under the plug) */
3472                   ++nn;
3473                }
3474             }
3475          }
3476          }
3477       }}}
3478       /* count holes left */
3479       for (ncand = 0, nn=0; nn<DSET_NVOX(aset); ++nn) {
3480          if (holi[nn]==0) {
3481             ++ncand;
3482          }
3483       }
3484       SUMA_LHv("Have %d candidates left\n", ncand);
3485    }
3486 
3487    /* compute average */
3488    for (nn=0; nn<DSET_NVOX(aset); ++nn) {
3489       if (Nv[nn]) fvn[nn]/=Nv[nn];
3490    }
3491 
3492    /* smooth result */
3493    if (smooth > 0.0) {
3494       THD_3dim_dataset *bset=NULL;
3495       THD_3dim_dataset *blurred=NULL;
3496       SUMA_LH("Blurring");
3497       NEW_FLOATYV(aset,1,"blurry",bset,fvn);
3498       SUMA_VolumeBlur(bset,
3499                 holi,&blurred,
3500                 smooth);
3501       DSET_delete(bset); fvn=NULL; bset=NULL;
3502       fvn = (float *)DSET_ARRAY(blurred , 0);
3503       memcpy(fv, fvn, sizeof(float)*DSET_NVOX(aset));
3504       DSET_delete(blurred); fvn=NULL; blurred=NULL;
3505    } else {
3506       memcpy(fv, fvn, sizeof(float)*DSET_NVOX(aset));
3507       SUMA_free(fvn); fvn=NULL;
3508    }
3509 
3510    /* Put result in output dset */
3511    if (filledp) {
3512       if (!filled) {
3513          filled = EDIT_full_copy(aset, FuncName);
3514          *filledp = filled;
3515       }
3516       EDIT_substscale_brick(  filled, 0, MRI_float, fv,
3517                               DSET_BRICK_TYPE(filled,0), -1.0);
3518       EDIT_BRICK_LABEL(filled,0,"ExteriorFilled");
3519       if (DSET_BRICK_TYPE(filled,0) == MRI_float) {
3520          /* don't free fv, even if created here */
3521          fv = NULL;
3522       } else {
3523         if (!ufv) free(fv); fv=NULL;
3524       }
3525    }
3526 
3527    if (wt) SUMA_free(wt); wt=NULL;
3528    if (ref) {
3529       if (ref[0]) SUMA_free(ref[0]);
3530       if (ref[1]) SUMA_free(ref[1]);
3531       SUMA_free(ref); ref = NULL;
3532    }
3533    if (holi) SUMA_free(holi); holi=NULL;
3534    if (Nv) SUMA_free(Nv); Nv = NULL;
3535    if (!ufv) free(fv); fv=NULL;
3536    if (!ucmask) free(cmask); cmask=NULL;
3537    if (vals) SUMA_free(vals); vals=NULL;
3538    if (ivals) SUMA_free(ivals); ivals=NULL;
3539    SUMA_RETURN(1);
3540 }
3541 
3542 #define FILL_REF(ref,nref,veclen,mid) {\
3543    int m_nok=0, m_iref=0;  \
3544    if (nref>1) {  \
3545       for (m_nok=0; m_nok<veclen; ++m_nok) \
3546          ref[1][m_nok] = (float)(m_nok-mid);\
3547       for (m_iref=2; m_iref<nref; ++m_iref) {\
3548          if (m_iref==2) {\
3549                for (m_nok=0; m_nok<veclen; ++m_nok) \
3550                    ref[m_iref][m_nok] = SUMA_POW2(ref[1][m_nok]);\
3551          }\
3552          if (m_iref==3) {\
3553                for (m_nok=0; m_nok<veclen; ++m_nok) \
3554                    ref[m_iref][m_nok] = SUMA_POW3(ref[1][m_nok]);\
3555          }\
3556          if (m_iref==4) {\
3557                for (m_nok=0; m_nok<veclen; ++m_nok) \
3558                    ref[m_iref][m_nok] = SUMA_POW2(ref[2][m_nok]);\
3559          }\
3560          if (m_iref==5) {\
3561                for (m_nok=0; m_nok<veclen; ++m_nok) \
3562                    ref[m_iref][m_nok] = ref[2][m_nok]*ref[3][m_nok];\
3563          }\
3564          if (m_iref==6) {\
3565                for (m_nok=0; m_nok<veclen; ++m_nok) \
3566                    ref[m_iref][m_nok] = SUMA_POW2(ref[3][m_nok]);\
3567          }\
3568       }\
3569    }\
3570 }
3571 #define FIT_AT(wref,nref,nno, ft)  {\
3572    int m_iref;\
3573    ft=wref[0]; \
3574    if (nref > 1) {   \
3575       ft += wref[1]*nno;   \
3576       if (nref > 2) {   \
3577          ft += wref[2]*nno*nno;  \
3578          if (nref > 3) {   \
3579             ft += wref[3]*nno*nno*nno;  \
3580             for (m_iref=4; m_iref<nref; ++m_iref) {   \
3581                ft += wref[m_iref]*pow(nno,m_iref); \
3582             }  \
3583          }  \
3584       }  \
3585    }  \
3586 }
3587 
3588 #define INIT_CM_OFFSET(off, pl, iim, iiM, jjm, jjM, kkm, kkM) {\
3589    switch (pl) {\
3590       case 0:\
3591          iim=              0; iiM=DSET_NX(aset);\
3592          jjm=              0; jjM=DSET_NY(aset);\
3593          kkm=              0; kkM=            1;\
3594          off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;\
3595          off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;\
3596          off[3][0] =  0; off[3][1] =  1; off[3][2] =  0;\
3597          off[4][0] =  0; off[4][1] = -1; off[4][2] =  0;\
3598          off[5][0] =  1; off[5][1] =  1; off[5][2] =  0;\
3599          off[6][0] =  1; off[6][1] = -1; off[6][2] =  0;\
3600          off[7][0] = -1; off[7][1] =  1; off[7][2] =  0;\
3601          off[8][0] = -1; off[8][1] = -1; off[8][2] =  0;\
3602          break;\
3603       case 1:\
3604          iim=              0; iiM=DSET_NX(aset);\
3605          jjm=              0; jjM=DSET_NY(aset);\
3606          kkm=DSET_NZ(aset)-1; kkM=DSET_NZ(aset);\
3607          off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;\
3608          off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;\
3609          off[3][0] =  0; off[3][1] =  1; off[3][2] =  0;\
3610          off[4][0] =  0; off[4][1] = -1; off[4][2] =  0;\
3611          off[5][0] =  1; off[5][1] =  1; off[5][2] =  0;\
3612          off[6][0] =  1; off[6][1] = -1; off[6][2] =  0;\
3613          off[7][0] = -1; off[7][1] =  1; off[7][2] =  0;\
3614          off[8][0] = -1; off[8][1] = -1; off[8][2] =  0;\
3615         break;\
3616       case 2:\
3617          iim=              0; iiM=DSET_NX(aset);\
3618          jjm=              0; jjM=            1;\
3619          kkm=              0; kkM=DSET_NZ(aset);\
3620          off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;\
3621          off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;\
3622          off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;\
3623          off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;\
3624          off[5][0] =  1; off[5][1] =  0; off[5][2] =  1;\
3625          off[6][0] =  1; off[6][1] =  0; off[6][2] = -1;\
3626          off[7][0] = -1; off[7][1] =  0; off[7][2] =  1;\
3627          off[8][0] = -1; off[8][1] =  0; off[8][2] = -1;\
3628          break;\
3629       case 3:\
3630          iim=              0; iiM=DSET_NX(aset);\
3631          jjm=DSET_NY(aset)-1; jjM=DSET_NY(aset);\
3632          kkm=              0; kkM=DSET_NZ(aset);\
3633          off[1][0] =  1; off[1][1] =  0; off[1][2] =  0;\
3634          off[2][0] = -1; off[2][1] =  0; off[2][2] =  0;\
3635          off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;\
3636          off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;\
3637          off[5][0] =  1; off[5][1] =  0; off[5][2] =  1;\
3638          off[6][0] =  1; off[6][1] =  0; off[6][2] = -1;\
3639          off[7][0] = -1; off[7][1] =  0; off[7][2] =  1;\
3640          off[8][0] = -1; off[8][1] =  0; off[8][2] = -1;\
3641          break;\
3642       case 4:\
3643          iim=              0; iiM=            1;\
3644          jjm=              0; jjM=DSET_NY(aset);\
3645          kkm=              0; kkM=DSET_NZ(aset);\
3646          off[1][0] =  0; off[1][1] =  1; off[1][2] =  0;\
3647          off[2][0] =  0; off[2][1] = -1; off[2][2] =  0;\
3648          off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;\
3649          off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;\
3650          off[5][0] =  0; off[5][1] =  1; off[5][2] =  1;\
3651          off[6][0] =  0; off[6][1] = -1; off[6][2] =  1;\
3652          off[7][0] =  0; off[7][1] =  1; off[7][2] = -1;\
3653          off[8][0] =  0; off[8][1] = -1; off[8][2] = -1;\
3654          break;\
3655       case 5:\
3656          iim=DSET_NX(aset)-1; iiM=DSET_NX(aset);\
3657          jjm=              0; jjM=DSET_NY(aset);\
3658          kkm=              0; kkM=DSET_NZ(aset);\
3659          off[1][0] =  0; off[1][1] =  1; off[1][2] =  0;\
3660          off[2][0] =  0; off[2][1] = -1; off[2][2] =  0;\
3661          off[3][0] =  0; off[3][1] =  0; off[3][2] =  1;\
3662          off[4][0] =  0; off[4][1] =  0; off[4][2] = -1;\
3663          off[5][0] =  0; off[5][1] =  1; off[5][2] =  1;\
3664          off[6][0] =  0; off[6][1] = -1; off[6][2] =  1;\
3665          off[7][0] =  0; off[7][1] =  1; off[7][2] = -1;\
3666          off[8][0] =  0; off[8][1] = -1; off[8][2] = -1;\
3667          break;\
3668       default:\
3669          SUMA_S_Err("Rats");\
3670          SUMA_RETURN(NULL);\
3671    }\
3672 }
3673 
SUMA_Volume_RadFill_Fit(THD_3dim_dataset * aset,float * fv,byte * cmask,float * cm,int nplug,int nlin,int fitord,int N_off)3674 float *SUMA_Volume_RadFill_Fit(THD_3dim_dataset *aset, float *fv, byte *cmask,
3675                             float *cm, int nplug, int nlin, int fitord,
3676                             int N_off)
3677 {
3678    static char FuncName[]={"SUMA_Volume_RadFill_Fit"};
3679    float cmo[3], xyz_ijk[3], *vals=NULL, mid,
3680          *wt=NULL, **ref=NULL, *wref=NULL, *wtls, *data, *fvn=NULL;
3681    THD_fvec3 ccc, ncoord;
3682    int ii, jj, kk, nsamp, vv, nn, mm, *ivals=NULL,
3683        off[10][3], ioff, nstrt,
3684        found=0, ijk[3], nok, veclen, ncand, niter,
3685        *Nv=NULL, iim, iiM, jjm, jjM, kkm, kkM, pl, nref;
3686    byte *holi=NULL;
3687    double nno, ft;
3688    MRI_IMAGE *imin=NULL;
3689    SUMA_Boolean LocalHead = NOPE;
3690 
3691    SUMA_ENTRY;
3692 
3693    if (!aset || !fv || !cmask || !cm) {
3694       SUMA_S_Err("Need input parameters");
3695       SUMA_RETURN(fvn);
3696    }
3697    if (N_off == -1) N_off = 9;
3698    if (N_off != 1 && N_off != 5 && N_off != 9) {
3699       SUMA_S_Errv("N_off (%d) must be one of 1,5, or 9\n", N_off);
3700       SUMA_RETURN(fvn);
3701    }
3702    if (fitord < 0) fitord = 0;
3703 
3704    nref = fitord+1;
3705    if (nref >= nlin) {
3706       SUMA_S_Errv(
3707          "You're asking for a fit of order %d with a sample of %d\n",
3708          nref, nlin);
3709       SUMA_RETURN(fvn);
3710    }
3711    if (nref > 6) {
3712       SUMA_S_Errv("Not willing to go beyond 6th order now. Have %d\n",
3713                   nref);
3714       SUMA_RETURN(fvn);
3715    }
3716    /* a vector to flag the holiness of a voxel */
3717    holi = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
3718    for (vv=0; vv<DSET_NVOX(aset); ++vv) {
3719       if (cmask[vv]) holi[vv] = 1; /* in mask */
3720       else holi[vv] = 0;
3721    }
3722 
3723    SUMA_S_Notev("Center of mass in ijk is %d %d %d, fit order %d\n",
3724              (int)cm[0], (int)cm[1], (int) cm[2], fitord);
3725 
3726    /* Overkill buffer but won't hurt */
3727    vals = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3728                                sizeof(float));
3729    wt = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3730                                sizeof(float));
3731    ref = (float **)SUMA_allocate2D(nref,
3732                      DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset), sizeof(float));
3733 
3734    ivals = (int *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3735                                sizeof(int));
3736    fvn = (float *)SUMA_calloc(DSET_NVOX(aset),sizeof(float));
3737    Nv = (int *)SUMA_calloc(DSET_NVOX(aset), sizeof(int));
3738 
3739    /* initialize constant term for fit */
3740    for (nn=0; nn<(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset)); ++nn)
3741       ref[0][nn]=1.0;
3742 
3743    /* Because we're traversing by a constant offset
3744    on an irregular grid. Some voxels may never get
3745    hit, even if we start from them. Using multiple
3746    center of masses, via the offsets, will help
3747    increase the coverage and smooth the result */
3748    off[0][0] = 0; off[0][1] = 0; off[0][2] = 0;
3749    for (pl=0; pl<6; ++pl) {
3750       SUMA_LHv("pl=%d\n", pl);
3751       INIT_CM_OFFSET(off, pl, iim, iiM, jjm, jjM, kkm, kkM);
3752       /* When you pick a voxel to debug, make sure you select
3753          it from one of the perimeter voxels */
3754       for (kk=kkm; kk<kkM; ++kk) {
3755       for (jj=jjm; jj<jjM; ++jj) {
3756       for (ii=iim; ii<iiM; ++ii) {
3757          vv = ii+jj*DSET_NX(aset)+kk*DSET_NX(aset)*DSET_NY(aset);
3758          if (1) {/* enter whether or not in mask,
3759                    you're coming from the perimeter */
3760          for (ioff=0; ioff<N_off; ++ioff) {
3761             xyz_ijk[0]= ii; xyz_ijk[1]= jj; xyz_ijk[2]= kk;
3762             /* offset center of mass */
3763             cmo[0]=cm[0]+off[ioff][0];
3764             cmo[1]=cm[1]+off[ioff][1];
3765             cmo[2]=cm[2]+off[ioff][2];
3766             if (!(nsamp = SUMA_Vox_Radial_Samples(fv,
3767                      DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
3768                      xyz_ijk, cmo, vals, ivals))) {
3769                SUMA_S_Errv("Failed at voxel %d %d %d\n",
3770                            ii, jj, kk);
3771                SUMA_RETURN(NOPE);
3772             }
3773             /* search from outside in until you hit plug */
3774             nn=nsamp-1; found = 0;
3775             while (nn - nplug > 0 && !found) {
3776                found = nn;
3777                for (mm=0; mm<nplug && found; ++mm) {
3778                   if (!cmask[ivals[nn-mm]]) found=0;
3779                }
3780                if (!found) --nn;
3781             }
3782             if (found) {
3783                /* just for debugging, fillup the ray, note that
3784                   some voxels will get revisited and the order
3785                   of the visitation might affect the outcome
3786                   in the modified holi array */
3787                nn = found+1;
3788                while (nn<nsamp) {
3789                   holi[ivals[nn]] = 11; /* exterior (over the plug)
3790                                         overwrite clumps thinner than nplug*/
3791                   ++nn;
3792                }
3793                nn=0;
3794                while (nn<=found) {
3795                   if (!holi[ivals[nn]])
3796                      holi[ivals[nn]] = 21; /* interior (under the plug) */
3797                   ++nn;
3798                }
3799                veclen = -1;
3800                if (found < nlin) { /* work with what you've got, if possible */
3801                   /* count the number of values in the mask */
3802                   /* This for loop is not needed except
3803                      for clarity while writing debugging trace
3804                   for (nn=0; nn<nsamp; ++nn) wt[nn]=0.0; */
3805                   for (nok=0, nn=0; nn <= found; ++nn) {
3806                      if (cmask[ivals[nn]]) {
3807                         wt[nn] = 1.0;
3808                         ++nok;
3809                      } else {
3810                         wt[nn] = 0.0;
3811                      }
3812                   }
3813                   if (nok > nplug && nok > nref) { /* attempt a fit */
3814                      veclen = found+1;
3815                      data = vals;
3816                      wtls = wt;
3817                      mid = (float)veclen/2.0;
3818                      nstrt=0;
3819                      FILL_REF(ref, nref, veclen, mid);
3820                   } else {
3821                      SUMA_S_Warnv(
3822                        "No fit for voxel %d %d %d (found=%d, nok=%d, nref=%d)\n",
3823                               ii, jj, kk, found, nok, nref);
3824                   }
3825                } else { /* more points than we need */
3826                   /* This for loop is not needed except
3827                      for clarity while writing debugging trace
3828                   for (nn=0; nn<nsamp; ++nn) wt[nn]=0.0; */
3829                   /* found a healthy amount of unmasked voxels */
3830                   for (nok=0, nn=found; nn>=0 && nok<=nlin; --nn) {
3831                      if (cmask[ivals[nn]]) {
3832                         ++nok;
3833                         wt[nn] = 1.0;
3834                      } else {
3835                         wt[nn] = 0.0;
3836                      }
3837                   }
3838                   ++nn;
3839                   veclen = found - nn+1;
3840                   data = vals+nn;
3841                   wtls = wt+nn;
3842                   mid = (float)veclen/2.0;
3843                   nstrt=nn;
3844                   FILL_REF(ref, nref, veclen, mid);
3845                }
3846                if (veclen > 0) {
3847                   for (nn=0; vv == VoxDbg && nn<veclen; ++nn) {
3848                      for (mm=0; mm<nref; ++mm) fprintf(stdout,"%f ",ref[mm][nn]);
3849                      fprintf(stdout,"%f %f\n",
3850                              data[nn], wtls[nn]);
3851                   }
3852                   /* Now we fit using the lower points */
3853                   if (!(wref = lsqfit( veclen , data , wtls , nref , ref ))) {
3854                      SUMA_S_Err("Failed in lsqfit");
3855                      SUMA_RETURN(NOPE);
3856                   }
3857 
3858                   /* and then fill up the output */
3859                   for (nn=0; nn<nsamp; ++nn) {
3860                      Nv[ivals[nn]] += 1;
3861                      nno = ((double)nn-mid-nstrt);
3862                      FIT_AT(wref,nref,nno, ft);
3863                      fvn[ivals[nn]] += ft;
3864                   }
3865                   if (vv == VoxDbg) {
3866                    SUMA_LHv(
3867                   "Trace at ijk %d %d %d, nsamp=%d, nlin=%d, nok=%d , nref=%d\n",
3868                               ii, jj, kk, nsamp, nlin, nok, nref);
3869                 fprintf(stdout,
3870                         "#i  j  k  hol      vals    wtls   n<=f msk  fit\n");
3871                      for (nn=0; nn<nsamp; ++nn) {
3872                         Vox1D2Vox3D(ivals[nn],DSET_NX(aset),
3873                                     DSET_NX(aset)*DSET_NY(aset), ijk);
3874                         nno = ((double)nn-mid-nstrt);
3875                         FIT_AT(wref,nref,nno, ft);
3876                         fprintf(stdout,"%d %d %d %d    %f %f   %d  %d    %f\n",
3877                               ijk[0],  ijk[1],
3878                               ijk[2],  holi[ivals[nn]],
3879                               vals[nn], wt[nn],
3880                               nn<=found ? 1:0, cmask[ivals[nn]],
3881                               ft);
3882                      }
3883                 fprintf(stdout,
3884                         "#i  j  k  hol      vals    wtls   n<f msk  fit\n");
3885                   }
3886                   free(wref); wref=NULL;
3887                }
3888             } else if (0) {
3889                nn=0;
3890                while (nn<nsamp) {
3891                   if (!holi[ivals[nn]])
3892                      holi[ivals[nn]] = 21; /* interior (under the plug) */
3893                   ++nn;
3894                }
3895             }
3896          }
3897          }
3898       }}}
3899       /* count holes left */
3900       for (ncand = 0, nn=0; nn<DSET_NVOX(aset); ++nn) {
3901          if (holi[nn]==0) {
3902             ++ncand;
3903          }
3904       }
3905       SUMA_LHv("Have %d candidates left\n", ncand);
3906    }
3907 
3908    /* compute average */
3909    for (nn=0; nn<DSET_NVOX(aset); ++nn) {
3910       if (Nv[nn]) fvn[nn]/=Nv[nn];
3911    }
3912 
3913    if (wt) SUMA_free(wt); wt=NULL;
3914    if (ref) SUMA_free2D((char **)ref,nref); ref=NULL;
3915    if (holi) SUMA_free(holi); holi=NULL;
3916    if (Nv) SUMA_free(Nv); Nv = NULL;
3917    if (vals) SUMA_free(vals); vals=NULL;
3918    if (ivals) SUMA_free(ivals); ivals=NULL;
3919 
3920    SUMA_RETURN(fvn);
3921 }
3922 
SUMA_Volume_RadFill_Blend(THD_3dim_dataset * aset,float * fv,byte * cmask,float * bl,byte * cmaskbl,float * cm,int nplug,float spow,float soff,int N_off)3923 float *SUMA_Volume_RadFill_Blend(THD_3dim_dataset *aset, float *fv, byte *cmask,
3924                             float *bl, byte *cmaskbl, float *cm, int nplug,
3925                             float spow, float soff, int N_off)
3926 {
3927    static char FuncName[]={"SUMA_Volume_RadFill_Blend"};
3928    float cmo[3], xyz_ijk[3], *vals=NULL, mid,
3929          *fvn=NULL;
3930    THD_fvec3 ccc, ncoord;
3931    int ii, jj, kk, nsamp, vv, nn, mm, *ivals=NULL,
3932        off[10][3], ioff, nstrt,
3933        found=0, ijk[3], nok, ncand, niter,
3934        *Nv=NULL, iim, iiM, jjm, jjM, kkm, kkM, pl;
3935    byte *holi=NULL;
3936    double bb, tt, bo;
3937    MRI_IMAGE *imin=NULL;
3938    SUMA_Boolean LocalHead = NOPE;
3939 
3940    SUMA_ENTRY;
3941 
3942    if (!aset || !fv || !cmask || !cm || !bl || !cmaskbl) {
3943       SUMA_S_Err("Need input parameters");
3944       SUMA_RETURN(fvn);
3945    }
3946    if (N_off == -1) N_off = 9;
3947    if (N_off != 1 && N_off != 5 && N_off != 9) {
3948       SUMA_S_Errv("N_off (%d) must be one of 1,5, or 9\n", N_off);
3949       SUMA_RETURN(fvn);
3950    }
3951    SUMA_S_Warn("Is Holiness Necessary?");
3952    /* a vector to flag the holiness of a voxel */
3953    holi = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
3954    for (vv=0; vv<DSET_NVOX(aset); ++vv) {
3955       if (cmask[vv]) holi[vv] = 1; /* in mask */
3956       else holi[vv] = 0;
3957    }
3958 
3959    SUMA_S_Notev("Center of mass in ijk is %d %d %d\n",
3960              (int)cm[0], (int)cm[1], (int) cm[2]);
3961 
3962    /* Overkill buffer but won't hurt */
3963    vals = (float *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3964                                sizeof(float));
3965    ivals = (int *)SUMA_calloc(DSET_NX(aset)+DSET_NY(aset)+DSET_NZ(aset),
3966                                sizeof(int));
3967    fvn = (float *)SUMA_calloc(DSET_NVOX(aset),sizeof(float));
3968    Nv = (int *)SUMA_calloc(DSET_NVOX(aset), sizeof(int));
3969 
3970 
3971    /* Because we're traversing by a constant offset
3972    on an irregular grid. Some voxels may never get
3973    hit, even if we start from them. Using multiple
3974    center of masses, via the offsets, will help
3975    increase the coverage and smooth the result */
3976    off[0][0] = 0; off[0][1] = 0; off[0][2] = 0;
3977    for (pl=0; pl<6; ++pl) {
3978       SUMA_LHv("pl=%d\n", pl);
3979       INIT_CM_OFFSET(off, pl, iim, iiM, jjm, jjM, kkm, kkM);
3980       /* When you pick a voxel to debug, make sure you select
3981          it from one of the perimeter voxels */
3982       for (kk=kkm; kk<kkM; ++kk) {
3983       for (jj=jjm; jj<jjM; ++jj) {
3984       for (ii=iim; ii<iiM; ++ii) {
3985          vv = ii+jj*DSET_NX(aset)+kk*DSET_NX(aset)*DSET_NY(aset);
3986          if (1) {/* enter whether or not in mask,
3987                    you're coming from the perimeter */
3988          for (ioff=0; ioff<N_off; ++ioff) {
3989             xyz_ijk[0]= ii; xyz_ijk[1]= jj; xyz_ijk[2]= kk;
3990             /* offset center of mass */
3991             cmo[0]=cm[0]+off[ioff][0];
3992             cmo[1]=cm[1]+off[ioff][1];
3993             cmo[2]=cm[2]+off[ioff][2];
3994             if (!(nsamp = SUMA_Vox_Radial_Samples(fv,
3995                      DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
3996                      xyz_ijk, cmo, vals, ivals))) {
3997                SUMA_S_Errv("Failed at voxel %d %d %d\n",
3998                            ii, jj, kk);
3999                SUMA_RETURN(NOPE);
4000             }
4001             /* search from outside in until you hit plug */
4002             nn=nsamp-1; found = 0;
4003             while (nn - nplug > 0 && !found) {
4004                found = nn;
4005                for (mm=0; mm<nplug && found; ++mm) {
4006                   if (!cmask[ivals[nn-mm]]) found=0;
4007                }
4008                if (!found) --nn;
4009             }
4010             if (found) {
4011                /* just for debugging, fillup the ray, note that
4012                   some voxels will get revisited and the order
4013                   of the visitation might affect the outcome
4014                   in the modified holi array */
4015                nn = found+1;
4016                while (nn<nsamp) {
4017                   holi[ivals[nn]] = 11; /* exterior (over the plug)
4018                                         overwrite clumps thinner than nplug*/
4019                   ++nn;
4020                }
4021                nn=0;
4022                while (nn<=found) {
4023                   if (!holi[ivals[nn]])
4024                      holi[ivals[nn]] = 21; /* interior (under the plug) */
4025                   ++nn;
4026                }
4027                if (found > 0) {
4028                   /* blend the two into the output */
4029                   for (nn=0; nn<nsamp; ++nn) {
4030                      Nv[ivals[nn]] += 1;
4031                      if (cmask[ivals[nn]] && cmaskbl[ivals[nn]]) {
4032                         tt = spow*(nn-found+soff);
4033                         bb = 1.0/(1.0+exp(-tt));
4034                         fvn[ivals[nn]] += (1-bb)*fv[ivals[nn]]+bb*bl[ivals[nn]];
4035                      } else if (cmaskbl[ivals[nn]]) {
4036                         fvn[ivals[nn]] += bl[ivals[nn]];
4037                      } else fvn[ivals[nn]] += fv[ivals[nn]];
4038                   }
4039                   if (vv == VoxDbg) {
4040                    SUMA_LHv(
4041                      "Trace at ijk %d %d %d, nsamp=%d\n",
4042                               ii, jj, kk, nsamp);
4043                 fprintf(stdout,
4044                         "#i  j  k  hol      fv  bl  bb res\n");
4045                      for (nn=0; nn<nsamp; ++nn) {
4046                         Vox1D2Vox3D(ivals[nn],DSET_NX(aset),
4047                                     DSET_NX(aset)*DSET_NY(aset), ijk);
4048                         if (cmask[ivals[nn]] && cmaskbl[ivals[nn]]) {
4049                            tt = spow*(nn-found+soff);
4050                            bb = 1.0/(1.0+exp(-tt));
4051                         } else if (cmaskbl[ivals[nn]]) {
4052                            bb = 1.0;
4053                         } else bb = 0.0;
4054                         bo = (1-bb)*fv[ivals[nn]]+bb*bl[ivals[nn]];
4055                         fprintf(stdout,"%d %d %d %d    %f %f %f  %f\n",
4056                               ijk[0],  ijk[1],
4057                               ijk[2],  holi[ivals[nn]],
4058                               fv[ivals[nn]], bl[ivals[nn]], bb, bo);
4059                      }
4060                 fprintf(stdout,
4061                         "#i  j  k  hol      fv  bl  bb res\n");
4062                   }
4063                }
4064             } else if (0) {
4065                nn=0;
4066                while (nn<nsamp) {
4067                   if (!holi[ivals[nn]])
4068                      holi[ivals[nn]] = 21; /* interior (under the plug) */
4069                   ++nn;
4070                }
4071             }
4072          }
4073          }
4074       }}}
4075       /* count holes left */
4076       for (ncand = 0, nn=0; nn<DSET_NVOX(aset); ++nn) {
4077          if (holi[nn]==0) {
4078             ++ncand;
4079          }
4080       }
4081       SUMA_LHv("Have %d candidates left\n", ncand);
4082    }
4083 
4084    /* compute average */
4085    for (nn=0; nn<DSET_NVOX(aset); ++nn) {
4086       if (Nv[nn]) fvn[nn]/=Nv[nn];
4087    }
4088 
4089    if (holi) SUMA_free(holi); holi=NULL;
4090    if (Nv) SUMA_free(Nv); Nv = NULL;
4091    if (vals) SUMA_free(vals); vals=NULL;
4092    if (ivals) SUMA_free(ivals); ivals=NULL;
4093 
4094    SUMA_RETURN(fvn);
4095 }
4096 
4097 
SUMA_Volume_RadFill(THD_3dim_dataset * aset,float * ufv,byte * ucmask,float * ucm,THD_3dim_dataset ** filledp,int nplug,int nlin,int fitord,float smooth,int N_off)4098 int SUMA_Volume_RadFill(THD_3dim_dataset *aset, float *ufv, byte *ucmask,
4099                       float *ucm, THD_3dim_dataset **filledp,
4100                       int nplug, int nlin, int fitord, float smooth, int N_off)
4101 {
4102    static char FuncName[]={"SUMA_Volume_RadFill"};
4103    float *fv=NULL, cm[3], cmo[3], xyz_ijk[3],
4104          *fvn=NULL, *fvnb=NULL;
4105    THD_fvec3 ccc, ncoord;
4106    int vv, niter,  pl;
4107    byte *cmask=NULL, *cmaskbl=NULL;
4108    THD_3dim_dataset *filled = *filledp;
4109    MRI_IMAGE *imin=NULL;
4110    SUMA_Boolean LocalHead = NOPE;
4111 
4112    SUMA_ENTRY;
4113 
4114    if (!aset) {
4115       SUMA_S_Err("Need input dataset");
4116       SUMA_RETURN(NOPE);
4117    }
4118    if (!ufv && !filledp) {
4119       SUMA_S_Err("No way to return results");
4120       SUMA_RETURN(NOPE);
4121    }
4122    if (N_off == -1) N_off = 9;
4123    if (N_off != 1 && N_off != 5 && N_off != 9) {
4124       SUMA_S_Errv("N_off (%d) must be one of 1,5, or 9\n", N_off);
4125       SUMA_RETURN(NOPE);
4126    }
4127 
4128    /* If no float vector given get one */
4129    if (!ufv) {
4130       if (!(fv = THD_extract_to_float(0,aset))) {
4131          SUMA_S_Err("Failed to extract float");
4132          SUMA_RETURN(0);
4133       }
4134    } else {
4135       fv = ufv;
4136    }
4137 
4138    /* If no mask is given make one */
4139    if (!ucmask) {
4140       cmask = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
4141       for (vv=0; vv<DSET_NVOX(aset); ++vv) {
4142          if (!fv[vv]) cmask[vv]=0; else cmask[vv]=1;
4143       }
4144    } else {
4145       cmask = ucmask;
4146    }
4147 
4148    /* center of mass */
4149    if (!ucm) {
4150       ccc = THD_cmass(aset, 0, cmask, 0);
4151       cm[0] = ccc.xyz[0];
4152       cm[1] = ccc.xyz[1];
4153       cm[2] = ccc.xyz[2];
4154    } else {
4155       cm[0] = ucm[0];
4156       cm[1] = ucm[1];
4157       cm[2] = ucm[2];
4158    }
4159 
4160    /* change cm to index coords */
4161    ccc.xyz[0]=cm[0]; ccc.xyz[1]=cm[1]; ccc.xyz[2]=cm[2];
4162    ncoord = THD_dicomm_to_3dmm(aset, ccc);
4163    ccc = THD_3dmm_to_3dfind(aset, ncoord);
4164    cm[0] = ccc.xyz[0];
4165    cm[1] = ccc.xyz[1];
4166    cm[2] = ccc.xyz[2];
4167 
4168 
4169    SUMA_S_Notev("Center of mass in ijk is %d %d %d\n",
4170              (int)cm[0], (int)cm[1], (int) cm[2]);
4171 
4172    if (!(fvn = SUMA_Volume_RadFill_Fit(aset, fv, cmask, cm,
4173                                        nplug, nlin, fitord, N_off))){
4174       SUMA_S_Err("Misericorde! Can't fit in these pants anymore");
4175       SUMA_RETURN(NOPE);
4176    }
4177 
4178    #if 1
4179    SUMA_S_Note("Now blending");
4180    /* blend */
4181    cmaskbl = (byte *)SUMA_calloc(DSET_NVOX(aset), sizeof(byte));
4182    for (vv=0; vv<DSET_NVOX(aset); ++vv) if (fvn[vv] != 0.0f) cmaskbl[vv] = 1;
4183    fvnb = SUMA_Volume_RadFill_Blend(aset, fv, cmask,
4184                             fvn, cmaskbl, cm, nplug,
4185                             1.0, 6, N_off);
4186    memcpy(fvn, fvnb, sizeof(float)*DSET_NVOX(aset));
4187    SUMA_free(fvnb); fvnb = NULL;
4188    SUMA_free(cmaskbl); cmaskbl = NULL;
4189    #endif
4190 
4191    /* smooth result */
4192    if (smooth > 0.0) {
4193       THD_3dim_dataset *bset=NULL;
4194       THD_3dim_dataset *blurred=NULL;
4195       SUMA_LH("Blurring");
4196       NEW_FLOATYV(aset,1,"blurry",bset,fvn);
4197       SUMA_VolumeBlur(bset,
4198                 NULL,&blurred,
4199                 smooth);
4200       DSET_delete(bset); fvn=NULL; bset=NULL;
4201       fvn = (float *)DSET_ARRAY(blurred , 0);
4202       memcpy(fv, fvn, sizeof(float)*DSET_NVOX(aset));
4203       DSET_delete(blurred); fvn=NULL; blurred=NULL;
4204    } else {
4205       memcpy(fv, fvn, sizeof(float)*DSET_NVOX(aset));
4206       SUMA_free(fvn); fvn=NULL;
4207    }
4208 
4209    /* Put result in output dset */
4210    if (filledp) {
4211       if (!filled) {
4212          filled = EDIT_full_copy(aset, FuncName);
4213          *filledp = filled;
4214       }
4215       EDIT_substscale_brick(  filled, 0, MRI_float, fv,
4216                               DSET_BRICK_TYPE(filled,0), -1.0);
4217       EDIT_BRICK_LABEL(filled,0,"ExteriorFilled");
4218       if (DSET_BRICK_TYPE(filled,0) == MRI_float) {
4219          /* don't free fv, even if created here */
4220          fv = NULL;
4221       } else {
4222         if (!ufv) free(fv); fv=NULL;
4223       }
4224    }
4225 
4226    if (!ufv) free(fv); fv=NULL;
4227    if (!ucmask) free(cmask); cmask=NULL;
4228 
4229    SUMA_RETURN(1);
4230 }
4231 /*!
4232    A local stat moving average blurring of each sub-brick inside mask .
4233    This was tested only once and FWHM is not handled properly.
4234    It just uses a sphere of radius FWHM/2, but it is much slower
4235    than SUMA_VolumeBlurInMask, so fughet about it
4236 */
SUMA_VolumeLSBlurInMask(THD_3dim_dataset * aset,byte * cmask,THD_3dim_dataset ** blurredp,float FWHM,float mxvx)4237 int SUMA_VolumeLSBlurInMask(THD_3dim_dataset *aset,
4238                                   byte *cmask,
4239                                   THD_3dim_dataset **blurredp,
4240                                   float FWHM, float mxvx)
4241 {
4242    static char FuncName[]={"SUMA_VolumeLSBlurInMask"};
4243    int  sb = 0, nx_in, nxy_in, ny_in, nz_in,
4244          nx, ny, nz, ih, nxyz_o;
4245    MRI_vectim *vecim=NULL;
4246    float *mm=NULL, dx , dy , dz, na, redx[3];
4247    THD_3dim_dataset *blurred= NULL, *tset=NULL;
4248    MCW_cluster *nbhd=NULL ;
4249    float *fa=NULL;
4250    MRI_IMAGE *imin=NULL;
4251    SUMA_Boolean LocalHead = NOPE;
4252 
4253    SUMA_ENTRY;
4254 
4255    SUMA_S_Warn("This function should not be used");
4256    SUMA_RETURN(0);
4257 
4258    if( FWHM < 0.0f ){ dx = dy = dz = 1.0f ; FWHM = -FWHM ; }
4259    else         { dx = fabsf(DSET_DX(aset)) ;
4260                   dy = fabsf(DSET_DY(aset)) ;
4261                   dz = fabsf(DSET_DZ(aset)) ; }
4262    nx = DSET_NX(aset); ny = DSET_NY(aset); nz = DSET_NZ(aset);
4263 
4264    na = FWHM/2.0;
4265    nbhd = MCW_spheremask( dx,dy,dz , na ) ;
4266    redx[0] = redx[1] = redx[2] = 1.0;
4267    if (mxvx) {
4268       if (na/dx > mxvx) redx[0] = na / (mxvx * dx);
4269       if (na/dy > mxvx) redx[1] = na / (mxvx * dy);
4270       if (na/dz > mxvx) redx[2] = na / (mxvx * dz);
4271    }
4272 
4273 
4274    /* output dset */
4275    if (!(blurred  = THD_reduced_grid_copy(aset, redx))) {
4276       SUMA_S_Err("Failed to create output dset");
4277       SUMA_RETURN(0);
4278    }
4279    nxyz_o = DSET_NVOX(blurred);
4280    if (LocalHead) {
4281       SUMA_S_Notev("nbhd: %p %d voxels.\n"
4282                    "redx = %f %f %f, Reduction from %d to %d voxels\n",
4283                      nbhd, nbhd->num_pt,
4284                      redx[0], redx[1], redx[2], nx*ny*nz, nxyz_o);
4285    }
4286    nx_in = DSET_NX(aset); ny_in = DSET_NY(aset); nz_in = DSET_NZ(aset);
4287    nxy_in = nx_in*ny_in;
4288    for (sb=0; sb<DSET_NVALS(aset); ++sb) {
4289       if (!mm) mm = (float *)calloc(nxyz_o, sizeof(float));
4290       imin = THD_extract_float_brick(sb,aset) ;
4291       fa = MRI_FLOAT_PTR(imin);   /* array of values */
4292 AFNI_OMP_START ;
4293 #pragma omp parallel if( nxyz_o > 500000 ) /* Does not offer much speedups */
4294       {
4295          int  ijk_o, ijk_in, ii, jj, kk,
4296                   nhood, *nind=NULL;
4297          float ws;
4298          if (!nind) nind = (int *)calloc(nbhd->num_pt, sizeof(int));
4299 #pragma omp for
4300          for( ijk_o=0 ; ijk_o < nxyz_o ; ijk_o++ ){   /* parallelized loop */
4301             /* get ii, jj, kk on original resolution */
4302             DSET_1Dindex_to_regrid_ijk(blurred, ijk_o, aset, &ii, &jj, &kk);
4303             ijk_in = ii+jj*nx_in+kk*nxy_in;
4304             if (IN_MASK(cmask,ijk_in)) { /* get a mask for that location */
4305                nhood = mri_load_nbhd_indices( nx_in , ny_in, nz_in,
4306                                  cmask , ii,jj,kk , nbhd, nind);
4307                               /* nhood will not be constant,
4308                                  when you are close to mask's edge */
4309                ws = (float)nhood+1.0;
4310                mm[ijk_o] = fa[ijk_in];
4311                for (ih=0; ih<nhood; ++ih) {
4312                   mm[ijk_o] += fa[nind[ih]];
4313                }
4314                mm[ijk_o] = mm[ijk_o]/ws;
4315             } /* in mask */
4316          }
4317          SUMA_ifree(nind); nind = NULL;
4318       }
4319 AFNI_OMP_END ;
4320       /* Stick mm back into dset */
4321       EDIT_substscale_brick(blurred, sb, MRI_float, mm,
4322                             DSET_BRICK_TYPE(blurred,sb), -1.0);
4323       if (DSET_BRICK_TYPE(blurred,sb) != MRI_float) free(mm);
4324       mm = NULL;
4325       EDIT_BRICK_LABEL(blurred,sb,"LSBlurredInMask");
4326       mri_free(imin); imin = NULL;
4327    } /* sb */
4328    /* now resample back to original resolution */
4329    tset = r_new_resam_dset( blurred, aset, 0.0, 0.0, 0.0,
4330                                  NULL, resam_str2mode("Linear"), NULL, 1, 1);
4331    if (*blurredp) DSET_delete(*blurredp) ; *blurredp = tset; tset = NULL;
4332    SUMA_RETURN(1);
4333 }
4334 
4335 /*!
4336    Blur each sub-brick inside mask.
4337     if unifac = 0.0 : Auto brick factor for each sub-brick (safest)
4338               > 0.0 : Use unifac for all sub-brick factors
4339               = -1.0: Auto factor for 0th brick, other bricks
4340                      get same factor.
4341 */
SUMA_VolumeBlurInMask(THD_3dim_dataset * aset,byte * cmask,THD_3dim_dataset ** blurredp,float FWHM,float unifac,int speed)4342 int SUMA_VolumeBlurInMask(THD_3dim_dataset *aset,
4343                                      byte *cmask,
4344                                      THD_3dim_dataset **blurredp,
4345                                      float FWHM, float unifac,
4346                                      int speed)
4347 {
4348    static char FuncName[]={"SUMA_VolumeBlurInMask"};
4349    float fac = 0.0, *fa=NULL;
4350    MRI_IMAGE *imin=NULL;
4351    int k=0, nfloat_err=0;
4352    THD_3dim_dataset *blurred = *blurredp;
4353    SUMA_Boolean LocalHead = NOPE;
4354 
4355    SUMA_ENTRY;
4356 
4357 
4358    /* get data into float image, preserve scale */
4359    fac = -1.0;
4360    for (k=0; k<DSET_NVALS(aset); ++k) {
4361       imin = THD_extract_float_brick(k,aset) ;
4362 
4363       if (LocalHead &&
4364           (nfloat_err = thd_floatscan( imin->nvox , MRI_FLOAT_PTR(imin) ))) {
4365       WARNING_message("You have %d bad numbers in dset to be blurred\n"
4366                       "Blurring output might be corrupted.", nfloat_err);
4367       }
4368 
4369 
4370       if (speed == 2) {
4371          /* selection of number of reps is a crapshoot here
4372             Need to do better, and take into account voxel
4373             resolution and uneven sizes...*/
4374          mri_blur3D_inmask_NN( imin, cmask, (int)(3.0*FWHM) );
4375       } else if (speed == 1) {
4376          mri_blur3D_addfwhm_speedy(imin, cmask, FWHM);
4377       } else {
4378          SUMA_S_Note("Going the slow route");
4379          mri_blur3D_addfwhm(imin, cmask, FWHM);
4380       }
4381 
4382       /* put results in dset */
4383       fa = MRI_FLOAT_PTR(imin);
4384 
4385       /* Put result in output dset */
4386       if (!blurred) {
4387          blurred = EDIT_full_copy(aset, FuncName);
4388          *blurredp = blurred;
4389       }
4390 
4391       if (unifac > 0.0) fac = unifac;
4392       else if (unifac == -1.0) {
4393          if (k==0) fac = -1.0; /* auto at 1st sub-brick */
4394          else fac = DSET_BRICK_FACTOR(blurred, k - 1);
4395       } else fac = -1.0;
4396 
4397       SUMA_LHv("aset %s, k %d, fac %f\n",
4398             DSET_PREFIX(aset), k, fac);
4399       EDIT_substscale_brick(blurred, k, MRI_float, fa,
4400                               DSET_BRICK_TYPE(blurred,k), fac);
4401       if (DSET_BRICK_TYPE(blurred,k) == MRI_float) {
4402          mri_clear_and_free(imin); /* data pointer was recycled */
4403       } else {
4404          mri_free(imin);
4405       }
4406       imin = NULL; fa = NULL;
4407       EDIT_BRICK_LABEL(blurred,k,"BlurredInMask");
4408    }
4409 
4410    SUMA_RETURN(1);
4411 }
4412 
4413 /*!
4414    Blur the volume with AFNI's EDIT_blur_volume
4415 */
SUMA_VolumeBlur(THD_3dim_dataset * aset,byte * cmask,THD_3dim_dataset ** blurredp,float FWHM)4416 int SUMA_VolumeBlur(THD_3dim_dataset *aset,
4417                    byte *cmask,
4418                    THD_3dim_dataset **blurredp,
4419                    float FWHM)
4420 {
4421    static char FuncName[]={"SUMA_VolumeBlur"};
4422    float *fa=NULL;
4423    MRI_IMAGE *imin=NULL;
4424    int k=0, iii, n_avg=0;
4425    double avg;
4426    THD_3dim_dataset *blurred = *blurredp;
4427    EDIT_options *edopt=NULL;
4428    SUMA_Boolean LocalHead = NOPE;
4429 
4430    SUMA_ENTRY;
4431 
4432    if (!blurred) {
4433       blurred = EDIT_full_copy(aset, FuncName);
4434       *blurredp = blurred;
4435    }
4436 
4437    /* copy data into output and plug masked areas with mean of dset */
4438    for (k=0; k<DSET_NVALS(aset); ++k) {
4439       /* padd masked region with average over image */
4440       imin = THD_extract_float_brick(k,aset) ;
4441       fa = MRI_FLOAT_PTR(imin);
4442       for (iii=0; iii<DSET_NVOX(aset); ++iii) {
4443          avg = 0;
4444          if (IN_MASK(cmask, iii)) { avg += fa[iii]; ++n_avg; }
4445          avg /= (double)n_avg;
4446       }
4447       for (iii=0; iii<DSET_NVOX(aset); ++iii) {
4448          if (!IN_MASK(cmask, iii)) fa[iii] = (float)avg;
4449       }
4450       EDIT_substscale_brick(blurred, k,
4451                      MRI_float, fa,
4452                      DSET_BRICK_TYPE(blurred,k), DSET_BRICK_FACTOR(aset,k));
4453       if (DSET_BRICK_TYPE(blurred,k) == MRI_float) {
4454          mri_clear_and_free(imin);
4455       } else {
4456          mri_free(imin);
4457       }
4458       imin = NULL; fa = NULL;
4459       EDIT_BRICK_LABEL(blurred,k,"BlurredNoMask");
4460    }
4461 
4462    edopt = SUMA_BlankAfniEditOptions();
4463    edopt->blur = FWHM_TO_SIGMA(FWHM);
4464    if (debug > 1) edopt->verbose = 1;
4465    EDIT_blur_allow_fir(0) ;
4466    EDIT_one_dataset( blurred , edopt);
4467    SUMA_free(edopt);
4468 
4469    SUMA_RETURN(1);
4470 }
4471 
4472 
4473 /*!Energy assigned to the contrast between voxel values a0 and a1.
4474    Sign of contrast is irrelevant
4475    Bigger contrast --> Higher energy
4476    Denominator is meant to neutralize the effect of bias field.
4477 */
4478    /*
4479    EDGE_EN1: First pass, seems to work.  */
4480    #define EDGE_EN1(a1,a0, d0, d1) (SUMA_ABS((a1)-(a0))/((d1)+(d0)+0.01))
4481    #define EDGE_EN0(a1,a0) (SUMA_ABS((a1)-(a0)))
SUMA_EdgeEnergy(short * a,float af,short * b,float bf,int Ni,int Nj,int Nk,short * c,short k1,short k2,byte * mask,SUMA_CLASS_STAT * cs,int method,short * skel,int * n_en)4482 double SUMA_EdgeEnergy(short *a, float af, short *b, float bf,
4483                       int Ni, int Nj, int Nk,
4484                       short *c, short k1, short k2,
4485                       byte *mask, SUMA_CLASS_STAT *cs,
4486                       int method, short *skel,
4487                       int *n_en) {
4488    static char FuncName[]={"SUMA_EdgeEnergy"};
4489    int ii, jj, kk, Nij, Nijk, off, n1, n0;
4490    short c1, c2;
4491    double en = 0.0;
4492 
4493    SUMA_ENTRY;
4494 
4495    c1 = cs->keys[k1];
4496    c2 = cs->keys[k2];
4497 
4498    *n_en = 0;
4499    Nij = Ni*Nj; Nijk = Nij*Nk;
4500    if (skel) memset(skel, 0, Nijk*sizeof(short));
4501 
4502    if (!b) bf = 1.0;
4503 
4504    /* The i direction */
4505    for (kk=0; kk<Nk; ++kk) { for (jj=0; jj<Nj; ++jj) {
4506       off = jj*Ni+kk*Nij;
4507       for (ii=1; ii<Ni; ++ii) {
4508          n1 = ii+off; n0 = n1-1;
4509          if ( IN_MASK(mask, n1) &&
4510               IN_MASK(mask, n0) &&
4511               c[n1] != c[n0]              &&
4512              (c[n1] == c1 || c[n1] == c2) &&
4513              (c[n0] == c1 || c[n0] == c2) ) {
4514             if (skel) {
4515                skel[n1] = c[n1]; skel[n0] = c[n0];
4516             }
4517 
4518             switch (method) {
4519                case 1:
4520                   /* Passing bias estimate in denominator, not
4521                   particularly exciting since those can be lousy
4522                   Has been tested in no bias case so far and works ok,
4523                   also works well in presence of bias field, even when
4524                   field is ignored.
4525                   Looking at edges in presence of bias field shows that
4526                   results not all that sensitive to bias. Though edges in
4527                   example below are not along boundaries, bias effect should
4528                   be comparable:
4529                   3dcalc   -a banat+orig. \
4530                            -b 'a[-1,0,0,0]' -c 'a[1,0,0,0]' -d 'a[0,-1,0,0]' \
4531                            -e 'a[0,1,0,0]' -f 'a[0,0,-1,0]' -g 'a[0,0,1,0]' \
4532                            -h banat.ns+orig. \
4533                            -expr '(a-(b+c+d+e+f+g)/6)*step(h)' -prefix 'contr'
4534 
4535                   3dcalc   -a banat+orig. \
4536                            -b 'a[-1,0,0,0]' -c 'a[1,0,0,0]' -d 'a[0,-1,0,0]' \
4537                            -e 'a[0,1,0,0]' -f 'a[0,0,-1,0]' -g 'a[0,0,1,0]' \
4538                            -h banat.ns+orig. \
4539                         -expr '(a-(b+c+d+e+f+g)/6)/(a+(b+c+d+e+f+g)/6)*step(h)'\
4540                         -prefix 'contr_rat'
4541                   */
4542                   if (b) { en += EDGE_EN1(a[n1], a[n0], b[n1], b[n0]); }
4543                   else   { en += EDGE_EN0(a[n1], a[n0]); }
4544                   break;
4545                case 2:
4546                   /* (a-b)/(a+b) is independent of bias field.
4547                   However the ratio changes the energy rankings
4548                   from (a-b) alone. The energy sum is then multiplied
4549                   by (Mean(a)+Mean(b). Works OK too, but not better than 1*/
4550                   en += EDGE_EN1(a[n1], a[n0], a[n1], a[n0]);
4551                   break;
4552             }
4553             ++(*n_en);
4554             /*fprintf(stderr,"%d %d, %d, %d, %f, %f, %f\n",
4555                         a[n1], a[n0], b[n1], b[n0], af, bf,
4556                         EDGE_EN1(a[n1], a[n0], b ? b[n1]:1.0, b ? b[n0]:1.0)); */
4557          }
4558       }
4559    } }
4560 
4561    /* the j direction */
4562    for (kk=0; kk<Nk; ++kk) { for (ii=0; ii<Ni; ++ii) {
4563       off = ii+kk*Nij;
4564       for (jj=1; jj<Nj; ++jj) {
4565          n1 = (jj*Ni)+off; n0 = n1-Ni;
4566          if ( IN_MASK(mask, n1) &&
4567               IN_MASK(mask, n0) &&
4568               c[n1] != c[n0]              &&
4569              (c[n1] == c1 || c[n1] == c2) &&
4570              (c[n0] == c1 || c[n0] == c2) ) {
4571             if (skel) {
4572                skel[n1] = c[n1]; skel[n0] = c[n0];
4573             }
4574             switch (method) {
4575                case 1:
4576                   if (b) { en += EDGE_EN1(a[n1], a[n0], b[n1], b[n0]); }
4577                   else   { en += EDGE_EN0(a[n1], a[n0]); }
4578                   break;
4579                case 2:
4580                   en += EDGE_EN1(a[n1], a[n0], a[n1], a[n0]);
4581                   break;
4582             }
4583             ++(*n_en);
4584          }
4585       }
4586    } }
4587 
4588    /* the k direction */
4589    for (ii=0; ii<Ni; ++ii) { for (jj=0; jj<Nj; ++jj) {
4590       off = ii+jj*Ni;
4591       for (kk=1; kk<Nk; ++kk) {
4592          n1 = (kk*Nij)+off; n0 = n1-Nij;
4593          if ( IN_MASK(mask, n1) &&
4594               IN_MASK(mask, n0) &&
4595               c[n1] != c[n0]              &&
4596              (c[n1] == c1 || c[n1] == c2) &&
4597              (c[n0] == c1 || c[n0] == c2) ) {
4598             if (skel) {
4599                skel[n1] = c[n1]; skel[n0] = c[n0];
4600             }
4601             switch (method) {
4602                case 1:
4603                   if (b) { en += EDGE_EN1(a[n1], a[n0], b[n1], b[n0]); }
4604                   else   { en += EDGE_EN0(a[n1], a[n0]); }
4605                   break;
4606                case 2:
4607                   en += EDGE_EN1(a[n1], a[n0], a[n1], a[n0]);
4608                   break;
4609             }
4610             ++(*n_en);
4611          }
4612       }
4613    } }
4614 
4615    en *= af/bf;
4616 
4617    switch (method) {
4618       case 1:
4619          en = 2.0* en / SUMA_ABS( SUMA_get_Stat(cs, cs->label[k2], "mean")-
4620                                   SUMA_get_Stat(cs, cs->label[k1], "mean") );
4621          break;
4622       case 2:
4623          en = en * (SUMA_get_Stat(cs, cs->label[k2], "mean")+
4624                     SUMA_get_Stat(cs, cs->label[k1], "mean") );
4625          break;
4626       default:
4627          SUMA_S_Errv("Stupid method %d\n", method);
4628          SUMA_RETURN(0);
4629    }
4630    SUMA_RETURN(en);
4631 }
4632 
SUMA_DsetEdgeEnergy(THD_3dim_dataset * aset,THD_3dim_dataset * cset,byte * mask,THD_3dim_dataset * fset,THD_3dim_dataset * skelset,SUMA_CLASS_STAT * cs,int method,int * UseK,int N_kok)4633 double SUMA_DsetEdgeEnergy(THD_3dim_dataset *aset,
4634                       THD_3dim_dataset *cset,
4635                       byte *mask,
4636                       THD_3dim_dataset *fset,
4637                       THD_3dim_dataset *skelset,
4638                       SUMA_CLASS_STAT *cs, int method,
4639                       int *UseK, int N_kok)
4640 {
4641    static char FuncName[]={"SUMA_DsetEdgeEnergy"};
4642    double en=0.0, env[64];
4643    short *a=NULL;
4644    short *f=NULL;
4645    short *c=NULL;
4646    short *skel=NULL;
4647    float af=1.0, ff= 1.0;
4648    int c1,c2, ke, n_env[64], n_en=0, sum_n_en=0, kc1, kc2;
4649    char slab[256];
4650    SUMA_Boolean LocalHead = NOPE;
4651 
4652    SUMA_ENTRY;
4653 
4654    if (fset) {
4655       f = (short *)DSET_ARRAY(fset, 0);
4656       ff = DSET_BRICK_FACTOR(fset,0); if (ff == 0.0) ff = 1.0;
4657    } else {
4658       f = NULL;
4659    }
4660    a = (short *)DSET_ARRAY(aset, 0);
4661    af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0) af = 1.0;
4662    c = (short *)DSET_ARRAY(cset,0);
4663 
4664    ke = 0; sum_n_en = 0.0;
4665    for (kc1=0; kc1<N_kok; ++kc1) {
4666       for (kc2=kc1+1; kc2<N_kok; ++kc2) {
4667          c1 = UseK[kc1]; c2 = UseK[kc2];
4668          snprintf(slab,64,"%s-e-%s", cs->label[c1], cs->label[c2]);
4669          if (skelset) {
4670             skel = (short *)DSET_ARRAY(skelset, ke);
4671             EDIT_BRICK_LABEL(skelset,ke,slab);
4672          }
4673 
4674          en = SUMA_EdgeEnergy(a, af, f, ff,
4675                   DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
4676                   c, c1, c2, mask, cs, method, skel,  &n_en);
4677          env[ke] = en;
4678          n_env[ke] = n_en; sum_n_en += n_en;
4679          SUMA_LHv("%s %s:     Energy %f, Nedges %d,         E/N = %f\n",
4680                            cs->label[c1], cs->label[c2],
4681                            en,  n_en,
4682                            env[ke]/(float)n_en);
4683          ++ke;
4684       }
4685    }
4686 
4687    /* combine energies from each combination,
4688       weigh by number of edges involved */
4689 
4690    en = 0;
4691    for (c1=0; c1<ke; ++c1) {
4692       en += env[c1]/(double)n_env[c1];
4693    }
4694    SUMA_RETURN(en);
4695 }
4696 
SUMA_estimate_bias_field(SEG_OPTS * Opt,int polorder,THD_3dim_dataset * aset,THD_3dim_dataset * cset,THD_3dim_dataset * pset,THD_3dim_dataset * pout)4697 THD_3dim_dataset *SUMA_estimate_bias_field (SEG_OPTS *Opt,
4698                                        int polorder,
4699                                        THD_3dim_dataset *aset,
4700                                        THD_3dim_dataset *cset,
4701                                        THD_3dim_dataset *pset,
4702                                        THD_3dim_dataset *pout) {
4703    static char FuncName[]={"SUMA_estimate_bias_field"};
4704    int i, j,k, dtable_key[1024], N_g[256], N_mm = 0;
4705    THD_3dim_dataset *dmset=NULL;
4706    float *dmv=NULL, FWHMbias=0.0;
4707    short *c=NULL, *a=NULL;
4708    char *str_lab=NULL;
4709    byte *mm=NULL;
4710    NI_str_array *bc=NULL, *bsc=NULL;
4711    MRI_IMAGE *imout=NULL, *imin=NULL;
4712    double M_dg=0.0, M_v[256];
4713    Dtable *vl_dtable=NULL;
4714 
4715    SUMA_ENTRY;
4716 
4717    /* checks */
4718    if (!aset || !cset || !Opt->clss || !Opt->bias_classes) {
4719       SUMA_S_Errv("Bad input %p %p %p %p\n",
4720                   aset, cset, Opt->clss, Opt->bias_classes);
4721       SUMA_RETURN(NULL);
4722    }
4723 
4724    /* init */
4725    if (!pout) {
4726       NEW_SHORTY(aset, 1, Opt->frefix, pout);
4727    }
4728    if (!pout) SUMA_RETURN(NULL);
4729    if( !THD_ok_overwrite() && THD_is_file( DSET_HEADNAME(pout) ) ){
4730       SUMA_S_Warnv("Output file %s already exists and not in overwrite mode!\n",
4731                   DSET_HEADNAME(pout) ) ;
4732    }
4733 
4734    if (polorder < 0) polorder = Opt->bias_param;
4735    if (polorder < 0) {
4736       SUMA_S_Err("Failed to set polorder");
4737       SUMA_RETURN(NULL);
4738    }
4739 
4740    /* How many groups of classes do we have? */
4741    vl_dtable = SUMA_LabelsKeys2Dtable( Opt->clss->str, Opt->clss->num,
4742                                        Opt->keys);
4743    c = (short *)DSET_ARRAY(cset, 0);
4744    a = (short *)DSET_ARRAY(aset, 0);
4745    mm = (byte *)calloc (DSET_NVOX(cset), sizeof(byte));
4746    bc = NI_strict_decode_string_list(Opt->bias_classes,";");
4747    Opt->N_biasgroups = bc->num;
4748    for (i=0; i<Opt->N_biasgroups; ++i) {
4749       if (Opt->debug > 1)
4750          fprintf(stderr,"   Group %s %d/%d\n", bc->str[i], i+1, bc->num);
4751       /* which keys belong to this group ? */
4752       bsc = NI_strict_decode_string_list(bc->str[i],", ");
4753       for (j=0; j<bsc->num; ++j) {
4754          if (Opt->debug > 1)
4755             fprintf(stderr,"     Sub Group %s %d/%d\n",
4756                            bsc->str[j], j+1, bsc->num);
4757          if ((dtable_key[j] = SUMA_KeyofLabel_Dtable(vl_dtable, bsc->str[j]))<0){
4758             SUMA_S_Errv("Failed to find bias label %s in table", bsc->str[j]);
4759             SUMA_RETURN(NULL);
4760          }
4761       }
4762       NI_delete_str_array(bsc );
4763       /* mark the class group */
4764       for (j=0; j<bsc->num; ++j) {
4765          for (k=0; k<DSET_NVOX(aset);++k) {
4766             if (  c[k] == dtable_key[j] &&
4767                   IN_MASK(Opt->cmask,k) )  { mm[k] = i+1; ++N_mm; }
4768          }
4769       }
4770    }
4771    NI_delete_str_array(bc );
4772    destroy_Dtable(vl_dtable); vl_dtable=NULL;
4773 
4774    /* store mask for debugging and some stats later on*/
4775    {
4776       short *am=NULL;
4777       if (!Opt->gset) {
4778          NEW_SHORTY(aset, 1, "bias_estimate_groups", Opt->gset);
4779       }
4780       am = (short *)DSET_ARRAY(Opt->gset,0);
4781       for (i=0; i<DSET_NVOX(aset);++i) {
4782          am[i]=mm[i];
4783       }
4784    }
4785    if (Opt->debug > 1) {
4786       DSET_quiet_overwrite(Opt->gset);
4787    }
4788 
4789    /* calculate the mean (unscaled) for each group */
4790    if (!group_mean(Opt, aset, mm, Opt->pweight ? pset:NULL,
4791                    Opt->N_biasgroups, M_v, 1)) {
4792       ERROR_exit("Could not calculate scaled mean\n");
4793    }
4794 
4795    /* Create a demeaned version of the data */
4796    imin = THD_extract_float_brick(0,aset) ;
4797    dmv = MRI_FLOAT_PTR(imin) ;
4798    for (i=0; i<DSET_NVOX(aset);++i) {
4799       if (mm[i]) {
4800          dmv[i] /= M_v[mm[i]-1];
4801       } else {
4802          dmv[i] = 0.0;
4803       }
4804    }
4805 
4806 
4807    if (Opt->debug > 1) {/* store scaled intensities */
4808       THD_3dim_dataset *pbb=NULL;
4809       short *am=NULL;
4810       if (!pbb) {
4811          NEW_SHORTY(aset, 1, "bias_data", pbb);
4812       }
4813       am = (short *)DSET_ARRAY(pbb,0);
4814       for (i=0; i<DSET_NVOX(aset);++i) {
4815          am[i]=(short)(dmv[i]*10000.0);
4816       }
4817       EDIT_BRICK_FACTOR(pbb,0, 1.0/10000.0);
4818       DSET_quiet_overwrite(pbb);
4819       DSET_delete(pbb); pbb=NULL;
4820    }
4821 
4822    FWHMbias = 25;
4823 
4824    if (1) {/* fill the thing, then blur*/
4825       byte *fm = (byte *)SUMA_calloc(DSET_NVOX(aset), sizeof(byte));
4826       if (Opt->debug > 1) SUMA_S_Note("Filling then blurring");
4827       SUMA_mri_volume_infill_zoom(imin, 0, 0, -1);
4828       for (i=0; i<DSET_NVOX(aset);++i) {
4829          if (SUMA_ABS(dmv[i]-0.0f)>0.00001) fm[i]=1;
4830       }
4831       mri_blur3D_addfwhm(imin, fm, FWHMbias);
4832       SUMA_ifree(fm);
4833    } else {/* blur then do polyfit, bad fitting artifacts, not worth it*/
4834       if (Opt->debug > 1) SUMA_S_Note("Blurring then fitting with mri_polyfit");
4835       mri_blur3D_addfwhm(imin, mm, FWHMbias);
4836       /* do the fit */
4837       mri_polyfit_verb(Opt->debug) ;
4838       /* now takes 'exar' parameter; pass NULL    29 Dec 2012 [rickr] */
4839       if (!(imout = mri_polyfit(imin, polorder, NULL, mm, 0.0, Opt->fitmeth))){
4840          ERROR_exit("Failed to fit");
4841       }
4842       dmv = MRI_FLOAT_PTR(imout) ;
4843    }
4844 
4845 
4846 
4847    if (Opt->debug > 1) {/* save the fit */
4848       THD_3dim_dataset *pbb=NULL;
4849       short *am=NULL;
4850       if (!pbb) {
4851          NEW_SHORTY(aset, 1, "bias_estimate", pbb);
4852       }
4853       am = (short *)DSET_ARRAY(pbb,0);
4854       for (i=0; i<DSET_NVOX(aset);++i) {
4855          am[i]=(short)(dmv[i]*10000.0);
4856       }
4857       EDIT_BRICK_FACTOR(pbb,0, 1.0/10000.0);
4858       DSET_quiet_overwrite(pbb);
4859       DSET_delete(pbb); pbb=NULL;
4860    }
4861 
4862    /* calculate average bias */
4863    M_dg = 0.0;
4864    for (i=0; i<DSET_NVOX(aset); ++i) {
4865       if (mm[i]) M_dg += dmv[i];
4866    }
4867    M_dg /= (double)N_mm;  /* grand mean */
4868 
4869 
4870 
4871    /* scale bias by mean to make output image be closer to input */
4872    for (i=0; i<DSET_NVOX(aset); ++i) dmv[i] /= M_dg;
4873 
4874    /* store */
4875    EDIT_substscale_brick(pout, 0, MRI_float, dmv, MRI_short, -1.0);
4876    EDIT_BRICK_LABEL(pout,0,"BiasField");
4877 
4878    /* cleanup  */
4879    mri_free(imin); imin = NULL;
4880    if (imout) mri_free(imout); imout = NULL;
4881 
4882 
4883    SUMA_RETURN(pout);
4884 }
4885 
SUMA_Class_k_Label_Locator(SUMA_CLASS_STAT * cs,char * label)4886 int SUMA_Class_k_Label_Locator(SUMA_CLASS_STAT *cs, char *label)
4887 {
4888    static char FuncName[]={"SUMA_Class_k_Label_Locator"};
4889    int k=0;
4890 
4891    SUMA_ENTRY;
4892 
4893    if (!label) SUMA_RETURN(-1);
4894 
4895    while (k < cs->N_label) {
4896       if (!strcmp(cs->label[k],label)) SUMA_RETURN(k);
4897       ++k;
4898    }
4899 
4900    SUMA_RETURN(-1);
4901 }
4902 
SUMA_Class_k_Key_Locator(SUMA_CLASS_STAT * cs,int kk)4903 int SUMA_Class_k_Key_Locator(SUMA_CLASS_STAT *cs, int kk)
4904 {
4905    static char FuncName[]={"SUMA_Class_k_Key_Locator"};
4906    int k=0;
4907 
4908    SUMA_ENTRY;
4909 
4910    while (k < cs->N_label) {
4911       if (k == kk) SUMA_RETURN(k);
4912       ++k;
4913    }
4914 
4915    SUMA_RETURN(-1);
4916 }
4917 
4918 /*!
4919    Find indices of particular classes in SUMA_CLASS_STAT
4920    \param cs: The classes statistics structure
4921    \param action: A string specifying what to do with the
4922                   'value' string
4923    \param value: A string of parameters, ';' separated
4924                   for the 'action' string. If Value is NULL,
4925                   everything matches.
4926       action == "classes_string": Search for classes in 'value'
4927                 "not_classes_string": Search for classes not in 'value'
4928    \param UseK: A vector of indices into classes matching the search.
4929                 Can be NULL if you just care for the count.
4930    \return N_UseK: the number of classes found.
4931                    -1 in error
4932 */
SUMA_Class_k_Selector(SUMA_CLASS_STAT * cs,char * action,char * value,int * UseK)4933 int SUMA_Class_k_Selector(
4934    SUMA_CLASS_STAT *cs, char *action, char *value, int *UseK)
4935 {
4936    static char FuncName[]={"SUMA_Class_k_Selector"};
4937    NI_str_array *bc=NULL;
4938    int k, ii, N_kok;
4939 
4940    SUMA_ENTRY;
4941 
4942    N_kok = -1;
4943 
4944    if (!strcmp(action, "classes_string")) {
4945       if (!value) {
4946          N_kok=cs->N_label;
4947          if (UseK) {
4948             for (k=0; k<N_kok; ++k) UseK[k] = k;
4949          }
4950       } else {
4951          bc = NI_strict_decode_string_list(value,";");
4952          N_kok=0;
4953          for (ii=0; ii < bc->num; ++ii) {
4954             for (k=0; k<cs->N_label; ++k) {
4955                if (!strcmp(bc->str[ii], cs->label[k])) {
4956                   if (UseK) { UseK[N_kok] = k; }
4957                   ++N_kok;
4958                }
4959             }
4960          }
4961          NI_delete_str_array(bc );
4962       }
4963       SUMA_RETURN(N_kok);
4964    }
4965 
4966    if (!strcmp(action, "not_classes_string")) {
4967       if (!value) {
4968          N_kok=cs->N_label;
4969          if (UseK) {
4970             for (k=0; k<N_kok; ++k) UseK[k] = k;
4971          }
4972       } else {
4973          bc = NI_strict_decode_string_list(value,";");
4974          N_kok=0;
4975          for (ii=0; ii < bc->num; ++ii) {
4976             for (k=0; k<cs->N_label; ++k) {
4977                if (strcmp(bc->str[ii], cs->label[k])) {
4978                   if (UseK) {
4979                      UseK[N_kok] = k;
4980                   }
4981                   ++N_kok;
4982                }
4983             }
4984          }
4985          NI_delete_str_array(bc );
4986       }
4987       SUMA_RETURN(N_kok);
4988    }
4989 
4990    /* See LabelToGroupedIndex to add actions for getting sub-groups */
4991 
4992    SUMA_S_Errv("Action %s not supported\n", action);
4993 
4994    SUMA_RETURN(-1);
4995 }
4996 
SUMA_estimate_bias_field_Wells(SEG_OPTS * Opt,byte * cmask,SUMA_CLASS_STAT * cs,float fwhm,char * bias_classes,THD_3dim_dataset * Aset,THD_3dim_dataset * pstCgALL,THD_3dim_dataset ** Bsetp)4997 int SUMA_estimate_bias_field_Wells (SEG_OPTS *Opt,
4998                                        byte *cmask, SUMA_CLASS_STAT *cs,
4999                                        float fwhm, char *bias_classes,
5000                                        THD_3dim_dataset *Aset,
5001                                        THD_3dim_dataset *pstCgALL,
5002                                        THD_3dim_dataset **Bsetp)
5003 {
5004    static char FuncName[]={"SUMA_estimate_bias_field_Wells"};
5005    int ijk, k, N_kok, kok, *UseK , ii, jj, kk;
5006    THD_3dim_dataset *Rset=NULL, *Psset=NULL, *Bset = *Bsetp;
5007    float *fpstCgALL, fAset, fBset, *R, *Ps;
5008    char *str_lab=NULL;
5009    MRI_IMAGE *imout=NULL, *imin=NULL;
5010    double df, sdf, Ai, Gik, Ri, *Mg, *Sg;
5011    static int iter = 0, iwarn=0;
5012    struct  timeval tti;
5013    SUMA_Boolean LocalHead = NOPE;
5014 
5015    SUMA_ENTRY;
5016 
5017    /* checks */
5018    if (!Aset || !pstCgALL || !cs) {
5019       SUMA_S_Errv("Bad input %p %p %p \n",
5020                   Aset, pstCgALL, cs);
5021       SUMA_RETURN(0);
5022    }
5023 
5024    /* init */
5025    if (!Bset) {
5026       NEW_SHORTY(Aset, 1, "bWells", Bset);
5027       *Bsetp = Bset;
5028    }
5029 
5030    if (!Bset) SUMA_RETURN(0);
5031 
5032    UseK = (int *)SUMA_calloc(cs->N_label, sizeof(int));
5033    if ((N_kok = SUMA_Class_k_Selector(cs, "classes_string",
5034                                           bias_classes, UseK))<0) {
5035       SUMA_S_Err("Failed to find classes");
5036       SUMA_RETURN(0);
5037    }
5038 
5039    NEW_FLOATY(Aset, 1, "Rset", Rset);
5040    NEW_FLOATY(Aset, 1, "Psset", Psset);
5041 
5042    Mg = SUMA_get_Stats(cs, "meanL");
5043    Sg = SUMA_get_Stats(cs, "stdvL");
5044    fpstCgALL = (float*)SUMA_calloc(cs->N_label, sizeof(float));
5045    GET_BFs(pstCgALL, fpstCgALL);
5046    fAset = DSET_BRICK_FACTOR(Aset,0); if (fAset == 0.0) fAset = 1.0;
5047    R = (float *)DSET_ARRAY(Rset,0);
5048    Ps = (float *)DSET_ARRAY(Psset,0);
5049    for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
5050       if (IN_MASK(cmask, ijk)) {
5051          sdf = 0.0; Ri = 0.0;
5052          for (kok=0; kok<N_kok; ++kok) {
5053             k = UseK[kok];
5054             GSCVAL(pstCgALL, k, ijk, fpstCgALL[k], Gik);
5055             GSCVAL(Aset, 0, ijk, fAset, Ai);
5056             if (Ai == 0) {
5057                if (!iwarn) {
5058                   SUMA_S_Warn("Have 0s in dset, this condition is not handled\n"
5059                            "robustly yet.\n");
5060                   ++iwarn;
5061                }
5062                Ai = 11;
5063             }
5064             df = Gik/(Sg[k]*Sg[k]);
5065             Ri += (log(Ai)-Mg[k])*df;
5066             sdf += df;
5067             if (ijk == Opt->VoxDbg || isnan(Ri)) {
5068                short *jc=DSET_ARRAY(Aset,0);
5069                SUMA_1D_2_3D_index(ijk, ii, jj, kk,
5070                                   DSET_NX(Aset), DSET_NX(Aset)*DSET_NY(Aset));
5071                SUMA_S_Notev("Debug or NAN for voxel %d [%d %d %d]\n"
5072                         "Cls %s, M %f, S %f, Ai %f (fAset=%f * %d)\n"
5073                         "Gik %f, Ri %f, sdf %f\n",
5074                         ijk, ii, jj,  kk,
5075                         cs->label[k], Mg[k], Sg[k], Ai, fAset, jc[ijk],
5076                         Gik, Ri, sdf);
5077 
5078                if (isnan(Ri)) {
5079                   SUMA_Seg_Write_Dset(Opt->proot, "AsetNAN",
5080                                        Aset, iter, Opt->hist);
5081                   SUMA_S_Err("Have NAN, will return");
5082                   SUMA_RETURN(0);
5083                }
5084             }
5085          }
5086          R[ijk] = Ri;
5087          Ps[ijk] = sdf;
5088 
5089       }
5090    }
5091 
5092    if (Opt->debug > 1) {/* store scaled intensities */
5093       SUMA_Seg_Write_Dset(Opt->proot, "Rset-PreBlur", Rset, iter, Opt->hist);
5094       SUMA_Seg_Write_Dset(Opt->proot, "Psset-PreBlur", Psset, iter, Opt->hist);
5095    }
5096 
5097    SUMA_etime (&tti, 0);
5098 
5099 
5100    /* Blur the two sets */
5101    if (LocalHead || Opt->debug > 1) {
5102       SUMA_S_Note("Blurring Psset & Rset");
5103    }
5104 
5105 if (Opt->blur_meth == SEG_BIM || Opt->blur_meth == SEG_BNN) {
5106    AFNI_OMP_START ;
5107    #pragma omp parallel
5108    {
5109       THD_3dim_dataset *bb[2];
5110       int is=0;
5111 
5112       bb[0] = Rset;
5113       bb[1] = Psset;
5114    #pragma omp for
5115       for (is=0; is<2; ++is) {
5116          if (!(SUMA_VolumeBlurInMask(bb[is],
5117                                cmask,
5118                                bb+is, fwhm, 0.0,
5119                                Opt->blur_meth == SEG_BIM ? 1:2))) {
5120             SUMA_S_Err("Failed to blur");
5121          }
5122       }
5123    } /* end OpenMP */
5124    AFNI_OMP_END ;
5125 
5126    if (Opt->debug) { SUMA_S_Notev("%f smoothing meth %d duration %f seconds\n",
5127                                    fwhm, Opt->blur_meth, SUMA_etime (&tti, 1)); }
5128 } else if (Opt->blur_meth == SEG_LSB) {
5129    if (!(SUMA_VolumeLSBlurInMask(Rset,
5130                             cmask,
5131                             &Rset, fwhm, 5))) {
5132          SUMA_S_Err("Failed to LSblur");
5133          SUMA_RETURN(0);
5134    }
5135    if (!(SUMA_VolumeLSBlurInMask(Psset,
5136                             cmask,
5137                             &Psset, fwhm, 5))) {
5138          SUMA_S_Err("Failed to LSblur");
5139          SUMA_RETURN(0);
5140    }
5141    if (Opt->debug) { SUMA_S_Notev("%f  smoothing meth %d duration %f seconds\n",
5142                                    fwhm, Opt->blur_meth, SUMA_etime (&tti, 1)); }
5143 
5144 } else if (Opt->blur_meth == SEG_BFT) {
5145    if (!(SUMA_VolumeBlur(Rset,
5146                          cmask,
5147                          &Rset, fwhm))) {
5148          SUMA_S_Err("Failed to FTblur");
5149          SUMA_RETURN(0);
5150    }
5151    if (!(SUMA_VolumeBlur(Psset,
5152                          cmask,
5153                          &Psset, fwhm))) {
5154          SUMA_S_Err("Failed to FTblur");
5155          SUMA_RETURN(0);
5156    }
5157    if (Opt->debug) { SUMA_S_Notev("%f  smoothing meth %d duration %f seconds\n",
5158                                    fwhm, Opt->blur_meth, SUMA_etime (&tti, 1)); }
5159 } else {
5160    SUMA_S_Err("Bad blur option");
5161    SUMA_RETURN(0);
5162 }
5163    if (Opt->debug > 1) {/* store scaled intensities */
5164       SUMA_Seg_Write_Dset(Opt->proot, "Rset-PostBlur", Rset, iter, Opt->hist);
5165       SUMA_Seg_Write_Dset(Opt->proot, "Psset-PostBlur", Psset, iter, Opt->hist);
5166    }
5167 
5168    fBset = 1.0/10000.0;
5169    R = (float *)DSET_ARRAY(Rset,0);
5170    Ps = (float *)DSET_ARRAY(Psset,0);
5171    for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
5172       if (IN_MASK(cmask, ijk)) {
5173          PSCVAL(Bset, 0, ijk, fBset, exp(R[ijk]/Ps[ijk]));
5174       }
5175    }
5176    EDIT_BRICK_FACTOR(Bset, 0, fBset);
5177 
5178    if (Opt->debug > 1) {/* store scaled intensities */
5179       SUMA_Seg_Write_Dset(Opt->proot, "Bset", Bset, iter, Opt->hist);
5180    }
5181 
5182 
5183    SUMA_ifree(fpstCgALL); SUMA_ifree(UseK);
5184    DSET_delete(Psset); DSET_delete(Rset);
5185 
5186    ++iter;
5187 
5188    SUMA_RETURN(1);
5189 }
5190 
5191 /*!
5192    Apply bias field.
5193 */
SUMA_apply_bias_field(SEG_OPTS * Opt,THD_3dim_dataset * aset,THD_3dim_dataset * fset,THD_3dim_dataset ** xsetp)5194 int SUMA_apply_bias_field (SEG_OPTS *Opt,
5195                                        THD_3dim_dataset *aset,
5196                                        THD_3dim_dataset *fset,
5197                                        THD_3dim_dataset **xsetp) {
5198    static char FuncName[]={"SUMA_apply_bias_field"};
5199    int i;
5200    float *d=NULL;
5201    float bf = 1.0, bfa=1.0, bfb=1.0;
5202    short *b=NULL, *a=NULL;
5203    THD_3dim_dataset *xset = *xsetp;
5204 
5205    SUMA_ENTRY;
5206 
5207    /* checks */
5208    if (!aset || !fset ) {
5209       SUMA_S_Errv("Bad input %p %p \n",
5210                   aset, fset);
5211       SUMA_RETURN(0);
5212    }
5213 
5214    /* init */
5215    if (!xset) {
5216       NEW_SHORTY(aset, 1, Opt->xrefix, xset);
5217       *xsetp = xset;
5218    }
5219    if (!xset) RETURN(0);
5220    if( !THD_ok_overwrite() && THD_is_file( DSET_HEADNAME(xset) ) ){
5221       SUMA_S_Warnv("Output file %s already exists and not in overwrite mode!\n",
5222                   DSET_HEADNAME(xset) ) ;
5223    }
5224 
5225    /* apply the bias field */
5226    if (Opt->debug > 1) INFO_message("Applying field");
5227    bfa = DSET_BRICK_FACTOR(aset,0); if (bfa == 0.0) bfa = 1.0;
5228    bfb = DSET_BRICK_FACTOR(fset,0); if (bfb == 0.0) bfb = 1.0;
5229    b = (short *)DSET_ARRAY(fset,0);
5230    a = (short *)DSET_ARRAY(aset,0);
5231    d = (float *)calloc(DSET_NVOX(xset), sizeof(float));
5232    for (i=0; i<DSET_NVOX(xset); ++i) {
5233       bf = b[i]*bfb;
5234       if (bf > 0.5 && bf < 2.0) /* Extremists at edges cause mayhem
5235                                      with division */
5236          d[i] = (float)a[i]/bf*bfa;
5237       else d[i] = a[i]*bfa;
5238    }
5239    EDIT_substscale_brick(xset, 0, MRI_float, d, MRI_short, -1.0);
5240    EDIT_BRICK_LABEL(xset,0,"BiasCorrected");
5241    free(d); d = NULL;
5242    SUMA_RETURN(1);
5243 }
5244 
bias_stats(SEG_OPTS * Opt,THD_3dim_dataset * aset,THD_3dim_dataset * gset,THD_3dim_dataset * xset,int N_cl)5245 int bias_stats (SEG_OPTS *Opt,
5246                 THD_3dim_dataset *aset, THD_3dim_dataset *gset,
5247                 THD_3dim_dataset *xset, int N_cl) {
5248    int i=0, j = 0;
5249    short *mm=NULL, *a=NULL, *x=NULL;
5250    float af=1.0, xf=1.0;
5251    double n, Asum2, Asum, Xsum2, Xsum,
5252          Amean[N_cl], Astd[N_cl], Xmean[N_cl], Xstd[N_cl];
5253 
5254    ENTRY("bias_stats");
5255 
5256       af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0f) af = 1.0;
5257       xf = DSET_BRICK_FACTOR(xset,0); if (xf == 0.0f) xf = 1.0;
5258    a  = (short *)DSET_ARRAY(aset,0);
5259    x  = (short *)DSET_ARRAY(xset,0);
5260    mm = (short *)DSET_ARRAY(gset,0);
5261    for (j=0; j<N_cl; ++j) {
5262       n = 0;
5263       Asum2 = 0.0; Asum = 0.0; Xsum2 = 0.0; Xsum = 0.0;
5264       for (i=0; i<DSET_NVOX(aset); ++i) {
5265          if (mm[i] == j+1) {
5266             Asum2 += a[i]*a[i];
5267             Asum  += a[i];
5268             Xsum2 += x[i]*x[i];
5269             Xsum  += x[i];
5270             ++n;
5271          }
5272       }
5273       Astd[j] = sqrt((Asum2-Asum*Asum/n)/(n-1))*af;
5274       Xstd[j] = sqrt((Xsum2-Xsum*Xsum/n)/(n-1))*xf;
5275       Amean[j] = Asum/n*af;
5276       Xmean[j] = Xsum/n*xf;
5277       fprintf(stdout,"Group %d, PRE : mean %04.2f   std %04.2f    SNR %04.2f\n"
5278                      "Group %d, POST: mean %04.2f   std %04.2f    SNR %04.2f\n"
5279                    , j+1, Amean[j], Astd[j], Amean[j]/Astd[j],
5280                      j+1, Xmean[j], Xstd[j], Xmean[j]/Xstd[j] );
5281 
5282    }
5283 
5284    RETURN(1);
5285 }
5286 
SUMA_Class_stats(THD_3dim_dataset * aset,THD_3dim_dataset * cset,byte * cmask,int cmask_count,THD_3dim_dataset * pstCgALL,THD_3dim_dataset * priCgALL,THD_3dim_dataset * gold,SUMA_CLASS_STAT * cs,float mixfloor)5287 int SUMA_Class_stats(THD_3dim_dataset *aset,
5288                      THD_3dim_dataset *cset,
5289                      byte *cmask, int cmask_count,
5290                      THD_3dim_dataset *pstCgALL,
5291                      THD_3dim_dataset *priCgALL,
5292                      THD_3dim_dataset *gold,
5293                      SUMA_CLASS_STAT *cs, float mixfloor)
5294 {
5295    static char FuncName[]={"SUMA_Class_stats"};
5296    int i=0, j = 0, sb=0, l, bad=0;
5297    short *a=NULL, *c=NULL, *w=NULL;
5298    float af=1.0, wf=1.0, fpriCgALL;
5299    double n, Asum2, Asum, Amean, Astd, wsum, ff, *nv=NULL, ww=0.0,
5300           la, AmeanL, AsumL, AstdL, Asum2L, *w0=NULL, *mixden=NULL;
5301 
5302    SUMA_ENTRY;
5303 
5304    af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0f) af = 1.0;
5305    a  = (short *)DSET_ARRAY(aset,0);
5306    if (cset) {
5307       c  = (short *)DSET_ARRAY(cset,0);
5308       if (DSET_BRICK_FACTOR(cset,0) != 0.0 && DSET_BRICK_FACTOR(cset,0) != 1.0) {
5309          SUMA_S_Err("Cset factor != 0.0 || != 1.0");
5310          SUMA_RETURN(0);
5311       }
5312    }
5313    if (!pstCgALL) {
5314       if (!c) {
5315          SUMA_S_Err("No classes, and no weighting set");
5316          SUMA_RETURN(0);
5317       }
5318       for (j=0; j<cs->N_label; ++j) {
5319          n = 0;
5320          Asum2 = 0.0; Asum = 0.0;
5321          Asum2L = 0.0; AsumL = 0.0;
5322          for (i=0; i<DSET_NVOX(aset); ++i) {
5323             if (IN_MASK(cmask,i) && c[i] == cs->keys[j]) {
5324                Asum2 += a[i]*a[i];
5325                Asum  += a[i];
5326                la = log(SUMA_MAX_PAIR(a[i]*af,0.00001));
5327                Asum2L += la*la; AsumL += la;
5328                ++n;
5329             }
5330          }
5331          if (n>1 && af != 0.0) {
5332             Astd = sqrt((Asum2-Asum*Asum/n)/(n-1))*af;
5333          } else {
5334             Astd = 0.0;
5335          }
5336          if (n*af != 0.0) Amean = Asum/n*af;
5337          else Amean = 0.0;
5338          if (n>1) {
5339             AstdL = sqrt((Asum2L-AsumL*AsumL/n)/(n-1));
5340          } else {
5341             AstdL = 0.0;
5342          }
5343          if (n) AmeanL = AsumL/n;
5344          else AmeanL = 0.0;
5345 
5346          if (isnan(Astd)) Astd = 0;
5347          if (isnan(AstdL)) AstdL = 0;
5348          SUMA_set_Stat(cs, cs->label[j], "num", n);
5349          SUMA_set_Stat(cs, cs->label[j], "mean", Amean);
5350          SUMA_set_Stat(cs, cs->label[j], "stdv", Astd);
5351          SUMA_set_Stat(cs, cs->label[j], "meanL", AmeanL);
5352          SUMA_set_Stat(cs, cs->label[j], "stdvL", AstdL);
5353          if (cmask_count) SUMA_set_Stat(cs, cs->label[j], "mix", n/cmask_count);
5354          else SUMA_set_Stat(cs, cs->label[j], "mix", 0);
5355       }
5356       SUMA_set_Stat_mix_floor(cs, mixfloor);
5357    } else {
5358       /* Check classes at input */
5359       bad = 0;
5360       for (j=0; j<cs->N_label; ++j) {
5361          if (isnan(SUMA_get_Stat(cs, cs->label[j], "meanL")) ||
5362              isnan(SUMA_get_Stat(cs, cs->label[j], "stdvL")) ||
5363              isnan(SUMA_get_Stat(cs, cs->label[j], "mean")) ||
5364              isnan(SUMA_get_Stat(cs, cs->label[j], "stdv")) ) {
5365             SUMA_S_Err("Bad parameters for class %s", cs->label[j]);
5366             ++bad;
5367          }
5368       }
5369       if (bad) {
5370           SUMA_show_Class_Stat(cs,
5371                         "Bad Stats At SUMA_Class_stats() entry:\n", NULL);
5372           SUMA_RETURN(0);
5373       }
5374       if (DSET_NVALS(pstCgALL) != cs->N_label &&
5375           DSET_NVALS(pstCgALL) != 1) {
5376          SUMA_S_Errv("Weight set must be 1 or %d sub-bricks. Have %d\n",
5377                      cs->N_label, DSET_NVALS(pstCgALL));
5378          SUMA_RETURN(0);
5379       }
5380       mixden = (double *)SUMA_calloc(cs->N_label, sizeof(double));
5381       for (j=0; j<cs->N_label; ++j) {
5382          if (DSET_NVALS(pstCgALL) != 1) {
5383             sb = j;
5384          } else sb = 1;
5385 
5386          if (0 && priCgALL) { /* Need to setup denom for mix frac,
5387                            as in Ashburner 2005.
5388                            I turned it off for now because
5389                            convergence (as deemed from Dice and Bias
5390                            correction in banat) seems better without it */
5391             fpriCgALL =  DSET_BRICK_FACTOR(priCgALL, j);
5392             if (!(w0=SUMA_get_Stats(cs, "mix"))) {
5393                mixden[j] = 0.0;
5394                for (i=0; i<DSET_NVOX(aset); ++i) {
5395                   if (IN_MASK(cmask,i)) {
5396                      GSCVAL(priCgALL, j, i, fpriCgALL, ww);
5397                      mixden[j] += ww;
5398                   }
5399                }
5400                mixden[j] *= (double)cs->N_label;
5401             } else {
5402                mixden[j] = 0.0;
5403                for (i=0; i<DSET_NVOX(aset); ++i) {
5404                   if (IN_MASK(cmask,i)) {
5405                      GSCVAL(priCgALL, j, i, fpriCgALL, ff);
5406                      Asum = 0.0;
5407                      for (l=0; l<cs->N_label; ++l) {
5408                         GSCVAL(priCgALL, l, i, fpriCgALL, ww);
5409                         Asum += w0[l]*ww;
5410                      }
5411                      mixden[j] += ff/Asum;
5412                   }
5413               }
5414             }
5415          }else {
5416             mixden[j] = cmask_count;
5417          }
5418          wf = DSET_BRICK_FACTOR(pstCgALL,sb); if (wf == 0.0f) wf = 1.0;
5419          w = (short *)DSET_ARRAY(pstCgALL,sb);
5420          if (DSET_BRICK_TYPE(pstCgALL,sb) != MRI_short) {
5421             SUMA_S_Errv("Dset %s is not SHORT!\n", DSET_PREFIX(pstCgALL));
5422             exit(1);
5423          }
5424          Asum2 = 0.0; Asum = 0.0; wsum = 0.0;
5425          Asum2L = 0.0; AsumL = 0.0;
5426          for (i=0; i<DSET_NVOX(aset); ++i) {
5427             if (IN_MASK(cmask,i)) {
5428                ww = w[i]*wf;
5429                Asum  += ww*a[i]; wsum += ww;
5430                la = log(SUMA_MAX_PAIR(a[i]*af,0.00001));
5431                AsumL += ww*la;
5432             }
5433          }
5434          Amean = Asum/wsum;
5435          AmeanL = AsumL/wsum;
5436          n = 0.0;
5437          Asum2 = 0.0; Asum = 0.0;
5438          for (i=0; i<DSET_NVOX(aset); ++i) {
5439             if (IN_MASK(cmask,i)) {
5440                ff = (a[i]-Amean);
5441                ww = w[i]*wf;
5442                Asum2  += ww*(ff*ff);
5443                la = log(SUMA_MAX_PAIR(a[i]*af,0.00001))-AmeanL;
5444                Asum2L += ww*(la*la);
5445                if (c && c[i] == cs->keys[j]) ++n;
5446             }
5447          }
5448          Astd = sqrt(Asum2/wsum)*af;
5449          Amean = Amean*af;
5450          AstdL = sqrt(Asum2L/wsum);
5451          if (isnan(Astd)) Astd = 0;
5452          if (isnan(AstdL)) AstdL = 0;
5453 
5454          SUMA_set_Stat(cs, cs->label[j], "num", n);
5455          SUMA_set_Stat(cs, cs->label[j], "mean", Amean);
5456          SUMA_set_Stat(cs, cs->label[j], "stdv", Astd);
5457          SUMA_set_Stat(cs, cs->label[j], "meanL", AmeanL);
5458          SUMA_set_Stat(cs, cs->label[j], "stdvL", AstdL);
5459          if (mixden[j]) SUMA_set_Stat(cs, cs->label[j], "mix", wsum/mixden[j]);
5460          else SUMA_set_Stat(cs, cs->label[j], "mix", 0);
5461       }
5462       SUMA_ifree(mixden);
5463    }
5464 
5465    SUMA_set_Stat_mix_floor(cs, mixfloor);
5466 
5467    /* Check classes at output */
5468    bad = 0;
5469    for (j=0; j<cs->N_label; ++j) {
5470       if (isnan(SUMA_get_Stat(cs, cs->label[j], "meanL")) ||
5471           isnan(SUMA_get_Stat(cs, cs->label[j], "stdvL")) ||
5472           isnan(SUMA_get_Stat(cs, cs->label[j], "mean")) ||
5473           isnan(SUMA_get_Stat(cs, cs->label[j], "stdv")) ) {
5474          SUMA_S_Err("Bad parameters for class %s", cs->label[j]);
5475          ++bad;
5476       }
5477    }
5478    if (bad) {
5479        SUMA_show_Class_Stat(cs,
5480                      "Bad Stats At SUMA_Class_stats() exit:\n", NULL);
5481        SUMA_RETURN(0);
5482    }
5483 
5484    /* and the dice */
5485    if (gold && cset) {
5486       SUMA_CompareSegDsets(gold, cset, cmask, 1, cs );
5487    }
5488 
5489    SUMA_RETURN(1);
5490 }
5491 
SUMA_Add_Class_Stat(SUMA_CLASS_STAT * cs,char * pname)5492 int SUMA_Add_Class_Stat(SUMA_CLASS_STAT *cs, char *pname)
5493 {
5494    static char FuncName[]={"SUMA_Add_Class_Stat"};
5495    int i=0;
5496 
5497    SUMA_ENTRY;
5498 
5499    if (cs->pname) {
5500       for (i=0; i<cs->nP; ++i) {
5501          if (!strcmp(cs->pname[i],pname)) SUMA_RETURN(i);
5502       }
5503    }
5504 
5505    /* nothing found */
5506    cs->nP = cs->nP+1;
5507    cs->pname = (char **)SUMA_realloc(cs->pname, sizeof(char*)*cs->nP);
5508    cs->pname[cs->nP-1] = SUMA_copy_string(pname);
5509    cs->Pv = (double **)SUMA_realloc(cs->Pv,sizeof(double*)*cs->nP);
5510    cs->Pv[cs->nP-1] = (double *)SUMA_calloc(cs->N_label, sizeof(double));
5511 
5512    SUMA_RETURN(cs->nP-1);
5513 }
5514 
SUMA_Add_Class_Label(SUMA_CLASS_STAT * cs,char * label,int key)5515 int SUMA_Add_Class_Label(SUMA_CLASS_STAT *cs, char *label, int key)
5516 {
5517    static char FuncName[]={"SUMA_Add_Class_Label"};
5518    int i=0;
5519 
5520    SUMA_ENTRY;
5521 
5522    for (i=0; i<cs->N_label; ++i) {
5523       if (!strcmp(cs->label[i],label)) SUMA_RETURN(i);
5524    }
5525 
5526    for (i=0; i<cs->N_label; ++i) {
5527       if (cs->keys[i]==key) {
5528          SUMA_S_Errv("key %d for new label %s is in use already for %s\n",
5529                      key, label, cs->label[i]);
5530          SUMA_RETURN(0);
5531       }
5532    }
5533 
5534    /* nothing found */
5535    cs->N_label = cs->N_label+1;
5536    cs->label = (char **)SUMA_realloc(cs->label, cs->N_label*sizeof(char *));
5537    cs->label[cs->N_label-1] = SUMA_copy_string(label);
5538 
5539    cs->keys = (int *)SUMA_realloc(cs->keys, cs->N_label*sizeof(int));
5540    cs->keys[cs->N_label-1] = key;
5541 
5542    for (i=0; i<cs->nP; ++i) {
5543       cs->Pv[i] = (double *)SUMA_realloc(cs->Pv[i],cs->N_label*sizeof(double));
5544    }
5545 
5546    SUMA_RETURN(cs->N_label-1);
5547 }
5548 
5549 
SUMA_New_Class_Stat(char ** clssl,int N_clssl,int * keys,int nP,NI_str_array * pnames)5550 SUMA_CLASS_STAT *SUMA_New_Class_Stat(char **clssl, int N_clssl, int *keys,
5551                                     int nP, NI_str_array *pnames)
5552 {
5553    static char FuncName[]={"SUMA_New_Class_Stat"};
5554    SUMA_CLASS_STAT *cs=NULL;
5555    int i;
5556 
5557    SUMA_ENTRY;
5558 
5559    cs = (SUMA_CLASS_STAT *) SUMA_calloc(1, sizeof(SUMA_CLASS_STAT));
5560 
5561    if (pnames) {
5562       if (nP < 0) nP = pnames->num;
5563       if (nP != pnames->num) {
5564          SUMA_S_Errv("Mismatch between nP %d and pnames->num %d\n",
5565                   nP, pnames->num);
5566          SUMA_RETURN(NULL);
5567       }
5568    } else {
5569       if (nP != 3) {
5570          SUMA_S_Errv("Can only handle 3 parameters (not %d) without names\n",
5571                      nP);
5572          SUMA_RETURN(NULL);
5573       }
5574    }
5575    cs->N_label = N_clssl;
5576    cs->nP = 0; cs->pname=NULL, cs->Pv=NULL;
5577    cs->label = (char **)SUMA_calloc(cs->N_label,sizeof(char *));
5578    cs->keys = (int *)SUMA_calloc(cs->N_label, sizeof(int));
5579    for (i=0; i<N_clssl; ++i) {
5580       cs->label[i] = SUMA_copy_string(clssl[i]);
5581       if (keys) cs->keys[i] = keys[i];
5582       else cs->keys[i] = i+1;
5583    }
5584    for (i=0; i<nP; ++i) {
5585       if (pnames) {
5586          if (SUMA_Add_Class_Stat(cs, pnames->str[i]) < 0) {
5587             SUMA_S_Errv("Failed to add %s\n", pnames->str[i]);
5588             SUMA_RETURN(NULL);
5589          }
5590       } else {
5591          switch(i) {
5592             case 0:
5593                if (SUMA_Add_Class_Stat(cs, "num") < 0) {
5594                   SUMA_S_Errv("Failed to add %s\n", pnames->str[i]);
5595                   SUMA_RETURN(NULL);
5596                }
5597                break;
5598             case 1:
5599                if (SUMA_Add_Class_Stat(cs, "mean") < 0) {
5600                   SUMA_S_Errv("Failed to add %s\n", pnames->str[i]);
5601                   SUMA_RETURN(NULL);
5602                }
5603                break;
5604             case 2:
5605                if (SUMA_Add_Class_Stat(cs, "stdv") < 0) {
5606                   SUMA_S_Errv("Failed to add %s\n", pnames->str[i]);
5607                   SUMA_RETURN(NULL);
5608                }
5609                break;
5610             default:
5611                SUMA_S_Errv("Can't handle %d\n", i);
5612                SUMA_RETURN(NULL);
5613          }
5614       }
5615    }
5616    SUMA_RETURN(cs);
5617 }
5618 
SUMA_Free_Class_Stat(SUMA_CLASS_STAT * cs)5619 SUMA_CLASS_STAT *SUMA_Free_Class_Stat(SUMA_CLASS_STAT *cs)
5620 {
5621    static char FuncName[]={"SUMA_Free_Class_Stat"};
5622    int i=0;
5623 
5624    SUMA_ENTRY;
5625 
5626    if (cs) {
5627       if (cs->pname) {
5628          for (i=0; i<cs->nP; ++i) {
5629             SUMA_ifree(cs->pname[i]);
5630             SUMA_ifree(cs->Pv[i]);
5631          }
5632       }
5633       SUMA_ifree(cs->Pv);
5634       SUMA_ifree(cs->pname);
5635       if (cs->label) {
5636          for (i=0; i<cs->N_label; ++i) { SUMA_ifree(cs->label[i]); }
5637       }
5638       SUMA_ifree(cs->label);
5639       SUMA_ifree(cs->keys);
5640    }
5641    SUMA_RETURN(NULL);
5642 }
5643 
SUMA_Stat_position(SUMA_CLASS_STAT * cs,char * label,char * pname,int pp[])5644 int SUMA_Stat_position (SUMA_CLASS_STAT *cs, char *label, char *pname,
5645                         int pp[])
5646 {
5647    static char FuncName[]={"SUMA_Stat_position"};
5648    int i=0,k=0;
5649 
5650    SUMA_ENTRY;
5651 
5652    pp[0] = pp[1] = -1;
5653 
5654    if (label) {
5655       for (i=0; i<cs->N_label; ++i) {
5656          if (!strcmp(cs->label[i], label)) {
5657             pp[0] = i;
5658             break;
5659          }
5660       }
5661    }
5662 
5663 
5664    if (pname) {
5665       for (k=0; k<cs->nP; ++k) {
5666          if (!strcmp(cs->pname[k], pname)) {
5667             pp[1] = k;
5668             break;
5669          }
5670       }
5671    }
5672 
5673    if ( (pp[0] < 0 && label) || (pp[1] < 0 && pname)) SUMA_RETURN(0);
5674 
5675    SUMA_RETURN(1);
5676 }
5677 
SUMA_get_Stat(SUMA_CLASS_STAT * cs,char * label,char * pname)5678 double SUMA_get_Stat(SUMA_CLASS_STAT *cs, char *label, char *pname)
5679 {
5680    static char FuncName[]={"SUMA_get_Stat"};
5681    int pp[2];
5682 
5683    SUMA_ENTRY;
5684 
5685    if (!SUMA_Stat_position(cs, label, pname, pp)) {
5686       SUMA_S_Errv("Failed to locate %s of %s\n",
5687                   pname, label);
5688       SUMA_RETURN(0.0);
5689    }
5690 
5691    SUMA_RETURN(cs->Pv[pp[1]][pp[0]]);
5692 }
5693 
SUMA_get_Stats(SUMA_CLASS_STAT * cs,char * pname)5694 double *SUMA_get_Stats(SUMA_CLASS_STAT *cs,  char *pname)
5695 {
5696    static char FuncName[]={"SUMA_get_Stats"};
5697    double *vv=NULL;
5698    int pp[2];
5699    SUMA_Boolean LocalHead = NOPE;
5700 
5701    SUMA_ENTRY;
5702 
5703    if (!SUMA_Stat_position(cs, NULL,  pname, pp)) {
5704       if (LocalHead) {
5705          SUMA_S_Notev("Failed to locate %s\n",
5706                   pname);
5707       }
5708       SUMA_RETURN(NULL);
5709    }
5710    SUMA_RETURN(cs->Pv[pp[1]]);
5711 }
5712 
SUMA_set_Stat(SUMA_CLASS_STAT * cs,char * label,char * pname,double val)5713 int SUMA_set_Stat(SUMA_CLASS_STAT *cs, char *label, char *pname, double val)      {
5714    static char FuncName[]={"SUMA_set_Stat"};
5715    int pp[2];
5716 
5717    SUMA_ENTRY;
5718    SUMA_Stat_position(cs, label, pname, pp);
5719    if (pp[0] < 0) {
5720       SUMA_S_Errv("Failed to locate class label %s \n",
5721                   label);
5722       SUMA_RETURN(0);
5723    }
5724    if (pp[1] < 0) {
5725       /* add new stat */
5726       if ((pp[1] = SUMA_Add_Class_Stat(cs, pname)) < 0) {
5727          SUMA_S_Errv("Failed to add stat %s\n", pname);
5728          SUMA_RETURN(0);
5729       }
5730    }
5731 
5732    cs->Pv[pp[1]][pp[0]] = val;
5733    SUMA_RETURN(1);
5734 }
5735 
SUMA_show_Class_Stat(SUMA_CLASS_STAT * cs,char * head,char * fout)5736 int SUMA_show_Class_Stat(SUMA_CLASS_STAT *cs, char *head, char *fout) {
5737    FILE *ff = NULL;
5738    int a;
5739    if (!fout) return(SUMA_dump_Class_Stat(cs, head, SUMA_STDERR));
5740    else {
5741       a = 0;
5742       ff = fopen(fout,"w");
5743       if (ff) {
5744          a = SUMA_dump_Class_Stat(cs, head, ff);
5745          fclose(ff);
5746       }
5747       return(a);
5748    }
5749 }
5750 
SUMA_dump_Class_Stat(SUMA_CLASS_STAT * cs,char * head,FILE * Out)5751 int SUMA_dump_Class_Stat(SUMA_CLASS_STAT *cs, char *head, FILE *Out)
5752 {
5753    static char FuncName[]={"SUMA_dump_Class_Stat"};
5754    char *s=NULL;
5755 
5756    SUMA_ENTRY;
5757    s = SUMA_Class_Stat_Info(cs, head);
5758 
5759    if (!Out) Out = SUMA_STDERR;
5760 
5761    fprintf(Out,"%s", s);
5762 
5763    SUMA_ifree(s);
5764 
5765    SUMA_RETURN(1);
5766 }
5767 
SUMA_Class_Stat_Info(SUMA_CLASS_STAT * cs,char * head)5768 char *SUMA_Class_Stat_Info(SUMA_CLASS_STAT *cs, char *head)
5769 {
5770    static char FuncName[]={"SUMA_Class_Stat_Info"};
5771    int i, j;
5772    SUMA_STRING *SS;
5773    char *s=NULL;
5774    char buf[36];
5775 
5776    SUMA_ENTRY;
5777 
5778    SS = SUMA_StringAppend (NULL, NULL);
5779 
5780    if (head) {
5781       SS = SUMA_StringAppend_va(SS,"%s", head);
5782    }
5783    SS = SUMA_StringAppend_va(SS,"%8s %4s   ", "Class", "Key");
5784    for (j=0; j<cs->nP; ++j) {
5785       SS = SUMA_StringAppend_va(SS,"%8s   ",cs->pname[j]);
5786    }
5787    SS = SUMA_StringAppend_va(SS,"\n");
5788    for (i=0; i<cs->N_label; ++i) {
5789       sprintf (buf, "%s",
5790                MV_format_fval2(cs->keys[i], 4));
5791       SS = SUMA_StringAppend_va(SS,"%8s %4s   ",
5792                                     cs->label[i], buf);
5793       for (j=0; j<cs->nP; ++j) {
5794          sprintf (buf, "%s",
5795                MV_format_fval2(cs->Pv[j][i], 8));
5796          SS = SUMA_StringAppend_va(SS,"%8s   ", buf );
5797       }
5798       SS = SUMA_StringAppend_va(SS,"\n");
5799    }
5800 
5801    SUMA_SS2S(SS,s);
5802 
5803    SUMA_RETURN(s);
5804 }
5805 
SUMA_MixFrac_from_ClassStat(SUMA_CLASS_STAT * cs,float * mf)5806 int SUMA_MixFrac_from_ClassStat(SUMA_CLASS_STAT *cs, float *mf)
5807 {
5808    static char FuncName[]={"SUMA_MixFrac_from_ClassStat"};
5809    float ss=0.0;
5810    int i;
5811 
5812    SUMA_ENTRY;
5813 
5814    for (i=0; i<cs->N_label; ++i) {
5815       mf[i] = SUMA_get_Stat(cs, cs->label[i], "num");
5816       ss += mf[i];
5817    }
5818    for (i=0; i<cs->N_label; ++i) mf[i] /= ss;
5819 
5820    SUMA_RETURN(1);
5821 }
5822 
SUMA_ZeroSamp_from_ClassStat(SUMA_CLASS_STAT * cs)5823 int SUMA_ZeroSamp_from_ClassStat(SUMA_CLASS_STAT *cs)
5824 {
5825    static char FuncName[]={"SUMA_ZeroSamp_from_ClassStat"};
5826    int out;
5827    int i;
5828 
5829    SUMA_ENTRY;
5830 
5831    for (i=0, out=0; i<cs->N_label; ++i) {
5832       if (!SUMA_get_Stat(cs, cs->label[i], "num")) ++out;
5833    }
5834 
5835    SUMA_RETURN(out);
5836 }
5837 
5838 
SUMA_mixopt_2_mixfrac(char * mixopt,char * label,int key,int N_clss,byte * cmask,THD_3dim_dataset * cset)5839 double SUMA_mixopt_2_mixfrac(char *mixopt, char *label, int key, int N_clss,
5840                              byte *cmask, THD_3dim_dataset *cset)
5841 {
5842    static char FuncName[]={"SUMA_mixopt_2_mixfrac"};
5843    int i, ntot, nkey;
5844    short *a=NULL;
5845    double frac=-1.0;
5846 
5847    SUMA_ENTRY;
5848 
5849 
5850    if (!mixopt || !strncmp(mixopt,"UNI",3) || !strcmp(mixopt,"IGNORE")) {
5851       frac = 1.0/(double)N_clss;
5852    } else if (!strcmp(mixopt,"TOY_DEBUG")) {
5853            if (!strcmp(label, "CSF"))frac = 0.1;
5854       else if (!strcmp(label, "GM")) frac = 0.45;
5855       else if (!strcmp(label, "WM")) frac = 0.45;
5856       else {
5857          SUMA_S_Errv("Not ready for class %s in mixopt %s\n",
5858                label, mixopt);
5859          SUMA_RETURN(-1.0);
5860       }
5861    } else if (!strcmp(mixopt,"WHOLE_BRAIN")) {
5862            if (!strcmp(label, "CSF"))frac = 0.015;
5863       else if (!strcmp(label, "GM")) frac = 0.650;
5864       else if (!strcmp(label, "WM")) frac = 0.335;
5865       else {
5866          SUMA_S_Errv("Not ready for class %s in mixopt %s\n",
5867                label, mixopt);
5868          SUMA_RETURN(-1.0);
5869       }
5870    } else if (!strcmp(mixopt,"AVG152_BRAIN_MASK")) {
5871            if (!strcmp(label, "CSF"))frac = 0.155;
5872       else if (!strcmp(label, "GM")) frac = 0.550;
5873       else if (!strcmp(label, "WM")) frac = 0.295;
5874       else {
5875          SUMA_S_Errv("Not ready for class %s in mixopt %s\n",
5876                label, mixopt);
5877          SUMA_RETURN(-1.0);
5878       }
5879    } else if (!strcmp(mixopt,"AVG152p_BRAIN_MASK")) {
5880            if (!strcmp(label, "CSF"))frac = 0.149;
5881       else if (!strcmp(label, "GM")) frac = 0.480;
5882       else if (!strcmp(label, "WM")) frac = 0.371;
5883       else {
5884          SUMA_S_Errv("Not ready for class %s in mixopt %s\n",
5885                label, mixopt);
5886          SUMA_RETURN(-1.0);
5887       }
5888    } else if (!strcmp(mixopt,"CSET")) {
5889       if (!cset) {
5890          SUMA_S_Err("No -cset input to use with CSET");
5891          SUMA_RETURN(-1.0);
5892       }
5893       /* not the most efficient, doing this once at a time,
5894       but this is not called often*/
5895       a = (short *)DSET_BRICK_ARRAY(cset,0);
5896       ntot = 0; nkey = 0;
5897       for (i=0; i<DSET_NVOX(cset); ++i) {
5898          if (IN_MASK(cmask,i)) {
5899             ++ntot;
5900             if (key == a[i]) ++nkey;
5901          }
5902       }
5903       if (ntot) frac = (double)nkey/(double)ntot;
5904    } else {
5905       SUMA_S_Errv("-mixopt '%s' cannot be interpreted\n", mixopt);
5906       SUMA_RETURN(-1.0);
5907    }
5908 
5909    SUMA_RETURN(frac);
5910 }
pdfnorm(double x,double mean,double stdv)5911 double pdfnorm(double x, double mean, double stdv) {
5912    double x0=x-mean;
5913    return(1.0/(SQ2PI*stdv)*exp(-(x0*x0)/(2*stdv*stdv)));
5914 }
5915 
SUMA_p_Y_GIV_C_B_O(THD_3dim_dataset * aset,THD_3dim_dataset * cset,byte * cmask,SUMA_CLASS_STAT * cs,THD_3dim_dataset * pygc)5916 THD_3dim_dataset *SUMA_p_Y_GIV_C_B_O(
5917                            THD_3dim_dataset *aset, THD_3dim_dataset *cset,
5918                                  byte *cmask, SUMA_CLASS_STAT *cs,
5919                                  THD_3dim_dataset *pygc)
5920 {
5921    static char FuncName[]={"SUMA_p_Y_GIV_C_B_O"};
5922    int i, k;
5923    double x0, mean, stdv, c1, c2, *p=NULL;
5924    float af=0.0;
5925    short *a=NULL, *c=NULL;
5926    THD_3dim_dataset *pout=pygc;
5927 
5928    SUMA_ENTRY;
5929 
5930    if (!pout) {
5931       NEW_SHORTY(aset,1,"p_Y_GIV_C_B_O",pout);
5932    }
5933    af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0f) af = 1.0;
5934    a = (short *)DSET_ARRAY(aset,0);
5935    c = (short *)DSET_ARRAY(cset,0);
5936    p = (double *)SUMA_calloc(DSET_NVOX(aset), sizeof(double));
5937 
5938    for (k=0; k<cs->N_label; ++k) {
5939       mean=SUMA_get_Stat(cs, cs->label[k], "mean");
5940       stdv=SUMA_get_Stat(cs, cs->label[k], "stdv");
5941       c1 = 1.0/(SQ2PI*stdv); c2 = (2*stdv*stdv);
5942       for (i=0; i<DSET_NVOX(aset); ++i) {
5943          if (IN_MASK(cmask, i) && c[i] == cs->keys[k]) {
5944             x0 = (double)a[i]*af - mean;
5945             p[i] = c1 * exp(-(x0*x0)/c2);
5946          }
5947       }
5948    }
5949 
5950    /* put vector back in pout */
5951    EDIT_substscale_brick(pout, 0, MRI_double, p, MRI_short, -1.0);
5952    free(p); p = NULL;
5953    SUMA_RETURN(pout);
5954 }
5955 
5956 
5957 typedef struct {
5958    SUMA_CLASS_STAT *cs;
5959    THD_3dim_dataset *aset;
5960    THD_3dim_dataset *cset;
5961    THD_3dim_dataset *Bset;
5962    THD_3dim_dataset *pstCgALL;
5963    THD_3dim_dataset *priCgALL;
5964    THD_3dim_dataset *pCgN;
5965    float mrfB;
5966    float Temp;
5967    byte *cmask;
5968    int cmask_count;
5969    int method;
5970    int *UseK;
5971    int N_kok;
5972 } EEO_UD; /* user data for SUMA_EdgeEnergy_OptimCost */
5973 
5974 static EEO_UD eeoud;
5975 
SUMA_free_eeoud()5976 void SUMA_free_eeoud() {
5977    if (eeoud.pstCgALL) DSET_delete(eeoud.pstCgALL); eeoud.pstCgALL=NULL;
5978    if (eeoud.UseK) SUMA_ifree(eeoud.UseK);
5979    return;
5980 }
5981 
SUMA_set_eeoud(SUMA_CLASS_STAT * cs,THD_3dim_dataset * aset,THD_3dim_dataset * Bset,THD_3dim_dataset * cset,THD_3dim_dataset * priCgAll,THD_3dim_dataset * pCgN,float mrfB,float Temp,byte * cmask,int cmask_count,int method,char * classes)5982 void SUMA_set_eeoud(SUMA_CLASS_STAT *cs, THD_3dim_dataset *aset,
5983                     THD_3dim_dataset *Bset, THD_3dim_dataset *cset,
5984                     THD_3dim_dataset *priCgAll, THD_3dim_dataset *pCgN,
5985                     float mrfB, float Temp,
5986                     byte *cmask, int cmask_count,
5987                     int method, char *classes) {
5988    static char FuncName[]={"SUMA_set_eeoud"};
5989 
5990    SUMA_ENTRY;
5991 
5992    SUMA_free_eeoud();
5993    eeoud.cs = cs;
5994    eeoud.aset = aset;
5995    eeoud.Bset = Bset;
5996    eeoud.cset = cset;
5997    if (!eeoud.cset) {
5998       SUMA_S_Err("Need cset"); SUMA_RETURNe;
5999    }
6000    eeoud.mrfB = mrfB;
6001    eeoud.Temp = Temp;
6002    eeoud.priCgALL = priCgAll;
6003    eeoud.pstCgALL = NULL;
6004    eeoud.pCgN = pCgN;
6005    eeoud.cmask = cmask;
6006    eeoud.method = method;
6007    eeoud.UseK = (int *)SUMA_calloc(cs->N_label, sizeof(int));
6008    if ((eeoud.N_kok = SUMA_Class_k_Selector(eeoud.cs, "classes_string",
6009                                           classes, eeoud.UseK))<0) {
6010       SUMA_S_Err("Failed to find classes");
6011       SUMA_RETURNe;
6012    }
6013 
6014    SUMA_RETURNe;
6015 }
6016 
SUMA_EdgeEnergy_Gassign(THD_3dim_dataset * aset,THD_3dim_dataset * fset,byte * cmask,SUMA_CLASS_STAT * cs,int * UseK,int N_kok,double * par,int npar,THD_3dim_dataset * cset)6017 void SUMA_EdgeEnergy_Gassign(THD_3dim_dataset *aset, THD_3dim_dataset *fset,
6018                         byte *cmask, SUMA_CLASS_STAT *cs, int *UseK, int N_kok,
6019                         double *par, int npar, THD_3dim_dataset *cset)
6020 {
6021    static char FuncName[]={"SUMA_EdgeEnergy_Gassign"};
6022    int i, ku;
6023    double cost = 0.0, eem=0.0, dd=0.0, ee=0.0, mean=0.0, stdv=0.0;
6024    short *a=NULL, *fb=NULL;
6025    short *cout=NULL;
6026    float af, ff=1.0;
6027    float aof;
6028    SUMA_Boolean LocalHead = NOPE;
6029 
6030    SUMA_ENTRY;
6031 
6032    SUMA_LHv("aset %p, fset %p\n", aset, fset);
6033 
6034    a = (short *)DSET_ARRAY(aset,0);
6035    af = DSET_BRICK_FACTOR(aset, 0);
6036 
6037    if (fset) {
6038       fb = (short *)DSET_ARRAY(fset,0);
6039       ff = DSET_BRICK_FACTOR(fset, 0);
6040    }
6041    cout = (short *)DSET_ARRAY(cset,0);
6042 
6043    if (af == 0.0f) af = 1.0;
6044    if (ff == 0.0f) ff = 1.0;
6045    aof = af/ff;
6046    /* Assign classes based on gaussians into gcset */
6047    for (i=0; i<DSET_NVOX(aset); ++i) {
6048       if (IN_MASK(cmask,i)) {
6049          eem = -1.0;
6050          for (ku=0; ku<N_kok; ++ku) {
6051             mean = par[2*ku]; stdv = par[2*ku+1];
6052             if (fset) dd = ((double)a[i]/fb[i]*aof-mean);
6053             else dd = ((double)a[i]*aof-mean);
6054             dd *= dd;
6055             ee = exp(-dd/(2.0*stdv*stdv) -log(stdv));
6056             if (LocalHead) {
6057                if (i == 1631822) {
6058                   fprintf(stderr,
6059                      "%d: a=%f, f=%f, m=%f, s=%f, k=%d, ee=%f, eem=%f\n",
6060                      i, a[i]*af, fset ? fb[i]*ff:1.0, mean, stdv, ku, ee, eem);
6061                }
6062             }
6063             if (ee > eem) { eem = ee; cout[i] = cs->keys[UseK[ku]]; }
6064          }
6065       }
6066    }
6067 
6068    SUMA_RETURNe;
6069 }
6070 
6071 
SUMA_EdgeEnergy_OptimCost(int n,double * par)6072 double SUMA_EdgeEnergy_OptimCost(int n, double *par)
6073 {
6074    static char FuncName[]={"SUMA_EdgeEnergy_OptimCost"};
6075    static int iter;
6076    int i;
6077    double cost;
6078    THD_3dim_dataset *pstCgALL=NULL;
6079    THD_3dim_dataset *cset=NULL;
6080 
6081    /* put parameters into cs */
6082    for (i=0; i<eeoud.N_kok; ++i) {
6083       SUMA_set_Stat(eeoud.cs, eeoud.cs->label[eeoud.UseK[i]],
6084                         "mean", par[2*i  ]);
6085       SUMA_set_Stat(eeoud.cs, eeoud.cs->label[eeoud.UseK[i]],
6086                         "stdv", par[2*i+1]);
6087    }
6088 
6089    #if 0
6090    /* Assign classes based on Gaussians only */
6091    SUMA_EdgeEnergy_Gassign(eeoud.aset, eeoud.Bset,
6092                         eeoud.cmask, eeoud.cs,
6093                         eeoud.UseK, eeoud.N_kok,
6094                         par, n, eeoud.cset);
6095    #else
6096    /*SUMA_S_Warn("HAD NO EFFECT on RESULTS whether -edge was used or not\n"
6097                "Not exactly sure why yet. Either a stupic coding mistake,\n"
6098                "Or the priors are too powerful. Although the bug comes up\n"
6099                "even at the simplest command such as: \n"
6100                "3dSeg   -anat banat+orig -mask anat.ns+orig \n"
6101                "        -gold goldseg+orig -gold_bias goldbias+orig \n"
6102                "        -classes 'CSF ; GM ; WM' -Bmrf 0.0 -edge1 \n"
6103                "        -bias_classes 'GM ; WM' -bias_fwhm 25 \n"
6104                "-prefix case.A.1nopst.ss -overwrite");
6105    */
6106    /* stick par parameters parameters here */
6107 
6108    /* compute posterior as would be done in main routine */
6109    if (!SUMA_pst_C_giv_ALL( eeoud.aset,
6110                              eeoud.cmask, eeoud.cmask_count,
6111                              eeoud.cs, eeoud.priCgALL, eeoud.pCgN,
6112                              eeoud.mrfB, eeoud.Temp, 1,
6113                              &(pstCgALL))) {
6114          fprintf(stderr,"Error SUMA_EdgeEnergy_OptimCost:\n"
6115                         "Failed in SUMA_pst_C_giv_ALL\n");
6116          return(0);
6117    }
6118    eeoud.pstCgALL = pstCgALL; pstCgALL = NULL;
6119 
6120    /* assign classes */
6121    if (!(SUMA_assign_classes( eeoud.pstCgALL, eeoud.cs,
6122                               eeoud.cmask, &cset))) {
6123       fprintf(stderr,"Error SUMA_EdgeEnergy_OptimCost:\n"
6124                         "Failed in SUMA_assign_classes\n");
6125       return(0);
6126    }
6127    memcpy(DSET_ARRAY(eeoud.cset,0),
6128           DSET_ARRAY(cset,0), sizeof(short)*DSET_NVOX(cset));
6129    DSET_delete(cset); cset=NULL;
6130    #endif
6131    /* call energy function */
6132    cost = -1.0 * SUMA_DsetEdgeEnergy(eeoud.aset,
6133                        eeoud.cset,
6134                        eeoud.cmask,
6135                        eeoud.Bset, NULL,
6136                        eeoud.cs, eeoud.method,
6137                        eeoud.UseK, eeoud.N_kok);
6138 
6139    if (debug) fprintf(SUMA_STDERR,"%cMethod %d. iter %d, Edge Cost %f",
6140             0xd, eeoud.method, iter, cost);
6141 
6142 
6143 
6144    ++iter;
6145    return(cost);
6146 }
6147 
SUMA_MAP_EdgeEnergy(THD_3dim_dataset * aset,byte * cmask,int cmask_count,THD_3dim_dataset * Bset,SUMA_CLASS_STAT * cs,THD_3dim_dataset * cset,int method,THD_3dim_dataset * priCgAll,THD_3dim_dataset * pCgN,float mrfB,float Temp,float deltamean,float deltastd,SEG_OPTS * Opt)6148 double SUMA_MAP_EdgeEnergy(THD_3dim_dataset *aset, byte *cmask, int cmask_count,
6149                         THD_3dim_dataset *Bset, SUMA_CLASS_STAT *cs,
6150                         THD_3dim_dataset *cset, int method,
6151                         THD_3dim_dataset *priCgAll, THD_3dim_dataset *pCgN,
6152                         float mrfB, float Temp,
6153                         float deltamean, float deltastd,
6154                         SEG_OPTS * Opt)
6155 {
6156    static char FuncName[]={"SUMA_MAP_EdgeEnergy"};
6157    int ncalls = 0, nparmax = 36, npar, i, nrand, ntry, nkeep, maxcall;
6158    double par[nparmax], bot[nparmax], top[nparmax], cost,
6159           gap[nparmax], rstart, rend;
6160    static int icall = 0;
6161    SUMA_Boolean LocalHead = NOPE;
6162 
6163    SUMA_ENTRY;
6164 
6165    /* load user data */
6166    SUMA_set_eeoud(cs, aset, Bset, cset, priCgAll, pCgN, mrfB, Temp,
6167                   cmask, cmask_count, method, "CSF; GM; WM");
6168 
6169    /* load parameters into par, bot, top */
6170    if (cs->N_label*2 > nparmax) {
6171       SUMA_S_Err("Too many parameters");
6172       SUMA_RETURN(cost);
6173    }
6174 
6175    /* initialize parameters and estimate gap between classes.
6176    For 1st and last class, assume gap is one stdv */
6177    npar = eeoud.N_kok*2;
6178    for (i=0; i<eeoud.N_kok; ++i) {
6179       par[2*i  ] = SUMA_get_Stat(cs, cs->label[eeoud.UseK[i]], "mean");
6180       par[2*i+1] = SUMA_get_Stat(cs, cs->label[eeoud.UseK[i]], "stdv");
6181       if (i>0) {
6182          gap[i] = par[2*i  ] - par[2*(i-1)]; /* mean difference
6183                                                      between classes i and i-1 */
6184          if (gap[i] <= 0) {
6185             SUMA_S_Err("Classes not sorted by increasing mean");
6186             SUMA_RETURN(0.0);
6187          }
6188       } else {
6189          gap[0] = SUMA_get_Stat(cs, cs->label[eeoud.UseK[0]], "stdv")*2.0;
6190                      /* Can't compute mean difference,
6191                       allow something function of stdv */
6192       }
6193    }
6194    gap[eeoud.N_kok] = SUMA_get_Stat(cs,
6195                         cs->label[eeoud.UseK[eeoud.N_kok-1]], "stdv")*2.0;
6196 
6197    /* min and max params for mean parameters */
6198    for (i=0; i<eeoud.N_kok; ++i) {
6199       if (i==0)   bot[2*i] = SUMA_MAX_PAIR(par[2*i] - gap[i]/10.0,
6200                                           (1.0-deltamean/2.0)*par[2*i]);
6201       else        bot[2*i] = par[2*i] - gap[i]/10.0;
6202       if (i==eeoud.N_kok-1)
6203                   top[2*i] = SUMA_MIN_PAIR(par[2*i]+gap[i+1]/10.0,
6204                                           (1.0+deltamean/2.0)*par[2*i]);
6205       else        top[2*i] = par[2*i] + gap[i+1]/10.0;
6206    }
6207    /* min and max params for stdv parameters */
6208    for (i=0; i<eeoud.N_kok; ++i) {
6209       bot[2*i+1] = (1.0-deltastd/2.0)*par[2*i+1];
6210       top[2*i+1] = (1.0+deltastd/2.0)*par[2*i+1];
6211    }
6212 
6213    if (Opt->debug || LocalHead ) {
6214       if (Opt->debug > 1 || LocalHead) {
6215          for (i=0; i<eeoud.N_kok; ++i) {
6216             fprintf(SUMA_STDERR,
6217                "Pre Optimization:\n"
6218             "%3s:  mean [%.3f <- %.3f -> %.3f], stdv [%.3f <- %.3f -> %.3f]\n",
6219                      cs->label[eeoud.UseK[i]],
6220                                     bot[2*i  ], par[2*i  ], top[2*i  ],
6221                                     bot[2*i+1], par[2*i+1], top[2*i+1]);
6222          }
6223       } else {
6224          fprintf(SUMA_STDERR,
6225             "Pre Optimization: ");
6226          for (i=0; i<eeoud.N_kok; ++i) {
6227             fprintf(SUMA_STDERR,
6228                "%3s:  mean %.3f, stdv %.3f   ",
6229                cs->label[eeoud.UseK[i]],  par[2*i  ], par[2*i+1]);
6230          }
6231          fprintf(SUMA_STDERR, "\n");
6232       }
6233 
6234       if (Opt->debug > 1){
6235         SUMA_Seg_Write_Dset(Opt->proot, "PreEdgeOptim", cset, icall, Opt->hist);
6236       }
6237    }
6238 
6239    nrand = 0; nkeep = 0; ntry = 2;
6240    rstart = 0.2; rend = 0.05;
6241    maxcall = 50;
6242    if ( (ncalls = powell_newuoa_constrained (npar, par, &cost,
6243                                              bot, top,
6244                                              nrand, nkeep, 2,
6245                                              rstart, rend,
6246                                              maxcall,
6247                                     SUMA_EdgeEnergy_OptimCost)) < 0) {
6248       SUMA_S_Err("Failed in optimization");
6249       SUMA_RETURN(0);
6250    }
6251    if (debug) fprintf(SUMA_STDERR,"\n");
6252 
6253    if (Opt->debug || LocalHead ) {
6254       if (Opt->debug > 1 || LocalHead) {
6255          for (i=0; i<eeoud.N_kok; ++i) {
6256             fprintf(SUMA_STDERR,
6257                "Post Optimization:\n"
6258             "%3s:  mean [%.3f <- %.3f -> %.3f], stdv [%.3f <- %.3f -> %.3f]\n",
6259                      cs->label[eeoud.UseK[i]],
6260                                     bot[2*i  ], par[2*i  ], top[2*i  ],
6261                                     bot[2*i+1], par[2*i+1], top[2*i+1]);
6262          }
6263       } else {
6264          fprintf(SUMA_STDERR,
6265             "Post Optimization: ");
6266          for (i=0; i<eeoud.N_kok; ++i) {
6267             fprintf(SUMA_STDERR,
6268                "%3s:  mean %.3f, stdv %.3f   ",
6269                cs->label[eeoud.UseK[i]],  par[2*i  ], par[2*i+1]);
6270          }
6271          fprintf(SUMA_STDERR, "\n");
6272       }
6273       /* write out the classification result based on EdgeEnergy Optimization*/
6274       if (Opt->debug > 1){
6275         SUMA_Seg_Write_Dset(Opt->proot, "PostEdgeOptim",
6276                             cset, icall, Opt->hist);
6277       }
6278    }
6279 
6280    /* reload optimized params into cs */
6281    for (i=0; i<eeoud.N_kok; ++i) {
6282       SUMA_set_Stat(cs, cs->label[eeoud.UseK[i]], "mean", par[2*i  ]);
6283       SUMA_set_Stat(cs, cs->label[eeoud.UseK[i]], "stdv", par[2*i+1]);
6284    }
6285 
6286    SUMA_free_eeoud();
6287 
6288    ++icall;
6289    SUMA_RETURN(cost);
6290 }
6291 
6292 /* See Berthod et al. */
SUMA_MAP_labels(THD_3dim_dataset * aset,byte * cmask,SUMA_CLASS_STAT * cs,int neighopt,THD_3dim_dataset * pC,THD_3dim_dataset ** csetp,THD_3dim_dataset ** pCgNp,SEG_OPTS * Opt)6293 int SUMA_MAP_labels(THD_3dim_dataset *aset,
6294                         byte *cmask,
6295                         SUMA_CLASS_STAT *cs, int neighopt,
6296                         THD_3dim_dataset *pC,
6297                         THD_3dim_dataset **csetp,
6298                         THD_3dim_dataset **pCgNp,
6299                         SEG_OPTS *Opt)
6300 {
6301    static char FuncName[]={"SUMA_MAP_labels"};
6302    THD_3dim_dataset *cset = *csetp;
6303    THD_3dim_dataset *pCgN = *pCgNp;
6304    float af=0.0, *fpCgN=NULL, *fpC=NULL;
6305    int iter=0, i=0, k, ni, nj, nk, nij, ijkn[6];
6306    int Niter = 3, kmin;
6307    double eG[Niter], e, eG1, eG2, *mv, *sv, dd, *e1=NULL,
6308           *e2=NULL, BoT, pp, *wv;
6309    short *ci=NULL, *co=NULL, *a=NULL;
6310 
6311    SUMA_ENTRY;
6312 
6313    BoT = Opt->B/Opt->T;
6314 
6315    if (neighopt != 4 && neighopt != 6) {
6316       SUMA_S_Errv("Allowing neighopt of 4 or 6 only. Have %d\n", neighopt);
6317       SUMA_RETURN(0);
6318    }
6319 
6320    if (!cset) {
6321       NEW_SHORTY(aset,1,"MAP_labels",cset);
6322       *csetp = cset;
6323    }
6324 
6325    af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0f) af = 1.0;
6326    a = (short *)DSET_ARRAY(aset,0);
6327    co = (short *)DSET_ARRAY(cset,0);
6328    ci = (short *)malloc(sizeof(short)*DSET_NVOX(aset));
6329    e1 = (double *)calloc(cs->N_label, sizeof(double));
6330    e2 = (double *)calloc(cs->N_label, sizeof(double));
6331    fpCgN = (float *)calloc(cs->N_label, sizeof(float));
6332    fpC = (float *)calloc(cs->N_label, sizeof(float));
6333 
6334    if (!pCgN) {
6335       NEW_SHORTY(aset,cs->N_label, "pCgN", pCgN);
6336       *pCgNp = pCgN;
6337       for (k=0; k<cs->N_label; ++k) {
6338          EDIT_BRICK_FACTOR(pCgN, k, 1/10000.0);
6339       }
6340    }
6341    GET_BFs(pCgN,fpCgN);
6342 
6343    ni = DSET_NX(aset);
6344    nj = DSET_NY(aset);
6345    nk = DSET_NZ(aset);
6346    nij = ni*nj;
6347 
6348    if (pC) { /* The priors for the classes */
6349       GET_BFs(pC, fpC);
6350    }
6351 
6352    /* get vector of parameters  */
6353    mv = SUMA_get_Stats(cs, "mean");
6354    sv = SUMA_get_Stats(cs, "stdv");
6355    wv = SUMA_get_Stats(cs, "mix");
6356 
6357    for (iter=0; iter<Niter; ++iter) {
6358       memcpy(ci, co, sizeof(short)*DSET_NVOX(aset));
6359       eG[iter] = 0.0; eG1=0.0; eG2 = 0.0;
6360       for (i=0; i<DSET_NVOX(aset); ++i) {
6361          if (IN_MASK(cmask,i)) {
6362             for(k=0; k<cs->N_label; ++k) {
6363                /* term 1, energy of y given class */
6364                dd = ((double)a[i]*af-mv[k]); dd *= dd;
6365                e1[k] = (dd/(2.0*sv[k]*sv[k]) + log(sv[k]*SQ2PI ))/Opt->T;
6366 
6367                /* term 2, energy of label given neighbors */
6368                GET_NEIGHBS_IN_MASK( cmask, i,
6369                                     ni, nj, nk, nij,
6370                                     ijkn);
6371                E_l_GIV_NEIGHBS(ci, ijkn, cs->keys[k], neighopt, e2[k]);
6372                e2[k] = e2[k]*BoT;
6373             }
6374 
6375             /* modulate e1 by pC and w, if specified*/
6376             #if 1
6377             if (i==0 && iter == 0) SUMA_S_Note("Check again");
6378             /* KEEP or kill? w alone seems OK, pC needs some love, perhaps.
6379                Test again */
6380             if (pC) {
6381                e = 0.0; dd = 0.0;
6382                for(k=0; k<cs->N_label; ++k) {
6383                   GSCVAL(pC, k, i, fpC[k], dd);
6384                   e1[k] = exp(-e1[k])*dd*wv[k]; e += dd*wv[k];
6385                }
6386                for(k=0; k<cs->N_label; ++k) { e1[k] = -log(e1[k]/e); }
6387             } else {
6388                e = 0.0; dd = 0.0;
6389                for(k=0; k<cs->N_label; ++k) {
6390                   e1[k] = exp(-e1[k])*wv[k]; e += wv[k];
6391                }
6392                for(k=0; k<cs->N_label; ++k) { e1[k] = -log(e1[k]/e); }
6393             }
6394             #endif
6395             /* find min e */
6396             e = e1[0]+e2[0]; kmin=0;
6397             for(k=1; k<cs->N_label; ++k) {
6398                if (e1[k]+e2[k] < e) {
6399                   e=e1[k]+e2[k]; kmin=k;
6400                }
6401             }
6402 
6403             if (i == Opt->VoxDbg) {
6404                int IJK[3], pp;
6405                Vox1D2Vox3D(i,DSET_NX(aset), DSET_NX(aset)*DSET_NY(aset), IJK);
6406                fprintf(Opt->VoxDbgOut, "at %d %d %d, a=%d (%f)\n",
6407                                        IJK[0], IJK[1], IJK[2],
6408                                        a[i], a[i]*af);
6409                fprintf(Opt->VoxDbgOut, "e1:   ");
6410                for(k=0; k<cs->N_label; ++k) {
6411                   fprintf(Opt->VoxDbgOut, "%f   ", e1[k]);
6412                }
6413                fprintf(Opt->VoxDbgOut, "\n");
6414                fprintf(Opt->VoxDbgOut, "e2:   ");
6415                for(k=0; k<cs->N_label; ++k) {
6416                   fprintf(Opt->VoxDbgOut, "%f   ", e2[k]);
6417                }
6418                fprintf(Opt->VoxDbgOut, "\n");
6419                fprintf(Opt->VoxDbgOut, "e:   ");
6420                for(k=0; k<cs->N_label; ++k) {
6421                   if (k!=kmin) fprintf(Opt->VoxDbgOut, "%f   ", e1[k]+e2[k]);
6422                   else fprintf(Opt->VoxDbgOut, "%f*  ", e1[k]+e2[k]);
6423                }
6424                fprintf(Opt->VoxDbgOut, "\n\n");
6425             }
6426             eG1 += e1[kmin];
6427             eG2 += e2[kmin]; /* <- Not quite,
6428                                  cliques get counted more than once
6429                                  this way. This should be a sum over
6430                                  all clicques (Eq. 19 Berthod et al 96),
6431                                   not over all cliques sums
6432                                  at each voxel (Sum of Eq.20). Revisit */
6433             eG[iter] = eG1+eG2;
6434             co[i] = cs->keys[kmin];
6435             /* store the prob. for each class p(c|Neighb) */
6436             pp = 0;
6437             for (k=0; k<cs->N_label; ++k) {
6438                e2[k] = exp(-e2[k]); /* now e2 is a prob. * scaling factor*/
6439                pp += e2[k];
6440             }
6441             for (k=0; k<cs->N_label; ++k) {
6442                PSCVAL(pCgN,k, i, fpCgN[k], e2[k]/pp);
6443             }
6444          } else {
6445             co[i] = 0;
6446          }
6447       }/* for i */
6448       if (Opt->debug > 1) SUMA_S_Notev("Iter %d, e=%f\n", iter, eG[iter]);
6449    } /* for iter */
6450 
6451 
6452    SUMA_ifree(ci);   SUMA_ifree(fpCgN); SUMA_ifree(fpC);
6453    SUMA_RETURN(1);
6454 }
6455 
6456 /*!
6457    Posterior distribution of class given the whole enchilada
6458 
6459 */
SUMA_pst_C_giv_ALL(THD_3dim_dataset * aset,byte * cmask,int cmask_count,SUMA_CLASS_STAT * cs,THD_3dim_dataset * pC,THD_3dim_dataset * pCgN,float mrfB,float Temp,byte mix,THD_3dim_dataset ** pcgallp)6460 int SUMA_pst_C_giv_ALL(THD_3dim_dataset *aset,
6461                                  byte *cmask, int cmask_count,
6462                                  SUMA_CLASS_STAT *cs,
6463                                  THD_3dim_dataset *pC, THD_3dim_dataset *pCgN,
6464                                  float mrfB, float Temp, byte mix,
6465                                  THD_3dim_dataset **pcgallp)
6466 {
6467    static char FuncName[]={"SUMA_pst_C_giv_ALL"};
6468    short *a=NULL;
6469    double *p=NULL, *m=NULL, *s=NULL,  *gd, *ds2, *ps=NULL,
6470             sp,  BoT, x0, e, PP[64], PG[64], *w=NULL, eN=0.0, pp, pg,
6471             wconst, wg;
6472    float af, fpCw, fpC, *fpCgN=NULL;
6473    int i, k, ni, nj, nk, nij, ijkn[6], shft;
6474    THD_3dim_dataset *pout = *pcgallp;
6475    THD_3dim_dataset *pCw=NULL;
6476    char sbuf[256];
6477    static int icall=0, iwarn=0;
6478 
6479    SUMA_ENTRY;
6480 
6481    if (!pout) {
6482       NEW_SHORTY(aset,cs->N_label,"SUMA_pst_C_giv_ALL",pout);
6483       *pcgallp = pout;
6484    }
6485 
6486    BoT = mrfB/Temp; /* THIS is not being used ... Check before deleting*/
6487 
6488    ni = DSET_NX(aset);
6489    nj = DSET_NY(aset);
6490    nk = DSET_NZ(aset);
6491    nij = ni*nj;
6492 
6493    af = DSET_BRICK_FACTOR(aset,0); if (af == 0.0f) af = 1.0;
6494    a = (short *)DSET_ARRAY(aset,0);
6495    p = (double *)SUMA_calloc(cs->N_label*DSET_NVOX(aset), sizeof(double));
6496    ps = (double *)SUMA_calloc(DSET_NVOX(aset), sizeof(double));
6497    m = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6498    s = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6499    ds2 = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6500    gd = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6501    fpCgN = (float*)SUMA_calloc(cs->N_label, sizeof(float));
6502 
6503    if (pCgN) {
6504       GET_BFs(pCgN, fpCgN);
6505    }
6506 
6507    /* get the global (average) mixing fraction */
6508    w = SUMA_get_Stats(cs, "mix");
6509    wconst=1.0/(double)cs->N_label;
6510 
6511    /* prepare the voxelwise mixing fraction denominator */
6512    fpCw = 1/10000.0;
6513    NEW_SHORTY(aset,cs->N_label,"pCw",pCw);
6514    for (k=0; k<cs->N_label; ++k) {
6515       EDIT_BRICK_FACTOR(pCw,k, fpCw);
6516    }
6517    if (pC) {
6518       fpC = DSET_BRICK_FACTOR(pC, 0);
6519       for (i=0; i<DSET_NVOX(aset); ++i) {
6520          sp = 0.0;
6521          for (k=0; k<cs->N_label; ++k) {
6522             if (IN_MASK(cmask, i)) {
6523                if (mix) wg = w[k];
6524                else wg = wconst;
6525                GSCVAL(pC, k, i, fpC, e);
6526                PP[k] = wg*e;
6527                if (pCgN) {
6528                   GSCVAL(pCgN, k, i, fpCgN[k], eN);
6529                   PP[k] *= eN;
6530                }
6531                sp += PP[k];
6532             }
6533          }
6534          for (k=0; k<cs->N_label; ++k) {
6535             if (IN_MASK(cmask, i)) {
6536                PSCVAL(pCw,k,i,fpCw, PP[k]/sp);
6537             }
6538          }
6539       }
6540    } else {
6541       /* Just use the global mixing fractions */
6542       for (k=0; k<cs->N_label; ++k) {
6543          if (mix) wg = w[k];
6544          else wg = wconst;
6545          for (i=0; i<DSET_NVOX(aset); ++i) {
6546             if (IN_MASK(cmask, i)) {
6547                if (pCgN) {
6548                   GSCVAL(pCgN, k, i, fpCgN[k], eN);
6549                   PSCVAL(pCw,k,i,fpCw, wg*eN);
6550                } else {
6551                   PSCVAL(pCw,k,i,fpCw, wg);
6552                }
6553             }
6554          }
6555       }
6556    }
6557 
6558 
6559    /* Get class stats */
6560    for (k=0; k<cs->N_label; ++k) {
6561       m[k]=SUMA_get_Stat(cs, cs->label[k], "mean");
6562       s[k]=SUMA_get_Stat(cs, cs->label[k], "stdv");
6563       ds2[k] = 2.0*s[k]*s[k];
6564       gd[k] = 1.0/(SQ2PI*s[k]);
6565    }
6566 
6567    fpCw = DSET_BRICK_FACTOR(pCw, 0);
6568    for (k=0; k<cs->N_label; ++k) {
6569       shft = k*DSET_NVOX(aset); PP[k]=0; PG[k]=0;
6570       for (i=0; i<DSET_NVOX(aset); ++i) {
6571          if (IN_MASK(cmask, i)) {
6572             x0 = (double)a[i]*af - m[k];
6573             pg = exp(-(x0*x0)/ds2[k])*gd[k];
6574 
6575             GSCVAL(pCw, k, i, fpCw, pp);
6576 
6577             p[i+shft] = (pg)*(pp);
6578             ps[i] += p[i+shft];
6579                if (i == VoxDbg) { /* store for debugging */
6580                   PP[k] = pp;
6581                   PG[k] = pg;
6582                }
6583          }
6584       }
6585    }
6586 
6587    /* and marginalize */
6588    for (k=0; k<cs->N_label; ++k) {
6589       shft = k*DSET_NVOX(aset);
6590       for (i=0; i<DSET_NVOX(aset); ++i) {
6591          if (IN_MASK(cmask, i)) {
6592             if (ps[i] == 0.0) {
6593                p[i+shft] = 1.0/(double)(cs->N_label);
6594                if (!iwarn) {
6595                   SUMA_S_Warnv("Have prob. sum of 0.0 at voxel %d\n"
6596                                "Such voxels should be masked.\n"
6597                                "Setting all such voxels to %f\n"
6598                                "Further warnings muted.\n",
6599                                i, 1.0/cs->N_label);
6600                   ++iwarn;
6601                }
6602             } else p[i+shft] /= ps[i];
6603          }
6604       }
6605    }
6606    SUMA_ifree(ps); SUMA_ifree(fpCgN);
6607 
6608 
6609       if (VoxDbg >= 0) {
6610          int IJK[3], pp;
6611          i = VoxDbg;
6612          Vox1D2Vox3D(i,DSET_NX(aset), DSET_NX(aset)*DSET_NY(aset), IJK);
6613          fprintf(VoxDbgOut, "at %d %d %d, a=%d (%f)\n",
6614                                  IJK[0], IJK[1], IJK[2],
6615                                  a[i], a[i]*af);
6616          fprintf(VoxDbgOut, "p(y|params)[]:   ");
6617          for(k=0; k<cs->N_label; ++k) {
6618             fprintf(VoxDbgOut, "%f   ", PG[k]);
6619          }
6620          fprintf(VoxDbgOut, "\n");
6621          fprintf(VoxDbgOut, "w[]:   ");
6622          for(k=0; k<cs->N_label; ++k) {
6623             fprintf(VoxDbgOut, "%f   ", PP[k]);
6624          }
6625          fprintf(VoxDbgOut, "\n\n");
6626       }
6627 
6628    /* put vector back in pout */
6629    for (k=0; k<cs->N_label; ++k) {
6630       EDIT_substscale_brick(pout, k, MRI_double,
6631                             (p+k*DSET_NVOX(pout)), MRI_short, -1.0);
6632       sprintf(sbuf,"P(%s|y)",cs->label[k]);
6633       EDIT_BRICK_LABEL( pout, k, sbuf);
6634    }
6635 
6636    SUMA_ifree(p);
6637    SUMA_ifree(m);
6638    SUMA_ifree(s);
6639    SUMA_ifree(ds2);
6640    SUMA_ifree(gd);
6641    DSET_delete(pCw); pCw = NULL;
6642 
6643    ++icall;
6644    SUMA_RETURN(1);
6645 }
6646 
SUMA_SegInitCset(THD_3dim_dataset * aseti,THD_3dim_dataset ** csetp,byte * cmask,int cmask_count,char * mixopt,SUMA_CLASS_STAT * cs,SEG_OPTS * Opt)6647 int SUMA_SegInitCset(THD_3dim_dataset *aseti,
6648                      THD_3dim_dataset **csetp,
6649                      byte *cmask, int cmask_count,
6650                      char *mixopt,
6651                      SUMA_CLASS_STAT *cs,
6652                      SEG_OPTS *Opt)
6653 {
6654    static char FuncName[]={"SUMA_SegInitCset"};
6655    int ibias = 0, border;
6656    short *a=NULL;
6657    double *p=NULL, *m=NULL, *s=NULL,  *gd, *ds2, *ps=NULL,
6658             sp,  x0, e, PP[64], PG[64],  pp, pg;
6659    float af;
6660    int i, k, shft;
6661    char sbuf[256];
6662    THD_3dim_dataset *cset=*csetp, *pstC=NULL, *cset_init=NULL;
6663    OPT_KMEANS oc;
6664 
6665    SUMA_ENTRY;
6666 
6667    oc = new_kmeans_oc();
6668    oc.k = SUMA_Class_k_Selector(cs,
6669                                 "not_classes_string", "OTHER",NULL);
6670    oc.remap = MAG;
6671    oc.verb = Opt->debug-1; if (oc.verb < 0) oc.verb = 0;
6672    oc.distmetric = 'e';
6673    for (i=0; i<oc.k; ++i) oc.clabels[i] = cs->label[i];
6674    oc.jobname=SUMA_append_replace_string(Opt->proot?Opt->proot:".",
6675                                          FuncName, "/", 0);
6676    if (!cset) {
6677       /* Let clustering do the whole deal*/
6678       oc.r = 3;
6679       cset_init = NULL;
6680    } else {
6681       oc.r = 0;
6682       /* initialize by cset */
6683       cset_init = cset;
6684    }
6685    if (Opt->debug > 1) {
6686       SUMA_S_Notev("Calling clustering function %d voxels in mask\n",
6687                    cmask_count);
6688    }
6689    if (!(thd_Acluster1 (aseti,
6690                cmask, cmask_count,
6691                &cset,
6692                NULL,
6693                cset_init,
6694                oc))) {
6695       SUMA_S_Err("Failed to cluster");
6696       SUMA_RETURN(0);
6697    }
6698    if (!cset_init) { /* happens when cset is NULL before thd_Acluster1 call */
6699       *csetp = cset;
6700    }
6701 
6702    if (Opt->debug > 1) {
6703       SUMA_S_Note("Stats on clusters");
6704    }
6705    /* compute class stats */
6706    if (!SUMA_Class_stats( aseti, cset, cmask, cmask_count,
6707                            NULL, NULL, Opt->gold, cs, Opt->mix_frac_floor)) {
6708       SUMA_S_Err("Failed in class stats");
6709       SUMA_RETURN(0);
6710    }
6711 
6712    af = DSET_BRICK_FACTOR(aseti,0); if (af == 0.0f) af = 1.0;
6713    a = (short *)DSET_ARRAY(aseti,0);
6714    p = (double *)SUMA_calloc(cs->N_label*DSET_NVOX(aseti), sizeof(double));
6715    ps = (double *)SUMA_calloc(DSET_NVOX(aseti), sizeof(double));
6716    m = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6717    s = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6718    ds2 = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6719    gd = (double *)SUMA_calloc(cs->N_label, sizeof(double));
6720 
6721    /* Compute pst(c) = p(y|stats) * mixrac */
6722    NEW_SHORTY(aseti, cs->N_label, "ini.pstC", pstC);
6723    for (k=0; k<cs->N_label; ++k) {
6724       m[k]=SUMA_get_Stat(cs, cs->label[k], "mean");
6725       s[k]=SUMA_get_Stat(cs, cs->label[k], "stdv");
6726       ds2[k] = 2.0*s[k]*s[k];
6727       gd[k] = 1.0/(SQ2PI*s[k]);
6728    }
6729 
6730    for (k=0; k<cs->N_label; ++k) {
6731       if (mixopt) {
6732          pp = SUMA_mixopt_2_mixfrac(mixopt, cs->label[k],
6733                              cs->keys[k], cs->N_label,
6734                              cmask, cset);
6735          if (pp < 0.0) {
6736             SUMA_S_Err("Failed to get mixfrac");
6737             SUMA_RETURN(0);
6738          }
6739       } else {
6740          pp = 1.0/cs->N_label;
6741       }
6742       shft = k*DSET_NVOX(aseti); PP[k]=0; PG[k]=0;
6743       for (i=0; i<DSET_NVOX(aseti); ++i) {
6744          if (IN_MASK(cmask, i)) {
6745             x0 = (double)a[i]*af - m[k];
6746             pg = exp(-(x0*x0)/ds2[k])*gd[k];
6747             p[i+shft] = (pg)*(pp);
6748             ps[i] += p[i+shft];
6749                if (i == Opt->VoxDbg) { /* store for debugging */
6750                   PP[k] = pp;
6751                   PG[k] = pg;
6752                }
6753          }
6754       }
6755    }
6756 
6757    /* and marginalize */
6758    for (k=0; k<cs->N_label; ++k) {
6759       shft = k*DSET_NVOX(aseti);
6760       for (i=0; i<DSET_NVOX(aseti); ++i) {
6761          if (IN_MASK(cmask, i)) {
6762             p[i+shft] /= ps[i];
6763          }
6764       }
6765    }
6766    SUMA_ifree(ps);
6767 
6768    /* store p in dset */
6769    for (k=0; k<cs->N_label; ++k) {
6770       EDIT_substscale_brick( pstC, k, MRI_double,
6771                             (p+k*DSET_NVOX(pstC)), MRI_short, -1.0);
6772       sprintf(sbuf,"Pinit(%s|y)",cs->label[k]);
6773       EDIT_BRICK_LABEL( pstC, k, sbuf);
6774    }
6775 
6776    /* reassign class membership */
6777    if (!(SUMA_assign_classes( pstC, cs,
6778                               cmask, &cset))) {
6779       SUMA_S_Err("Failed in assign_classes");
6780       SUMA_RETURN(0);
6781    }
6782 
6783    /* recompute class stats using this posterior */
6784    if (!SUMA_Class_stats( aseti, cset, cmask, cmask_count,
6785                           pstC, NULL, Opt->gold, cs, Opt->mix_frac_floor)) {
6786       SUMA_S_Err("Failed in class stats");
6787       SUMA_RETURN(0);
6788    }
6789 
6790    /* free temps */
6791    SUMA_ifree(p);
6792    SUMA_ifree(m);
6793    SUMA_ifree(s);
6794    SUMA_ifree(ds2);
6795    SUMA_ifree(gd);
6796    DSET_delete(pstC); pstC = NULL;
6797 
6798    SUMA_RETURN(1);
6799 }
6800 
SUMA_GetConstFactor(THD_3dim_dataset * pset)6801 float SUMA_GetConstFactor(THD_3dim_dataset *pset) {
6802    int k;
6803    for (k=1; k<DSET_NVALS(pset); ++k) {
6804       if (SUMA_ABS(DSET_BRICK_FACTOR(pset,k)-DSET_BRICK_FACTOR(pset,k-1))
6805                > 0.000001) {
6806          return(-1.0);
6807       }
6808    }
6809 
6810    return(DSET_BRICK_FACTOR(pset,0));
6811 }
6812 
6813 /*!
6814    Take an input dset and make sure it is formatted
6815    as a shortized probability dset with equal scaling
6816    factors.
6817    If the input is a single sub-brick of class indices,
6818    it creates an binarized prob. dset
6819 */
SUMA_ShortizeProbDset(THD_3dim_dataset ** csetp,SUMA_CLASS_STAT * cs,byte * cmask,int cmask_count,SEG_OPTS * Opt,THD_3dim_dataset ** psetp)6820 int SUMA_ShortizeProbDset(THD_3dim_dataset **csetp,
6821                         SUMA_CLASS_STAT *cs,
6822                         byte *cmask, int cmask_count,
6823                         SEG_OPTS *Opt,
6824                         THD_3dim_dataset **psetp)
6825 {
6826    static char FuncName[]={"SUMA_ClassToProbDset"};
6827    THD_3dim_dataset *pset = NULL;
6828    THD_3dim_dataset *cset = *csetp;
6829    float fpset;
6830    byte *bb=NULL, shortize=0;
6831    short *gb=NULL, *C=NULL;
6832    int ijk=0, k;
6833    SUMA_Boolean LocalHead = NOPE;
6834 
6835    SUMA_ENTRY;
6836 
6837    fpset = 1.0/10000.0;
6838 
6839    if (!psetp) {
6840       SUMA_S_Err("NULL psetp");
6841       SUMA_RETURN(0);
6842    }
6843    pset = *psetp;
6844 
6845    if (DSET_NVALS(cset) == 1) { /* gave me classes, make probabilities */
6846       SUMA_LH("Changing classes to probabilities");
6847       if (!pset || pset == cset) NEW_SHORTY(cset, cs->N_label, "prior_p", pset);
6848       if (DSET_NVALS(pset)!=cs->N_label) {
6849          SUMA_S_Errv("Bad input, have %d vals in pset, need %d\n",
6850             DSET_NVALS(pset), cs->N_label);
6851          SUMA_RETURN(0);
6852       }
6853 
6854       NEW_SHORTY(cset, cs->N_label, "prior_p", pset);
6855       C = (short *)DSET_ARRAY(cset,0);
6856       bb = (byte *)SUMA_calloc(DSET_NVOX(cset), sizeof(byte));
6857       for (k=0; k<cs->N_label; ++k) {
6858          gb = (short *)DSET_ARRAY(pset,k);
6859          for (ijk=0; ijk<DSET_NVOX(cset); ++ijk) {
6860             if (IN_MASK(cmask,ijk) && C[ijk] == cs->keys[k]) {
6861                gb[ijk] = (short )1.0/fpset; bb[ijk] = 1;
6862             }
6863          }
6864          EDIT_BRICK_FACTOR(pset, k, fpset);
6865       }
6866       /* make sure each voxel in mask got something */
6867       for (k=0; k<cs->N_label; ++k) {
6868          gb = (short *)DSET_ARRAY(pset,k);
6869          for (ijk=0; ijk<DSET_NVOX(cset); ++ijk) {
6870             if (IN_MASK(cmask,ijk) && !bb[ijk]) {
6871                gb[ijk] = (short )(1.0/fpset/cs->N_label);
6872             }
6873          }
6874       }
6875       SUMA_ifree(bb);
6876    } else {
6877       /* cset is considered to be a probs. dset */
6878       pset = cset; *csetp = NULL; /* to guard against multiple copies */
6879       /* make sure you don't get a bad number of sub-bricks */
6880       if (DSET_NVALS(pset) != cs->N_label) {
6881          SUMA_S_Errv( "Bad news in tennis shoes, \n"
6882                       "have %d sub-bricks in %s and %d labels",
6883                       DSET_NVALS(pset), DSET_PREFIX(pset), cs->N_label);
6884          SUMA_RETURN(0);
6885       }
6886    }
6887 
6888    shortize = 0;
6889    if (SUMA_GetConstFactor(pset) < 0.0) {  shortize = 1; }
6890    else {
6891       for (k=0; k<cs->N_label; ++k) {
6892          if (DSET_BRICK_TYPE(pset,k) != MRI_short) {
6893             shortize = 1;
6894             break;
6895          }
6896       }
6897    }
6898 
6899    if (shortize) {
6900       SUMA_LHv("Shortizing %s\n", DSET_PREFIX(pset));
6901       if (!SUMA_ShortizeDset(&pset, fpset)) {
6902          SUMA_S_Err("Failed to shortize");
6903          SUMA_RETURN(0);
6904       }
6905    }
6906 
6907    *psetp=pset;
6908    SUMA_RETURN(1);
6909 }
6910 
SUMA_FlattenProb(THD_3dim_dataset * pC,byte * cmask,int cmask_count,int mode)6911 int SUMA_FlattenProb(THD_3dim_dataset *pC,
6912                      byte *cmask, int cmask_count,
6913                      int mode)
6914 {
6915    static char FuncName[]={"SUMA_FlattenProb"};
6916    int i, k, nbrick=DSET_NVALS(pC);
6917    double ss, pp;
6918    float fpC[nbrick];
6919    SUMA_Boolean LocalHead = NOPE;
6920 
6921    SUMA_ENTRY;
6922 
6923    GET_BFs(pC,fpC);
6924 
6925    switch (mode) {
6926       case 1:
6927          for (i=0; i<DSET_NVOX(pC); ++i) {
6928             if (1 || IN_MASK(cmask,i)) { /* focusing on mask only gives ugly
6929                                        edge artifacts, they won't bother with
6930                                        segmentation, but they're ugly */
6931                ss = 0;
6932                for (k=0; k<nbrick; ++k) {
6933                   GSCVAL(pC,k,i,fpC[k],pp);
6934                   ss += pp;
6935                }
6936                ss = ss/(double)(nbrick);
6937                for (k=0; k<nbrick; ++k) {
6938                   PSCVAL(pC,k,i,fpC[k], ss);
6939                }
6940             }
6941          }
6942          break;
6943       default:
6944          SUMA_S_Err("Not ready for this mode");
6945          SUMA_RETURN(0);
6946    }
6947 
6948    if (LocalHead) SUMA_Seg_Write_Dset(NULL, "FLAT", pC, -1, NULL);
6949 
6950    SUMA_RETURN(1);
6951 }
6952 
6953 /*!
6954    Given a prob. dset, add an 'OTHER' class
6955 */
SUMA_OtherizeProbDset(THD_3dim_dataset * pC,byte * cmask,int cmask_count)6956 int SUMA_OtherizeProbDset(THD_3dim_dataset *pC,
6957                           byte *cmask, int cmask_count)
6958 {
6959    static char FuncName[]={"SUMA_OtherizeProbDset"};
6960    int i, k, nbrick = DSET_NVALS(pC);
6961    double ss, pp;
6962    short *cc=NULL;
6963    float fpC[nbrick+1];
6964 
6965    SUMA_ENTRY;
6966 
6967    GET_BFs(pC,fpC);
6968    fpC[nbrick] = 1/10000.0;
6969 
6970    cc = (short *)calloc(DSET_NVOX(pC), sizeof(short));
6971    EDIT_add_brick(pC, MRI_short, fpC[nbrick], cc);
6972    EDIT_BRICK_LABEL(pC, nbrick, "OTHER");
6973    for (i=0; i<DSET_NVOX(pC); ++i) {
6974       if (1 || IN_MASK(cmask,i)) { /* no need to stick to mask here */
6975          ss = 0;
6976          for (k=0; k<nbrick; ++k) {
6977             GSCVAL(pC,k,i,fpC[k],pp);
6978             ss += pp;
6979             if (i==1332180) {
6980                fprintf(stderr,"%d: %f --> %f\n", k, pp, ss);
6981             }
6982          }
6983          ss = (1.0 - SUMA_MIN_PAIR(ss, 1.0));
6984          PSCVAL(pC, nbrick, i, fpC[nbrick], ss);
6985          if (i==1332180) {
6986                GSCVAL(pC,k,i,fpC[nbrick],pp);
6987                fprintf(stderr,"%d:  --> %f (%f)\n", nbrick,  ss, pp);
6988             }
6989       }
6990    }
6991    SUMA_RETURN(1);
6992 }
6993 
SUMA_AddOther(NI_str_array * clss,int ** keysp,byte * cmask,int cmask_count,THD_3dim_dataset * cset,THD_3dim_dataset * pstCgALL,THD_3dim_dataset * pCgA,THD_3dim_dataset * pCgL,SUMA_CLASS_STAT * cs)6994 int SUMA_AddOther(  NI_str_array *clss, int **keysp,
6995                     byte *cmask, int cmask_count,
6996                     THD_3dim_dataset *cset, THD_3dim_dataset *pstCgALL,
6997                     THD_3dim_dataset *pCgA, THD_3dim_dataset *pCgL,
6998                     SUMA_CLASS_STAT *cs)
6999 {
7000    static char FuncName[]={"SUMA_AddOther"};
7001    int i, mxkey=0;
7002    int *keys=*keysp;
7003    short *cc=NULL;
7004    float fpG;
7005    SUMA_Boolean LocalHead = NOPE;
7006 
7007    SUMA_ENTRY;
7008 
7009    SUMA_S_Warn("Adding OTHER CLASS");
7010    /* clss */
7011    clss->num = clss->num+1;
7012    clss->str =
7013       NI_realloc(clss->str, char *, sizeof(char *)*(clss->num));
7014    clss->str[clss->num-1] = NI_malloc(char, strlen("OTHER")+1);
7015    strcpy(clss->str[clss->num-1], "OTHER");
7016 
7017    /* keys */
7018    mxkey = keys[0];
7019    for (i=1; i<clss->num-1; ++i) {
7020       if (mxkey < keys[i]) mxkey = keys[i];
7021    }
7022    keys = (int *)SUMA_realloc(keys, sizeof(int)*clss->num);
7023    keys[clss->num-1] = mxkey+1;
7024    *keysp = keys;
7025 
7026    /* cset ? */
7027    if (cset) {
7028       cc = DSET_ARRAY(cset,0);
7029       for (i=0; i<DSET_NVOX(cset); ++i) {
7030          if (IN_MASK(cmask,i)) {
7031             if (!cc[i]) cc[i] = mxkey+1;
7032          }
7033       }
7034    }
7035 
7036    /* pstCgALL ? */
7037    if (pstCgALL) {
7038       fpG = DSET_BRICK_FACTOR(pstCgALL,0);
7039       EDIT_add_brick(pstCgALL, MRI_short, fpG, NULL);
7040       EDIT_substitute_brick( pstCgALL, DSET_NVALS(pstCgALL)-1,
7041                               MRI_short, NULL ) ;
7042       EDIT_BRICK_LABEL(pstCgALL, DSET_NVALS(pstCgALL)-1, "OTHER");
7043    }
7044 
7045    if (pCgA) {
7046       if (!SUMA_OtherizeProbDset(pCgA,
7047                         cmask, cmask_count)) {
7048          SUMA_S_Errv("Failed to otherize pCgA %s\n", DSET_PREFIX(pCgA));
7049          SUMA_RETURN(0);
7050       }
7051       if (LocalHead) SUMA_Seg_Write_Dset(NULL, "pCgA-Otherized", pCgA,
7052                                          -1, NULL);
7053    }
7054    if (pCgL) {
7055       if (!SUMA_OtherizeProbDset(pCgL,
7056                         cmask, cmask_count)) {
7057          SUMA_S_Errv("Failed to otherize pCgL %s\n", DSET_PREFIX(pCgL));
7058          SUMA_RETURN(0);
7059       }
7060       if (LocalHead) SUMA_Seg_Write_Dset(NULL, "pCgL-Otherized", pCgL, -1, NULL);
7061    }
7062 
7063    /* cs ? */
7064    if (cs) {
7065       if (SUMA_Add_Class_Label(cs, "OTHER", keys[clss->num-1]) < 0) {
7066          SUMA_S_Err("Failed to SUMA_Add_Class_Label OTHER");
7067          SUMA_RETURN(0);
7068       }
7069    }
7070 
7071    SUMA_RETURN(1);
7072 }
7073 
7074 /*!
7075    A convenience function for SUMA_hist to build a histogram
7076    for a sub-brick based on a pre-existing histogram
7077    oscifreq and optmethods are only used if href is NULL
7078    and if optmethods is not == "NONE"
7079 */
SUMA_dset_hist(THD_3dim_dataset * dset,int ia,byte * cmask,char * label,SUMA_HIST * href,int ignoreout,float oscifreq,char * optmethods)7080 SUMA_HIST *SUMA_dset_hist(THD_3dim_dataset *dset, int ia,
7081                           byte *cmask, char *label,
7082                           SUMA_HIST *href, int ignoreout,
7083                           float oscifreq, char *optmethods )
7084 {
7085    static char FuncName[]={"SUMA_dset_hist"};
7086    int i = 0, N_k = 0;
7087    float orange[2]={0.0, 0.0}, *fv=NULL;
7088    SUMA_HIST *hh=NULL;
7089 
7090    SUMA_ENTRY;
7091 
7092    if (!dset || ia < 0 || ia >= DSET_NVALS(dset)) SUMA_RETURN(hh);
7093    if (!(fv = THD_extract_to_float(ia, dset))) {
7094       SUMA_S_Errv("Failed to extract sub-brick %d\n", ia);
7095       SUMA_RETURN(hh);
7096    }
7097    if (cmask) {
7098       N_k = 0;
7099       for (i=0; i<DSET_NVOX(dset); ++i) {
7100          if (cmask[i]) { fv[N_k] = fv[i]; ++N_k; }
7101       }
7102    } else {
7103       N_k = DSET_NVOX(dset);
7104    }
7105    if (!label) label = "unloved";
7106 
7107    if (href) {
7108       orange[0] = href->min; orange[1] = href->max;
7109       hh = SUMA_hist(fv, N_k, href->K, href->W, orange, "lll", ignoreout);
7110    } else {
7111       if (optmethods && !strcasecmp(optmethods,"NONE")) {
7112          hh = SUMA_hist(fv, N_k, 0, 0, NULL, "lll",ignoreout);
7113       } else {
7114          hh = SUMA_hist_opt(fv, N_k, 0, 0, NULL, "lll",ignoreout,
7115                           oscifreq, optmethods);
7116       }
7117    }
7118 
7119    /* set cdfmin */
7120    hh->cdfmin = -1.0; i = 0;
7121    while (hh->cdfmin < 0 && i < hh->K) {
7122       if (hh->c[i++] > 0) hh->cdfmin = hh->c[i];
7123    }
7124 
7125    free(fv); fv = NULL;
7126    SUMA_RETURN(hh);
7127 }
7128 
7129 /*!
7130    \brief Create histogram from n data values in v
7131    \param v (float *) vector of values
7132    \param n (int) number of values in v
7133    \param Ku (int) if > 0 set the number of bins
7134    \param Wu (float) if > 0 set the bin width
7135                (Ku and Wu are mutually exclusive, Ku takes precedence)
7136    \param range (float *) if !NULL set min = range[0], max = range[1]
7137    \param label (char *) what you think it is
7138    \param ignoreout (int ) if == 0 then values < min are set in bottom bin
7139                                    and  values > max are set in top bin
7140                               == 1 then values < min or > max are ignored
7141 */
7142 
SUMA_hist(float * v,int n,int Ku,float Wu,float * range,char * label,int ignoreout)7143 SUMA_HIST *SUMA_hist(float *v, int n, int Ku, float Wu, float *range,
7144                      char *label, int ignoreout)
7145 {
7146    static char FuncName[]={"SUMA_hist"};
7147    int i=0, minloc, maxloc, ib=0;
7148    float min=0.0, max=0.0;
7149    SUMA_HIST *hh=NULL;
7150    SUMA_Boolean LocalHead = NOPE;
7151 
7152    SUMA_ENTRY;
7153 
7154 
7155    if (!v && !n) {/* special case for uniform histogram */
7156       if (Ku < 1 || !range || (range[1]<=range[0])) {
7157          SUMA_S_Err("Need proper range and number of bins for uniform hist");
7158          SUMA_RETURN(hh);
7159       }
7160       min = range[0]; max = range[1];
7161       hh = (SUMA_HIST *)SUMA_calloc(1, sizeof(SUMA_HIST));
7162       hh->max = max;
7163       hh->min = min;
7164       if (label) hh->label = SUMA_copy_string(label);
7165       SUMA_LHv("User opts: %d, %f, range [%f %f]\n",
7166                   Ku, Wu,
7167                   range?range[0]:-1111, range?range[1]:-1111);
7168       hh->K = Ku;
7169       hh->W = (max-min)/(float)hh->K;
7170       SUMA_LHv("Window %f, Bins %d, min %f max %f\n",
7171             hh->W, hh->K , min, max);
7172       /* initialize */
7173       hh->b = (float *)SUMA_calloc(hh->K, sizeof(float));
7174       hh->c = (int *)SUMA_calloc(hh->K, sizeof(int));
7175       hh->cn = (float *)SUMA_calloc(hh->K, sizeof(float));
7176 
7177       /* fill it */
7178       hh->N_ignored = 0;
7179       hh->n = 10*hh->K;
7180       for (i=0; i<hh->K;++i) {
7181          hh->c[i] = 10;
7182          hh->b[i] = hh->min+(i+0.5)*hh->W;
7183          hh->cn[i] = 1.0/hh->K;
7184       }
7185       SUMA_RETURN(hh);
7186    }
7187 
7188    if (!v) SUMA_RETURN(hh);
7189    if (n < 10) {
7190       SUMA_S_Errv("A hist with n = %d samples!!!\n", n);
7191       SUMA_RETURN(hh);
7192    }
7193    if (!range || (range[0]==0.0 && range[1] == 0.0)) {
7194       SUMA_MIN_MAX_VEC(v,n,min, max, minloc, maxloc);
7195       if (min == max) {
7196          SUMA_S_Errv("Single value of %f in samples. No good.\n", min);
7197          SUMA_RETURN(hh);
7198       }
7199    } else {
7200       min = range[0]; max = range[1];
7201    }
7202 
7203    hh = (SUMA_HIST *)SUMA_calloc(1, sizeof(SUMA_HIST));
7204    hh->max = max;
7205    hh->min = min;
7206    hh->n = n;
7207    if (label) hh->label = SUMA_copy_string(label);
7208    SUMA_LHv("User opts: %d, %f, range [%f %f]\n",
7209                Ku, Wu,
7210                range?range[0]:-1111, range?range[1]:-1111);
7211    if (Ku > 0) { /* user sets number of bins */
7212       hh->K = Ku;
7213       hh->W = (max-min)/(float)hh->K;
7214    } else if (Wu > 0.0) {
7215       hh->W = Wu;
7216       hh->K = (int)ceil((max-min)/hh->W);
7217    } else {
7218       hh->K = sqrt(n);
7219       hh->W = (max-min)/(float)hh->K;
7220    }
7221 
7222    SUMA_LHv("Window %f, Bins %d, min %f max %f\n",
7223             hh->W, hh->K , min, max);
7224    /* initialize */
7225    hh->b = (float *)SUMA_calloc(hh->K, sizeof(float));
7226    hh->c = (int *)SUMA_calloc(hh->K, sizeof(int));
7227    hh->cn = (float *)SUMA_calloc(hh->K, sizeof(float));
7228 
7229    /* fill it */
7230    hh->N_ignored = 0;
7231    if (ignoreout) {
7232       for (i=0; i<n;++i) {
7233          if (v[i]>=min && v[i]<=max) {
7234             ib = (int)((v[i]-min)/hh->W); if (ib==hh->K) ib = hh->K-1;
7235             ++hh->c[ib];
7236          } else {
7237             ++hh->N_ignored;
7238          }
7239       }
7240    } else {
7241       for (i=0; i<n;++i) {
7242          ib = (int)((v[i]-min)/hh->W);
7243          if (ib>=hh->K) ib = hh->K-1;
7244          else if (ib < 0) ib = 0;
7245          ++hh->c[ib];
7246       }
7247    }
7248 
7249    /* for convenience */
7250    for (i=0; i<hh->K;++i) {
7251       hh->b[i] = hh->min+(i+0.5)*hh->W;
7252       hh->cn[i] = (float)hh->c[i]/(float)n;
7253    }
7254 
7255    SUMA_RETURN(hh);
7256 }
7257 
SUMA_hist_opt(float * v,int n,int Ku,float Wu,float * range,char * label,int ignoreout,float oscfreqthr,char * methods)7258 SUMA_HIST *SUMA_hist_opt(float *v, int n, int Ku, float Wu, float *range,
7259                      char *label, int ignoreout,
7260                      float oscfreqthr, char *methods)
7261 {
7262    static char FuncName[]={"SUMA_hist_opt"};
7263    int i=0, minloc, maxloc, ib=0, N_iter=0, N_itermax=10, N_osci=0;
7264    float min=0.0, max=0.0, orange[2]={0.0, 0.0}, mxcn=0.0, osfrac=0.0;
7265    float minmaxfrac=0.0, oscfracthr=0.0;
7266    SUMA_HIST *hh=NULL, *hhn=NULL;
7267    SUMA_Boolean LocalHead = NOPE;
7268 
7269    SUMA_ENTRY;
7270 
7271    hh = SUMA_hist(v,n,Ku,Wu,range,label,ignoreout);
7272    if (!hh || (!v && n == 0)) SUMA_RETURN(hh);
7273    if (!methods) {
7274       methods = "Range|OsciBinWidth";
7275    }
7276 
7277    if (strstr(methods, "Range")) {
7278       Ku = 0; /* if set by user, should be loosened because we're controlling
7279                  range and binwidth here */
7280       /* try tightening the range */
7281       if (1) {
7282          mxcn = SUMA_hist_perc_freq(hh, 50, 1, NULL, 0.01);
7283          SUMA_LHv("Trying range tightening for hist %s\n"
7284                   "Nig/n=%f, min=%f, max=%f, mxcn=%f\n",
7285                 hh->label,
7286                 100*(float)hh->N_ignored/(float)hh->n,
7287                 hh->min, hh->max, mxcn);
7288          /* try to shrink the range and repeat */
7289          i = 0;
7290          while (i < hh->K && (hh->cn[i]/mxcn < 0.001)) ++i;
7291          orange[0] = hh->b[i]-0.5*hh->W;
7292          i = hh->K-1;
7293          while (i > 0 && (hh->cn[i]/mxcn < 0.001)) --i;
7294          orange[1] = hh->b[i]+0.5*hh->W;
7295          if (orange[0] != hh->min || orange[1] != hh->max) {
7296             /* retry with new range */
7297             SUMA_LHv("Retrying with n=%d, Ku=%d, Wu=%f, orange=[%f %f]\n",
7298                      n, Ku, Wu, orange[0], orange[1]);
7299             if ((hhn = SUMA_hist(v,n,Ku,Wu,orange,label,ignoreout))) {
7300                SUMA_Free_hist(hh); hh=hhn; hhn=NULL;
7301             } else {
7302                SUMA_S_Err("Unexpected error, returning with what I have");
7303                SUMA_RETURN(hh);
7304             }
7305          }
7306       }
7307 
7308       /* Still no good, range has to increase */
7309       N_iter = 0; N_itermax = 25;
7310       while (((float)hh->N_ignored/(float)hh->n > 0.01) && N_iter <= N_itermax) {
7311          if (N_iter < N_itermax) {
7312             SUMA_LHv("For histogram %s, %.2f%% of the samples were\n"
7313                          "ignored for being outside the range [%f %f]\n"
7314                          "Attempting range expansion, iteration %d\n",
7315                    hh->label,
7316                    100*(float)hh->N_ignored/(float)hh->n,
7317                    hh->min, hh->max, N_iter);
7318             /* try to increase the range the range and repeat */
7319             mxcn = SUMA_hist_perc_freq(hh, 50, 1, NULL, 0.01);
7320             if (hh->cn[hh->K-1] > hh->cn[0]) orange[1] += hh->K/20.0*hh->W;
7321             else if (hh->cn[hh->K-1] < hh->cn[0]) orange[0] -= hh->K/20.0*hh->W;
7322             else {
7323                orange[0] -= hh->K/20.0*hh->W;
7324                orange[1] += hh->K/20.0*hh->W;
7325             }
7326             if (orange[0] != hh->min || orange[1] != hh->max) {
7327                /* retry with new range */
7328                SUMA_LHv("Retry with Ku=%d, Wu=%f, orange=[%f %f], iter %d\n",
7329                         Ku, Wu, orange[0], orange[1], N_iter);
7330                if ((hhn = SUMA_hist(v,n,0,Wu,orange,label,ignoreout))) {
7331                   SUMA_Free_hist(hh); hh=hhn; hhn=NULL;
7332                } else {
7333                   SUMA_S_Err("Unexpected error, returning with what I have");
7334                   SUMA_RETURN(hh);
7335                }
7336             }
7337          } else {
7338             SUMA_S_Warnv("For histogram %s, %.2f%% of the samples were\n"
7339                          "ignored for being outside the range [%f %f]\n"
7340                          "Range expansion halted after %d iterations\n",
7341                    hh->label,
7342                    100*(float)hh->N_ignored/(float)hh->n,
7343                    hh->min, hh->max, N_itermax);
7344          }
7345          ++N_iter;
7346       }
7347    }
7348 
7349    /* bin oscillations? */
7350    if (oscfreqthr >= 0.0 && strstr(methods,"Osci")) {
7351       if (oscfreqthr == 0.0f) oscfreqthr = 0.3;
7352       N_iter = 0; N_itermax = 50;
7353 
7354       while (((osfrac = SUMA_hist_oscillation(hh, minmaxfrac,
7355                                              oscfracthr, &N_osci))
7356                         > oscfreqthr ||
7357                N_osci > 5 ) &&
7358              N_iter <= N_itermax) {
7359          if (N_iter < N_itermax) {
7360             if (strstr(methods,"OsciSmooth")) { /* by smoothing */
7361                SUMA_LHv("Histogram %s oscifraq = %f, N_osci=%d, "
7362                         "needs smoothing, iter %d\n",
7363                      hh->label, osfrac, N_osci, N_iter);
7364                SUMA_hist_smooth(hh,1);
7365             } else if (strstr(methods,"OsciBinWidth")){
7366                SUMA_LHv("Histogram %s oscifraq = %f, N_osci=%d, "
7367                         "needs bin adjustment, iter %d\n",
7368                         hh->label,osfrac,  N_osci, N_iter);
7369                Wu = hh->W*1.1; orange[0] = hh->min; orange[1] = hh->max;
7370                /* retry with new width */
7371                SUMA_LHv("Retry with Ku=%d, Wu=%f, orange=[%f %f], iter %d\n",
7372                         Ku, Wu, orange[0], orange[1], N_iter);
7373                if ((hhn = SUMA_hist(v,n,0,Wu,orange,label,ignoreout))) {
7374                   SUMA_Free_hist(hh); hh=hhn; hhn=NULL;
7375                } else {
7376                   SUMA_S_Err("Unexpected error, returning with what I have");
7377                   SUMA_RETURN(hh);
7378                }
7379             } else {
7380                SUMA_S_Errv("Bad Osci option in %s\n", methods);
7381                SUMA_RETURN(hh);
7382             }
7383          } else {
7384             SUMA_S_Warnv("Histogram %s oscifraq = %f still needs fixing"
7385                          " but iterations are exhasuted at %d\n",
7386                          hh->label,osfrac,  N_itermax);
7387          }
7388          ++N_iter;
7389       }
7390    }
7391    /* you probably want to consider the range again, perhaps loop back
7392       We'll see if that will be needed.*/
7393 
7394    SUMA_RETURN(hh);
7395 }
7396 
SUMA_hist_smooth(SUMA_HIST * hh,int N_iter)7397 int SUMA_hist_smooth( SUMA_HIST *hh, int N_iter )
7398 {
7399    static char FuncName[]={"SUMA_hist_smooth"};
7400    float *fbuf=NULL, *fbufn=NULL;
7401    int i, iter=0;
7402    SUMA_Boolean LocalHead = NOPE;
7403 
7404    SUMA_ENTRY;
7405 
7406    if (!hh) SUMA_RETURN(NOPE);
7407 
7408    if (N_iter == 0) N_iter = 1;
7409 
7410    iter = 0;
7411    while (iter < N_iter) {
7412       if (!fbuf) fbuf = (float *)SUMA_calloc(hh->K, sizeof(float));
7413       if (!fbufn) fbufn = (float *)SUMA_calloc(hh->K, sizeof(float));
7414 
7415       fbuf[0] = (hh->c[0]+hh->c[1])/2.0;
7416       fbuf[hh->K-1] = (hh->c[hh->K-1]+hh->c[hh->K-2])/2.0;
7417       fbufn[0] = (hh->cn[0]+hh->cn[1])/2.0;
7418       fbufn[hh->K-1] = (hh->cn[hh->K-1]+hh->cn[hh->K-2])/2.0;
7419       if (fbuf[0] > fbuf[hh->K-1]) {
7420          hh->min = fbuf[hh->K-1]; hh->max = fbuf[0];
7421       } else {
7422          hh->max = fbuf[hh->K-1]; hh->min = fbuf[0];
7423       }
7424       for (i=1; i<hh->K-1; ++i) {
7425          fbuf[i] = (hh->c[i-1]+hh->c[i]+hh->c[i+1])/3.0;
7426          fbufn[i] = (hh->cn[i-1]+hh->cn[i]+hh->cn[i+1])/3.0;
7427          if (fbuf[i]>hh->max) hh->max = fbuf[i];
7428          else if (fbuf[i]<hh->min) hh->min = fbuf[i];
7429       }
7430       memcpy(hh->cn, fbufn, hh->K*sizeof(float));
7431       memcpy(hh->c, fbuf, hh->K*sizeof(float));
7432       ++iter;
7433    }
7434 
7435    if (hh->isrt) { /* no longer valid */
7436       SUMA_free(hh->isrt); hh->isrt = NULL;
7437    }
7438    if (fbuf) SUMA_free(fbuf); fbuf=NULL;
7439    if (fbufn) SUMA_free(fbufn); fbufn=NULL;
7440 
7441    SUMA_RETURN(YUP);
7442 }
7443 
7444 /*!
7445    Look for oscillation (bad bin width) in histogram
7446 
7447    minfreq : Do not look for oscillation if a bin frequency is
7448                 is less than minfreq
7449                 Set to 0.0 to get default of 0.001
7450    oscfracthr : Do not consider a bin to exhibit oscillation if
7451                 the average of the absolute differences at that location
7452                 divided by the bin's frequency is less than oscfracthr
7453                 Set to 0.0 to get default of 0.05
7454    Returns the fraction of bins exhibiting oscillation from the total
7455    number of candidate bins (those exceeding minfreq).
7456    If N_osci is not null, return the actual count of oscillations
7457 */
SUMA_hist_oscillation(SUMA_HIST * hh,float minfreq,float oscfracthr,int * N_osci)7458 float SUMA_hist_oscillation( SUMA_HIST *hh,
7459                              float minfreq, float oscfracthr, int *N_osci)
7460 {
7461    static char FuncName[]={"SUMA_hist_oscillation"};
7462    int iosc=0,mxosc,i=0;
7463    double db, df, oscfrac, mx=0.0, oscfreq=0.0;
7464    SUMA_Boolean LocalHead = NOPE;
7465 
7466    SUMA_ENTRY;
7467 
7468    if (minfreq==0.0f) minfreq = 0.001;
7469    if (oscfracthr==0.0f) oscfracthr = 0.05;
7470 
7471    mx = SUMA_hist_perc_freq(hh,50,1,NULL, 0.01);
7472    if (mx == 0.0f) SUMA_RETURN(YUP);
7473 
7474    iosc=0; mxosc=0;
7475    for (i=1; i<hh->K-1;++i) {
7476       if (hh->cn[i] > minfreq) {
7477          ++mxosc;
7478          db = hh->cn[i] - hh->cn[i-1];
7479          df = hh->cn[i] - hh->cn[i+1];
7480          oscfrac = (SUMA_ABS(db)+SUMA_ABS(df))/(2*hh->cn[i]);
7481          if (db*df > 0 && oscfrac > oscfracthr) {
7482             SUMA_LHv("Oscfrac at bin %d of %f = %f\n",
7483                          i, hh->cn[i], oscfrac);
7484             ++iosc;
7485          }
7486       }
7487    }
7488 
7489    if (N_osci) *N_osci = iosc;
7490    oscfreq = 0;
7491    if (mxosc) oscfreq = (float)iosc/mxosc;
7492    SUMA_LHv("Osci frac of histogram %s is %f (%d oscis)\n",
7493                   hh->label, oscfreq, iosc);
7494 
7495    SUMA_RETURN(oscfreq);
7496 }
7497 
7498 
7499 /*
7500    If you change this function, be sure to reflect
7501    changes in SUMA_HIST_FREQ macro */
SUMA_hist_freq(SUMA_HIST * hh,float vv)7502 float SUMA_hist_freq(SUMA_HIST *hh, float vv)
7503 {
7504    float a = 0.0;
7505    int i0;
7506    if (!hh) return(-1.0);
7507    if (vv<hh->b[0]) return(hh->cn[0]);
7508    if (vv>hh->b[hh->K-1]) return(hh->cn[hh->K-1]);
7509    a = ((vv-hh->b[0])/hh->W);
7510    i0 = (int)a; a = a-i0;
7511    return(a*hh->cn[i0+1]+(1.0-a)*hh->cn[i0]);
7512 }
7513 
SUMA_hist_value(SUMA_HIST * hh,double vv,char * what)7514 double SUMA_hist_value(SUMA_HIST *hh, double vv, char *what)
7515 {
7516    double a = 0.0, val=0.0;
7517    int i0=0, ii=0;
7518 
7519    if (!hh) return(-1.0);
7520    if (vv<hh->b[0]) {
7521       a = -2; /* Before first bin */
7522    } else if (vv>hh->b[hh->K-1]) {
7523       a = -1; /* After last bin */
7524    } else {
7525       a = ((vv-hh->b[0])/hh->W);
7526       i0 = (int)a; a = a-i0;
7527       val = 0.0;
7528    }
7529    if (!what || !strcmp(what,"freq")) { /* return the frequency */
7530       if ( a < -1.0 ) {
7531          return(hh->cn[0]);
7532       } else if ( a < 0 ) {
7533          return(hh->cn[hh->K-1]);
7534       }
7535       val = a*hh->cn[i0+1]+(1.0-a)*hh->cn[i0];
7536    } else if (!strcmp(what,"count")) { /* return the count */
7537       if ( a < -1.0 ) {
7538          return(hh->c[0]);
7539       } else if ( a < 0 ) {
7540          return(hh->c[hh->K-1]);
7541       }
7542       val = a*hh->c[i0+1]+(1.0-a)*hh->c[i0];
7543    } else if (!strcmp(what,"bin")) { /* return the location on bin axis */
7544       if ( a < -1.0 ) {
7545          return(0.0);
7546       } else if ( a < 0 ) {
7547          return(hh->K);
7548       }
7549       val = i0+a;
7550    } else if ( !strcmp(what,"cdf") ||
7551                !strcmp(what,"ncdf")) { /* return the cdf */
7552       if (what[0] == 'n') { /* normalized */
7553          if ( a < -1.0 ) {
7554             return(0.0);
7555          } else if ( a < 0 ) {
7556             return(1.0);
7557          }
7558          for (ii=0; ii<=i0; ++ii) {
7559             val += hh->cn[ii];
7560          }
7561          val += a*hh->cn[i0+1];
7562       } else { /* count */
7563          if ( a < -1.0 ) {
7564             return(0.0);
7565          } else if ( a < 0 ) {
7566             return(hh->n);
7567          }
7568          for (ii=0; ii<=i0; ++ii) {
7569             val += hh->c[ii];
7570          }
7571          val += a*hh->c[i0+1];
7572       }
7573    } else if (!strcmp(what,"rcdf") ||
7574               !strcmp(what,"nrcdf")) { /* return the reverse cdf */
7575       if (what[0] == 'n') { /* normalized */
7576          if ( a < -1.0 ) {
7577             return(1.0);
7578          } else if ( a < 0 ) {
7579             return(0.0);
7580          }
7581          for (ii=hh->K-1; ii>i0; --ii) {
7582             val += hh->cn[ii];
7583          }
7584          val += (1.0-a)*hh->cn[i0];
7585       } else { /* count */
7586          if ( a < -1.0 ) {
7587             return(hh->n);
7588          } else if ( a < 0 ) {
7589             return(0.0);
7590          }
7591          for (ii=hh->K-1; ii>i0; --ii) {
7592             val += hh->c[ii];
7593          }
7594          val += (1.0-a)*hh->c[i0];
7595       }
7596    } else if (!strcmp(what,"outl")) {/* approx of (1- 2 x smallest tail area)
7597                                         1 means value is at tail ends
7598                                         0 means value splits histogram area
7599                                           in half*/
7600       if ( a < -1.0 ) {
7601          return(1);
7602       } else if ( a < 0 ) {
7603          return(1);
7604       }
7605       /* Outlierness of point */
7606       for (ii=hh->K-1; ii>i0; --ii) {
7607          val += hh->cn[ii];
7608       }
7609       val += (1.0-a)*hh->cn[i0];
7610       if (val > 0.5) {
7611          val = 2.0*(-0.5+val);
7612       } else val = 2.0*(0.5-val);
7613    }
7614    return(val);
7615 }
7616 
SUMA_Free_hist(SUMA_HIST * hh)7617 SUMA_HIST *SUMA_Free_hist(SUMA_HIST *hh)
7618 {
7619    static char FuncName[]={"SUMA_Free_hist"};
7620    SUMA_ENTRY;
7621    if (hh) {
7622       if (hh->b) SUMA_free(hh->b);
7623       if (hh->c) SUMA_free(hh->c);
7624       if (hh->cn) SUMA_free(hh->cn);
7625       if (hh->label) SUMA_free(hh->label);
7626       if (hh->isrt) SUMA_free(hh->isrt);
7627       SUMA_free(hh); hh=NULL;
7628    }
7629    SUMA_RETURN(NULL);
7630 }
7631 
7632 /*
7633    Sorts the histogram frequencies and returns the desired percentile
7634 */
SUMA_hist_perc_freq(SUMA_HIST * hh,float perc,int norm,int * iperc,float minperc)7635 float SUMA_hist_perc_freq(SUMA_HIST *hh, float perc, int norm, int *iperc,
7636                           float minperc)
7637 {
7638    static char FuncName[]={"SUMA_hist_perc_freq"};
7639    float ff = -1.0, *vvv=NULL;
7640    int ides=-1, ioff=0;
7641    SUMA_Boolean LocalHead = NOPE;
7642 
7643    SUMA_ENTRY;
7644 
7645    if (iperc) *iperc = -1;
7646    if (!hh) SUMA_RETURN(ff);
7647 
7648    /* sort the frequencies */
7649    if (!hh->isrt) {
7650       vvv = (float *)SUMA_calloc(hh->K,sizeof(float));
7651       memcpy(vvv, hh->cn, hh->K*sizeof(float));
7652       if (!(hh->isrt = SUMA_z_qsort ( vvv , hh->K ))) {
7653          SUMA_free(vvv);
7654          SUMA_S_Err("Failed to sort");
7655          SUMA_RETURN(ff);
7656       }
7657       SUMA_free(vvv); vvv=NULL;
7658    }
7659 
7660    if (minperc > 0.0) {
7661       ioff=0;
7662       if (norm) minperc *= hh->n;
7663       while (ioff < hh->K && hh->isrt[ioff] < minperc) ++ioff;
7664    }
7665    /* get the percentile */
7666    ides = ioff+SUMA_ROUND(perc/100.0*(hh->K-ioff)) - 1;
7667    if (ides < 0) ides = 0;
7668    else if (ides > hh->K-1) ides = hh->K-1;
7669 
7670    if (iperc) *iperc = hh->isrt[ides];
7671    if (norm) ff = hh->cn[hh->isrt[ides]];
7672    else ff = hh->cn[hh->isrt[ides]];
7673 
7674    SUMA_RETURN(ff);
7675 }
7676 
7677 /* Return the value at which you exceed a certain count.
7678    if from_top == 1 then start counting (reverse cdf) from the right
7679    else count from the left (cdf)
7680 */
SUMA_val_at_count(SUMA_HIST * hh,double count,int norm,int from_top)7681 double SUMA_val_at_count(SUMA_HIST *hh, double count, int norm, int from_top)
7682 {
7683    static char FuncName[]={"SUMA_val_at_count"};
7684    int ii=0;
7685    double val=0.0;
7686    double vacc, ith;
7687 
7688    SUMA_ENTRY;
7689 
7690    if (!hh) SUMA_RETURN(val);
7691    if (norm) count *= hh->n;
7692 
7693    if (from_top) {
7694       ii=hh->K-1; vacc=0.0;
7695       while(ii >=0 && vacc < count) { vacc += hh->c[ii]; --ii; }
7696       if (ii==hh->K-1 || ii==0) {
7697          val = hh->b[ii];
7698       } else {
7699          ith = (vacc-count)/hh->c[ii+1];
7700          val = -ith*hh->W+hh->b[ii+1];
7701       }
7702    } else {
7703       ii=0; vacc=0.0;
7704       while(ii < hh->K && vacc < count) { vacc += hh->c[ii]; ++ii; }
7705       if (ii==hh->K-1 || ii==0) {
7706          val = hh->b[ii];
7707       } else {
7708          ith = (vacc-count)/hh->c[ii-1];
7709          val = -ith*hh->W+hh->b[ii-1];
7710       }
7711    }
7712 
7713    SUMA_RETURN(val);
7714 }
7715 
SUMA_hist_info(SUMA_HIST * hh,int norm,int level)7716 char *SUMA_hist_info(SUMA_HIST *hh, int norm, int level)
7717 {
7718    static char FuncName[]={"SUMA_hist_info"};
7719    int i, mx, nc;
7720    float gscl=0.0;
7721    SUMA_STRING *SS=NULL;
7722    char *sss=NULL, *s=NULL;
7723    SUMA_Boolean LocalHead = NOPE;
7724 
7725    SUMA_ENTRY;
7726 
7727    SS = SUMA_StringAppend(NULL, NULL);
7728 
7729    if (!hh) SS = SUMA_StringAppend(SS,"NULL hh");
7730    else {
7731       mx = 0;
7732       for (i=0; i<hh->K; ++i) {
7733          if (hh->c[i]>mx) mx = hh->c[i];
7734       }
7735       if (mx > 50) {
7736          gscl = mx/50.0;
7737          mx = 50;
7738       } else gscl = 1.0;
7739 
7740       sss = (char *)SUMA_calloc(mx+2, sizeof(char));
7741       for (i=0; i<mx; ++i) sss[i]='*'; sss[i]='\0';
7742 
7743       SS = SUMA_StringAppend_va(SS,"Histog %s, %d bins of width %f,"
7744               "N_samp. = %d, N_ignored = %d, range = [%f,%f], cdfmin = %f\n",
7745                                     hh->label?hh->label:"NO LABEL",
7746                                     hh->K, hh->W,
7747                                     hh->n, hh->N_ignored,
7748                                     hh->min, hh->max, hh->cdfmin);
7749       SUMA_LH("About to freq at midrange");
7750       SS = SUMA_StringAppend_va(SS,"Freq at mid range %f is: %f\n",
7751                (hh->min+hh->max)/2.0, SUMA_hist_freq(hh,(hh->min+hh->max)/2.0));
7752       for (i=0; i<hh->K; ++i) {
7753          if (norm) {
7754             SS = SUMA_StringAppend_va(SS,"   %.5f, %.5f:", hh->b[i], hh->cn[i]);
7755          } else {
7756             SS = SUMA_StringAppend_va(SS,"   %.5f, %8d:", hh->b[i], hh->c[i]);
7757          }
7758          nc = (int)((float)hh->c[i]/gscl+0.5);
7759          sss[nc]='\0';
7760          SS = SUMA_StringAppend_va(SS,"%s\n", sss);
7761          sss[nc]='*';
7762       }
7763       SUMA_free(sss); sss=NULL;
7764    }
7765 
7766    SUMA_SS2S(SS, s);
7767    SUMA_RETURN(s);
7768 }
7769 
SUMA_dist_info(SUMA_FEAT_DIST * FD,int level)7770 char *SUMA_dist_info(SUMA_FEAT_DIST *FD, int level)
7771 {
7772    static char FuncName[]={"SUMA_dist_info"};
7773    int i, mx, nc;
7774    float gscl=0.0;
7775    SUMA_STRING *SS=NULL;
7776    char *sss=NULL, *s=NULL;
7777 
7778    SUMA_ENTRY;
7779 
7780    SS = SUMA_StringAppend(NULL, NULL);
7781 
7782    if (!FD) SS = SUMA_StringAppend(SS,"NULL dist struct!");
7783    else {
7784       SS = SUMA_StringAppend_va(SS, "Distribution %s\n", FD->label);
7785       switch (FD->tp) {
7786          case SUMA_FEAT_GAMMA:
7787             SS = SUMA_StringAppend_va(SS, "type gamma (shape %f, rate %f)\n"
7788                                           "feature scale %f, shift %f\n",
7789                                           FD->par[0], FD->par[1],
7790                                           FD->scpar[0], FD->scpar[1]);
7791             if (FD->hh) {
7792                sss = SUMA_hist_info(FD->hh, 1, 1);
7793                SS = SUMA_StringAppend_va(SS, "histogram:\n%s\n", sss);
7794                SUMA_free(sss); sss = NULL;
7795             }
7796             break;
7797          case SUMA_FEAT_NP:
7798             SS = SUMA_StringAppend(SS, "type non-parametric\n");
7799             if (FD->hh) {
7800                sss = SUMA_hist_info(FD->hh, 1, 1);
7801                SS = SUMA_StringAppend_va(SS, "%s\n", sss);
7802                SUMA_free(sss); sss = NULL;
7803             } else {
7804                SS = SUMA_StringAppend(SS,"NULL histogram!\n");
7805             }
7806             break;
7807          default:
7808             SS = SUMA_StringAppend_va(SS,"Not ready for type %d\n", FD->tp);
7809             break;
7810       }
7811    }
7812 
7813    SUMA_SS2S(SS, s);
7814 
7815    SUMA_RETURN(s);
7816 }
7817 
SUMA_dists_info(SUMA_FEAT_DISTS * FDV,int level)7818 char *SUMA_dists_info(SUMA_FEAT_DISTS *FDV, int level)
7819 {
7820    static char FuncName[]={"SUMA_dists_info"};
7821    int i, mx, nc;
7822    float gscl=0.0;
7823    SUMA_STRING *SS=NULL;
7824    char *sss=NULL, *s=NULL;
7825 
7826    SUMA_ENTRY;
7827 
7828    SS = SUMA_StringAppend(NULL, NULL);
7829 
7830    if (!FDV) SS = SUMA_StringAppend(SS,"NULL dist struct!");
7831    else {
7832       SS = SUMA_StringAppend_va(SS, "%d distributions in FDV.\n", FDV->N_FD);
7833       for (i=0; i<FDV->N_FD; ++i) {
7834          SS = SUMA_StringAppend_va(SS, "  Distribution %d/%d for %s\n",
7835                                        i, FDV->N_FD, FDV->FD[i]->label);
7836          if (level) {
7837             sss = SUMA_dist_info(FDV->FD[i],level);
7838             SS = SUMA_StringAppend_va(SS, "%s\n", sss);
7839             SUMA_free(sss); sss = NULL;
7840          }
7841       }
7842    }
7843 
7844    SUMA_SS2S(SS, s);
7845 
7846    SUMA_RETURN(s);
7847 }
7848 
SUMA_Show_hist(SUMA_HIST * hh,int norm,FILE * out)7849 void SUMA_Show_hist(SUMA_HIST *hh, int norm, FILE *out)
7850 {
7851    static char FuncName[]={"SUMA_Show_hist"};
7852    int i, mx, nc;
7853    float gscl=0.0;
7854    char  *s=NULL;
7855 
7856    SUMA_ENTRY;
7857 
7858    if (!out) out = SUMA_STDOUT;
7859 
7860    s = SUMA_hist_info(hh, norm, 1);
7861 
7862    fprintf(out, "%s\n", s);
7863 
7864    SUMA_free(s); s = NULL;
7865 
7866    SUMA_RETURNe;
7867 }
7868 
SUMA_Show_dist(SUMA_FEAT_DIST * FD,FILE * out)7869 void SUMA_Show_dist(SUMA_FEAT_DIST *FD, FILE *out)
7870 {
7871    static char FuncName[]={"SUMA_Show_dist"};
7872    int i, mx, nc;
7873    float gscl=0.0;
7874    char  *s=NULL;
7875 
7876    SUMA_ENTRY;
7877 
7878    if (!out) out = SUMA_STDOUT;
7879 
7880    s = SUMA_dist_info(FD, 1);
7881 
7882    fprintf(out, "%s\n", s);
7883 
7884    SUMA_free(s); s = NULL;
7885 
7886    SUMA_RETURNe;
7887 }
7888 
SUMA_Show_dists(SUMA_FEAT_DISTS * FDV,FILE * out,int level)7889 void SUMA_Show_dists(SUMA_FEAT_DISTS *FDV, FILE *out, int level)
7890 {
7891    static char FuncName[]={"SUMA_Show_dists"};
7892    int i, mx, nc;
7893    float gscl=0.0;
7894    char  *s=NULL;
7895 
7896    SUMA_ENTRY;
7897 
7898    if (!out) out = SUMA_STDOUT;
7899 
7900    s = SUMA_dists_info(FDV, level);
7901 
7902    fprintf(out, "%s\n", s);
7903 
7904    SUMA_free(s); s = NULL;
7905 
7906    SUMA_RETURNe;
7907 }
7908 
7909 
7910 
7911 /*!
7912    Initialize all voxels in a dset.
7913    \param aset: Dataset to initialize
7914    \param val: Vector of values, one for each sub-brick
7915                or just one value for all sub-bricks
7916    \param nval: Either 1 or DSET_NVALS(aset)
7917    \param cmask
7918    \param setsf: if 1, then set the scaling factor based on vv
7919                 for dsets that are shorts
7920    \return 0 bad, 1 good
7921 
7922 */
SUMA_InitDset(THD_3dim_dataset * aset,float * val,int nval,byte * cmask,byte setsf)7923 int SUMA_InitDset(THD_3dim_dataset  *aset, float *val, int nval,
7924                   byte *cmask, byte setsf)
7925 {
7926    static char FuncName[]={"SUMA_InitDset"};
7927    int i, k;
7928    float vv, *fv, fsc;
7929 
7930    SUMA_ENTRY;
7931 
7932    for (k=0; k<DSET_NVALS(aset); ++k) {
7933       if (!val) vv = 0.0;
7934       else if (nval > 1) vv = val[k];
7935       else vv = *val;
7936       fsc = DSET_BRICK_FACTOR(aset,k);
7937       if (fsc == 0.0) fsc = 1.0;
7938       switch (DSET_BRICK_TYPE(aset,k)) {
7939          case MRI_float:
7940             fv = (float *)DSET_ARRAY(aset,k);
7941             for (i=0; i<DSET_NVOX(aset); ++i) {
7942                if (IN_MASK(cmask,i)) {
7943                   fv[i] = vv;
7944                }
7945             }
7946             break;
7947          case MRI_short:
7948             if (setsf) {
7949                if (vv != 0.0) fsc = vv/32767.0;
7950                EDIT_BRICK_FACTOR(aset,k,fsc);
7951             }
7952             for (i=0; i<DSET_NVOX(aset); ++i) {
7953                if (IN_MASK(cmask,i)) {
7954                   PSCVAL(aset, k, i, fsc, 1.0);
7955                }
7956             }
7957             break;
7958          default:
7959             SUMA_S_Errv("Not dealing with type %d\n",
7960                         DSET_BRICK_TYPE(aset,k));
7961             SUMA_RETURN(0);
7962       }
7963    }
7964 
7965    SUMA_RETURN(1);
7966 
7967 }
7968 
7969 /*!
7970    Produce Class priors:
7971       \param cs : Class stats from which mixing fractions will be used
7972                   if no Location- or Spatial-based priors are provided
7973       \param Aset: The anatomical dset, for a grid template
7974       \param priCgA: Prior of C given A (feature)
7975       \param wA: Weight of feature priors
7976       \param priCgL: Prior of C given L (location)
7977       \param wL: Weight of Location priors
7978          wA + wL = 1.0
7979       \param priCgALLp: Pointer to dset pointer where results will be placed
7980                         upon the function's return. If *priCgALLp == NULL,
7981                         a new dset is created, otherwise dset is recycled.
7982       \param Opt: The catchall structure of options, just for debugging params.
7983       \return :0 Bad, 1 good
7984 
7985     If (priCgA and priCgL) {
7986       For all k in classes != OTHER
7987          priCgALLp[k] = (0.05+0.85*priCgA[k])^wA * (0.05+0.85*priCgL[k])^wL /
7988                            SUM(priCgALLp[k!=OTHER])
7989             (0.05+0.85*p) is to keep one prior from nulling another.
7990       For k == OTHER
7991          priCgALLp[OTHER] = priCgL[OTHER];
7992          (and readjust priCgALLp[k!=OTHER] so that SUM(priCgALLp[.]) = 1.0
7993    } else if (priCgA) {
7994       priCgALLp[k] = priCgA[k]
7995    } else if (priCgL) {
7996       priCgALLp[k] = priCgL[k]
7997    } else {
7998       priCgALLp[k] = cs->mix[k] (constant at all voxels)
7999    }
8000 
8001 */
SUMA_MergeCpriors(SUMA_CLASS_STAT * cs,byte * cmask,THD_3dim_dataset * Aset,THD_3dim_dataset * priCgA,float wA,THD_3dim_dataset * priCgL,float wL,THD_3dim_dataset ** priCgALLp,SEG_OPTS * Opt)8002 int SUMA_MergeCpriors(SUMA_CLASS_STAT *cs, byte *cmask,
8003                                       THD_3dim_dataset  *Aset,
8004                                       THD_3dim_dataset  *priCgA, float wA,
8005                                       THD_3dim_dataset  *priCgL, float wL,
8006                                       THD_3dim_dataset  **priCgALLp,
8007                                       SEG_OPTS *Opt)
8008 {
8009    static char FuncName[]={"SUMA_MergeCpriors"};
8010    float *fPCGA = NULL, *fPCGL=NULL, *fpriCgALL=NULL;
8011    int k, ijk;
8012    double sdf, df, dfA, dfL, *ggkk=NULL,*mixfrac=NULL;
8013    THD_3dim_dataset  *priCgALL = *priCgALLp;
8014 
8015    SUMA_Boolean LocalHead = NOPE;
8016 
8017    SUMA_ENTRY;
8018 
8019    /* merge priors */
8020    if (!priCgALL) {
8021       NEW_SHORTY(Aset, cs->N_label, "InitialPriors", priCgALL);
8022       *priCgALLp = priCgALL;
8023    }
8024 
8025    fpriCgALL = (float *)SUMA_calloc(cs->N_label, sizeof(float));
8026    fPCGA = (float *)SUMA_calloc(cs->N_label, sizeof(float));
8027    fPCGL = (float *)SUMA_calloc(cs->N_label, sizeof(float));
8028    ggkk   = (double*)SUMA_calloc(cs->N_label, sizeof(double));
8029 
8030    for (k=0; k<cs->N_label; ++k) EDIT_BRICK_FACTOR(priCgALL, k, 1.0/10000.0);
8031    GET_BFs(priCgALL,fpriCgALL);
8032 
8033 
8034    if (priCgA) {
8035       GET_BFs(priCgA,fPCGA);
8036    }
8037    if (priCgL) {
8038       GET_BFs(priCgL, fPCGL);
8039    }
8040    if (priCgA && priCgL) { /* have both types of priors, combine */
8041        double dfA, dfL;
8042        int UseK[cs->N_label], N_kok, uk, ko;
8043 
8044        if (wA == -1.0) {
8045          wA = 0.8; wL = 0.2;
8046        }
8047        /* find which classes can be merged */
8048        SUMA_LHv("Mixing %f priCgA & %f priCgL\n",
8049                wA, wL);
8050        if ((N_kok = SUMA_Class_k_Selector(cs,
8051             "not_classes_string", "OTHER",  UseK))<0) {
8052          SUMA_S_Err("No classes found"); SUMA_RETURN(0);
8053        }
8054        for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8055          if (IN_MASK(cmask, ijk)) {
8056             sdf = 0.0;
8057             for (uk=0; uk<N_kok; ++uk) {
8058                k = UseK[uk];
8059                GSCVAL(priCgA, k, ijk, fPCGA[k], dfA);
8060                GSCVAL(priCgL, k, ijk, fPCGL[k], dfL);
8061                /* pad dfA and dfL to guard against multiplying with 0 */
8062                dfA = 0.05+0.85*dfA;
8063                dfL = 0.05+0.85*dfL;
8064                ggkk[k] = pow(dfA,wA)*pow(dfL,wL);
8065                sdf+=ggkk[k];
8066             }
8067             for (uk=0; uk<N_kok; ++uk) {
8068                k = UseK[uk];
8069                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], (ggkk[k]/sdf));
8070                if (Opt->VoxDbg == ijk) {
8071                   GSCVAL(priCgA, k, ijk, fPCGA[k], dfA);
8072                   GSCVAL(priCgL, k, ijk, fPCGL[k], dfL);
8073                   SUMA_S_Notev("At %d %d %d:\n"
8074                                "%s, priCgA=%fx%f, priCgL=%fx%f, pC=%f \n",
8075                         Opt->VoxDbg3[0], Opt->VoxDbg3[1], Opt->VoxDbg3[2],
8076                         cs->label[k], dfA, wA, dfL, wL, ggkk[k]/sdf);
8077                }
8078             }
8079          }
8080       }
8081       ko = SUMA_Class_k_Label_Locator(cs, "OTHER");
8082       if (ko >= 0) {
8083          SUMA_S_Note("Imposing OTHER class from pCgL onto pC");
8084          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8085             if (IN_MASK(cmask, ijk)) {
8086                GSCVAL(priCgL, ko, ijk, fPCGL[ko], dfL);
8087                for (uk=0; uk<N_kok; ++uk) {
8088                   k = UseK[uk];
8089                   GSCVAL(priCgALL, k, ijk, fpriCgALL[k], df);
8090                   PSCVAL(priCgALL, k, ijk, fpriCgALL[k], (df - df*dfL));
8091                }
8092                PSCVAL(priCgALL, ko, ijk, fpriCgALL[ko],dfL);
8093             }
8094          }
8095       }
8096    } else {
8097       if (priCgA) {
8098          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8099             for (k=0; k<cs->N_label; ++k) {
8100                GSCVAL(priCgA, k, ijk, fPCGA[k], df);
8101                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], df);
8102             }
8103          }
8104       } else if (priCgL) {
8105          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8106             for (k=0; k<cs->N_label; ++k) {
8107                GSCVAL(priCgL, k, ijk, fPCGL[k], df);
8108                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], df);
8109             }
8110          }
8111       } else if ((mixfrac=SUMA_get_Stats(cs, "mix"))) {
8112          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8113             for (k=0; k<cs->N_label; ++k) {
8114                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], mixfrac[k]);
8115             }
8116          }
8117       } else if ((mixfrac=SUMA_get_Stats(cs, "mix.init"))) {
8118          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8119             for (k=0; k<cs->N_label; ++k) {
8120                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], mixfrac[k]);
8121             }
8122          }
8123       } else  {
8124          for (ijk=0; ijk<DSET_NVOX(Aset); ++ijk) {
8125             for (k=0; k<cs->N_label; ++k) {
8126                PSCVAL(priCgALL, k, ijk, fpriCgALL[k], 1.0/(double)cs->N_label);
8127             }
8128          }
8129       }
8130    }
8131 
8132    SUMA_ifree(fpriCgALL); SUMA_ifree(fPCGA); SUMA_ifree(fPCGL); SUMA_ifree(ggkk);
8133 
8134    SUMA_RETURN(1);
8135 }
8136 
8137 /*!
8138    Compare a field bias estimate to the true estimate.
8139       rat = (ideal/mean_ideal - estimate/mean_estimate) /
8140                (ideal/mean_ideal)
8141    \param gold_bias: Reference field bias
8142    \param bias: Estimate field bias
8143    \param cmask: mask region
8144    \param cmask_count: Number of vox. in mask
8145    \param thresh: Threshold for summary stat
8146    \param prat: If !NULL, this will contain the ratio
8147                of the two volumes.
8148    \param bad_count: The fraction of voxels in the mask where
8149                   rat >= thresh
8150 */
8151 
SUMA_CompareBiasDsets(THD_3dim_dataset * gold_bias,THD_3dim_dataset * bias,byte * cmask,int cmask_count,float thresh,THD_3dim_dataset * prat)8152 double SUMA_CompareBiasDsets(THD_3dim_dataset *gold_bias, THD_3dim_dataset *bias,
8153                          byte *cmask, int cmask_count,
8154                          float thresh, THD_3dim_dataset *prat )
8155 {
8156    static char FuncName[]={"SUMA_CompareBiasDsets"};
8157    float fprat = 1.0/3200, fgi, fgd;
8158    double md, mi, rat, ai, ad, bad_count;
8159    int ii;
8160    SUMA_ENTRY;
8161 
8162    if (!gold_bias || !bias) {
8163       SUMA_RETURN(-1);
8164    }
8165 
8166    fgi = DSET_BRICK_FACTOR(gold_bias, 0);
8167    fgd = DSET_BRICK_FACTOR(bias, 0);
8168    md = 0.0; mi = 0.0;
8169    for (ii=0; ii<DSET_NVOX(bias); ++ii) {
8170       if ( IN_MASK(cmask, ii) ) {
8171          GVAL(gold_bias, 0, ii, ai);
8172          GVAL(bias, 0, ii, ad);
8173          md += ad;
8174          mi += ai;
8175       }
8176    }
8177    md *=  ((double)fgd/(double)cmask_count);
8178    mi *=  ((double)fgi/(double)cmask_count);
8179 
8180    bad_count = 0.0;
8181    for (ii=0; ii<DSET_NVOX(bias); ++ii) {
8182       if ( IN_MASK(cmask, ii) ) {
8183          GSCVAL(gold_bias, 0, ii, fgi, ai);
8184          ai /= mi;
8185          GSCVAL(bias, 0, ii, fgd, ad);
8186          ad /= md;
8187          rat = (ai-ad)/ai;
8188          if (prat) {
8189             PSCVAL(prat, 0, ii, fprat, rat);
8190          }
8191          if (SUMA_ABS(rat) >= thresh) ++bad_count;
8192       }
8193    }
8194 
8195    bad_count = bad_count/(double)cmask_count*100.0;
8196    if (prat) {
8197       EDIT_BRICK_FACTOR(prat, 0 , fprat);
8198    }
8199 
8200    SUMA_RETURN(bad_count);
8201 }
8202 
8203 /*!
8204    Compute the Dice coefficient between base and segmentation volumes
8205    \param base: Gold std segmentaion
8206    \param seg: segmentation
8207    \param cmask: Restrict all to cmask
8208    \param mask_by_base: If (1) then exclude locations where base == 0,
8209                         even if this location is in cmask
8210    \param cs: The Dice coefficient is stored as stat "DICE" in cs
8211 Options only used when Seg's classes are split into sub-classes of
8212    classes in base. It is assumed that cs contains stats for the
8213    all the split classes.
8214 */
SUMA_CompareSegDsets(THD_3dim_dataset * base,THD_3dim_dataset * seg,byte * cmask,byte mask_by_base,SUMA_CLASS_STAT * cs)8215 int SUMA_CompareSegDsets(THD_3dim_dataset *base, THD_3dim_dataset *seg,
8216                          byte *cmask, byte mask_by_base,
8217                          SUMA_CLASS_STAT *cs)
8218 {
8219    static char FuncName[]={"SUMA_CompareSegDsets"};
8220    int ii=0, kk=0, nbb, nss, nmatch, gk=0;
8221    short *bb=NULL, *ss=NULL, *ssc=NULL;
8222    float bf = 1.0, sf=1.0;
8223    double *sp2grp=NULL;
8224    SUMA_ENTRY;
8225 
8226    if (!base) {
8227       for (kk=0; kk<cs->N_label; ++kk) {
8228          SUMA_set_Stat(cs, cs->label[kk], "DICE", 0.0);
8229       }
8230    }
8231 
8232    sf = DSET_BRICK_FACTOR(seg,0); if (sf == 0.0f) sf = 1.0;
8233    ss = (short *)DSET_ARRAY(seg,0);
8234 
8235    sp2grp = SUMA_get_Stats(cs, "GRkey");
8236    if (sp2grp) { /* have split classes, merge them */
8237       ssc = (short *)SUMA_calloc(sizeof(short), DSET_NVOX(seg));
8238       for (kk=0; kk<cs->N_label; ++kk) {
8239          for (ii=0; ii<DSET_NVOX(seg); ++ii) {
8240             if (IN_MASK(cmask,ii) && ss[ii]*(int)sf==cs->keys[kk]) {
8241                ssc[ii] = (int)sp2grp[kk];
8242             }
8243          }
8244       }
8245       sf = 1.0;
8246       ss = ssc;
8247    }
8248 
8249    bf = DSET_BRICK_FACTOR(base,0); if (bf == 0.0f) bf = 1.0;
8250    bb = (short *)DSET_ARRAY(base,0);
8251    for (kk=0; kk<cs->N_label; ++kk) {
8252       nmatch = 0; nss=0; nbb=0;
8253       if (sp2grp) gk = (int)sp2grp[kk];
8254          else gk = cs->keys[kk];
8255       for (ii=0; ii<DSET_NVOX(base); ++ii) {
8256          if ( IN_MASK(cmask, ii) &&
8257               (!mask_by_base || bb[ii]) ) {
8258             if ((ss[ii]*(int)sf) == gk) ++nss;
8259             if ((bb[ii]*(int)bf) == gk) {
8260                ++nbb;
8261                if (bb[ii] == ss[ii]) ++nmatch;
8262             }
8263          }
8264       }
8265       SUMA_set_Stat(cs, cs->label[kk], "DICE", (double)(nmatch*2)/(nss+nbb));
8266    }
8267 
8268    if (ssc) SUMA_free(ssc); ssc=NULL;
8269    SUMA_RETURN(0);
8270 }
8271 
8272 
8273 /*
8274    A convenience function to create convex hull
8275    from a dataset and a threshold
8276 */
SUMA_Dset_ConvexHull(THD_3dim_dataset * dset,int isb,float th,byte * umask)8277 SUMA_SurfaceObject *SUMA_Dset_ConvexHull(THD_3dim_dataset *dset, int isb,
8278                                         float th, byte *umask)
8279 {
8280    static char FuncName[]={"SUMA_Dset_ConvexHull"};
8281    SUMA_SurfaceObject *SO=NULL;
8282    int ii, i, j, k, npt=0, nxx, nyy, nzz, nxyz, *ijk=NULL, nf;
8283    float sbf = 0.0, *xyz=NULL;
8284    byte *mask=NULL;
8285    THD_fvec3 fv, iv;
8286    SUMA_Boolean LocalHead = NOPE;
8287 
8288    SUMA_ENTRY;
8289 
8290    if (!dset) SUMA_RETURN(SO);
8291 
8292    nxx = (DSET_NX(dset));
8293    nyy = (DSET_NY(dset));
8294    nzz = (DSET_NZ(dset));
8295    nxyz = nxx*nyy*nzz;
8296 
8297    if (!umask) {
8298       if (!(mask = (byte *)SUMA_malloc(nxyz*sizeof(byte)))){
8299          SUMA_S_Err("Failed to allocate"); SUMA_RETURN(NULL);
8300       }
8301       memset(mask, 1, nxyz*sizeof(byte));
8302    } else {
8303       mask = umask;
8304    }
8305 
8306    if (th != 0.0) {
8307       sbf = DSET_BRICK_FACTOR(dset, isb);
8308       if (sbf == 0.0) sbf = 1.0;
8309       SUMA_LHv("Threshold %f, with scaling = %f\n", th, th/sbf);
8310       th /= sbf;
8311       switch( DSET_BRICK_TYPE(dset,isb) ){
8312          default:
8313             SUMA_S_Errv("Unsupported sub-brick datum %d\n",
8314                         DSET_BRICK_TYPE(dset,isb)) ;
8315             SUMA_RETURN(NULL) ;
8316          case MRI_float:{
8317             float *pp = (float *) DSET_ARRAY(dset,0) ;
8318             for( ii=0 ; ii < nxyz ; ii++ ) { if (pp[ii] < th) mask[ii] = 0; }
8319             }
8320             break ;
8321          case MRI_short:{
8322             short *pp = (short *) DSET_ARRAY(dset,0) ;
8323             for( ii=0 ; ii < nxyz ; ii++ ) { if (pp[ii] < th) mask[ii] = 0; }
8324             }
8325             break ;
8326          case MRI_byte:{
8327             byte *pp = (byte *) DSET_ARRAY(dset,0) ;
8328             for( ii=0 ; ii < nxyz ; ii++ ) { if (pp[ii] < th) mask[ii] = 0; }
8329             }
8330             break ;
8331       }
8332    }
8333 
8334    /* How many voxels? */
8335    npt = 0;
8336    for ( ii=0 ; ii < nxyz ; ii++ ) { if (mask[ii]) ++npt; }
8337 
8338    if (!(xyz = (float *)SUMA_malloc(3*npt*sizeof(float)))) {
8339       SUMA_S_Err("Failed to allocate"); SUMA_RETURN(NULL);
8340    }
8341 
8342    ii = 0; npt = 0;
8343    for(  k = 0 ; k < nzz ; k++ ) {
8344       for(  j = 0 ; j < nyy ; j++ ) {
8345          for(  i = 0 ; i < nxx ; i++ ) {
8346             if (mask[ii++]) {
8347                fv.xyz[0] = DSET_XORG(dset) + i * DSET_DX(dset);
8348                fv.xyz[1] = DSET_YORG(dset) + j * DSET_DY(dset);
8349                fv.xyz[2] = DSET_ZORG(dset) + k * DSET_DZ(dset);
8350                /* change mm to RAI coords */
8351 		         iv = SUMA_THD_3dmm_to_dicomm( dset->daxes->xxorient,
8352                                              dset->daxes->yyorient,
8353                                              dset->daxes->zzorient,
8354                                              fv );
8355                xyz[3*npt  ] = iv.xyz[0];
8356                xyz[3*npt+1] = iv.xyz[1];
8357                xyz[3*npt+2] = iv.xyz[2];
8358                npt++;
8359             }
8360          }
8361       }
8362    }
8363    if (mask != umask) SUMA_free(mask); mask = NULL;
8364    SUMA_LHv("Have %d/%d voxels in mask for hull\n", npt, nxyz);
8365    if (! (nf = SUMA_qhull_wrap(npt, xyz, &ijk, 1, NULL)) ) {
8366       SUMA_S_Err("Failed in SUMA_qhull_wrap");
8367       SUMA_free(xyz); SUMA_RETURN(NULL);
8368    }
8369 
8370    if (!(SO = SUMA_Patch2Surf(xyz, npt, ijk, nf, 3))) {
8371       SUMA_S_Err("Failed in SUMA_Patch2Surf");
8372       SUMA_free(xyz); SUMA_RETURN(NULL);
8373    }
8374 
8375    SUMA_free(ijk); SUMA_free(xyz);
8376 
8377    SUMA_RETURN(SO);
8378 }
8379 
8380 /*!
8381    A function to create a convex hull of the head.
8382    hullvolthr is the expected hull volume in liters.
8383               It is used to select a threshold to mask unwanted
8384               voxels. A generous value of 1.0 works well in
8385               most cases. However for datasets that have too
8386               much junk in them (lots of neck coverage, too much
8387               ghosting, etc.) you need to lower that value down
8388               to 0.75, or even 0.35 liters.
8389               You can set hullvolthr to 0 and let the function
8390               choose a good guess at the expense of time, however
8391               when the result of this function is combined with
8392               SUMA_ShrinkSkullHull, the result is about the same
8393               regardless of how good or bad the initial hull is.
8394 */
SUMA_ExtractHead_Hull(THD_3dim_dataset * iset,float hullvolthr,SUMA_HIST ** uhh,SUMA_COMM_STRUCT * cs)8395 SUMA_SurfaceObject *SUMA_ExtractHead_Hull(THD_3dim_dataset *iset,
8396                                      float hullvolthr, SUMA_HIST **uhh,
8397                                      SUMA_COMM_STRUCT *cs)
8398 {
8399    static char FuncName[]={"SUMA_ExtractHead_Hull"};
8400    SUMA_HIST *hh=NULL;
8401    float voxvol = 0.0, volthr[12], voxthr = 0.0;
8402    double hvol[12], d1[12], d2[12];
8403    int iv=0, ivolsel;
8404    SUMA_SurfaceObject *SOv[12], *SO=NULL;
8405    SUMA_Boolean LocalHead = NOPE;
8406 
8407    SUMA_ENTRY;
8408 
8409    if (!iset) SUMA_RETURN(SO);
8410    for (iv=0; iv<12; ++iv) SOv[iv]=NULL;
8411 
8412    voxvol = SUMA_ABS(DSET_DX(iset)*DSET_DY(iset)*DSET_DZ(iset));
8413 
8414    if (!(hh = SUMA_dset_hist( iset, 0, NULL, DSET_PREFIX(iset), NULL,
8415                               0, 0.0, NULL))) {
8416       SUMA_S_Errv("Failed to create histogram from %s\n",
8417                   DSET_PREFIX(iset));
8418       SUMA_RETURN(SO);
8419    }
8420 
8421    if (hullvolthr <= 0.0) {
8422       for (iv=0; iv<11; ++iv) {
8423          volthr[iv] = (1.0-0.9*(iv/10.0))*1.0e6; /* volume in micro liters */
8424             /* get the value above which there remains volthr[iv]/voxvol
8425                voxels */
8426          voxthr = SUMA_val_at_count(hh, volthr[iv]/voxvol, 0, 1);
8427             /* create the convex hull */
8428          SOv[iv] = SUMA_Dset_ConvexHull(iset, 0, voxthr, NULL);
8429             /* What is the volume of this hull ? */
8430          hvol[iv] = SUMA_Mesh_Volume(SOv[iv], NULL, -1, 0, NULL);
8431          SUMA_LHv("Hull volume (thr=%f) at %f liters (count=%f) = %f\n",
8432                   voxthr, volthr[iv], volthr[iv]/voxvol, hvol[iv]);
8433       }
8434 
8435       /* look at change in volume versus change in threshold,
8436         Pick the highest volume for which there is stability
8437         in hull volume change with mask volume change  */
8438       if (LocalHead) {
8439          SUMA_S_Note("Mask Volume (in liters)");
8440          for (iv=0; iv<11; ++iv) {
8441             fprintf(SUMA_STDOUT,"%.3f ",
8442                      volthr[iv]/1.0e6);
8443          } fprintf(SUMA_STDOUT,"\n");
8444          SUMA_S_Note("Hull Volume");
8445          for (iv=0; iv<11; ++iv) {
8446             fprintf(SUMA_STDOUT,"%f ",
8447                      hvol[iv]);
8448          } fprintf(SUMA_STDOUT,"\n");
8449 
8450          SUMA_S_Note("Delta(Hull Volume) / (Hull Volume) * 100");
8451          /* The mean and variance of this measure can tell you
8452          something about the volume you have.
8453          Too variable means too much high intensity noise. Good
8454          head only volumes have means close to 0 */
8455          for (iv=1; iv<11; ++iv) {
8456             fprintf(SUMA_STDOUT,"%f ",
8457                      200.0*(hvol[iv]-hvol[iv-1])/(hvol[iv]+hvol[iv-1]));
8458          } fprintf(SUMA_STDOUT,"\n");
8459       }
8460       for (iv=1; iv < 11; ++iv) {
8461          d1[iv-1] = (hvol[iv]-hvol[iv-1]);
8462       }
8463       for (iv=1; iv < 11-1; ++iv) {
8464          d2[iv-1] = ((d1[iv]-d1[iv-1])/(volthr[iv]-volthr[iv-1]));
8465       }
8466       ivolsel = -1;
8467       if (ivolsel < 0) { /* be demanding */
8468          SUMA_LH("Option 1\n");
8469          for (iv=0; iv < 7 && ivolsel < 0; ++iv) {
8470             if (SUMA_ABS(d2[iv]) < 0.5 &&
8471                 SUMA_ABS(d2[iv+1]) < 0.5 &&
8472                 SUMA_ABS(d2[iv+2]) < 0.5) {
8473                ivolsel = iv;
8474             }
8475          }
8476       }
8477       if (ivolsel < 0) { /* try again */
8478          SUMA_LH("Option 2\n");
8479          for (iv=0; iv < 8 && ivolsel < 0; ++iv) {
8480             if (SUMA_ABS(d2[iv]) < 0.5 &&
8481                 SUMA_ABS(d2[iv+1]) < 0.5 ) {
8482                ivolsel = iv;
8483             }
8484          }
8485       }
8486       if (ivolsel < 0) { /* anything */
8487          SUMA_LH("Option 3\n");
8488          for (iv=0; iv < 9 && ivolsel < 0; ++iv) {
8489             if (SUMA_ABS(d2[iv]) < 0.5 ) {
8490                ivolsel = iv;
8491             }
8492          }
8493       }
8494       if (ivolsel < 0) ivolsel = 0;
8495       SUMA_LHv("Selected hull at mask volume threshold of %f liters\n",
8496                volthr[ivolsel]/1.0e6);
8497       SO = SOv[ivolsel]; SOv[ivolsel]=NULL;
8498       for (iv=0; iv < 11; ++iv) {
8499          if (SOv[iv]) SUMA_Free_Surface_Object(SOv[iv]); SOv[iv]=NULL;
8500       }
8501    } else {
8502       iv = 0;
8503       volthr[iv] = hullvolthr *1.0e6; /* volume in micro liters */
8504       voxthr = SUMA_val_at_count(hh, volthr[iv]/voxvol, 0, 1);
8505       SUMA_LHv("Extract Head Hull at voxthr %f\n", voxthr);
8506       SO = SUMA_Dset_ConvexHull(iset, 0, voxthr, NULL);
8507    }
8508 
8509    if (uhh) {  *uhh = hh; hh = NULL; }
8510    if (hh) {
8511       SUMA_Free_hist(hh); hh=NULL;
8512    }
8513    SUMA_RETURN(SO);
8514 }
8515 
8516 /*!
8517    Shrink hull of skull so that the surface lies
8518    on bright voxels that at least exceed the threshold.
8519 
8520    This function can use a lot of cleanup.
8521 */
SUMA_ShrinkSkullHull2Mask(SUMA_SurfaceObject * SO,THD_3dim_dataset * iset,float thr,int smooth_final,float * shish_length_mm,int zero_attractor,SUMA_COMM_STRUCT * cs)8522 SUMA_Boolean SUMA_ShrinkSkullHull2Mask(SUMA_SurfaceObject *SO,
8523                              THD_3dim_dataset *iset, float thr,
8524                              int smooth_final, float *shish_length_mm,
8525                              int zero_attractor, SUMA_COMM_STRUCT *cs)
8526 {
8527    static char FuncName[]={"SUMA_ShrinkSkullHull2Mask"};
8528    char sbuf[256]={""};
8529    byte *mask=NULL;
8530    int   in=0, vxi_bot[30], vxi_top[30], iter, N_movers,
8531          ndbg=SUMA_getBrainWrap_NodeDbg(), nn,N_um,
8532          itermax1 = 50;
8533    float *fvec=NULL, *xyz, *dir, P2[2][3], travstep, shs_bot[60], shs_top[60];
8534    float rng_bot[2], rng_top[2], rdist_bot[2], rdist_top[2], avg[3], nodeval,
8535          area=0.0, larea=0.0, ftr=0.0, darea=0.0;
8536    float  *fnz=NULL, *alt=NULL;
8537    float maxtop, maxbot;
8538    int   nmaxtop, nmaxbot, Max_nn, nsteps[2];
8539    float dirZ[3], *dots=NULL, U3[3], Un, fv2[2];
8540    THD_3dim_dataset *inset=NULL;
8541    SUMA_Boolean stop = NOPE;
8542    SUMA_Boolean LocalHead = NOPE;
8543 
8544    SUMA_ENTRY;
8545 
8546    if (thr == 0.0f) thr = 1.0;
8547 
8548    SUMA_LHv("Begin shrinkage, thr=%f, ndbg = %d\n", thr, ndbg);
8549 
8550    travstep = SUMA_ABS(DSET_DX(iset));
8551    if (travstep > SUMA_ABS(DSET_DY(iset))) travstep = SUMA_ABS(DSET_DY(iset));
8552    if (travstep > SUMA_ABS(DSET_DZ(iset))) travstep = SUMA_ABS(DSET_DZ(iset));
8553 
8554    if (!shish_length_mm) {
8555       shish_length_mm =  (float *)fv2;
8556       shish_length_mm[0] = 11*travstep;
8557       shish_length_mm[1] = 2*travstep;
8558    } else {
8559       if (shish_length_mm[0]/travstep > 59) {
8560          SUMA_S_Err("Undershish distance (%f) exceeds static allocation limit.\n"
8561                     "Complain to author.", shish_length_mm[0]);
8562          SUMA_RETURN(NOPE);
8563       }
8564       if (shish_length_mm[1]/travstep > 59) {
8565          SUMA_S_Err("Overshish distance (%f) exceeds static allocation limit.\n"
8566                     "Complain to author.", shish_length_mm[1]);
8567          SUMA_RETURN(NOPE);
8568       }
8569    }
8570    nsteps[0] = (int)(shish_length_mm[0]/travstep);
8571    nsteps[1] = (int)(shish_length_mm[1]/travstep);
8572 
8573    if (!(mask = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node))) {
8574       SUMA_S_Crit("Failed to allocate");
8575       SUMA_RETURN(NOPE);
8576    }
8577 
8578    /* For a clean, bust like button, anchor bottom nodes */
8579    /* For now, the decision is solely based on the normals
8580       being parallel to the Z direction.
8581       Perhaps I should add a depth criterion, but that
8582       is not necessary it seems */
8583       dirZ[0]=0.0; dirZ[1]=0.0; dirZ[2]=1.0;
8584       dots = NULL;
8585       if (!(SUMA_DotNormals(SO, dirZ, &dots))) {
8586          SUMA_S_Err("Failed to get dots");
8587       } else {
8588          if (LocalHead) {
8589             SUMA_WRITE_ARRAY_1D(dots, SO->N_Node, 1, "DOTS.1D.dset");
8590             THD_force_ok_overwrite(1) ;
8591             sprintf(sbuf,"shrink.init");
8592             SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
8593                                  SUMA_GIFTI, SUMA_ASCII, NULL);
8594          }
8595       }
8596 
8597 
8598    stop = NOPE;
8599    N_movers = 0; iter=0; Max_nn = 3;
8600    while (!stop) {
8601       N_movers = 0;
8602       memset(mask, 1, sizeof(byte)*SO->N_Node);
8603       /* Keep bottom nodes fixed. (consider recomputing dots?)*/
8604       if (SO->normdir < 0) {
8605          for (in=0; in<SO->N_Node; ++in) {
8606             if (dots[in]>0.8) mask[in]=0;
8607          }
8608       } else {
8609          for (in=0; in<SO->N_Node; ++in) {
8610             if (dots[in]<-0.8) mask[in]=0;
8611          }
8612       }
8613       for (in=0; in<SO->N_Node; ++in) {
8614          if (!mask[in]) { /* skip it, masked by being bottom node */
8615             if (in == ndbg) {
8616                SUMA_S_Note("Node %d anchored", in);
8617             }
8618             continue;
8619          }
8620          xyz = SO->NodeList+3*in;
8621          dir = SO->NodeNormList+3*in;
8622          SUMA_Find_IminImax_2(xyz, dir,
8623                             iset, &fvec, travstep,
8624                             shish_length_mm[0], shish_length_mm[1],
8625                             0.5*thr, in==ndbg?1:0,
8626                             rng_bot, rdist_bot,
8627                             rng_top, rdist_top,
8628                             avg,
8629                             shs_bot, shs_top,
8630                             vxi_bot, vxi_top);
8631          nodeval = shs_bot[0];
8632          if (in==ndbg || LocalHead) SUMA_S_Note("Node %d, %f, thr %f\n",
8633                                                 in, nodeval, thr);
8634          if (nodeval >= thr) { /* we're OK, minor adjustment */
8635             mask[in] = 0; /* anchor node, outside smoothing mask*/
8636             if (nodeval <= rng_top[1]) { /* higher val above, move up one step */
8637                if (in == ndbg) { SUMA_S_Note("tiny nudge up\n"); }
8638                memset(P2,0,6*sizeof(float));
8639                SUMA_POINT_AT_DISTANCE(dir, xyz, travstep, P2);
8640                xyz[0] = P2[0][0]; xyz[1] = P2[0][1]; xyz[2] = P2[0][2];
8641             }
8642          } else {
8643             {
8644                if (in == ndbg || LocalHead) {
8645                         SUMA_S_Note(
8646                            "Must look down for %d\n", in); }
8647                maxbot = shs_bot[0]; nmaxbot = 0;
8648                for (nn=1; nn<nsteps[0] && vxi_bot[nn]>=0; ++nn) {
8649                   if (shs_bot[nn] > maxbot) {
8650                      nmaxbot = nn; maxbot = shs_bot[nn];
8651                   }
8652                }
8653                /* look down for better option */
8654                if (nodeval < maxbot || nodeval == 0) {
8655                   { /* Go down to the 1st voxel meeting threshold
8656                               and a good edge */
8657                      if (in == ndbg || LocalHead) {
8658                         SUMA_S_Notev(
8659                            "Looking down nodeval %f\n",
8660                            nodeval ); }
8661                      nn = 0;
8662                      while (nn<nsteps[0] && (shs_bot[nn]<thr &&
8663                                              vxi_bot[nn]>=0 )) {
8664                         ++nn;
8665                      }
8666                      if (shs_bot[nn] >= thr) {
8667                         if (in == ndbg|| LocalHead){
8668                            SUMA_S_Notev("Going down %d steps to edge+anchor\n",
8669                                        nn);}
8670                         nn = SUMA_MIN_PAIR(nn,Max_nn);/* slowly, avoid folding */
8671                         ftr = travstep*nn;
8672                         xyz[0] -= ftr*dir[0];
8673                         xyz[1] -= ftr*dir[1];
8674                         xyz[2] -= ftr*dir[2];
8675                         if (shs_bot[nn]>= thr)
8676                            mask[in]=0;
8677                      } else { /* keep going if sitting on no value */
8678                         if (nodeval < 0.1*thr) {
8679                            if (maxbot > nodeval || nodeval == 0) {
8680                               if (in == ndbg){
8681                                  SUMA_S_Note("Still want to go down");
8682                               }
8683                               nn = nmaxbot;
8684                               if (!nn && zero_attractor) {
8685                                  nn = 1; /* If too far in space and nothing
8686                                                   is found nmaxbot can be 0, so
8687                                                   keep going */
8688                                                /* slowly, avoid folding*/
8689                               }
8690                               nn = SUMA_MIN_PAIR(nn,Max_nn);
8691                               if (nn) {
8692                                  ftr = travstep*nn;
8693                                  if (in == ndbg){
8694                                     SUMA_S_Note(
8695                                        "Going down max from %f %f %f to\n"
8696                                        "                    %f %f %f\n",
8697                                        xyz[0], xyz[1], xyz[2],
8698                                        xyz[0] -ftr*dir[0],
8699                                        xyz[1] -ftr*dir[1],
8700                                        xyz[2] -ftr*dir[2]        );}
8701                                  xyz[0] -= ftr*dir[0];
8702                                  xyz[1] -= ftr*dir[1];
8703                                  xyz[2] -= ftr*dir[2];
8704                               }
8705                            }
8706                            ++N_movers;
8707                        }
8708                      }
8709                   }
8710                }
8711             }
8712          }
8713       }
8714 
8715       SUMA_LHv("Smoothing round %d, surface \n",
8716                iter);
8717       /* Make sure no one node is an anchor holdout */
8718       for (in=0; in<SO->N_Node; ++in) {
8719          if (mask[in] == 0) { /* an anchored node */
8720             N_um = 0; /* number of unanchored neighbors */
8721             for (nn=0; nn<SO->FN->N_Neighb[in]; ++nn) {
8722                if (mask[SO->FN->FirstNeighb[in][nn]]) ++N_um;
8723             }
8724             if ((float)N_um/SO->FN->N_Neighb[in] > 0.75) {
8725                mask[in]=1;
8726                if (LocalHead || in == ndbg) {
8727                   SUMA_LHv("Node %d was anchored but now released %f\n",
8728                            in, (float)N_um/SO->FN->N_Neighb[in]);
8729                }
8730             }
8731          }
8732       }
8733 
8734 
8735       /* Are we making a difference in this world? */
8736       larea=area;
8737       area=SUMA_Mesh_Area(SO, NULL, -1);
8738       darea = (area-larea)/area*100.0;
8739 
8740       /* write it out for debugging */
8741       if (LocalHead) {
8742          SUMA_LHv("Iteration %d, N_movers = %d, area = %f (Darea=%f%%)\n",
8743                iter, N_movers, area, darea);
8744          THD_force_ok_overwrite(1) ;
8745          sprintf(sbuf,"shrink.%03d",iter);
8746          SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
8747                                  SUMA_GIFTI, SUMA_ASCII, NULL);
8748       } else if (ndbg >= 0) {
8749         SUMA_S_Note("Iteration %d, N_movers = %d (%f), area = %f (Darea=%f%%)\n",
8750                iter, N_movers, (float)N_movers/SO->N_Node, area, darea);
8751       }
8752       ++iter;
8753       if (iter > itermax1 || ( SUMA_ABS(darea) < 0.005 &&
8754                                (float)N_movers/SO->N_Node < 0.01 ) ) stop = YUP;
8755       if (!stop && SUMA_ABS(darea) < 0.01) {
8756          /* A quick smoothing with anchors in place */
8757          SUMA_NN_GeomSmooth_SO(SO, mask, 0, 10);
8758       } else {
8759          /* A Taubin smooth */
8760          if (!stop || (stop && smooth_final))
8761             SUMA_Taubin_Smooth_SO(SO, SUMA_EQUAL, 0.1, NULL, 0, 20);
8762       }
8763       if (cs && cs->talk_suma && cs->Send) {
8764          if (!SUMA_SendToSuma (SO, cs, (void *)SO->NodeList,
8765                                SUMA_NODE_XYZ, 1)) {
8766             SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
8767                          "Communication halted.");
8768          }
8769       }
8770    }
8771 
8772    if (LocalHead) {
8773       SUMA_LHv("End of iterations N_movers = %d, area = %f\n",
8774             N_movers, SUMA_Mesh_Area(SO, NULL, -1));
8775       THD_force_ok_overwrite(1) ;
8776       SUMA_Save_Surface_Object_Wrap("shrink", NULL, SO,
8777                               SUMA_GIFTI, SUMA_ASCII, NULL);
8778    }
8779 
8780    if (iter >= itermax1) {
8781       SUMA_LH("Convergence criterion not reached. Darea=%f. Check results.",
8782                   darea);
8783    }
8784 
8785 
8786    if (dots) SUMA_free(dots); dots = NULL;
8787    if (mask) free(mask); mask = NULL;
8788    if (fvec) free(fvec); fvec = NULL;
8789    if (inset) DSET_delete(inset); inset=NULL;
8790    SUMA_RETURN(YUP);
8791 }
8792 
8793 /*!
8794    Shrink hull of skull so that the surface lies
8795    on bright voxels that at least exceed the threshold.
8796    It is not attracted to the edge of the skull per se,
8797    but brighter voxels outside the current surface would
8798    attract the surface towards them.
8799 */
SUMA_ShrinkSkullHull(SUMA_SurfaceObject * SO,THD_3dim_dataset * iset,float thr,int use_rad_stat,SUMA_COMM_STRUCT * cs)8800 SUMA_Boolean SUMA_ShrinkSkullHull(SUMA_SurfaceObject *SO,
8801                              THD_3dim_dataset *iset, float thr,
8802                              int use_rad_stat,
8803                              SUMA_COMM_STRUCT *cs)
8804 {
8805    static char FuncName[]={"SUMA_ShrinkSkullHull"};
8806    char sbuf[256]={""};
8807    byte *mask=NULL;
8808    int   in=0, vxi_bot[30], vxi_top[30], iter, N_movers,
8809          ndbg=SUMA_getBrainWrap_NodeDbg(), nn,N_um,
8810          itermax1 = 50, itermax2 = 10;
8811    float *fvec=NULL, *xyz, *dir, P2[2][3], travstep, shs_bot[30], shs_top[30];
8812    float rng_bot[2], rng_top[2], rdist_bot[2], rdist_top[2], avg[3], nodeval,
8813          area=0.0, larea=0.0, ftr=0.0, darea=0.0;
8814    float *fedges=NULL, edge_thr=0.0, *fnz=NULL, *inedges=NULL, inedge_thr=0.0,
8815          *alt=NULL;
8816    float maxetop, maxebot, maxtop, maxbot, okethr;
8817    int maxentop,maxenbot, nmaxtop, nmaxbot;
8818    float dirZ[3], *dots=NULL, *curedge=NULL, curemean, curestd, U3[3], Un;
8819    THD_3dim_dataset *inset=NULL;
8820    SUMA_Boolean stop = NOPE;
8821    SUMA_Boolean LocalHead = NOPE;
8822 
8823    SUMA_ENTRY;
8824 
8825    SUMA_LHv("Begin shrinkage, thr=%f\n", thr);
8826 
8827 
8828    /* get the edges on the input set */
8829    if (!DSET_ARRAY(iset,0)) {
8830       SUMA_S_Err("Very strange, pointer lost");
8831       exit(1);
8832    }
8833 
8834 
8835    SUMA_LHv("Getting edges %p %p\n", iset, DSET_ARRAY(iset, 0));
8836    fedges = (float *)SUMA_calloc(DSET_NVOX(iset),  sizeof(float));
8837    if (!SUMA_3dedge3(iset, fedges, NULL)){
8838       SUMA_S_Err("Failed to get edges");
8839       SUMA_free(fedges); fedges = NULL;
8840    }
8841 
8842    if (!DSET_ARRAY(iset,0)) {
8843       SUMA_S_Err("Very strange, pointer lost after edge");
8844       exit(1);
8845    }
8846 
8847    /* non-zero edges */
8848    for (in=0, N_um=0; in<DSET_NVOX(iset); ++in) {
8849       if (fedges[in] > 0.0) ++N_um;
8850    }
8851    fnz = (float*)SUMA_calloc(N_um, sizeof(float));
8852    for (in=0, N_um=0; in<DSET_NVOX(iset); ++in) {
8853       if (fedges[in] > 0.0) fnz[N_um++]=fedges[in];
8854    }
8855    qsort(fnz, N_um, sizeof(float),
8856          (int(*) (const void *, const void *)) SUMA_compare_float);
8857    /* get the median */
8858    edge_thr = fnz[(int)(N_um/2)];
8859    SUMA_free(fnz); fnz=NULL;
8860    SUMA_LHv("Edge threshold of %f\n", edge_thr);
8861 
8862    travstep = SUMA_ABS(DSET_DX(iset));
8863    if (travstep > SUMA_ABS(DSET_DY(iset))) travstep = SUMA_ABS(DSET_DY(iset));
8864    if (travstep > SUMA_ABS(DSET_DZ(iset))) travstep = SUMA_ABS(DSET_DZ(iset));
8865    if (!(mask = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node))) {
8866       SUMA_S_Crit("Failed to allocate");
8867       SUMA_RETURN(NOPE);
8868    }
8869 
8870    /* For a clean, bust like button, anchor bottom nodes */
8871    /* For now, the decision is solely based on the normals
8872       being parallel to the Z direction.
8873       Perhaps I should add a depth criterion, but that
8874       is not necessary it seems */
8875       dirZ[0]=0.0; dirZ[1]=0.0; dirZ[2]=1.0;
8876       dots = NULL;
8877       if (!(SUMA_DotNormals(SO, dirZ, &dots))) {
8878          SUMA_S_Err("Failed to get dots");
8879       } else {
8880          if (LocalHead) SUMA_WRITE_ARRAY_1D(dots, SO->N_Node, 1, "DOTS.1D.dset");
8881       }
8882 
8883 
8884    /* Get distributions of values around the surface */
8885    curedge = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
8886    for (in=0; in<SO->N_Node; ++in) {
8887       xyz = SO->NodeList+3*in;
8888       dir = SO->NodeNormList+3*in;
8889       SUMA_Find_IminImax_2(xyz, dir,
8890                          iset, &fvec, travstep, 1*travstep, 1*travstep,
8891                          0.5*thr, (LocalHead && in==ndbg)?1:0,
8892                          rng_bot, rdist_bot,
8893                          rng_top, rdist_top,
8894                          avg,
8895                          shs_bot, shs_top,
8896                          vxi_bot, vxi_top);
8897       curedge[in] = SUMA_MAX_PAIR(fedges[vxi_bot[1]],fedges[vxi_bot[0]]);
8898       curedge[in] = SUMA_MAX_PAIR(curedge[in], fedges[vxi_top[1]]);
8899    }
8900    SUMA_MEAN_STD_VEC(curedge,SO->N_Node,curemean, curestd, 1);
8901    SUMA_LHv("About to loop, surface edge mean=%f, std=%f", curemean, curestd);
8902    stop = NOPE;
8903    N_movers = 0; iter=0;
8904    while (!stop) {
8905       N_movers = 0;
8906       memset(mask, 1, sizeof(byte)*SO->N_Node);
8907       /* Keep bottom nodes fixed. (consider recomputing dots?)*/
8908       if (SO->normdir < 0) {
8909          for (in=0; in<SO->N_Node; ++in) {
8910             if (dots[in]>0.8) mask[in]=0;
8911          }
8912       } else {
8913          for (in=0; in<SO->N_Node; ++in) {
8914             if (dots[in]<-0.8) mask[in]=0;
8915          }
8916       }
8917       for (in=0; in<SO->N_Node; ++in) {
8918          if (!mask[in]) { /* skip it, masked by being bottom node */
8919             curedge[in]=0.0;
8920             continue;
8921          }
8922          SUMA_LHv("Node %d\n", in);
8923          xyz = SO->NodeList+3*in;
8924          dir = SO->NodeNormList+3*in;
8925          SUMA_Find_IminImax_2(xyz, dir,
8926                             iset, &fvec, travstep, 11*travstep, 11*travstep,
8927                             0.5*thr, in==ndbg?1:0,
8928                             rng_bot, rdist_bot,
8929                             rng_top, rdist_top,
8930                             avg,
8931                             shs_bot, shs_top,
8932                             vxi_bot, vxi_top);
8933          nodeval = shs_bot[0];
8934          curedge[in] = SUMA_MAX_PAIR(fedges[vxi_bot[1]],fedges[vxi_bot[0]]);
8935          curedge[in] = SUMA_MAX_PAIR(curedge[in], fedges[vxi_top[1]]);
8936          if (nodeval >= thr) { /* we're OK, minor adjustment */
8937             if (in == ndbg || LocalHead){
8938                SUMA_S_Notev("Case 1:Edge threshold %f\n", edge_thr); }
8939             mask[in] = 0; /* anchor node, outside smoothing mask*/
8940             if (nodeval < rng_top[1]) { /* higher val above, move up one step */
8941                if (in == ndbg) { SUMA_S_Note("tiny nudge up\n"); }
8942                memset(P2,0,6*sizeof(float));
8943                SUMA_POINT_AT_DISTANCE(dir, xyz, travstep, P2);
8944                xyz[0] = P2[0][0]; xyz[1] = P2[0][1]; xyz[2] = P2[0][2];
8945             } else  { /* any edge above without dipping
8946                                too much in intensity? */
8947                nn = 0;
8948                while (nn<10 && vxi_top[nn]>=0 &&
8949                                ((shs_top[nn]> 0.5*thr || avg[1]>0.5*thr) &&
8950                                  fedges[vxi_top[nn]]<edge_thr)) {
8951                   ++nn;
8952                }
8953                if (vxi_top[nn]>=0 &&
8954                    fedges[vxi_top[nn]]>=edge_thr &&
8955                    fedges[vxi_top[nn]]>=fedges[vxi_bot[0]]) { /* go up   */
8956                   if (in == ndbg) { SUMA_S_Note("Going up to better edge\n"); }
8957                   ftr = travstep*nn;
8958                   xyz[0] += ftr*dir[0];
8959                   xyz[1] += ftr*dir[1];
8960                   xyz[2] += ftr*dir[2];
8961                }
8962             }
8963          } else {
8964             /* find strongest edge above*/
8965             maxetop = fedges[vxi_top[0]]; maxentop = 0;
8966             okethr = SUMA_MAX_PAIR(curemean-2*curestd, edge_thr);
8967             maxtop = shs_top[0]; nmaxtop =0;
8968             for (nn=1; nn<10 && vxi_top[nn]>=0; ++nn) {
8969                if (fedges[vxi_top[nn]]>maxetop ||
8970                    fedges[vxi_top[nn]]>okethr) {/*also accept higher
8971                                                   decent edges*/
8972                   maxetop = fedges[vxi_top[nn]]; maxentop=nn;
8973                }
8974                if (shs_top[nn] > maxtop) {
8975                   nmaxtop = nn;
8976                   maxtop = shs_top[nn];
8977                }
8978             }
8979            /* find strongest edge below */
8980             maxebot = fedges[vxi_bot[0]]; maxenbot = 0;
8981             maxbot = shs_bot[0]; nmaxbot = 0;
8982             for (nn=1; nn<10 && vxi_bot[nn]>=0; ++nn) {
8983                if (fedges[vxi_bot[nn]]>maxebot && vxi_top[nn]>=0) {
8984                   maxebot = fedges[vxi_bot[nn]]; maxenbot=nn;
8985                }
8986                if (shs_bot[nn] > maxbot) {
8987                   nmaxbot = nn; maxbot = shs_bot[nn];
8988                }
8989             }
8990 
8991 
8992             if (maxetop >= maxebot || maxetop >=okethr){/* go up for better
8993                                                              edge*/
8994                nn = maxentop;
8995                if (in == ndbg|| LocalHead){
8996                   SUMA_S_Notev(
8997                      "Better edge above (%f vs %f, ethr %f, [%f %f]) %d steps\n",
8998                      maxetop, maxebot, edge_thr, curemean, curestd, nn); }
8999                if (fedges[vxi_top[nn]]>=edge_thr) { /* go up  */
9000                   if (in == ndbg) { SUMA_S_Note("Moving up\n");  }
9001                   ftr = travstep*nn;
9002                   xyz[0] += ftr*dir[0];
9003                   xyz[1] += ftr*dir[1];
9004                   xyz[2] += ftr*dir[2];
9005                }
9006             } else {
9007                /* look down for better option */
9008                if (nodeval < rng_bot[1]) {
9009                   { /* Go down to the 1st voxel meeting threshold
9010                               and a good edge */
9011                      if (in == ndbg|| LocalHead) {
9012                         SUMA_S_Notev(
9013                            "Looking down nodeval %f, edge %f, ethr %f\n",
9014                            nodeval, fedges[vxi_bot[0]], edge_thr); }
9015                      nn = 0;
9016                      while (nn<10 && (shs_bot[nn]<thr &&
9017                                       vxi_bot[nn]>=0 &&
9018                                       fedges[vxi_bot[nn]]<edge_thr)) {
9019                         ++nn;
9020                      }
9021                      if (fedges[vxi_bot[nn]]>=edge_thr) {
9022                         if (in == ndbg|| LocalHead){
9023                            SUMA_S_Notev("Going down %d steps to edge+anchor\n",
9024                                        nn);}
9025                         nn = SUMA_MIN_PAIR(nn,3);/* slowly to avoid folding */
9026                         ftr = travstep*nn;
9027                         xyz[0] -= ftr*dir[0];
9028                         xyz[1] -= ftr*dir[1];
9029                         xyz[2] -= ftr*dir[2];
9030                         if (fedges[vxi_bot[nn]]> fedges[vxi_bot[0]])
9031                            mask[in]=0;
9032                      } else { /* no good edge found, keep going if sitting
9033                                  on no edge or no value */
9034                         if (fedges[vxi_bot[0]] < edge_thr || nodeval < 0.1*thr) {
9035                            if (maxbot > nodeval) {
9036                               if (in == ndbg){ SUMA_S_Note("Going down max\n");}
9037                               nn = nmaxbot;
9038                               nn = SUMA_MIN_PAIR(nn,3);/* slowly, avoid folding*/
9039                               ftr = travstep*nn;
9040                               xyz[0] -= ftr*dir[0];
9041                               xyz[1] -= ftr*dir[1];
9042                               xyz[2] -= ftr*dir[2];
9043                            }
9044                         }
9045                      }
9046                   }
9047                }
9048             }
9049                ++N_movers;
9050          }
9051       }
9052 
9053       SUMA_MEAN_STD_VEC(curedge, SO->N_Node, curemean, curestd, 1);
9054       SUMA_LHv("Smoothing round %d, surface edge mean=%f, std=%f\n",
9055                iter, curemean, curestd);
9056       /* Make sure no one node is an anchor holdout */
9057       for (in=0; in<SO->N_Node; ++in) {
9058          if (mask[in] == 0) { /* an anchored node */
9059             N_um = 0; /* number of unanchored neighbors */
9060             for (nn=0; nn<SO->FN->N_Neighb[in]; ++nn) {
9061                if (mask[SO->FN->FirstNeighb[in][nn]]) ++N_um;
9062             }
9063             if ((float)N_um/SO->FN->N_Neighb[in] > 0.75) {
9064                mask[in]=1;
9065                if (LocalHead && in == ndbg) {
9066                   SUMA_LHv("Node %d was anchored but now released %f\n",
9067                            in, (float)N_um/SO->FN->N_Neighb[in]);
9068                }
9069             }
9070          }
9071       }
9072 
9073 
9074       /* Are we making a difference in this world? */
9075       larea=area;
9076       area=SUMA_Mesh_Area(SO, NULL, -1);
9077       darea = (area-larea)/area*100.0;
9078 
9079       /* write it out for debugging */
9080       if (LocalHead) {
9081          SUMA_LHv("Iteration %d, N_movers = %d, area = %f (Darea=%f)\n",
9082                iter, N_movers, area, darea);
9083          THD_force_ok_overwrite(1) ;
9084          sprintf(sbuf,"shrink.02%d",iter);
9085          SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
9086                                  SUMA_GIFTI, SUMA_ASCII, NULL);
9087       }
9088       ++iter;
9089       if (iter > itermax1 || SUMA_ABS(darea) < 0.05) stop = YUP;
9090       if (!stop) {
9091          /* A quick smoothing with anchors in place */
9092          SUMA_NN_GeomSmooth_SO(SO, mask, 0, 10);
9093       } else {
9094          /* A Taubin smooth */
9095          SUMA_Taubin_Smooth_SO(SO, SUMA_EQUAL, 0.1, NULL, 0, 20);
9096       }
9097       if (cs && cs->talk_suma && cs->Send) {
9098          if (!SUMA_SendToSuma (SO, cs, (void *)SO->NodeList,
9099                                SUMA_NODE_XYZ, 1)) {
9100             SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
9101                          "Communication halted.");
9102          }
9103       }
9104    }
9105 
9106    if (LocalHead) {
9107       SUMA_LHv("End of iterations N_movers = %d, area = %f\n",
9108             N_movers, SUMA_Mesh_Area(SO, NULL, -1));
9109       THD_force_ok_overwrite(1) ;
9110       SUMA_Save_Surface_Object_Wrap("shrink", NULL, SO,
9111                               SUMA_GIFTI, SUMA_ASCII, NULL);
9112    }
9113 
9114    if (iter >= itermax1) {
9115       SUMA_S_Note("Convergence criterion not reached. Check results.");
9116    }
9117 
9118 
9119    if (curedge) SUMA_free(curedge); curedge = NULL;
9120    if (dots) SUMA_free(dots); dots = NULL;
9121    if (mask) free(mask); mask = NULL;
9122    if (fvec) free(fvec); fvec = NULL;
9123    if (fedges) SUMA_free(fedges); fedges = NULL;
9124    if (inedges) SUMA_free(inedges); inedges = NULL;
9125    if (inset) DSET_delete(inset); inset=NULL;
9126    SUMA_RETURN(YUP);
9127 }
9128 
9129 /* Shrink a surface so that it ends up resting on the skull
9130    SO: Is the surface, derived from some head convex hull
9131    aset: The anatomical volume
9132    arset: The radial stats of aset
9133    thr: The threshold for the skull region, 0.5 is plenty good, I hope
9134 
9135    Resist attempt to change this function without testing any change
9136    on ALL 9 anatomical datasets from hell.
9137 
9138    Improvements should be carried out in an extra step where the surface
9139    is walked back slowly to rest of a local acceptable bright voxel of the
9140    anatomy. For now, this is pretty good as is.
9141 */
9142 
9143 /* Macros for function SUMA_ShrinkSkullHull_RS only */
9144 
9145          /* Does the voxel or the one below it have a rat > thr ? */
9146 #define HI_RAT_EDGE(vxi,mm) ((rat[vxi[mm]]>thr || (mm>0 && rat[vxi[mm-1]]>thr)))
9147          /* Does the voxel or the one below it have an OK rat > thr ? */
9148 #define HI_RATOK_EDGE(vxi,mm) ((rvec[vxi[mm]]>thr || \
9149                                  (mm>0 && rvec[vxi[mm-1]]>thr)))
9150          /* Is there good signal at that voxel or below it?
9151             A good signal is at location when:
9152                A voxel's signal Z is more than -2, or the voxel below it is
9153             Or A voxel's signal Z is more than -2.5, but with its noise Z
9154                greater than 5, or if the voxel below it satisfies the
9155                condition.*/
9156 #define HI_SIG_EDGE(vxi,mm) ( ( vxZ[vxi[mm]] > -2.0/vxZfac ||                   \
9157                                 (mm>0 && \
9158                                 vxZ[vxi[mm-1]]  > -2.0/vxZfac ))     ||\
9159                               ( (vxZ[vxi[mm]]   > -2.5/vxZfac &&                \
9160                                         vxNZ[vxi[mm]]   > 5.0/vxNZfac)   ||     \
9161                                 (mm>0 && \
9162                                  vxZ[vxi[mm-1]] > -2.5/vxNZfac &&               \
9163                                         vxNZ[vxi[mm-1]] > 5.0/vxNZfac) )  )
9164         /* Using some location nn along the top search direction, sum the voxels'
9165            signal Z values over nsteps starting at nn */
9166 #define SUM_Z_BELOW(nsteps, nn, sum) {\
9167    int kk;  \
9168    sum=0;   \
9169    for (kk=0; kk<nsteps; ++kk) { \
9170       if (nn-kk > 0) sum += vxZ[vxi_top[nn-kk]];   \
9171       else if (kk-nn < 10) sum += vxZ[vxi_bot[kk-nn]];   \
9172       sum /= vxZfac; \
9173    }    \
9174 }
9175         /* Using some location nn along the top search direction, sum the voxels'
9176            noise Z values over nsteps starting at nn */
9177 #define SUM_NZ_BELOW(nsteps, nn, sum) {\
9178    int kk;  \
9179    sum=0;   \
9180    for (kk=0; kk<nsteps; ++kk) { \
9181       if (nn-kk > 0) sum += vxNZ[vxi_top[nn-kk]];   \
9182       else if (kk-nn < 10) sum += vxNZ[vxi_bot[kk-nn]];   \
9183       sum /= vxNZfac; \
9184    }    \
9185 }
9186 
SUMA_ShrinkSkullHull_RS(SUMA_SurfaceObject * SO,THD_3dim_dataset * aset,THD_3dim_dataset * arset,float thr,SUMA_COMM_STRUCT * cs)9187 SUMA_Boolean SUMA_ShrinkSkullHull_RS(SUMA_SurfaceObject *SO,
9188                              THD_3dim_dataset *aset,
9189                              THD_3dim_dataset *arset, float thr,
9190                              SUMA_COMM_STRUCT *cs)
9191 {
9192    static char FuncName[]={"SUMA_ShrinkSkullHull_RS"};
9193    char sbuf[256]={""};
9194    byte *mask=NULL;
9195    short *sb=NULL;
9196    int   in=0, vxi_bot[30], vxi_top[30], iter, N_movers,
9197          ndbg=SUMA_getBrainWrap_NodeDbg(), nn,N_um,
9198          itermax1 = 50, itermax2 = 10, firstpass;
9199    float *rvec=NULL, *xyz, *dir, P2[2][3], travstep, shs_bot[30], shs_top[30];
9200    float rng_bot[2], rng_top[2], rdist_bot[2], rdist_top[2], avg[3], nodeval,
9201          area=0.0, larea=0.0, ftr=0.0, darea=0.0, fac, *rat=NULL;
9202    float *fedges=NULL, edge_thr=0.0, *fnz=NULL, *inedges=NULL, inedge_thr=0.0,
9203          *alt=NULL, szt=0.0, sNzt=0.0;
9204    float maxetop, maxebot, maxtop, maxbot, okethr;
9205    int maxentop,maxenbot, nmaxtop, nmaxbot, *okrat=NULL, smdisp;
9206    short *vxZ=NULL, *vxNZ=NULL;
9207    float dirZ[3], *dots=NULL, *curedge=NULL, curemean, curestd, U3[3], Un,
9208          *disp=NULL, *dispsm=NULL, *trv=NULL, vxZfac=0.0, vxNZfac=0.0;
9209    THD_3dim_dataset *inset=NULL, *rset=NULL;
9210    SUMA_Boolean stop = NOPE;
9211    SUMA_Boolean LocalHead = NOPE;
9212 
9213    SUMA_ENTRY;
9214 
9215    SUMA_LHv("Begin shrinkage, thr=%f\n", thr);
9216 
9217    /* make a copy get rid of unwanted voxels */
9218    NEW_SHORTY(arset,1,FuncName,rset);
9219    sb = DSET_BRICK_ARRAY(rset,0);
9220    rat = THD_extract_to_float(2,arset);
9221    okrat = THD_extract_to_int(3,arset);
9222    for (nn=0; nn<DSET_NVOX(arset); ++nn) {
9223       if (rat[nn]> 0.0 && okrat[nn]) {
9224          if (rat[nn]>1.0) rat[nn]=1.0;
9225          sb[nn]=1000.0*rat[nn];
9226       } else {
9227          sb[nn]=0;
9228       }
9229    }
9230    EDIT_BRICK_FACTOR(rset, 0, 1/1000.0);
9231 
9232    /* get vxNZ and vxZ */
9233    vxZ  = DSET_BRICK_ARRAY(arset,5);
9234    if ((vxZfac = DSET_BRICK_FACTOR(arset,5))==0.0) vxZfac=1.0;
9235 
9236    vxNZ = DSET_BRICK_ARRAY(arset,6);
9237    if ((vxNZfac = DSET_BRICK_FACTOR(arset,6))==0.0) vxNZfac=1.0;
9238 
9239    /* get the edges on the input set anatomical */
9240    if (!DSET_ARRAY(aset,0)) {
9241       SUMA_S_Err("Very strange, pointer lost");
9242       exit(1);
9243    }
9244 
9245    fedges = THD_extract_to_float(4,arset);
9246 
9247    if (!DSET_ARRAY(aset,0)) {
9248       SUMA_S_Err("Very strange, pointer lost after edge");
9249       exit(1);
9250    }
9251 
9252    /* non-zero edges, masked by rat*/
9253    for (in=0, N_um=0; in<DSET_NVOX(aset); ++in) {
9254       if (fedges[in] > 0.0 && okrat[in] && rat[in] > thr) ++N_um;
9255    }
9256    fnz = (float*)SUMA_calloc(N_um, sizeof(float));
9257    for (in=0, N_um=0; in<DSET_NVOX(aset); ++in) {
9258       if (fedges[in] > 0.0 && okrat[in] && rat[in] > thr) fnz[N_um++]=fedges[in];
9259    }
9260    qsort(fnz, N_um, sizeof(float),
9261          (int(*) (const void *, const void *)) SUMA_compare_float);
9262    /* get the lowest 2% */
9263    edge_thr = fnz[(int)(0.02*N_um)];
9264    SUMA_free(fnz); fnz=NULL;
9265    SUMA_S_Notev("Edge threshold of %f\n", edge_thr);
9266 
9267    travstep = SUMA_ABS(DSET_DX(aset));
9268    if (travstep > SUMA_ABS(DSET_DY(aset))) travstep = SUMA_ABS(DSET_DY(aset));
9269    if (travstep > SUMA_ABS(DSET_DZ(aset))) travstep = SUMA_ABS(DSET_DZ(aset));
9270    if (!(mask = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node))) {
9271       SUMA_S_Crit("Failed to allocate");
9272       SUMA_RETURN(NOPE);
9273    }
9274 
9275    /* For a clean, bust like button, anchor bottom nodes */
9276    /* For now, the decision is solely based on the normals
9277       being parallel to the Z direction.
9278       Perhaps I should add a depth criterion, but that
9279       is not necessary it seems */
9280       dirZ[0]=0.0; dirZ[1]=0.0; dirZ[2]=1.0;
9281       dots = NULL;
9282       if (!(SUMA_DotNormals(SO, dirZ, &dots))) {
9283          SUMA_S_Err("Failed to get dots");
9284       } else {
9285          if (LocalHead) SUMA_WRITE_ARRAY_1D(dots, SO->N_Node, 1, "DOTS.1D.dset");
9286       }
9287 
9288 
9289    /* Get distributions of values around the surface */
9290    curedge = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
9291    for (in=0; in<SO->N_Node; ++in) {
9292       xyz = SO->NodeList+3*in;
9293       dir = SO->NodeNormList+3*in;
9294       SUMA_Find_IminImax_2(xyz, dir,
9295                          rset, &rvec, travstep, 1*travstep, 1*travstep,
9296                          thr, (LocalHead && in==ndbg)?1:0,
9297                          rng_bot, rdist_bot,
9298                          rng_top, rdist_top,
9299                          avg,
9300                          shs_bot, shs_top,
9301                          vxi_bot, vxi_top);
9302       curedge[in] = SUMA_MAX_PAIR(fedges[vxi_bot[1]],fedges[vxi_bot[0]]);
9303       curedge[in] = SUMA_MAX_PAIR(curedge[in], fedges[vxi_top[1]]);
9304    }
9305    SUMA_MEAN_STD_VEC(curedge,SO->N_Node,curemean, curestd, 1);
9306    SUMA_LHv("About to loop, surface edge mean=%f, std=%f", curemean, curestd);
9307    disp = (float *)SUMA_calloc(SO->N_Node*3, sizeof(float));
9308    dispsm = (float *)SUMA_calloc(SO->N_Node*3, sizeof(float));
9309    stop = NOPE;
9310    N_movers = 0; iter=0;
9311    smdisp = 2;
9312    firstpass = 1;
9313    while (!stop) {
9314       N_movers = 0;
9315       memset(mask, 1, sizeof(byte)*SO->N_Node);
9316       /* Keep bottom nodes fixed. (consider recomputing dots?)*/
9317       SUMA_DotNormals(SO, dirZ, &dots);
9318       if (SO->normdir < 0) {
9319          for (in=0; in<SO->N_Node; ++in) {
9320             if (dots[in]>0.8) mask[in]=0;
9321          }
9322       } else {
9323          for (in=0; in<SO->N_Node; ++in) {
9324             if (dots[in]<-0.8) mask[in]=0;
9325          }
9326       }
9327       okethr = SUMA_MAX_PAIR(curemean-2*curestd, edge_thr);
9328       for (in=0; in<SO->N_Node; ++in) {
9329          SUMA_LHv("Node %d\n", in);
9330          xyz = SO->NodeList+3*in;
9331          dir = SO->NodeNormList+3*in;
9332          trv = disp+3*in; memset(trv,0,sizeof(float)*3);
9333          if (!mask[in]) { /* skip it, masked by being bottom node */
9334             curedge[in]=0.0;
9335             continue;
9336          }
9337          SUMA_Find_IminImax_2(xyz, dir,
9338                             rset, &rvec, travstep, 11*travstep, 11*travstep,
9339                             thr, (LocalHead && in==ndbg)?1:0,
9340                             rng_bot, rdist_bot,
9341                             rng_top, rdist_top,
9342                             avg,
9343                             shs_bot, shs_top,
9344                             vxi_bot, vxi_top);
9345          nodeval = shs_bot[0];
9346          curedge[in] = SUMA_MAX_PAIR(fedges[vxi_bot[1]],fedges[vxi_bot[0]]);
9347          curedge[in] = SUMA_MAX_PAIR(curedge[in], fedges[vxi_top[1]]);
9348          if (1) {
9349             /* find strongest edge above*/
9350             maxetop = fedges[vxi_top[0]]; maxentop = 0;
9351             maxtop = shs_top[0]; nmaxtop =0;
9352             for (nn=1; nn<10 && vxi_top[nn]>=0; ++nn) {
9353                if (  (HI_RAT_EDGE(vxi_top,nn) || HI_SIG_EDGE(vxi_top,nn)) &&
9354                      (fedges[vxi_top[nn]]>maxetop ||
9355                       fedges[vxi_top[nn]]>okethr)) {/*also accept higher
9356                                                   decent edges*/
9357                   maxetop = fedges[vxi_top[nn]]; maxentop=nn;
9358                }
9359                if (shs_top[nn] > maxtop) {
9360                   nmaxtop = nn;
9361                   maxtop = shs_top[nn];
9362                }
9363             }
9364             SUM_Z_BELOW(5, maxentop, szt);
9365             SUM_NZ_BELOW(5, maxentop, sNzt);
9366 
9367             /* find strongest edge below */
9368             maxebot = fedges[vxi_bot[0]]; maxenbot = 0;
9369             maxbot = shs_bot[0]; nmaxbot = 0;
9370             for (nn=1; nn<10 && vxi_bot[nn]>=0; ++nn) {
9371                if (fedges[vxi_bot[nn]]>maxebot && vxi_top[nn]>=0) {
9372                   maxebot = fedges[vxi_bot[nn]]; maxenbot=nn;
9373                }
9374                if (shs_bot[nn] > maxbot &&
9375                      (rat[vxi_bot[nn]]>thr || rat[vxi_bot[nn-1]]>thr)) {
9376                   nmaxbot = nn; maxbot = shs_bot[nn];
9377                }
9378             }
9379 
9380 
9381             if (  (maxetop >= maxebot) ||
9382                   ( maxentop > 0 && maxetop >=okethr &&
9383                      (  HI_RAT_EDGE(vxi_top, maxentop) ||
9384                         HI_SIG_EDGE(vxi_top, maxentop) ) &&
9385                      (  szt > -10 || sNzt > 25 ) )
9386                ) {
9387                                     /* go up for better edge*/
9388                nn = maxentop;
9389                if (in == ndbg|| LocalHead){
9390                   SUMA_S_Notev(
9391                      "Better edge above (%f vs %f, ethr %f, [%f %f]) \n"
9392                      "%d steps (rat %f, %f, thr %f)\n",
9393                      maxetop, maxebot, edge_thr, curemean, curestd, nn,
9394                      rat[vxi_top[nn]], rat[vxi_top[nn-1]], thr); }
9395                if (fedges[vxi_top[nn]]>=edge_thr) { /* go up  */
9396                   ftr = travstep*nn;
9397                   trv[0] = ftr*dir[0];
9398                   trv[1] = ftr*dir[1];
9399                   trv[2] = ftr*dir[2];
9400                   if ((fedges[vxi_top[nn]]>= fedges[vxi_top[0]] ||
9401                        fedges[vxi_top[nn]]>= okethr ) &&
9402                       ( HI_RATOK_EDGE(vxi_top, nn) && HI_SIG_EDGE(vxi_top, nn) &&
9403                         ( szt > 0.0 || (szt > -5 && sNzt > 25) ))
9404                      )
9405                            mask[in]=0;
9406                   if (in == ndbg) {
9407                      SUMA_S_Notev("Moving up by %f %f %f, mask[%d]=%d, %d %d\n",
9408                                    trv[0], trv[1], trv[2], in, mask[in],
9409                                    HI_RATOK_EDGE(vxi_top, nn),
9410                                    HI_SIG_EDGE(vxi_top, nn));
9411                   }
9412                }
9413             } else {
9414                /* look down for better option */
9415                if (nodeval < rng_bot[1]) {
9416                   { /* Go down to the 1st voxel meeting threshold
9417                               and a good edge */
9418                      if (in == ndbg|| LocalHead) {
9419                         SUMA_S_Notev(
9420                            "Looking down nodeval %f, edge %f, ethr %f\n",
9421                            nodeval, fedges[vxi_bot[0]], edge_thr); }
9422                      nn = 0;
9423                      while (nn<10 && (shs_bot[nn]<thr &&
9424                                       vxi_bot[nn]>=0 &&
9425                                       fedges[vxi_bot[nn]]<edge_thr)) {
9426                         ++nn;
9427                      }
9428                      if (fedges[vxi_bot[nn]]>=edge_thr) {
9429                         if (in == ndbg|| LocalHead){
9430                            SUMA_S_Notev("Going down %d steps to edge+anchor\n",
9431                                        nn);}
9432                         nn = SUMA_MIN_PAIR(nn,3);/* slowly to avoid folding */
9433                         ftr = travstep*nn;
9434                         trv[0] = -ftr*dir[0];
9435                         trv[1] = -ftr*dir[1];
9436                         trv[2] = -ftr*dir[2];
9437                         if (fedges[vxi_bot[nn]]> fedges[vxi_bot[0]])
9438                            mask[in]=0;
9439                      } else  { /* no good edge found, keep going if sitting
9440                                  on no edge or no value, including in
9441                                  unmasked ratio*/
9442                         if (fedges[vxi_bot[0]] < edge_thr || nodeval < 0.1*thr) {
9443                            if (maxbot > nodeval) {
9444                               nn = nmaxbot;
9445                               nn = SUMA_MIN_PAIR(nn,3);/* slowly, avoid folding*/
9446                               if (in == ndbg){
9447                                  SUMA_S_Notev("Going down %d steps\n", nn);}
9448                               ftr = travstep*nn;
9449                               trv[0] = -ftr*dir[0];
9450                               trv[1] = -ftr*dir[1];
9451                               trv[2] = -ftr*dir[2];
9452                            } else if ( nodeval == 0.0 &&
9453                                        rat[vxi_bot[0]]<0.5*thr ) {
9454                               nn = 10;
9455                               if (in == ndbg){
9456                                  SUMA_S_Notev("Going down fast %d steps\n", nn);}
9457                               ftr = travstep*nn;
9458                               trv[0] = -ftr*dir[0];
9459                               trv[1] = -ftr*dir[1];
9460                               trv[2] = -ftr*dir[2];
9461 
9462                            }
9463                         }
9464                      }
9465                   }
9466                }
9467             }
9468                ++N_movers;
9469          }
9470       }
9471 
9472       SUMA_MEAN_STD_VEC(curedge, SO->N_Node, curemean, curestd, 1);
9473       SUMA_LHv("Smoothing round %d, surface edge mean=%f, std=%f\n",
9474                iter, curemean, curestd);
9475       /* Make sure no one node is an anchor holdout */
9476       for (in=0; in<SO->N_Node; ++in) {
9477          if (mask[in] == 0) { /* an anchored node */
9478             N_um = 0; /* number of unanchored neighbors */
9479             for (nn=0; nn<SO->FN->N_Neighb[in]; ++nn) {
9480                if (mask[SO->FN->FirstNeighb[in][nn]]) ++N_um;
9481             }
9482             if ((float)N_um/SO->FN->N_Neighb[in] > 0.75) {
9483                mask[in]=1;
9484                if (LocalHead && in == ndbg) {
9485                   SUMA_LHv("Node %d was anchored but now released %f\n",
9486                            in, (float)N_um/SO->FN->N_Neighb[in]);
9487                }
9488             }
9489          }
9490       }
9491 
9492       /* smooth displacement and add it */
9493       if (smdisp) {
9494          dispsm = SUMA_SmoothAttr_Neighb_Rec(disp, SO->N_Node*3, dispsm,
9495                                              SO->FN, 3, smdisp, mask, 0);
9496       } else {
9497          memcpy(dispsm, disp, 3*SO->N_Node*sizeof(float));
9498       }
9499       for (in=0; in<SO->N_Node; ++in) {
9500          if (in == ndbg) {
9501             SUMA_S_Notev("Post smth & mask manip. Node %d(%d) trv: %f %f %f\n",
9502                          in, mask[in], trv[0], trv[1], trv[2]);
9503          }
9504          xyz = SO->NodeList+3*in;
9505          if (mask[in]) {
9506             trv = dispsm+3*in;
9507          } else {
9508             trv = disp+3*in;
9509          }
9510          xyz[0] += trv[0];
9511          xyz[1] += trv[1];
9512          xyz[2] += trv[2];
9513       }
9514 
9515       /* Are we making a difference in this world? */
9516       larea=area;
9517       area=SUMA_Mesh_Area(SO, NULL, -1);
9518       darea = (area-larea)/area*100.0;
9519 
9520       /* write it out for debugging */
9521       if (LocalHead || ndbg>=0) {
9522          SUMA_S_Notev("Iteration %d, N_movers = %d, area = %f (Darea=%f)\n",
9523                iter, N_movers, area, darea);
9524          if (LocalHead) {
9525             THD_force_ok_overwrite(1) ;
9526             sprintf(sbuf,"shrink.02%d",iter);
9527             SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
9528                                     SUMA_GIFTI, SUMA_ASCII, NULL);
9529          }
9530       }
9531       ++iter;
9532 
9533       if (iter > itermax1 || SUMA_ABS(darea) < 0.01) stop = YUP;
9534 
9535       /* By turning off firstpass, you'll allow more flexibility
9536       in the mesh which can capture noble shapes like the nose.
9537       Mais bien sur! If you don't want such things, don't let
9538       firstpass go to 0
9539       Also, you need not go to 0.01 if you do not care for the nose
9540       and eye sockets. */
9541       if (firstpass && SUMA_ABS(darea) < 0.05) firstpass = 0;
9542 
9543       if (!stop && firstpass) {
9544          /* A quick smoothing with anchors in place,
9545             Use it even if you are smoothing attributes up there.
9546             Otherwise you could get skirts at the bottom in
9547             certain cases. */
9548          SUMA_NN_GeomSmooth_SO(SO, mask, 0, 10);
9549       } else {
9550          /* A Taubin smooth */
9551          if (1) {
9552             SUMA_Taubin_Smooth_SO(SO, SUMA_EQUAL, 0.1, NULL, 0, 20);
9553          } else {
9554             SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
9555             SUMA_DIM_CENTER(SO);
9556          }
9557       }
9558       if (cs && cs->talk_suma && cs->Send) {
9559          if (!SUMA_SendToSuma (SO, cs, (void *)SO->NodeList,
9560                                SUMA_NODE_XYZ, 1)) {
9561             SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
9562                          "Communication halted.");
9563          }
9564       }
9565    }
9566 
9567    if (LocalHead) {
9568       SUMA_LHv("End of iterations N_movers = %d, area = %f\n",
9569             N_movers, SUMA_Mesh_Area(SO, NULL, -1));
9570       THD_force_ok_overwrite(1) ;
9571       SUMA_Save_Surface_Object_Wrap("shrink", NULL, SO,
9572                               SUMA_GIFTI, SUMA_ASCII, NULL);
9573    }
9574 
9575    if (iter >= itermax1) {
9576       SUMA_S_Note("Convergence criterion not reached. Check results.");
9577    }
9578 
9579    if (rat) free(rat); rat=NULL;
9580    if (okrat) free(okrat); okrat=NULL;
9581    if (curedge) SUMA_free(curedge); curedge = NULL;
9582    if (dots) SUMA_free(dots); dots = NULL;
9583    if (mask) free(mask); mask = NULL;
9584    if (rvec) free(rvec); rvec = NULL;
9585    if (fedges) free(fedges); fedges = NULL;
9586    if (inedges) SUMA_free(inedges); inedges = NULL;
9587    if (inset) DSET_delete(inset); inset=NULL;
9588    if (rset) DSET_delete(rset); rset=NULL;
9589    if (disp) SUMA_free(disp); disp=NULL;
9590    if (dispsm) SUMA_free(dispsm); dispsm=NULL;
9591    SUMA_RETURN(YUP);
9592 }
9593 
SUMA_ShrinkHeadSurf_RS(SUMA_SurfaceObject * SO,THD_3dim_dataset * aset,THD_3dim_dataset * arset,float * ucm,SUMA_COMM_STRUCT * cs)9594 SUMA_Boolean SUMA_ShrinkHeadSurf_RS(SUMA_SurfaceObject *SO,
9595                              THD_3dim_dataset *aset,
9596                              THD_3dim_dataset *arset,
9597                              float *ucm,
9598                              SUMA_COMM_STRUCT *cs)
9599 {
9600    static char FuncName[]={"SUMA_ShrinkHeadSurf_RS"};
9601    char sbuf[256]={""};
9602    byte *mask=NULL;
9603    short *oke=NULL, *okb=NULL, *ov=NULL, *un=NULL;
9604    short *sb=NULL, *isin=NULL;
9605    int   in=0, vxi_bot[30], vxi_top[30], iter, N_movers,
9606          ndbg=SUMA_getBrainWrap_NodeDbg(), nn,N_um, trvoff[2],trv[2],
9607          itermax1 = 100, itermax2 = 10, pass, passiter, vv, IJK[3];
9608    THD_fvec3 ccc, ncoord;
9609    float cm[3], xyz_ijk[3], *avec=NULL, *xyz, *dir, P2[2][3],
9610          travstep, shs_bot[30], shs_top[30], ovfac, unfac;
9611    float rng_bot[2], rng_top[2], rdist_bot[2], rdist_top[2], avg[3], nodeval,
9612          area=0.0, larea=0.0, ftr=0.0, darea=0.0, fac, *rat=NULL;
9613    float *fedges=NULL, edge_thr=0.0, *fnz=NULL, *inedges=NULL, inedge_thr=0.0,
9614          *alt=NULL, szt=0.0, sNzt=0.0, mvoxd, means[3], ztop=0.0;
9615    float maxetop, maxebot, maxtop, maxbot, okethr;
9616    int maxentop,maxenbot, nmaxtop, nmaxbot, *okrat=NULL, smdisp, N_in, cmijk;
9617    short *vxZ=NULL, *vxNZ=NULL;
9618    float dirZ[3], *dots=NULL, *curedge=NULL, curemean, curestd, U3[3], Un,
9619          *disp=NULL, *dispsm=NULL, vxZfac=0.0, vxNZfac=0.0, cmstats[5],
9620          *prvec=NULL, *drvec=NULL, *ftrv=NULL;
9621    THD_3dim_dataset *inset=NULL, *rset=NULL;
9622    SUMA_SPHERE_QUALITY *SSQ=NULL;
9623    SUMA_VOLPAR *vp=NULL;
9624    SUMA_Boolean stop = NOPE;
9625    SUMA_Boolean LocalHead = NOPE;
9626 
9627    SUMA_ENTRY;
9628 
9629    SUMA_LH("Begin head shrinkage, on %s and %s get Zs\n",
9630            DSET_PREFIX(aset), DSET_PREFIX(arset));
9631    /* get under and over */
9632    un = DSET_BRICK_ARRAY(arset,0);
9633    if ((unfac = DSET_BRICK_FACTOR(arset,0))==0.0) unfac=1.0;
9634    ov = DSET_BRICK_ARRAY(arset,1);
9635    if ((ovfac = DSET_BRICK_FACTOR(arset,1))==0.0) ovfac=1.0;
9636 
9637    /* get vxNZ and vxZ */
9638    vxZ  = DSET_BRICK_ARRAY(arset,5);
9639    if ((vxZfac = DSET_BRICK_FACTOR(arset,5))==0.0) vxZfac=1.0;
9640 
9641    vxNZ = DSET_BRICK_ARRAY(arset,6);
9642    if ((vxNZfac = DSET_BRICK_FACTOR(arset,6))==0.0) vxNZfac=1.0;
9643 
9644    /* get the edges  */
9645    SUMA_LH("get edgess\n");
9646    fedges = THD_extract_to_float(4,arset);
9647 
9648    /* get regions around sign change for (U-O)/O */
9649    prvec = THD_extract_to_float(8,arset);
9650    drvec = THD_extract_to_float(7,arset);
9651 
9652    /* get mask of voxels in the head surface */
9653    SUMA_LH("is inning\n");
9654    vp = SUMA_VolParFromDset(aset);
9655    if (!(isin = SUMA_FindVoxelsInSurface (SO, vp, &N_in, 1, NULL))) {
9656       SUMA_S_Err("Failed to get insiders");
9657       SUMA_RETURN(NOPE);
9658    }
9659    EDIT_add_brick (arset,MRI_short, 0.0, isin);
9660    EDIT_BRICK_LABEL (arset, DSET_NVALS(arset)-1, "inhead");
9661 
9662    /* travel step */
9663    travstep = SUMA_ABS(DSET_DX(aset));
9664    if (travstep > SUMA_ABS(DSET_DY(aset))) travstep = SUMA_ABS(DSET_DY(aset));
9665    if (travstep > SUMA_ABS(DSET_DZ(aset))) travstep = SUMA_ABS(DSET_DZ(aset));
9666    if (!(mask = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node))) {
9667       SUMA_S_Crit("Failed to allocate");
9668       SUMA_RETURN(NOPE);
9669    }
9670 
9671    /* Get angle between normal and radius */
9672    SUMA_LH("Compute dots\n");
9673    dirZ[0]=0.0; dirZ[1]=0.0; dirZ[2]=1.0;
9674    dots = NULL;
9675    if (!(SUMA_DotNormals(SO, dirZ, &dots))) {
9676       SUMA_S_Err("Failed to get dots");
9677    } else {
9678       if (LocalHead) SUMA_WRITE_ARRAY_1D(dots, SO->N_Node, 1, "DOTS.1D.dset");
9679    }
9680 
9681 
9682    /* Get distributions of edge values around the surface */
9683    SUMA_LH("Edge dist\n");
9684    curedge = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
9685    for (in=0; in<SO->N_Node; ++in) {
9686       xyz = SO->NodeList+3*in;
9687       dir = SO->NodeNormList+3*in;
9688       SUMA_LH("in=%d fvecp=%p, *fvec=%p", in, &avec, avec);
9689       SUMA_Find_IminImax_2(xyz, dir,
9690                          aset, &avec, travstep, 1*travstep, 1*travstep,
9691                          0.0, (1 || LocalHead && in==ndbg)?1:0,
9692                          rng_bot, rdist_bot,
9693                          rng_top, rdist_top,
9694                          avg,
9695                          shs_bot, shs_top,
9696                          vxi_bot, vxi_top);
9697       SUMA_LH("back fvecp=%p, *fvec=%p", &avec, avec);
9698       curedge[in] = SUMA_MAX_PAIR(fedges[vxi_bot[1]],fedges[vxi_bot[0]]);
9699       curedge[in] = SUMA_MAX_PAIR(curedge[in], fedges[vxi_top[1]]);
9700    }
9701    SUMA_MEAN_STD_VEC(curedge,SO->N_Node,curemean, curestd, 1);
9702 
9703 
9704    /* What is the top 5% z coord? */
9705    {
9706       byte *okmask = (byte *)SUMA_malloc(sizeof(byte)*DSET_NVOX(aset));
9707       for (vv=0; vv<DSET_NVOX(aset); ++vv)
9708          if (isin[vv] > 1) okmask[vv]=1; else okmask[vv]=0;
9709       if (!SUMA_VoxelDepth_Z(aset, okmask, NULL, 0.0, NULL, 0, 0, &ztop)) {
9710          SUMA_S_Err("Failed to get depth");
9711          SUMA_RETURN(NOPE);
9712       }
9713       SUMA_free(okmask); okmask=NULL;
9714    }
9715    /* center of mass */
9716    if (!ucm) {
9717       byte *cmmm = (byte *)SUMA_calloc(DSET_NVOX(aset), sizeof(byte));
9718       for (vv=0; vv<DSET_NVOX(aset); ++vv) {
9719          if (isin[vv]>1) cmmm[vv]=1;
9720       }
9721       ccc = THD_cmass(aset, 0, cmmm, 0); SUMA_free(cmmm); cmmm=NULL;
9722       cm[0] = ccc.xyz[0];
9723       cm[1] = ccc.xyz[1];
9724       cm[2] = ccc.xyz[2];
9725    } else {
9726       cm[0] = ucm[0];
9727       cm[1] = ucm[1];
9728       cm[2] = ucm[2];
9729    }
9730 
9731    /* adjust Z of cm so that we're at 50mm from the top ,
9732       this way we're less likely to be too low */
9733    if (cm[2] < ztop - 50) {
9734       SUMA_S_Notev(
9735          "computed cmass Z = %f, top estimated at %f, Z now set to %f\n",
9736          cm[2], ztop, ztop - 50);
9737       cm[2] = ztop - 50;
9738    } else {
9739       SUMA_S_Notev(
9740          "computed cmass Z = %f, top estimated at %f, Z left alone\n",
9741          cm[2], ztop);
9742    }
9743    /* change cm to index units */
9744    ccc.xyz[0]=cm[0]; ccc.xyz[1]=cm[1]; ccc.xyz[2]=cm[2];
9745    ncoord = THD_dicomm_to_3dmm(aset, ccc);
9746    ccc = THD_3dmm_to_3dfind(aset, ncoord);
9747    cm[0] = ccc.xyz[0];
9748    cm[1] = ccc.xyz[1];
9749    cm[2] = ccc.xyz[2];
9750    cmijk = (int)cm[0]+(int)cm[1]*DSET_NX(aset)+
9751                       (int)cm[2]*DSET_NX(aset)*DSET_NY(aset);
9752 
9753 
9754    /* compute some stats around the center of mass */
9755    SUMA_S_Note("Computing stats around new cm");
9756    {
9757       float *nbar=NULL;
9758       int nbar_num;
9759       MCW_cluster *nbhd=NULL;
9760       MRI_IMAGE *dsim=NULL;
9761       dsim = THD_extract_float_brick(0, aset);
9762       float *fin = (float*)MRI_FLOAT_PTR(dsim);
9763       nbhd = MCW_rectmask( SUMA_ABS(DSET_DX(aset)),
9764                            SUMA_ABS(DSET_DY(aset)),
9765                            SUMA_ABS(DSET_DZ(aset)),
9766                            40, 40, 40 ) ;
9767       nbar = (float*)SUMA_calloc(nbhd->num_pt, sizeof(float));
9768       nbar_num = mri_get_nbhd_array( dsim , NULL,
9769                         (int)cm[0], (int)cm[1], (int)cm[2] , nbhd , nbar ) ;
9770       mri_nstat_mMP2S( nbar_num , nbar, fin[cmijk], cmstats ) ;
9771       SUMA_free(nbar); nbar = NULL;
9772       KILL_CLUSTER(nbhd); nbhd = NULL;
9773 
9774       cmstats[3] = 1.4826*cmstats[3];     /* turn MAD to stdv */
9775       SUMA_S_Notev("cmijk=[%f %f %f], median %f, stdv from MAD %f\n",
9776                cm[0], cm[1], cm[2], cmstats[1], cmstats[3]);
9777       /* if (Rcmstats) { Rcmstats[0]=cmstats[1]; Rcmstats[1]=cmstats[3]; }*/
9778       mri_free(dsim); dsim = NULL; fin = NULL;
9779    }
9780 
9781    /* min voxel dim */
9782    mvoxd = SUMA_MIN_PAIR(SUMA_ABS(DSET_DX(aset)), SUMA_ABS(DSET_DY(aset)));
9783    mvoxd = SUMA_MIN_PAIR(mvoxd, SUMA_ABS(DSET_DZ(aset)));
9784    trv[0] = 0; trv[1] = 1;
9785    trv[0] = SUMA_ROUND(3.0 / mvoxd); if (trv[0]<3) trv[0]=3;
9786    trv[1] = SUMA_ROUND(3.0 / mvoxd); if (trv[1]<3) trv[1]=3;
9787    trvoff[0]=0; trvoff[1]=1;
9788 
9789    /* Now create a vector marking good edges in the volume */
9790    SUMA_LHv("Good edging trv: %d %d, trvoff %d %d\nThis has not been used yet.",
9791          trv[0], trv[1], trvoff[0], trvoff[1]);
9792    oke = (short *)SUMA_calloc(DSET_NVOX(aset), sizeof(short));
9793    for (vv=0; vv<DSET_NVOX(aset); ++vv) {
9794       if (fedges[vv] && prvec[vv]>-0.1 && isin[vv]>1 && drvec[vv] > 0.0) {
9795          /* check also that the signal over 2mm above is less than signal over
9796             2 mm below */
9797          Vox1D2Vox3D(vv, DSET_NX(aset), DSET_NX(aset)*DSET_NY(aset), IJK);
9798          xyz_ijk[0]= IJK[0]; xyz_ijk[1]= IJK[1]; xyz_ijk[2]= IJK[2];
9799 
9800          if (!SUMA_Vox_Radial_Stats(avec,
9801                   DSET_NX(aset), DSET_NY(aset), DSET_NZ(aset),
9802                   xyz_ijk, cm, trv, trvoff,
9803                   means,
9804                   NULL, NULL, NULL, NULL, 1)) {
9805             SUMA_S_Errv("Failed at voxel %d %d %d\n",
9806                         IJK[0], IJK[1], IJK[2]);
9807             SUMA_RETURN(NOPE);
9808          }
9809          #if 0
9810                fprintf(stdout,"%d %d %d %f %f %f\n",
9811                         IJK[0], IJK[1], IJK[2], fedges[vv], means[1], means[2]);
9812          #endif
9813          if (means[1] > 0.0 &&
9814              (means[1]-means[2])/(means[1]+means[2]) > 0.0) {
9815                oke[vv] = 1;
9816          }
9817       }
9818    }
9819 
9820    /* append to features set */
9821    EDIT_add_brick (arset,MRI_short, 0.0, oke);
9822    EDIT_BRICK_LABEL (arset, DSET_NVALS(arset)-1, "oke");
9823 
9824    /* Create a vector marking good candidates for brain contour */
9825    SUMA_S_Note("Getting good contour candidates, also not used yet");
9826    okb = (short *)SUMA_calloc(DSET_NVOX(aset), sizeof(short));
9827    for (vv=0; vv<DSET_NVOX(aset); ++vv) {
9828       if ( prvec[vv] > 0.0 && drvec[vv]>0.0 && isin[vv]>1 &&
9829            (vxNZ[vv]*vxNZfac > 10 ||
9830                      ( vxNZ[vv]*vxNZfac > 5 && vxZ[vv]*vxZfac > -2 ) )  ) {
9831          if (vv == 4758873) {
9832          Vox1D2Vox3D(vv, DSET_NX(aset), DSET_NX(aset)*DSET_NY(aset), IJK);
9833                fprintf(stdout,"Voxel %d %d %d \n"
9834                         "%f %f %d \n"
9835                         "%f %f %f %f %f \n",
9836                         IJK[0], IJK[1], IJK[2],
9837                         prvec[vv], drvec[vv], isin[vv],
9838                         vxNZ[vv]*vxNZfac, vxZ[vv]*vxZfac,
9839                         un[vv]*unfac, ov[vv]*ovfac, cmstats[1]);
9840          }
9841          okb[vv] = 1;
9842       }
9843    }
9844    EDIT_add_brick (arset,MRI_short, 0.0, okb);
9845    EDIT_BRICK_LABEL (arset, DSET_NVALS(arset)-1, "okb");
9846 
9847 
9848    /* DO THIS: STOPPED HERE
9849    Instead of plodding along on a mixture of drvec and pdrvec,
9850    it would be easier to create an ok voxel mask with the following:
9851    step(prvec)*step(drvec)*(step(vxNZ-10)+step(vxNZ-5)*step(vxZ+2))*
9852    UnderMean - OverMean > 0 && UnderMean - OverMean / BrainAvg
9853    (BrainAvg winged it at 235 here) should be decent
9854       step(a)*step(b)*
9855       (step(c-10)+step(c-5)*step(d+2))*step((e-f)/235-0.1)
9856    It behooves you after the first pass to recompute the stats inside the volume
9857    and possibly, all of voxZ then start using the mean of voxels inside, etc.
9858    Might also want to use e/235-0.2 or compute the Z of UnderMean.
9859 
9860    okb is the implementation for the comment above. Consider using it directly
9861    when searching for where to go next.
9862    */
9863 
9864    SUMA_LHv("About to loop, surface edge mean=%f, std=%f\n", curemean, curestd);
9865    disp = (float *)SUMA_calloc(SO->N_Node*3, sizeof(float));
9866    dispsm = (float *)SUMA_calloc(SO->N_Node*3, sizeof(float));
9867    stop = NOPE;
9868    N_movers = 0; iter=0;
9869    smdisp = 2;
9870    pass = 0; passiter=0;
9871    while (!stop) {
9872       N_movers = 0;
9873       memset(mask, 1, sizeof(byte)*SO->N_Node);
9874       /* Keep bottom nodes fixed. (consider recomputing dots?)*/
9875       SUMA_DotNormals(SO, dirZ, &dots);
9876       okethr = SUMA_MAX_PAIR(curemean-2*curestd, edge_thr);
9877       for (in=0; in<SO->N_Node; ++in) {
9878          xyz = SO->NodeList+3*in;
9879          dir = SO->NodeNormList+3*in;
9880          ftrv = disp+3*in; memset(ftrv,0,sizeof(float)*3);
9881          if (!mask[in]) { /* skip it, masked by being bottom node */
9882             curedge[in]=0.0;
9883             continue;
9884          }
9885          SUMA_Find_IminImax_2(xyz, dir,
9886                             arset, &drvec, travstep, 13*travstep, 13*travstep,
9887                             0.0, (0 && LocalHead && in==ndbg)?1:0,
9888                             rng_bot, rdist_bot,
9889                             rng_top, rdist_top,
9890                             avg,
9891                             shs_bot, shs_top,
9892                             vxi_bot, vxi_top);
9893          nodeval = shs_bot[0];
9894 
9895          /* find positive drvec below */
9896          nn = 0;
9897          while ( nn < 10 && (
9898                   (shs_bot[0] >= 0.0 && shs_bot[nn+1] < 0) ||
9899                   (shs_bot[0] < 0 && avec[vxi_bot[0]] > avec[vxi_bot[nn+1]]) ||
9900                   (prvec[vxi_bot[nn]] < 0)
9901                             ) ) ++nn;
9902          if (nn==10) { /* no good opt */
9903             nn = 0;
9904          }
9905          if (in == ndbg){
9906             /* note that conv. measure is not valid here because it reflects
9907             things before any movement */
9908             SUMA_S_Notev(
9909                "Node %d, voxel %d, Better below %d steps\n"
9910                "%f %f %f\n%f %f %f\n%f, %f, %f\n"
9911                ,
9912                in,vxi_bot[0],nn,
9913                prvec[vxi_bot[nn]], prvec[vxi_bot[nn+1]], prvec[vxi_bot[nn+2]],
9914                drvec[vxi_bot[nn]], drvec[vxi_bot[nn+1]], drvec[vxi_bot[nn+2]],
9915                SSQ ? SSQ->node_DelDot[in]:0.0,
9916                   SSQ ? SSQ->node_DelDist[in]/SSQ->AvgDist:0.0,
9917                      SSQ ? SSQ->node_Conv[in]:0.0);
9918          }
9919          if (prvec[vxi_bot[nn]] > 0 && prvec[vxi_bot[nn+1]] > 0 &&
9920               prvec[vxi_bot[nn+2]] > 0 &&
9921              drvec[vxi_bot[nn]] > 0  &&  drvec[vxi_bot[nn+1]] > 0 &&
9922               drvec[vxi_bot[nn+2]] > 0 &&
9923               vxNZ[vxi_bot[0]] > 10 && /* assuredly signal */
9924               (pass < 1 ||
9925                   (SSQ->node_DelDot[in] < 0.3 &&
9926                    SSQ->node_DelDist[in]/SSQ->AvgDist < 0.5 ) ) ) {
9927             mask[in] = 0;
9928          }
9929 
9930          if (nn == 0 && mask[in]) {
9931             if (SSQ && SSQ->node_Conv[in] < -0.5) nn = 0;
9932                                        /* too concave, don't go down */
9933             else /* go down a little? */
9934                nn = 3;
9935          }
9936          if (nn) {
9937             ftr = travstep*nn;
9938             ftrv[0] = -ftr*dir[0];
9939             ftrv[1] = -ftr*dir[1];
9940             ftrv[2] = -ftr*dir[2];
9941             ++N_movers;
9942          }
9943       }
9944 
9945       SUMA_MEAN_STD_VEC(curedge, SO->N_Node, curemean, curestd, 1);
9946       SUMA_LHv("Smoothing round %d, surface edge mean=%f, std=%f\n",
9947                iter, curemean, curestd);
9948       /* Make sure no one node is an anchor holdout */
9949       for (in=0; in<SO->N_Node; ++in) {
9950          if (mask[in] == 0) { /* an anchored node */
9951             N_um = 0; /* number of unanchored neighbors */
9952             for (nn=0; nn<SO->FN->N_Neighb[in]; ++nn) {
9953                if (mask[SO->FN->FirstNeighb[in][nn]]) ++N_um;
9954             }
9955             if ((float)N_um/SO->FN->N_Neighb[in] > 0.75) {
9956                mask[in]=1;
9957                if (LocalHead && in == ndbg) {
9958                   SUMA_LHv("Node %d was anchored but now released %f\n",
9959                            in, (float)N_um/SO->FN->N_Neighb[in]);
9960                }
9961             }
9962          }
9963       }
9964 
9965       /* smooth displacement and add it */
9966       if (smdisp) {
9967          dispsm = SUMA_SmoothAttr_Neighb_Rec(disp, SO->N_Node*3, dispsm,
9968                                              SO->FN, 3, smdisp, mask, 0);
9969       } else {
9970          memcpy(dispsm, disp, 3*SO->N_Node*sizeof(float));
9971       }
9972       for (in=0; in<SO->N_Node; ++in) {
9973          if (in == ndbg) {
9974             SUMA_S_Notev("Post smth & mask manip. Node %d(%d) ftrv: %f %f %f\n",
9975                          in, mask[in], ftrv[0], ftrv[1], ftrv[2]);
9976          }
9977          xyz = SO->NodeList+3*in;
9978          if (mask[in]) {
9979             ftrv = dispsm+3*in;
9980          } else {
9981             ftrv = disp+3*in;
9982          }
9983          xyz[0] += ftrv[0];
9984          xyz[1] += ftrv[1];
9985          xyz[2] += ftrv[2];
9986       }
9987 
9988       /* Are we making a difference in this world? */
9989       larea=area;
9990       area=SUMA_Mesh_Area(SO, NULL, -1);
9991       darea = (area-larea)/area*100.0;
9992 
9993       /* write it out for debugging */
9994       if (LocalHead || ndbg>=0) {
9995          SUMA_S_Notev("Iteration %d, N_movers = %d, area = %f (Darea=%f)\n",
9996                iter, N_movers, area, darea);
9997          if (LocalHead) {
9998             THD_force_ok_overwrite(1) ;
9999             sprintf(sbuf,"shrink.02%d",iter);
10000             SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
10001                                     SUMA_GIFTI, SUMA_ASCII, NULL);
10002          }
10003       }
10004       ++iter;
10005       if (pass == 0 && SUMA_ABS(darea) < 0.05) {
10006          pass = 1;
10007          passiter=iter;
10008       }
10009       if (iter > itermax1 ||
10010             (pass && (iter - passiter) > 10 && SUMA_ABS(darea) < 0.001))
10011          stop = YUP;
10012 
10013       if (!stop) {
10014          /* A quick smoothing with anchors in place,
10015             Use it even if you are smoothing attributes up there.
10016             Otherwise you could get skirts at the bottom in
10017             certain cases. */
10018          SUMA_NN_GeomSmooth_SO(SO, mask, 0, 10);
10019       } else {
10020          /* A Taubin smooth */
10021          if (1) {
10022             SUMA_Taubin_Smooth_SO(SO, SUMA_EQUAL, 0.1, NULL, 0, 20);
10023          } else {
10024             SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
10025             SUMA_DIM_CENTER(SO);
10026          }
10027       }
10028       if (cs && cs->talk_suma && cs->Send) {
10029          if (!SUMA_SendToSuma (SO, cs, (void *)SO->NodeList,
10030                                SUMA_NODE_XYZ, 1)) {
10031             SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
10032                          "Communication halted.");
10033          }
10034       }
10035       if (pass) SSQ = SUMA_SphericalDeviations(SO, SSQ,"dist,dot,conv");
10036    }
10037 
10038    if (LocalHead) {
10039       SUMA_LHv("End of iterations N_movers = %d, area = %f\n",
10040             N_movers, SUMA_Mesh_Area(SO, NULL, -1));
10041       THD_force_ok_overwrite(1) ;
10042       SUMA_Save_Surface_Object_Wrap("shrink", NULL, SO,
10043                               SUMA_GIFTI, SUMA_ASCII, NULL);
10044    }
10045 
10046    if (iter >= itermax1) {
10047       SUMA_S_Note("Convergence criterion not reached. Check results.");
10048    }
10049 
10050    /* Now compute mask of voxels in surface and add to isin */
10051    {
10052       short *isin2=NULL;
10053       if (!(isin2 = SUMA_FindVoxelsInSurface (SO, vp, &N_in, 1, NULL))) {
10054          SUMA_S_Err("Failed to get insiders");
10055          SUMA_RETURN(NOPE);
10056       }
10057       for (vv=0; vv<DSET_NVOX(aset); ++vv) {
10058          if (isin[vv] > 1 && isin2[vv]>1) {
10059             isin[vv] += 10+isin2[vv];
10060          }
10061       }
10062       SUMA_free(isin2); isin2=NULL;
10063    }
10064 
10065 
10066    SUMA_Free_VolPar(vp); vp=NULL;
10067    if (SSQ) SSQ = SUMA_Free_SphereQuality(SSQ);
10068    if (rat) free(rat); rat=NULL;
10069    if (okrat) free(okrat); okrat=NULL;
10070    if (curedge) SUMA_free(curedge); curedge = NULL;
10071    if (dots) SUMA_free(dots); dots = NULL;
10072    if (mask) free(mask); mask = NULL;
10073    if (fedges) free(fedges); fedges = NULL;
10074    if (inedges) SUMA_free(inedges); inedges = NULL;
10075    if (inset) DSET_delete(inset); inset=NULL;
10076    if (rset) DSET_delete(rset); rset=NULL;
10077    if (disp) SUMA_free(disp); disp=NULL;
10078    if (dispsm) SUMA_free(dispsm); dispsm=NULL;
10079    if (prvec) SUMA_free(prvec); prvec=NULL;
10080    if (drvec) SUMA_free(drvec); drvec=NULL;
10081    SUMA_RETURN(YUP);
10082 }
10083 
10084 
10085 /*!
10086    Get a good mask of the whole head.
10087    hullvolthr is the expected volume of the head, it is
10088    used to get an approximate threshold.
10089 
10090    A value of 1.0 liters is good, but you can go down to 0.2liters
10091    if the dataset has a lot of junk it (lots of extra tissue,
10092    plenty of ghosting).
10093    Use 0.0 for some optimization, but that should not be
10094    necessary. See SUMA_ExtractHead_Hull for more info.
10095 
10096    The approach in this function is not all that robust.
10097    You should use SUMA_ExtractHead_RS instead.
10098 */
SUMA_ExtractHead(THD_3dim_dataset * iset,float hullvolthr,SUMA_COMM_STRUCT * cs)10099 SUMA_SurfaceObject *SUMA_ExtractHead(THD_3dim_dataset *iset,
10100                                      float hullvolthr,
10101                                      SUMA_COMM_STRUCT *cs)
10102 {
10103    static char FuncName[]={"SUMA_ExtractHead"};
10104    SUMA_SurfaceObject *SOh = NULL, *SOi = NULL;
10105    SUMA_HIST *hh=NULL;
10106    float newvol = 0.0, voxvol = 0.0, sklthr = 0.0;
10107    SUMA_Boolean LocalHead = NOPE;
10108 
10109    SUMA_ENTRY;
10110 
10111    if (!iset) SUMA_RETURN(SOh);
10112 
10113    if (!(SOh = SUMA_ExtractHead_Hull(iset,hullvolthr, &hh, cs))) {
10114       SUMA_S_Err("Failed to get HULL");
10115       SUMA_RETURN(SOi);
10116    }
10117    if (LocalHead) {
10118       THD_force_ok_overwrite(1);
10119       SUMA_Save_Surface_Object_Wrap("hull", NULL, SOh,
10120                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10121    }
10122    /* compute surface center, etc. */
10123    SUMA_SetSODims(SOh);
10124 
10125    /* Create a little icosahedron that fits inside the hull */
10126    SOi = SUMA_CreateIcosahedron(0.99*SOh->MinCentDist, 20, SOh->Center, "n",1);
10127    if (LocalHead) {
10128       THD_force_ok_overwrite(1);
10129       SUMA_Save_Surface_Object_Wrap("icos", NULL, SOi,
10130                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10131    }
10132    if (cs && cs->talk_suma && cs->Send) {
10133       SUMA_LH("Sending BrainHull2");
10134       SOi->VolPar = SUMA_VolParFromDset (iset);
10135       SOi->SUMA_VolPar_Aligned = YUP;
10136       SOi->AnatCorrect = 1;
10137       if (!SOi->State) {SOi->State = SUMA_copy_string("3dSkullStrip"); }
10138       if (!SOi->Group) {SOi->Group = SUMA_copy_string("3dSkullStrip"); }
10139       if (!SOi->Label) {SOi->Label = SUMA_copy_string("BrainHull2"); }
10140       if (!SOi->idcode_str) { SOi->idcode_str = UNIQ_hashcode("BrainHull2"); }
10141       SUMA_SendSumaNewSurface(SOi, cs);
10142    }
10143 
10144    /* Now inflate the icosahedron to make it fit the hull */
10145    SUMA_Set_SurfSmooth_NodeDebug(SUMA_getBrainWrap_NodeDbg());
10146    if (!SUMA_NN_GeomSmooth3_SO(SOi, NULL, 0, 50, 5, SOh, NULL, NULL,
10147                                LocalHead ? cs: NULL)) {
10148       SUMA_S_Err("Failed to inflate to anchor");
10149       SUMA_RETURN(SOi);
10150    }
10151    if (LocalHead) {
10152       THD_force_ok_overwrite(1);
10153       SUMA_Save_Surface_Object_Wrap("icosinfl", NULL, SOi,
10154                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10155    }
10156    /* Now drive ico mesh inwards until it hits the brightest voxels below
10157       To settle on brightest voxels threshold, compute area of icosahedron
10158       and consider a thickness of 10mm
10159    */
10160    newvol = fabs(SUMA_Mesh_Area(SOi, NULL, -1)*10);
10161    voxvol = SUMA_ABS(DSET_DX(iset)*DSET_DY(iset)*DSET_DZ(iset));
10162    sklthr = SUMA_val_at_count(hh, newvol/voxvol, 0, 1);
10163    SUMA_LHv("Skull threshold for contraction = %f, volume =%f liters\n",
10164             sklthr, newvol/1.0e6);
10165 
10166    /* for each node on the surface, if it is at the threshold or above,
10167       leave it in place, otherwise smooth, repeat*/
10168    SUMA_ShrinkSkullHull(SOi, iset, sklthr, 1, cs);
10169 
10170    if (SOh) SUMA_Free_Surface_Object(SOh); SOh = NULL;
10171    if (hh) SUMA_Free_hist(hh); hh = NULL;
10172    SUMA_RETURN(SOi);
10173 }
10174 
10175 /*!
10176    A head extration tool using the radial stats function.
10177    iset is the T1 volume
10178    urset is a pointer to the radial stats volume, should you
10179          want it back
10180 
10181 */
SUMA_ExtractHead_RS(THD_3dim_dataset * iset,THD_3dim_dataset ** urset,SUMA_COMM_STRUCT * cs)10182 SUMA_SurfaceObject *SUMA_ExtractHead_RS(THD_3dim_dataset *iset,
10183                                      THD_3dim_dataset **urset,
10184                                      SUMA_COMM_STRUCT *cs)
10185 {
10186    static char FuncName[]={"SUMA_ExtractHead_RS"};
10187    SUMA_SurfaceObject *SOh = NULL, *SOi = NULL;
10188    THD_3dim_dataset *rset=NULL, *mrset=NULL;
10189    SUMA_HIST *hh=NULL;
10190    float newvol = 0.0, voxvol = 0.0, *rat=NULL;
10191    int *ok=NULL, vv=0;
10192    short *sb=NULL;
10193    SUMA_Boolean LocalHead = NOPE;
10194 
10195    SUMA_ENTRY;
10196 
10197    if (!iset) SUMA_RETURN(SOh);
10198    rset = NULL;
10199    if (urset) {
10200       if (*urset) {
10201          SUMA_LH("Using user supplied radial stats");
10202          rset = *urset;
10203       }
10204    }
10205    if (!rset) {
10206       /* Compute the radial stats */
10207       SUMA_THD_Radial_HeadBoundary( iset, 0.0, NULL, NULL, &rset,
10208                                     1, 0.0, 0.0, 0, 0, NULL, NULL);
10209       if (LocalHead) {
10210          SUMA_S_Note("Writing rset");
10211          DSET_overwrite(rset);
10212       }
10213       if (urset) *urset = rset;
10214    }
10215 
10216    /* make a copy get rid of unwanted voxels */
10217    NEW_SHORTY(rset,1,FuncName,mrset);
10218    sb = DSET_BRICK_ARRAY(mrset,0);
10219    rat = THD_extract_to_float(2,rset);
10220    ok = THD_extract_to_int(3,rset);
10221    for (vv=0; vv<DSET_NVOX(iset); ++vv) {
10222       if (rat[vv]> 0.0 && ok[vv]) {
10223          if (rat[vv]>1.0) rat[vv]=1.0;
10224          sb[vv]=1000.0*rat[vv];
10225       } else {
10226          sb[vv]=0;
10227       }
10228    }
10229    EDIT_BRICK_FACTOR(mrset, 0, 1/1000.0);
10230    free(rat); rat=NULL; free(ok); ok=NULL;
10231    if (!(SOh = SUMA_Dset_ConvexHull(mrset, 0, 0.5, NULL))) {
10232       SUMA_S_Err("Failed to get HULL");
10233       SUMA_RETURN(SOi);
10234    }
10235 
10236    if (LocalHead) {
10237       THD_force_ok_overwrite(1);
10238       SUMA_Save_Surface_Object_Wrap("hull_rs", NULL, SOh,
10239                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10240    }
10241    /* compute surface center, etc. */
10242    SUMA_SetSODims(SOh);
10243 
10244    /* Create a little icosahedron that fits inside the hull */
10245    SOi = SUMA_CreateIcosahedron(0.99*SOh->MinCentDist, 20, SOh->Center, "n",1);
10246    if (LocalHead) {
10247       THD_force_ok_overwrite(1);
10248       SUMA_Save_Surface_Object_Wrap("icos", NULL, SOi,
10249                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10250    }
10251    if (cs && cs->talk_suma && cs->Send) {
10252       SUMA_LH("Sending BrainHull2");
10253       SOi->VolPar = SUMA_VolParFromDset (iset);
10254       SOi->SUMA_VolPar_Aligned = YUP;
10255       SOi->AnatCorrect = 1;
10256       if (!SOi->State) {SOi->State = SUMA_copy_string("3dSkullStrip"); }
10257       if (!SOi->Group) {SOi->Group = SUMA_copy_string("3dSkullStrip"); }
10258       if (!SOi->Label) {SOi->Label = SUMA_copy_string("BrainHull2_RS"); }
10259       if (!SOi->idcode_str) { SOi->idcode_str = UNIQ_hashcode("BrainHull2"); }
10260       SUMA_SendSumaNewSurface(SOi, cs);
10261    }
10262 
10263    /* Now inflate the icosahedron to make it fit the hull */
10264    SUMA_Set_SurfSmooth_NodeDebug(SUMA_getBrainWrap_NodeDbg());
10265    if (!SUMA_NN_GeomSmooth3_SO(SOi, NULL, 0, 50, 5, SOh, NULL, NULL,
10266                                LocalHead ? cs: NULL)) {
10267       SUMA_S_Err("Failed to inflate to anchor");
10268       SUMA_RETURN(SOi);
10269    }
10270    if (LocalHead) {
10271       THD_force_ok_overwrite(1);
10272       SUMA_Save_Surface_Object_Wrap("icosinfl", NULL, SOi,
10273                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10274    }
10275 
10276    /* Get outer surface */
10277    SUMA_LH("hull shrinkage");
10278    SUMA_ShrinkSkullHull_RS(SOi, iset, rset, 0.5, cs);
10279 
10280    /* Shrink the surface to get at the brain */
10281    SUMA_LH("brain shrinkage");
10282    SUMA_ShrinkHeadSurf_RS(SOi, iset, rset, NULL, cs);
10283 
10284    if (SOh) SUMA_Free_Surface_Object(SOh); SOh = NULL;
10285    if (hh) SUMA_Free_hist(hh); hh = NULL;
10286    if (mrset) DSET_delete(mrset); mrset=NULL;
10287    if (urset) { *urset = rset; rset = NULL; }
10288    if (rset) DSET_delete(rset);  rset=NULL;
10289 
10290    SUMA_RETURN(SOi);
10291 }
10292 
SUMA_Mask_Skin(THD_3dim_dataset * iset,int ld,int smooth_final,int shrink_mode,SUMA_COMM_STRUCT * cs)10293 SUMA_SurfaceObject *SUMA_Mask_Skin(THD_3dim_dataset *iset, int ld,
10294                                     int smooth_final, int shrink_mode,
10295                                     SUMA_COMM_STRUCT *cs)
10296 {
10297    static char FuncName[]={"SUMA_Mask_Skin"};
10298    SUMA_SurfaceObject *SOh = NULL, *SOi = NULL;
10299    SUMA_HIST *hh=NULL;
10300    float newvol = 0.0, voxvol = 0.0, *rat=NULL;
10301    int *ok=NULL, vv=0;
10302    short *sb=NULL;
10303    SUMA_Boolean LocalHead = NOPE;
10304 
10305    SUMA_ENTRY;
10306 
10307    if (!iset) SUMA_RETURN(SOh);
10308 
10309    if (!(SOh = SUMA_Dset_ConvexHull(iset, 0, 1.0, NULL))) {
10310       SUMA_S_Err("Failed to get HULL");
10311       SUMA_RETURN(SOi);
10312    }
10313 
10314    if (ld < 1) ld = 20;
10315 
10316    if (LocalHead) {
10317       THD_force_ok_overwrite(1);
10318       SUMA_Save_Surface_Object_Wrap("hull_rs", NULL, SOh,
10319                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10320    }
10321    /* compute surface center, etc. */
10322    SUMA_SetSODims(SOh);
10323 
10324    /* Create a little icosahedron that fits inside the hull */
10325    SOi = SUMA_CreateIcosahedron(0.99*SOh->MinCentDist, ld, SOh->Center, "n",1);
10326    if (LocalHead) {
10327       THD_force_ok_overwrite(1);
10328       SUMA_Save_Surface_Object_Wrap("icos", NULL, SOi,
10329                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10330    }
10331    if (cs && cs->talk_suma && cs->Send) {
10332       SUMA_LH("Sending BrainHull2");
10333       SOi->VolPar = SUMA_VolParFromDset (iset);
10334       SOi->SUMA_VolPar_Aligned = YUP;
10335       SOi->AnatCorrect = 1;
10336       if (!SOi->State) {SOi->State = SUMA_copy_string("3dSkullStrip"); }
10337       if (!SOi->Group) {SOi->Group = SUMA_copy_string("3dSkullStrip"); }
10338       if (!SOi->Label) {SOi->Label = SUMA_copy_string("BrainHull2_RS"); }
10339       if (!SOi->idcode_str) { SOi->idcode_str = UNIQ_hashcode("BrainHull2"); }
10340       SUMA_SendSumaNewSurface(SOi, cs);
10341    }
10342 
10343    /* Now inflate the icosahedron to make it fit the hull */
10344    SUMA_Set_SurfSmooth_NodeDebug(SUMA_getBrainWrap_NodeDbg());
10345    if (!SUMA_NN_GeomSmooth3_SO(SOi, NULL, 0, 50, 5, SOh, NULL, NULL,
10346                                LocalHead ? cs: NULL)) {
10347       SUMA_S_Err("Failed to inflate to anchor");
10348       SUMA_RETURN(SOi);
10349    }
10350    if (LocalHead) {
10351       THD_force_ok_overwrite(1);
10352       SUMA_Save_Surface_Object_Wrap("icosinfl", NULL, SOi,
10353                                  SUMA_GIFTI, SUMA_ASCII, NULL);
10354    }
10355 
10356    if (shrink_mode) {
10357       float uo_dist[2]={11, 2};
10358       /* Shrink */
10359       SUMA_LH("hull shrinkage");
10360       SUMA_ShrinkSkullHull2Mask(SOi, iset, 0.0, smooth_final,
10361                                 uo_dist, shrink_mode > 1 ? 1:0 ,cs);
10362       if (LocalHead) {
10363          THD_force_ok_overwrite(1);
10364          SUMA_Save_Surface_Object_Wrap("icoshead", NULL, SOi,
10365                                     SUMA_GIFTI, SUMA_ASCII, NULL);
10366       }
10367    }
10368 
10369    if (SOh) SUMA_Free_Surface_Object(SOh); SOh = NULL;
10370    if (hh) SUMA_Free_hist(hh); hh = NULL;
10371 
10372    SUMA_RETURN(SOi);
10373 }
10374 
10375 
10376 static char labels[7][64]={
10377                            "Out",
10378                            "Out, In box",
10379                            "Out, Touching",
10380                            "Contains Node",
10381                            "In, Touching",
10382                            "In, In Box",
10383                            "In" };
10384 static char labels_slow[3][64]={
10385                            "Out",
10386                            "Out, In box",
10387                            "In" };
10388 static int keys[7]={0, 1, 2, 3, 4, 5, 6};
10389 static int N_labels = 7;
10390 static int N_labels_slow = 3;
10391 
10392 
10393 /*!
10394    A convenience function to call SUMA_FindVoxelsInSurface* functions
10395    meth == 1: SUMA_FindVoxelsInSurface_SLOW
10396            0: SUMA_FindVoxelsInSurface
10397            2: SUMA_FindVoxelsInSurface, voxels touching surface only
10398    maskonly == 1: 0/1 output
10399                2: output reflecting relative position of voxel to surface
10400    You can use only one of iset, or (vp and vpname)
10401 */
SUMA_Dset_FindVoxelsInSurface(SUMA_SurfaceObject * SO,THD_3dim_dataset * iset,SUMA_VOLPAR * vp,char * vpname,char * prefix,int meth,int mask_only)10402 THD_3dim_dataset *SUMA_Dset_FindVoxelsInSurface(SUMA_SurfaceObject *SO,
10403                      THD_3dim_dataset *iset,
10404                      SUMA_VOLPAR *vp, char *vpname,
10405                      char *prefix, int meth, int mask_only)
10406 {
10407    static char FuncName[]={"SUMA_Dset_FindVoxelsInSurface"};
10408    THD_3dim_dataset *dset = NULL;
10409    short *isin = NULL;
10410    int N_in = 0, i=0;
10411    float * isin_float = NULL;
10412    char **lblv = NULL;
10413    SUMA_FileName NewName;
10414    SUMA_FORM_AFNI_DSET_STRUCT *OptDs = NULL;
10415 
10416    SUMA_ENTRY;
10417 
10418    if (!SO) SUMA_RETURN(NULL);
10419    if (iset && vp) {
10420       SUMA_S_Err("iset and vp, no good");
10421       SUMA_RETURN(NULL);
10422    }
10423    if (!iset && (!vp || !vpname))  {
10424       SUMA_S_Err("both vp and vpname must be set if iset=NULL");
10425       SUMA_RETURN(NULL);
10426    }
10427    if (iset) vp = SUMA_VolParFromDset(iset);
10428 
10429    switch (meth) {
10430       default:
10431          SUMA_S_Errv("Bad meth %d\n", meth);
10432          SUMA_RETURN(NULL);
10433       case 0:
10434          isin = SUMA_FindVoxelsInSurface (SO, vp, &N_in, 1, NULL);
10435          break;
10436       case 2:
10437          isin = SUMA_FindVoxelsInSurface (SO, vp, &N_in, -1, NULL);
10438          break;
10439       case 1:
10440          isin = SUMA_FindVoxelsInSurface_SLOW (SO, vp, &N_in, 0);
10441          break;
10442    }
10443    if (!isin) {
10444       SUMA_S_Err("No voxels in surface");
10445       SUMA_RETURN(NULL);
10446    }
10447 
10448    OptDs = SUMA_New_FormAfniDset_Opt();
10449    NewName = SUMA_StripPath(prefix ? prefix:FuncName);
10450    OptDs->prefix = SUMA_copy_string(NewName.FileName);
10451    OptDs->prefix_path = SUMA_copy_string(NewName.Path);
10452    if (iset) OptDs->mset = iset;
10453    else OptDs->master = SUMA_copy_string(vpname);
10454    OptDs->datum = MRI_byte;
10455    OptDs->full_list = 1;
10456 
10457    isin_float = (float *)SUMA_malloc(sizeof(float)*vp->nx*vp->ny*vp->nz);
10458    if (!isin_float) {
10459       SUMA_SL_Crit("Failed to allocate");
10460       exit(1);
10461    }
10462 
10463    if (mask_only == 1) {
10464       for (i=0; i<vp->nx*vp->ny*vp->nz; ++i) {
10465          if (isin[i] > 1) isin_float[i] = 1.0;
10466          else isin_float[i] = 0.0;
10467       }
10468    } else {
10469       for (i=0; i<vp->nx*vp->ny*vp->nz; ++i) isin_float[i] = (float)isin[i];
10470    }
10471    dset = SUMA_FormAfnidset (NULL, isin_float, vp->nx*vp->ny*vp->nz, OptDs);
10472    if (!dset) {
10473       SUMA_SL_Err("Failed to create output dataset!");
10474    } else if (!mask_only) {
10475       if (meth == 0) {
10476          lblv = (char **)SUMA_calloc(N_labels, sizeof(char*));
10477          for (i=0; i<N_labels; ++i) lblv[i] = SUMA_copy_string(labels[i]);
10478          if (!SUMA_SetDsetLabeltable(dset, lblv, N_labels, keys)) {
10479             SUMA_S_Err("Failed to add labels");
10480          }
10481          for (i=0; i<N_labels; ++i) SUMA_free(lblv[i]);
10482       } else if (meth == 1) {
10483          lblv = (char **)SUMA_calloc(N_labels_slow, sizeof(char*));
10484          for (i=0; i<N_labels_slow; ++i)
10485             lblv[i] = SUMA_copy_string(labels_slow[i]);
10486          if (!SUMA_SetDsetLabeltable(dset, lblv, N_labels_slow, keys)) {
10487             SUMA_S_Err("Failed to add labels");
10488          }
10489          for (i=0; i<N_labels_slow; ++i) SUMA_free(lblv[i]);
10490       }
10491       SUMA_free(lblv); lblv=NULL;
10492    }
10493 
10494    SUMA_free(isin_float); isin_float = NULL;
10495    SUMA_free(isin); isin = NULL;
10496    if (iset && vp) SUMA_Free_VolPar(vp); vp = NULL;
10497    if (OptDs) { OptDs->mset = NULL; OptDs = SUMA_Free_FormAfniDset_Opt(OptDs);  }
10498 
10499    SUMA_RETURN(dset);
10500 }
10501 
SUMA_dset_hist_equalize(THD_3dim_dataset * din,int sb,byte * cmask,SUMA_HIST * hh)10502 THD_3dim_dataset *SUMA_dset_hist_equalize(THD_3dim_dataset *din,
10503                                           int sb, byte *cmask, SUMA_HIST *hh)
10504 {
10505    static char FuncName[]={"SUMA_dset_hist_equalize"};
10506    float cdfv, lvls;
10507    float fac, *fv=NULL, fout;
10508    int i;
10509    char labout[256]={""};
10510    THD_3dim_dataset *dout=NULL;
10511    SUMA_Boolean LocalHead = NOPE;
10512 
10513    SUMA_ENTRY;
10514 
10515    if (!din || sb < 0 || sb >= DSET_NVALS(din)) SUMA_RETURN(dout);
10516    if (!hh) SUMA_RETURN(dout);
10517 
10518    if (hh->max < 255) lvls = 255;
10519    else lvls = hh->max;
10520 
10521    fac = lvls/(float)((DSET_NVOX(din) - hh->cdfmin));
10522 
10523    if (!(fv = THD_extract_to_float(sb, din))) {
10524       SUMA_S_Errv("Failed to extract sub-brick %d\n", sb);
10525       SUMA_RETURN(dout);
10526    }
10527    if (cmask) {
10528       for (i=0; i<DSET_NVOX(din); ++i) {
10529          if (cmask[i]) {
10530             cdfv = SUMA_hist_value(hh, fv[i], "cdf");
10531             fout = SUMA_ROUND((cdfv-hh->cdfmin)*fac);
10532             #if 0
10533             if (LocalHead) {
10534                SUMA_LH(
10535                   "fin = %f, fout = %f, fac = %f, cdfmin=%f, cdfv=%f",
10536                               fv[i], fout, fac, hh->cdfmin, cdfv);
10537 
10538             }
10539             #endif
10540             fv[i] = fout;
10541          } else fv[i] = 0.0;
10542       }
10543    } else {
10544       for (i=0; i<DSET_NVOX(din); ++i) {
10545          cdfv = SUMA_val_at_count(hh, fv[i], 0, 0);
10546          fout = SUMA_ROUND((cdfv-hh->cdfmin)*fac);
10547          if (i==5295) {
10548             fprintf(stderr,"fin = %f, fout = %f, fac = %f, cdfmin=%f, cdfv=%f",
10549                            fv[i], fout, fac, hh->cdfmin, cdfv);
10550 
10551          }
10552          fv[i] = fout;
10553       }
10554    }
10555 
10556    dout = EDIT_empty_copy(din);
10557    EDIT_substitute_brick( dout , 0 , DSET_BRICK_TYPE(din, sb) , NULL ) ;
10558    fac = EDIT_coerce_autoscale_new( DSET_NVOX(dout), MRI_float, fv,
10559              DSET_BRICK_TYPE(din, sb), DSET_BRICK_ARRAY(dout,0));
10560    if (fac > 0.0f) {
10561       fac = 1.0 / fac;
10562    } else fac = 0.0;
10563    snprintf(labout,255,"EQ.%s",DSET_BRICK_LABEL(din, sb));
10564    EDIT_BRICK_LABEL (dout, 0, labout);
10565    EDIT_BRICK_FACTOR (dout, 0, fac);
10566 
10567    if (DSET_BRICK_TYPE(din,sb) != MRI_float) free(fv); fv = NULL;
10568 
10569 
10570    SUMA_RETURN(dout);
10571 }
10572