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