#!/usr/bin/env tcsh # Make AFNI's help readable in a text editor again! @global_parse `basename $0` "$*" ; if ($status) exit 0 # --------------------- revision history ------------------------- # #set version = "1.0"; #set rev_dat = "some prior event in the spacetime continuum" # + RWC started and developed this program # #set version = "1.1"; set rev_dat = "May 18, 2018" # + PT started optionizing this program # #set version = "1.1"; set rev_dat = "July 1, 2018" # + fixed searching for path of dset, use explicit check first # #set version = "1.2"; set rev_dat = "July 5, 2018" # + set check if OMP_NUM_THREADS is explicitly set, so echoing don't # break it -- thanks P. Molfese et al. # #set version = "1.3"; set rev_dat = "Jan 17, 2019" # + [PT] bug fix-- 3danisosmooth cmd was missing ${odir} on I/O # #set version = "1.4"; set rev_dat = "Feb 11, 2019" # + [PT] new opt: turn unifize off (e.g., it 'twere already done # before) # #set version = "1.41"; set rev_dat = "Feb 11, 2019" # + [PT] new opt: can turn skullstrip and/or anisosmooth off (e.g., # it 'twere already done before) # #set version = "1.42"; set rev_dat = "Feb 11, 2019" # + [PT] rename the 'turn off skull strip' to a less potentially # confusing name: '-init_skullstr_off'. (So it doesn't falsely # seem like *no* skullstripping at all would be done.) # #set version = "1.43"; set rev_dat = "Feb 21, 2019" # + [PT] include '-Urad 30' in unifizing part, for improving # final output-- thanks for careful code reading, Yoichi # #set version = "1.44"; set rev_dat = "Mar 27, 2019" # + [RWC] add '-SSopt' option for adding options to 3dSkullStrip # (per the request of Allison Nugent) # #set version = "1.45"; set rev_dat = "Mar 29, 2019" # + [RWC] if SubID is a dataset name, funny things happened; # so edit out suffixes like '.nii', '.HEAD', etc.` # #set version = "1.5"; set rev_dat = "April 15, 2019" # + [PT] add in a couple new opts # "-giant_move": larger opening angle, etc. # "-deoblique": can deoblique # #set version = "1.51"; set rev_dat = "May 13, 2019" # + [PT] fixed help file for sphinxification: got rid of some # wandering "+" symbols in subheading titles # #set version = "1.52"; set rev_dat = "June 18, 2019" # + [RWC] add 3dAutomask step to clean up some of the # little junk at the edge of the brain # #set version = "1.6"; set rev_dat = "Jan 7, 2020" # + [PT] put ceiling (98%ile of non-zero vals) after 3danisosmooth # + also, use lpa+ZZ and lpa cost function for all rounds of align # -> should give better results, but is also slower (slightly)... # + add in options for putting in cost function values (backwards # compatibility): -cost_* # + opt for turn ceiling off: -ceil_off (backward compat) # #set version = "1.61"; set rev_dat = "Jan 20, 2020" # + [PT] new opt for deobliquing a la 3drefit-- probably would be more # useful than 3dWarp-style? # #set version = "2.0"; set rev_dat = "Jan 27, 2020" # + [PT] This is a majorly new version of @SSwarper. # + final set of options from testing a loooot of things with # the mixed groups of 178 subj from different studies. This # set of options performed the best: fewest weird things, # sharpest mean across groups, and smallest stdev (both # inside and outside the brain). # + several opts have been added for additional control, as well # #set version = "2.1"; set rev_dat = "Feb 20, 2020" # + [PT] Extra QC output: QC*jpg montages # + one to check skullstripping, one to view warping in more detail # #set version = "2.11"; set rev_dat = "Feb 20, 2020" # + [PT] fix paths in the ulay/olay of extra QC*jpg images # #set version = "2.2"; set rev_dat = "Feb 27, 2020" # + [PT] new warpscale opt for 3dQwarp, courtesy of RWC # #set version = "2.21"; set rev_dat = "Sep 1, 2020" # + [PT] if '-skipwarp' is used, now set DO_EXTRA_WC = 0. # #set version = "2.3"; set rev_dat = "Sep 23, 2020" # + [PT] put ${status} checks to terminate on ~first failures # #set version = "2.31"; set rev_dat = "Oct 19, 2020" # + [PT] new qc image of initial overlap (with/without applying obl) # #set version = "2.33"; set rev_dat = "Dec 1, 2020" # + [PT] minorly change default "junk" file names # - amusing anecdote: one AFNI user got an unexpected failure # when a random string started with "1D", so a file name # looked like "junk.SSwarper.1DfZbuq78qk.nii". When 3dcalc # got hold of such a file, it complained about not being # able to read a *.1D file, because that combination # appears in the middle of a filename (???), but it output # correctly; however, 3dNwarpApply wanted none of it, and # produced a fatal error. # - Anyways, we can avoid this one in a bazillion hassleby # changing a dot to an underscore, so did. # - And added new "-tmp_nice_name" opt flag to have to a # simpler temp file name (use esp. when odir holds single # subj output) # #set version = "2.4"; set rev_dat = "Feb 5, 2020" # + [PT] more QC along the processing: 2 new init*jpg files # + add "-echo" as an opt # #set version = "2.5"; set rev_dat = "Feb 10, 2020" # + [PT] allow input mask # + try Strategy 1 of just replacing 3dSkullStrip with it # #set version = "2.51"; set rev_dat = "June 15, 2021" # + [PT] minor, minor replacements to avoid errors on old tcsh: # + "if(" -> "if (" # + ")then" -> ") then" # #set version = "2.52"; set rev_dat = "Sep 27, 2021" # + [PT] chauffeur label_size 3 -> 4, bc imseq.c shifted all sizes # down one level # #set version = "2.53"; set rev_dat = "Sep 28, 2021" # + [PT] cp initial/raw anat to $odir # + also cp any initial/raw mask_ss to $odir # #set version = "2.54"; set rev_dat = "Feb 1, 2022" # + [PT] Put clearer ERROR messaging in a couple spots # + remove any non-programmatic exclamation marks. those are BAD # to have in t/csh scripts, even in comments. # #set version = "2.55"; set rev_dat = "May 10, 2022" # + [PT] for 3dWarp-deobliqueing, use NN interp for mask, not wsinc5 # -> thanks, RCR # set version = "2.6"; set rev_dat = "OCt 31, 2022" # + [PT] removing use of -cost_aff opt (it will just produce a warning # message). It had been ignored, anyways. Affine cost just # matches the cost_nl_init choice. # # ---------------------------------------------------------------- # some AFNI environment variables setenv AFNI_DONT_LOGFILE YES setenv AFNI_COMPRESSOR NONE # set number of threads if run via SLURM if ( $?SLURM_CPUS_PER_TASK ) then setenv OMP_NUM_THREADS $SLURM_CPUS_PER_TASK else if ( $?NSLOTS ) then setenv OMP_NUM_THREADS $NSLOTS endif # =================================================================== set this_prog = "@SSwarper" set Adataset = "" # req/ input dataset set SubID = "" # req/ the subject ID set Basedset = "" # req/ reference dset- must have 4 bricks set odir = "" # opt/ output dir set minp = "11" # opt/ the minimum warp patch size set btemplate = '$btemplate' set tpath = '$tpath' set subj = '$subj' set str_msg = '`@FindAfniDsetPath $btemplate`' set liteopt = "-lite" set tightness = 0 set doclean = 1 set verbopt = "" set skipwarp = 0 set warpscale = 1 # def; [Feb, 2020] RWC added opt to 3dQwarp set DO_UNIFIZE = 1 set DO_SKULLST = 1 set DO_ANISO = 1 set DO_CEIL = 1 # have a ceiling value on anat set DO_GIANT = 0 set DO_DEOB = 0 # 3dWarp-style set DO_DEOB_REF = 0 # 3drefit-style set DO_EXTRA_QC = 1 # more chauffeur output set JUMP_TO_EXTRA_QC = 0 # just for testing/internal purposes set DO_RANDOM_TMP = 1 # use random chars in tmp fname; can use nicer set USER_COST_AFF = 0 # did user use -cost_aff? set cost_nli = "-lpa" # used in: 3dQwarp, initial rounds set cost_nlf = "-pcl" # used in: 3dQwarp, final rounds set gaus_wt = 4.5 # def for 3dQwarp set saveall = "" # for 3dQwarp (def: don't); just for debugging set SSopt = " " set mask_ss = "" set more_echo = "" # ------------------- process options, a la rr ---------------------- if ( $#argv == 0 ) goto SHOW_HELP set ac = 1 while ( $ac <= $#argv ) # terminal options if ( ("$argv[$ac]" == "-h" ) || ("$argv[$ac]" == "-help" )) then goto SHOW_HELP endif if ( "$argv[$ac]" == "-ver" ) then goto SHOW_VERSION endif # --------- required --------------- if ( "$argv[$ac]" == "-input" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set Adataset = "$argv[$ac]" else if ( "$argv[$ac]" == "-subid" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set SubID = "$argv[$ac]" else if ( "$argv[$ac]" == "-base" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set Basedset = "$argv[$ac]" # --------- optional --------------- # [PT: Feb 25, 2020] RWC added this opt to 3dQwarp # lower warpscale -> less flexible warps # may be useful if odd bumps occur. vals: [0.1, 1.0] else if ( "$argv[$ac]" == "-warpscale" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set warpscale = "$argv[$ac]" # min patch else if ( "$argv[$ac]" == "-minp" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set minp = "$argv[$ac]" # [Jan 2019 - RWC] else if ( "$argv[$ac]" == "-nolite" ) then set liteopt = "-nolite" # [Jan 2019 - RWC] else if ( "$argv[$ac]" == "-tight" ) then @ tightness ++ # [Jan 2019 - RWC] else if ( "$argv[$ac]" == "-noclean" ) then set doclean = 0 # [PT: April 15, 2019] else if ( "$argv[$ac]" == "-giant_move" ) then set DO_GIANT = 1 # [PT: April 15, 2019] else if ( "$argv[$ac]" == "-deoblique" ) then set DO_DEOB = 1 # [PT: Jan 14, 2020] be able to purge obliquity info, too else if ( "$argv[$ac]" == "-deoblique_refitly" ) then set DO_DEOB_REF = 1 # [Jan 2019 - RWC] else if ( "$argv[$ac]" == "-skipwarp" ) then set skipwarp = 1 # [PT: Feb 11, 2019] else if ( "$argv[$ac]" == "-unifize_off" ) then set DO_UNIFIZE = 0 # [PT: Feb 11, 2019] else if ( "$argv[$ac]" == "-init_skullstr_off" ) then set DO_SKULLST = 0 # [PT: Feb 11, 2019] else if ( "$argv[$ac]" == "-aniso_off" ) then set DO_ANISO = 0 # [PT: Feb 11, 2019] else if ( "$argv[$ac]" == "-mask_ss" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set mask_ss = "$argv[$ac]" set DO_SKULLST = 0 # [PT: Jan 31, 2020] else if ( "$argv[$ac]" == "-extra_qc_off" ) then set DO_EXTRA_QC = 0 # just for internal running/testing purposes else if ( "$argv[$ac]" == "-jump_to_extra_qc" ) then set JUMP_TO_EXTRA_QC = 1 # [PT: Feb 11, 2019] else if ( "$argv[$ac]" == "-ceil_off" ) then set DO_CEIL = 0 else if ( "$argv[$ac]" == "-saveall" ) then set saveall = "-saveall" # [PT: Jan 14, 2020] probably only for backwards compatibility: # default will be lpa+ZZ (could be 'hel' for back-compat) else if ( "$argv[$ac]" == "-cost_aff" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 ##set cost_aff = "$argv[$ac]" echo "+* WARNING ********" echo " The option '-cost_aff' is no longer used. The affine" echo " alignment cost function will simply match the" echo " '-cost_nl_init ..' value." set USER_COST_AFF = 1 # [PT: Jan 14, 2020] probably just for backwards compatibility: # default will be lpa hereafter (could be '-pcl' for back-compat) else if ( "$argv[$ac]" == "-cost_nl_init" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set cost_nli = "-$argv[$ac]" # [PT: Jan 15, 2020] separate early and later rounds of NL # warping, because using LPA for later rounds can be slooow else if ( "$argv[$ac]" == "-cost_nl_final" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set cost_nlf = "-$argv[$ac]" else if ( "$argv[$ac]" == "-wtgaus" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set gaus_wt = "$argv[$ac]" # [Jan 2019 - RWC] else if ( "$argv[$ac]" == "-verb" ) then set verbopt = "-verb" # [PT: Dec 1, 2020] changing default tmp fnames; this reverts to # old/ugly style else if ( "$argv[$ac]" == "-tmp_name_nice" ) then set DO_RANDOM_TMP = 0 # output dir else if ( "$argv[$ac]" == "-odir" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set odir = "$argv[$ac]" # 3dSkullStrip options for Allison [RWC: 27 Mar 2019] else if ( "$argv[$ac]" == "-SSopt" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set SSopt = "$argv[$ac]" else if ( "$argv[$ac]" == "-echo" ) then set echo set more_echo = "-echo" # ---------- fin ------------------ else echo "** unexpected option #$ac = '$argv[$ac]'" goto BAD_EXIT endif @ ac += 1 end # ==================================================================== echo "++ Starting: $this_prog v$version" if ( ! $?OMP_NUM_THREADS ) then set sss = `dirname $0` if ( -e $sss/afni_check_omp ) then set nnn = `$sss/afni_check_omp` echo "++ Default OMP_NUM_THREADS is $nnn" unset nnn else echo "++ OMP_NUM_THREADS is: not set by user, " echo " so possibly just using one CPU, depending on system config" endif unset sss else echo "++ OMP_NUM_THREADS set to $OMP_NUM_THREADS" endif if ( "$Adataset" == "" ) then echo "** ERROR: No -input option? :(" goto BAD_EXIT endif if ( "$Basedset" == "" ) then echo "** ERROR No -base option? :(" goto BAD_EXIT endif if ( ! -f "$Adataset" ) then set chad = `@CheckForAfniDset "$Adataset"` if ( "$chad" == "0" || "$chad" == "1" ) then echo "** ERROR: ${this_prog} -- Not finding dset $Adataset -- exiting :((" goto BAD_EXIT endif endif if ( "$SubID" == "" ) then echo "** ERROR ${this_prog} -- no subject ID entered with -subid? :(" goto BAD_EXIT endif if ( "${mask_ss}" != "" ) then set check = `3dinfo -prefix "${mask_ss}"` if ( "$check" == "NO-DSET" ) then echo "** ERROR: can't find inset file ${mask_ss}" goto BAD_EXIT endif endif # edit SubID to remove any dataset suffixes [29 Mar 2019] set sss = `basename "$SubID" .nii.gz` set sss = `basename "$sss" .nii` set sss = `basename "$sss" .` set sss = `basename "$sss" .HEAD` set sss = `basename "$sss" .BRIK.gz` set sss = `basename "$sss" .BRIK` set sss = `basename "$sss" +orig` set sss = `basename "$sss" +tlrc` if ( "$sss" != "$SubID" ) then echo "++ Editing subject ID (-subid) $SubID to $sss" set SubID = "$sss" endif unset sss # output dir from $Adataset, if not set by user if ( "$odir" == "" ) then set odir = `dirname "$Adataset"` echo "" echo "++ Based on input, the output directory will be:" echo " $odir" echo "" endif # [PT: Sep 1, 2020] skipwarp basically means a lot of other stuff gets # skipped... Have it turn off "extra" QC: if ( ${skipwarp} ) then echo "+* Since we are skipping the warp, there is no 'extra' QC" set DO_EXTRA_QC = 0 endif \mkdir -p "${odir}" # set random prefix for temp files ### [PT: Dec 1, 2020] doesn't seem a need to have giant random string ### chars in temporary "junk" file names if ( $DO_RANDOM_TMP ) then # original style, slightly modified bc the purely random chars # after '.' caaaan cause mischief (see amusing anecdote in # comments at top for this date). set pppp = "`3dnewid -fun11`" set pref = "$odir/junk.SSwarper_${pppp}_" else set pref = "$odir/junk_ssw_" endif # ---------------------------- this will change ------------------- #### ----> also add check that it has multiple subbricks ## find the base template dataset: start by seeing if it has been ## given directly (not just needing @Find*Path): if ( -e "$Basedset" ) then set tpath = `dirname "$Basedset"` else set tpath = `@FindAfniDsetPath "$Basedset"` if ( "$tpath" == '' ) then echo "** ERROR: ${this_prog} -- Failed to find template $Basedset" echo " -- exiting :(" goto BAD_EXIT endif set Basedset = $tpath/$Basedset endif ## Require it to have enough bricks to really be a ref set nvolbase = `3dinfo -nv "$Basedset"` if ( $nvolbase < 5 ) then echo "** ERROR: Base $Basedset only has $nvolbase volumes:" echo " to serve as a reference for $this_prog, it needs 4." echo " See '$this_prog -help' -> 'The Template Dataset' for more info" goto BAD_EXIT endif # [PT: Oct 19, 2020] Add in new QC: initial source-base overlap @djunct_overlap_check ${more_echo} \ -ulay "${Adataset}" \ -olay "$Basedset" \ -prefix "${odir}/init_qc_00_overlap_usrc_obase" if ( ${JUMP_TO_EXTRA_QC} ) then echo "\n++ Just jumpin' to extra QC\n" goto JUMP_TO_EXTRA_QC endif # -------------------------------------------------------------------- ## Step #-1: copy the raw anat (and perhaps mask_ss) into output ## dir---often useful to compare later and/or check header info set dset_cp = "$odir/anat_cp.${SubID}.nii" if ( ! -f "${dset_cp}" ) then 3dTcat \ -prefix "${dset_cp}" \ "$Adataset" if ( ${status} ) then echo "** ERROR: in getting dset" goto BAD_EXIT endif endif if ( "${mask_ss}" != "" ) then set mask_ss_cp = "$odir/mask_ss_cp.${SubID}.nii" if ( ! -f "${mask_ss_cp}" ) then 3dTcat \ -prefix "${mask_ss_cp}" \ "${mask_ss}" if ( ${status} ) then echo "** ERROR: in getting dset" goto BAD_EXIT endif endif endif ## start the work ## Step #0: Deoblique, either in style of 3dWarp or 3drefit if ( ${DO_DEOB} ) then set dset_do = "$odir/anatDO.${SubID}.nii" 3dWarp \ -deoblique \ -wsinc5 \ -prefix "${dset_do}" \ "$Adataset" # ... and this will be new "input" dset set Adataset = "${dset_do}" if ( "${mask_ss}" != "" ) then set mask_do = "$odir/maskDO.${SubID}.nii.gz" # [PT: 10 May 2022] kernel from wsinc5 -> NN, as noticed by RCR 3dWarp \ -deoblique \ -NN \ -prefix "${mask_do}" \ "${mask_ss}" # ... and this will be new "input" dset set mask_ss = "${mask_do}" endif else if ( ${DO_DEOB_REF} ) then set dset_do = "$odir/anatDO.${SubID}.nii" # copy 3dcalc \ -a "$Adataset" \ -expr 'a' \ -prefix "${dset_do}" # purge obliquity info 3drefit \ -deoblique \ "${dset_do}" if ( ${status} ) then echo "** ERROR: program failed in Step deob" goto BAD_EXIT endif # ... and this will be new "input" dset set Adataset = "${dset_do}" if ( "${mask_ss}" != "" ) then set mask_do = "$odir/maskDO.${SubID}.nii.gz" # copy 3dcalc \ -a "${mask_ss}" \ -expr 'a' \ -prefix "${mask_do}" # purge obliquity info 3drefit \ -deoblique \ "${mask_do}" if ( ${status} ) then echo "** ERROR: program failed in Step deob" goto BAD_EXIT endif # ... and this will be new "input" dset set mask_ss = "${mask_do}" endif endif ## Step #1: Unifize the input T1 echo "++ SSW Step 1" # [PT: Feb 11, 2019] now allows for option *not* to unifize # [PT: Feb 21, 2019] use Urad=30 when unifizing (better for final output) if ( ! -f "$odir/anatU.${SubID}.nii" ) then if ( ${DO_UNIFIZE} ) then 3dUnifize \ -GM \ -clfrac 0.4 \ -Urad 30 \ -prefix "$odir/anatU.$SubID.nii" \ -input "$Adataset" if ( ${status} ) then echo "** ERROR: program failed in Step U" goto BAD_EXIT endif else echo "++ NOT unifizing dset." 3dcopy \ "$Adataset" \ "$odir/anatU.$SubID.nii" if ( ${status} ) then echo "** ERROR: program failed in Step U" goto BAD_EXIT endif endif endif # [PT: Feb 11, 2019] now allows for option *not* to anismoothize if ( ! -f "$odir/anatUA.${SubID}.nii" ) then if ( ${DO_ANISO} ) then 3danisosmooth \ -iters 1 \ -3D \ -automask \ -noneg \ -prefix "$odir/anatUA.${SubID}.nii" \ "$odir/anatU.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step UA" goto BAD_EXIT endif else echo "++ NOT anisosmoothing dset." 3dcopy \ "$odir/anatU.$SubID.nii" \ "$odir/anatUA.$SubID.nii" if ( ${status} ) then echo "** ERROR: program failed in Step UA" goto BAD_EXIT endif endif endif # [PT: Jan 6, 2020] get rid of potentially *very* high vals: take # 98%ile of non-zero vox if ( ! -f "$odir/anatUAC.${SubID}.nii" ) then if ( ${DO_CEIL} ) then set vvv = `3dBrickStat \ -percentile 98 1 98 \ -non-zero \ "$odir/anatUA.${SubID}.nii"` # apply ceiling 3dcalc \ -a "$odir/anatUA.${SubID}.nii" \ -expr "maxbelow(${vvv[2]},a)" \ -prefix "$odir/anatUAC.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step UAC" goto BAD_EXIT endif else echo "++ NOT anisosmoothing dset." 3dcopy \ "$odir/anatUA.$SubID.nii" \ "$odir/anatUAC.$SubID.nii" if ( ${status} ) then echo "** ERROR: program failed in Step UAC" goto BAD_EXIT endif endif endif # Step #2: Strip Skull (Ziad's way) echo "++ SSW Step 2" # [PT: Feb 11, 2019] now allows for option *not* to skullstrip if ( ! -f "$odir/anatS.${SubID}.nii" ) then if ( ${DO_SKULLST} ) then 3dSkullStrip \ -input "$odir/anatUAC.$SubID.nii" \ -prefix "$odir/anatS.$SubID.nii" \ -debug 1 -ld 33 -niter 777 \ -shrink_fac_bot_lim 0.777 \ -exp_frac 0.0666 -orig_vol \ $SSopt if ( ${status} ) then echo "** ERROR: program failed in Step 2" goto BAD_EXIT endif else if ( "${mask_ss}" != "" ) then echo "++ Instead of skullstripping, apply mask_ss dset." 3dcalc \ -a "$odir/anatUAC.$SubID.nii" \ -b ${mask_ss} \ -expr 'a*step(b)' \ -prefix "$odir/anatS.$SubID.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 2" goto BAD_EXIT endif else echo "++ NOT pre-skullstripping dset." 3dcopy \ "$odir/anatUAC.$SubID.nii" \ "$odir/anatS.$SubID.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 2" goto BAD_EXIT endif endif endif ## Step #3: run 3dQwarp first time to a moderate level (skull on) echo "++ SSW Step 3" if ( ${DO_GIANT} ) then 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[1]" \ ${cost_nli} \ -warpscale ${warpscale} \ -source "$odir/anatUAC.$SubID.nii" \ -weight "${Basedset}[2]" \ -allineate -noneg -maxlev 5 -iwarp -awarp \ -wtgaus ${gaus_wt} \ -inedge \ -workhard:3:5 -nopenalty \ -prefix "${pref}TAL5.nii" \ -allopt '-twobest 11 -twopass -maxrot 45 -maxshf 40 -source_automask+2 -cmass' if ( ${status} ) then echo "** ERROR: program failed in Step 3" goto BAD_EXIT endif else 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[1]" \ ${cost_nli} \ -warpscale ${warpscale} \ -source "$odir/anatUAC.$SubID.nii" \ -weight "${Basedset}[2]" \ -allineate -noneg -maxlev 5 -iwarp -awarp \ -wtgaus ${gaus_wt} \ -inedge \ -workhard:3:5 -nopenalty \ -prefix "${pref}TAL5.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 3" goto BAD_EXIT endif endif # -------- check early NL alignment to ref vol space --------- set ulay = "${pref}TAL5.nii" set olay = "${Basedset}" set opref_qc_nl0 = "${odir}/init_qc_01_nl0.${SubID}.jpg" @djunct_edgy_align_check ${more_echo} \ -montx 8 \ -monty 1 \ -ulay "${ulay}" \ -olay "${olay}" \ -box_focus_slices "${olay}" \ -prefix "${pref}MONT3" 2dcat \ -gap 5 \ -gap_col 255 204 153 \ -nx 1 \ -ny 3 \ -prefix "${opref_qc_nl0}" \ "${pref}"MONT3*jpg ## Step #4: mask off the skull using the template (second skull-strip) echo "++ SSW Step 4" 3dmask_tool \ -input "${Basedset}[3]" \ -dilate_input 2 \ -prefix "${pref}MASK.nii" 3dcalc \ -a "${pref}MASK.nii" \ -b "${pref}TAL5.nii" \ -expr 'step(a)*b' \ -prefix "${pref}TAL5mm.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 4" goto BAD_EXIT endif ## Step #5: warp this masked dataset back to original space echo "++ SSW Step 5" 3dNwarpApply -echo_edu \ -nwarp "${pref}TAL5_WARPINV.nii" \ -master "$odir/anatS.$SubID.nii" \ -source "${pref}TAL5mm.nii" \ -prefix "${pref}TAL5ww.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 5a" goto BAD_EXIT endif ## warp the mask itself (dilated) back to orig space \rm -f "${pref}MASK.nii" 3dmask_tool -echo_edu \ -input "${Basedset}[3]" \ -dilate_input 3 \ -prefix "${pref}MASK.nii" 3dNwarpApply \ -nwarp "${pref}TAL5_WARPINV.nii" \ -master "$odir/anatS.$SubID.nii" \ -source "${pref}MASK.nii" \ -prefix "${pref}MASKO.nii" \ -ainterp NN if ( ${status} ) then echo "** ERROR: program failed in Step 5b" goto BAD_EXIT endif ## merge these backward warped datasets with the 3dSkullStrip ## output to get a better original skull-stripped result 3dcalc -a "$odir/anatS.${SubID}.nii" \ -b "${pref}TAL5ww.nii" \ -c "${pref}MASKO.nii" \ -expr 'step(c)*max(a,b)' \ -prefix "$odir/anatSS.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 5c" goto BAD_EXIT endif # DRG's erode-dilate trick for cleanup of little crap [16 Jan 2019] 3dmask_tool \ -dilate_inputs -3 3 \ -prefix "${pref}de3.nii" \ -input "$odir/anatSS.${SubID}.nii" 3dcalc \ -a "$odir/anatSS.${SubID}.nii" \ -b "${pref}de3.nii" \ -expr 'a*step(b)' \ -prefix "$odir/anatSSc.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 5d" goto BAD_EXIT endif # Throw in an automask step to clean up the outer edges [18 Jun 2019] \rm -f "${pref}de3.nii" "$odir/anatSS.${SubID}.nii" 3dAutomask \ -apply_prefix "$odir/anatSSd.${SubID}.nii" \ "$odir/anatSSc.${SubID}.nii" \mv -f "$odir/anatSSd.${SubID}.nii" "$odir/anatSS.${SubID}.nii" \rm -f "$odir/anatSSc.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 5e" goto BAD_EXIT endif # don't do any more if skipping the final (precision) warp if ( $skipwarp ) goto CLEANUP ## Step #6: affine transform that result to template space echo "++ SSW Step 6" # [PT: Jan 7, 2019] use lpa+ZZ cost func by default; also add in -twopass # [PT: Oct 31, 2022] um, $cost_aff has no effect here echo "++ SSW Step 6 ... piece 0" 3dAllineate \ -1Dmatrix_apply "${pref}TAL5_Allin.aff12.1D" \ -source "$odir/anatSS.${SubID}.nii" \ -master "${pref}TAL5mm.nii" \ #-cost ${cost_aff} \ #-twopass \ #-autoweight -source_automask \ -final wsinc5 \ -prefix "${pref}AffSS.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.0" goto BAD_EXIT endif # -------- check aff alignment to ref vol space --------- set ulay = "${pref}AffSS.nii" set olay = "${Basedset}" set opref_qc_aff = "${odir}/init_qc_02_aff.${SubID}.jpg" @djunct_edgy_align_check \ -montx 8 \ -monty 1 \ -ulay "${ulay}" \ -olay "${olay}" \ -box_focus_slices "${olay}" \ -prefix "${pref}MONT6" 2dcat \ -gap 5 \ -gap_col 255 204 153 \ -nx 1 \ -ny 3 \ -prefix "${opref_qc_aff}" \ "${pref}"MONT6*jpg ## warp to template space (skull off), ## initializing using the previous 3dQwarp -awarp output # Run 3dQwarp in several segments, to avoid gcc OpenMP bug # where it freezes sometimes with inter-thread conflicts; # this happens only in long runs, so breaking the run # into pieces will (I hope) elide this annoyance - RWC :( # Piece number 1 echo "++ SSW Step 6 ... piece 1" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}AffSS.nii" \ -iniwarp "${pref}TAL5_AWARP.nii" \ -warpscale ${warpscale} \ ${cost_nli} \ -inilev 1 -maxlev 5 \ -wtgaus ${gaus_wt} \ -inedge \ -pblur -workhard:5:5 \ -nodset -prefix "${pref}QQ5.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.1" goto BAD_EXIT endif # Piece number 2 echo "++ SSW Step 6 ... piece 2" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}AffSS.nii" \ -iniwarp "${pref}QQ5_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -inilev 6 -maxlev 7 -workhard:6:7 \ -wtgaus ${gaus_wt} \ -inedge \ -pblur \ -nodset -prefix "${pref}QQ7.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.2" goto BAD_EXIT endif if ( $minp > 13 ) then # Final piece for coarse final patch size echo "++ SSW Step 6 ... piece 3" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}AffSS.nii" \ -iniwarp "${pref}QQ7_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -inilev 8 \ -wtgaus ${gaus_wt} \ -inedge \ -pblur -minpatch $minp \ -Qfinal \ -prefix "$odir/anatQQ.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.3a" goto BAD_EXIT endif else # Penultimate piece for refined final patch size echo "++ SSW Step 6 ... piece 3a" @ mpp = $minp + 6 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}AffSS.nii" \ -iniwarp "${pref}QQ7_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -inilev 8 -minpatch $mpp \ -wtgaus ${gaus_wt} \ -inedge \ -pblur \ -nodset -prefix "${pref}QQ9.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.3b" goto BAD_EXIT endif # Ultimate piece for refined final patch size echo "++ SSW Step 6 ... piece 3b" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}AffSS.nii" \ -iniwarp "${pref}QQ9_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -inilev 10 \ -wtgaus ${gaus_wt} \ -inedge \ -pblur -minpatch $minp \ -prefix "$odir/anatQQ.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Step 6.3c" goto BAD_EXIT endif endif echo "++ SSW: done warping. Finalize." # DRG's erode-dilate trick for cleanup of little stuff [16 Jan 2019] 3dmask_tool \ -dilate_inputs -1 1 \ -prefix "${pref}de3.nii" \ -input "$odir/anatQQ.${SubID}.nii" 3dcalc \ -a "$odir/anatQQ.${SubID}.nii" \ -b "${pref}de3.nii" \ -expr 'a*step(b)' \ -prefix "$odir/anatQQc.${SubID}.nii" if ( ${status} ) then echo "** ERROR: program failed in Finalize" goto BAD_EXIT endif \rm -f "${pref}de3.nii" "anatQQ.${SubID}.nii" \mv -f "$odir/anatQQc.${SubID}.nii" "$odir/anatQQ.${SubID}.nii" ## Step #7: Make two pretty pictures, and scram echo "++ SSW Step 7" @snapshot_volreg "$odir/anatQQ.${SubID}.nii" "$Basedset" "$odir/AM${SubID}" @snapshot_volreg "$Basedset" "$odir/anatQQ.${SubID}.nii" "$odir/MA${SubID}" JUMP_TO_EXTRA_QC: if ( ${DO_EXTRA_QC} ) then # -------- check skullstripping in orig space --------- set ulay = "${odir}/anatU.${SubID}.nii" set olay0 = "${odir}/anatSS.${SubID}.nii" set olay = "${pref}mask.nii" set opref_exqc1 = "${odir}/QC_anatSS.${SubID}.jpg" 3dcalc \ -overwrite \ -a ${olay0} \ -expr 'bool(a)' \ -prefix ${olay} \ -byte @chauffeur_afni \ -ulay ${ulay} \ -ulay_range "2%" "98%" \ -olay ${olay} \ -func_range_perc_nz 1 \ -set_subbricks 0 0 0 \ -box_focus_slices ${olay} \ -cbar "Reds_and_Blues_Inv" \ -opacity 4 \ -prefix "${pref}MONT1" \ -save_ftype JPEG \ -montx 9 -monty 1 \ -montgap 3 \ -set_xhairs OFF \ -label_mode 1 -label_size 4 2dcat \ -gap 5 \ -gap_col 66 184 254 \ -nx 1 \ -ny 3 \ -prefix "${opref_exqc1}" \ "${pref}"MONT1*jpg # -------- check alignment in ref vol space --------- set ulay = "${odir}/anatQQ.${SubID}.nii" set olay = "${Basedset}" set opref_exqc2 = "${odir}/QC_anatQQ.${SubID}.jpg" @djunct_edgy_align_check \ -montx 8 \ -monty 1 \ -ulay "${ulay}" \ -olay "${olay}" \ -box_focus_slices "${olay}" \ -prefix "${pref}MONT2" 2dcat \ -gap 5 \ -gap_col 66 184 254 \ -nx 1 \ -ny 3 \ -prefix "${opref_exqc2}" \ "${pref}"MONT2*jpg endif # ------------------------------------------------------------------ ## Clean up the junk CLEANUP: ## Rename affine warp matrix echo "++ SSW cleanup" # [PT: Feb 19, 2020] Put the mv cmd in an IF condition, because with # re-running extra QC images, might not have the first file name; # now, avoid an unnecessary error msg. if ( -e "${pref}TAL5_Allin.aff12.1D" ) then \mv -f "${pref}TAL5_Allin.aff12.1D" "$odir/anatQQ.${SubID}.aff12.1D" endif if ( $doclean ) then \rm -f "${pref}"* endif echo "--------------------------------------------------------" echo "++ DONE with SSW. Check QC images:" printf " $odir/AM${SubID}.jpg $odir/MA${SubID}.jpg " if ( ${DO_EXTRA_QC} ) then printf " ${opref_exqc1} ${opref_exqc2}" endif echo "" echo "" echo " In ref/base space (check warping):" echo " ulay=anat, olay=base edges : $odir/AM${SubID}.jpg" echo " ulay=base edges, olay=anat : $odir/MA${SubID}.jpg" if ( ${DO_EXTRA_QC} ) then echo " ulay=anat, olay=base edges : ${opref_exqc2}" echo "" echo " In native space (check skullstripping):" echo " ulay=anat, olay=SS mask : ${opref_exqc1}" endif echo "" echo "--------------------------------------------------------" goto GOOD_EXIT # ======================================================================== # ======================================================================== SHOW_HELP: cat < anatU.sub007.nii #2: Strip the skull with 3dSkullStrip, with mildly aggressive settings. ==> anatS.sub007.nii #3: Nonlinearly warp (3dQwarp) the result from #1 to the skull-on template, driving the warping to a medium level of refinement. #4: Use a slightly dilated brain mask from the template to crop off the non-brain tissue resulting from #3 (3dcalc). #5: Warp the output of #4 back to original anatomical space, along with the template brain mask, and combine those with the output of #2 to get a better skull-stripped result in original space (3dNwarpApply and 3dcalc). ==> anatSS.sub007.nii #6 Restart the nonlinear warping, registering the output of #5 to the skull-off template brain volume (3dQwarp). ==> anatQQ.sub007.nii (et cetera) #7 Use @snapshot_volreg to make the pretty pictures. ==> AMsub007.jpg and MAsub007.jpg Temporary files ~2~ If the script crashes for some reason, it might leave behind files whose names start with 'junk' -- you should delete these files manually. WHAT TO DO IF RESULTS ARE WAY OFF? ~1~ The importance of initial dset overlap ~2~ Always, always, always check the initial image made by SSW when it runs: init_qc_00_overlap_uinp_obase.jpg This image tells you how well your datasets overlap initially before the alignment work begins. **The better the overlap, the lower the chance that something weird happens in your output.** All the SSW templates have reasonable coordinates, meaning that (x, y, z) = (0, 0, 0) is in a good spot for it. If there is poor overlap, probably your input dataset has weird/bad coordinates for some reason. You can use @Align_Centers to put your anatomical dset in a better spot (though note, if you are going to be processing EPI data afterwards, you will want to move that along, as well, perhaps as a "child" dataset). By far the most common problem leading to obviously bad outputs is that the initial datasets are waaay far apart when they start, and the program gets stuck in a false minimum of solutions. Other issues ~2~ Sometimes, it can be hard to separate the brain from dura and/or skull surrounding the brain. If little bits are left around in the masking images, then perhaps adding one of the following options for will help (this can help the initial skullstripping): -SSopt '-blur_fwhm 2' -SSopt '-blur_fwhm 3' Any other questions/oddities, please don't hesitate to inquire on the AFNI Message Board EXAMPLES ~1~ *** This program has been superseded by the newer version, sswarper2, which has essentially the same syntax and usage as this one. *** 1) Run the program, deciding what the main output directory will be called (e.g., based on the subject ID): @SSwarper \ -input anat_t1w.nii.gz \ -base MNI152_2009_template_SSW.nii.gz \ -subid sub-001 \ -odir group/o.aw_sub-001 2) Same as above, but since we are using one outdir per subject, use more aesthetically pleasing names of temporary files (which get deleted, anyways): @SSwarper \ -tmp_name_nice \ -input anat_t1w.nii.gz \ -base MNI152_2009_template_SSW.nii.gz \ -subid sub-001 \ -odir group/o.aw_sub-001 3) As of version 2.5, you can input a mask to be used instead of skullstripping. For example, a good one might be the parcellation-derived (but filled in) mask from @SUMA_Make_Spec_FS after running FS's recon-all (though you will have to resample it from the FS output grid to that of your input anatomical): @SSwarper \ -tmp_name_nice \ -input anat_t1w.nii.gz \ -mask_ss fs_parc_wb_mask_RES.nii.gz \ -base MNI152_2009_template_SSW.nii.gz \ -subid sub-001 \ -odir group/o.aw_sub-001 EOF if ( ${USER_COST_AFF} == 1 ) then echo "+* WARNING: the user used the '-cost_aff ..' option," echo " but it is no longer used within the program---it has no effect." echo " The affine cost function just follows what is set by" echo " The '-cost_nl_init ..' option value (or its default)." endif goto GOOD_EXIT SHOW_VERSION: echo "version $version (${rev_dat})" goto GOOD_EXIT FAIL_MISSING_ARG: echo "** ERROR: Missing an argument after option flag: '$argv[$ac]'" goto BAD_EXIT BAD_EXIT: exit 1 GOOD_EXIT: exit 0