CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees.
The 1 match
- [1] § Material and methods › Average volumetric template ↔ iterativeN4_multispectral.sh, lines 297–344 · score 0.78 · nlin sym 09c, MNI space, iterativeN4_multispectral.sh, resampled, masking
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Shell · 1,378 lines · 72 KB · other · 1 match
- #!/bin/bash
- # Created by argbash-init v2.8.0
- # Rearrange the order of options below according to what you would like to see in the help message.
- # ARG_OPTIONAL_SINGLE([exclude],[e],[Mask file defining regions to exclude from classifcation, region is still corrected])
- # ARG_OPTIONAL_SINGLE([config],[c],[Path to an alternative config file defining priors to use, use "auto" to use automatic template selection])
- # ARG_OPTIONAL_SINGLE([logfile],[l],[Path to file to log all output])
- # ARG_OPTIONAL_BOOLEAN([standalone],[s],[Script is run standalone so save all outputs])
- # ARG_OPTIONAL_BOOLEAN([autocrop],[a],[Crop the final output to 10 mm around the head determined by headmask from modelspace])
- # ARG_OPTIONAL_SINGLE([max-iterations],[],[Maximum number of iterations to run],[10])
- # ARG_OPTIONAL_SINGLE([convergence-threshold],[],[Coeffcient of variation limit between two bias field estimates],[0.01])
- # ARG_OPTIONAL_SINGLE([classification-prior-weight],[],[How much weight is given to prior classification proabilities during iteration],[0.25])
- # ARG_OPTIONAL_BOOLEAN([debug],[],[Debug mode, increase verbosity further, don't cleanup])
- # ARG_VERBOSE([v])
- # ARG_POSITIONAL_SINGLE([input],[T1w scan to be corrected])
- # ARG_POSITIONAL_SINGLE([output],[Output filename for corrected T1w (also used as basename for other outputs)])
- # ARGBASH_SET_INDENT([ ])
- # ARGBASH_SET_DELIM([ =])
- # ARG_OPTION_STACKING([getopt])
- # ARG_RESTRICT_VALUES([no-local-options])
- # ARG_DEFAULTS_POS([])
- # ARG_HELP([iterativeN4_multispectral.sh is script which performs iterative inhomogeneity (bias field) correction and classification on T1w (and optionally T2w/PDw) MRI scans])
- # ARGBASH_GO()
- # needed because of Argbash --> m4_ignore([
- ### START OF CODE GENERATED BY Argbash v2.8.1 one line above ###
- # Argbash is a bash code generator used to get arguments parsing right.
- # Argbash is FREE SOFTWARE, see https://argbash.io for more info
- die()
- {
- local _ret=$2
- test -n "$_ret" || _ret=1
- test "$_PRINT_HELP" = yes && print_help >&2
- echo "$1" >&2
- exit ${_ret}
- }
- evaluate_strictness()
- {
- [[ "$2" =~ ^-(-(exclude|config|logfile|standalone|autocrop|max-iterations|convergence-threshold|classification-prior-weight|debug|verbose|input|output|help)$|[eclsavh]) ]] && die "You have passed '$2' as a value of argument '$1', which makes it look like that you have omitted the actual value, since '$2' is an option accepted by this script. This is considered a fatal error."
- }
- begins_with_short_option()
- {
- local first_option all_short_options='eclsavh'
- first_option="${1:0:1}"
- test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
- }
- # THE DEFAULTS INITIALIZATION - POSITIONALS
- _positionals=()
- _arg_input=
- _arg_output=
- # THE DEFAULTS INITIALIZATION - OPTIONALS
- _arg_exclude=
- _arg_config=
- _arg_logfile=
- _arg_standalone="off"
- _arg_autocrop="off"
- _arg_max_iterations="10"
- _arg_convergence_threshold="0.01"
- _arg_classification_prior_weight="0.25"
- _arg_debug="off"
- _arg_verbose=0
- print_help()
- {
- printf '%s\n' "iterativeN4_multispectral.sh is script which performs iterative inhomogeneity (bias field) correction and classification on T1w (and optionally T2w/PDw) MRI scans"
- printf 'Usage: %s [-e|--exclude <arg>] [-c|--config <arg>] [-l|--logfile <arg>] [-s|--(no-)standalone] [-a|--(no-)autocrop] [--max-iterations <arg>] [--convergence-threshold <arg>] [--classification-prior-weight <arg>] [--(no-)debug] [-v|--verbose] [-h|--help] <input> <output>\n' "$0"
- printf '\t%s\n' "<input>: T1w scan to be corrected"
- printf '\t%s\n' "<output>: Output filename for corrected T1w (also used as basename for other outputs)"
- printf '\t%s\n' "-e, --exclude: Mask file defining regions to exclude from classifcation, region is still corrected (no default)"
- printf '\t%s\n' "-c, --config: Path to an alternative config file defining priors to use, use \"auto\" to use automatic template selection (no default)"
- printf '\t%s\n' "-l, --logfile: Path to file to log all output (no default)"
- printf '\t%s\n' "-s, --standalone, --no-standalone: Script is run standalone so save all outputs (off by default)"
- printf '\t%s\n' "-a, --autocrop, --no-autocrop: Crop the final output to 10 mm around the head determined by headmask from modelspace (off by default)"
- printf '\t%s\n' "--max-iterations: Maximum number of iterations to run (default: '10')"
- printf '\t%s\n' "--convergence-threshold: Coeffcient of variation limit between two bias field estimates (default: '0.01')"
- printf '\t%s\n' "--classification-prior-weight: How much weight is given to prior classification proabilities during iteration (default: '0.25')"
- printf '\t%s\n' "--debug, --no-debug: Debug mode, increase verbosity further, don't cleanup (off by default)"
- printf '\t%s\n' "-v, --verbose: Set verbose output (can be specified multiple times to increase the effect)"
- printf '\t%s\n' "-h, --help: Prints help"
- }
- parse_commandline()
- {
- _positionals_count=0
- while test $# -gt 0
- do
- _key="$1"
- case "$_key" in
- -e|--exclude)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_exclude="$2"
- shift
- evaluate_strictness "$_key" "$_arg_exclude"
- ;;
- --exclude=*)
- _arg_exclude="${_key##--exclude=}"
- evaluate_strictness "$_key" "$_arg_exclude"
- ;;
- -e*)
- _arg_exclude="${_key##-e}"
- evaluate_strictness "$_key" "$_arg_exclude"
- ;;
- -c|--config)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_config="$2"
- shift
- evaluate_strictness "$_key" "$_arg_config"
- ;;
- --config=*)
- _arg_config="${_key##--config=}"
- evaluate_strictness "$_key" "$_arg_config"
- ;;
- -c*)
- _arg_config="${_key##-c}"
- evaluate_strictness "$_key" "$_arg_config"
- ;;
- -l|--logfile)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_logfile="$2"
- shift
- evaluate_strictness "$_key" "$_arg_logfile"
- ;;
- --logfile=*)
- _arg_logfile="${_key##--logfile=}"
- evaluate_strictness "$_key" "$_arg_logfile"
- ;;
- -l*)
- _arg_logfile="${_key##-l}"
- evaluate_strictness "$_key" "$_arg_logfile"
- ;;
- -s|--no-standalone|--standalone)
- _arg_standalone="on"
- test "${1:0:5}" = "--no-" && _arg_standalone="off"
- ;;
- -s*)
- _arg_standalone="on"
- _next="${_key##-s}"
- if test -n "$_next" -a "$_next" != "$_key"
- then
- { begins_with_short_option "$_next" && shift && set -- "-s" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
- fi
- ;;
- -a|--no-autocrop|--autocrop)
- _arg_autocrop="on"
- test "${1:0:5}" = "--no-" && _arg_autocrop="off"
- ;;
- -a*)
- _arg_autocrop="on"
- _next="${_key##-a}"
- if test -n "$_next" -a "$_next" != "$_key"
- then
- { begins_with_short_option "$_next" && shift && set -- "-a" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
- fi
- ;;
- --max-iterations)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_max_iterations="$2"
- shift
- evaluate_strictness "$_key" "$_arg_max_iterations"
- ;;
- --max-iterations=*)
- _arg_max_iterations="${_key##--max-iterations=}"
- evaluate_strictness "$_key" "$_arg_max_iterations"
- ;;
- --convergence-threshold)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_convergence_threshold="$2"
- shift
- evaluate_strictness "$_key" "$_arg_convergence_threshold"
- ;;
- --convergence-threshold=*)
- _arg_convergence_threshold="${_key##--convergence-threshold=}"
- evaluate_strictness "$_key" "$_arg_convergence_threshold"
- ;;
- --classification-prior-weight)
- test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
- _arg_classification_prior_weight="$2"
- shift
- evaluate_strictness "$_key" "$_arg_classification_prior_weight"
- ;;
- --classification-prior-weight=*)
- _arg_classification_prior_weight="${_key##--classification-prior-weight=}"
- evaluate_strictness "$_key" "$_arg_classification_prior_weight"
- ;;
- --no-debug|--debug)
- _arg_debug="on"
- test "${1:0:5}" = "--no-" && _arg_debug="off"
- ;;
- -v|--verbose)
- _arg_verbose=$((_arg_verbose + 1))
- ;;
- -v*)
- _arg_verbose=$((_arg_verbose + 1))
- _next="${_key##-v}"
- if test -n "$_next" -a "$_next" != "$_key"
- then
- { begins_with_short_option "$_next" && shift && set -- "-v" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
- fi
- ;;
- -h|--help)
- print_help
- exit 0
- ;;
- -h*)
- print_help
- exit 0
- ;;
- *)
- _last_positional="$1"
- _positionals+=("$_last_positional")
- _positionals_count=$((_positionals_count + 1))
- ;;
- esac
- shift
- done
- }
- handle_passed_args_count()
- {
- local _required_args_string="'input' and 'output'"
- test "${_positionals_count}" -ge 2 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require exactly 2 (namely: $_required_args_string), but got only ${_positionals_count}." 1
- test "${_positionals_count}" -le 2 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect exactly 2 (namely: $_required_args_string), but got ${_positionals_count} (the last one was: '${_last_positional}')." 1
- }
- assign_positional_args()
- {
- local _positional_name _shift_for=$1
- _positional_names="_arg_input _arg_output "
- shift "$_shift_for"
- for _positional_name in ${_positional_names}
- do
- test $# -gt 0 || break
- eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
- shift
- done
- }
- parse_commandline "$@"
- handle_passed_args_count
- assign_positional_args 1 "${_positionals[@]}"
- # OTHER STUFF GENERATED BY Argbash
- ### END OF CODE GENERATED BY Argbash (sortof) ### ])
- # [ <-- needed because of Argbash
- set -euoE pipefail
- #Special trick to redirect all output within script into logfile
- #https://unix.stackexchange.com/questions/145651/using-exec-and-tee-to-redirect-logs-to-stdout-and-a-log-file-in-the-same-time
- if [[ -n ${_arg_logfile} ]]; then
- exec > >(tee -ia ${_arg_logfile})
- exec 2> >(tee -ia ${_arg_logfile} >&2)
- fi
- #If debug, print timestamps and every command run
- if [[ ${_arg_debug} == "on" ]]; then
- set -xT
- set -o functrace
- PS4='+\t '
- fi
- #If verbose (or debug) turn on verbose outputs for commands
- if [[ ${_arg_verbose} -ge 1 || ${_arg_debug} == "on" ]]; then
- N4_VERBOSE=1
- fi
- #Create temporary directory for work
- tmpdir=$(mktemp -d)
- #Setup exit trap for cleanup, don't do if debug
- function finish() {
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf "${tmpdir}"
- fi
- }
- trap finish EXIT
- #Add handler for failure to show where things went wrong
- failure() {
- local lineno=$1
- local msg=$2
- echo "Failed at $lineno: $msg"
- }
- trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
- #Set local parallelism inherited from QBATCH
- export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=${THREADS_PER_COMMAND:-$(nproc)}
- export OMP_NUM_THREADS=${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS}
- ### DEFAULT PRIORS ###
- #BeAST configuration
- BEASTLIBRARY_DIR="${QUARANTINE_PATH}/resources/BEaST_libraries/combined"
- BEAST_CONFIG=${BEASTLIBRARY_DIR}/default.1mm.conf
- #mni_icbm152_nlin_sym_09c priors as default
- REGISTRATIONMODEL="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c.mnc"
- REGISTRATIONBRAINMASK="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c_mask.mnc"
- WMPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_wm_tal_nlin_sym_09c.mnc"
- GMPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_gm_tal_nlin_sym_09c.mnc"
- CSFPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_csf_tal_nlin_sym_09c.mnc"
- #Files used to define MNI space
- RESAMPLEMODEL="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c.mnc"
- RESAMPLEMODELBRAINMASK="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c_mask.mnc"
- # Check config files, eventually argbash will do this
- if [[ -n ${_arg_config} && ${_arg_config} != "auto" ]]; then
- if [[ -r ${_arg_config} ]]; then
- source ${_arg_config}
- else
- echo "iterativeN4_multispectral.sh ERROR: config file does not exist or is not readable" && exit 2
- fi
- fi
- if [[ ! -d ${BEASTLIBRARY_DIR} ]]; then
- echo "iterativeN4_multispectral.sh ERROR: ${BEASTLIBRARY_DIR} does not exist"
- fi
- for prior in ${REGISTRATIONMODEL} ${REGISTRATIONBRAINMASK} ${WMPRIOR} ${GMPRIOR} ${CSFPRIOR} ${RESAMPLEMODEL} ${RESAMPLEMODELBRAINMASK} ${BEAST_CONFIG}; do
- if [[ ! -s ${prior} ]]; then
- echo "iterativeN4_multispectral.sh ERROR: File ${prior} does not exist or is zero size" && exit 3
- fi
- done
- #Setup internal variables
- output=${_arg_output}
- originput=${_arg_input}
- #Internal resampled input used for processing
- input=${tmpdir}/t1.mnc
- function outlier_mask() {
- #Generate an outlier mask which combines the vessel segmentation >4.75 * MAD of WM
- local outlier_input=$1
- local outlier_mask=$2
- local outlier_output=$3
- local median
- local mad
- median=$(mincstats -quiet -median -mask ${outlier_mask} -mask_binvalue 1 ${outlier_input})
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -expression "abs(A[0]-${median})" ${outlier_input} ${tmpdir}/${n}/madmap.mnc
- mad=$(mincstats -quiet -median -mask ${outlier_mask} -mask_binvalue 1 ${tmpdir}/${n}/madmap.mnc)
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression "(((0.6745*(A[0]-${median}))/${mad})<4.75)&&(A[1]<45)?1:0" \
- ${outlier_input} ${tmpdir}/vessels.mnc ${outlier_output}
- }
- function renorm() {
- #Renormalize image 0.1%-(GM/WM mean)-99.9% to 0-32767-65535 using a classification mask
- #Compute the percentiles using the GM/WM mask
- #Achieved via solving a linear system to get a 2nd order polynomial remapping
- #of the intensity values
- local renorm_input=$1
- local renorm_classification=$2
- local usebrainmask="${3:-}"
- local wmbinvalue
- local gmbinvalue
- if [[ -n ${usebrainmask} ]]; then
- wmbinvalue=1
- gmbinvalue=1
- else
- wmbinvalue=3
- gmbinvalue=2
- fi
- #Compute the percentiles and median values of GM and WM
- valuelow=$(mincstats -quiet -mask ${tmpdir}/headmask.mnc -mask_binvalue 1 -pctT 1 ${renorm_input})
- valuewm=$(mincstats -quiet -median -mask ${renorm_classification} -mask_binvalue ${wmbinvalue} ${renorm_input})
- valuegm=$(mincstats -quiet -median -mask ${renorm_classification} -mask_binvalue ${gmbinvalue} ${renorm_input})
- valuehigh=$(mincstats -quiet -mask ${tmpdir}/headmask.mnc -mask_binvalue 1 -pctT 99 ${renorm_input})
- #Solve the linear system of a quadratic polynomial mapping the input values to 0-32767-65535
- mapping=($(python -c "import numpy as np; print(np.array2string(np.linalg.solve(np.array([[1, ${valuelow}, ${valuelow}**2], [1, ((${valuewm}+${valuegm})/2.0), ((${valuewm}+${valuegm})/2.0)**2], [1, ${valuehigh}, ${valuehigh}**2]]),np.array([0,32767,65535])),separator= ' ')[1:-1])"))
- #Apply the mpapping
- minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(A[0]^2*${mapping[2]} + A[0]*${mapping[1]} + ${mapping[0]},0,65535)" \
- ${renorm_input} $(dirname ${renorm_input})/$(basename ${renorm_input} .mnc).norm.mnc
- mv -f $(dirname ${renorm_input})/$(basename ${renorm_input} .mnc).norm.mnc ${renorm_input}
- }
- #Function used to do bias field correction
- function do_N4_correct() {
- #input fov mask weight output bias shrink classifymask
- local n4input=$1
- local n4initmask=$2
- local n4brainmask=$3
- local n4weight=$4
- local n4corrected=$5
- local n4bias=$6
- local n4shrink=$7
- local n4classifymask=$8
- #Estimate bias field
- N4BiasFieldCorrection ${N4_VERBOSE:+--verbose} -d 3 -s ${n4shrink} -w ${n4weight} -x ${n4initmask} \
- -b [ 200 ] -c [ 300x300x300x300,1e-5 ] --histogram-sharpening [ 0.05,0.01,200 ] \
- -i ${tmpdir}/${n}/t1.mnc \
- -o [ ${n4corrected},${tmpdir}/${n}/bias2.mnc ] -r 0
- ImageMath 3 ${tmpdir}/${n}/bias2.mnc / ${tmpdir}/${n}/bias2.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias2.mnc)
- ImageMath 3 ${n4bias} m ${tmpdir}/prebias.mnc ${tmpdir}/${n}/bias2.mnc
- ImageMath 3 ${n4bias} / ${n4bias} $(mincstats -quiet -mean ${n4bias})
- ImageMath 3 ${n4corrected} / ${n4input} ${n4bias}
- cp -f ${n4bias} ${tmpdir}/prebias.mnc
- renorm ${n4corrected} ${n4classifymask}
- }
- function iterative_precorrect() {
- local pctTlow
- local pctThigh
- #Foreground/background via multi-level otsu
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight1.mnc Otsu 4 ${tmpdir}/nonzero.mnc
- ThresholdImage 3 ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/weight1.mnc 2 Inf 1 0
- ImageMath 3 ${tmpdir}/${n}/weight1.mnc GetLargestComponent ${tmpdir}/${n}/weight1.mnc
- iMath 3 ${tmpdir}/${n}/weight1.mnc MC ${tmpdir}/${n}/weight1.mnc 2 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight1.mnc FillHoles ${tmpdir}/${n}/weight1.mnc 2
- cp -f ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/mask1.mnc
- ImageMath 3 ${tmpdir}/${n}/weight1.mnc m ${tmpdir}/${n}/weight1.mnc ${tmpdir}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight1.mnc GetLargestComponent ${tmpdir}/${n}/weight1.mnc
- #Exclude Hotspots
- minccalc -quiet ${N4_VERBOSE:+-verbose} \
- -expression "A[0]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)?A[1]:0" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/weighttemp.mnc
- mv -f ${tmpdir}/${n}/weighttemp.mnc ${tmpdir}/${n}/weight1.mnc
- #First round of correction
- N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 50x50x50x50,0 ] \
- -w ${tmpdir}/${n}/weight1.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 4 --verbose \
- --histogram-sharpening [ 0.15,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
- ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc \
- $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
- ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/bias.mnc
- #Renormalize intensity
- pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
- pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
- mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
- #Second round Foreground/background via multi-level otsu
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight2.mnc Otsu 4 ${tmpdir}/nonzero.mnc
- ThresholdImage 3 ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/weight2.mnc 2 Inf 1 0
- ImageMath 3 ${tmpdir}/${n}/weight2.mnc GetLargestComponent ${tmpdir}/${n}/weight2.mnc
- iMath 3 ${tmpdir}/${n}/weight2.mnc MC ${tmpdir}/${n}/weight2.mnc 3 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight2.mnc FillHoles ${tmpdir}/${n}/weight2.mnc 2
- cp -f ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/mask2.mnc
- cp -f ${tmpdir}/${n}/mask2.mnc ${tmpdir}/fgmask.mnc
- ImageMath 3 ${tmpdir}/${n}/weight2.mnc m ${tmpdir}/${n}/weight2.mnc ${tmpdir}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight2.mnc GetLargestComponent ${tmpdir}/${n}/weight2.mnc
- pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 0.1 ${input})
- pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.9 ${input})
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
- ${input} ${tmpdir}/${n}/t1.mnc
- cp ${tmpdir}/${n}/t1.mnc ${tmpdir}/t1.renorm.mnc
- input=${tmpdir}/t1.renorm.mnc
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight2.mnc m ${tmpdir}/${n}/weight2.mnc ${tmpdir}/nonzero.mnc
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
- -expression "A[0]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.5 ${tmpdir}/${n}/t1.mnc)?A[1]:0" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/weighttemp.mnc
- mv -f ${tmpdir}/${n}/weighttemp.mnc ${tmpdir}/${n}/weight2.mnc
- N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 50x50x50x50,0 ] \
- -w ${tmpdir}/${n}/weight2.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 4 --verbose \
- --histogram-sharpening [ 0.15,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
- ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc \
- $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
- ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/bias.mnc
- pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
- pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
- mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
- cp -f ${tmpdir}/${n}/bias.mnc ${tmpdir}/${n}/prebias.mnc
- itk_vesselness --scales 8 --rescale ${tmpdir}/${n}/t1.mnc ${tmpdir}/vessels.mnc
- i=0
- #Iterative correction using tissue masking, some badly biased scans can't be
- #corrected in one-shot
- while true; do
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/mask$((2 + i)).mnc
- ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/otsu.mnc 2 Inf 1 0
- ImageMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc GetLargestComponent ${tmpdir}/${n}/otsu.mnc
- iMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc MC ${tmpdir}/${n}/mask$((3 + i)).mnc 8 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc FillHoles ${tmpdir}/${n}/mask$((3 + i)).mnc 2
- cp -f ${tmpdir}/${n}/mask$((3 + i)).mnc ${tmpdir}/fgmask.mnc
- if [[ $i == 0 ]]; then
- ImageMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc + ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/mask$((2 + i)).mnc
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
- -expression "(A[0]<45)&&(A[1]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 99.5 ${tmpdir}/${n}/t1.mnc))?A[2]:0" \
- ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight$((3 + i)).mnc
- else
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
- -expression "(A[0]<45)&&(A[1]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 99.5 ${tmpdir}/${n}/t1.mnc))?A[2]:0" \
- ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight$((3 + i)).mnc
- iMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc ME ${tmpdir}/${n}/otsu.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc GetLargestComponent ${tmpdir}/${n}/weight$((3 + i)).mnc
- iMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc MD ${tmpdir}/${n}/weight$((3 + i)).mnc 1 1 ball 1
- fi
- N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 300x300x300x300,1e-4 ] \
- -w ${tmpdir}/${n}/weight$((3 + i)).mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 2 --verbose \
- --histogram-sharpening [ 0.05,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
- ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
- ImageMath 3 ${tmpdir}/${n}/prebias.mnc m ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias.mnc
- ImageMath 3 ${tmpdir}/${n}/prebias.mnc / ${tmpdir}/${n}/prebias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/prebias.mnc)
- ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/prebias.mnc
- ((++i))
- [[ ( ${i} -le 2 ) ]] || break
- pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
- pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
- mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
- done
- pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask5.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
- pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask5.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
- minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
- ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/corrected.mnc
- minc_anlm ${N4_VERBOSE:+--verbose} --clobber --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/${n}/corrected.mnc ${tmpdir}/${n}/t1.mnc
- }
- function classify_to_mask() {
- #Convert classify image into a mask
- #Mostly a clone of the supersteps of the antsBrainExtraction supersteps
- ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/gm.mnc 2 2 1 0
- ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/wm.mnc 3 3 1 0
- ImageMath 3 ${tmpdir}/${n}/gm.mnc GetLargestComponent ${tmpdir}/${n}/gm.mnc
- ImageMath 3 ${tmpdir}/${n}/wm.mnc GetLargestComponent ${tmpdir}/${n}/wm.mnc
- ImageMath 3 ${tmpdir}/${n}/gm.mnc FillHoles ${tmpdir}/${n}/gm.mnc 2
- ImageMath 3 ${tmpdir}/${n}/classifymask.mnc addtozero ${tmpdir}/${n}/gm.mnc ${tmpdir}/${n}/wm.mnc
- iMath 3 ${tmpdir}/${n}/classifymask.mnc ME ${tmpdir}/${n}/classifymask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/classifymask.mnc GetLargestComponent ${tmpdir}/${n}/classifymask.mnc
- iMath 3 ${tmpdir}/${n}/classifymask.mnc MD ${tmpdir}/${n}/classifymask.mnc 2 1 ball 1
- iMath 3 ${tmpdir}/bmask_E.mnc ME ${tmpdir}/masks/mnimask.mnc 10 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/classifymask.mnc addtozero ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/bmask_E.mnc
- ImageMath 3 ${tmpdir}/${n}/classifymask.mnc FillHoles ${tmpdir}/${n}/classifymask.mnc 2
- }
- function make_qc() {
- #Generate a standardized view of the final correct brain in MNI space, with classification overlayed
- #Create animated version if img2webp is available
- mkdir -p ${tmpdir}/qc
- #Resample into MNI space for all the inputs
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
- -i ${tmpdir}/${n}/classify.mnc -o ${tmpdir}/qc/classify.mnc -r ${RESAMPLEMODEL} -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
- -i ${tmpdir}/corrected.mnc -o ${tmpdir}/qc/corrected.mnc -r ${RESAMPLEMODEL} -n BSpline[5]
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
- -i ${tmpdir}/origqcref.mnc -o ${tmpdir}/qc/orig.mnc -r ${RESAMPLEMODEL} -n BSpline[5]
- mincmath -clobber -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/qc/corrected.mnc ${tmpdir}/qc/corrected.clamp.mnc
- mv -f ${tmpdir}/qc/corrected.clamp.mnc ${tmpdir}/qc/corrected.mnc
- mincmath -clobber -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/qc/orig.mnc ${tmpdir}/qc/orig.clamp.mnc
- mv -f ${tmpdir}/qc/orig.clamp.mnc ${tmpdir}/qc/orig.mnc
- #Create the bounding box for create_verify_image
- mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} $(mincbbox -mincresample ${tmpdir}/qc/classify.mnc) ${tmpdir}/qc/classify.mnc ${tmpdir}/qc/label-crop.mnc
- minccalc -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression '1' ${tmpdir}/qc/label-crop.mnc ${tmpdir}/qc/bounding.mnc
- #Trasverse
- create_verify_image -range_floor 0 ${tmpdir}/qc/trans_classify.rgb \
- -width 1920 -autocols 10 -autocol_planes t \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
- volume_overlay:${tmpdir}/qc/classify.mnc:0.4
- create_verify_image -range_floor 0 ${tmpdir}/qc/trans_corrected.rgb \
- -width 1920 -autocols 10 -autocol_planes t \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/trans_corrected_gray.rgb \
- -width 1920 -autocols 10 -autocol_planes t \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/trans_orig.rgb \
- -width 1920 -autocols 10 -autocol_planes t \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
- #Sagital
- create_verify_image -range_floor 0 ${tmpdir}/qc/sag_classify.rgb \
- -width 1920 -autocols 10 -autocol_planes s \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
- volume_overlay:${tmpdir}/qc/classify.mnc:0.4
- create_verify_image -range_floor 0 ${tmpdir}/qc/sag_corrected.rgb \
- -width 1920 -autocols 10 -autocol_planes s \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/sag_corrected_gray.rgb \
- -width 1920 -autocols 10 -autocol_planes s \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/sag_orig.rgb \
- -width 1920 -autocols 10 -autocol_planes s \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
- #Coronal
- create_verify_image -range_floor 0 ${tmpdir}/qc/cor_classify.rgb \
- -width 1920 -autocols 10 -autocol_planes c \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
- volume_overlay:${tmpdir}/qc/classify.mnc:0.4
- create_verify_image -range_floor 0 ${tmpdir}/qc/cor_corrected.rgb \
- -width 1920 -autocols 10 -autocol_planes c \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/cor_corrected_gray.rgb \
- -width 1920 -autocols 10 -autocol_planes c \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
- create_verify_image -range_floor 0 ${tmpdir}/qc/cor_orig.rgb \
- -width 1920 -autocols 10 -autocol_planes c \
- -bounding_volume ${tmpdir}/qc/bounding.mnc \
- -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
- convert -background black -strip -append \
- ${tmpdir}/qc/cor_corrected.rgb \
- ${tmpdir}/qc/cor_classify.rgb \
- ${tmpdir}/qc/sag_corrected.rgb \
- ${tmpdir}/qc/sag_classify.rgb \
- ${tmpdir}/qc/trans_corrected.rgb \
- ${tmpdir}/qc/trans_classify.rgb \
- ${tmpdir}/qc/corrected.mpc
- convert -background black -strip -append \
- ${tmpdir}/qc/cor_orig.rgb \
- ${tmpdir}/qc/cor_corrected_gray.rgb \
- ${tmpdir}/qc/sag_orig.rgb \
- ${tmpdir}/qc/sag_corrected_gray.rgb \
- ${tmpdir}/qc/trans_orig.rgb \
- ${tmpdir}/qc/trans_corrected_gray.rgb \
- ${tmpdir}/qc/orig.mpc
- #Save static QC jpg
- convert -background black -strip -interlace Plane -sampling-factor 4:2:0 -quality "85%" \
- ${tmpdir}/qc/corrected.mpc $(dirname ${output})/$(basename ${output} .mnc).jpg
- #If webp software is available animate a before/after image
- if command -v img2webp; then
- convert -background black ${tmpdir}/qc/corrected.mpc ${tmpdir}/qc/corrected.png
- convert -background black ${tmpdir}/qc/orig.mpc ${tmpdir}/qc/orig.png
- img2webp -d 750 -lossy -min_size ${tmpdir}/qc/corrected.png ${tmpdir}/qc/orig.png -o $(dirname ${output})/$(basename ${output} .mnc).webp || true
- fi
- }
- function test_templates() {
- #Automatic template selection for most similar template for use as prior
- #Loop over the configs/auto config files and choose the best one based on ants CC
- mkdir -p ${tmpdir}/test_templates
- for configfile in $(dirname "$(readlink -f "$0")")/configs/auto/*cfg; do
- source ${configfile}
- antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
- --output [ ${tmpdir}/test_templates/$(basename ${configfile} .cfg),${tmpdir}/test_templates/$(basename ${configfile} .cfg).mnc ] \
- --use-histogram-matching 1 \
- --initial-moving-transform [ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1 ] \
- --transform Translation[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x500x500x500x500,1e-6,10 ] \
- --shrink-factors 6x6x6x6x6x6x6x6 \
- --smoothing-sigmas 6.35574237559x5.93006674681x5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Rigid[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x500x500x500,1e-6,10 ] \
- --shrink-factors 6x6x6x6x6x6x5 \
- --smoothing-sigmas 4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Similarity[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
- --shrink-factors 6x6x5x4x3x2 \
- --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Similarity[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
- --shrink-factors 6x6x5x4x3x2 \
- --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
- --transform Affine[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
- --convergence [ 500x450x150x50,1e-6,10 ] \
- --shrink-factors 4x3x2x1 \
- --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
- echo ${configfile},$(MeasureImageSimilarity -d 3 -m CC[${REGISTRATIONMODEL},${tmpdir}/test_templates/$(basename ${configfile} .cfg).mnc,1,4] \
- -x ${REGISTRATIONBRAINMASK}) >> ${tmpdir}/test_templates/results.csv
- done
- #Prep and load winner template
- unset MNI_XFM
- echo "Choosing template $(sort -k2 -g -t, ${tmpdir}/test_templates/results.csv | cut -d"," -f 1 | head -1)"
- source $(sort -k2 -g -t, ${tmpdir}/test_templates/results.csv | cut -d"," -f 1 | head -1)
- #Store template registration for later use
- cp -f ${tmpdir}/test_templates/$(basename $(sort -k2 -g -t, ${tmpdir}/test_templates/results.csv | cut -d"," -f 1 | head -1) .cfg)0_GenericAffine.xfm ${tmpdir}/template_bootstrap.xfm
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf ${tmpdir}/test_templates
- fi
- }
- ##########START OF SCRIPT#############
- #Forceably convert to MINC2, and clamp range to avoid negative numbers, rescale to 0-65535
- mincconvert -2 ${originput} ${tmpdir}/originput.mnc
- #Rescale initial data into entirely positive range (fix for completely negative data)
- ImageMath 3 ${tmpdir}/originput.mnc RescaleImage ${tmpdir}/originput.mnc 0 65535
- #Very mild range clamp for very hot voxels
- mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp \
- -const2 $(mincstats -quiet -floor 1e-12 -pctT 0.1 ${tmpdir}/originput.mnc) \
- $(mincstats -quiet -floor 1e-12 -pctT 99.9 ${tmpdir}/originput.mnc) \
- ${tmpdir}/originput.mnc ${tmpdir}/originput.clamp.mnc
- ImageMath 3 ${tmpdir}/originput.clamp.mnc RescaleImage ${tmpdir}/originput.clamp.mnc 0 65535
- mincresample -quiet ${N4_VERBOSE:+-verbose} -like ${tmpdir}/originput.mnc -keep -unsigned -short \
- ${tmpdir}/originput.clamp.mnc ${tmpdir}/originput.clamp.resample.mnc
- mv -f ${tmpdir}/originput.clamp.resample.mnc ${tmpdir}/originput.mnc
- rm -f ${tmpdir}/originput.clamp.mnc
- originput=${tmpdir}/originput.mnc
- cp -f ${originput} ${tmpdir}/origqcref.mnc
- #Isotropize, and normalize intensity range, this is the file that will be processed in the pipeline
- #Need smoothing for downsampling to avoid aliasing
- #Ideas stolen from https://discourse.itk.org/t/resampling-to-isotropic-signal-processing-theory/1403
- isostep=1.0
- inputres=$(python -c "print('\n'.join([str(abs(x)) for x in [float(x) for x in \"$(PrintHeader ${originput} 1)\".split(\"x\")]]))")
- blurs=""
- for dim in ${inputres}; do
- if [[ $(python -c "print(${dim}>(${isostep}-1e-6))") == True ]]; then
- blurs+=1e-12x
- else
- blurs+=$(python -c "import math; print(math.sqrt((${isostep}**2.0 - ${dim}**2.0)/(2.0*math.sqrt(2.0*math.log(2.0)))**2.0))")x
- fi
- done
- SmoothImage 3 ${originput} "${blurs%?}" ${tmpdir}/smoothed.mnc 1 0
- ResampleImage 3 ${tmpdir}/smoothed.mnc ${input} ${isostep}x${isostep}x${isostep} 0 4
- mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 $(mincstats -max -quiet ${input}) ${input} ${tmpdir}/input.clamp.mnc
- ImageMath 3 ${input} RescaleImage ${tmpdir}/input.clamp.mnc 0 65535
- rm -f ${tmpdir}/input.clamp.mnc
- ImageMath 3 ${input} PadImage ${input} 20
- #Generate a global nonzero mask to always exclude pure background
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
- #If exclusion mask exists, negate it to produce a multiplicative exlcusion mask, resample to internal resolution
- if [[ -n ${_arg_exclude} ]]; then
- ImageMath 3 ${tmpdir}/exclude.mnc Neg ${_arg_exclude}
- excludemask=${tmpdir}/exclude.mnc
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${excludemask} -r ${input} -n GenericLabel -o ${excludemask}
- else
- excludemask=""
- fi
- mkdir -p ${tmpdir}/masks
- ################################################################################
- #Round 0
- #Iterative estimation of a mask with multilevel otsu to find foreground-background
- #Also found forground mask and use it combined to template FOV registration to
- #trim the FOV to generate a headmask
- ################################################################################
- n=0
- mkdir -p ${tmpdir}/${n}
- minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${input} ${tmpdir}/${n}/t1.mnc
- iterative_precorrect
- #If the "auto" template method is selected, do the registrations and CC
- #estimate to find best matching model
- if [[ ${_arg_config} == "auto" ]]; then
- test_templates
- fi
- #Register to model to resample back a FOV mask
- if [[ -s ${tmpdir}/template_bootstrap.xfm ]]; then
- cp -f ${tmpdir}/template_bootstrap.xfm ${tmpdir}/${n}/mni0_GenericAffine.xfm
- else
- antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
- --output [ ${tmpdir}/${n}/mni ] \
- --use-histogram-matching 1 \
- --initial-moving-transform [ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1 ] \
- --transform Translation[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x500x500x500x500,1e-6,10 ] \
- --shrink-factors 6x6x6x6x6x6x6x6 \
- --smoothing-sigmas 6.35574237559x5.93006674681x5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Rigid[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x500x500x500,1e-6,10 ] \
- --shrink-factors 6x6x6x6x6x6x5 \
- --smoothing-sigmas 4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Similarity[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
- --shrink-factors 6x6x5x4x3x2 \
- --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
- --masks [ NOMASK,NOMASK ] \
- --transform Similarity[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
- --shrink-factors 6x6x5x4x3x2 \
- --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
- --transform Affine[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
- --convergence [ 500x450x150x0,1e-6,10 ] \
- --shrink-factors 4x3x2x1 \
- --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
- fi
- #Make a fov mask from all 1's of the
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression '1' ${REGISTRATIONMODEL} ${tmpdir}/modelfovmask.mnc
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/modelfovmask.mnc \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -o ${tmpdir}/headmask.mnc -r ${tmpdir}/${n}/t1.mnc -n GenericLabel
- #Headmask is intersection of filled otsu foreground mask and FOV from model
- ImageMath 3 ${tmpdir}/headmask.mnc m ${tmpdir}/headmask.mnc ${tmpdir}/fgmask.mnc
- cp -f ${tmpdir}/fgmask.mnc ${tmpdir}/fgmask_orig.mnc
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
- -expression "(A[0]<45)&&(A[1]<$(mincstats -quiet -mask ${tmpdir}/headmask.mnc -mask_binvalue 1 -pctT 99.5 ${tmpdir}/${n}/t1.mnc))?A[2]:0" \
- ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/headmask.mnc ${tmpdir}/${n}/otsu.mnc
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/otsu.mnc
- ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight6.mnc 2 Inf 1 0
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/weight6.mnc
- ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight6.mnc 2 Inf 1 0
- iMath 3 ${tmpdir}/${n}/weight6.mnc ME ${tmpdir}/${n}/weight6.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight6.mnc GetLargestComponent ${tmpdir}/${n}/weight6.mnc
- iMath 3 ${tmpdir}/${n}/weight6.mnc MD ${tmpdir}/${n}/weight6.mnc 1 1 ball 1
- #Use exclude mask if provided
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/weight6.mnc m ${tmpdir}/${n}/weight6.mnc ${excludemask}
- fi
- N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 300x300x300x300,1e-4 ] \
- -w ${tmpdir}/${n}/weight6.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 2 --verbose \
- --histogram-sharpening [ 0.05,0.01,200 ] -r 0
- ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
- ImageMath 3 ${tmpdir}/${n}/prebias.mnc m ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias.mnc
- ImageMath 3 ${tmpdir}/${n}/prebias.mnc / ${tmpdir}/${n}/prebias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/prebias.mnc)
- ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/prebias.mnc
- renorm ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight6.mnc usemask
- cp ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/corrected.mnc
- #Resample headmask into subject space, zero background and recrop
- ImageMath 3 ${input} PadImage ${input} 50
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/headmask.mnc -r ${input} -n GenericLabel
- ImageMath 3 ${input} m ${input} ${tmpdir}/headmask.mnc
- ExtractRegionFromImageByMask 3 ${input} ${tmpdir}/input.crop.mnc ${tmpdir}/headmask.mnc 1 10
- mv -f ${tmpdir}/input.crop.mnc ${input}
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/headmask.mnc -r ${input} -n GenericLabel
- minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
- #Backup the original bias field estimate, in case cropping is not done, so we can correct the neck tissues
- cp -f ${tmpdir}/${n}/prebias.mnc ${tmpdir}/bias_orig.mnc
- #Need to fill the bias field with 1's in case we're padding the image
- mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} -fill -fillvalue 1 -like ${input} ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias_resample.mnc
- cp -f ${tmpdir}/${n}/prebias.mnc ${tmpdir}/prebias.mnc
- mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} -fill -fillvalue 1 -like ${input} ${tmpdir}/prebias.mnc ${tmpdir}/prebias_resample.mnc
- mv -f ${tmpdir}/${n}/bias_resample.mnc ${tmpdir}/${n}/bias.mnc
- mv -f ${tmpdir}/prebias_resample.mnc ${tmpdir}/prebias.mnc
- #Resample the exlude mask into the new recropped space
- if [[ -n ${_arg_exclude} ]]; then
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${excludemask} -r ${input} -n GenericLabel -o ${excludemask}
- fi
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/corrected.mnc -o ${tmpdir}/${n}/corrected.mnc -r ${input}
- ImageMath 3 ${tmpdir}/${n}/corrected.mnc m ${tmpdir}/${n}/corrected.mnc ${tmpdir}/headmask.mnc
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression '1' ${input} ${tmpdir}/initmask.mnc
- ################################################################################
- #Round 1, N4 with estimate weight mask using affine registered GM/WM/CSF priors
- ################################################################################
- ((++n))
- mkdir -p ${tmpdir}/${n}
- minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
- ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
- itk_vesselness --clobber --scales 8 --rescale ${tmpdir}/${n}/t1.mnc ${tmpdir}/vessels.mnc
- antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
- --output [ ${tmpdir}/${n}/mni ] \
- --use-histogram-matching 1 \
- --initial-moving-transform ${tmpdir}/$((n - 1))/mni0_GenericAffine.xfm \
- --transform Similarity[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
- --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
- --shrink-factors 6x6x5x4x3x2 \
- --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
- --transform Affine[ 0.1 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
- --convergence [ 500x450x150x50,1e-6,10 ] \
- --shrink-factors 4x3x2x1 \
- --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
- unset reg_initalization
- #Make MNI-space copy of brain for BeAST
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/t1.mnc \
- ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/${n}/mni0_GenericAffine.xfm -n BSpline[ 5 ] -o ${tmpdir}/${n}/mni.mnc -r ${RESAMPLEMODEL}
- #BSpline[ 5 ] does weird things to intensity, clip back to positive range
- mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/${n}/mni.mnc ${tmpdir}/${n}/mni.clamp.mnc
- mv -f ${tmpdir}/${n}/mni.clamp.mnc ${tmpdir}/${n}/mni.mnc
- #Shrink the MNI mask for the first intensity matching
- iMath 3 ${tmpdir}/${n}/shrinkmask.mnc ME ${RESAMPLEMODELBRAINMASK} 2 1 ball 1
- #Intensity normalize
- volume_pol ${N4_VERBOSE:+--verbose} --order 1 --min 0 --max 100 --noclamp \
- --source_mask ${tmpdir}/${n}/shrinkmask.mnc --target_mask ${RESAMPLEMODELBRAINMASK} \
- ${tmpdir}/${n}/mni.mnc ${RESAMPLEMODEL} ${tmpdir}/${n}/mni.norm.mnc
- #Run a quick beast to get a brain mask
- mincbeast ${N4_VERBOSE:+-verbose} -sparse -v2 -double -fill -median -same_res -flip -conf ${BEAST_CONFIG} \
- ${BEASTLIBRARY_DIR} ${tmpdir}/${n}/mni.norm.mnc ${tmpdir}/${n}/beastmask.mnc
- #Resample beast mask and MNI mask to native space
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${tmpdir}/${n}/t1.mnc \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] ${MNI_XFM:+-t [${MNI_XFM},1]} \
- -i ${tmpdir}/${n}/beastmask.mnc -o ${tmpdir}/${n}/bmask.mnc -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${tmpdir}/${n}/t1.mnc \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -i ${REGISTRATIONBRAINMASK} \
- -o ${tmpdir}/${n}/mnimask.mnc -n GenericLabel
- #BeAST Failure mode of a chunk of almost unattached voxels, try to remove
- iMath 3 ${tmpdir}/${n}/bmask.mnc ME ${tmpdir}/${n}/bmask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/bmask.mnc GetLargestComponent ${tmpdir}/${n}/bmask.mnc
- iMath 3 ${tmpdir}/${n}/bmask.mnc MD ${tmpdir}/${n}/bmask.mnc 1 1 ball 1
- cp -f ${tmpdir}/${n}/bmask.mnc ${tmpdir}/masks/bmask.mnc
- cp -f ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/masks/affinemask.mnc
- ImageMath 3 ${tmpdir}/${n}/mask.mnc addtozero ${tmpdir}/masks/bmask.mnc ${tmpdir}/masks/affinemask.mnc
- ImageMath 3 ${tmpdir}/${n}/mask.mnc GetLargestComponent ${tmpdir}/${n}/mask.mnc
- iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 2 1 ball 1
- #Resample MNI Priors to Native space for classification
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${WMPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior3.mnc -n Linear
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${GMPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior2.mnc -n Linear
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${CSFPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior1.mnc -n Linear
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
- fi
- #Estimate outlier to exclude from classification
- outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/bmask.mnc ${tmpdir}/${n}/hotmask.mnc
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
- #Classify brain
- Atropos ${N4_VERBOSE:+--verbose} -d 3 -x ${tmpdir}/${n}/mask_D.mnc -c [ 5,0.005 ] -a ${tmpdir}/${n}/t1.mnc -s 1x2 -s 2x3 \
- -i PriorProbabilityImages[ 3,${tmpdir}/${n}/SegmentationPrior%d.mnc,0.1 ] -k Gaussian -m [ 0.1,1x1x1 ] \
- -o ${tmpdir}/${n}/classify.mnc -r 1 -p Socrates[ 0 ] --winsorize-outliers BoxPlot
- #Convert classification to a brain mask and brain tissue mask
- ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/2.mnc 2 2 1 0
- ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/3.mnc 3 3 1 0
- ImageMath 3 ${tmpdir}/${n}/2.mnc GetLargestComponent ${tmpdir}/${n}/2.mnc
- ImageMath 3 ${tmpdir}/${n}/3.mnc GetLargestComponent ${tmpdir}/${n}/3.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc addtozero ${tmpdir}/${n}/2.mnc ${tmpdir}/${n}/3.mnc
- iMath 3 ${tmpdir}/${n}/weight.mnc ME ${tmpdir}/${n}/weight.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
- iMath 3 ${tmpdir}/${n}/weight.mnc MD ${tmpdir}/${n}/weight.mnc 2 1 ball 1
- iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/weight.mnc 5 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/mask2.mnc FillHoles ${tmpdir}/${n}/mask2.mnc 2
- cp -f ${tmpdir}/${n}/mask2.mnc ${tmpdir}/masks/classifymask${n}.mnc
- #User provided exclusion mask
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
- fi
- #Remove outliers round 2
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
- ImageMath 3 ${tmpdir}/${n}/classify.mnc m ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/mask2.mnc
- #Always exclude 0 from correction
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
- do_N4_correct ${input} ${tmpdir}/initmask.mnc ${tmpdir}/${n}/mask2.mnc ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/corrected.mnc ${tmpdir}/${n}/bias.mnc 2 ${tmpdir}/${n}/classify.mnc
- #Calculate coeffcient of variation between this round bias field and prior round
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -zero -expression 'A[0]/A[1]' ${tmpdir}/$((n - 1))/bias.mnc ${tmpdir}/${n}/bias.mnc ${tmpdir}/${n}/ratio.mnc
- python -c "print(float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -stddev ${tmpdir}/${n}/ratio.mnc)\") / float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -mean ${tmpdir}/${n}/ratio.mnc)\"))" >>${tmpdir}/convergence.txt
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf ${tmpdir}/$((n - 1))
- fi
- ################################################################################
- #Round 2, N4 with classification from nonlinear registered priors
- ################################################################################
- ((++n))
- mkdir -p ${tmpdir}/${n}
- minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
- ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
- #Affine register to MNI space, tweak registration
- antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
- --output [ ${tmpdir}/${n}/mni ] \
- --use-histogram-matching 1 \
- --initial-moving-transform ${tmpdir}/$((n - 1))/mni0_GenericAffine.xfm \
- --transform Affine[ 0.05 ] \
- --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
- --convergence [ 500x450x150x50,1e-6,10 ] \
- --shrink-factors 4x3x2x1 \
- --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ ${REGISTRATIONBRAINMASK},${tmpdir}/$((n - 1))/mask2.mnc ]
- cp -f ${tmpdir}/$((n - 1))/mask2.mnc ${tmpdir}/${n}/mask.mnc
- iMath 3 ${tmpdir}/${n}/extractmask.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/t1.extracted.mnc m ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/extractmask.mnc
- ImageMath 3 ${tmpdir}/extractmodel.mnc m ${REGISTRATIONMODEL} ${REGISTRATIONBRAINMASK}
- #Non linearly register priors
- #We use the extracted images because subjects with different distance between
- #brain and skull consistently fail
- antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
- --output [ ${tmpdir}/${n}/nonlin ] \
- --initial-moving-transform ${tmpdir}/${n}/mni0_GenericAffine.xfm \
- --use-histogram-matching 1 \
- --transform SyN[ 0.1,3,0 ] \
- --metric CC[ ${tmpdir}/extractmodel.mnc,${tmpdir}/${n}/t1.extracted.mnc,1,4 ] \
- --convergence [ 500x500x500x500x500x500x500x500x500x500x0x0x0,1e-6,10 ] \
- --shrink-factors 5x5x5x5x5x5x5x5x5x4x3x2x1 \
- --smoothing-sigmas 5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ NOMASK,NOMASK ] \
- --transform SyN[ 0.1,3,0 ] \
- --metric CC[ ${tmpdir}/extractmodel.mnc,${tmpdir}/${n}/t1.extracted.mnc,1,2 ] \
- --convergence [ 500x500x225x225x0,1e-6,10 ] \
- --shrink-factors 5x4x3x2x1 \
- --smoothing-sigmas 2.08040503813x1.64470459404x1.20112240879x0.735534255037x0.0mm \
- --masks [ ${REGISTRATIONBRAINMASK},${tmpdir}/${n}/extractmask.mnc ]
- #Save MNI space registration for QC later
- cp -f ${tmpdir}/${n}/mni0_GenericAffine.xfm ${tmpdir}/mni0_GenericAffine.xfm
- #Resample MNI Priors to Native space for classification
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${WMPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
- -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior3.mnc -n Linear
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${GMPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
- -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior2.mnc -n Linear
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${CSFPRIOR} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
- -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior1.mnc -n Linear
- #Resample back to subject space
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${REGISTRATIONBRAINMASK} \
- -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/mnimask.mnc -n GenericLabel
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression '(A[0]>=0.25||A[1]>=0.25)?1:0' \
- ${tmpdir}/${n}/SegmentationPrior3.mnc ${tmpdir}/${n}/SegmentationPrior2.mnc ${tmpdir}/${n}/mniprobmask.mnc
- #Make MNI mask a combination of both the MNI mask and the tissue probabilty mask
- ImageMath 3 ${tmpdir}/${n}/mnimask.mnc addtozero ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/${n}/mniprobmask.mnc
- #Last time we generate MNI mask, save it outside iterations
- cp -f ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/masks/mnimask.mnc
- #Vote a consensus mask from prior masking estimates
- ImageMath 3 ${tmpdir}/${n}/mask.mnc MajorityVoting ${tmpdir}/masks/*mnc
- iMath 3 ${tmpdir}/${n}/mask.mnc MC ${tmpdir}/${n}/mask.mnc 1 1 ball 1
- #Expand the mask a bit
- iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
- fi
- #Find outliers to exclude from classification
- ThresholdImage 3 ${tmpdir}/$((n - 1))/classify.mnc ${tmpdir}/${n}/outlier_wm.mnc 3 3 1 0
- ImageMath 3 ${tmpdir}/${n}/outlier_wm.mnc GetLargestComponent ${tmpdir}/${n}/outlier_wm.mnc
- outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/outlier_wm.mnc ${tmpdir}/${n}/hotmask.mnc
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
- #Do an initial classification using the MNI priors
- Atropos ${N4_VERBOSE:+--verbose} -d 3 -x ${tmpdir}/${n}/mask_D.mnc -c [ 5,0.005 ] -a ${tmpdir}/${n}/t1.mnc -s 1x2 -s 2x3 \
- -i PriorProbabilityImages[ 3,${tmpdir}/${n}/SegmentationPrior%d.mnc,${_arg_classification_prior_weight} ] -k Gaussian -m [ 0.1,1x1x1 ] \
- -o [ ${tmpdir}/${n}/classify.mnc,${tmpdir}/${n}/SegmentationPosteriors%d.mnc ] -r 1 -p Aristotle[ 0 ] --winsorize-outliers BoxPlot \
- -l [ 0.69314718055994530942,1 ]
- #Convert classification to the mask
- classify_to_mask
- cp ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/masks/classifymask${n}.mnc
- ImageMath 3 ${tmpdir}/${n}/mask2.mnc MajorityVoting ${tmpdir}/masks/*mnc
- iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/mask2.mnc 1 1 ball 1
- #Combine GM and WM proabability images into a N4 mask,
- ImageMath 3 ${tmpdir}/${n}/weight.mnc PureTissueN4WeightMask ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc RescaleImage ${tmpdir}/${n}/weight.mnc 0 1
- ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
- iMath 3 ${tmpdir}/${n}/weightmask.mnc ME ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weightmask.mnc
- iMath 3 ${tmpdir}/${n}/weightmask.mnc MD ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/weightmask.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
- #Clip the classification weight and posteriors
- for item in ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/SegmentationPosteriors1.mnc ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc; do
- ImageMath 3 ${item} m ${item} ${tmpdir}/${n}/mask2.mnc
- done
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
- fi
- #Always exclude 0 from correction
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
- do_N4_correct ${input} ${tmpdir}/initmask.mnc ${tmpdir}/${n}/mask2.mnc ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/corrected.mnc ${tmpdir}/${n}/bias.mnc 2 ${tmpdir}/${n}/classify.mnc
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -zero -expression 'A[0]/A[1]' ${tmpdir}/$((n - 1))/bias.mnc ${tmpdir}/${n}/bias.mnc ${tmpdir}/${n}/ratio.mnc
- python -c "print(float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -stddev ${tmpdir}/${n}/ratio.mnc)\") / float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -mean ${tmpdir}/${n}/ratio.mnc)\"))" >>${tmpdir}/convergence.txt
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf ${tmpdir}/$((n - 1))
- fi
- ################################################################################
- #Remaining rounds, N4 with segmentation posteriors bootstrapped from prior run until convergence
- ################################################################################
- while true; do
- ((++n))
- mkdir -p ${tmpdir}/${n}
- minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
- ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
- ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
- cp -f ${tmpdir}/$((n - 1))/mask2.mnc ${tmpdir}/${n}/mask.mnc
- iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
- fi
- ThresholdImage 3 ${tmpdir}/$((n - 1))/classify.mnc ${tmpdir}/${n}/outlier_wm.mnc 3 3 1 0
- ImageMath 3 ${tmpdir}/${n}/outlier_wm.mnc GetLargestComponent ${tmpdir}/${n}/outlier_wm.mnc
- outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/outlier_wm.mnc ${tmpdir}/${n}/hotmask.mnc
- ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
- #Do a classification using the last round posteriors, remove outliers
- Atropos ${N4_VERBOSE:+--verbose} -d 3 -x ${tmpdir}/${n}/mask_D.mnc -c [ 5,0.005 ] -a ${tmpdir}/${n}/t1.mnc -s 1x2 -s 2x3 \
- -i PriorProbabilityImages[ 3,${tmpdir}/$((n - 1))/SegmentationPosteriors%d.mnc,0.5 ] -k Gaussian -m [ 0.1,1x1x1 ] \
- -o [ ${tmpdir}/${n}/classify.mnc,${tmpdir}/${n}/SegmentationPosteriors%d.mnc ] -r 1 -p Aristotle[ 1 ] --winsorize-outliers BoxPlot \
- -l [ 0.69314718055994530942,1 ]
- classify_to_mask
- cp -f ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/masks/classifymask${n}.mnc
- ImageMath 3 ${tmpdir}/${n}/mask2.mnc MajorityVoting ${tmpdir}/masks/*mnc
- iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/mask2.mnc 1 1 ball 1
- #Combine GM and WM probably images into a N4 mask,
- ImageMath 3 ${tmpdir}/${n}/weight.mnc PureTissueN4WeightMask ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc RescaleImage ${tmpdir}/${n}/weight.mnc 0 1
- ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
- iMath 3 ${tmpdir}/${n}/weightmask.mnc ME ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weightmask.mnc
- iMath 3 ${tmpdir}/${n}/weightmask.mnc MD ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/weightmask.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
- #Clip the classification weight and posteriors
- for item in ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/SegmentationPosteriors1.mnc ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc; do
- ImageMath 3 ${item} m ${item} ${tmpdir}/${n}/mask2.mnc
- done
- if [[ -n ${excludemask} ]]; then
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
- fi
- #Always exclude 0 from correction
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
- ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
- do_N4_correct ${input} ${tmpdir}/initmask.mnc ${tmpdir}/${n}/mask2.mnc ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/corrected.mnc ${tmpdir}/${n}/bias.mnc 2 ${tmpdir}/${n}/classify.mnc
- #Compute coeffcient of variation
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -zero -expression 'A[0]/A[1]' ${tmpdir}/$((n - 1))/bias.mnc ${tmpdir}/${n}/bias.mnc ${tmpdir}/${n}/ratio.mnc
- python -c "print(float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -stddev ${tmpdir}/${n}/ratio.mnc)\") / float(\"$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -mean ${tmpdir}/${n}/ratio.mnc)\"))" >>${tmpdir}/convergence.txt
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf ${tmpdir}/$((n - 1))
- fi
- # Break if greater than max iterations or less than convergence threshold
- [[ (${n} -lt ${_arg_max_iterations}) && ($(python -c "print($(tail -1 ${tmpdir}/convergence.txt) > ${_arg_convergence_threshold})") == "True") ]] || break
- done
- echo "--------------------"
- echo "Convergence results:"
- cat ${tmpdir}/convergence.txt
- echo "--------------------"
- #If cropping is enabled, recrop the originput file and resample the mask again
- if [[ ${_arg_autocrop} == "on" ]]; then
- ImageMath 3 ${originput} PadImage ${originput} 50
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/finalheadmask.mnc -r ${originput} -n GenericLabel
- ImageMath 3 ${originput} m ${originput} ${tmpdir}/finalheadmask.mnc
- ExtractRegionFromImageByMask 3 ${originput} ${tmpdir}/originput.crop.mnc ${tmpdir}/finalheadmask.mnc 1 10
- mv -f ${tmpdir}/originput.crop.mnc ${originput}
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} -i ${tmpdir}/headmask.mnc \
- -o ${tmpdir}/headmask.mnc -n GenericLabel
- cp -f ${tmpdir}/headmask.mnc ${tmpdir}/fgmask.mnc
- else
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} -i ${tmpdir}/fgmask_orig.mnc \
- -o ${tmpdir}/fgmask.mnc -n GenericLabel
- ImageMath 3 ${originput} m ${originput} ${tmpdir}/fgmask.mnc
- fi
- #Resample final results into original space and correct original input file
- n4input=${originput}
- n4corrected=${tmpdir}/corrected.mnc
- n4classifymask=${tmpdir}/finalclassify.mnc
- #Reconstruct a final bias field
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} \
- -n BSpline[5] -i ${tmpdir}/${n}/bias.mnc -o ${tmpdir}/finalbias.mnc
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} \
- -n BSpline[5] -i ${tmpdir}/bias_orig.mnc -o ${tmpdir}/bias_orig.mnc
- ImageMath 3 ${tmpdir}/finalbias.mnc addtozero ${tmpdir}/finalbias.mnc ${tmpdir}/bias_orig.mnc
- ImageMath 3 ${tmpdir}/finalbias.mnc addtozero ${tmpdir}/finalbias.mnc 1
- ImageMath 3 ${n4corrected} / ${originput} ${tmpdir}/finalbias.mnc
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/mask2.mnc -o ${tmpdir}/finalmask.mnc -r ${n4corrected} -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/masks/bmask.mnc -o ${tmpdir}/finalbmask.mnc -r ${n4corrected} -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/classifymask.mnc -o ${tmpdir}/finalclassifymask.mnc -r ${n4corrected} -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/masks/mnimask.mnc -o ${tmpdir}/finalmnimask.mnc -r ${n4corrected} -n GenericLabel
- antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/classify.mnc -o ${tmpdir}/finalclassify.mnc -r ${n4corrected} -n GenericLabel
- valuelow=$(mincstats -quiet -mask ${tmpdir}/fgmask.mnc -mask_binvalue 1 -pctT 0.1 ${n4corrected})
- valuewm=$(mincstats -quiet -median -mask ${n4classifymask} -mask_binvalue 3 ${n4corrected})
- valuegm=$(mincstats -quiet -median -mask ${n4classifymask} -mask_binvalue 2 ${n4corrected})
- valuehigh=$(mincstats -quiet -mask ${tmpdir}/fgmask.mnc -mask_binvalue 1 -pctT 99.9 ${n4corrected})
- mapping=($(python -c "import numpy as np; print(np.array2string(np.linalg.solve(np.array([[1, ${valuelow}, ${valuelow}**2], [1, ((${valuewm}+${valuegm})/2.0), ((${valuewm}+${valuegm})/2.0)**2], [1, ${valuehigh}, ${valuehigh}**2]]),np.array([0,32767,65535])),separator= ' ')[1:-1])"))
- minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(A[0]^2*${mapping[2]} + A[0]*${mapping[1]} + ${mapping[0]},0,65535)" \
- ${n4corrected} $(dirname ${n4corrected})/$(basename ${n4corrected} .mnc).norm.mnc
- mv -f $(dirname ${n4corrected})/$(basename ${n4corrected} .mnc).norm.mnc ${n4corrected}
- cp -f ${tmpdir}/corrected.mnc ${output}
- #Output final classification files if standalone
- if [[ ${_arg_standalone} == "on" || ${_arg_debug} == "on" ]]; then
- make_qc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalbmask.mnc $(dirname ${output})/$(basename ${output} .mnc).beastmask.mnc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalmnimask.mnc $(dirname ${output})/$(basename ${output} .mnc).mnimask.mnc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalclassify.mnc $(dirname $output)/$(basename ${output} .mnc).classify.mnc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalmask.mnc $(dirname $output)/$(basename ${output} .mnc).mask.mnc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalclassifymask.mnc $(dirname $output)/$(basename ${output} .mnc).classifymask.mnc
- minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned -expression 'A[0]*A[1]' ${output} ${tmpdir}/finalmask.mnc ${tmpdir}/output.extracted.mnc
- ExtractRegionFromImageByMask 3 ${tmpdir}/output.extracted.mnc ${tmpdir}/output.extracted.crop.mnc ${tmpdir}/finalmask.mnc 1 10
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned ${tmpdir}/output.extracted.crop.mnc $(dirname $output)/$(basename ${output} .mnc).extracted.mnc
- minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/corrected.mnc ${tmpdir}/corrected.denoise.mnc
- mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned ${tmpdir}/corrected.denoise.mnc $(dirname $output)/$(basename ${output} .mnc).denoise.mnc
- fi
- if [[ ${_arg_debug} == "off" ]]; then
- rm -rf ${tmpdir}
- fi
- # ] <-- needed because of Argbash
iterativeN4_multispectral.sh at commit 70dbcef, under other · at the source
Overview
- Brain Imaging Center, Montréal Neurological Institute, McGill University, Montréal, Canada
- McGill University, Montréal, Canada
- Cerebral Imaging Centre, Douglas Mental Health University Institute, Verdun, Canada
- Department of Psychiatry, McGill University, Montréal, Canada
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repositories
Its files are read in the Code ↔ Paper reader above, with 1 match between paragraphs and lines of code.
visionandcognition/nhp-freesurfer
097785ceb428b96e1fae9dc772a31a716b2854b2, 14 February 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
12 files
- .ipynb_checkpoints/
average_mulitple_t1-chec , Shell, 36 lineskpoint.sh - Documentation/
fs_demo.sh , Shell, 24 lines - Notebooks/
NHP-Freesurfer_2025.ipyn , Jupyter, 640 linesb - Notebooks/
NHP_Surfaces_and Flatmaps.ipynb , Jupyter, 640 lines - Notebooks/
NMTinFS.ipynb , Jupyter, 468 lines - Notebooks/
Results_to_surface.ipynb , Jupyter, 244 lines - Notebooks/
freesurfer2pycortex.ipyn , Jupyter, 137 linesb - Notebooks/
nmt2pycortex.ipynb , Jupyter, 137 lines - Notebooks/
swap_xdir_voxels.sh , Shell, 6 lines - average_multiple_t1.sh, Shell, 36 lines
- LICENSE, License, 21 lines
- README.md, Text, 13 lines
neurabenn/precon_all
58b4bc581b5cf713b1f84e2f875649904052af89, 12 August 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
26 files
- bin/
ants_to_fsl_warp.sh , Shell, 249 lines - bin/
art.sh , Shell, 67 lines - bin/
bet_animal.sh , Shell, 236 lines - bin/
cortex_labelgen.sh , Shell, 156 lines - bin/
denoise.sh , Shell, 81 lines - bin/
down_surf.sh , Shell, 81 lines - bin/
fill_animal.sh , Shell, 357 lines - bin/
group_scripts/ , Shell, 316 linesaverage_surface_maker.sh - bin/
group_scripts/ , Shell, 111 linesconsensus_label.sh - bin/
group_scripts/ , Shell, 252 linesmake_surftemp.sh - bin/
group_scripts/ , Shell, 190 linespet_sounds.sh - bin/
precon_logging.sh , Shell, 117 lines - bin/
precon_qc.sh , Shell, 342 lines - bin/
seg_animal.sh , Shell, 208 lines - bin/
surfing_safari.sh , Shell, 665 lines - bin/
tess_animal.sh , Shell, 206 lines - utils/
IntensityNormalizeHeadIm , Shell, 26 linesage.sh - utils/
bedpostdir2pseudoT1.sh , Shell, 31 lines - utils/
calc_label_dice.sh , Shell, 59 lines - utils/
intensity_afni_unifize.s , Shell, 68 linesh - utils/
manual_bet_fix.sh , Shell, 65 lines - utils/
register_brain_extracted , Shell, 67 lines_volume.sh - utils/
register_surfaces.sh , Shell, 245 lines - utils/
wm_regional_dual_thresho , Shell, 57 linesld.sh - LICENSE, License, 21 lines
- README.MD, Text, 151 lines
CoBrALab/iterativeN4_multispectral
70dbcef412e7bf6a13cefffd2c9a73115499803d, 12 November 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
3 files
- iterativeN4_multispectra
l.sh , Shell, 1,378 lines, 1 match - LICENSE, License, 76 lines
- README.md, Text, 79 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 35 scripts, each with its path and the digest of its content;
- 1 match between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Code and data availability statement
The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- no repository, dataset or request procedure was recognized in it
Read it in the paper: doi.org/10.1016/j.ynirp.2026.100360.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Authors: added Erika Nolan (0000-0001-9921-6028); Trisanna Sprung-Much (0000-0003-3477-8155); removed Erika Nolan; Trisanna Sprung-Much
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 6 keywords, 1 funder, 58 references.
Cite
This paper
Lepage, C., Nolan, E., Sprung-Much, T., Devenyi, G. A., Petrides, M., & Evans, A. C. (2026). CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees. Neuroimage. Reports, 6(3), 100360. https://
BibTeX
@article{lepage2026civet
author = {Lepage, Claude and Nolan, Erika and Sprung-Much, Trisanna and Devenyi, Gabriel A. and Petrides, Michael and Evans, Alan C.},
title = {{CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees}},
journal = {Neuroimage. Reports},
year = {2026},
month = jun,
volume = {6},
number = {3},
pages = {100360},
publisher = {Elsevier},
issn = {2666-9560},
doi = {10.1016/
url = {https://
pmid = {42318323},
pmcid = {PMC13273796}
}
RIS
TY - JOUR
AU - Lepage, Claude
AU - Nolan, Erika
AU - Sprung-Much, Trisanna
AU - Devenyi, Gabriel A.
AU - Petrides, Michael
AU - Evans, Alan C.
TI - CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees
T2 - Neuroimage. Reports
J2 - Neuroimage Rep
PY - 2026
DA - 2026/
VL - 6
IS - 3
SP - 100360
SN - 2666-9560
PB - Elsevier
DO - 10.1016/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1016/
"type": "article-journal",
"title": "CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees",
"container-title": "Neuroimage. Reports",
"author": [
{
"family": "Lepage",
"given": "Claude"
},
{
"family": "Nolan",
"given": "Erika"
},
{
"family": "Sprung-Much",
"given": "Trisanna"
},
{
"family": "Devenyi",
"given": "Gabriel A."
},
{
"family": "Petrides",
"given": "Michael"
},
{
"family": "Evans",
"given": "Alan C."
}
],
"container-title-short":
"volume": "6",
"issue": "3",
"page": "100360",
"DOI": "10.1016/
"PMID": "42318323",
"PMCID": "PMC13273796",
"ISSN": "2666-9560",
"publisher": "Elsevier",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
10
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s42003-026-10066-6 [code]
- Morphological and anatomical variations in subcortical anatomy between humans and chimpanzees associated with heritability patterns related to human behavioral traits.Journal: Communications biologyIn common: FreeSurfer, NumPy, non-human primate, 4 references, author Gabriel A. Devenyi
- [2] doi:10.1002/nbm.70353 [code]
- Automated Surface-Based Segmentation of Deep Gray Matter Regions Based on Diffusion Tensor Images Reveals Unique Age Trajectories Over the Healthy Lifespan.Journal: NMR in biomedicineIn common: Connectome Workbench, ANTs, FreeSurfer, 2 other tools, structural MRI / diffusion, 5 references
- [3] doi:10.1162/imag.a.1222 [code]
- Network-based near-scalp personalized brain stimulation targets.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Connectome Workbench, AFNI, ANTs, 3 other tools, 2 references
- [4] doi:10.1038/s41467-026-71151-2 [code]
- Common and distinct neural correlates of social interaction processing and theory of mind in narratives.Journal: Nature communicationsIn common: Connectome Workbench, AFNI, ANTs, 3 other tools, 2 references
- [5] doi:10.1038/s41467-026-76011-7 [code]
- Human cortex organizes dynamic co-fluctuations along the sensorimotor-association
axis. Journal: Nature communicationsIn common: Connectome Workbench, AFNI, ANTs, 3 other tools, 1 reference - [6] doi:10.1016/j.isci.2026.116903 [code]
- Neurobiological and behavioral relevance of intrinsic functional connectome constraints on task-evoked neural activation.Journal: iScienceIn common: Connectome Workbench, AFNI, ANTs, 3 other tools, 1 reference
- [7] doi:10.1016/j.crmeth.2026.101473 [code]
- AmygdalaGo-BOLT for boundary-aware segmentation of the human amygdala.Journal: Cell reports methodsIn common: Connectome Workbench, AFNI, ANTs, 3 other tools, methods / tools, structural MRI / diffusion
- [8] doi:10.1162/imag.a.1212 [code]
- Intracortical microstructure profiling: A cross-modal method for indexing cortical lamination.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Connectome Workbench, ANTs, FreeSurfer, 2 other tools, methods / tools, structural MRI / diffusion, 2 references
- [9] doi:10.1016/j.neuron.2026.04.011 [code]
- Precision fMRI reveals densely interdigitated network patches with conserved motifs in the lateral prefrontal cortex.Journal: NeuronIn common: Connectome Workbench, AFNI, ANTs, 3 other tools, 1 reference
- [10] doi:10.1162/imag.a.1198 [code]
- MEPrep: A robust pipeline for multi-echo fMRI denoising and preprocessing.Journal: Imaging neuroscience (Cambridge, Mass.)In common: AFNI, FreeSurfer, FSL, 1 other tool, methods / tools, 4 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 3 repositories of the authors' code, each at its verified commit and with its license, 35 scripts, and 1 match between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:2cb56cc9fd9d4c67…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
