OSCR

CIVET-Chimp: An automated pipeline for MRI-based cortical surface extraction in chimpanzees.

Code ↔ Paper

1 match between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 1 match
  1. [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

  1. #!/bin/bash
  2. # Created by argbash-init v2.8.0
  3. # Rearrange the order of options below according to what you would like to see in the help message.
  4. # ARG_OPTIONAL_SINGLE([exclude],[e],[Mask file defining regions to exclude from classifcation, region is still corrected])
  5. # ARG_OPTIONAL_SINGLE([config],[c],[Path to an alternative config file defining priors to use, use "auto" to use automatic template selection])
  6. # ARG_OPTIONAL_SINGLE([logfile],[l],[Path to file to log all output])
  7. # ARG_OPTIONAL_BOOLEAN([standalone],[s],[Script is run standalone so save all outputs])
  8. # ARG_OPTIONAL_BOOLEAN([autocrop],[a],[Crop the final output to 10 mm around the head determined by headmask from modelspace])
  9. # ARG_OPTIONAL_SINGLE([max-iterations],[],[Maximum number of iterations to run],[10])
  10. # ARG_OPTIONAL_SINGLE([convergence-threshold],[],[Coeffcient of variation limit between two bias field estimates],[0.01])
  11. # ARG_OPTIONAL_SINGLE([classification-prior-weight],[],[How much weight is given to prior classification proabilities during iteration],[0.25])
  12. # ARG_OPTIONAL_BOOLEAN([debug],[],[Debug mode, increase verbosity further, don't cleanup])
  13. # ARG_VERBOSE([v])
  14. # ARG_POSITIONAL_SINGLE([input],[T1w scan to be corrected])
  15. # ARG_POSITIONAL_SINGLE([output],[Output filename for corrected T1w (also used as basename for other outputs)])
  16. # ARGBASH_SET_INDENT([ ])
  17. # ARGBASH_SET_DELIM([ =])
  18. # ARG_OPTION_STACKING([getopt])
  19. # ARG_RESTRICT_VALUES([no-local-options])
  20. # ARG_DEFAULTS_POS([])
  21. # ARG_HELP([iterativeN4_multispectral.sh is script which performs iterative inhomogeneity (bias field) correction and classification on T1w (and optionally T2w/PDw) MRI scans])
  22. # ARGBASH_GO()
  23. # needed because of Argbash --> m4_ignore([
  24. ### START OF CODE GENERATED BY Argbash v2.8.1 one line above ###
  25. # Argbash is a bash code generator used to get arguments parsing right.
  26. # Argbash is FREE SOFTWARE, see https://argbash.io for more info
  27. die()
  28. {
  29. local _ret=$2
  30. test -n "$_ret" || _ret=1
  31. test "$_PRINT_HELP" = yes && print_help >&2
  32. echo "$1" >&2
  33. exit ${_ret}
  34. }
  35. evaluate_strictness()
  36. {
  37. [[ "$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."
  38. }
  39. begins_with_short_option()
  40. {
  41. local first_option all_short_options='eclsavh'
  42. first_option="${1:0:1}"
  43. test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
  44. }
  45. # THE DEFAULTS INITIALIZATION - POSITIONALS
  46. _positionals=()
  47. _arg_input=
  48. _arg_output=
  49. # THE DEFAULTS INITIALIZATION - OPTIONALS
  50. _arg_exclude=
  51. _arg_config=
  52. _arg_logfile=
  53. _arg_standalone="off"
  54. _arg_autocrop="off"
  55. _arg_max_iterations="10"
  56. _arg_convergence_threshold="0.01"
  57. _arg_classification_prior_weight="0.25"
  58. _arg_debug="off"
  59. _arg_verbose=0
  60. print_help()
  61. {
  62. 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"
  63. 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"
  64. printf '\t%s\n' "<input>: T1w scan to be corrected"
  65. printf '\t%s\n' "<output>: Output filename for corrected T1w (also used as basename for other outputs)"
  66. printf '\t%s\n' "-e, --exclude: Mask file defining regions to exclude from classifcation, region is still corrected (no default)"
  67. 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)"
  68. printf '\t%s\n' "-l, --logfile: Path to file to log all output (no default)"
  69. printf '\t%s\n' "-s, --standalone, --no-standalone: Script is run standalone so save all outputs (off by default)"
  70. 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)"
  71. printf '\t%s\n' "--max-iterations: Maximum number of iterations to run (default: '10')"
  72. printf '\t%s\n' "--convergence-threshold: Coeffcient of variation limit between two bias field estimates (default: '0.01')"
  73. printf '\t%s\n' "--classification-prior-weight: How much weight is given to prior classification proabilities during iteration (default: '0.25')"
  74. printf '\t%s\n' "--debug, --no-debug: Debug mode, increase verbosity further, don't cleanup (off by default)"
  75. printf '\t%s\n' "-v, --verbose: Set verbose output (can be specified multiple times to increase the effect)"
  76. printf '\t%s\n' "-h, --help: Prints help"
  77. }
  78. parse_commandline()
  79. {
  80. _positionals_count=0
  81. while test $# -gt 0
  82. do
  83. _key="$1"
  84. case "$_key" in
  85. -e|--exclude)
  86. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  87. _arg_exclude="$2"
  88. shift
  89. evaluate_strictness "$_key" "$_arg_exclude"
  90. ;;
  91. --exclude=*)
  92. _arg_exclude="${_key##--exclude=}"
  93. evaluate_strictness "$_key" "$_arg_exclude"
  94. ;;
  95. -e*)
  96. _arg_exclude="${_key##-e}"
  97. evaluate_strictness "$_key" "$_arg_exclude"
  98. ;;
  99. -c|--config)
  100. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  101. _arg_config="$2"
  102. shift
  103. evaluate_strictness "$_key" "$_arg_config"
  104. ;;
  105. --config=*)
  106. _arg_config="${_key##--config=}"
  107. evaluate_strictness "$_key" "$_arg_config"
  108. ;;
  109. -c*)
  110. _arg_config="${_key##-c}"
  111. evaluate_strictness "$_key" "$_arg_config"
  112. ;;
  113. -l|--logfile)
  114. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  115. _arg_logfile="$2"
  116. shift
  117. evaluate_strictness "$_key" "$_arg_logfile"
  118. ;;
  119. --logfile=*)
  120. _arg_logfile="${_key##--logfile=}"
  121. evaluate_strictness "$_key" "$_arg_logfile"
  122. ;;
  123. -l*)
  124. _arg_logfile="${_key##-l}"
  125. evaluate_strictness "$_key" "$_arg_logfile"
  126. ;;
  127. -s|--no-standalone|--standalone)
  128. _arg_standalone="on"
  129. test "${1:0:5}" = "--no-" && _arg_standalone="off"
  130. ;;
  131. -s*)
  132. _arg_standalone="on"
  133. _next="${_key##-s}"
  134. if test -n "$_next" -a "$_next" != "$_key"
  135. then
  136. { 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."
  137. fi
  138. ;;
  139. -a|--no-autocrop|--autocrop)
  140. _arg_autocrop="on"
  141. test "${1:0:5}" = "--no-" && _arg_autocrop="off"
  142. ;;
  143. -a*)
  144. _arg_autocrop="on"
  145. _next="${_key##-a}"
  146. if test -n "$_next" -a "$_next" != "$_key"
  147. then
  148. { 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."
  149. fi
  150. ;;
  151. --max-iterations)
  152. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  153. _arg_max_iterations="$2"
  154. shift
  155. evaluate_strictness "$_key" "$_arg_max_iterations"
  156. ;;
  157. --max-iterations=*)
  158. _arg_max_iterations="${_key##--max-iterations=}"
  159. evaluate_strictness "$_key" "$_arg_max_iterations"
  160. ;;
  161. --convergence-threshold)
  162. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  163. _arg_convergence_threshold="$2"
  164. shift
  165. evaluate_strictness "$_key" "$_arg_convergence_threshold"
  166. ;;
  167. --convergence-threshold=*)
  168. _arg_convergence_threshold="${_key##--convergence-threshold=}"
  169. evaluate_strictness "$_key" "$_arg_convergence_threshold"
  170. ;;
  171. --classification-prior-weight)
  172. test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
  173. _arg_classification_prior_weight="$2"
  174. shift
  175. evaluate_strictness "$_key" "$_arg_classification_prior_weight"
  176. ;;
  177. --classification-prior-weight=*)
  178. _arg_classification_prior_weight="${_key##--classification-prior-weight=}"
  179. evaluate_strictness "$_key" "$_arg_classification_prior_weight"
  180. ;;
  181. --no-debug|--debug)
  182. _arg_debug="on"
  183. test "${1:0:5}" = "--no-" && _arg_debug="off"
  184. ;;
  185. -v|--verbose)
  186. _arg_verbose=$((_arg_verbose + 1))
  187. ;;
  188. -v*)
  189. _arg_verbose=$((_arg_verbose + 1))
  190. _next="${_key##-v}"
  191. if test -n "$_next" -a "$_next" != "$_key"
  192. then
  193. { 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."
  194. fi
  195. ;;
  196. -h|--help)
  197. print_help
  198. exit 0
  199. ;;
  200. -h*)
  201. print_help
  202. exit 0
  203. ;;
  204. *)
  205. _last_positional="$1"
  206. _positionals+=("$_last_positional")
  207. _positionals_count=$((_positionals_count + 1))
  208. ;;
  209. esac
  210. shift
  211. done
  212. }
  213. handle_passed_args_count()
  214. {
  215. local _required_args_string="'input' and 'output'"
  216. 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
  217. 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
  218. }
  219. assign_positional_args()
  220. {
  221. local _positional_name _shift_for=$1
  222. _positional_names="_arg_input _arg_output "
  223. shift "$_shift_for"
  224. for _positional_name in ${_positional_names}
  225. do
  226. test $# -gt 0 || break
  227. eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
  228. shift
  229. done
  230. }
  231. parse_commandline "$@"
  232. handle_passed_args_count
  233. assign_positional_args 1 "${_positionals[@]}"
  234. # OTHER STUFF GENERATED BY Argbash
  235. ### END OF CODE GENERATED BY Argbash (sortof) ### ])
  236. # [ <-- needed because of Argbash
  237. set -euoE pipefail
  238. #Special trick to redirect all output within script into logfile
  239. #https://unix.stackexchange.com/questions/145651/using-exec-and-tee-to-redirect-logs-to-stdout-and-a-log-file-in-the-same-time
  240. if [[ -n ${_arg_logfile} ]]; then
  241. exec > >(tee -ia ${_arg_logfile})
  242. exec 2> >(tee -ia ${_arg_logfile} >&2)
  243. fi
  244. #If debug, print timestamps and every command run
  245. if [[ ${_arg_debug} == "on" ]]; then
  246. set -xT
  247. set -o functrace
  248. PS4='+\t '
  249. fi
  250. #If verbose (or debug) turn on verbose outputs for commands
  251. if [[ ${_arg_verbose} -ge 1 || ${_arg_debug} == "on" ]]; then
  252. N4_VERBOSE=1
  253. fi
  254. #Create temporary directory for work
  255. tmpdir=$(mktemp -d)
  256. #Setup exit trap for cleanup, don't do if debug
  257. function finish() {
  258. if [[ ${_arg_debug} == "off" ]]; then
  259. rm -rf "${tmpdir}"
  260. fi
  261. }
  262. trap finish EXIT
  263. #Add handler for failure to show where things went wrong
  264. failure() {
  265. local lineno=$1
  266. local msg=$2
  267. echo "Failed at $lineno: $msg"
  268. }
  269. trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
  270. #Set local parallelism inherited from QBATCH
  271. export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=${THREADS_PER_COMMAND:-$(nproc)}
  272. export OMP_NUM_THREADS=${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS}
  273. ### DEFAULT PRIORS ###
  274. #BeAST configuration
  275. BEASTLIBRARY_DIR="${QUARANTINE_PATH}/resources/BEaST_libraries/combined"
  276. BEAST_CONFIG=${BEASTLIBRARY_DIR}/default.1mm.conf
  277. #mni_icbm152_nlin_sym_09c priors as default
  278. REGISTRATIONMODEL="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c.mnc"
  279. REGISTRATIONBRAINMASK="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c_mask.mnc"
  280. WMPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_wm_tal_nlin_sym_09c.mnc"
  281. GMPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_gm_tal_nlin_sym_09c.mnc"
  282. CSFPRIOR="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_csf_tal_nlin_sym_09c.mnc"
  283. #Files used to define MNI space
  284. RESAMPLEMODEL="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c.mnc"
  285. RESAMPLEMODELBRAINMASK="${QUARANTINE_PATH}/resources/mni_icbm152_nlin_sym_09c_minc2/mni_icbm152_t1_tal_nlin_sym_09c_mask.mnc"
  286. # Check config files, eventually argbash will do this
  287. if [[ -n ${_arg_config} && ${_arg_config} != "auto" ]]; then
  288. if [[ -r ${_arg_config} ]]; then
  289. source ${_arg_config}
  290. else
  291. echo "iterativeN4_multispectral.sh ERROR: config file does not exist or is not readable" && exit 2
  292. fi
  293. fi
  294. if [[ ! -d ${BEASTLIBRARY_DIR} ]]; then
  295. echo "iterativeN4_multispectral.sh ERROR: ${BEASTLIBRARY_DIR} does not exist"
  296. fi
  297. for prior in ${REGISTRATIONMODEL} ${REGISTRATIONBRAINMASK} ${WMPRIOR} ${GMPRIOR} ${CSFPRIOR} ${RESAMPLEMODEL} ${RESAMPLEMODELBRAINMASK} ${BEAST_CONFIG}; do
  298. if [[ ! -s ${prior} ]]; then
  299. echo "iterativeN4_multispectral.sh ERROR: File ${prior} does not exist or is zero size" && exit 3
  300. fi
  301. done
  302. #Setup internal variables
  303. output=${_arg_output}
  304. originput=${_arg_input}
  305. #Internal resampled input used for processing
  306. input=${tmpdir}/t1.mnc
  307. function outlier_mask() {
  308. #Generate an outlier mask which combines the vessel segmentation >4.75 * MAD of WM
  309. local outlier_input=$1
  310. local outlier_mask=$2
  311. local outlier_output=$3
  312. local median
  313. local mad
  314. median=$(mincstats -quiet -median -mask ${outlier_mask} -mask_binvalue 1 ${outlier_input})
  315. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -expression "abs(A[0]-${median})" ${outlier_input} ${tmpdir}/${n}/madmap.mnc
  316. mad=$(mincstats -quiet -median -mask ${outlier_mask} -mask_binvalue 1 ${tmpdir}/${n}/madmap.mnc)
  317. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression "(((0.6745*(A[0]-${median}))/${mad})<4.75)&&(A[1]<45)?1:0" \
  318. ${outlier_input} ${tmpdir}/vessels.mnc ${outlier_output}
  319. }
  320. function renorm() {
  321. #Renormalize image 0.1%-(GM/WM mean)-99.9% to 0-32767-65535 using a classification mask
  322. #Compute the percentiles using the GM/WM mask
  323. #Achieved via solving a linear system to get a 2nd order polynomial remapping
  324. #of the intensity values
  325. local renorm_input=$1
  326. local renorm_classification=$2
  327. local usebrainmask="${3:-}"
  328. local wmbinvalue
  329. local gmbinvalue
  330. if [[ -n ${usebrainmask} ]]; then
  331. wmbinvalue=1
  332. gmbinvalue=1
  333. else
  334. wmbinvalue=3
  335. gmbinvalue=2
  336. fi
  337. #Compute the percentiles and median values of GM and WM
  338. valuelow=$(mincstats -quiet -mask ${tmpdir}/headmask.mnc -mask_binvalue 1 -pctT 1 ${renorm_input})
  339. valuewm=$(mincstats -quiet -median -mask ${renorm_classification} -mask_binvalue ${wmbinvalue} ${renorm_input})
  340. valuegm=$(mincstats -quiet -median -mask ${renorm_classification} -mask_binvalue ${gmbinvalue} ${renorm_input})
  341. valuehigh=$(mincstats -quiet -mask ${tmpdir}/headmask.mnc -mask_binvalue 1 -pctT 99 ${renorm_input})
  342. #Solve the linear system of a quadratic polynomial mapping the input values to 0-32767-65535
  343. 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])"))
  344. #Apply the mpapping
  345. minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(A[0]^2*${mapping[2]} + A[0]*${mapping[1]} + ${mapping[0]},0,65535)" \
  346. ${renorm_input} $(dirname ${renorm_input})/$(basename ${renorm_input} .mnc).norm.mnc
  347. mv -f $(dirname ${renorm_input})/$(basename ${renorm_input} .mnc).norm.mnc ${renorm_input}
  348. }
  349. #Function used to do bias field correction
  350. function do_N4_correct() {
  351. #input fov mask weight output bias shrink classifymask
  352. local n4input=$1
  353. local n4initmask=$2
  354. local n4brainmask=$3
  355. local n4weight=$4
  356. local n4corrected=$5
  357. local n4bias=$6
  358. local n4shrink=$7
  359. local n4classifymask=$8
  360. #Estimate bias field
  361. N4BiasFieldCorrection ${N4_VERBOSE:+--verbose} -d 3 -s ${n4shrink} -w ${n4weight} -x ${n4initmask} \
  362. -b [ 200 ] -c [ 300x300x300x300,1e-5 ] --histogram-sharpening [ 0.05,0.01,200 ] \
  363. -i ${tmpdir}/${n}/t1.mnc \
  364. -o [ ${n4corrected},${tmpdir}/${n}/bias2.mnc ] -r 0
  365. ImageMath 3 ${tmpdir}/${n}/bias2.mnc / ${tmpdir}/${n}/bias2.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias2.mnc)
  366. ImageMath 3 ${n4bias} m ${tmpdir}/prebias.mnc ${tmpdir}/${n}/bias2.mnc
  367. ImageMath 3 ${n4bias} / ${n4bias} $(mincstats -quiet -mean ${n4bias})
  368. ImageMath 3 ${n4corrected} / ${n4input} ${n4bias}
  369. cp -f ${n4bias} ${tmpdir}/prebias.mnc
  370. renorm ${n4corrected} ${n4classifymask}
  371. }
  372. function iterative_precorrect() {
  373. local pctTlow
  374. local pctThigh
  375. #Foreground/background via multi-level otsu
  376. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight1.mnc Otsu 4 ${tmpdir}/nonzero.mnc
  377. ThresholdImage 3 ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/weight1.mnc 2 Inf 1 0
  378. ImageMath 3 ${tmpdir}/${n}/weight1.mnc GetLargestComponent ${tmpdir}/${n}/weight1.mnc
  379. iMath 3 ${tmpdir}/${n}/weight1.mnc MC ${tmpdir}/${n}/weight1.mnc 2 1 ball 1
  380. ImageMath 3 ${tmpdir}/${n}/weight1.mnc FillHoles ${tmpdir}/${n}/weight1.mnc 2
  381. cp -f ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/mask1.mnc
  382. ImageMath 3 ${tmpdir}/${n}/weight1.mnc m ${tmpdir}/${n}/weight1.mnc ${tmpdir}/nonzero.mnc
  383. ImageMath 3 ${tmpdir}/${n}/weight1.mnc GetLargestComponent ${tmpdir}/${n}/weight1.mnc
  384. #Exclude Hotspots
  385. minccalc -quiet ${N4_VERBOSE:+-verbose} \
  386. -expression "A[0]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)?A[1]:0" \
  387. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight1.mnc ${tmpdir}/${n}/weighttemp.mnc
  388. mv -f ${tmpdir}/${n}/weighttemp.mnc ${tmpdir}/${n}/weight1.mnc
  389. #First round of correction
  390. N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 50x50x50x50,0 ] \
  391. -w ${tmpdir}/${n}/weight1.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 4 --verbose \
  392. --histogram-sharpening [ 0.15,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
  393. ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc \
  394. $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
  395. ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/bias.mnc
  396. #Renormalize intensity
  397. pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
  398. pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask1.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
  399. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
  400. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
  401. mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
  402. #Second round Foreground/background via multi-level otsu
  403. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight2.mnc Otsu 4 ${tmpdir}/nonzero.mnc
  404. ThresholdImage 3 ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/weight2.mnc 2 Inf 1 0
  405. ImageMath 3 ${tmpdir}/${n}/weight2.mnc GetLargestComponent ${tmpdir}/${n}/weight2.mnc
  406. iMath 3 ${tmpdir}/${n}/weight2.mnc MC ${tmpdir}/${n}/weight2.mnc 3 1 ball 1
  407. ImageMath 3 ${tmpdir}/${n}/weight2.mnc FillHoles ${tmpdir}/${n}/weight2.mnc 2
  408. cp -f ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/mask2.mnc
  409. cp -f ${tmpdir}/${n}/mask2.mnc ${tmpdir}/fgmask.mnc
  410. ImageMath 3 ${tmpdir}/${n}/weight2.mnc m ${tmpdir}/${n}/weight2.mnc ${tmpdir}/nonzero.mnc
  411. ImageMath 3 ${tmpdir}/${n}/weight2.mnc GetLargestComponent ${tmpdir}/${n}/weight2.mnc
  412. pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 0.1 ${input})
  413. pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.9 ${input})
  414. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
  415. ${input} ${tmpdir}/${n}/t1.mnc
  416. cp ${tmpdir}/${n}/t1.mnc ${tmpdir}/t1.renorm.mnc
  417. input=${tmpdir}/t1.renorm.mnc
  418. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
  419. ImageMath 3 ${tmpdir}/${n}/weight2.mnc m ${tmpdir}/${n}/weight2.mnc ${tmpdir}/nonzero.mnc
  420. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
  421. -expression "A[0]<$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.5 ${tmpdir}/${n}/t1.mnc)?A[1]:0" \
  422. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight2.mnc ${tmpdir}/${n}/weighttemp.mnc
  423. mv -f ${tmpdir}/${n}/weighttemp.mnc ${tmpdir}/${n}/weight2.mnc
  424. N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 50x50x50x50,0 ] \
  425. -w ${tmpdir}/${n}/weight2.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 4 --verbose \
  426. --histogram-sharpening [ 0.15,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
  427. ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc \
  428. $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
  429. ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/bias.mnc
  430. pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
  431. pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask2.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
  432. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
  433. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
  434. mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
  435. cp -f ${tmpdir}/${n}/bias.mnc ${tmpdir}/${n}/prebias.mnc
  436. itk_vesselness --scales 8 --rescale ${tmpdir}/${n}/t1.mnc ${tmpdir}/vessels.mnc
  437. i=0
  438. #Iterative correction using tissue masking, some badly biased scans can't be
  439. #corrected in one-shot
  440. while true; do
  441. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/mask$((2 + i)).mnc
  442. ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/otsu.mnc 2 Inf 1 0
  443. ImageMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc GetLargestComponent ${tmpdir}/${n}/otsu.mnc
  444. iMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc MC ${tmpdir}/${n}/mask$((3 + i)).mnc 8 1 ball 1
  445. ImageMath 3 ${tmpdir}/${n}/mask$((3 + i)).mnc FillHoles ${tmpdir}/${n}/mask$((3 + i)).mnc 2
  446. cp -f ${tmpdir}/${n}/mask$((3 + i)).mnc ${tmpdir}/fgmask.mnc
  447. if [[ $i == 0 ]]; then
  448. ImageMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc + ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/mask$((2 + i)).mnc
  449. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
  450. -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" \
  451. ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight$((3 + i)).mnc
  452. else
  453. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
  454. -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" \
  455. ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight$((3 + i)).mnc
  456. iMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc ME ${tmpdir}/${n}/otsu.mnc 1 1 ball 1
  457. ImageMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc GetLargestComponent ${tmpdir}/${n}/weight$((3 + i)).mnc
  458. iMath 3 ${tmpdir}/${n}/weight$((3 + i)).mnc MD ${tmpdir}/${n}/weight$((3 + i)).mnc 1 1 ball 1
  459. fi
  460. N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 300x300x300x300,1e-4 ] \
  461. -w ${tmpdir}/${n}/weight$((3 + i)).mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 2 --verbose \
  462. --histogram-sharpening [ 0.05,0.01,200 ] -r 0 -x ${tmpdir}/initmask.mnc
  463. ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
  464. ImageMath 3 ${tmpdir}/${n}/prebias.mnc m ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias.mnc
  465. ImageMath 3 ${tmpdir}/${n}/prebias.mnc / ${tmpdir}/${n}/prebias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/prebias.mnc)
  466. ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/prebias.mnc
  467. ((++i))
  468. [[ ( ${i} -le 2 ) ]] || break
  469. pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
  470. pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask$((2 + i)).mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
  471. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
  472. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/t1.norm.mnc
  473. mv -f ${tmpdir}/${n}/t1.norm.mnc ${tmpdir}/${n}/t1.mnc
  474. done
  475. pctThigh=$(mincstats -quiet -mask ${tmpdir}/${n}/mask5.mnc -mask_binvalue 1 -pctT 99.9 ${tmpdir}/${n}/t1.mnc)
  476. pctTlow=$(mincstats -quiet -mask ${tmpdir}/${n}/mask5.mnc -mask_binvalue 1 -pctT 0.1 ${tmpdir}/${n}/t1.mnc)
  477. minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(clamp(A[0]-${pctTlow},0,65535)/(${pctThigh}-${pctTlow})*65535,0,65535)" \
  478. ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/corrected.mnc
  479. minc_anlm ${N4_VERBOSE:+--verbose} --clobber --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/${n}/corrected.mnc ${tmpdir}/${n}/t1.mnc
  480. }
  481. function classify_to_mask() {
  482. #Convert classify image into a mask
  483. #Mostly a clone of the supersteps of the antsBrainExtraction supersteps
  484. ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/gm.mnc 2 2 1 0
  485. ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/wm.mnc 3 3 1 0
  486. ImageMath 3 ${tmpdir}/${n}/gm.mnc GetLargestComponent ${tmpdir}/${n}/gm.mnc
  487. ImageMath 3 ${tmpdir}/${n}/wm.mnc GetLargestComponent ${tmpdir}/${n}/wm.mnc
  488. ImageMath 3 ${tmpdir}/${n}/gm.mnc FillHoles ${tmpdir}/${n}/gm.mnc 2
  489. ImageMath 3 ${tmpdir}/${n}/classifymask.mnc addtozero ${tmpdir}/${n}/gm.mnc ${tmpdir}/${n}/wm.mnc
  490. iMath 3 ${tmpdir}/${n}/classifymask.mnc ME ${tmpdir}/${n}/classifymask.mnc 1 1 ball 1
  491. ImageMath 3 ${tmpdir}/${n}/classifymask.mnc GetLargestComponent ${tmpdir}/${n}/classifymask.mnc
  492. iMath 3 ${tmpdir}/${n}/classifymask.mnc MD ${tmpdir}/${n}/classifymask.mnc 2 1 ball 1
  493. iMath 3 ${tmpdir}/bmask_E.mnc ME ${tmpdir}/masks/mnimask.mnc 10 1 ball 1
  494. ImageMath 3 ${tmpdir}/${n}/classifymask.mnc addtozero ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/bmask_E.mnc
  495. ImageMath 3 ${tmpdir}/${n}/classifymask.mnc FillHoles ${tmpdir}/${n}/classifymask.mnc 2
  496. }
  497. function make_qc() {
  498. #Generate a standardized view of the final correct brain in MNI space, with classification overlayed
  499. #Create animated version if img2webp is available
  500. mkdir -p ${tmpdir}/qc
  501. #Resample into MNI space for all the inputs
  502. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
  503. -i ${tmpdir}/${n}/classify.mnc -o ${tmpdir}/qc/classify.mnc -r ${RESAMPLEMODEL} -n GenericLabel
  504. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
  505. -i ${tmpdir}/corrected.mnc -o ${tmpdir}/qc/corrected.mnc -r ${RESAMPLEMODEL} -n BSpline[5]
  506. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/mni0_GenericAffine.xfm \
  507. -i ${tmpdir}/origqcref.mnc -o ${tmpdir}/qc/orig.mnc -r ${RESAMPLEMODEL} -n BSpline[5]
  508. mincmath -clobber -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/qc/corrected.mnc ${tmpdir}/qc/corrected.clamp.mnc
  509. mv -f ${tmpdir}/qc/corrected.clamp.mnc ${tmpdir}/qc/corrected.mnc
  510. mincmath -clobber -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/qc/orig.mnc ${tmpdir}/qc/orig.clamp.mnc
  511. mv -f ${tmpdir}/qc/orig.clamp.mnc ${tmpdir}/qc/orig.mnc
  512. #Create the bounding box for create_verify_image
  513. mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} $(mincbbox -mincresample ${tmpdir}/qc/classify.mnc) ${tmpdir}/qc/classify.mnc ${tmpdir}/qc/label-crop.mnc
  514. minccalc -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression '1' ${tmpdir}/qc/label-crop.mnc ${tmpdir}/qc/bounding.mnc
  515. #Trasverse
  516. create_verify_image -range_floor 0 ${tmpdir}/qc/trans_classify.rgb \
  517. -width 1920 -autocols 10 -autocol_planes t \
  518. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  519. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
  520. volume_overlay:${tmpdir}/qc/classify.mnc:0.4
  521. create_verify_image -range_floor 0 ${tmpdir}/qc/trans_corrected.rgb \
  522. -width 1920 -autocols 10 -autocol_planes t \
  523. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  524. -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
  525. create_verify_image -range_floor 0 ${tmpdir}/qc/trans_corrected_gray.rgb \
  526. -width 1920 -autocols 10 -autocol_planes t \
  527. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  528. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
  529. create_verify_image -range_floor 0 ${tmpdir}/qc/trans_orig.rgb \
  530. -width 1920 -autocols 10 -autocol_planes t \
  531. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  532. -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
  533. #Sagital
  534. create_verify_image -range_floor 0 ${tmpdir}/qc/sag_classify.rgb \
  535. -width 1920 -autocols 10 -autocol_planes s \
  536. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  537. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
  538. volume_overlay:${tmpdir}/qc/classify.mnc:0.4
  539. create_verify_image -range_floor 0 ${tmpdir}/qc/sag_corrected.rgb \
  540. -width 1920 -autocols 10 -autocol_planes s \
  541. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  542. -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
  543. create_verify_image -range_floor 0 ${tmpdir}/qc/sag_corrected_gray.rgb \
  544. -width 1920 -autocols 10 -autocol_planes s \
  545. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  546. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
  547. create_verify_image -range_floor 0 ${tmpdir}/qc/sag_orig.rgb \
  548. -width 1920 -autocols 10 -autocol_planes s \
  549. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  550. -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
  551. #Coronal
  552. create_verify_image -range_floor 0 ${tmpdir}/qc/cor_classify.rgb \
  553. -width 1920 -autocols 10 -autocol_planes c \
  554. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  555. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535 \
  556. volume_overlay:${tmpdir}/qc/classify.mnc:0.4
  557. create_verify_image -range_floor 0 ${tmpdir}/qc/cor_corrected.rgb \
  558. -width 1920 -autocols 10 -autocol_planes c \
  559. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  560. -row ${tmpdir}/qc/corrected.mnc color:spect:0:65535
  561. create_verify_image -range_floor 0 ${tmpdir}/qc/cor_corrected_gray.rgb \
  562. -width 1920 -autocols 10 -autocol_planes c \
  563. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  564. -row ${tmpdir}/qc/corrected.mnc color:gray:0:65535
  565. create_verify_image -range_floor 0 ${tmpdir}/qc/cor_orig.rgb \
  566. -width 1920 -autocols 10 -autocol_planes c \
  567. -bounding_volume ${tmpdir}/qc/bounding.mnc \
  568. -row ${tmpdir}/qc/orig.mnc color:spect:0:65535
  569. convert -background black -strip -append \
  570. ${tmpdir}/qc/cor_corrected.rgb \
  571. ${tmpdir}/qc/cor_classify.rgb \
  572. ${tmpdir}/qc/sag_corrected.rgb \
  573. ${tmpdir}/qc/sag_classify.rgb \
  574. ${tmpdir}/qc/trans_corrected.rgb \
  575. ${tmpdir}/qc/trans_classify.rgb \
  576. ${tmpdir}/qc/corrected.mpc
  577. convert -background black -strip -append \
  578. ${tmpdir}/qc/cor_orig.rgb \
  579. ${tmpdir}/qc/cor_corrected_gray.rgb \
  580. ${tmpdir}/qc/sag_orig.rgb \
  581. ${tmpdir}/qc/sag_corrected_gray.rgb \
  582. ${tmpdir}/qc/trans_orig.rgb \
  583. ${tmpdir}/qc/trans_corrected_gray.rgb \
  584. ${tmpdir}/qc/orig.mpc
  585. #Save static QC jpg
  586. convert -background black -strip -interlace Plane -sampling-factor 4:2:0 -quality "85%" \
  587. ${tmpdir}/qc/corrected.mpc $(dirname ${output})/$(basename ${output} .mnc).jpg
  588. #If webp software is available animate a before/after image
  589. if command -v img2webp; then
  590. convert -background black ${tmpdir}/qc/corrected.mpc ${tmpdir}/qc/corrected.png
  591. convert -background black ${tmpdir}/qc/orig.mpc ${tmpdir}/qc/orig.png
  592. img2webp -d 750 -lossy -min_size ${tmpdir}/qc/corrected.png ${tmpdir}/qc/orig.png -o $(dirname ${output})/$(basename ${output} .mnc).webp || true
  593. fi
  594. }
  595. function test_templates() {
  596. #Automatic template selection for most similar template for use as prior
  597. #Loop over the configs/auto config files and choose the best one based on ants CC
  598. mkdir -p ${tmpdir}/test_templates
  599. for configfile in $(dirname "$(readlink -f "$0")")/configs/auto/*cfg; do
  600. source ${configfile}
  601. antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
  602. --output [ ${tmpdir}/test_templates/$(basename ${configfile} .cfg),${tmpdir}/test_templates/$(basename ${configfile} .cfg).mnc ] \
  603. --use-histogram-matching 1 \
  604. --initial-moving-transform [ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1 ] \
  605. --transform Translation[ 0.1 ] \
  606. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  607. --convergence [ 500x500x500x500x500x500x500x500,1e-6,10 ] \
  608. --shrink-factors 6x6x6x6x6x6x6x6 \
  609. --smoothing-sigmas 6.35574237559x5.93006674681x5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994mm \
  610. --masks [ NOMASK,NOMASK ] \
  611. --transform Rigid[ 0.1 ] \
  612. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  613. --convergence [ 500x500x500x500x500x500x500,1e-6,10 ] \
  614. --shrink-factors 6x6x6x6x6x6x5 \
  615. --smoothing-sigmas 4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813mm \
  616. --masks [ NOMASK,NOMASK ] \
  617. --transform Similarity[ 0.1 ] \
  618. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  619. --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
  620. --shrink-factors 6x6x5x4x3x2 \
  621. --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
  622. --masks [ NOMASK,NOMASK ] \
  623. --transform Similarity[ 0.1 ] \
  624. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  625. --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
  626. --shrink-factors 6x6x5x4x3x2 \
  627. --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
  628. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
  629. --transform Affine[ 0.1 ] \
  630. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
  631. --convergence [ 500x450x150x50,1e-6,10 ] \
  632. --shrink-factors 4x3x2x1 \
  633. --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
  634. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
  635. echo ${configfile},$(MeasureImageSimilarity -d 3 -m CC[${REGISTRATIONMODEL},${tmpdir}/test_templates/$(basename ${configfile} .cfg).mnc,1,4] \
  636. -x ${REGISTRATIONBRAINMASK}) >> ${tmpdir}/test_templates/results.csv
  637. done
  638. #Prep and load winner template
  639. unset MNI_XFM
  640. echo "Choosing template $(sort -k2 -g -t, ${tmpdir}/test_templates/results.csv | cut -d"," -f 1 | head -1)"
  641. source $(sort -k2 -g -t, ${tmpdir}/test_templates/results.csv | cut -d"," -f 1 | head -1)
  642. #Store template registration for later use
  643. 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
  644. if [[ ${_arg_debug} == "off" ]]; then
  645. rm -rf ${tmpdir}/test_templates
  646. fi
  647. }
  648. ##########START OF SCRIPT#############
  649. #Forceably convert to MINC2, and clamp range to avoid negative numbers, rescale to 0-65535
  650. mincconvert -2 ${originput} ${tmpdir}/originput.mnc
  651. #Rescale initial data into entirely positive range (fix for completely negative data)
  652. ImageMath 3 ${tmpdir}/originput.mnc RescaleImage ${tmpdir}/originput.mnc 0 65535
  653. #Very mild range clamp for very hot voxels
  654. mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp \
  655. -const2 $(mincstats -quiet -floor 1e-12 -pctT 0.1 ${tmpdir}/originput.mnc) \
  656. $(mincstats -quiet -floor 1e-12 -pctT 99.9 ${tmpdir}/originput.mnc) \
  657. ${tmpdir}/originput.mnc ${tmpdir}/originput.clamp.mnc
  658. ImageMath 3 ${tmpdir}/originput.clamp.mnc RescaleImage ${tmpdir}/originput.clamp.mnc 0 65535
  659. mincresample -quiet ${N4_VERBOSE:+-verbose} -like ${tmpdir}/originput.mnc -keep -unsigned -short \
  660. ${tmpdir}/originput.clamp.mnc ${tmpdir}/originput.clamp.resample.mnc
  661. mv -f ${tmpdir}/originput.clamp.resample.mnc ${tmpdir}/originput.mnc
  662. rm -f ${tmpdir}/originput.clamp.mnc
  663. originput=${tmpdir}/originput.mnc
  664. cp -f ${originput} ${tmpdir}/origqcref.mnc
  665. #Isotropize, and normalize intensity range, this is the file that will be processed in the pipeline
  666. #Need smoothing for downsampling to avoid aliasing
  667. #Ideas stolen from https://discourse.itk.org/t/resampling-to-isotropic-signal-processing-theory/1403
  668. isostep=1.0
  669. inputres=$(python -c "print('\n'.join([str(abs(x)) for x in [float(x) for x in \"$(PrintHeader ${originput} 1)\".split(\"x\")]]))")
  670. blurs=""
  671. for dim in ${inputres}; do
  672. if [[ $(python -c "print(${dim}>(${isostep}-1e-6))") == True ]]; then
  673. blurs+=1e-12x
  674. else
  675. 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
  676. fi
  677. done
  678. SmoothImage 3 ${originput} "${blurs%?}" ${tmpdir}/smoothed.mnc 1 0
  679. ResampleImage 3 ${tmpdir}/smoothed.mnc ${input} ${isostep}x${isostep}x${isostep} 0 4
  680. mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 $(mincstats -max -quiet ${input}) ${input} ${tmpdir}/input.clamp.mnc
  681. ImageMath 3 ${input} RescaleImage ${tmpdir}/input.clamp.mnc 0 65535
  682. rm -f ${tmpdir}/input.clamp.mnc
  683. ImageMath 3 ${input} PadImage ${input} 20
  684. #Generate a global nonzero mask to always exclude pure background
  685. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
  686. #If exclusion mask exists, negate it to produce a multiplicative exlcusion mask, resample to internal resolution
  687. if [[ -n ${_arg_exclude} ]]; then
  688. ImageMath 3 ${tmpdir}/exclude.mnc Neg ${_arg_exclude}
  689. excludemask=${tmpdir}/exclude.mnc
  690. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${excludemask} -r ${input} -n GenericLabel -o ${excludemask}
  691. else
  692. excludemask=""
  693. fi
  694. mkdir -p ${tmpdir}/masks
  695. ################################################################################
  696. #Round 0
  697. #Iterative estimation of a mask with multilevel otsu to find foreground-background
  698. #Also found forground mask and use it combined to template FOV registration to
  699. #trim the FOV to generate a headmask
  700. ################################################################################
  701. n=0
  702. mkdir -p ${tmpdir}/${n}
  703. minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${input} ${tmpdir}/${n}/t1.mnc
  704. iterative_precorrect
  705. #If the "auto" template method is selected, do the registrations and CC
  706. #estimate to find best matching model
  707. if [[ ${_arg_config} == "auto" ]]; then
  708. test_templates
  709. fi
  710. #Register to model to resample back a FOV mask
  711. if [[ -s ${tmpdir}/template_bootstrap.xfm ]]; then
  712. cp -f ${tmpdir}/template_bootstrap.xfm ${tmpdir}/${n}/mni0_GenericAffine.xfm
  713. else
  714. antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
  715. --output [ ${tmpdir}/${n}/mni ] \
  716. --use-histogram-matching 1 \
  717. --initial-moving-transform [ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1 ] \
  718. --transform Translation[ 0.1 ] \
  719. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  720. --convergence [ 500x500x500x500x500x500x500x500,1e-6,10 ] \
  721. --shrink-factors 6x6x6x6x6x6x6x6 \
  722. --smoothing-sigmas 6.35574237559x5.93006674681x5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994mm \
  723. --masks [ NOMASK,NOMASK ] \
  724. --transform Rigid[ 0.1 ] \
  725. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  726. --convergence [ 500x500x500x500x500x500x500,1e-6,10 ] \
  727. --shrink-factors 6x6x6x6x6x6x5 \
  728. --smoothing-sigmas 4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813mm \
  729. --masks [ NOMASK,NOMASK ] \
  730. --transform Similarity[ 0.1 ] \
  731. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  732. --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
  733. --shrink-factors 6x6x5x4x3x2 \
  734. --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
  735. --masks [ NOMASK,NOMASK ] \
  736. --transform Similarity[ 0.1 ] \
  737. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  738. --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
  739. --shrink-factors 6x6x5x4x3x2 \
  740. --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
  741. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
  742. --transform Affine[ 0.1 ] \
  743. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
  744. --convergence [ 500x450x150x0,1e-6,10 ] \
  745. --shrink-factors 4x3x2x1 \
  746. --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
  747. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
  748. fi
  749. #Make a fov mask from all 1's of the
  750. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression '1' ${REGISTRATIONMODEL} ${tmpdir}/modelfovmask.mnc
  751. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/modelfovmask.mnc \
  752. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -o ${tmpdir}/headmask.mnc -r ${tmpdir}/${n}/t1.mnc -n GenericLabel
  753. #Headmask is intersection of filled otsu foreground mask and FOV from model
  754. ImageMath 3 ${tmpdir}/headmask.mnc m ${tmpdir}/headmask.mnc ${tmpdir}/fgmask.mnc
  755. cp -f ${tmpdir}/fgmask.mnc ${tmpdir}/fgmask_orig.mnc
  756. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} \
  757. -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" \
  758. ${tmpdir}/vessels.mnc ${tmpdir}/${n}/t1.mnc ${tmpdir}/headmask.mnc ${tmpdir}/${n}/otsu.mnc
  759. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/otsu.mnc
  760. ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight6.mnc 2 Inf 1 0
  761. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/otsu.mnc Otsu 4 ${tmpdir}/${n}/weight6.mnc
  762. ThresholdImage 3 ${tmpdir}/${n}/otsu.mnc ${tmpdir}/${n}/weight6.mnc 2 Inf 1 0
  763. iMath 3 ${tmpdir}/${n}/weight6.mnc ME ${tmpdir}/${n}/weight6.mnc 1 1 ball 1
  764. ImageMath 3 ${tmpdir}/${n}/weight6.mnc GetLargestComponent ${tmpdir}/${n}/weight6.mnc
  765. iMath 3 ${tmpdir}/${n}/weight6.mnc MD ${tmpdir}/${n}/weight6.mnc 1 1 ball 1
  766. #Use exclude mask if provided
  767. if [[ -n ${excludemask} ]]; then
  768. ImageMath 3 ${tmpdir}/${n}/weight6.mnc m ${tmpdir}/${n}/weight6.mnc ${excludemask}
  769. fi
  770. N4BiasFieldCorrection -d 3 -i ${tmpdir}/${n}/t1.mnc -b [ 200 ] -c [ 300x300x300x300,1e-4 ] \
  771. -w ${tmpdir}/${n}/weight6.mnc -o [ ${tmpdir}/${n}/t1.mnc,${tmpdir}/${n}/bias.mnc ] -s 2 --verbose \
  772. --histogram-sharpening [ 0.05,0.01,200 ] -r 0
  773. ImageMath 3 ${tmpdir}/${n}/bias.mnc / ${tmpdir}/${n}/bias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/bias.mnc)
  774. ImageMath 3 ${tmpdir}/${n}/prebias.mnc m ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias.mnc
  775. ImageMath 3 ${tmpdir}/${n}/prebias.mnc / ${tmpdir}/${n}/prebias.mnc $(mincstats -quiet -mean ${tmpdir}/${n}/prebias.mnc)
  776. ImageMath 3 ${tmpdir}/${n}/t1.mnc / ${input} ${tmpdir}/${n}/prebias.mnc
  777. renorm ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/weight6.mnc usemask
  778. cp ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/corrected.mnc
  779. #Resample headmask into subject space, zero background and recrop
  780. ImageMath 3 ${input} PadImage ${input} 50
  781. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/headmask.mnc -r ${input} -n GenericLabel
  782. ImageMath 3 ${input} m ${input} ${tmpdir}/headmask.mnc
  783. ExtractRegionFromImageByMask 3 ${input} ${tmpdir}/input.crop.mnc ${tmpdir}/headmask.mnc 1 10
  784. mv -f ${tmpdir}/input.crop.mnc ${input}
  785. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/headmask.mnc -r ${input} -n GenericLabel
  786. minccalc -clobber -quiet ${N4_VERBOSE:+-verbose} -unsigned -byte -expression 'A[0]>1.01?1:0' ${input} ${tmpdir}/nonzero.mnc
  787. #Backup the original bias field estimate, in case cropping is not done, so we can correct the neck tissues
  788. cp -f ${tmpdir}/${n}/prebias.mnc ${tmpdir}/bias_orig.mnc
  789. #Need to fill the bias field with 1's in case we're padding the image
  790. mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} -fill -fillvalue 1 -like ${input} ${tmpdir}/${n}/prebias.mnc ${tmpdir}/${n}/bias_resample.mnc
  791. cp -f ${tmpdir}/${n}/prebias.mnc ${tmpdir}/prebias.mnc
  792. mincresample -clobber -quiet ${N4_VERBOSE:+-verbose} -fill -fillvalue 1 -like ${input} ${tmpdir}/prebias.mnc ${tmpdir}/prebias_resample.mnc
  793. mv -f ${tmpdir}/${n}/bias_resample.mnc ${tmpdir}/${n}/bias.mnc
  794. mv -f ${tmpdir}/prebias_resample.mnc ${tmpdir}/prebias.mnc
  795. #Resample the exlude mask into the new recropped space
  796. if [[ -n ${_arg_exclude} ]]; then
  797. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${excludemask} -r ${input} -n GenericLabel -o ${excludemask}
  798. fi
  799. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/corrected.mnc -o ${tmpdir}/${n}/corrected.mnc -r ${input}
  800. ImageMath 3 ${tmpdir}/${n}/corrected.mnc m ${tmpdir}/${n}/corrected.mnc ${tmpdir}/headmask.mnc
  801. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression '1' ${input} ${tmpdir}/initmask.mnc
  802. ################################################################################
  803. #Round 1, N4 with estimate weight mask using affine registered GM/WM/CSF priors
  804. ################################################################################
  805. ((++n))
  806. mkdir -p ${tmpdir}/${n}
  807. minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
  808. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
  809. ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
  810. itk_vesselness --clobber --scales 8 --rescale ${tmpdir}/${n}/t1.mnc ${tmpdir}/vessels.mnc
  811. antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
  812. --output [ ${tmpdir}/${n}/mni ] \
  813. --use-histogram-matching 1 \
  814. --initial-moving-transform ${tmpdir}/$((n - 1))/mni0_GenericAffine.xfm \
  815. --transform Similarity[ 0.1 ] \
  816. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,32,None ] \
  817. --convergence [ 500x500x500x500x450x150,1e-6,10 ] \
  818. --shrink-factors 6x6x5x4x3x2 \
  819. --smoothing-sigmas 2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037mm \
  820. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ] \
  821. --transform Affine[ 0.1 ] \
  822. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
  823. --convergence [ 500x450x150x50,1e-6,10 ] \
  824. --shrink-factors 4x3x2x1 \
  825. --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
  826. --masks [ ${REGISTRATIONBRAINMASK},NOMASK ]
  827. unset reg_initalization
  828. #Make MNI-space copy of brain for BeAST
  829. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/t1.mnc \
  830. ${MNI_XFM:+-t ${MNI_XFM}} -t ${tmpdir}/${n}/mni0_GenericAffine.xfm -n BSpline[ 5 ] -o ${tmpdir}/${n}/mni.mnc -r ${RESAMPLEMODEL}
  831. #BSpline[ 5 ] does weird things to intensity, clip back to positive range
  832. mincmath -quiet ${N4_VERBOSE:+-verbose} -clamp -const2 0 65535 ${tmpdir}/${n}/mni.mnc ${tmpdir}/${n}/mni.clamp.mnc
  833. mv -f ${tmpdir}/${n}/mni.clamp.mnc ${tmpdir}/${n}/mni.mnc
  834. #Shrink the MNI mask for the first intensity matching
  835. iMath 3 ${tmpdir}/${n}/shrinkmask.mnc ME ${RESAMPLEMODELBRAINMASK} 2 1 ball 1
  836. #Intensity normalize
  837. volume_pol ${N4_VERBOSE:+--verbose} --order 1 --min 0 --max 100 --noclamp \
  838. --source_mask ${tmpdir}/${n}/shrinkmask.mnc --target_mask ${RESAMPLEMODELBRAINMASK} \
  839. ${tmpdir}/${n}/mni.mnc ${RESAMPLEMODEL} ${tmpdir}/${n}/mni.norm.mnc
  840. #Run a quick beast to get a brain mask
  841. mincbeast ${N4_VERBOSE:+-verbose} -sparse -v2 -double -fill -median -same_res -flip -conf ${BEAST_CONFIG} \
  842. ${BEASTLIBRARY_DIR} ${tmpdir}/${n}/mni.norm.mnc ${tmpdir}/${n}/beastmask.mnc
  843. #Resample beast mask and MNI mask to native space
  844. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${tmpdir}/${n}/t1.mnc \
  845. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] ${MNI_XFM:+-t [${MNI_XFM},1]} \
  846. -i ${tmpdir}/${n}/beastmask.mnc -o ${tmpdir}/${n}/bmask.mnc -n GenericLabel
  847. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${tmpdir}/${n}/t1.mnc \
  848. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -i ${REGISTRATIONBRAINMASK} \
  849. -o ${tmpdir}/${n}/mnimask.mnc -n GenericLabel
  850. #BeAST Failure mode of a chunk of almost unattached voxels, try to remove
  851. iMath 3 ${tmpdir}/${n}/bmask.mnc ME ${tmpdir}/${n}/bmask.mnc 1 1 ball 1
  852. ImageMath 3 ${tmpdir}/${n}/bmask.mnc GetLargestComponent ${tmpdir}/${n}/bmask.mnc
  853. iMath 3 ${tmpdir}/${n}/bmask.mnc MD ${tmpdir}/${n}/bmask.mnc 1 1 ball 1
  854. cp -f ${tmpdir}/${n}/bmask.mnc ${tmpdir}/masks/bmask.mnc
  855. cp -f ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/masks/affinemask.mnc
  856. ImageMath 3 ${tmpdir}/${n}/mask.mnc addtozero ${tmpdir}/masks/bmask.mnc ${tmpdir}/masks/affinemask.mnc
  857. ImageMath 3 ${tmpdir}/${n}/mask.mnc GetLargestComponent ${tmpdir}/${n}/mask.mnc
  858. iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 2 1 ball 1
  859. #Resample MNI Priors to Native space for classification
  860. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${WMPRIOR} \
  861. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior3.mnc -n Linear
  862. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${GMPRIOR} \
  863. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior2.mnc -n Linear
  864. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${CSFPRIOR} \
  865. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior1.mnc -n Linear
  866. if [[ -n ${excludemask} ]]; then
  867. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
  868. fi
  869. #Estimate outlier to exclude from classification
  870. outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/bmask.mnc ${tmpdir}/${n}/hotmask.mnc
  871. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
  872. #Classify brain
  873. 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 \
  874. -i PriorProbabilityImages[ 3,${tmpdir}/${n}/SegmentationPrior%d.mnc,0.1 ] -k Gaussian -m [ 0.1,1x1x1 ] \
  875. -o ${tmpdir}/${n}/classify.mnc -r 1 -p Socrates[ 0 ] --winsorize-outliers BoxPlot
  876. #Convert classification to a brain mask and brain tissue mask
  877. ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/2.mnc 2 2 1 0
  878. ThresholdImage 3 ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/3.mnc 3 3 1 0
  879. ImageMath 3 ${tmpdir}/${n}/2.mnc GetLargestComponent ${tmpdir}/${n}/2.mnc
  880. ImageMath 3 ${tmpdir}/${n}/3.mnc GetLargestComponent ${tmpdir}/${n}/3.mnc
  881. ImageMath 3 ${tmpdir}/${n}/weight.mnc addtozero ${tmpdir}/${n}/2.mnc ${tmpdir}/${n}/3.mnc
  882. iMath 3 ${tmpdir}/${n}/weight.mnc ME ${tmpdir}/${n}/weight.mnc 1 1 ball 1
  883. ImageMath 3 ${tmpdir}/${n}/weight.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
  884. iMath 3 ${tmpdir}/${n}/weight.mnc MD ${tmpdir}/${n}/weight.mnc 2 1 ball 1
  885. iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/weight.mnc 5 1 ball 1
  886. ImageMath 3 ${tmpdir}/${n}/mask2.mnc FillHoles ${tmpdir}/${n}/mask2.mnc 2
  887. cp -f ${tmpdir}/${n}/mask2.mnc ${tmpdir}/masks/classifymask${n}.mnc
  888. #User provided exclusion mask
  889. if [[ -n ${excludemask} ]]; then
  890. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
  891. fi
  892. #Remove outliers round 2
  893. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
  894. ImageMath 3 ${tmpdir}/${n}/weight.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
  895. ImageMath 3 ${tmpdir}/${n}/classify.mnc m ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/mask2.mnc
  896. #Always exclude 0 from correction
  897. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
  898. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
  899. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
  900. 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
  901. #Calculate coeffcient of variation between this round bias field and prior round
  902. 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
  903. 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
  904. if [[ ${_arg_debug} == "off" ]]; then
  905. rm -rf ${tmpdir}/$((n - 1))
  906. fi
  907. ################################################################################
  908. #Round 2, N4 with classification from nonlinear registered priors
  909. ################################################################################
  910. ((++n))
  911. mkdir -p ${tmpdir}/${n}
  912. minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
  913. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
  914. ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
  915. #Affine register to MNI space, tweak registration
  916. antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
  917. --output [ ${tmpdir}/${n}/mni ] \
  918. --use-histogram-matching 1 \
  919. --initial-moving-transform ${tmpdir}/$((n - 1))/mni0_GenericAffine.xfm \
  920. --transform Affine[ 0.05 ] \
  921. --metric Mattes[ ${REGISTRATIONMODEL},${tmpdir}/${n}/t1.mnc,1,64,None ] \
  922. --convergence [ 500x450x150x50,1e-6,10 ] \
  923. --shrink-factors 4x3x2x1 \
  924. --smoothing-sigmas 1.64470459404x1.20112240879x0.735534255037x0.0mm \
  925. --masks [ ${REGISTRATIONBRAINMASK},${tmpdir}/$((n - 1))/mask2.mnc ]
  926. cp -f ${tmpdir}/$((n - 1))/mask2.mnc ${tmpdir}/${n}/mask.mnc
  927. iMath 3 ${tmpdir}/${n}/extractmask.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
  928. ImageMath 3 ${tmpdir}/${n}/t1.extracted.mnc m ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/extractmask.mnc
  929. ImageMath 3 ${tmpdir}/extractmodel.mnc m ${REGISTRATIONMODEL} ${REGISTRATIONBRAINMASK}
  930. #Non linearly register priors
  931. #We use the extracted images because subjects with different distance between
  932. #brain and skull consistently fail
  933. antsRegistration ${N4_VERBOSE:+--verbose} -d 3 --float 1 --minc \
  934. --output [ ${tmpdir}/${n}/nonlin ] \
  935. --initial-moving-transform ${tmpdir}/${n}/mni0_GenericAffine.xfm \
  936. --use-histogram-matching 1 \
  937. --transform SyN[ 0.1,3,0 ] \
  938. --metric CC[ ${tmpdir}/extractmodel.mnc,${tmpdir}/${n}/t1.extracted.mnc,1,4 ] \
  939. --convergence [ 500x500x500x500x500x500x500x500x500x500x0x0x0,1e-6,10 ] \
  940. --shrink-factors 5x5x5x5x5x5x5x5x5x4x3x2x1 \
  941. --smoothing-sigmas 5.50423435717x5.07820577132x4.65192708599x4.22532260674x3.79828256043x3.37064139994x2.94213702015x2.51232776601x2.08040503813x1.64470459404x1.20112240879x0.735534255037x0.0mm \
  942. --masks [ NOMASK,NOMASK ] \
  943. --transform SyN[ 0.1,3,0 ] \
  944. --metric CC[ ${tmpdir}/extractmodel.mnc,${tmpdir}/${n}/t1.extracted.mnc,1,2 ] \
  945. --convergence [ 500x500x225x225x0,1e-6,10 ] \
  946. --shrink-factors 5x4x3x2x1 \
  947. --smoothing-sigmas 2.08040503813x1.64470459404x1.20112240879x0.735534255037x0.0mm \
  948. --masks [ ${REGISTRATIONBRAINMASK},${tmpdir}/${n}/extractmask.mnc ]
  949. #Save MNI space registration for QC later
  950. cp -f ${tmpdir}/${n}/mni0_GenericAffine.xfm ${tmpdir}/mni0_GenericAffine.xfm
  951. #Resample MNI Priors to Native space for classification
  952. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${WMPRIOR} \
  953. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
  954. -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior3.mnc -n Linear
  955. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${GMPRIOR} \
  956. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
  957. -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior2.mnc -n Linear
  958. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${CSFPRIOR} \
  959. -t [ ${tmpdir}/${n}/mni0_GenericAffine.xfm,1 ] -t ${tmpdir}/${n}/nonlin1_inverse_NL.xfm \
  960. -r ${tmpdir}/${n}/t1.mnc -o ${tmpdir}/${n}/SegmentationPrior1.mnc -n Linear
  961. #Resample back to subject space
  962. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${REGISTRATIONBRAINMASK} \
  963. -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
  964. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression '(A[0]>=0.25||A[1]>=0.25)?1:0' \
  965. ${tmpdir}/${n}/SegmentationPrior3.mnc ${tmpdir}/${n}/SegmentationPrior2.mnc ${tmpdir}/${n}/mniprobmask.mnc
  966. #Make MNI mask a combination of both the MNI mask and the tissue probabilty mask
  967. ImageMath 3 ${tmpdir}/${n}/mnimask.mnc addtozero ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/${n}/mniprobmask.mnc
  968. #Last time we generate MNI mask, save it outside iterations
  969. cp -f ${tmpdir}/${n}/mnimask.mnc ${tmpdir}/masks/mnimask.mnc
  970. #Vote a consensus mask from prior masking estimates
  971. ImageMath 3 ${tmpdir}/${n}/mask.mnc MajorityVoting ${tmpdir}/masks/*mnc
  972. iMath 3 ${tmpdir}/${n}/mask.mnc MC ${tmpdir}/${n}/mask.mnc 1 1 ball 1
  973. #Expand the mask a bit
  974. iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
  975. if [[ -n ${excludemask} ]]; then
  976. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
  977. fi
  978. #Find outliers to exclude from classification
  979. ThresholdImage 3 ${tmpdir}/$((n - 1))/classify.mnc ${tmpdir}/${n}/outlier_wm.mnc 3 3 1 0
  980. ImageMath 3 ${tmpdir}/${n}/outlier_wm.mnc GetLargestComponent ${tmpdir}/${n}/outlier_wm.mnc
  981. outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/outlier_wm.mnc ${tmpdir}/${n}/hotmask.mnc
  982. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
  983. #Do an initial classification using the MNI priors
  984. 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 \
  985. -i PriorProbabilityImages[ 3,${tmpdir}/${n}/SegmentationPrior%d.mnc,${_arg_classification_prior_weight} ] -k Gaussian -m [ 0.1,1x1x1 ] \
  986. -o [ ${tmpdir}/${n}/classify.mnc,${tmpdir}/${n}/SegmentationPosteriors%d.mnc ] -r 1 -p Aristotle[ 0 ] --winsorize-outliers BoxPlot \
  987. -l [ 0.69314718055994530942,1 ]
  988. #Convert classification to the mask
  989. classify_to_mask
  990. cp ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/masks/classifymask${n}.mnc
  991. ImageMath 3 ${tmpdir}/${n}/mask2.mnc MajorityVoting ${tmpdir}/masks/*mnc
  992. iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/mask2.mnc 1 1 ball 1
  993. #Combine GM and WM proabability images into a N4 mask,
  994. ImageMath 3 ${tmpdir}/${n}/weight.mnc PureTissueN4WeightMask ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc
  995. ImageMath 3 ${tmpdir}/${n}/weight.mnc RescaleImage ${tmpdir}/${n}/weight.mnc 0 1
  996. ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
  997. iMath 3 ${tmpdir}/${n}/weightmask.mnc ME ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
  998. ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weightmask.mnc
  999. iMath 3 ${tmpdir}/${n}/weightmask.mnc MD ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
  1000. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/weightmask.mnc
  1001. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
  1002. #Clip the classification weight and posteriors
  1003. for item in ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/SegmentationPosteriors1.mnc ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc; do
  1004. ImageMath 3 ${item} m ${item} ${tmpdir}/${n}/mask2.mnc
  1005. done
  1006. if [[ -n ${excludemask} ]]; then
  1007. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
  1008. fi
  1009. #Always exclude 0 from correction
  1010. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
  1011. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
  1012. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
  1013. 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
  1014. 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
  1015. 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
  1016. if [[ ${_arg_debug} == "off" ]]; then
  1017. rm -rf ${tmpdir}/$((n - 1))
  1018. fi
  1019. ################################################################################
  1020. #Remaining rounds, N4 with segmentation posteriors bootstrapped from prior run until convergence
  1021. ################################################################################
  1022. while true; do
  1023. ((++n))
  1024. mkdir -p ${tmpdir}/${n}
  1025. minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/$((n - 1))/corrected.mnc ${tmpdir}/${n}/t1.mnc
  1026. ThresholdImage 3 ${tmpdir}/${n}/t1.mnc ${tmpdir}/masks/tissuemask.mnc Otsu 4 ${tmpdir}/headmask.mnc
  1027. ThresholdImage 3 ${tmpdir}/masks/tissuemask.mnc ${tmpdir}/masks/tissuemask.mnc 2 Inf 1 0
  1028. cp -f ${tmpdir}/$((n - 1))/mask2.mnc ${tmpdir}/${n}/mask.mnc
  1029. iMath 3 ${tmpdir}/${n}/mask_D.mnc MD ${tmpdir}/${n}/mask.mnc 1 1 ball 1
  1030. if [[ -n ${excludemask} ]]; then
  1031. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${excludemask}
  1032. fi
  1033. ThresholdImage 3 ${tmpdir}/$((n - 1))/classify.mnc ${tmpdir}/${n}/outlier_wm.mnc 3 3 1 0
  1034. ImageMath 3 ${tmpdir}/${n}/outlier_wm.mnc GetLargestComponent ${tmpdir}/${n}/outlier_wm.mnc
  1035. outlier_mask ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/outlier_wm.mnc ${tmpdir}/${n}/hotmask.mnc
  1036. ImageMath 3 ${tmpdir}/${n}/mask_D.mnc m ${tmpdir}/${n}/mask_D.mnc ${tmpdir}/${n}/hotmask.mnc
  1037. #Do a classification using the last round posteriors, remove outliers
  1038. 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 \
  1039. -i PriorProbabilityImages[ 3,${tmpdir}/$((n - 1))/SegmentationPosteriors%d.mnc,0.5 ] -k Gaussian -m [ 0.1,1x1x1 ] \
  1040. -o [ ${tmpdir}/${n}/classify.mnc,${tmpdir}/${n}/SegmentationPosteriors%d.mnc ] -r 1 -p Aristotle[ 1 ] --winsorize-outliers BoxPlot \
  1041. -l [ 0.69314718055994530942,1 ]
  1042. classify_to_mask
  1043. cp -f ${tmpdir}/${n}/classifymask.mnc ${tmpdir}/masks/classifymask${n}.mnc
  1044. ImageMath 3 ${tmpdir}/${n}/mask2.mnc MajorityVoting ${tmpdir}/masks/*mnc
  1045. iMath 3 ${tmpdir}/${n}/mask2.mnc MC ${tmpdir}/${n}/mask2.mnc 1 1 ball 1
  1046. #Combine GM and WM probably images into a N4 mask,
  1047. ImageMath 3 ${tmpdir}/${n}/weight.mnc PureTissueN4WeightMask ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc
  1048. ImageMath 3 ${tmpdir}/${n}/weight.mnc RescaleImage ${tmpdir}/${n}/weight.mnc 0 1
  1049. ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weight.mnc
  1050. iMath 3 ${tmpdir}/${n}/weightmask.mnc ME ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
  1051. ImageMath 3 ${tmpdir}/${n}/weightmask.mnc GetLargestComponent ${tmpdir}/${n}/weightmask.mnc
  1052. iMath 3 ${tmpdir}/${n}/weightmask.mnc MD ${tmpdir}/${n}/weightmask.mnc 1 1 ball 1
  1053. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/weightmask.mnc
  1054. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/hotmask.mnc
  1055. #Clip the classification weight and posteriors
  1056. for item in ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/classify.mnc ${tmpdir}/${n}/SegmentationPosteriors1.mnc ${tmpdir}/${n}/SegmentationPosteriors2.mnc ${tmpdir}/${n}/SegmentationPosteriors3.mnc; do
  1057. ImageMath 3 ${item} m ${item} ${tmpdir}/${n}/mask2.mnc
  1058. done
  1059. if [[ -n ${excludemask} ]]; then
  1060. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${excludemask}
  1061. fi
  1062. #Always exclude 0 from correction
  1063. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -unsigned -byte -expression 'A[0]>1.01?1:0' ${tmpdir}/${n}/t1.mnc ${tmpdir}/${n}/nonzero.mnc
  1064. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/${n}/nonzero.mnc
  1065. ImageMath 3 ${tmpdir}/${n}/weight.mnc m ${tmpdir}/${n}/weight.mnc ${tmpdir}/nonzero.mnc
  1066. 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
  1067. #Compute coeffcient of variation
  1068. 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
  1069. 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
  1070. if [[ ${_arg_debug} == "off" ]]; then
  1071. rm -rf ${tmpdir}/$((n - 1))
  1072. fi
  1073. # Break if greater than max iterations or less than convergence threshold
  1074. [[ (${n} -lt ${_arg_max_iterations}) && ($(python -c "print($(tail -1 ${tmpdir}/convergence.txt) > ${_arg_convergence_threshold})") == "True") ]] || break
  1075. done
  1076. echo "--------------------"
  1077. echo "Convergence results:"
  1078. cat ${tmpdir}/convergence.txt
  1079. echo "--------------------"
  1080. #If cropping is enabled, recrop the originput file and resample the mask again
  1081. if [[ ${_arg_autocrop} == "on" ]]; then
  1082. ImageMath 3 ${originput} PadImage ${originput} 50
  1083. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/headmask.mnc -o ${tmpdir}/finalheadmask.mnc -r ${originput} -n GenericLabel
  1084. ImageMath 3 ${originput} m ${originput} ${tmpdir}/finalheadmask.mnc
  1085. ExtractRegionFromImageByMask 3 ${originput} ${tmpdir}/originput.crop.mnc ${tmpdir}/finalheadmask.mnc 1 10
  1086. mv -f ${tmpdir}/originput.crop.mnc ${originput}
  1087. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} -i ${tmpdir}/headmask.mnc \
  1088. -o ${tmpdir}/headmask.mnc -n GenericLabel
  1089. cp -f ${tmpdir}/headmask.mnc ${tmpdir}/fgmask.mnc
  1090. else
  1091. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} -i ${tmpdir}/fgmask_orig.mnc \
  1092. -o ${tmpdir}/fgmask.mnc -n GenericLabel
  1093. ImageMath 3 ${originput} m ${originput} ${tmpdir}/fgmask.mnc
  1094. fi
  1095. #Resample final results into original space and correct original input file
  1096. n4input=${originput}
  1097. n4corrected=${tmpdir}/corrected.mnc
  1098. n4classifymask=${tmpdir}/finalclassify.mnc
  1099. #Reconstruct a final bias field
  1100. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} \
  1101. -n BSpline[5] -i ${tmpdir}/${n}/bias.mnc -o ${tmpdir}/finalbias.mnc
  1102. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -r ${originput} \
  1103. -n BSpline[5] -i ${tmpdir}/bias_orig.mnc -o ${tmpdir}/bias_orig.mnc
  1104. ImageMath 3 ${tmpdir}/finalbias.mnc addtozero ${tmpdir}/finalbias.mnc ${tmpdir}/bias_orig.mnc
  1105. ImageMath 3 ${tmpdir}/finalbias.mnc addtozero ${tmpdir}/finalbias.mnc 1
  1106. ImageMath 3 ${n4corrected} / ${originput} ${tmpdir}/finalbias.mnc
  1107. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/mask2.mnc -o ${tmpdir}/finalmask.mnc -r ${n4corrected} -n GenericLabel
  1108. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/masks/bmask.mnc -o ${tmpdir}/finalbmask.mnc -r ${n4corrected} -n GenericLabel
  1109. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/classifymask.mnc -o ${tmpdir}/finalclassifymask.mnc -r ${n4corrected} -n GenericLabel
  1110. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/masks/mnimask.mnc -o ${tmpdir}/finalmnimask.mnc -r ${n4corrected} -n GenericLabel
  1111. antsApplyTransforms ${N4_VERBOSE:+--verbose} -d 3 -i ${tmpdir}/${n}/classify.mnc -o ${tmpdir}/finalclassify.mnc -r ${n4corrected} -n GenericLabel
  1112. valuelow=$(mincstats -quiet -mask ${tmpdir}/fgmask.mnc -mask_binvalue 1 -pctT 0.1 ${n4corrected})
  1113. valuewm=$(mincstats -quiet -median -mask ${n4classifymask} -mask_binvalue 3 ${n4corrected})
  1114. valuegm=$(mincstats -quiet -median -mask ${n4classifymask} -mask_binvalue 2 ${n4corrected})
  1115. valuehigh=$(mincstats -quiet -mask ${tmpdir}/fgmask.mnc -mask_binvalue 1 -pctT 99.9 ${n4corrected})
  1116. 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])"))
  1117. minccalc -quiet ${N4_VERBOSE:+-verbose} -short -unsigned -expression "clamp(A[0]^2*${mapping[2]} + A[0]*${mapping[1]} + ${mapping[0]},0,65535)" \
  1118. ${n4corrected} $(dirname ${n4corrected})/$(basename ${n4corrected} .mnc).norm.mnc
  1119. mv -f $(dirname ${n4corrected})/$(basename ${n4corrected} .mnc).norm.mnc ${n4corrected}
  1120. cp -f ${tmpdir}/corrected.mnc ${output}
  1121. #Output final classification files if standalone
  1122. if [[ ${_arg_standalone} == "on" || ${_arg_debug} == "on" ]]; then
  1123. make_qc
  1124. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalbmask.mnc $(dirname ${output})/$(basename ${output} .mnc).beastmask.mnc
  1125. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalmnimask.mnc $(dirname ${output})/$(basename ${output} .mnc).mnimask.mnc
  1126. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalclassify.mnc $(dirname $output)/$(basename ${output} .mnc).classify.mnc
  1127. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalmask.mnc $(dirname $output)/$(basename ${output} .mnc).mask.mnc
  1128. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -byte -unsigned ${tmpdir}/finalclassifymask.mnc $(dirname $output)/$(basename ${output} .mnc).classifymask.mnc
  1129. minccalc -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned -expression 'A[0]*A[1]' ${output} ${tmpdir}/finalmask.mnc ${tmpdir}/output.extracted.mnc
  1130. ExtractRegionFromImageByMask 3 ${tmpdir}/output.extracted.mnc ${tmpdir}/output.extracted.crop.mnc ${tmpdir}/finalmask.mnc 1 10
  1131. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned ${tmpdir}/output.extracted.crop.mnc $(dirname $output)/$(basename ${output} .mnc).extracted.mnc
  1132. minc_anlm ${N4_VERBOSE:+--verbose} --mt ${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ${tmpdir}/corrected.mnc ${tmpdir}/corrected.denoise.mnc
  1133. mincreshape -quiet ${N4_VERBOSE:+-verbose} -clobber -short -unsigned ${tmpdir}/corrected.denoise.mnc $(dirname $output)/$(basename ${output} .mnc).denoise.mnc
  1134. fi
  1135. if [[ ${_arg_debug} == "off" ]]; then
  1136. rm -rf ${tmpdir}
  1137. fi
  1138. # ] <-- needed because of Argbash

iterativeN4_multispectral.sh at commit 70dbcef, under other · at the source

Overview

Authors: Claude Lepage1, Erika Nolan2, Trisanna Sprung-Much2, Gabriel A. Devenyi3,4, Michael Petrides2, Alan C. Evans1
  1. Brain Imaging Center, Montréal Neurological Institute, McGill University, Montréal, Canada
  2. McGill University, Montréal, Canada
  3. Cerebral Imaging Centre, Douglas Mental Health University Institute, Verdun, Canada
  4. Department of Psychiatry, McGill University, Montréal, Canada
Journal: Neuroimage. Reports, volume 6, issue 3, article 100360
Dates: received 11 December 2025; accepted 1 June 2026; published online 10 June 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1016/j.ynirp.2026.100360 · PMID 42318323 · PMCID PMC13273796 · OpenAlex W7164130369
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), human (organism), non-human primate (organism), methods / tools (subfield)
Methods: Connectivity, fMRI & imaging, Preprocessing
Keywords: Surface-based morphometry, Surface registration, Chimpanzee (pan troglodytes), NCBR, Atlas, Primate
Topic: Epilepsy research and treatment (Psychiatry and Mental health, Medicine), according to OpenAlex
Funding: Alliance de recherche numérique du Canada
Citations: not cited yet (Europe PMC); 61 references in the paper

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

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 097785ceb428b96e1fae9dc772a31a716b2854b2, 14 February 2025
Languages: Jupyter (6), Shell (4)
Size: 125 files, 10 scripts
Software Heritage: not archived
Found in: the text, “Introduction”
Holds: README, license file, documentation, 6 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration
Tools: FreeSurfer (5 files), FSL (5 files), NumPy (2 files), pycortex (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
12 files

neurabenn/precon_all

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 58b4bc581b5cf713b1f84e2f875649904052af89, 12 August 2026
Languages: Shell (24)
Size: 84 files, 24 scripts
Software Heritage: archived
Found in: the text, “Introduction”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: FreeSurfer (16 files), FSL (16 files), ANTs (8 files), AFNI (2 files), Connectome Workbench (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
26 files

CoBrALab/iterativeN4_multispectral

License: other
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 70dbcef412e7bf6a13cefffd2c9a73115499803d, 12 November 2025
Languages: Shell (1)
Size: 40 files, 1 script
Software Heritage: not archived
Found in: the text, “Average volumetric template”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ANTs (1 file), NumPy (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
3 files

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://doi.org/10.1016/j.ynirp.2026.100360

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/j.ynirp.2026.100360},
url = {https://doi.org/10.1016/j.ynirp.2026.100360},
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/06/10
VL - 6
IS - 3
SP - 100360
SN - 2666-9560
PB - Elsevier
DO - 10.1016/j.ynirp.2026.100360
UR - https://doi.org/10.1016/j.ynirp.2026.100360
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.ynirp.2026.100360",
"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": "Neuroimage Rep",
"volume": "6",
"issue": "3",
"page": "100360",
"DOI": "10.1016/j.ynirp.2026.100360",
"PMID": "42318323",
"PMCID": "PMC13273796",
"ISSN": "2666-9560",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.ynirp.2026.100360",
"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 biology
In 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 biomedicine
In 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 communications
In 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 communications
In 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: iScience
In 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 methods
In 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: Neuron
In 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.

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.