#include "SUMA_suma.h" #include "../thd_brainormalize.h" #include "../rickr/r_new_resam_dset.h" #undef STAND_ALONE #if defined SUMA_BrainWrap_STANDALONE #define STAND_ALONE #endif #ifdef STAND_ALONE /* these global variables must be declared even if they will not be used by this main */ SUMA_SurfaceViewer *SUMAg_cSV = NULL; /*!< Global pointer to current Surface Viewer structure*/ SUMA_SurfaceViewer *SUMAg_SVv = NULL; /*!< Global pointer to the vector containing the various Surface Viewer Structures SUMAg_SVv contains SUMA_MAX_SURF_VIEWERS structures */ int SUMAg_N_SVv = 0; /*!< Number of SVs realized by X */ SUMA_DO *SUMAg_DOv = NULL; /*!< Global pointer to Displayable Object structure vector*/ int SUMAg_N_DOv = 0; /*!< Number of DOs stored in DOv */ SUMA_CommonFields *SUMAg_CF = NULL; /*!< Global pointer to structure containing info common to all viewers */ #else extern SUMA_CommonFields *SUMAg_CF; extern SUMA_DO *SUMAg_DOv; extern SUMA_SurfaceViewer *SUMAg_SVv; extern int SUMAg_N_SVv; extern int SUMAg_N_DOv; #endif #ifdef SUMA_BrainWrap_STANDALONE void usage_SUMA_BrainWrap (SUMA_GENERIC_ARGV_PARSE *ps) { static char FuncName[]={"usage_SUMA_BrainWrap"}; char * s = NULL, *sio=NULL, *st = NULL, *sts = NULL; int i; s = SUMA_help_basics(); sio = SUMA_help_IO_Args(ps); sts = SUMA_help_talk(ps); printf ( "\n" "Usage: A program to extract the brain from surrounding.\n" " tissue from MRI T1-weighted images. The fully automated\n" " process consists of three steps:\n" " 1- Preprocessing of volume to remove gross spatial image \n" " non-uniformity artifacts and reposition the brain in\n" " a reasonable manner for convenience.\n" " 2- Expand a spherical surface iteratively until it envelopes\n" " the brain. This is a modified version of the BET algorithm:\n" " Fast robust automated brain extraction, \n" " by Stephen M. Smith, HBM 2002 v 17:3 pp 143-155\n" " Modifications include the use of:\n" " . outer brain surface\n" " . expansion driven by data inside and outside the surface\n" " . avoidance of eyes and ventricles\n" " . a set of operations to avoid the clipping of certain brain\n" " areas and reduce leakage into the skull in heavily shaded\n" " data\n" " . two additional processing stages to ensure convergence and\n" " reduction of clipped areas.\n" " . use of 3d edge detection, see Deriche and Monga references\n" " in 3dedge3 -help.\n" " 3- The creation of various masks and surfaces modeling brain\n" " and portions of the skull\n" "\n" " Common examples of usage:\n" " -------------------------\n" " o 3dSkullStrip -input VOL -prefix VOL_PREFIX\n" " Vanilla mode, should work for most datasets.\n" " o 3dSkullStrip -input VOL -prefix VOL_PREFIX -push_to_edge\n" " Adds an agressive push to brain edges. Use this option\n" " when the chunks of gray matter are not included. This option\n" " might cause the mask to leak into non-brain areas.\n" " o 3dSkullStrip -input VOL -surface_coil -prefix VOL_PREFIX -monkey\n" " Vanilla mode, for use with monkey data.\n" " o 3dSkullStrip -input VOL -prefix VOL_PREFIX -ld 30\n" " Use a denser mesh, in the cases where you have lots of \n" " csf between gyri. Also helps when some of the brain is clipped\n" " close to regions of high curvature.\n" "\n" " Tips:\n" " -----\n" " I ran the program with the default parameters on 200+ datasets.\n" " The results were quite good in all but a couple of instances, here\n" " are some tips on fixing trouble spots:\n" "\n" " Clipping in frontal areas, close to the eye balls:\n" " + Try -push_to_edge option first.\n" " Can also try -no_avoid_eyes option.\n" " Clipping in general:\n" " + Try -push_to_edge option first.\n" " Can also use lower -shrink_fac, start with 0.5 then 0.4\n" " Problems down below:\n" " + Piece of cerbellum missing, reduce -shrink_fac_bot_lim \n" " from default value.\n" " + Leakage in lower areas, increase -shrink_fac_bot_lim \n" " from default value.\n" " Some lobules are not included:\n" " + Use a denser mesh. Start with -ld 30. If that still fails,\n" " try even higher density (like -ld 50) and increase iterations \n" " (say to -niter 750). Expect the program to take much longer in that case.\n" " + Instead of using denser meshes, you could try blurring the data \n" " before skull stripping. Something like -blur_fwhm 2 did\n" " wonders for some of my data with the default options of 3dSkullStrip\n" " Blurring is a lot faster than increasing mesh density.\n" " + Use also a smaller -shrink_fac is you have lots of CSF between gyri.\n" " Massive chunks missing:\n" " + If brain has very large ventricles and lots of CSF between gyri, the\n" " ventricles will keep attracting the surface inwards. In such cases, use\n" " the -visual option to see what is happening and try these options to \n" " reduce the severity of the problem:\n" " -blur_fwhm 2 -use_skull\n" "\n" " Eye Candy Mode: \n" " ---------------\n" " You can run BrainWarp and have it send successive iterations\n" " to SUMA and AFNI. This is very helpful in following the\n" " progression of the algorithm and determining the source\n" " of trouble, if any.\n" " Example:\n" " afni -niml -yesplugouts &\n" " suma -niml &\n" " 3dSkullStrip -input Anat+orig -o_ply anat_brain -visual\n" "\n" " Help section for the intrepid:\n" " ------------------------------\n" " 3dSkullStrip < -input VOL >\n" " [< -o_TYPE PREFIX >] [< -prefix VOL_PREFIX >] \n" " [< -spatnorm >] [< -no_spatnorm >] [< -write_spatnorm >]\n" " [< -niter N_ITER >] [< -ld LD >] \n" " [< -shrink_fac SF >] [< -var_shrink_fac >] \n" " [< -no_var_shrink_fac >] [< -shrink_fac_bot_lim SFBL >]\n" " [< -pushout >] [< -no_pushout >] [< -exp_frac FRAC]\n" " [< -touchup >] [< -no_touchup >]\n" " [< -fill_hole R >] [< -NN_smooth NN_SM >]\n" " [< -smooth_final SM >] [< -avoid_vent >] [< -no_avoid_vent >]\n" " [< -use_skull >] [< -no_use_skull >] \n" " [< -avoid_eyes >] [< -no_avoid_eyes >] \n" " [< -use_edge >] [< -no_use_edge >] \n" " [< -push_to_edge >] [<-no_push_to_edge>]\n" " [< -perc_int PERC_INT >] \n" " [< -max_inter_iter MII >] [-mask_vol | -orig_vol | -norm_vol]\n" " [< -debug DBG >] [< -node_debug NODE_DBG >]\n" " [< -demo_pause >]\n" " [< -monkey >] [<-rat>]\n" "\n" " NOTE: Please report bugs and strange failures\n" " to saadz@mail.nih.gov\n" "\n" " Mandatory parameters:\n" " -input VOL: Input AFNI (or AFNI readable) volume.\n" " \n" "\n" " Optional Parameters:\n" " -monkey: the brain of a monkey.\n" " -rat: the brain of a rat.\n" " By default, no_touchup is used with the rat.\n" " -surface_coil: Data acquired with a surface coil.\n" " -o_TYPE PREFIX: prefix of output surface.\n" " where TYPE specifies the format of the surface\n" " and PREFIX is, well, the prefix.\n" " TYPE is one of: fs, 1d (or vec), sf, ply.\n" " More on that below.\n" " -skulls: Output surface models of the skull.\n" " -4Tom: The output surfaces are named based\n" " on PREFIX following -o_TYPE option below.\n" " -prefix VOL_PREFIX: prefix of output volume.\n" " If not specified, the prefix is the same\n" " as the one used with -o_TYPE.\n" " The output volume is skull stripped version\n" " of the input volume. In the earlier version\n" " of the program, a mask volume was written out.\n" " You can still get that mask volume instead of the\n" " skull-stripped volume with the option -mask_vol . \n" " NOTE: In the default setting, the output volume does not \n" " have values identical to those in the input. \n" " In particular, the range might be larger \n" " and some low-intensity values are set to 0.\n" " If you insist on having the same range of values as in\n" " the input, then either use option -orig_vol, or run:\n" " 3dcalc -nscale -a VOL+VIEW -b VOL_PREFIX+VIEW \\\n" " -expr 'a*step(b)' -prefix VOL_SAME_RANGE\n" " With the command above, you can preserve the range\n" " of values of the input but some low-intensity voxels would still\n" " be masked. If you want to preserve them, then use -mask_vol\n" " in the 3dSkullStrip command that would produce \n" " VOL_MASK_PREFIX+VIEW. Then run 3dcalc masking with voxels\n" " inside the brain surface envelope:\n" " 3dcalc -nscale -a VOL+VIEW -b VOL_MASK_PREFIX+VIEW \\\n" " -expr 'a*step(b-3.01)' -prefix VOL_SAME_RANGE_KEEP_LOW\n" " -norm_vol: Output a masked and somewhat intensity normalized and \n" " thresholded version of the input. This is the default,\n" " and you can use -orig_vol to override it.\n" " -orig_vol: Output a masked version of the input AND do not modify\n" " the values inside the brain as -norm_vol would.\n" " -mask_vol: Output a mask volume instead of a skull-stripped\n" " volume.\n" " The mask volume containes:\n" " 0: Voxel outside surface\n" " 1: Voxel just outside the surface. This means the voxel\n" " center is outside the surface but inside the \n" " bounding box of a triangle in the mesh. \n" " 2: Voxel intersects the surface (a triangle), but center\n" " lies outside.\n" " 3: Voxel contains a surface node.\n" " 4: Voxel intersects the surface (a triangle), center lies\n" " inside surface. \n" " 5: Voxel just inside the surface. This means the voxel\n" " center is inside the surface and inside the \n" " bounding box of a triangle in the mesh. \n" " 6: Voxel inside the surface. \n" " -spat_norm: (Default) Perform spatial normalization first.\n" " This is a necessary step unless the volume has\n" " been 'spatnormed' already.\n" " -no_spatnorm: Do not perform spatial normalization.\n" " Use this option only when the volume \n" " has been run through the 'spatnorm' process\n" " -spatnorm_dxyz DXYZ: Use DXY for the spatial resolution of the\n" " spatially normalized volume. The default \n" " is the lowest of all three dimensions.\n" " For human brains, use DXYZ of 1.0, for\n" " primate brain, use the default setting.\n" " -write_spatnorm: Write the 'spatnormed' volume to disk.\n" " -niter N_ITER: Number of iterations. Default is 250\n" " For denser meshes, you need more iterations\n" " N_ITER of 750 works for LD of 50.\n" " -ld LD: Parameter to control the density of the surface.\n" " Default is 20 if -no_use_edge is used,\n" " 30 with -use_edge. See CreateIcosahedron -help\n" " for details on this option.\n" " -shrink_fac SF: Parameter controlling the brain vs non-brain\n" " intensity threshold (tb). Default is 0.6.\n" " tb = (Imax - t2) SF + t2 \n" " where t2 is the 2 percentile value and Imax is the local\n" " maximum, limited to the median intensity value.\n" " For more information on tb, t2, etc. read the BET paper\n" " mentioned above. Note that in 3dSkullStrip, SF can vary across \n" " iterations and might be automatically clipped in certain areas.\n" " SF can vary between 0 and 1.\n" " 0: Intensities < median inensity are considered non-brain\n" " 1: Intensities < t2 are considered non-brain\n" " -var_shrink_fac: Vary the shrink factor with the number of\n" " iterations. This reduces the likelihood of a surface\n" " getting stuck on large pools of CSF before reaching\n" " the outer surface of the brain. (Default)\n" " -no_var_shrink_fac: Do not use var_shrink_fac.\n" " -shrink_fac_bot_lim SFBL: Do not allow the varying SF to go\n" " below SFBL . Default 0.65, 0.4 when edge detection is used. \n" " This option helps reduce potential for leakage below \n" " the cerebellum.\n" " In certain cases where you have severe non-uniformity resulting\n" " in low signal towards the bottom of the brain, you will need to reduce\n" " this parameter.\n" /* " -shrink_fac_bias SHRINK_BIAS_FILE: A file containing bias \n" " factors to apply to the shrink_fac at certain nodes.\n" " This option is experimental at the moment.\n" " Column 0 has node indices, Col. 1 has the bias factors\n" */ " -pushout: Consider values above each node in addition to values\n" " below the node when deciding on expansion. (Default)\n" " -no_pushout: Do not use -pushout.\n" " -exp_frac FRAC: Speed of expansion (see BET paper). Default is 0.1.\n" " -touchup: Perform touchup operations at end to include\n" " areas not covered by surface expansion. \n" " Use -touchup -touchup for aggressive makeup.\n" " (Default is -touchup)\n" " -no_touchup: Do not use -touchup\n" " -fill_hole R: Fill small holes that can result from small surface\n" " intersections caused by the touchup operation.\n" " R is the maximum number of pixels on the side of a hole\n" " that can be filled. Big holes are not filled.\n" " If you use -touchup, the default R is 10. Otherwise \n" " the default is 0.\n" " This is a less than elegant solution to the small\n" " intersections which are usually eliminated\n" " automatically. \n" " -NN_smooth NN_SM: Perform Nearest Neighbor coordinate interpolation\n" " every few iterations. Default is 72\n" " -smooth_final SM: Perform final surface smoothing after all iterations.\n" " Default is 20 smoothing iterations.\n" " Smoothing is done using Taubin's method, \n" " see SurfSmooth -help for detail.\n" " -avoid_vent: avoid ventricles. Default.\n" " -no_avoid_vent: Do not use -avoid_vent.\n" " -init_radius RAD: Use RAD for the initial sphere radius.\n" " For the automatic setting, there is an\n" " upper limit of 80mm for humans.\n" " -avoid_eyes: avoid eyes. Default\n" " -no_avoid_eyes: Do not use -avoid_eyes.\n" " -use_edge: Use edge detection to reduce leakage into meninges and eyes.\n" " Default.\n" " -no_use_edge: Do no use edges.\n" " -push_to_edge: Perform aggressive push to edge at the end.\n" " This option might cause leakage.\n" " -no_push_to_edge: (Default).\n" " -use_skull: Use outer skull to limit expansion of surface into\n" " the skull due to very strong shading artifacts.\n" " This option is buggy at the moment, use it only \n" " if you have leakage into skull.\n" " -no_use_skull: Do not use -use_skull (Default).\n" " -send_no_skull: Do not send the skull surface to SUMA if you are\n" " using -talk_suma\n" " -perc_int PERC_INT: Percentage of segments allowed to intersect\n" " surface. Ideally this should be 0 (Default). \n" " However, few surfaces might have small stubborn\n" " intersections that produce a few holes.\n" " PERC_INT should be a small number, typically\n" " between 0 and 0.1\n. A -1 means do not do\n" " any testing for intersection.\n" " -max_inter_iter N_II: Number of iteration to remove intersection\n" " problems. With each iteration, the program\n" " automatically increases the amount of smoothing\n" " to get rid of intersections. Default is 4\n" " -blur_fwhm FWHM: Blur dset after spatial normalization.\n" " Recommended when you have lots of CSF in brain\n" " and when you have protruding gyri (finger like)\n" " Recommended value is 2..4. \n" " -interactive: Make the program stop at various stages in the \n" " segmentation process for a prompt from the user\n" " to continue or skip that stage of processing.\n" " This option is best used in conjunction with options\n" " -talk_suma and -feed_afni\n" " -demo_pause: Pause at various step in the process to facilitate\n" " interactive demo while 3dSkullStrip is communicating\n" " with AFNI and SUMA. See 'Eye Candy' mode below and\n" " -talk_suma option. \n" "\n" "%s" " -visual: Equivalent to using -talk_suma -feed_afni -send_kth 5\n" "\n" " -debug DBG: debug levels of 0 (default), 1, 2, 3.\n" " This is no Rick Reynolds debug, which is oft nicer\n" " than the results, but it will do.\n" " -node_debug NODE_DBG: Output lots of parameters for node\n" " NODE_DBG for each iteration.\n" " The next 3 options are for specifying surface coordinates\n" " to keep the program from having to recompute them.\n" " The options are only useful for saving time during debugging.\n" " -brain_contour_xyz_file BRAIN_CONTOUR_XYZ.1D\n" " -brain_hull_xyz_file BRAIN_HULL_XYZ.1D\n" " -skull_outer_xyz_file SKULL_OUTER_XYZ.1D\n" "\n" /* " -sm_fac SMFAC: Smoothing factor (Default is 1)\n" " -d1 D1: Distance to search inward (Default 20 mm)\n" " -t2 T2: Force t2 to be T2 (Default automated)\n" " -t T: Force t to be T (Default automated)\n" " -tm TM: Force tm to be TM (Default automated)\n" " -t98 T98: Force t98 to be T98 (Default automated)\n" */ "%s" "\n", sio, s); SUMA_free(s); s = NULL; SUMA_free(st); st = NULL; SUMA_free(sio); sio = NULL; SUMA_free(sts); sts = NULL; s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL; printf(" Ziad S. Saad SSCC/NIMH/NIH saadz@mail.nih.gov \n"); exit (0); } /*! \brief parse the arguments for SurfSmooth program \param argv (char *) \param argc (int) \return Opt (SUMA_GENERIC_PROG_OPTIONS_STRUCT *) options structure. To free it, use SUMA_free(Opt->out_name); SUMA_free(Opt); */ SUMA_GENERIC_PROG_OPTIONS_STRUCT *SUMA_BrainWrap_ParseInput (char *argv[], int argc, SUMA_GENERIC_ARGV_PARSE *ps) { static char FuncName[]={"SUMA_BrainWrap_ParseInput"}; SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt=NULL; int kar, i, ind, exists; char *outname, cview[10]; SUMA_Boolean brk = NOPE; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; Opt = SUMA_Alloc_Generic_Prog_Options_Struct(); kar = 1; Opt->SpatNormDxyz = 0.0; Opt->spec_file = NULL; Opt->out_vol_prefix = NULL; Opt->out_prefix = NULL; Opt->sv_name = NULL; Opt->N_surf = -1; Opt->in_name = NULL; Opt->cmask = NULL; Opt->MaskMode = 0; for (i=0; i<SUMA_GENERIC_PROG_MAX_SURF; ++i) { Opt->surf_names[i] = NULL; } outname = NULL; Opt->in_vol = NULL; Opt->nvox = -1; Opt->ninmask = -1; Opt->mcdatav = NULL; Opt->debug = 0; Opt->v0 = 0.0; Opt->v1 = -1.0; Opt->fvec = NULL; Opt->SurfFileType = SUMA_PLY; Opt->SurfFileFormat = SUMA_ASCII; Opt->xform = SUMA_ISO_XFORM_MASK; Opt->obj_type = -1; Opt->obj_type_res = -1; Opt->XYZ = NULL; Opt->in_1D = NULL; Opt->N_XYZ = 0; Opt->Zt = 0.6; Opt->ExpFrac = 0.1; Opt->N_it = 250; Opt->Icold = -1; Opt->NodeDbg = -1; Opt->t2 = Opt->t98 = Opt->t = Opt->tm = -1; Opt->r = -1.0; Opt->d1 = -1; Opt->su1 = 1; Opt->UseNew = -1.0; Opt->d4 = -1; Opt->ztv = NULL; Opt->Kill98 = 0; Opt->NoEyes = 1; Opt->NNsmooth = 72; Opt->smootheach = 50; Opt->avoid_vent = 1; Opt->smooth_end = 20; Opt->k98mask = NULL; Opt->k98maskcnt = 0; Opt->dbg_eyenodes = NULL; Opt->travstp = 1.0; Opt->Stop = NULL; Opt->MaxIntIter = 4; Opt->UseExpansion = 1; Opt->PercInt = 0; Opt->UseSkull = 0; Opt->send_hull = 1; Opt->bot_lztclip = -1; /* 0.5 is OK but causes too much leakage below cerebellum in most dsets, 0.65 seems better. 0 if you do not want to use it*/ Opt->var_lzt = 1.0; /* a flag at the moment, set it to 1 to cause shirnk fac to vary during iterations. Helps escape certain large chunks of CSF just below the brain */ Opt->DemoPause = SUMA_3dSS_NO_PAUSE; Opt->DoSpatNorm = 1; Opt->WriteSpatNorm = 0; Opt->fillhole = -1; Opt->iset = NULL; Opt->SpatShift[0] = Opt->SpatShift[1] = Opt->SpatShift[2] = 0.0; Opt->OrigSpatNormedSet = NULL; Opt->in_edvol = NULL; Opt->blur_fwhm = 0.0; Opt->shrink_bias_name = NULL; Opt->shrink_bias = NULL; Opt->specie = HUMAN; Opt->Use_emask = 1; Opt->emask = NULL; Opt->PushToEdge = -1; Opt->DoSkulls = 0; Opt->UseThisBrain = NULL; Opt->UseThisBrainHull = NULL; Opt->UseThisSkullOuter = NULL; brk = NOPE; while (kar < argc) { /* loop accross command ine options */ /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/ if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) { usage_SUMA_BrainWrap(ps); exit (0); } SUMA_SKIP_COMMON_OPTIONS(brk, kar); if (!brk && (strcmp(argv[kar], "-pushout") == 0)) { Opt->UseExpansion = 1; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-4Tom") == 0) || (strcmp(argv[kar], "-skulls") == 0) ) ) { Opt->DoSkulls = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-monkey") == 0)) { Opt->specie = MONKEY; brk = YUP; } if (!brk && (strcmp(argv[kar], "-rat") == 0)) { Opt->specie = RAT; brk = YUP; } if (!brk && (strcmp(argv[kar], "-visual") == 0)) { ps->cs->talk_suma = 1; ps->cs->kth = 5; ps->cs->Feed2Afni = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_pushout") == 0)) { Opt->UseExpansion = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-spatnorm") == 0)) { Opt->DoSpatNorm = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_spatnorm") == 0)) { Opt->DoSpatNorm = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-write_spatnorm") == 0)) { Opt->WriteSpatNorm = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-use_skull") == 0)) { Opt->UseSkull = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_use_skull") == 0)) { Opt->UseSkull = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-send_no_skull") == 0)) { Opt->send_hull = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-var_shrink_fac") == 0)) { Opt->var_lzt = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_var_shrink_fac") == 0)) { Opt->var_lzt = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-fill_hole") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -fill_hole \n"); exit (1); } Opt->fillhole = atoi(argv[kar]); if ( (Opt->fillhole < 0 || Opt->fillhole > 50) ) { fprintf (SUMA_STDERR, "parameter after -fill_hole (%d) should be >= 0 and <= 50 \n", Opt->fillhole); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-perc_int") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -perc_int \n"); exit (1); } Opt->PercInt = atof(argv[kar]); if ( (Opt->PercInt < 0 && Opt->PercInt != -1) || Opt->PercInt > 10) { fprintf (SUMA_STDERR, "parameter after -perc_int should be -1 or between 0 and 10 (have %f) \n", Opt->PercInt); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-spatnorm_dxyz") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -spatnorm_dxyz \n"); exit (1); } Opt->SpatNormDxyz = atof(argv[kar]); if ( Opt->SpatNormDxyz < 0 || Opt->SpatNormDxyz > 10) { fprintf (SUMA_STDERR, "parameter after -spatnorm_dxyz should be between 0 and 10 (have %f) \n", Opt->SpatNormDxyz); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-blur_fwhm") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -blur_fwhm \n"); exit (1); } Opt->blur_fwhm = atof(argv[kar]); if ( Opt->blur_fwhm < 0 || Opt->blur_fwhm > 50) { fprintf (SUMA_STDERR, "parameter after -blur_fwhm should be between 0 and 50 (have %f) \n", Opt->blur_fwhm); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-init_radius") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -init_radius \n"); exit (1); } Opt->r = atof(argv[kar]); if ( Opt->r <= 0 || Opt->r > 100) { fprintf (SUMA_STDERR, "parameter after -init_radius should be between 0 and 100 (have %f) \n", Opt->r); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-input_1D") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -input_1D \n"); exit (1); } Opt->in_1D = argv[kar]; brk = YUP; } if (!brk && (strcmp(argv[kar], "-brain_contour_xyz_file") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -brain_contour_xyz_file \n"); exit (1); } Opt->UseThisBrain = argv[kar]; brk = YUP; } if (!brk && (strcmp(argv[kar], "-brain_hull_xyz_file") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -brain_hull_xyz_file \n"); exit (1); } Opt->UseThisBrainHull = argv[kar]; brk = YUP; } if (!brk && (strcmp(argv[kar], "-skull_outer_xyz_file") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -skull_outer_xyz_file \n"); exit (1); } Opt->UseThisSkullOuter = argv[kar]; brk = YUP; } if (!brk && (strcmp(argv[kar], "-avoid_vent") == 0)) { Opt->avoid_vent = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_avoid_vent") == 0)) { Opt->avoid_vent = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-demo_pause") == 0)) { Opt->DemoPause = SUMA_3dSS_DEMO_PAUSE; brk = YUP; } if (!brk && (strcmp(argv[kar], "-interactive") == 0)) { Opt->DemoPause = SUMA_3dSS_INTERACTIVE; brk = YUP; } if (!brk && (strcmp(argv[kar], "-d1") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -d1 \n"); exit (1); } Opt->d1 = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-max_inter_iter") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -max_inter_iter \n"); exit (1); } Opt->MaxIntIter = atoi(argv[kar]); if (Opt->MaxIntIter < 0 || Opt->MaxIntIter > 10) { fprintf (SUMA_STDERR, "-max_inter_iter parameter should be between 0 and 10 \n"); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-smooth_final") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -smooth_final \n"); exit (1); } Opt->smooth_end = atoi(argv[kar]); if (Opt->smooth_end < 0 || Opt->smooth_end > 100 || Opt->smooth_end % 2) { fprintf (SUMA_STDERR, "irrational or exhuberant values for smooth_final\n Must use even number between 0 to 100 (%d entered)\n", Opt->smooth_end); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-NNsmooth") == 0 || strcmp(argv[kar], "-NN_smooth") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -NN_smooth \n"); exit (1); } Opt->NNsmooth = atoi(argv[kar]); if (Opt->NNsmooth % 2) { fprintf (SUMA_STDERR, "Number of smoothing steps must be an even positive number (not %d) \n", Opt->NNsmooth); exit (1); } brk = YUP; } if (!brk && ((strcmp(argv[kar], "-shrink_fac") == 0) || (strcmp(argv[kar], "-bf") == 0))) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -shrink_fac || -bf \n"); exit (1); } Opt->Zt = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-shrink_fac_bot_lim") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -shrink_fac_bot_lim \n"); exit (1); } Opt->bot_lztclip = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-t2") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -t2 \n"); exit (1); } Opt->t2 = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-t98") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -t98 \n"); exit (1); } Opt->t98 = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-t") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -t \n"); exit (1); } Opt->t = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-tm") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -tm \n"); exit (1); } Opt->tm = atof(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-niter") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -niter \n"); exit (1); } Opt->N_it = atoi(argv[kar]); if (Opt->N_it < 0 || Opt->N_it > 100000) { fprintf (SUMA_STDERR, "%d is a bad iteration number.\n", Opt->N_it); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-exp_frac") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -exp_frac \n"); exit (1); } Opt->ExpFrac = atof(argv[kar]); if (Opt->ExpFrac < -1 || Opt->ExpFrac > 1) { /* accroding to algo, should be between 0 and 1 */ fprintf (SUMA_STDERR, "%f is a bad expantion fraction.\n", Opt->ExpFrac); exit (1); } brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-node_dbg") == 0) || (strcmp(argv[kar], "-node_debug") == 0)) ) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -node_debug \n"); exit (1); } Opt->NodeDbg = atoi(argv[kar]); if (Opt->NodeDbg < 0) { fprintf (SUMA_STDERR, "%d is a bad node number.\n", Opt->NodeDbg); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-sm_fac") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -sm_fac \n"); exit (1); } Opt->su1 = atof(argv[kar]); if (Opt->su1 < 0 || Opt->su1 > 1.5) { fprintf (SUMA_STDERR, "%f is a bad smoothness factor (acceptable range is 0 to 1\nbut values up to 1.5 are allowed for amusement).\n", Opt->su1); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-ld") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -ld \n"); exit (1); } Opt->Icold = atoi(argv[kar]); if (Opt->Icold < 0 || Opt->Icold > 300) { fprintf (SUMA_STDERR, "%d is a bad iteration number.\n", Opt->Icold); exit (1); } brk = YUP; } if (!brk && (strcmp(argv[kar], "-debug") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -debug \n"); exit (1); } Opt->debug = atoi(argv[kar]); if (Opt->debug > 2) { LocalHead = YUP; } brk = YUP; } if (!brk && (strcmp(argv[kar], "-input") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -input \n"); exit (1); } Opt->in_name = SUMA_copy_string(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-use_edge") == 0)) { Opt->Use_emask = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_use_edge") == 0)) { Opt->Use_emask = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-push_to_edge") == 0)) { Opt->PushToEdge = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-no_push_to_edge") == 0)) { Opt->PushToEdge = 0; brk = YUP; } if (!brk && (strcmp(argv[kar], "-shrink_fac_bias") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -shrink_fac_bias \n"); exit (1); } Opt->shrink_bias_name = SUMA_copy_string(argv[kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-prefix") == 0)) { kar ++; if (kar >= argc) { fprintf (SUMA_STDERR, "need argument after -prefix \n"); exit (1); } Opt->out_vol_prefix = SUMA_copy_string(argv[kar]); brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-mask_vol") == 0) ) ) { Opt->MaskMode = 1; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-orig_vol") == 0) ) ) { Opt->MaskMode = 2; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-norm_vol") == 0) ) ) { Opt->MaskMode = 0; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-touchup") == 0) ) ) { if (Opt->UseNew < 0) Opt->UseNew = 1.0; else ++ Opt->UseNew; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-no_touchup") == 0) ) ) { Opt->UseNew = 0.0; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-surface_coil") == 0) ) ) { Opt->SurfaceCoil = 1; brk = YUP; } if (!brk && (strcmp(argv[kar], "-k98") == 0)) { SUMA_SL_Warn("Bad option, causes trouble (big clipping) in other parts of brain."); Opt->Kill98 = 1; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-noeyes") == 0) || (strcmp(argv[kar], "-no_eyes") == 0) || (strcmp(argv[kar], "-avoid_eyes") == 0)) ) { Opt->NoEyes = 1; brk = YUP; } if (!brk && ( (strcmp(argv[kar], "-no_noeyes") == 0) || (strcmp(argv[kar], "-no_no_eyes") == 0) || (strcmp(argv[kar], "-no_avoid_eyes") == 0)) ) { Opt->NoEyes = 0; brk = YUP; } if (!brk && !ps->arg_checked[kar]) { fprintf (SUMA_STDERR,"Error %s:\nOption %s not understood. Try -help for usage\n", FuncName, argv[kar]); exit (1); } else { brk = NOPE; kar ++; } } if (Opt->UseNew < 0) { if (Opt->specie != RAT) { Opt->UseNew = Opt->UseNew * -1.0; } else { Opt->UseNew = 0; /* not for ze ghat monsieur */ } } if (Opt->PushToEdge < 0) Opt->PushToEdge = 0; if (Opt->PushToEdge > 0 && Opt->Use_emask <= 0) { fprintf(SUMA_STDERR,"Error %s:\nCannot use -push_to_edge without -use_edge\n", FuncName); exit (1); } if (Opt->bot_lztclip < 0) { if (!Opt->Use_emask) Opt->bot_lztclip = 0.65; else Opt->bot_lztclip = 0.4; } if (Opt->Icold < 0) { if (!Opt->Use_emask) Opt->Icold = 20; else Opt->Icold = 30; } if (Opt->fillhole < 0) { if (Opt->UseExpansion) { if (Opt->debug) { SUMA_SL_Note("Setting fill_hole to 10"); } Opt->fillhole = 10; } else Opt->fillhole = 0; } /* transfer some options to Opt from ps. Clunky because this is retrofitting */ if (ps->o_N_surfnames) { Opt->out_prefix = SUMA_copy_string(ps->o_surfnames[0]); Opt->SurfFileType = ps->o_FT[0]; Opt->SurfFileFormat = ps->o_FF[0]; } if (Opt->DoSkulls && !ps->o_N_surfnames) { fprintf (SUMA_STDERR,"Error %s:\n-skulls must be used in conjunction with -o_TYPE option\n", FuncName); exit(1); } if (!Opt->in_name) { fprintf (SUMA_STDERR,"Error %s:\n-input must be used.\n", FuncName); exit(1); } /* what is the view of the input ?*/ if (!SUMA_AfniView (Opt->in_name, cview)) { fprintf (SUMA_STDERR,"Error %s:\nCould not guess view from input dset %s\n", FuncName, Opt->in_name); exit(1); } if (!Opt->out_prefix) Opt->out_prefix = SUMA_copy_string("skull_strip_out"); if (!Opt->out_vol_prefix) { if (!Opt->out_prefix) Opt->out_vol_prefix = SUMA_AfniPrefix("skull_strip_out", NULL, NULL, &exists); else Opt->out_vol_prefix = SUMA_AfniPrefix(Opt->out_prefix, NULL, NULL, &exists); } else { char *stmp = Opt->out_vol_prefix; Opt->out_vol_prefix = SUMA_AfniPrefix(stmp, NULL, NULL, &exists); SUMA_free(stmp); stmp = NULL; } if (SUMA_AfniExistsView(exists, cview)) { fprintf (SUMA_STDERR,"Error %s:\nOutput dset %s%s exists, will not overwrite\n", FuncName, Opt->out_vol_prefix, cview); exit(1); } if (Opt->t2 >= 0 || Opt->t98 >= 0 || Opt->tm >= 0 || Opt->t >= 0 ) { if (!(Opt->t2 >= 0 && Opt->t98 > 0 && Opt->tm > 0 && Opt->t > 0)){ fprintf (SUMA_STDERR,"Error %s: \nAll or none of the t parameters are to be specified on command line:\nt2 %f t %f tm %f t98 %f\n", FuncName, Opt->t2, Opt->t, Opt->tm, Opt->t98); exit(1); } } SUMA_RETURN(Opt); } int main (int argc,char *argv[]) {/* Main */ static char FuncName[]={"3dSkullStrip"}; int i, N_in = 0, i3, kth_buf, hull_ld; int ii,jj,kk,ll,ijk , nx,ny,nz , nn, nint = 0 , nseg; void *SO_name=NULL, *SO_name_hull=NULL, *SO_name_bhull = NULL, *SO_name_iskull = NULL, *SO_name_oskull = NULL; float vol, *isin_float=NULL, pint, *dsmooth = NULL, XYZrai_shift[3]; SUMA_SurfaceObject *SO = NULL, *SOhull=NULL; SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt; char stmp[200], stmphull[200], *hullprefix=NULL, *prefix=NULL, *spatprefix=NULL, cbuf, *bhullprefix=NULL, *oskullprefix=NULL, *iskullprefix=NULL; SUMA_Boolean exists = NOPE; SUMA_GENERIC_ARGV_PARSE *ps=NULL; short *isin = NULL; SUMA_FORM_AFNI_DSET_STRUCT *OptDs = NULL; THD_3dim_dataset *dset = NULL; THD_ivec3 orixyz , nxyz ; THD_fvec3 dxyz , orgxyz , fv2, originRAIfv; THD_3dim_dataset *oset = NULL; MRI_IMAGE *imin=NULL, *imout=NULL, *imout_orig=NULL ; SUMA_Boolean LocalHead = NOPE; SUMA_STANDALONE_INIT; SUMA_mainENTRY; /* Allocate space for DO structure */ SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS); ps = SUMA_Parse_IO_Args(argc, argv, "-o;-talk;"); if (argc < 2) { usage_SUMA_BrainWrap(ps); exit (1); } Opt = SUMA_BrainWrap_ParseInput (argv, argc, ps); if (Opt->debug > 2) LocalHead = YUP; SO_name = SUMA_Prefix2SurfaceName(Opt->out_prefix, NULL, NULL, Opt->SurfFileType, &exists); if (exists && strcmp(Opt->out_prefix,"skull_strip_out")) { /* do not worry about the default name for the surface */ fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, Opt->out_prefix); exit(1); } bhullprefix = SUMA_append_string(Opt->out_prefix,"_brainhull"); oskullprefix = SUMA_append_string(Opt->out_prefix,"_outerskull"); iskullprefix = SUMA_append_string(Opt->out_prefix,"_innerskull"); SO_name_bhull = SUMA_Prefix2SurfaceName(bhullprefix, NULL, NULL, Opt->SurfFileType, &exists); if (exists && strcmp(Opt->out_prefix,"skull_strip_out")) { fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, Opt->out_prefix); exit(1); } SO_name_iskull = SUMA_Prefix2SurfaceName(iskullprefix, NULL, NULL, Opt->SurfFileType, &exists); if (exists && strcmp(Opt->out_prefix,"skull_strip_out")) { fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, Opt->out_prefix); exit(1); } SO_name_oskull = SUMA_Prefix2SurfaceName(oskullprefix, NULL, NULL, Opt->SurfFileType, &exists); if (exists && strcmp(Opt->out_prefix,"skull_strip_out")) { fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, Opt->out_prefix); exit(1); } hullprefix = SUMA_append_string(Opt->out_prefix,"_hull"); SO_name_hull = SUMA_Prefix2SurfaceName(hullprefix, NULL, NULL, Opt->SurfFileType, &exists); if (exists) { fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, hullprefix); exit(1); } kth_buf = ps->cs->kth; /* keep track of required value */ /* turn on debugging for inodes */ #if 0 SUMA_SL_Note("Debugging for eye nodes"); Opt->dbg_eyenodes = fopen("eyenodes.1D.dset", "w"); #endif /* Load the AFNI volume and prep it*/ if (Opt->DoSpatNorm) { /* chunk taken from 3dSpatNorm.c */ if (Opt->debug) SUMA_SL_Note("Loading dset, performing Spatial Normalization"); /* load the dset */ Opt->iset = THD_open_dataset( Opt->in_name ); if( !ISVALID_DSET(Opt->iset) ){ fprintf(stderr,"**ERROR: can't open dataset %s\n",Opt->in_name) ; exit(1); } Opt->iset_hand = SUMA_THD_handedness( Opt->iset ); if (LocalHead) fprintf(SUMA_STDERR,"%s: Handedness of orig dset %d\n", FuncName, Opt->iset_hand); /*--- get median brick --*/ imin = THD_median_brick( Opt->iset ) ; if( imin == NULL ){ fprintf(stderr,"**ERROR: can't load dataset %s\n",Opt->in_name) ; exit(1); } mri_speciebusiness(Opt->specie); if (Opt->SpatNormDxyz) { if (Opt->debug) SUMA_S_Note("Overriding default resampling"); mri_brainormalize_initialize(Opt->SpatNormDxyz, Opt->SpatNormDxyz, Opt->SpatNormDxyz); } else { float xxdel, yydel, zzdel, minres; if (Opt->specie == MONKEY) minres = 0.5; else if (Opt->specie == RAT) minres = 0.1; else minres = 0.5; /* don't allow for too low a resolution, please */ if (SUMA_ABS(Opt->iset->daxes->xxdel) < minres) xxdel = minres; else xxdel = SUMA_ABS(Opt->iset->daxes->xxdel); if (SUMA_ABS(Opt->iset->daxes->yydel) < minres) yydel = minres; else yydel = SUMA_ABS(Opt->iset->daxes->yydel); if (SUMA_ABS(Opt->iset->daxes->zzdel) < minres) zzdel = minres; else zzdel = SUMA_ABS(Opt->iset->daxes->zzdel); if (Opt->debug) { fprintf(SUMA_STDERR,"%s:\n Original resolution %f, %f, %f\n SpatNorm resolution %f, %f, %f\n", FuncName, Opt->iset->daxes->xxdel, Opt->iset->daxes->yydel, Opt->iset->daxes->zzdel, xxdel, yydel, zzdel); } mri_brainormalize_initialize(xxdel, yydel, zzdel); } if (Opt->d1 < 0) { Opt->d1 = 20.0/THD_BN_rat(); /* account for size difference */ } if (Opt->d4 < 0) { Opt->d4 = 15.0/THD_BN_rat(); /* account for size difference */ } if (Opt->debug > 1) fprintf(SUMA_STDERR,"%s: Size Ratio = %f\n", FuncName, THD_BN_rat()); imin->dx = fabs(Opt->iset->daxes->xxdel) ; imin->dy = fabs(Opt->iset->daxes->yydel) ; imin->dz = fabs(Opt->iset->daxes->zzdel) ; DSET_unload( Opt->iset ) ; /* don't need this data no more */ /*-- convert image to shorts, if appropriate --*/ if( DSET_BRICK_TYPE(Opt->iset,0) == MRI_short || DSET_BRICK_TYPE(Opt->iset,0) == MRI_byte ){ imout = mri_to_short(1.0,imin) ; mri_free(imin) ; imin = imout ; } /*--- normalize image spatially ---*/ mri_brainormalize_verbose( Opt->debug ) ; if (Opt->fillhole) { imout = mri_brainormalize( imin , Opt->iset->daxes->xxorient, Opt->iset->daxes->yyorient, Opt->iset->daxes->zzorient, &imout_orig, NULL) ; } else { imout = mri_brainormalize( imin , Opt->iset->daxes->xxorient, Opt->iset->daxes->yyorient, Opt->iset->daxes->zzorient, &imout_orig, NULL) ; } mri_free( imin ) ; if( imout == NULL ){ fprintf(stderr,"**ERROR: normalization fails!?\n"); exit(1); } if (imout_orig) { if (Opt->debug > 1) SUMA_S_Note("Creating an output dataset in original grid..."); /* me needs the origin of this dset in RAI world */ LOAD_FVEC3(originRAIfv , Opt->iset->daxes->xxorg , Opt->iset->daxes->yyorg , Opt->iset->daxes->zzorg) ; originRAIfv = THD_3dmm_to_dicomm( Opt->iset , originRAIfv ) ; LOAD_FVEC3(fv2 , Opt->iset->daxes->xxorg + (Opt->iset->daxes->nxx-1)*Opt->iset->daxes->xxdel , Opt->iset->daxes->yyorg + (Opt->iset->daxes->nyy-1)*Opt->iset->daxes->yydel , Opt->iset->daxes->zzorg + (Opt->iset->daxes->nzz-1)*Opt->iset->daxes->zzdel ) ; fv2 = THD_3dmm_to_dicomm( Opt->iset , fv2 ) ; if( originRAIfv.xyz[0] > fv2.xyz[0] ) { float tf; tf = originRAIfv.xyz[0]; originRAIfv.xyz[0] = fv2.xyz[0]; fv2.xyz[0] = tf; } if( originRAIfv.xyz[1] > fv2.xyz[1] ) { float tf; tf = originRAIfv.xyz[1]; originRAIfv.xyz[1] = fv2.xyz[1]; fv2.xyz[1] = tf; } if( originRAIfv.xyz[2] > fv2.xyz[2] ) { float tf; tf = originRAIfv.xyz[2]; originRAIfv.xyz[2] = fv2.xyz[2]; fv2.xyz[2] = tf; } if (LocalHead) { fprintf(stderr,"++3dSpatNorm (ZSS): RAI origin info: %f %f %f\n", originRAIfv.xyz[0], originRAIfv.xyz[1], originRAIfv.xyz[2]); } Opt->OrigSpatNormedSet = EDIT_empty_copy( NULL ) ; tross_Copy_History( Opt->iset , Opt->OrigSpatNormedSet ) ; tross_Make_History( "3dSkullStrip" , argc,argv , Opt->OrigSpatNormedSet ) ; LOAD_IVEC3( nxyz , imout_orig->nx , imout_orig->ny , imout_orig->nz ) ; LOAD_FVEC3( dxyz , imout_orig->dx , imout_orig->dy , imout_orig->dz ) ; LOAD_FVEC3( orgxyz , originRAIfv.xyz[0] , originRAIfv.xyz[1] , originRAIfv.xyz[2] ) ; LOAD_IVEC3( orixyz , ORI_R2L_TYPE , ORI_A2P_TYPE , ORI_I2S_TYPE ) ; prefix = SUMA_AfniPrefix(Opt->in_name, NULL, NULL, NULL); if (!prefix) { SUMA_SL_Err("Bad prefix!!!"); exit(1); } spatprefix = SUMA_append_string(prefix, "_SpatNorm_OrigSpace"); EDIT_dset_items( Opt->OrigSpatNormedSet , ADN_prefix , spatprefix , ADN_datum_all , imout_orig->kind , ADN_nxyz , nxyz , ADN_xyzdel , dxyz , ADN_xyzorg , orgxyz , ADN_xyzorient , orixyz , ADN_malloc_type , DATABLOCK_MEM_MALLOC , ADN_view_type , VIEW_ORIGINAL_TYPE , ADN_type , HEAD_ANAT_TYPE , ADN_func_type , ANAT_BUCK_TYPE , ADN_none ) ; EDIT_substitute_brick( Opt->OrigSpatNormedSet , 0 , imout_orig->kind , mri_data_pointer(imout_orig) ) ; oset = r_new_resam_dset ( Opt->OrigSpatNormedSet, Opt->iset, 0, 0, 0, NULL, MRI_LINEAR, NULL); if (!oset) { fprintf(stderr,"**ERROR: Failed to reslice!?\n"); exit(1); } EDIT_dset_items( oset , ADN_prefix , spatprefix, ADN_none ) ; tross_Copy_History( Opt->OrigSpatNormedSet , oset ) ; DSET_delete(Opt->OrigSpatNormedSet); Opt->OrigSpatNormedSet = oset; oset = NULL; if (Opt->WriteSpatNorm) { if (Opt->debug) SUMA_S_Note("Writing SpatNormed dset in original space"); DSET_write(Opt->OrigSpatNormedSet) ; } if (prefix) SUMA_free(prefix); prefix = NULL; if (spatprefix) SUMA_free(spatprefix); spatprefix = NULL; } oset = EDIT_empty_copy( NULL ) ; /* reset the idcode using a hash of a string formed by idcode or orig dset and _Spatnorm */ { char idstr[500], *nid=NULL; sprintf(idstr,"%s_Spatnorm", Opt->iset->idcode.str); SUMA_NEW_ID(nid, idstr); strncpy(oset->idcode.str, nid, IDCODE_LEN); SUMA_free(nid); nid = NULL;} tross_Copy_History( Opt->iset , oset ) ; tross_Make_History( "3dSkullStrip" , argc,argv , oset ) ; LOAD_IVEC3( nxyz , imout->nx , imout->ny , imout->nz ) ; LOAD_FVEC3( dxyz , imout->dx , imout->dy , imout->dz ) ; LOAD_FVEC3( orgxyz , imout->xo , imout->yo , imout->zo ) ; LOAD_IVEC3( orixyz , ORI_R2L_TYPE , ORI_A2P_TYPE , ORI_I2S_TYPE ) ; prefix = SUMA_AfniPrefix(Opt->in_name, NULL, NULL, NULL); if (!prefix) { SUMA_SL_Err("Bad prefix!!!"); exit(1); } spatprefix = SUMA_append_string(prefix, "_SpatNorm"); EDIT_dset_items( oset , ADN_prefix , spatprefix , ADN_datum_all , imout->kind , ADN_nxyz , nxyz , ADN_xyzdel , dxyz , ADN_xyzorg , orgxyz , ADN_xyzorient , orixyz , ADN_malloc_type , DATABLOCK_MEM_MALLOC , ADN_view_type , VIEW_ORIGINAL_TYPE , ADN_type , HEAD_ANAT_TYPE , ADN_func_type , ANAT_BUCK_TYPE , ADN_none ) ; EDIT_substitute_brick( oset , 0 , imout->kind , mri_data_pointer(imout) ) ; if (Opt->WriteSpatNorm) { SUMA_LH("Writing SpatNormed dset"); DSET_write(oset) ; } Opt->in_vol = oset; oset = NULL; if (prefix) SUMA_free(prefix); prefix = NULL; if (spatprefix) SUMA_free(spatprefix); spatprefix = NULL; if( Opt->debug ) fprintf(SUMA_STDERR,"%s: -spatnorm: Expecting %d voxels in in_vol dset (%d %d %d)\n", FuncName, DSET_NVOX( Opt->in_vol ), DSET_NX( Opt->in_vol ), DSET_NY( Opt->in_vol ), DSET_NZ( Opt->in_vol )); } else { /* volume already SpatNormed, or user does not want SpatNorm */ SUMA_SL_Note("Loading dset, performing no Spatial Normalization"); Opt->in_vol = THD_open_dataset( Opt->in_name ); if( !ISVALID_DSET(Opt->in_vol) ){ fprintf(stderr,"**ERROR: can't open dataset %s\n",Opt->in_name) ; exit(1); } if( Opt->debug ) fprintf(SUMA_STDERR,"%s: -no_spatnorm: Expecting %d voxels in in_vol dset (%d %d %d)\n", FuncName, DSET_NVOX( Opt->in_vol ), DSET_NX( Opt->in_vol ), DSET_NY( Opt->in_vol ), DSET_NZ( Opt->in_vol )); /* load the dset */ DSET_load(Opt->in_vol); if (Opt->fillhole) Opt->OrigSpatNormedSet = Opt->in_vol; /* original is same as in_vol */ /* initialize, just to make sure numbers are ok for if statement below */ mri_speciebusiness(Opt->specie); mri_brainormalize_initialize(Opt->in_vol->daxes->xxdel, Opt->in_vol->daxes->yydel, Opt->in_vol->daxes->zzdel); if (DSET_NX( Opt->in_vol) != THD_BN_nx() || DSET_NY( Opt->in_vol) != THD_BN_ny() || DSET_NZ( Opt->in_vol) != THD_BN_nz() ) { fprintf(SUMA_STDERR,"Error %s:\n SpatNormed Dset must be %d x %d x %d\n", FuncName, THD_BN_nx(), THD_BN_ny(), THD_BN_nz() ); exit(1); } if (Opt->d1 < 0) { Opt->d1 = 20.0/THD_BN_rat(); /* half as big */ } if (Opt->d4 < 0) { Opt->d4 = 15.0/THD_BN_rat(); /* half as big */ } if (LocalHead) fprintf(SUMA_STDERR,"%s: Size factor = %f\n", FuncName, THD_BN_rat()); Opt->iset_hand = SUMA_THD_handedness( Opt->in_vol ); if (LocalHead) fprintf(SUMA_STDERR,"%s: Handedness of orig dset %d\n", FuncName, Opt->iset_hand); } /* set the travel step based on the resolution of the voxels */ Opt->travstp = SUMA_MIN_PAIR(SUMA_ABS(Opt->in_vol->daxes->xxdel), SUMA_ABS(Opt->in_vol->daxes->yydel)); Opt->travstp = SUMA_MIN_PAIR(Opt->travstp, SUMA_ABS(Opt->in_vol->daxes->zzdel)); if (!ISVALID_DSET(Opt->in_vol)) { if (!Opt->in_name) { SUMA_SL_Err("NULL input volume."); exit(1); } else { SUMA_SL_Err("invalid volume."); exit(1); } } else if ( DSET_BRICK_TYPE(Opt->in_vol, 0) == MRI_complex) { SUMA_SL_Err("Can't do complex data."); exit(1); } /* calculate an edge mask ? */ if (Opt->Use_emask) { float *emask_sort = NULL, PercRange[2]= { 10, 90 }, PercRangeVal[2] = { 0.0, 0.0 }; Opt->emask = (float *) SUMA_malloc( sizeof(float)*DSET_NVOX(Opt->in_vol)); if (!Opt->emask) { fprintf(SUMA_STDERR,"Error %s:\n Failed to allocate for edge mask\n", FuncName); exit(1); } { if (0) { /* to get a fattened mask too */ THD_3dim_dataset *outset=NULL; THD_3dim_dataset *fatoutset=NULL; if (!SUMA_3dedge3(Opt->in_vol, Opt->emask, &outset)) { fprintf(SUMA_STDERR , "ERROR: gradient extraction failed.\n" ); exit( 1 ); } /* create a fat mask please */ { /* need a block for compiling 18 Apr 2006 [rickr] */ int code[3], ncode; MCW_cluster *nbhd=NULL ; if (Opt->debug) { SUMA_S_Note("Fattening the edge mask"); } code[0] = NSTAT_MEAN; ncode = 1; nbhd = MCW_rectmask ( 1.0f, 1.0f, 1.0f , 1.0f, 1.0f, 1.0f ) ; fatoutset = THD_localstat( outset , NULL , nbhd , 1 , code ) ; Opt->fatemask = (float *) SUMA_malloc( sizeof(float)*DSET_NVOX(fatoutset)); if (!Opt->fatemask) { fprintf(SUMA_STDERR,"Error %s:\n Failed to allocate for fat edge mask\n", FuncName); exit(1); } EDIT_coerce_scale_type( DSET_NVOX(fatoutset) , DSET_BRICK_FACTOR(fatoutset,0) , DSET_BRICK_TYPE(fatoutset,0), DSET_ARRAY(fatoutset, 0) , /* input */ MRI_float , Opt->fatemask ) ; /* output */ if (DSET_NVOX(fatoutset) != DSET_NVOX(outset) || DSET_NVOX(fatoutset) != DSET_NVOX(Opt->in_vol)) { SUMA_S_Err("Bad news in tennis shoes!"); exit(1); } if (Opt->debug > 2) { THD_write_3dim_dataset( NULL,NULL , fatoutset , True ) ; fprintf(stderr," ++ Wrote output: %s\n",DSET_BRIKNAME(fatoutset)) ; } } if (outset) DSET_delete(outset) ; outset = NULL; if (fatoutset) DSET_delete(fatoutset) ; fatoutset = NULL; } else { /* no fat junk */ if (!SUMA_3dedge3(Opt->in_vol, Opt->emask, NULL)) { fprintf(SUMA_STDERR , "ERROR: gradient extraction failed.\n" ); exit( 1 ); } } } /* get the mask threshold */ PercRange[0] = 92; PercRange[1] = 99.999; emask_sort = SUMA_PercRange (Opt->emask, NULL, DSET_NVOX(Opt->in_vol), PercRange, PercRangeVal, NULL); if (!emask_sort) { fprintf( stderr, "ERROR: mask sorting failed.\n" ); exit( 1 ); } else { SUMA_free(emask_sort); emask_sort = NULL; } /* The minimum acceptable edge is at least one tenth of the 99.999 percentile edge */ if (PercRangeVal[0] < PercRangeVal[1]/10.0) PercRangeVal[0] = PercRangeVal[1]/10.0; if (Opt->debug) fprintf (SUMA_STDERR, "%s: Edge threshold set to %f (perhaps from %f percentile, minimum acceptable was %f)\n" " (%f percentile = %f)\n", FuncName, PercRangeVal[0], PercRange[0], PercRange[1]/10.0, PercRange[1], PercRangeVal[1]); /* Now that we have an edge vector, select appropriate values */ for (ii=0; ii<DSET_NVOX(Opt->in_vol); ++ii) { if (Opt->emask[ii] < PercRangeVal[0]) Opt->emask[ii] = 0.0; } if (Opt->fatemask) { /* same for the fat mask */ PercRange[0] = 90; PercRange[1] = 95; emask_sort = SUMA_PercRange (Opt->fatemask, NULL, DSET_NVOX(Opt->in_vol), PercRange, PercRangeVal, NULL); if (!emask_sort) { fprintf( stderr, "ERROR: mask sorting failed.\n" ); exit( 1 ); } else { SUMA_free(emask_sort); emask_sort = NULL; } if (Opt->debug) fprintf (SUMA_STDERR,"%s: Fat Edge threshold set to %f (from %f percentile) , (%f percentile = %f)\n", FuncName, PercRangeVal[0], PercRange[0], PercRange[1], PercRangeVal[1]); /* Now that we have an edge vector, select appropriate values */ for (ii=0; ii<DSET_NVOX(Opt->in_vol); ++ii) { if (Opt->fatemask[ii] < PercRangeVal[0]) Opt->fatemask[ii] = 0.0; } } } if (Opt->blur_fwhm) { if (Opt->debug) fprintf (SUMA_STDERR,"%s: Blurring...\n", FuncName); EDIT_blur_volume( DSET_NX(Opt->in_vol), DSET_NY(Opt->in_vol), DSET_NZ(Opt->in_vol), SUMA_ABS((DSET_DX(Opt->in_vol))), SUMA_ABS((DSET_DY(Opt->in_vol))), SUMA_ABS((DSET_DZ(Opt->in_vol))), DSET_BRICK_TYPE(Opt->in_vol,0), DSET_ARRAY(Opt->in_vol,0), 0.42466090*Opt->blur_fwhm) ; } if (Opt->UseSkull) { SOhull = SUMA_Alloc_SurfObject_Struct(1); } else SOhull = NULL; sprintf(stmphull,"OuterHull"); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Prepping volume...\n", FuncName); vol = SUMA_LoadPrepInVol (Opt, &SOhull); if ( vol <= 0 ) { SUMA_S_Err("Failed to load/prep volume"); exit(1); } else { if (LocalHead) fprintf (SUMA_STDERR,"%s: Got me a volume of %f mm3\n", FuncName, vol); } /* create the ico, might need coords for bias correction below*/ SO = SUMA_CreateIcosahedron (Opt->r/2.0, Opt->Icold, Opt->cog, "n", 1); if (!SO) { SUMA_S_Err("Failed to create Icosahedron"); exit(1); } /* Need Brain_Contour holder at the very least*/ if (!Opt->Brain_Contour) Opt->Brain_Contour = (float *)SUMA_calloc(SO->N_Node * 3, sizeof(float)); /* allocate and initialize shrink bias vector */ { int nico = (2 + 10 * Opt->Icold * Opt->Icold ); Opt->shrink_bias = (float *)SUMA_calloc(nico, sizeof(float)); if (!Opt->shrink_bias) { SUMA_SL_Crit("Failed to allocate"); exit(1); } for (i=0; i<nico; ++i) Opt->shrink_bias[i] = 1.0; if (Opt->shrink_bias_name) { MRI_IMAGE *im = NULL; float *far = NULL; /* load the shrink_bias_file */ im = mri_read_1D(Opt->shrink_bias_name); if (!im) { SUMA_SL_Err("Failed to read 1D file of shrink factor bias file."); exit(1);} far = MRI_FLOAT_PTR(im); if (im->ny != 2) { SUMA_SL_Err("Need 2 columns in shrink factor bias file."); exit(1); } if (im->nx > nico) { fprintf(SUMA_STDERR, "Error %s: File too big. Maximum number of lines (%d) should not exceed %d,\n" "which is the number of nodes forming the surface.\n", FuncName, im->nx, nico); exit(1); } /* change to row major major and make it match nodeind */ for (i=0; i<im->nx; ++i) { if (far[i] < nico) { Opt->shrink_bias[(int)far[i]] = far[i+ im->nx]; } else { fprintf(SUMA_STDERR, "Error %s: Node index of (%d) in shrink factor bias file\n" "exceeds maximum allowed node index (%d) for surface.\n", FuncName, (int)far[i], nico-1); exit(1); } } mri_free(im); im = NULL; /* done with that baby */ } else if (Opt->specie == RAT) { #if 0 float p1[3], Y[3], Z[3], U[3], Un, dotz, doty; Y[0] = 0.0; Y[1] = 1.0; Y[2] = 0.0; /* The Y direction */ Z[0] = 0.0; Z[1] = 0.0; Z[2] = 1.0; /* The Z direction */ /* less on top, and sides, more in front */ for (i=0; i<SO->N_Node; ++i) { /* vector from center to node */ p1[0] = SO->NodeList[3*i ]; p1[1] = SO->NodeList[3*i+1]; p1[2] = SO->NodeList[3*i+2]; SUMA_UNIT_VEC(Opt->cog, p1, U, Un); SUMA_DOTP_VEC(U,Y, doty , 3,float,float); SUMA_DOTP_VEC(U,Z, dotz , 3,float,float); Opt->shrink_bias[i] = ((1.5 - (SUMA_ABS(doty)*(1-SUMA_ABS(dotz))))) ; } if (Opt->debug) { snprintf(stmp, 198, "%s_shrink_bias.1D.dset", Opt->out_vol_prefix); SUMA_WRITE_ARRAY_1D(Opt->shrink_bias,SO->N_Node,1, stmp); } #endif } else if (Opt->specie == MONKEY) { #if 0 float p1[3], Y[3], Z[3], U[3], Un, dotz, doty; Y[0] = 0.0; Y[1] = 1.0; Y[2] = 0.0; /* The Y direction */ Z[0] = 0.0; Z[1] = 0.0; Z[2] = 1.0; /* The Z direction */ /* less on top, and sides, more in front */ for (i=0; i<SO->N_Node; ++i) { /* vector from center to node */ p1[0] = SO->NodeList[3*i ]; p1[1] = SO->NodeList[3*i+1]; p1[2] = SO->NodeList[3*i+2]; SUMA_UNIT_VEC(Opt->cog, p1, U, Un); SUMA_DOTP_VEC(U,Y, doty , 3,float,float); SUMA_DOTP_VEC(U,Z, dotz , 3,float,float); if (doty < 0 && dotz > 0) { /* loosen front upper of brain */ Opt->shrink_bias[i] = ((1.1 - (SUMA_ABS(doty)*(1-(dotz))))) ; } } if (Opt->debug) { snprintf(stmp, 198, "%s_shrink_bias.1D.dset", Opt->out_vol_prefix); SUMA_WRITE_ARRAY_1D(Opt->shrink_bias,SO->N_Node,1, stmp); } #endif } } if (Opt->debug) fprintf (SUMA_STDERR,"%s: Beginning brain extraction...\n", FuncName); do { /* Now create that little sphere that is about to expand */ sprintf(stmp,"icobaby_ld%d", Opt->Icold); if (SO) { if (Opt->debug) fprintf (SUMA_STDERR,"%s: Have surface, OK for 1st entry.\n", FuncName); } else { if (Opt->debug) fprintf (SUMA_STDERR,"%s: Creating Ico.\n", FuncName); SO = SUMA_CreateIcosahedron (Opt->r/2.0, Opt->Icold, Opt->cog, "n", 1); if (!SO) { SUMA_S_Err("Failed to create Icosahedron"); exit(1); } } if (Opt->Stop) SUMA_free(Opt->Stop); Opt->Stop = NULL; if (!Opt->Stop) { Opt->Stop = (float *)SUMA_calloc(SO->N_Node, sizeof(float)); if (!Opt->Stop) { SUMA_SL_Crit("Failed to allocate"); exit(1); } } if (Opt->avoid_vent && Opt->specie != RAT) { /* No need for this avoidance in rodents*/ float U[3], Un, *a, P2[2][3]; for (i=0; i<SO->N_Node; ++i) { /* stretch the top coordinates by d1 and the back too*/ a = &(SO->NodeList[3*i]); if (a[2] - SO->Center[2] > Opt->r/3 || a[1] - SO->Center[1] > Opt->r/2) { SUMA_UNIT_VEC(SO->Center, a, U, Un); SUMA_POINT_AT_DISTANCE_NORM(U, SO->Center, (Un+1.1*Opt->d1), P2); SO->NodeList[3*i] = P2[0][0]; SO->NodeList[3*i+1] = P2[0][1]; SO->NodeList[3*i+2] = P2[0][2]; } } } /* allocate and fix zt */ Opt->ztv = (float *)SUMA_malloc(sizeof(float)*SO->N_Node); if (!Opt->ztv) { SUMA_SL_Crit("Failed to allocate"); exit(1); } for (i=0; i<SO->N_Node; ++i) Opt->ztv[i] = SUMA_MIN_PAIR(1.0, (Opt->Zt*Opt->shrink_bias[i])); /* need sv for communication to AFNI */ SO->VolPar = SUMA_VolParFromDset (Opt->in_vol); SO->SUMA_VolPar_Aligned = YUP; /* Surface is in alignment with volume, should not call SUMA_Align_to_VolPar ... */ if (!SO->State) {SO->State = SUMA_copy_string("3dSkullStrip"); } if (!SO->Group) {SO->Group = SUMA_copy_string("3dSkullStrip"); } if (!SO->Label) {SO->Label = SUMA_copy_string(stmp); } /* make the idcode_str depend on the Label, it is convenient to send the same surface all the time to SUMA */ if (SO->Label) { if (SO->idcode_str) SUMA_free(SO->idcode_str); SO->idcode_str = NULL; SUMA_NEW_ID(SO->idcode_str, SO->Label); } if (!nint && SOhull) { SOhull->VolPar = SUMA_VolParFromDset (Opt->in_vol); SOhull->SUMA_VolPar_Aligned = YUP; /* Surface is in alignment with volume, should not call SUMA_Align_to_VolPar ... */ if (!SOhull->State) {SOhull->State = SUMA_copy_string("3dSkullStrip"); } if (!SOhull->Group) {SOhull->Group = SUMA_copy_string("3dSkullStrip"); } if (!SOhull->Label) {SOhull->Label = SUMA_copy_string(stmphull); } if (SOhull->Label) { if (SOhull->idcode_str) SUMA_free(SOhull->idcode_str); SOhull->idcode_str = NULL; SUMA_NEW_ID(SOhull->idcode_str,SOhull->Label); } } /* see if SUMA talk is turned on */ if (ps->cs->talk_suma) { ps->cs->istream = SUMA_BRAINWRAP_LINE; ps->cs->afni_istream = SUMA_AFNI_STREAM_INDEX2; ps->cs->kth = 1; /* make sure all surfaces get sent */ if (!SUMA_SendToSuma (NULL, ps->cs, NULL, SUMA_NO_DSET_TYPE, 0)) { SUMA_SL_Err("Failed to initialize SUMA_SendToSuma"); ps->cs->Send = NOPE; ps->cs->afni_Send = NOPE; ps->cs->talk_suma = NOPE; } else if (!SUMA_SendToAfni (ps->cs, NULL, 0)) { SUMA_SL_Err("Failed to initialize SUMA_SendToAfni"); ps->cs->afni_Send = NOPE; ps->cs->Send = NOPE; } else { if (!nint && SOhull) { if (Opt->send_hull) { SUMA_LH("Sending Hull"); if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Sending HULL next"); } SUMA_SendSumaNewSurface(SOhull, ps->cs); } } SUMA_LH("Sending Ico"); if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Sending Ico next"); } SUMA_SendSumaNewSurface(SO, ps->cs); } ps->cs->kth = kth_buf; } if (!nint && Opt->UseSkull) { /* get a crude mask of outer skull */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Shrinking skull hull next"); } SUMA_SkullMask (SOhull, Opt, ps->cs); /* Now take mask and turn it into a volume */ fprintf (SUMA_STDERR,"%s: Locating voxels on skull boundary ...\n", FuncName); isin = SUMA_FindVoxelsInSurface (SOhull, SO->VolPar, &N_in, 0, NULL); for (i=0; i<SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz; ++i) { if (isin[i] <= SUMA_ON_NODE) Opt->fvec[i] = 0; } #if 0 SUMA_SL_Note("Writing hull mask"); { FILE *fout=fopen("hullmask.1D","w"); int ii, jj, kk; for (i=0; i<SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz; ++i) { SUMA_1D_2_3D_index(i, ii, jj, kk, SO->VolPar->nx, SO->VolPar->nx*SO->VolPar->ny); fprintf(fout,"%d %d %d %d\n",ii, jj, kk, isin[i]); } fclose(fout); } #endif if (isin) SUMA_free(isin); isin = NULL; } /* send in_vol to afni */ if (Opt->DoSpatNorm && ps->cs->afni_Send) { SUMA_SL_Note("Sending spatnormed volume to AFNI"); if (!SUMA_SendToAfni(ps->cs, Opt->in_vol, 1)) { SUMA_SL_Err("Failed to send volume to AFNI"); ps->cs->afni_Send = NOPE; } } /* This is it baby, start walking */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Brain expansion next"); } if ( !Opt->UseThisBrain ) { SUMA_StretchToFitLeCerveau (SO, Opt, ps->cs); } else { /* DEVELOPMENT ONLY! */ int ncol, nrow; float *far=SUMA_Load1D_s(Opt->UseThisBrain, &ncol, &nrow, 1, 0); if (!far || nrow != SO->N_Node || ncol != 3) { fprintf(SUMA_STDERR,"Error %s: SO has %d nodes, your coord file has %d cols, %d rows.\n", FuncName, SO->N_Node, ncol, nrow); exit(1); } else { fprintf(SUMA_STDERR,"Warning %s: adopting coordinates from %s\n", FuncName, Opt->UseThisBrain); } memcpy((void*)SO->NodeList,(void*)far, sizeof(float)*ncol*nrow); free(far); far = NULL; Opt->PercInt = -1; /* cancel intersection checking */ } /* Make a copy to brain contours, in case user jumps ahead */ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); /* check for intersections */ if (Opt->PercInt >= 0) { if (Opt->debug) fprintf(SUMA_STDERR,"%s: Checking for self intersection...\n", FuncName); nseg = 30 * Opt->Icold * Opt->Icold; /* number of segments in Ico */ nint = SUMA_isSelfIntersect(SO, (int)(Opt->PercInt * nseg / 100.0), NULL); if (nint < 0) { SUMA_SL_Err("Error in SUMA_isSelfIntersect. Ignoring self intersection test."); nint = 0; } /* percentage of guilty segments */ pint = (float)nint / (float)nseg * 100; if (LocalHead) fprintf (SUMA_STDERR,"%s: at least %d (%f%%) of segments intersect the surface.\nStopping criterion is %f%%\n", FuncName, nint, pint, Opt->PercInt); if (pint > Opt->PercInt) { static int SelfIntRep = 0; int smstep; if (SelfIntRep < Opt->MaxIntIter) { /* SUMA_PAUSE_PROMPT("SelfIntersection Baby"); */ if (!Opt->avoid_vent && !SelfIntRep) { Opt->avoid_vent = 1; } else { smstep = 12 * ( Opt->Icold / 25 ) * ( Opt->Icold / 25 ); /* 12 is OK for ld = 25 */ if ( smstep < 12) smstep = 12; else if ( smstep % 2 ) ++smstep; #if 0 if (!(SelfIntRep % 2)) { Opt->avoid_vent = 0; Opt->NNsmooth += smstep; } else { Opt->avoid_vent = 1; } #else /* don't mess with avoid_vent option, it works well all the time */ Opt->NNsmooth += smstep; #endif ++SelfIntRep; } fprintf(SUMA_STDERR,"Warning %s:****************\n Surface self intersecting! trying again:\n smoothing of %d, avoid_vent %d\n", FuncName, Opt->NNsmooth, (int)Opt->avoid_vent); SUMA_Free_Surface_Object(SO); SO = NULL; } else { fprintf(SUMA_STDERR,"Warning %s: Stubborn intersection remaining at smoothing of %d. Ignoring it.", FuncName, Opt->NNsmooth); nint = 0; } } else { if (Opt->debug) { if (nint) fprintf(SUMA_STDERR,"%s: Number of intersection below criterion.\n", FuncName); else fprintf(SUMA_STDERR,"%s: No intersections found.\n", FuncName); } nint = 0; } } else { fprintf(SUMA_STDERR,"%s: Self intersection check turned off.\n", FuncName); nint = 0; } } while (nint != 0); if ( !Opt->UseThisBrain ) { if (Opt->debug) fprintf(SUMA_STDERR,"%s: Final smoothing of %d\n", FuncName, Opt->NNsmooth); if (SUMA_DidUserQuit()) { if (Opt->debug) fprintf(SUMA_STDERR,"%s: straight to end per user demand (%d)...\n", FuncName, SUMA_DidUserQuit()); goto FINISH_UP; } /* touch up, these might cause some surface intersection, but their effects should be small */ /* TOUCHUP_1: */ if (Opt->UseNew) { double mval = 255; if (Opt->debug) fprintf (SUMA_STDERR,"%s: Touchup correction, pass 1 ...\n", FuncName); if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("touchup correction next"); } if (Opt->DemoPause == SUMA_3dSS_INTERACTIVE) { /* Make a copy to brain contours, in case user jumps ahead */ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); fprintf (SUMA_STDERR,"3dSkullStrip Interactive: \n" "Touchup, pass 1.\n" "Do you want to (C)ontinue, (P)ass or (S)ave this? "); cbuf = SUMA_ReadCharStdin ('c', 0,"csp"); switch (cbuf) { case 's': fprintf (SUMA_STDERR,"Saving mask as is.\n"); goto FINISH_UP; break; case 'p': fprintf (SUMA_STDERR,"Passing this stage \n"); goto PUSH_TO_EDGE; break; case 'c': fprintf (SUMA_STDERR,"Continuing with stage.\n"); break; } } /* recover the eye balls please */ if (mval < Opt->t98) { SUMA_SL_Warn("Looks like some values in dset might be larger than 255 !"); mval = Opt->t98+10; } if (Opt->k98maskcnt && Opt->k98mask) { for (ii=0; ii<Opt->k98maskcnt; ++ii) Opt->fvec[Opt->k98mask[ii]] = mval; } /* SUMA_REPOSITION_TOUCHUP(6);*/ SUMA_Reposition_Touchup(SO, Opt, 6, ps->cs); if (LocalHead) fprintf (SUMA_STDERR,"%s: Touchup correction Done.\n", FuncName); } PUSH_TO_EDGE: if (Opt->PushToEdge) { fprintf (SUMA_STDERR,"%s: Pushing to Edge ...\n", FuncName); ps->cs->kth = 1; /*make sure all gets sent at this stage */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Push To Edge Correction"); } if (Opt->debug) fprintf (SUMA_STDERR,"%s: Push to edge correction ...\n", FuncName); if (Opt->DemoPause == SUMA_3dSS_INTERACTIVE) { /* Make a copy to brain contours, in case user jumps ahead */ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); fprintf (SUMA_STDERR,"3dSkullStrip Interactive: \n" "Push To Edge.\n" "Do you want to (C)ontinue, (P)ass or (S)ave this? "); cbuf = SUMA_ReadCharStdin ('c', 0,"csp"); fprintf (SUMA_STDERR,"%c\n", cbuf); switch (cbuf) { case 's': fprintf (SUMA_STDERR,"Saving mask as is.\n"); goto FINISH_UP; break; case 'p': fprintf (SUMA_STDERR,"Passing this stage \n"); goto BEAUTY; break; case 'c': fprintf (SUMA_STDERR,"Continuing with stage.\n"); break; } } { int il = 0; float dtroub = 1.0; int N_troub = 1, past_N_troub = 0; while (il < 5 && dtroub > 0.1 && N_troub) { N_troub = SUMA_PushToEdge(SO, Opt, 4, ps->cs, 0); SUMA_RECOMPUTE_NORMALS(SO); if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } if (!past_N_troub) { past_N_troub = N_troub; if (LocalHead) fprintf (SUMA_STDERR,"%s: \n PushToEdge, pass %d %d troubled nodes, going for more...\n", FuncName, il, N_troub); } else { dtroub = (float)(past_N_troub - N_troub)/(float)past_N_troub; if (LocalHead) fprintf (SUMA_STDERR,"%s: \n PushToEdge, pass %d : %f change in troubled nodes.\n", FuncName, il, dtroub); past_N_troub = N_troub; } ++il; } } if (LocalHead) fprintf (SUMA_STDERR,"%s: Edge push correction Done.\n", FuncName); ps->cs->kth = kth_buf; } BEAUTY: /* smooth the surface a bit */ if (Opt->smooth_end) { ps->cs->kth = 1; /*make sure all gets sent at this stage */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("beauty treatment smoothing next"); } if (Opt->debug) fprintf (SUMA_STDERR,"%s: The beauty treatment smoothing.\n", FuncName); if (Opt->DemoPause == SUMA_3dSS_INTERACTIVE) { /* Make a copy to brain contours, in case user jumps ahead */ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); fprintf (SUMA_STDERR,"3dSkullStrip Interactive: \n" "Beauty treatment smoothing.\n" "Do you want to (C)ontinue, (P)ass or (S)ave this? "); cbuf = SUMA_ReadCharStdin ('c', 0,"csp"); switch (cbuf) { case 's': fprintf (SUMA_STDERR,"Saving mask as is.\n"); goto FINISH_UP; break; case 'p': fprintf (SUMA_STDERR,"Passing this stage \n"); goto TOUCHUP_2; break; case 'c': fprintf (SUMA_STDERR,"Continuing with stage.\n"); break; } } dsmooth = SUMA_Taubin_Smooth( SO, NULL, 0.6307, -.6732, SO->NodeList, Opt->smooth_end, 3, SUMA_ROW_MAJOR, dsmooth, ps->cs, NULL, 0); memcpy((void*)SO->NodeList, (void *)dsmooth, SO->N_Node * 3 * sizeof(float)); SUMA_RECOMPUTE_NORMALS(SO); if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } ps->cs->kth = kth_buf; } TOUCHUP_2: /* one more correction pass */ if (Opt->UseNew > 1.0) { ps->cs->kth = 1; /*make sure all gets sent at this stage */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("touchup correction 2 next"); } if (Opt->debug) fprintf (SUMA_STDERR,"%s: Final touchup correction ...\n", FuncName); if (Opt->DemoPause == SUMA_3dSS_INTERACTIVE) { /* Make a copy to brain contours, in case user jumps ahead */ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); fprintf (SUMA_STDERR,"3dSkullStrip Interactive: \n" "Touchup, pass 2.\n" "Do you want to (C)ontinue, (P)ass or (S)ave this? "); cbuf = SUMA_ReadCharStdin ('c', 0,"csp"); fprintf (SUMA_STDERR,"%c\n", cbuf); switch (cbuf) { case 's': fprintf (SUMA_STDERR,"Saving mask as is.\n"); goto FINISH_UP; break; case 'p': fprintf (SUMA_STDERR,"Passing this stage \n"); goto PUSH_TO_OUTER_SKULL; break; case 'c': fprintf (SUMA_STDERR,"Continuing with stage.\n"); break; } } /* SUMA_REPOSITION_TOUCHUP(2); */ SUMA_Reposition_Touchup(SO, Opt, 2, ps->cs); if (LocalHead) fprintf (SUMA_STDERR,"%s: Final touchup correction Done.\n", FuncName); ps->cs->kth = kth_buf; } } else { SUMA_S_Note("Using brain read from file"); } /* Put brain contours in Brain_Contour*/ memcpy((void*)Opt->Brain_Contour, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); PUSH_TO_OUTER_SKULL: if (Opt->DoSkulls) { if (ps->cs->Send) ps->cs->kth = 1; /* make sure all surfaces get sent */ if (Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE) { SUMA_PAUSE_PROMPT("Skull detection next"); } if (Opt->debug) fprintf (SUMA_STDERR,"%s: Skull detection next ...\n", FuncName); if (Opt->DemoPause == SUMA_3dSS_INTERACTIVE) { fprintf (SUMA_STDERR,"3dSkullStrip Interactive: \n" "Skull detection.\n" "Do you want to (C)ontinue, (P)ass or (S)ave this? "); cbuf = SUMA_ReadCharStdin ('c', 0,"csp"); fprintf (SUMA_STDERR,"%c\n", cbuf); switch (cbuf) { case 's': fprintf (SUMA_STDERR,"Saving mask as is.\n"); goto FINISH_UP; break; case 'p': fprintf (SUMA_STDERR,"Passing this stage \n"); goto FINISH_UP; break; case 'c': fprintf (SUMA_STDERR,"Continuing with stage.\n"); break; } } if (!Opt->UseThisBrainHull) { /* first get a convex hull volume of the brain surface */ SUMA_Push_Nodes_To_Hull(SO, Opt, ps->cs, 15); } else { /* DEVELOPMENT ONLY! */ int ncol, nrow; float *far=SUMA_Load1D_s(Opt->UseThisBrainHull, &ncol, &nrow, 1, 0); if (!far || nrow != SO->N_Node || ncol != 3) { fprintf(SUMA_STDERR,"Error %s: SO has %d nodes, your coord file has %d cols, %d rows.\n", FuncName, SO->N_Node, ncol, nrow); exit(1); } else { fprintf(SUMA_STDERR,"Warning %s: adopting brain hull coordinates from %s\n", FuncName, Opt->UseThisBrainHull); } memcpy((void*)SO->NodeList,(void*)far, sizeof(float)*ncol*nrow); free(far); far = NULL; } Opt->Brain_Hull = (float *)SUMA_calloc(SO->N_Node * 3, sizeof(float)); memcpy((void*)Opt->Brain_Hull, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } if (!Opt->UseThisSkullOuter) { if (Opt->debug) fprintf (SUMA_STDERR,"%s: Push to Outer Skull ... \n", FuncName); if ( LocalHead || Opt->debug > 1 || Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE || Opt->DemoPause == SUMA_3dSS_INTERACTIVE ) { SUMA_PAUSE_PROMPT("Brain hull done, Pausing before proceeding to outer skull "); } { int il = 0; float dtroub = 1.0; int N_exp, N_troub = 1, past_N_troub = 0; /* reset stop flags */ Opt->nmask = (byte*)SUMA_malloc(sizeof(byte)*SO->N_Node); for (il=0; il<SO->N_Node; ++il) Opt->nmask[il] = 1; N_exp = 20; il = 0; while (il < N_exp || (N_troub < 0.01 * SO->N_Node && dtroub < 0.010)) { N_troub = SUMA_PushToOuterSkull(SO, Opt, 10+(25.0*(N_exp-il)/(float)N_exp), ps->cs, N_exp); SUMA_RECOMPUTE_NORMALS(SO); if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } if (LocalHead) fprintf (SUMA_STDERR,"%s: %d trouble nodes reported by SUMA_PushToOuterSkull\n", FuncName, N_troub); if (!past_N_troub) { past_N_troub = N_troub; if (LocalHead) fprintf (SUMA_STDERR,"%s: \nSUMA_PushToOuterSkull , pass %d %d troubled nodes, going for more...\n", FuncName, il, N_troub); } else { dtroub = (float)(past_N_troub - N_troub)/(float)past_N_troub; if (LocalHead) fprintf (SUMA_STDERR,"%s: \nSUMA_PushToOuterSkull, pass %d : %f change in troubled nodes.\n", FuncName, il, dtroub); past_N_troub = N_troub; } ++il; } } if (LocalHead) fprintf (SUMA_STDERR,"%s: Outer Skull push correction Done.\n", FuncName); ps->cs->kth = kth_buf; dsmooth = SUMA_Taubin_Smooth( SO, NULL, 0.6307, -.6732, SO->NodeList, Opt->smooth_end, 3, SUMA_ROW_MAJOR, dsmooth, ps->cs, NULL, 0); memcpy((void*)SO->NodeList, (void *)dsmooth, SO->N_Node * 3 * sizeof(float)); SUMA_RECOMPUTE_NORMALS(SO); if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } } else { /* DEVELOPMENT ONLY! */ int ncol, nrow; float *far=SUMA_Load1D_s(Opt->UseThisSkullOuter, &ncol, &nrow, 1, 0); if (!far || nrow != SO->N_Node || ncol != 3) { fprintf(SUMA_STDERR,"Error %s: SO has %d nodes, your coord file has %d cols, %d rows.\n", FuncName, SO->N_Node, ncol, nrow); exit(1); } else { fprintf(SUMA_STDERR,"Warning %s: adopting outer skull coordinates from %s\n", FuncName, Opt->UseThisSkullOuter); } memcpy((void*)SO->NodeList,(void*)far, sizeof(float)*ncol*nrow); free(far); far = NULL; } Opt->Skull_Outer = (float *)SUMA_calloc(SO->N_Node * 3, sizeof(float)); memcpy((void*)Opt->Skull_Outer, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); if ( LocalHead || Opt->debug > 1 || Opt->DemoPause == SUMA_3dSS_DEMO_PAUSE || Opt->DemoPause == SUMA_3dSS_INTERACTIVE ) { SUMA_PAUSE_PROMPT("Outer Skull done, Pausing before proceeding to inner skull "); } /* now for the inner skull */ { int il = 0; /* reset stop flags */ Opt->nmask = (byte*)SUMA_malloc(sizeof(byte)*SO->N_Node); for (il=0; il<SO->N_Node; ++il) Opt->nmask[il] = 1; ps->cs->kth = 1; SUMA_PushToInnerSkull(SO, Opt, 35.0, ps->cs); SUMA_RECOMPUTE_NORMALS(SO); } if (ps->cs->Send) { if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } Opt->Skull_Inner = (float *)SUMA_calloc(SO->N_Node * 3, sizeof(float)); memcpy((void*)Opt->Skull_Inner, (void *)SO->NodeList, SO->N_Node * 3 * sizeof(float)); ps->cs->kth = kth_buf; } FINISH_UP: /* reset coords to brain contours */ memcpy((void *)SO->NodeList, (void*)Opt->Brain_Contour, SO->N_Node * 3 * sizeof(float)); /* send the last surface (kind a stupid since you have som many surfaces) ...*/ ps->cs->kth = 1; if (ps->cs->Send) { SUMA_LH("SendSuma"); if (!SUMA_SendToSuma (SO, ps->cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted."); } } ps->cs->kth = kth_buf; if (Opt->DoSpatNorm) { float ispat, jspat, kspat, iorig, jorig, korig , xrai_orig, yrai_orig, zrai_orig; SUMA_LH("Changing coords of output surface"); /* what does the origin point (THD_BN_XORG THD_BN_YORG THD_BN_ZORG, ijk 0 0 0 )in the spat normed brain correspond to ? */ ispat = 0; jspat = 0; kspat = 0; brainnormalize_coord( ispat , jspat, kspat, &iorig, &jorig, &korig , Opt->iset, &xrai_orig, &yrai_orig, &zrai_orig); Opt->SpatShift[0] = xrai_orig - THD_BN_xorg(); Opt->SpatShift[1] = yrai_orig - THD_BN_yorg(); Opt->SpatShift[2] = zrai_orig - THD_BN_zorg(); /* shift the surface */ for (i=0; i<SO->N_Node; ++i) { i3 = 3*i; SO->NodeList[i3 + 0] += Opt->SpatShift[0]; SO->NodeList[i3 + 1] += Opt->SpatShift[1]; SO->NodeList[i3 + 2] += Opt->SpatShift[2]; } /* do the deed for the hull thing */ if (SOhull) { for (i=0; i<SOhull->N_Node; ++i) { i3 = 3*i; SOhull->NodeList[i3 + 0] += Opt->SpatShift[0]; SOhull->NodeList[i3 + 1] += Opt->SpatShift[1]; SOhull->NodeList[i3 + 2] += Opt->SpatShift[2]; } } /* ditto for other layers */ if (Opt->Brain_Contour) { for (i=0; i<SO->N_Node; ++i) { i3 = 3*i; Opt->Brain_Contour[i3 + 0] += Opt->SpatShift[0]; Opt->Brain_Contour[i3 + 1] += Opt->SpatShift[1]; Opt->Brain_Contour[i3 + 2] += Opt->SpatShift[2]; } } if (Opt->Brain_Hull) { for (i=0; i<SO->N_Node; ++i) { i3 = 3*i; Opt->Brain_Hull[i3 + 0] += Opt->SpatShift[0]; Opt->Brain_Hull[i3 + 1] += Opt->SpatShift[1]; Opt->Brain_Hull[i3 + 2] += Opt->SpatShift[2]; } } if (Opt->Skull_Outer) { for (i=0; i<SO->N_Node; ++i) { i3 = 3*i; Opt->Skull_Outer[i3 + 0] += Opt->SpatShift[0]; Opt->Skull_Outer[i3 + 1] += Opt->SpatShift[1]; Opt->Skull_Outer[i3 + 2] += Opt->SpatShift[2]; } } if (Opt->Skull_Inner) { for (i=0; i<SO->N_Node; ++i) { i3 = 3*i; Opt->Skull_Inner[i3 + 0] += Opt->SpatShift[0]; Opt->Skull_Inner[i3 + 1] += Opt->SpatShift[1]; Opt->Skull_Inner[i3 + 2] += Opt->SpatShift[2]; } } /* Change the number of voxels in VolPar to reflect the number of voxels in the non-spatnormed dset */ /* SUMA_VolDims(Opt->iset, &SO->VolPar->nx, &SO->VolPar->ny, &SO->VolPar->nz); *//* remember, the dset that SO->VolPar represents is in RAI still */ if (LocalHead) fprintf(SUMA_STDERR,"%s: \nPre: %d %d %d\n", FuncName, SO->VolPar->nx, SO->VolPar->ny, SO->VolPar->nz); SUMA_Free_VolPar(SO->VolPar); SO->VolPar = NULL; SO->VolPar = SUMA_VolParFromDset(Opt->iset); if (LocalHead) fprintf(SUMA_STDERR,"%s: \nPost: %d %d %d\n", FuncName, SO->VolPar->nx, SO->VolPar->ny, SO->VolPar->nz); if (SOhull) { SUMA_Free_VolPar(SOhull->VolPar); SOhull->VolPar = NULL; SOhull->VolPar = SUMA_VolParFromDset (Opt->iset); } } /* write the surfaces to disk */ if (strcmp(Opt->out_prefix,"skull_strip_out")) { SUMA_LH("Output surfaces"); if (Opt->Brain_Hull) { memcpy((void *)SO->NodeList, (void*)Opt->Brain_Hull, SO->N_Node * 3 * sizeof(float)); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Writing Brain Hull surface ...\n", FuncName); if (!SUMA_Save_Surface_Object (SO_name_bhull, SO, Opt->SurfFileType, Opt->SurfFileFormat, NULL)) { fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName); exit (1); } } if (Opt->Skull_Outer) { memcpy((void *)SO->NodeList, (void*)Opt->Skull_Outer, SO->N_Node * 3 * sizeof(float)); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Writing Skull Outer surface ...\n", FuncName); if (!SUMA_Save_Surface_Object (SO_name_oskull, SO, Opt->SurfFileType, Opt->SurfFileFormat, NULL)) { fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName); exit (1); } } if (Opt->Skull_Inner) { memcpy((void *)SO->NodeList, (void*)Opt->Skull_Inner, SO->N_Node * 3 * sizeof(float)); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Writing Skull Inner surface ...\n", FuncName); if (!SUMA_Save_Surface_Object (SO_name_iskull, SO, Opt->SurfFileType, Opt->SurfFileFormat, NULL)) { fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName); exit (1); } } /* leave this one at the end since the work afterwards will assume we are working with Brain_Contour in SO->NodeList */ if (Opt->Brain_Contour) { memcpy((void *)SO->NodeList, (void*)Opt->Brain_Contour, SO->N_Node * 3 * sizeof(float)); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Writing Brain Contour surface ...\n", FuncName); if (!SUMA_Save_Surface_Object (SO_name, SO, Opt->SurfFileType, Opt->SurfFileFormat, NULL)) { fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName); exit (1); } } } if (Opt->debug) { float rad, vol, *p; int kkk; SUMA_LH("Rad Ratting"); vol = SUMA_Mesh_Volume(SO, NULL, -1); rad = pow(3.0/4.0/SUMA_PI*vol, 1.0/3.0); SUMA_MIN_MAX_SUM_VECMAT_COL(SO->NodeList, SO->N_Node, SO->NodeDim, SO->MinDims, SO->MaxDims, SO->Center); SO->Center[0] /= (float)SO->N_Node; SO->Center[1] /= (float)SO->N_Node; SO->Center[2] /= (float)SO->N_Node; /* what is the deviation from a sphere? */ for (kkk=0; kkk<SO->N_Node; ++kkk) { p=&(SO->NodeList[3*kkk]); SUMA_SEG_LENGTH(SO->Center, p , Opt->shrink_bias[kkk]); Opt->shrink_bias[kkk] /= rad; } snprintf(stmp, 198, "%s_radrat.1D.dset", Opt->out_vol_prefix); SUMA_WRITE_ARRAY_1D(Opt->shrink_bias, SO->N_Node, 1, stmp); } if (Opt->UseSkull && SOhull) { fprintf (SUMA_STDERR,"%s: Writing skull surface ...\n", FuncName); if (!SUMA_Save_Surface_Object (SO_name_hull, SOhull, Opt->SurfFileType, Opt->SurfFileFormat, NULL)) { fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName); exit (1); } } /* prepare to write masked volume */ OptDs = SUMA_New_FormAfniDset_Opt(); if (Opt->out_vol_prefix) { SUMA_FileName NewName = SUMA_StripPath(Opt->out_vol_prefix); OptDs->prefix = NewName.FileName; NewName.FileName = NULL; OptDs->prefix_path = NewName.Path; NewName.Path = NULL; } else { OptDs->prefix = SUMA_copy_string("3dSkullStrip"); OptDs->prefix_path = SUMA_copy_string("./"); } if (Opt->DoSpatNorm) { OptDs->mset = Opt->iset; } else { OptDs->mset = Opt->in_vol; } /* what voxels are inside the surface ? */ if (Opt->debug) fprintf (SUMA_STDERR,"%s: Locating voxels inside surface ...\n", FuncName); isin = SUMA_FindVoxelsInSurface (SO, SO->VolPar, &N_in, Opt->fillhole, Opt->OrigSpatNormedSet); isin_float = (float *)SUMA_malloc(sizeof(float) * SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz); if (!isin_float) { SUMA_SL_Crit("Failed to allocate"); exit(1); } /* change Opt->fvec to reflect original data (not spat normed baby)*/ { THD_3dim_dataset *dout=NULL; if (Opt->MaskMode == 0 || Opt->MaskMode == 1) { /* use spatnormed baby, OK for mask too*/ dout = Opt->OrigSpatNormedSet; } else if (Opt->MaskMode == 2) { /* use original dset */ DSET_load(Opt->iset); dout = Opt->iset; } else { SUMA_S_Errv("Bad MaskMode value of %d!\n", Opt->MaskMode); SUMA_RETURN(NOPE); } if (Opt->fvec) { SUMA_free(Opt->fvec); Opt->fvec = NULL; } Opt->nvox = DSET_NVOX( dout ); Opt->fvec = (float *)SUMA_malloc(sizeof(float) * Opt->nvox); if (!Opt->fvec) { SUMA_SL_Crit("Failed to allocate for fvec.\nOh misery."); SUMA_RETURN(NOPE); } EDIT_coerce_scale_type( Opt->nvox , DSET_BRICK_FACTOR(dout,0) , DSET_BRICK_TYPE(dout,0), DSET_ARRAY(dout, 0) , /* input */ MRI_float , Opt->fvec ) ; /* output */ } if (Opt->MaskMode == 0 || Opt->MaskMode == 2) { SUMA_LH("Creating skull-stripped volume"); for (i=0; i<SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz; ++i) { /* apply the mask automatically */ if (isin[i] >= SUMA_ON_NODE) isin_float[i] = (float)Opt->fvec[i]; else isin_float[i] = 0.0; } } else if (Opt->MaskMode == 1) { SUMA_LH("Creating mask volume"); for (i=0; i<SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz; ++i) { isin_float[i] = (float)isin[i]; } } else { SUMA_S_Errv("Bad MaskMode of %d!\n", Opt->MaskMode); SUMA_RETURN(NOPE); } if (isin) SUMA_free(isin); isin = NULL; if (Opt->debug) fprintf (SUMA_STDERR,"%s: Writing masked volume ...\n", FuncName); OptDs->full_list = 1; OptDs->dval = 1; dset = SUMA_FormAfnidset (NULL, isin_float, SO->VolPar->nx*SO->VolPar->ny*SO->VolPar->nz, OptDs); /* erode and dilate, a bit of a cleanup? */ if (1){ EDIT_options *edopt = SUMA_BlankAfniEditOptions(); if (Opt->debug) fprintf (SUMA_STDERR,"%s: Applying a bit of erosion and dilatation \n", FuncName); edopt->edit_clust = ECFLAG_SAME; edopt->clust_rmm = SUMA_MAX_PAIR(SUMA_ABS((DSET_DX(OptDs->mset))), SUMA_ABS((DSET_DY(OptDs->mset)))); edopt->clust_rmm = SUMA_MAX_PAIR(SUMA_ABS((DSET_DZ(OptDs->mset))), edopt->clust_rmm)*1.01; edopt->clust_vmul = 1000*SUMA_ABS((DSET_DX(OptDs->mset)))*SUMA_ABS((DSET_DY(OptDs->mset)))*SUMA_ABS((DSET_DZ(OptDs->mset))); edopt->erode_pv = 75.0 / 100.0; edopt->dilate = 1; EDIT_one_dataset( dset , edopt); SUMA_free(edopt); } if (!dset) { SUMA_SL_Err("Failed to create output dataset!"); } else { tross_Make_History( FuncName , argc,argv , dset ) ; DSET_write(dset) ; if (Opt->MaskMode != 2) { if (Opt->MaskMode == 0) { fprintf(SUMA_STDERR, "The intensity in the output dataset is a modified version\n" "of the intensity in the input volume.\n" ); } else { fprintf(SUMA_STDERR, "The output dataset is a mask reflecting where voxels in the\n" "input dataset lie in the brain.\n"); } fprintf(SUMA_STDERR, "To obtain a masked version of the input with identical values inside\n" "the brain, you can either use 3dSkullStrip's -orig_vol option\n" "or run the following command:\n" " 3dcalc -a %s -b %s+orig -expr 'a*step(b)' \\\n" " -prefix %s_orig_vol\n" "to generate a new masked version of the input.\n", Opt->in_name, Opt->out_vol_prefix, Opt->out_vol_prefix); } } /* you don't want to exit rapidly because the SUMA might not be done processing the last elements*/ if (ps->cs->Send && !ps->cs->GoneBad) { /* cleanup and close connections */ if (!SUMA_SendToSuma (SO, ps->cs, NULL, SUMA_NODE_XYZ, 2)) { SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCleanup failed"); } } if (dsmooth) SUMA_free(dsmooth); dsmooth = NULL; if (OptDs) { OptDs->mset = NULL; OptDs = SUMA_Free_FormAfniDset_Opt(OptDs); } if (dset) { DSET_delete(dset); dset = NULL; } if (isin) { SUMA_free(isin); isin = NULL; } if (isin_float) { SUMA_free(isin_float); isin_float = NULL; } if (ps) SUMA_FreeGenericArgParse(ps); ps = NULL; if (Opt) Opt = SUMA_Free_Generic_Prog_Options_Struct(Opt); if (hullprefix) SUMA_free(hullprefix); hullprefix = NULL; if (bhullprefix) SUMA_free(bhullprefix); bhullprefix = NULL; if (oskullprefix) SUMA_free(oskullprefix); oskullprefix = NULL; if (iskullprefix) SUMA_free(iskullprefix); iskullprefix = NULL; if (SO_name_hull) SUMA_free(SO_name_hull); SO_name_hull = NULL; if (SO_name_bhull) SUMA_free(SO_name_bhull); SO_name_bhull = NULL; if (SO_name_iskull) SUMA_free(SO_name_iskull); SO_name_iskull = NULL; if (SO_name_oskull) SUMA_free(SO_name_oskull); SO_name_oskull = NULL; if (SO_name) SUMA_free(SO_name); SO_name = NULL; if (SO) SUMA_Free_Surface_Object(SO); SO = NULL; if (SOhull) SUMA_Free_Surface_Object(SOhull); SOhull = NULL; 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); exit(0); } #endif