1 /*USE This sample to start writing standalone programs.
2 Change RetinoMap to the program name of your choosing.
3 */
4 #include "SUMA_suma.h"
5 
SUMA_LocalRetinoGrad(SUMA_SurfaceObject * SO,int node,SUMA_DSET * decc,SUMA_DSET * dpol,float * pNodeList,float Gre[2],float Grp[2],int NodeDBG)6 float SUMA_LocalRetinoGrad (SUMA_SurfaceObject *SO,
7                                    int node,
8                                    SUMA_DSET *decc,
9                                    SUMA_DSET *dpol,
10                                    float *pNodeList,
11                                    float Gre[2],
12                                    float Grp[2],
13                                    int NodeDBG)
14 {
15    static char FuncName[]={"SUMA_LocalRetinoGrad"};
16    matrix X, XtXiXt;
17    vector Y, B;
18    SUMA_DSET *dset=NULL;
19    int in=0, in3=0, k;
20    float vfr = 0.0;
21    char *tp="not set";
22 
23    SUMA_ENTRY;
24 
25    /* phi(x,y) = b0 x + b1 y + const for each of the dsets */
26    /* PHI = X B + e */
27    matrix_initialize(&X);
28    matrix_create(SO->FN->N_Neighb[node]+1, 3, &X);
29    matrix_initialize(&XtXiXt);
30    matrix_create(SO->FN->N_Neighb[node]+1, 3, &XtXiXt);
31    vector_initialize(&Y);
32    vector_create(SO->FN->N_Neighb[node]+1, &Y);
33    vector_initialize(&B);
34    vector_create(3, &B);
35    for (in=0; in<=SO->FN->N_Neighb[node]; ++in) {
36      in3=3*in;
37      X.elts[in][0] = pNodeList[in3  ];
38      X.elts[in][1] = pNodeList[in3+1];
39      X.elts[in][2] = 1.0;
40    }
41 
42    for (k=0; k<2; ++k) {
43       if (k==0) {
44          dset = decc;
45          tp = "eccentricity";
46       } else {
47          dset = dpol;
48          tp = "polar";
49       }
50       for (in=0; in<SO->FN->N_Neighb[node]; ++in) {
51          Y.elts[in] = SUMA_GetDsetNodeValInCol2(dset, 0,
52                                  SO->FN->FirstNeighb[node][in], -1);
53       }
54          Y.elts[SO->FN->N_Neighb[node]]
55                      = SUMA_GetDsetNodeValInCol2(dset, 0, node, -1);
56 
57       matrix_psinv(X, NULL, &XtXiXt);
58       vector_multiply(XtXiXt, Y, &B);
59 
60       if (node == NodeDBG) {
61          SUMA_S_Notev("Gradient computations for node %d, dset %d/2 (%s)\n",
62                      NodeDBG, k+1, k ? "pol":"ecc");
63          SUMA_S_Note("Nodes");
64          for (in=0; in<SO->FN->N_Neighb[node]; ++in)
65             fprintf(stderr,"%d\n", SO->FN->FirstNeighb[node][in]);
66          fprintf(stderr,"%d\n",node);
67          SUMA_S_Note("X");
68          matrix_print(X);
69          SUMA_S_Note("XtXiXt");
70          matrix_print(XtXiXt);
71          SUMA_S_Notev("Y, %s\n", tp);
72          vector_print(Y);
73          SUMA_S_Notev("B, %s (degrees/mm)\n", tp);
74          vector_print(B);
75       }
76 
77       if (k==0) {
78          Gre[0] = B.elts[0];
79          Gre[1] = B.elts[1];
80       } else {
81          Grp[0] = B.elts[0];
82          Grp[1] = B.elts[1];
83       }
84    }
85 
86    matrix_destroy(&X);
87    matrix_destroy(&XtXiXt);
88    vector_destroy(&Y);
89    vector_destroy(&B);
90 
91    /* the VFR */
92 
93    vfr = Grp[0]*Gre[1]-Grp[1]*Gre[0];
94 
95    /* block ridiculously large values */
96    if (vfr < -100) vfr = -100.0;
97    else if (vfr > 100) vfr = 100.0;
98 
99    SUMA_RETURN(vfr);
100 }
101 
SUMA_RetinoMap(SUMA_SurfaceObject * SO,SUMA_DSET * decc,SUMA_DSET * dpol,byte * nmask,int NodeDBG,char * pref)102 SUMA_DSET * SUMA_RetinoMap (SUMA_SurfaceObject *SO,
103                             SUMA_DSET * decc, SUMA_DSET *dpol,
104                             byte *nmask, int NodeDBG, char *pref)
105 {
106    static char FuncName[]={"SUMA_RetinoMap"};
107    SUMA_DSET *dout=NULL;
108    int i=0, i3=0, in=0, neigh3, in3, j;
109    float *pNodeList=NULL, fv[3], **fm=NULL, *vfr=NULL, *mxxc=NULL, xc1, xc2;
110    float Gp[2]={0.0, 0.0}, Ge[2]={0.0, 0.0};
111    double Eq[4], proj[3], U[3], d,
112           P2[2][3]={ {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0} } ;
113    char *prefix=NULL;
114    FILE *ffgp=NULL, *ffge=NULL;
115    SUMA_Boolean LocalHead = NOPE;
116 
117    SUMA_ENTRY;
118 
119    if (!SO || !SO->NodeNormList || !SO->FN) {
120       SUMA_S_Err("Surface null or not ready for prime time");
121       goto CLEANOUT;
122    }
123    if (!dpol || !decc) {
124       SUMA_S_Err("NULL input");
125       goto CLEANOUT;
126    }
127 
128    if (SDSET_VECNUM(dpol) == 3 && SDSET_VECNUM(decc) == 3) {
129       /* have correlations in these datasets at sb # 3 */
130       if (!(mxxc = (float *)SUMA_calloc(SO->N_Node, sizeof(float)))) {
131          SUMA_S_Err("Failed to allocate");
132          goto CLEANOUT;
133       }
134    }
135 
136    if (!(pNodeList =  (float *)
137             SUMA_calloc((SO->FN->N_Neighb_max+1)*SO->NodeDim, sizeof(float))) ||
138        !(fm = (float **)SUMA_allocate2D(4,4,sizeof(float))) ||
139        !(vfr = (float *)SUMA_calloc(SO->N_Node, sizeof(float)))  ){
140       SUMA_S_Err("Failed to allocate");
141       goto CLEANOUT;
142    }
143 
144    if (pref) {
145       prefix = SUMA_RemoveDsetExtension_s(pref,SUMA_NO_DSET_FORMAT);
146       prefix = SUMA_append_replace_string(prefix, "",".",1);
147    } else {
148       prefix = SUMA_copy_string("");
149    }
150 
151    for (i=0; i<SO->N_Node; ++i) {   if (!nmask || nmask[i]) {
152       if (!(i % 10000)) fprintf(SUMA_STDERR,".");
153       i3 = SO->NodeDim *i;
154       /* tangent plane at node */
155       SUMA_PLANE_NORMAL_POINT((SO->NodeNormList+i3),
156                               (SO->NodeList+i3),
157                               Eq);
158       for (in=0; in<SO->FN->N_Neighb[i]; ++in) {
159          in3 = 3*in;
160          neigh3 = SO->NodeDim *SO->FN->FirstNeighb[i][in];
161          SUMA_PROJECT_ONTO_PLANE(Eq, (SO->NodeList+neigh3), proj);
162          /* move projection so that it is equidistant from origin */
163          d = 0.0;
164          for (j=0; j<SO->NodeDim; ++j) {
165             U[j] = proj[j] - SO->NodeList[i3+j];
166             d += U[j]*U[j];
167          }
168          d = sqrt(d);
169          if (d != 0.0f) {
170             for (j=0; j<SO->NodeDim; ++j) U[j] /= d;
171          } else {
172             SUMA_S_Warn("zero edge length!");
173          }
174          SUMA_POINT_AT_DISTANCE_NORM(U, (SO->NodeList+i3), d, P2);
175          for (j=0; j<SO->NodeDim; ++j) pNodeList[in3+j] = P2[0][j];
176       }
177       /* add the dear node */
178       pNodeList[3*SO->FN->N_Neighb[i]  ] = SO->NodeList[i3  ];
179       pNodeList[3*SO->FN->N_Neighb[i]+1] = SO->NodeList[i3+1];
180       pNodeList[3*SO->FN->N_Neighb[i]+2] = SO->NodeList[i3+2];
181 
182       if (i == NodeDBG) {
183          FILE *ff=NULL;
184          char stmp[256];
185          SUMA_S_Notev("Debug files will be written for node %d\n",i);
186          sprintf(stmp,"%snorm.%d.1D.do", prefix, i);
187          ff = fopen(stmp,"w");
188          fprintf(ff,"#node-based_vectors\n");
189          fprintf(ff,"%d %f %f %f 1 0 0 1\n",
190                      i, SO->EL->AvgLe*SO->NodeNormList[i3],
191                         SO->EL->AvgLe*SO->NodeNormList[i3+1],
192                         SO->EL->AvgLe*SO->NodeNormList[i3+2]);
193          fclose (ff);
194          sprintf(stmp,"%sinit.%d.1D.do", prefix,i);
195          ff = fopen(stmp,"w");
196          fprintf(SUMA_STDOUT,"Story for node %d \n", i);
197          fprintf(ff,"#spheres\n");
198          for (in=0; in<SO->FN->N_Neighb[i]; ++in) {
199             neigh3 = SO->NodeDim *SO->FN->FirstNeighb[i][in];
200             fprintf(ff,"#%d/%d Neighb[%d]=%d, projected to:\n",
201                         in,SO->FN->N_Neighb[i], i,
202                         SO->FN->FirstNeighb[i][in]);
203              fprintf(ff,"%f %f %f 1 0 0 1\n",
204                     SO->NodeList[neigh3  ],
205                     SO->NodeList[neigh3+1],
206                     SO->NodeList[neigh3+2]);
207          }
208          fclose (ff);
209          sprintf(stmp,"%sproj.%d.1D.do", prefix,i);
210          ff = fopen(stmp,"w");
211          fprintf(ff,"#spheres\n");
212          for (in=0; in<SO->FN->N_Neighb[i]; ++in) {
213             in3=3*in;
214             fprintf(ff,"#%d/%d Neighb[%d]=%d, projected to:\n",
215                         in,SO->FN->N_Neighb[i], i,
216                         SO->FN->FirstNeighb[i][in]);
217             fprintf(ff,"%f %f %f 0 1 0 1\n",
218                     pNodeList[in3],pNodeList[in3+1],pNodeList[in3+2]);
219          }
220          fclose (ff);
221       }
222       /* pNodeList contains all the neighbors projected onto the tangent plane */
223       /* Next, we rotate that plane so that it is parallel to the horiz. plane */
224       fv[0]=fv[1]=0.0; fv[2]=1.0; /* Z axis */
225       if (!SUMA_FromToRotation ((SO->NodeNormList+i3),fv,  fm)) {
226             SUMA_S_Err("Failed to get rotation");
227          goto CLEANOUT;
228       }
229       /* Apply rotation to all elements, including the dear node */
230       for (in=0; in<=SO->FN->N_Neighb[i]; ++in) {
231          in3=3*in;
232          proj[0] = pNodeList[in3  ]*fm[0][0] +
233                    pNodeList[in3+1]*fm[0][1] +
234                    pNodeList[in3+2]*fm[0][2] ;
235          proj[1] = pNodeList[in3  ]*fm[1][0] +
236                    pNodeList[in3+1]*fm[1][1] +
237                    pNodeList[in3+2]*fm[1][2] ;
238          proj[2] = pNodeList[in3  ]*fm[2][0] +
239                    pNodeList[in3+1]*fm[2][1] +
240                    pNodeList[in3+2]*fm[2][2] ;
241          pNodeList[in3  ] = proj[0];
242          pNodeList[in3+1] = proj[1];
243          pNodeList[in3+2] = proj[2];
244       }
245       if (i == NodeDBG) {
246          FILE *ff=NULL;
247          char stmp[256];
248          sprintf(stmp,"%srotmat.%d.1D", prefix,  i);
249          ff = fopen(stmp,"w");
250          for (in=0; in<3; ++in)
251             fprintf(ff,"%f %f %f\n",fm[in][0], fm[in][1], fm[in][2]);
252          fclose (ff);
253          sprintf(stmp,"%srotproj.%d.1D.do", prefix, i);
254          ff = fopen(stmp,"w");
255          fprintf(ff,"#spheres\n");
256          for (in=0; in<=SO->FN->N_Neighb[i]; ++in) {
257             in3=3*in;
258             fprintf(ff,"#%d/%d Neighb[%d]=%d, rotation of projection:\n",
259                         in,SO->FN->N_Neighb[i], i,
260                         SO->FN->FirstNeighb[i][in]);
261             fprintf(ff,"%f %f %f 0 0 1 1\n",
262                     pNodeList[in3],pNodeList[in3+1],pNodeList[in3+2]);
263          }
264          fclose (ff);
265       }
266       /* Now you want to get the local gradients */
267       vfr[i] = SUMA_LocalRetinoGrad(SO, i, decc, dpol, pNodeList,
268                                  Ge, Gp, NodeDBG);
269 
270       /* get a max correlation from two dsets */
271       if (mxxc) {
272          xc1 = SUMA_GetDsetNodeValInCol2(decc, 2, i, -1);
273          xc2 = SUMA_GetDsetNodeValInCol2(dpol, 2, i, -1);
274          if (xc2 > xc1) mxxc[i] = xc2; else mxxc[i] =xc1;
275       }
276 
277       if (i==NodeDBG) {
278          SUMA_S_Notev("Node %d: Ge = [%f %f], Gp = [%f %f], VFR=%f\n",
279                         i, Ge[0], Ge[1], Gp[0], Gp[1], vfr[i]);
280       }
281 
282       #if 0 /* extreme debugging, output gradient estimates into dset */
283          if (i==0) {
284             char stmp[256];
285             SUMA_S_Note("DBG Stuff");
286             sprintf(stmp,"%sgradpol.1D.dset", prefix);
287             ffgp = fopen(stmp, "w");
288             sprintf(stmp,"%sgradecc.1D.dset", prefix);
289             ffge = fopen(stmp, "w");
290          }
291          fprintf(ffgp, "%f %f %f\n",
292                Gp[0], Gp[1],
293                sqrtf(Gp[0]*Gp[0]+Gp[1]*Gp[1]));
294          fprintf(ffge, "%f %f %f\n",
295                Ge[0], Ge[1],
296                sqrtf(Ge[0]*Ge[0]+Ge[1]*Ge[1]));
297          if (i==SO->N_Node) {
298             fclose(ffgp); ffgp=NULL;
299             fclose(ffge); ffge=NULL;
300          }
301       #endif
302 
303    } }
304 
305    fprintf(SUMA_STDERR,"\n");
306 
307    SUMA_LH("Hard work over");
308    if (!(dout = SUMA_CreateDsetPointer(
309                   "dodo", SUMA_NODE_BUCKET, NULL,
310                   SDSET_IDMDOM(decc),
311                   SO->N_Node  ) ) ){
312       SUMA_S_Err("Failed to create dout");
313       goto CLEANOUT;
314    }else if (!SUMA_PopulateDsetNodeIndexNel(dout,0)) {
315       SUMA_S_Err("Failed populating node indices");
316       goto CLEANOUT;
317    }
318 
319    if (!SUMA_AddDsetNelCol(dout, "VFR", SUMA_NODE_VFR, vfr, NULL, 1)) {
320       SUMA_S_Err("Failed to add column");
321       SUMA_FreeDset(dout); dout=NULL;
322       goto CLEANOUT;
323    }
324    if (mxxc) {
325       /* Not sure if we're getting power ratios or correlation coefficients
326          Just use generic term 'Thresh.'*/
327       if (!SUMA_AddDsetNelCol(dout, "Max.Thresh.",
328             SUMA_NODE_XCORR, mxxc, NULL, 1)) {
329          SUMA_S_Err("Failed to add column");
330          SUMA_FreeDset(dout); dout=NULL;
331          goto CLEANOUT;
332       }
333    }
334 
335    CLEANOUT:
336    SUMA_LH("CLEANUP");
337    if (prefix) SUMA_free(prefix); prefix=NULL;
338    if (pNodeList) SUMA_free(pNodeList); pNodeList=NULL;
339    if (fm) SUMA_free2D((char **)fm, 2); fm = NULL;
340    if (vfr) SUMA_free(vfr); vfr = NULL;
341    if (mxxc) SUMA_free(mxxc); mxxc = NULL;
342 
343    SUMA_LH("ADIOS");
344    SUMA_RETURN(dout);
345 }
346 
usage_RetinoMap(SUMA_GENERIC_ARGV_PARSE * ps)347 void usage_RetinoMap (SUMA_GENERIC_ARGV_PARSE *ps)
348 {
349       static char FuncName[]={"usage_RetinoMap"};
350       char * s = NULL, *sio=NULL, *st = NULL, *sts = NULL;
351       int i;
352       s = SUMA_help_basics();
353       sio  = SUMA_help_IO_Args(ps);
354       printf ( "\n"
355 "Usage: SurfRetinoMap <SURFACE> <-input POLAR ECCENTRICITY>\n"
356 "                 [<-prefix PREFIX>] [<-node_dbg NODE>]\n"
357 "      \n"
358 "  <SURFACE> : Surface on which distances are computed.\n"
359 "              (For option's syntax, see \n"
360 "              'Specifying input surfaces' section below).\n"
361 "  <-input POLAR ECCENTRICITY>: Retinotopic datasets.\n"
362 "              POLAR is the polar angle dataset.\n"
363 "              ECCENTRICITY is the eccentricity angle dataset.\n"
364 "     Those datasets are produced by 3dRetinoPhase.\n"
365 "     If the datasets are produced outside of 3dRetinoPhase, note that\n"
366 "     The angle data is to be in the [0] sub-brick, and a thresholding\n"
367 "     parameter, if any, is to be in the [2] sub-brick.\n"
368 "\n"
369 "  [<-node_dbg NODE>]: Index of node number for which debugging\n"
370 "                    information is output.\n"
371 "  [<-prefix PREFIX>]: Prefix for output datasets.\n"
372 "                      The program outputs the Visual Field Ratio (VFR),\n"
373 "                      the sign of which is used to differentiate between\n"
374 "                      adjacent areas. \n"
375 "\n"
376 "     VFR computations based on paper by Warnking et al. Neuroimage 17, (2002)\n"
377 "            'FMRI Retinotopic Mapping - Step by Step\n"
378 "\n"
379 "A note on the output thresholding sub-brick:\n"
380 "  In addition to VFR, you get a maximum threshold\n"
381 "     sub-brick which retains the highest threshold at\n"
382 "     each node in input datasets. This thresholding \n"
383 "     parameter is like a union mask of input data \n"
384 "     thresholded at the same level.\n"
385 "  The signficance value is not provided on purpose.\n"
386 "     I don't know of a good way to compute it, but \n"
387 "     it serves its function or wedding out low SNR nodes.\n"
388 "\n"
389 "%s"
390 "%s"
391                "\n",
392                ps->hverb > 1 ? sio:"Use -help for more detail.\n",
393                ps->hverb > 1 ? s:"");
394       if (s) SUMA_free(s); s = NULL;
395       if (st) SUMA_free(st); st = NULL;
396       if (sio) SUMA_free(sio); sio = NULL;
397       s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL;
398       printf("       Ziad S. Saad SSCC/NIMH/NIH saadz@mail.nih.gov     \n");
399       return;
400 }
401 
402 SUMA_GENERIC_PROG_OPTIONS_STRUCT *
SUMA_RetinoMap_ParseInput(char * argv[],int argc,SUMA_GENERIC_ARGV_PARSE * ps)403       SUMA_RetinoMap_ParseInput(
404                      char *argv[], int argc, SUMA_GENERIC_ARGV_PARSE *ps)
405 {
406    static char FuncName[]={"SUMA_RetinoMap_ParseInput"};
407    SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt=NULL;
408    int kar;
409    SUMA_Boolean brk;
410    SUMA_Boolean LocalHead = NOPE;
411 
412    SUMA_ENTRY;
413 
414    Opt = SUMA_Alloc_Generic_Prog_Options_Struct();
415    Opt->ps = ps;  /* just hold it there for convenience */
416    Opt->NodeDbg = -1;
417    Opt->out_prefix = NULL;
418    kar = 1;
419    brk = NOPE;
420    while (kar < argc) { /* loop accross command ine options */
421       /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/
422       if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) {
423           ps->hverb = strlen(argv[kar])>3?2:1;
424           usage_RetinoMap(ps);
425           exit (0);
426       }
427 
428       SUMA_SKIP_COMMON_OPTIONS(brk, kar);
429 
430       if (!brk && (strcmp(argv[kar], "-debug") == 0))
431       {
432          if (kar+1 >= argc)
433          {
434             fprintf (SUMA_STDERR, "need a number after -debug \n");
435             exit (1);
436          }
437 
438          Opt->debug = atoi(argv[++kar]);
439          brk = YUP;
440       }
441 
442       if (!brk && (strcmp(argv[kar], "-prefix") == 0))
443       {
444          if (kar+1 >= argc)
445          {
446             fprintf (SUMA_STDERR, "need a dset prefix after -prefix \n");
447             exit (1);
448          }
449 
450          Opt->out_prefix = SUMA_copy_string(argv[++kar]);
451          brk = YUP;
452       }
453 
454       if (!brk && (  (strcmp(argv[kar], "-node_dbg") == 0) ||
455                      (strcmp(argv[kar], "-node_debug") == 0)) ) {
456          kar ++;
457          if (kar >= argc)  {
458             fprintf (SUMA_STDERR, "need argument after -node_debug \n");
459             exit (1);
460          }
461          Opt->NodeDbg = atoi(argv[kar]);
462          if (Opt->NodeDbg < 0) {
463             fprintf (SUMA_STDERR, "%d is a bad node number.\n", Opt->NodeDbg);
464             exit (1);
465          }
466          brk = YUP;
467       }
468 
469       if (!brk && !ps->arg_checked[kar]) {
470          SUMA_S_Errv("Option %s not understood.\n"
471                      "Try -help for usage\n",
472                      argv[kar]);
473          exit (1);
474       } else {
475          brk = NOPE;
476          kar ++;
477       }
478    }
479 
480    SUMA_RETURN(Opt);
481 }
482 
main(int argc,char * argv[])483 int main (int argc,char *argv[])
484 {/* Main */
485    static char FuncName[]={"SurfRetinoMap"};
486    SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt;
487    SUMA_GENERIC_ARGV_PARSE *ps=NULL;
488    SUMA_DSET_FORMAT iform = SUMA_NO_DSET_FORMAT;
489    SUMA_DSET *dpol=NULL, *decc=NULL, *dout=NULL;
490    SUMA_SurfSpecFile *Spec = NULL;
491    int ii, N_Spec, N_inmask = -1;
492    int oform= SUMA_NO_DSET_FORMAT;
493    SUMA_SurfaceObject *SO=NULL;
494    char *s=NULL;
495    SUMA_Boolean LocalHead = NOPE;
496 
497    SUMA_STANDALONE_INIT;
498    SUMA_mainENTRY;
499 
500    /* Allocate space for DO structure */
501    SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS);
502    ps = SUMA_Parse_IO_Args(argc, argv, "-i;-spec;-s;-o;-talk;-mask;-dset;");
503 
504    if (argc < 2) {
505       usage_RetinoMap(ps);
506       exit (1);
507    }
508 
509    Opt = SUMA_RetinoMap_ParseInput (argv, argc, ps);
510 
511    if (Opt->debug > 2) LocalHead = YUP;
512    if (Opt->ps->N_dsetname != 2) {
513       SUMA_S_Errv("Need two and only two dsets please.\n"
514                   "Have %d on command line.\n",
515                   Opt->ps->N_dsetname);
516       exit(1);
517    }
518    if (!(dpol = SUMA_LoadDset_s (Opt->ps->dsetname[0], &iform, 0))) {
519       SUMA_S_Errv("Failed to load dpol named %s\n", Opt->ps->dsetname[0]);
520       exit(1);
521    }
522    if (!SUMA_PopulateDsetNodeIndexNel(dpol,0)) {
523       SUMA_S_Err("Failed populating node indices");
524       exit(1);
525    }
526    if (!(decc = SUMA_LoadDset_s (Opt->ps->dsetname[1], &iform, 0))) {
527       SUMA_S_Errv("Failed to load decc named %s\n", Opt->ps->dsetname[1]);
528       exit(1);
529    }
530    if (!SUMA_PopulateDsetNodeIndexNel(decc,0)) {
531       SUMA_S_Err("Failed populating node indices");
532       exit(1);
533    }
534 
535    Spec = SUMA_IO_args_2_spec(ps, &N_Spec);
536    if (N_Spec == 0) {
537       SUMA_S_Err("No surfaces found.");
538       exit(1);
539    }
540 
541    SUMA_LH("Loading surface...");
542    SO = SUMA_Load_Spec_Surf(Spec, 0, ps->sv[0], Opt->debug);
543    if (!SO) {
544          fprintf (SUMA_STDERR,"Error %s:\n"
545                               "Failed to find surface\n"
546                               "in spec file. \n",
547                               FuncName );
548          exit(1);
549 
550    }
551    if (!SUMA_SurfaceMetrics_eng(SO, "EdgeList|PolyArea|",
552                                  NULL, Opt->debug, SUMAg_CF->DsetList)) {
553       SUMA_S_Err("Failed in SUMA_SurfaceMetrics.\n");
554       exit(1);
555    }
556 
557    if (!(Opt->nmask = SUMA_load_all_command_masks(
558                         Opt->ps->bmaskname, Opt->ps->nmaskname, Opt->ps->cmask,
559                         SO->N_Node, &N_inmask))
560          && N_inmask < 0) {
561          SUMA_S_Err("Failed loading mask");
562          exit(1);
563    }
564    if (Opt->nmask) {
565       SUMA_LHv("%d nodes in mask for SO of %d nodes (%.2f perc. of surface)\n",
566                N_inmask, SO->N_Node, 100.0*N_inmask/SO->N_Node);
567    } else {
568       SUMA_LH("no mask");
569    }
570    if (!(dout = SUMA_RetinoMap (SO, decc, dpol, Opt->nmask,
571                                  Opt->NodeDbg, Opt->out_prefix))) {
572       SUMA_S_Err("Failed in RetinoMap");
573       exit(1);
574    }
575 
576    if (Opt->out_prefix) {
577       s = SUMA_WriteDset_s (Opt->out_prefix, dout, oform, THD_ok_overwrite(), 0);
578    } else {
579       s = SUMA_WriteDset_s ("RetinoMap", dout, SUMA_ASCII_NIML, 1, 1);
580    }
581    SUMA_free(s);
582 
583    if (!SUMA_FreeSpecFields(Spec)) {
584       SUMA_S_Err("Failed to free Spec fields");
585    } SUMA_free(Spec); Spec = NULL;
586    if (ps) SUMA_FreeGenericArgParse(ps); ps = NULL;
587    if (Opt) Opt = SUMA_Free_Generic_Prog_Options_Struct(Opt);
588    if (!SUMA_Free_CommonFields(SUMAg_CF))
589       SUMA_error_message(FuncName,"SUMAg_CF Cleanup Failed!",1);
590 
591    exit(0);
592 
593 }
594