#!/bin/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.3"; set rev_dat = "March 30, 2020" # + [PT] new opt: "-mask_init .." # + can input mask to replace initial skullstripping # + might be particularly useful with MP2RAGE, and/or more generally # #set version = "2.4"; set rev_dat = "Apr 6, 2020" # [PT] output history of alignment with images (new ssw*hist subdir) # + done mainly making @djunct wrapper: @djunct_ssw_intermed_edge_imgs # #set version = "2.41"; set rev_dat = "Apr 6, 2020" # [PT] bug fix in ALHI conditions (end -> endif) # #set version = "2.42"; set rev_dat = "Apr 6, 2020" # [PT] turn off default "-nodset" in all 3dQwarp cases where it appeared # + those dsets get removed, anyways, but we want them now for the # alignment history QC images # + now, if user turns off alignment hist, then that flag is turned on # #set version = "2.5"; set rev_dat = "Apr 7, 2020" # [PT] split initial 3dQwarp -> 3dAllineate + 3dQwarp # + don't want to use skully version in second case # #set version = "2.51"; set rev_dat = "Apr 8, 2020" # [PT] no more skullstrip # #set version = "2.52"; set rev_dat = "Apr 9, 2020" # [PT] weirding ways of mask_tool to get rid of dura/non-brain stuff # #set version = "2.53"; set rev_dat = "Apr 10, 2020" # [PT] fix final masking # + more useful exit text # + still need to decide if 'skip warp' opt is still relevant... # #set version = "2.54"; set rev_dat = "Apr 11, 2020" # [PT] minor tweaks after checks of different tests # + use inedge in all 3dQwarp places # + 'TAL5' -> 'QQ2' # + comment: at the moment, warpscale=1 does still appear to be best opt # + compartmentalize blocks, and uniformize messaging throughout # + -pblur_* and -penfac* options added for extra control # #set version = "2.54"; set rev_dat = "Apr 11, 2020" # [PT] fix $mpp # #set version = "2.55"; set rev_dat = "Apr 12, 2020" # [PT] add -start2_thr opt, for param control # #set version = "2.56"; set rev_dat = "Apr 12, 2020" # [PT] from data testing, use set default: pblur_src = 0 # #set version = "2.6"; set rev_dat = "Feb 11, 2021" # [PT] clean up some options and help info # #set version = "2.7"; set rev_dat = "Feb 11, 2021" # [PT] introduce -mask_ss opt and branch of processing # #set version = "2.71"; set rev_dat = "Mar 16, 2021" # [PT] put -mask_ss info into help # set version = "2.72"; set rev_dat = "Apr 29, 2024" # [PT] remove old opts (that no longer exist) from help # # ---------------------------------------------------------------- # ---------------------------------------------------------------- # 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 = "sswarper2" 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_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 DO_ALHI = 1 # align history, intermed images, for QC set JUMP_TO_EXTRA_QC = 0 # just for testing/internal purposes set DO_RANDOM_TMP = 0 set pref_nice = junk_ssw set nodset = "" # just used if DO_ALHI==0 set cost_aff = "lpa+ZZ" # used in: 3dAllineate -cost ... set cost_nli = "-lpa" # used in: 3dQwarp, initial rounds set cost_nlf = "-pcl" # used in: 3dQwarp, final rounds set pblur_base = 0 # later Qwarp; so, don't blur template anymore set pblur_src = 0 # later Qwar; prog blur source dset a bit set penfac = 1 # later Qwarp; smaller val -> more warpy set gaus_wt = 4.5 # def for 3dQwarp set saveall = "" # for 3dQwarp (def: don't); just for debugging set SSopt = " " set start2_thr = 500 # used before mask_tool to get rid of nonbrain set mask_ss = "" set HAVE_MASK_SS = 0 # ------------------- 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]" else if ( "$argv[$ac]" == "-minp" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set minp = "$argv[$ac]" else if ( "$argv[$ac]" == "-nolite" ) then set liteopt = "-nolite" else if ( "$argv[$ac]" == "-tight" ) then @ tightness ++ else if ( "$argv[$ac]" == "-noclean" ) then set doclean = 0 else if ( "$argv[$ac]" == "-giant_move" ) then set DO_GIANT = 1 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 else if ( "$argv[$ac]" == "-skipwarp" ) then set skipwarp = 1 else if ( "$argv[$ac]" == "-unifize_off" ) then set DO_UNIFIZE = 0 else if ( "$argv[$ac]" == "-aniso_off" ) then set DO_ANISO = 0 else if ( "$argv[$ac]" == "-mask_ss" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set mask_ss = "$argv[$ac]" set HAVE_MASK_SS = 1 else if ( "$argv[$ac]" == "-align_hist_off" ) then set DO_ALHI = 0 set nodset = "-nodset" 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 else if ( "$argv[$ac]" == "-ceil_off" ) then set DO_CEIL = 0 else if ( "$argv[$ac]" == "-saveall" ) then set saveall = "-saveall" else if ( "$argv[$ac]" == "-tmp_name_rand" ) then set DO_RANDOM_TMP = 1 # [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]" # [PT: Apr 11, 2020] for control of later rounds of 3dQwarp else if ( "$argv[$ac]" == "-pblur_base" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set pblur_base = "$argv[$ac]" # [PT: Apr 11, 2020] for control of later rounds of 3dQwarp else if ( "$argv[$ac]" == "-pblur_src" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set pblur_src = "$argv[$ac]" # [PT: Apr 11, 2020] for control of later rounds of 3dQwarp else if ( "$argv[$ac]" == "-penfac" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set penfac = "$argv[$ac]" # [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]" # [PT: Apr 12, 2020] extra control for this param-- noticing it # might need to change for 7T folks else if ( "$argv[$ac]" == "-start2_thr" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set start2_thr = "$argv[$ac]" else if ( "$argv[$ac]" == "-wtgaus" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set gaus_wt = "$argv[$ac]" else if ( "$argv[$ac]" == "-verb" ) then set verbopt = "-verb" else if ( "$argv[$ac]" == "-echo" ) then set echo else if ( "$argv[$ac]" == "-odir" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set odir = "$argv[$ac]" # ---------- 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 "***** No -input option? :(" goto BAD_EXIT endif if ( ! -f "$Adataset" ) then set chad = `@CheckForAfniDset "$Adataset"` if( "$chad" == "0" || "$chad" == "1" )then echo "***** ${this_prog} -- Not finding dset $Adataset -- exiting :((" goto BAD_EXIT endif endif if( "$SubID" == "" )then echo "***** ${this_prog} -- no subject ID entered with -subid? :(" goto BAD_EXIT 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 \mkdir -p ${odir} # set prefix for temp files: default is no longer random, but can be # asked for to be random (in case people output into the same dir 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/${pref_nice} endif ## 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 "** ${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 "** 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 \ -ulay "${Adataset}" \ -olay "$Basedset" \ -prefix ${odir}/ssw_align_hist.${SubID}/init_qc_00_usrc_obase_${SubID} set pref_alhi = $odir/ssw_align_hist.${SubID}/ssw.${SubID} # pref for imgs set alhi_ct = `count_afni -digits 2 0 20` # padded idx vals, up to large N set alhidx = 0 # count index set olay_alhi = "${Basedset}[1]" # always use this for olay edges if ( ${JUMP_TO_EXTRA_QC} ) then echo "\n++ Just jumpin' to extra QC\n" goto JUMP_TO_EXTRA_QC endif # won't need any +ZZ for this one set cost_aff2 = `adjunct_simplify_cost.py "${cost_aff}"` ######################## start the work ############################# # ---------- Phase 0: prep/clean/unifize the input dset ---------- echo "++ SSW PHASE 0: prep dset" # preliminary masking/inflation 3dmask_tool -echo_edu \ -dilate_inputs 3 \ -prefix "${pref}MASK3.nii" \ -input "${Basedset}[3]" ## 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}" 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}" # ... and this will be new "input" dset set Adataset = "${dset_do}" endif ## Unifize the input T1: this is now hyper-necessary (because ## thresholding later relies on this level # [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 echo "++ SSW0: unifizing" 3dUnifize \ -GM \ -clfrac 0.4 \ -Urad 30 \ -prefix $odir/anatU."$SubID".nii \ -input "$Adataset" else echo "++ SSW0: NOT unifizing" 3dcopy \ "$Adataset" \ $odir/anatU."$SubID".nii endif endif # [PT: Feb 11, 2019] now allows for option *not* to anismoothize if( ! -f $odir/anatUA."${SubID}".nii ) then if ( ${DO_ANISO} ) then echo "++ SSW0: anisosmoothing" 3danisosmooth \ -iters 1 \ -3D \ -automask \ -noneg \ -prefix $odir/anatUA."${SubID}".nii \ $odir/anatU."${SubID}".nii else echo "++ SSW0: NOT anisosmoothing" 3dcopy \ $odir/anatU."$SubID".nii \ $odir/anatUA."$SubID".nii 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 echo "++ SSW0: ceiling" 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 else echo "++ SSW0: NOT ceiling" 3dcopy \ $odir/anatUA."$SubID".nii \ $odir/anatUAC."$SubID".nii endif endif if ( ${DO_ALHI} ) then # just for QC imaging purposes, we want an image of where we are # starting from 3dresample \ -input $odir/anatUAC."$SubID".nii \ -master "${Basedset}" \ -prefix "${pref}start.nii" set lab_alhi = start echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # ---------- Phase I: focus on approx match and outer edge ---------- #### no longer use skullstripping: focus on alignment to define edge if ( $HAVE_MASK_SS ) then # This branch applies the mask_ss, and so basically has to do only # 1 aff alignment. For aff alignment in this branch: # + alignment to [0] block # + got straight to Allin.nii # + output aff mat as *AllinC.aff12.1D, and just copy to final ver echo "++ SSW1: linear affine alignment (with mask_ss)" # apply mask_ss 3dcalc -echo_edu \ -a "${mask_ss}" \ -b $odir/anatUAC."$SubID".nii \ -expr 'step(a)*b' \ -prefix $odir/anatUACS."$SubID".nii if ( ${DO_GIANT} ) then 3dAllineate -echo_edu ${verbopt} \ -base "${Basedset}[0]" \ -cost ${cost_aff} \ -source $odir/anatUACS."$SubID".nii \ -weight "${Basedset}[2]" \ -noneg \ -twopass \ -prefix "${pref}Allin.nii" \ -1Dmatrix_save "${pref}AllinC.aff12.1D" \ -final wsinc5 \ -twobest 11 -maxrot 45 \ -maxshf 40 -source_automask+2 -cmass else 3dAllineate -echo_edu ${verbopt} \ -base "${Basedset}[0]" \ -cost ${cost_aff} \ -source $odir/anatUACS."$SubID".nii \ -weight "${Basedset}[2]" \ -noneg \ -twopass \ -prefix "${pref}Allin.nii" \ -1Dmatrix_save "${pref}AllinC.aff12.1D" \ -final wsinc5 \ -source_automask endif # this is now the full aff mat from orig space to templ; we will # keep the *.aff12.1D file as a final output; *just a copy in this # mask_ss case! cat_matvec \ -ONELINE \ ${pref}AllinC.aff12.1D \ > $odir/anatQQ."${SubID}".aff12.1D if ( ${DO_ALHI} ) then set lab_alhi = Allin_mask_ss echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif else ## Allineate first time (skull on) echo "++ SSW1: linear affine alignment" if ( ${DO_GIANT} ) then 3dAllineate -echo_edu ${verbopt} \ -base "${Basedset}[1]" \ -cost ${cost_aff} \ -source $odir/anatUAC."$SubID".nii \ -weight "${Basedset}[2]" \ -noneg \ -twopass \ -prefix "${pref}AllinA.nii" \ -1Dmatrix_save "${pref}AllinA.aff12.1D" \ -final wsinc5 \ -twobest 11 -maxrot 45 \ -maxshf 40 -source_automask+2 -cmass else 3dAllineate -echo_edu ${verbopt} \ -base "${Basedset}[1]" \ -cost ${cost_aff} \ -source $odir/anatUAC."$SubID".nii \ -weight "${Basedset}[2]" \ -noneg \ -twopass \ -prefix "${pref}AllinA.nii" \ -1Dmatrix_save "${pref}AllinA.aff12.1D" \ -final wsinc5 \ -source_automask endif if ( ${DO_ALHI} ) then set lab_alhi = AllinA echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif ## Refine Allineate (skull off) echo "++ SSW1: linear affine refinement (skull ~off)" 3dcalc -echo_edu \ -a "${pref}AllinA.nii" \ -b "${pref}MASK3.nii" \ -expr 'a*b' \ -prefix "${pref}AllinB.nii" 3dAllineate -echo_edu ${verbopt} \ -base "${Basedset}[0]" \ -cost ${cost_aff2} \ -source "${pref}AllinB.nii" \ -weight "${Basedset}[2]" \ -noneg \ -prefix "${pref}Allin.nii" \ -1Dmatrix_save "${pref}AllinC.aff12.1D" \ -final wsinc5 \ -source_automask # this is now the full aff mat from orig space to templ; we will keep # the *.aff12.1D file as a final output cat_matvec \ -ONELINE \ ${pref}AllinC.aff12.1D \ ${pref}AllinA.aff12.1D \ > $odir/anatQQ."${SubID}".aff12.1D if ( ${DO_ALHI} ) then set lab_alhi = Allin echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif endif # end of else for: $HAVE_MASK_SS ## Run 3dQwarp first time to a moderate level (skull off) echo "++ SSW1: first nonlinear alignment" # Allin is a bit smoothed from 2 allineate resamplings, but # probably is fine for this level of matching; could adjust later 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ ${cost_nli} \ -warpscale ${warpscale} \ -source "${pref}Allin.nii" \ -noneg -maxlev 2 \ -wtgaus ${gaus_wt} \ -inedge \ -prefix "${pref}QQ2.nii" # [PT: Apr 6, 2020] Add intermediate QC image: early alignment, affine # result if ( ${DO_ALHI} ) then set lab_alhi = QQ2 echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # Combine above, to be able to apply to original echo "++ SSW1: apply initial alignments, bring forward orig" 3dNwarpApply -echo_edu \ -warp "${pref}QQ2_WARP.nii $odir/anatQQ.${SubID}.aff12.1D" \ -source "$odir/anatUAC.$SubID.nii" \ -master "${Basedset}" \ -prefix ${pref}start2.nii if ( ${DO_ALHI} ) then set lab_alhi = start2 echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # both apply a 'generous' masking from the base dset (assume we are # within 3 voxels of boundary, basically) *and* apply a threshold echo "++ SSW1: loosely mask warped dset" 3dcalc \ -a "${pref}MASK3.nii" \ -b ${pref}start2.nii \ -expr "a*step(b-${start2_thr})*b" \ -prefix ${pref}s2_thr.nii # clean up some stuff 3dmask_tool \ -dilate_inputs -2 2 \ -inputs ${pref}s2_thr.nii \ -prefix ${pref}s2mask_dil-22.nii 3dmask_tool \ -dilate_inputs 2 \ -fill_holes \ -inputs ${pref}s2mask_dil-22.nii \ -prefix ${pref}s2mask_dil-222h.nii 3dmask_tool \ -dilate_inputs -2 \ -inputs ${pref}s2mask_dil-222h.nii \ -prefix ${pref}s2mask_dil-222h-2.nii # now use this to re-mask the start2 dset (purge neg vals from it, too) 3dcalc \ -a ${pref}s2mask_dil-222h-2.nii \ -b ${pref}s2_thr.nii \ -expr "a*step(b)*b" \ -prefix ${pref}start2_nice.nii if ( ${DO_ALHI} ) then set lab_alhi = start2_nice echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # -------------- Phase II: focus on matching structure ---------------- # don't do any more if skipping the final (precision) warp # ??????? keep this here? if( $skipwarp ) goto CLEANUP # 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; starting over from the "nice" version of start2 echo "++ SSW2: Qwarp to level 5" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}start2_nice.nii" \ -warpscale ${warpscale} \ ${cost_nli} \ -inilev 0 -maxlev 5 \ -noneg ${nodset} \ -weight "${Basedset}[2]" \ -inedge \ -pblur -workhard:5:5 \ -prefix "${pref}QQ5.nii" if ( ${DO_ALHI} ) then set lab_alhi = QQ5 echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # Piece number 2 echo "++ SSW2: Qwarp to level 7" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}start2_nice.nii" \ -iniwarp "${pref}QQ5_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -inilev 6 -maxlev 7 -workhard:6:7 \ -weight "${Basedset}[2]" \ -inedge \ -pblur "${pblur_base}" "${pblur_src}" \ -noneg ${nodset} \ -prefix "${pref}QQ7.nii" if ( ${DO_ALHI} ) then set lab_alhi = QQ7 echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif if( $minp > 13 )then # Final piece for coarse final patch size echo "++ SSW2: Qwarp to minp (patch = ${minp})" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}start2_nice.nii" \ -iniwarp "${pref}QQ7_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -pblur "${pblur_base}" "${pblur_src}" \ -inilev 8 \ -weight "${Basedset}[2]" \ -inedge \ -noneg -minpatch ${minp} \ -Qfinal \ -prefix "${pref}QQminp.nii" else # Penultimate piece for refined final patch size @ mpp = $minp + 6 echo "++ SSW2: Qwarp to mpp (patch = ${mpp})" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}start2_nice.nii" \ -iniwarp "${pref}QQ7_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -pblur "${pblur_base}" "${pblur_src}" \ -inilev 8 -minpatch $mpp \ -weight "${Basedset}[2]" \ -inedge ${nodset} \ -prefix "${pref}QQ9.nii" # Ultimate piece for refined final patch size echo "++ SSW2: Qwarp to minp (patch = ${minp})" 3dQwarp -echo_edu ${saveall} \ $liteopt $verbopt \ -base "${Basedset}[0]" \ -source "${pref}start2_nice.nii" \ -iniwarp "${pref}QQ9_WARP.nii" \ -warpscale ${warpscale} \ ${cost_nlf} \ -pblur "${pblur_base}" "${pblur_src}" \ -inilev 10 \ -weight "${Basedset}[2]" \ -minpatch $minp \ -inedge \ -Qfinal \ -prefix "${pref}QQminp.nii" endif if ( ${DO_ALHI} ) then set lab_alhi = QQminp echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # -------------- Phase III: create/apply final warps ---------------- echo "++ SSW3: Warping estimation done: finalize" # Make the full NL part of the warp; would still need to be # concatenated with affine echo "++ SSW3: create final warps and map dset to final space" 3dNwarpCat -echo_edu \ -warp1 "${pref}QQminp_WARP.nii" \ -warp2 "${pref}QQ2_WARP.nii" \ -prefix "${odir}/anatQQ.${SubID}_WARP.nii" 3dNwarpApply -echo_edu \ -warp "${odir}/anatQQ.${SubID}_WARP.nii ${odir}/anatQQ.${SubID}.aff12.1D" \ -source "${odir}/anatUAC.${SubID}.nii" \ -master "${Basedset}" \ -prefix ${pref}QQnew.nii if ( ${DO_ALHI} ) then set lab_alhi = QQnew echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = "${pref}${lab_alhi}.nii" set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # mask the dset warped from orig space 3dcalc \ -a "${pref}QQnew.nii" \ -b "${Basedset}[3]" \ -expr 'a*step(b)' \ -prefix $odir/anatQQ."${SubID}".nii if ( ${DO_ALHI} ) then set lab_alhi = anatQQ echo "++ SSW align hist: ${lab_alhi}" @ alhidx += 1 ; set jj = ${alhi_ct[${alhidx}]} set ulay = $odir/anatQQ."${SubID}".nii set opref_alhi = "${pref_alhi}.${jj}.${lab_alhi}.jpg" @djunct_ssw_intermed_edge_imgs \ -ulay "${ulay}" \ -olay "${olay_alhi}" \ -box_focus_slices "${olay_alhi}" \ -prefix "${opref_alhi}" endif # [PT: Apr 6, 2020] invert standard space mask result back to standard # space for skullstripping: should be better than anatSS. ## NB: have to use the 'temp' name of the aff12 matrix at this ## point. Use NN interp for the dset, because we will mask it ## eventually. echo "++ SSW3: skullstrip orig space UAC dset (via warp of template mask)" 3dNwarpApply \ -nwarp "${odir}/anatQQ.${SubID}_WARP.nii $odir/anatQQ.${SubID}.aff12.1D" \ -iwarp \ -ainterp NN \ -master "${odir}/anatUAC.${SubID}.nii" \ -source "${Basedset}[3]" \ -prefix "${pref}refmask_in_orig.nii" 3dmask_tool \ -dilate_inputs -1 1 \ -fill_holes \ -prefix "${odir}/anatSS_mask.${SubID}.nii" \ -input "${pref}refmask_in_orig.nii" 3dcalc \ -a $odir/anatUAC."$SubID".nii \ -b "${odir}/anatSS_mask.${SubID}.nii" \ -expr 'a*step(b)' \ -prefix "${odir}/anatSS.${SubID}.nii" # -------------- Phase IV: final QC images ---------------- ## Make pretty pictures echo "++ SSW4: snapshot_volreg QC" @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 --------- echo "++ SSW4: QC of skullstripping (via template mask)" 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 3 2dcat \ -gap 5 \ -gap_col 66 184 254 \ -nx 1 \ -ny 3 \ -prefix ${opref_exqc1} \ ${pref}MONT1*jpg # -------- check alignment in ref vol space --------- echo "++ SSW4: QC of alignment" 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: if ( $doclean ) then echo "++ SSW4: cleaning up" \rm -f "${pref}"* else echo "++ SSW4: NOT cleaning up" endif ## Closing messages/pointers to the User echo "" echo "--------------------------------------------------------" echo "" echo "++ DONE with SSW." echo "" if ( ${DO_ALHI} ) then echo "++ Alignment history QC images are here:" echo " ${pref_alhi}*.jpg" else echo "+* You chose *not* to keep the alignment history images for QC." echo " Oooook. Nothing to see there, folks..." endif echo "" echo "++ Check QC images:" printf " ${odir}/{AM,MA}*.jpg " if ( ${DO_EXTRA_QC} ) then printf " ${odir}/QC*.jpg " echo "" endif echo "" echo " ... for warping (in ref base space):" 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 " ... for skullstripping (in native space):" echo " ulay = anat // olay = SS mask : ${opref_exqc1}" endif echo "" echo "--------------------------------------------------------" goto GOOD_EXIT # ======================================================================== # ======================================================================== SHOW_HELP: cat < anatU.sub007.nii #2: ** now skipped ** #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_volreg3 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.SSwarper' -- you should delete these files manually. EXAMPLES ~1~ 1) Run the program, deciding what the main output directory will be called (e.g., based on the subject ID): sswarper2 \ -input anat_t1w.nii.gz \ -base MNI152_2009_template_SSW.nii.gz \ -subid sub-001 \ -odir group/o.aw_sub-001 2) 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): sswarper2 \ -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 # ------------------------------------------------------- Author: Bob, Bob, there is one Bob, He spells it B-O-B. # ------------------------------------------------------- EOF 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