/*! contains code shamelessly stolen from Rick who stole it from Bob. */ SUMA_Boolean SUMA_Get_isosurface_datasets ( SUMA_GENERIC_PROG_OPTIONS_STRUCT * Opt) { static char FuncName[]={"SUMA_Get_isosurface_datasets"}; int i; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; if (!Opt->in_vol && !Opt->in_name) { SUMA_S_Err("NULL input"); SUMA_RETURN(NOPE); } if (!Opt->in_vol) { /* open it */ Opt->in_vol = THD_open_dataset( Opt->in_name ); } if (!Opt->in_vol) { SUMA_S_Err("No volume could be had"); SUMA_RETURN(NOPE); } if (!ISVALID_DSET(Opt->in_vol)) { if (!Opt->in_name) { SUMA_SL_Err("NULL input volume."); SUMA_RETURN(NOPE); } else { SUMA_SL_Err("invalid volume."); SUMA_RETURN(NOPE); } } else if ( DSET_BRICK_TYPE(Opt->in_vol, 0) == MRI_complex) { SUMA_SL_Err("Can't do complex data."); SUMA_RETURN(NOPE); } Opt->nvox = DSET_NVOX( Opt->in_vol ); if (DSET_NVALS( Opt->in_vol) != 1) { SUMA_SL_Err("Input volume can only have one sub-brick in it.\n" "Use [.] selectors to choose sub-brick needed."); SUMA_RETURN(NOPE); } Opt->mcdatav = (double *)SUMA_malloc(sizeof(double)*Opt->nvox); if (!Opt->mcdatav) { SUMA_SL_Crit("Failed to allocate for maskv"); SUMA_RETURN(NOPE); } if (Opt->xform == SUMA_ISO_XFORM_MASK) { SUMA_LH("SUMA_ISO_XFORM_MASK"); switch (Opt->MaskMode) { case SUMA_ISO_CMASK: SUMA_LH("SUMA_ISO_CMASK"); if (Opt->cmask) { /* here's the second order grand theft */ int clen = strlen( Opt->cmask ); char * cmd; byte *bmask; cmd = (char *)malloc((clen + 1) * sizeof(char)); strcpy( cmd, Opt->cmask); bmask = EDT_calcmask( cmd, &Opt->ninmask, 0 ); SUMA_LHv("Have %d\n", Opt->ninmask); free( cmd ); /* free EDT_calcmask() string */ if ( bmask == NULL ) { SUMA_SL_Err("Failed to compute mask from -cmask option"); SUMA_free(Opt->mcdatav); Opt->mcdatav=NULL; SUMA_RETURN(NOPE); } if ( Opt->ninmask != Opt->nvox ) { SUMA_SL_Err("Input and cmask datasets do not have " "the same dimensions\n" ); SUMA_free(Opt->mcdatav); Opt->mcdatav=NULL; SUMA_RETURN(NOPE); } Opt->ninmask = THD_countmask( Opt->ninmask, bmask ); SUMA_LHv("Have %d\n", Opt->ninmask); for (i=0; i<Opt->nvox; ++i) if (bmask[i]) Opt->mcdatav[i] = (double)bmask[i]; else Opt->mcdatav[i] = -1; free(bmask);bmask=NULL; } else { SUMA_SL_Err("NULL cmask"); SUMA_RETURN(NOPE); } break; case SUMA_ISO_VAL: case SUMA_ISO_RANGE: SUMA_LH("SUMA_ISO_VAL, SUMA_ISO_RANGE"); /* load the dset */ DSET_load(Opt->in_vol); Opt->dvec = (double *)SUMA_malloc(sizeof(double) * Opt->nvox); if (!Opt->dvec) { SUMA_SL_Crit("Faile to allocate for dvec.\nOh misery."); SUMA_RETURN(NOPE); } EDIT_coerce_scale_type( Opt->nvox , DSET_BRICK_FACTOR(Opt->in_vol,0) , DSET_BRICK_TYPE(Opt->in_vol,0), DSET_ARRAY(Opt->in_vol, 0) , MRI_double , Opt->dvec ) ; /* no need for data in input volume anymore */ PURGE_DSET(Opt->in_vol); Opt->ninmask = 0; if (Opt->MaskMode == SUMA_ISO_VAL) { for (i=0; i<Opt->nvox; ++i) { if (Opt->dvec[i] == Opt->v0) { Opt->mcdatav[i] = 1; ++ Opt->ninmask; } else Opt->mcdatav[i] = -1; } } else if (Opt->MaskMode == SUMA_ISO_RANGE) { for (i=0; i<Opt->nvox; ++i) { if (Opt->dvec[i] >= Opt->v0 && Opt->dvec[i] < Opt->v1) { Opt->mcdatav[i] = 1; ++ Opt->ninmask; } else Opt->mcdatav[i] = -1; } } else { SUMA_SL_Err("Bad Miracle."); SUMA_RETURN(NOPE); } SUMA_free(Opt->dvec); Opt->dvec = NULL; /* this vector is not even created in SUMA_ISO_CMASK mode ...*/ break; default: SUMA_SL_Err("Unexpected value of MaskMode"); SUMA_RETURN(NOPE); break; } } else if (Opt->xform == SUMA_ISO_XFORM_SHIFT) { /* load the dset */ DSET_load(Opt->in_vol); Opt->dvec = (double *)SUMA_malloc(sizeof(double) * Opt->nvox); if (!Opt->dvec) { SUMA_SL_Crit("Failed to allocate for dvec.\nOh misery."); SUMA_RETURN(NOPE); } EDIT_coerce_scale_type( Opt->nvox , DSET_BRICK_FACTOR(Opt->in_vol,0) , DSET_BRICK_TYPE(Opt->in_vol,0), DSET_ARRAY(Opt->in_vol, 0) , MRI_double , Opt->dvec ) ; /* no need for data in input volume anymore */ PURGE_DSET(Opt->in_vol); Opt->ninmask = Opt->nvox; for (i=0; i<Opt->nvox; ++i) { Opt->mcdatav[i] = Opt->dvec[i] - Opt->v0; } SUMA_free(Opt->dvec); Opt->dvec = NULL; } else if (Opt->xform == SUMA_ISO_XFORM_NONE) { /* load the dset */ DSET_load(Opt->in_vol); Opt->dvec = (double *)SUMA_malloc(sizeof(double) * Opt->nvox); if (!Opt->dvec) { SUMA_SL_Crit("Faile to allocate for dvec.\nOh misery."); SUMA_RETURN(NOPE); } EDIT_coerce_scale_type( Opt->nvox , DSET_BRICK_FACTOR(Opt->in_vol,0) , DSET_BRICK_TYPE(Opt->in_vol,0), DSET_ARRAY(Opt->in_vol, 0) , MRI_double , Opt->dvec ) ; /* no need for data in input volume anymore */ PURGE_DSET(Opt->in_vol); Opt->ninmask = Opt->nvox; for (i=0; i<Opt->nvox; ++i) { Opt->mcdatav[i] = Opt->dvec[i]; } SUMA_free(Opt->dvec); Opt->dvec = NULL; } else { SUMA_SL_Err("Bad Opt->xform."); SUMA_RETURN(NOPE); } if ( Opt->ninmask <= 0 ) { if (Opt->ninmask == 0) { SUMA_SL_Err("A negative value!\nNothing to do." ); } else { SUMA_SL_Err("An empty mask!\n Nothing to do." ); } SUMA_RETURN(NOPE); } if (Opt->debug > 0) { fprintf( SUMA_STDERR, "%s:\nInput dset %s has nvox = %d, nvals = %d", FuncName, SUMA_CHECK_NULL_STR(Opt->in_name), Opt->nvox, DSET_NVALS(Opt->in_vol) ); fprintf( SUMA_STDERR, " (%d voxels in mask)\n", Opt->ninmask ); } SUMA_RETURN(YUP); }
int main (int argc,char *argv[]) { /* Main */ static char FuncName[]= {"SurfQual"}; char *OutName = NULL, ext[5], *prefix = NULL, *shist=NULL; SUMA_SURFQUAL_OPTIONS *Opt; int SO_read = -1; int i, cnt, trouble, consistent = -1, eu = -1, nsi = -1, N_Spec=0; SUMA_SurfaceObject *SO = NULL; SUMA_SurfSpecFile *Spec=NULL; void *SO_name = NULL; SUMA_Boolean DoConv = NOPE, DoSphQ = NOPE, DoSelfInt = NOPE; int N_bad_nodes, N_bad_facesets; SUMA_GENERIC_ARGV_PARSE *ps=NULL; SUMA_Boolean LocalHead = NOPE; SUMA_STANDALONE_INIT; SUMA_mainENTRY; ps = SUMA_Parse_IO_Args(argc, argv, "-i;-t;-spec;-s;-sv;"); /* Allocate space for DO structure */ SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS); Opt = SUMA_SurfQual_ParseInput (argv, argc, ps); if (argc < 2) { SUMA_S_Err("Too few options"); usage_SUMA_SurfQual(ps, 0); exit (1); } /* read all surfaces */ Spec = SUMA_IO_args_2_spec(ps, &N_Spec); if (N_Spec == 0) { SUMA_S_Err("No surfaces found."); exit(1); } if (N_Spec > 1 ) { SUMA_S_Err( "Mike, you cannot mix -spec with -i or -t options " "for specifying surfaces."); exit(1); } if (Spec->N_Surfs < 1) { SUMA_S_Err("No surfaces"); exit(1); } if (Opt->self_intersect) DoSelfInt = YUP; DoConv = NOPE; DoSphQ = NOPE; if (Opt->surftype) { if (!strcmp(Opt->surftype, "-sphere")) { DoSphQ = YUP; } else { /* Don't complain anymore, maybe winding checking is all users need */ } } for (i=0; i < Spec->N_Surfs; ++i) {/* loop to read in surfaces */ /* now identify surface needed */ if (!(SO = SUMA_Load_Spec_Surf_with_Metrics(Spec, i, ps->sv[0], 0))) { SUMA_S_Err("Failed to load surface .\n"); exit(1); } fprintf(SUMA_STDERR,"\nReport for Surface %s\n", SO->Label); /* do the quality thing based on the Opt->surftype */ if (!Opt->out_prefix) prefix = SUMA_copy_string(SO->Label); else prefix = SUMA_copy_string (Opt->out_prefix); /* check the winding */ if (!SUMA_MakeConsistent (SO->FaceSetList, SO->N_FaceSet, SO->EL, 0, &trouble)) { SUMA_S_Warn( "Failed to make sure surface's mesh is consistently wound.\n" "You should fix the mesh.\n"); consistent = 0; } { int iii=0, isbad=0, ht0; int *badedge=(int*)SUMA_calloc(SO->N_Node, sizeof(int)); /* check on troubled edges */ while (iii < SO->EL->N_EL) { ht0 = SO->EL->ELps[iii][1]; /* make sure edge is not part of three triangles, if it is,skip it*/ if (SO->EL->ELps[iii][2] > 2) { ++iii; fprintf( SUMA_STDERR, "%s: Bad edge (#%d: %d--%d), \n" " part of more than 2 triangles, skip it\n", FuncName, i, SO->EL->EL[iii][0], SO->EL->EL[iii][1]); ++badedge[SO->EL->EL[iii][0]]; ++badedge[SO->EL->EL[iii][1]]; isbad = 1; continue; } ++iii; } if (isbad) { if (Spec->N_Surfs > 1) { sprintf(ext,"_%c", 65+i); OutName = SUMA_append_replace_string ( prefix, "_BadEdgeNodes.1D.dset", ext, 0); } else { OutName = SUMA_append_string (prefix, "_BadEdgeNodes.1D.dset"); } SUMA_WRITE_ARRAY_1D(badedge,SO->N_Node,1,OutName); if (OutName) SUMA_free(OutName); OutName = NULL; } SUMA_free(badedge); badedge = NULL; } if (DoConv) { float *Cx = NULL; if (Spec->N_Surfs > 1) { sprintf(ext,"_%c", 65+i); OutName = SUMA_append_replace_string (prefix, "_Conv_detail.1D.dset", ext, 0); } else { OutName = SUMA_append_string (prefix, "_Conv_detail.1D.dset"); } Cx = SUMA_Convexity_Engine ( SO->NodeList, SO->N_Node, SO->NodeNormList, SO->FN, OutName, NULL); if (Cx) SUMA_free(Cx); Cx = NULL; if (OutName) SUMA_free(OutName); OutName = NULL; } if (DoSphQ) { if (Spec->N_Surfs > 1) { sprintf(ext,"_%c", 65+i); OutName = SUMA_append_string (prefix, ext); } else { OutName = SUMA_copy_string (prefix); } shist = SUMA_HistString (NULL, argc, argv, NULL); SUMA_SphereQuality (SO, OutName, shist, &N_bad_nodes, &N_bad_facesets); if (shist) SUMA_free(shist); shist = NULL; if (OutName) SUMA_free(OutName); OutName = NULL; } if (trouble) { /* put winding problem here to make it visible */ fprintf (SUMA_STDERR,"\n"); SUMA_S_Warn( "Mesh is not consistent, use ConvertSurface's -make_consistent \n" "option to fix the problem before proceeding further.\n" "Other results reported by this and other programs\n" "may be incorrect if mesh is not consistently wound.\n" ); consistent = 0; } else { consistent = 1; fprintf (SUMA_STDERR,"\n"); fprintf (SUMA_STDERR,"Surface is consistently wound\n"); } { SUMA_EULER_SO(SO, eu); fprintf (SUMA_STDERR,"\n"); fprintf(SUMA_STDERR,"Surface Euler Characteristic is: %d\n", eu); } if ((SO->EL->min_N_Hosts == 1 || SO->EL->max_N_Hosts == 1)) { fprintf (SUMA_STDERR,"\n"); fprintf(SUMA_STDERR, "Warning %s:\n" " Min/Max number of edge hosting triangles: [%d/%d] \n", FuncName, SO->EL->min_N_Hosts, SO->EL->max_N_Hosts); fprintf( SUMA_STDERR, " You have edges that form a border in the surface.\n"); } if (SO->EL->min_N_Hosts == 2 && SO->EL->max_N_Hosts == 2) { fprintf (SUMA_STDERR,"\n"); fprintf(SUMA_STDERR,"Surface is closed and is a 2-manifold."); } if (SO->EL->min_N_Hosts > 2 || SO->EL->max_N_Hosts > 2) { fprintf (SUMA_STDERR,"\n"); fprintf( SUMA_STDERR, "Warning %s:\n" "Min/Max number of edge hosting triangles: [%d/%d] \n", FuncName, SO->EL->min_N_Hosts, SO->EL->max_N_Hosts); fprintf(SUMA_STDERR, "Warning %s:\n" " You have edges that belong to more than two triangles.\n" " Bad for analysis assuming surface is a 2-manifold.\n", FuncName); if (1) { int iii=0; fprintf( SUMA_STDERR, " These edges are formed by the following nodes:\n"); for (iii = 0; iii < SO->EL->N_EL; ++iii) { if (SO->EL->ELps[iii][2] > 2) fprintf (SUMA_STDERR, " %d: Edge [%d %d] shared by %d triangles.\n", iii+1, SO->EL->EL[iii][0], SO->EL->EL[iii][1] , SO->EL->ELps[iii][2] ); } } } if (DoSelfInt) { int iii; FILE *fout=NULL; byte *report = (byte *)SUMA_calloc(SO->N_Node, sizeof(byte)); if (!report) { SUMA_SL_Crit("Failed to allocate for report"); report = NULL; } fprintf( SUMA_STDERR, "\n\nChecking for intersections...:\n"); nsi = SUMA_isSelfIntersect(SO, 500, report); if (nsi) { fprintf( SUMA_STDERR, " Surface is self intersecting.\n" "%d segments were found to intersect the surface.\n", nsi); if (nsi >= 500) { fprintf( SUMA_STDERR, " It is possible that you have additional segments" " intersecting the surface.\n"); } if (report) { if (Spec->N_Surfs > 1) { sprintf(ext,"_%c", 65+i); OutName = SUMA_append_replace_string ( prefix, "_IntersNodes.1D.dset", ext, 0); } else { OutName = SUMA_append_string (prefix, "_IntersNodes.1D.dset"); } fout = fopen(OutName, "w"); if (fout) { fprintf(fout, "#List of nodes that are part of segments which intersect " "the surface\n" "#%s\n" "#A total of %d segments (search limit is 500) were found to " "intersect the surface.\n" "#Col.1 : Node index\n" "#Col.2 : Dummy flag, always 1\n", SUMA_CHECK_NULL_STR(SO->Label), nsi ); for (iii=0; iii<SO->N_Node; ++iii) if (report[iii]) fprintf(fout, "%d\t1\n", iii); fclose(fout); fout = NULL; } else { SUMA_SL_Err("Failed to open file for output."); } if (OutName) SUMA_free(OutName); } } else { fprintf(SUMA_STDERR, " Surface is not self intersecting.\n"); } if (report) SUMA_free(report); report = NULL; } fprintf (SUMA_STDERR,"\n"); if (Opt->DoSum) { /* do not change syntax, scripts depend on this */ fprintf(stdout,"Summary for %s:\n", SO->Label); fprintf(stdout,"Euler_Charac. %d\n", eu); fprintf(stdout,"Consistent_Winding %d\n", consistent); if (DoSphQ) { fprintf(stdout,"Folding_Triangles %d\n", N_bad_facesets); fprintf(stdout,"Sketchy_nodes %d\n", N_bad_nodes); } if (DoSelfInt) fprintf(stdout,"Self_Intersections %d\n", nsi); fprintf(stdout,"\n"); } } SUMA_LH("clean up"); if (!SUMA_FreeSpecFields(Spec)) { SUMA_S_Err("Failed to free spec fields"); } SUMA_free(Spec); Spec = NULL; if (prefix) SUMA_free(prefix); prefix = NULL; if (Opt->out_prefix) SUMA_free(Opt->out_prefix); Opt->out_prefix = NULL; if (Opt) SUMA_free(Opt); if (!SUMA_Free_Displayable_Object_Vect (SUMAg_DOv, SUMAg_N_DOv)) { SUMA_SL_Err("DO Cleanup Failed!"); } if (!SUMA_Free_CommonFields(SUMAg_CF)) SUMA_error_message(FuncName,"SUMAg_CF Cleanup Failed!",1); SUMA_RETURN(0); }