Pipeline Documentation
Below are the scripts for the bash source code used in the pipeline.
Note
This page includes the documentation and source code for all of the scripts used.
lib.sh (Bash Library Functions )
Bash library functions that are referenced throughout the use of the pipeline.
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
# Bash/shell function library for logging.
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
#######################################
# Prints message to the command line interface
# in some arbitrary color.
# Args:
# msg
#######################################
echo_color(){
msg='\033[0;'"${@}"'\033[0m'
echo -e ${msg}
}
#######################################
# Prints message to the command line interface
# in red.
# Args:
# msg
#######################################
echo_red(){
echo_color '31m'"${@}"
}
#######################################
# Prints message to the command line interface
# in green.
# Args:
# msg
#######################################
echo_green(){
echo_color '32m'"${@}"
}
#######################################
# Prints message to the command line interface
# in blue.
# Args:
# msg
#######################################
echo_blue(){
echo_color '36m'"${@}"
}
#######################################
# Prints message to the command line interface
# in red when an error is intened to be raised.
# Args:
# msg
#######################################
exit_error(){
echo_red "${@}"
exit 1
}
#######################################
# Logs the command to file, and executes (runs) the command.
# Globals:
# log
# err
# Args:
# Command to be logged and performed.
#######################################
run(){
echo "${@}"
"${@}" >>${log} 2>>${err}
if [[ ! ${?} -eq 0 ]]; then
echo "failed: see log files ${log} ${err} for details"
exit 1
fi
echo "-----------------------"
}
#######################################
# Logs the command to file.
# Globals:
# log
# err
# Args:
# Command to be logged and performed.
#######################################
log(){
echo "${@}"
echo "${@}" >>${log} 2>>${err}
echo "-----------------------"
echo "-----------------------" >>${log} 2>>${err}
}
#######################################
# Extracts and merges b0s in a dMRI volume.
# Globals:
# log
# err
# Required Args:
# d, dwi: Input DWI file.
# b, bval: Corresponding bval file.
# e, bvec: Corresponding bvec file.
# o, out: Output file name.
# Returns
# 0 if no errors, non-zero on error.
#######################################
extract_b0(){
# Parse arguments
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; local dwi=${1} ;;
-b|--bval) shift; local bval=${1} ;;
-e|--bvec) shift; local bvec=${1} ;;
-o|--out) shift; local out=${1} ;;
-*) echo_red "$(basename ${0}) | extract_b0: Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Create tmp dir
local cwd=${PWD}
local tmp_dir=$(remove_ext ${out})_tmp_${RANDOM}
run mkdir -p ${tmp_dir}
run cd ${tmp_dir}
# Create mif file
run mrconvert -fslgrad ${bvec} ${bval} ${dwi} dwi.mif
# Extract b0s
run dwiextract -bzero dwi.mif dwi.b0s.nii.gz
# Merge b0s
run fslmaths dwi.b0s.nii.gz -Tmean ${out}
# Clean-up
cd ${cwd}
rm -rf ${tmp_dir}
}
#######################################
# N4 retrospective bias correction algorithm.
# Globals:
# log
# err
# Args:
# Same arguments as N4BiasFieldCorrection.
# Returns
# 0 if no errors, non-zero on error.
#######################################
N4(){
N4BiasFieldCorrection "${@}"
}
#######################################
# Creates mask from b0s of a dMRI.
#
# NOTE:
# * Still a work in progress.
# * Currently not used at the moment.
#
# Globals:
# log
# err
# Args:
# Same arguments as N4BiasFieldCorrection.
# Returns
# 0 if no errors, non-zero on error.
#######################################
create_mask(){
# Set defaults
frac_int=0.25
bias_correct="false"
# Parse arguments
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; local dwi=${1} ;;
-b|--bval) shift; local bval=${1} ;;
-e|--bvec) shift; local bvec=${1} ;;
-o|--outdir) shift; local out=${1} ;;
--bias-correct) local bias_correct="true" ;;
-f|-frac|--frac-int) shift; local frac_int=${1} ;;
-*) echo_red "$(basename ${0}) | extract_b0: Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Create tmp dir
local cwd=${PWD}
local tmp_dir=$(remove_ext ${out})_tmp_${RANDOM}
run mkdir -p ${tmp_dir}
run cd ${tmp_dir}
# Extract b0s
run extract_b0 --dwi ${dwi} --bval ${bval} --bvec ${bvec} --out b0s.nii.gz
b0=$(realpath b0s.nii.gz)
if [[ "${bias_correct}" = true ]]; then
run bet ${b0} tmp -R -f 0.1 -m
run N4 -i ${b0}.nii.gz \
-x tmp_mask.nii.gz \
-o "[restore.nii.gz,bias.nii.gz]" \
-c "[50x50x50,0.001]" \
-s 2 \
-b "[100,3]" \
-t "[0.15,0.01,200]"
else
run cp ${b0} restore.nii.gz
fi
# WORK IN PROGRESS
# # Copy files with output preifx
# run bet restore.nii.gz restore_brain -R -f ${frac_int} -m
# run imcp restore.nii.gz ${out}/hifib0
# run imcp restore_brain.nii.gz ${out}_hifi_brain.nii.gz
# run imcp restore_brain_mask.nii.gz ${out}_hifi_brain_mask.nii.gz
run cd ${cwd}
}
#######################################
# xfm_tck wrapper function for the
# tractography python CLI. CLI options
# are the same as the referenced command
# line tool.
# Globals:
# log
# err
# Args:
# Same arguments as xfm_tck.py
# Returns
# 0 if no errors, non-zero on error.
#######################################
xfm_tck(){
local scripts_dir=$(echo $(dirname $(realpath ${0})))
# local cmd=$(realpath ${scripts_dir}/../pkgs/xfm_tck/xfm_tck.py)
local cmd=$(realpath ${scripts_dir}/pkgs/xfm_tck/xfm_tck.py)
run ${cmd} "${@}"
}
#######################################
# dwinfo wrapper function for the
# python CLI. CLI options
# are the same as the referenced command
# line tool.
# Globals:
# log
# err
# Args:
# Same arguments as dwinfo.py
# Returns
# 0 if no errors, non-zero on error.
#######################################
dwinfo(){
local scripts_dir=$(echo $(dirname $(realpath ${0})))
local cmd=$(realpath ${scripts_dir}/../pkgs/dwinfo/dwinfo.py)
run ${cmd} "${@}"
}
#######################################
# ``exists`` wrapper function. Checks
# if the input file or directory exists.
# If the input exists (as either a file
# or directory), then 'True' is
# returned/printed to the command line.
# If the input does not exist, then
# 'False' is printed to the command
# line.
#
# Globals:
# log
# err
# Args:
# Input file or directory.
# Returns
# True if directory/file exists, and
# False otherwise
#######################################
exists(){
# Define input
input="${1}"
input=$(realpath ${input})
# Check if input exists as a file or directory
if [[ -f ${input} ]] || [[ -d ${input} ]]; then
echo "True"
else
echo "False"
fi
}
dwi_preproc.sh
The main preprocessing script that gathers all the required information and runs each stage of dMR image preprocessing.
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
# SET SCRIPT GLOBALS
scripts_dir=$(echo $(dirname $(realpath ${0})))
# SOURCE LOGGING FUNCTIONS
. ${scripts_dir}/src/lib.sh
xfm_tck=$(realpath ${scripts_dir}/pkgs/xfm_tck/xfm_tck.py)
#######################################
# Prints usage to the command line interface.
# Args:
# None
#######################################
Usage(){
cat << USAGE
Usage:
$(basename ${0}) <--options> [--options]
Performs similar preprocessing steps to that of the dHCP dMRI preprocessing pipeline.
These preprocessing steps include:
1. Topup (distortion estimation)
2. Eddy (eddy current, motion, distortion, and slice-to-volume motion correction)
3. QC
4. DTI model fitting
5. Tractography (Single-Shell CSD)
Options marked as REPEATABLE may be specified more than once, however all such options
must be specified the same number of times.
Lastly, input data is assumed to be named in the BIDS v1.4.1+ convention, with '*_acq-' containing
the shells (bvalues) of the acquisition. Other attributes in the filename should include:
* subject ID (sub-<sub_id>_...)
* run ID (..._run-<run_id>_...)
Required arguments
-d, --dwi Input 4D dMR/DW image file.
-b, --bval Corresponding bval file.
-e, --bvec Corresponding bvec file.
-b0, --b0, --sbref Reverse phase encoded b0 (single-band reference)
--slspec Slice order specification file.
--acqp Acquisition parameter file.
--data-dir Output parent data directory.
--template REPEATABLE: Standard whole-head template for registration and tractography.
--template-brain REPEATABLE: Standard brain template for registration and tractography.
--labels REPEATABLE: Corrsponding template labels for tractography.
--out-tract REPEATABLE: Corrsponding output directory basenames for tractography.
Optional arguments
--dwi-json Corresponding dMR/DW image JSON sidecar.
--b0-json, --sbref-json Corresponding b0/sbref JSON sidecar.
--echo-spacing Echo-spacing parameter for the parameter acquisition file [default: 0.05].
-mb, --multiband-factor Multiband acceleration factor. NOTE: If this parameter is provided then
'--slspec' does not need to be specified. Additionally, this parameter can
also be specified via a JSON (sidecar) file.
--idx Slice phase encoding index file.
--mporder Number of discrete cosine functions used to model slice-to-volume motion.
Set this parameter to 0 to disable slice-to-volume motion correction and
distortion correction. Otherwise, this parameter is automatically computed.
[default: automatically computed].
--factor Factor to divide the mporder by (if necessary). A factor division of 4
is recommended. [default: 0].
-h, -help, --help Prints the help menu, then exits.
USAGE
exit 1
}
#######################################
# Uses shell's built-in hash function to check dependency.
# Globals:
# log
# err
# Args:
# Dependency to check, e.g. shell command.
# Returns
# 0 if no errors, non-zero on error.
#######################################
dependency_check(){
if ! hash ${1} 2>/dev/null; then
exit_error "Dependency ${1} is not installed or added to the system path. Please check. Exiting..."
fi
}
# SCRIPT BODY
# Set defaults
mporder=""
mb=""
echo_spacing=0.05
factor=0
b0_json=""
slspec=""
acqp=""
# Parse arguments
[[ ${#} -eq 0 ]] && Usage;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; dwi=${1} ;;
-b|--bval) shift; bval=${1} ;;
-e|--bvec) shift; bvec=${1} ;;
-b0|--b0|--sbref) shift; sbref=${1} ;;
--factor) shift; factor=${1} ;;
--slspec) shift; slspec=${1} ;;
-mb|--multiband-factor) shift; mb=${1} ;;
--idx) shift; idx=${1} ;;
--acqp) shift; acqp=${1} ;;
--dwi-json) shift; dwi_json=${1} ;;
--b0-json|--sbref-json) shift; b0_json=${1} ;;
--data-dir) shift; data_dir=${1} ;;
--template) shift; templates+=( ${1} ) ;;
--template-brain) shift; template_brains+=( ${1} ) ;;
--labels) shift; labels+=( ${1} ) ;;
--out-tract) shift; out_tracts+=( ${1} ) ;;
--mporder) shift; mporder=${1} ;;
--echo-spacing) shift; echo_spacing=${1} ;;
-h|-help|--help) shift; Usage; ;;
-*) echo_red "$(basename ${0}): Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Check args
if [[ ${#templates[@]} -eq ${#template_brains[@]} ]] && [[ ${#template_brains[@]} -eq ${#labels[@]} ]] && [[ ${#labels[@]} -eq ${#out_tracts[@]} ]]; then
echo ""
else
echo_red "Unequal number of mulit-input options."
Usage;
fi
# Check dependencies
deps=( topup eddy mrconvert dwiextract ss3t_csd_beta1 )
for dep in ${deps[@]}; do
dependency_check ${dep}
done
# variable info
sub_id=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $1}' | sed "s@sub-@@g")
run_id=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $4}' | sed "s@run-@@g")
bshell=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $2}' | sed "s@acq-@@g")
outdir=${data_dir}/sub-${sub_id}/${bshell}/run-${run_id}
topup_dir=${outdir}/topup
eddy_dir=${outdir}/eddy
preproc_dir=${outdir}/preprocessed_data
log_dir=${outdir}/logs
log=${log_dir}/dwi.log
err=${log_dir}/dwi.err
mkdir -p ${log_dir}
# Preprocess data
${scripts_dir}/src/import.sh \
--b0 ${sbref} \
--dwi ${dwi} \
--bval ${bval} \
--bvec ${bvec} \
--data-dir ${data_dir} \
--dwi-json ${dwi_json}
# --slspec ${slspec} \
# --acqp ${acqp} \
# --multiband-factor ${mb} \
# --echo-spacing ${echo_spacing} \
# --b0-json ${b0_json}
${scripts_dir}/src/run_topup.sh \
--phase ${outdir}/import/phase \
--acqp ${outdir}/import/dwi.params.acqp \
--out-dir ${outdir}
${scripts_dir}/src/run_eddy.sh \
--dwi ${dwi} \
--bval ${bval} \
--bvec ${bvec} \
--outdir ${outdir} \
--acqp ${outdir}/import/dwi.params.acqp \
--slspec ${outdir}/import/dwi.slice_order \
--topup-dir ${topup_dir} \
--factor ${factor}
# --mporder ${mporder} \
${scripts_dir}/src/postproc.sh \
--dwi ${eddy_dir}/eddy_corrected.nii.gz \
--bval ${bval} \
--bvec ${eddy_dir}/eddy_corrected.eddy_rotated_bvecs \
--dwi-json ${dwi_json} \
--outdir ${outdir} \
--eddy-dir ${eddy_dir} \
--slspec ${outdir}/import/dwi.slice_order \
--idx ${outdir}/import/dwi.idx \
--acqp ${outdir}/import/dwi.params.acqp \
--topup-dir ${topup_dir}
# DISREGARD_CONDITIONS=0
DISREGARD_CONDITIONS=1
for (( i=0; i < ${#templates[@]}; i++)); do
if [[ ! -d ${outdir}/tractography/${out_tracts[$i]} ]] || [[ ${DISREGARD_CONDITIONS} -eq 1 ]]; then
# xfm_tck \
${xfm_tck} \
--dwi=${preproc_dir}/dwi.nii.gz \
--bval=${preproc_dir}/dwi.bval \
--bvec=${preproc_dir}/dwi.bvec \
--json=${preproc_dir}/dwi.json \
--log=${log_dir}/tract.log \
--template=${templates[$i]} \
--template-brain=${template_brains[$i]} \
--labels=${labels[$i]} \
--out-dir=${outdir}/tractography/${out_tracts[$i]} \
--frac-int=0.25 \
--QIT \
--symmetric \
--zero-diagonal \
--FA --MD --AD --RD # --no-cleanup
fi
done
log "END: dMRI Preprocessing"
Import Data (Stage 0)
Import/copy the necessary data to a storage location with the required read/write access.
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
# SET SCRIPT GLOBALS
scripts_dir=$(echo $(dirname $(realpath ${0})))
# SOURCE LOGGING FUNCTIONS
. ${scripts_dir}/lib.sh
dwinfo=$(realpath ${scripts_dir}/../pkgs/dwinfo/dwinfo.py)
#######################################
# Prints usage to the command line interface.
# Args:
# None
#######################################
Usage(){
cat << USAGE
Usage:
$(basename ${0}) <--options> [--options]
Imports the dMR (diffusion magnetic resonance) image data for preprocessing.
Required arguments
-d, --dwi Input DWI file.
-b, --bval Corresponding bval file.
-e, --bvec Corresponding bvec file.
-b0, --b0 Reversed phase encoded b0 (or single band reference).
--data-dir Output parent data directory.
--slspec Slice order specification file.
--acqp Acquisition parameter file.
Optional arguments
--idx Slice phase encoding index file.
-mb, --multiband-factor Multiband acceleration factor.
--dwi-json DWI/dMRI JSON sidecar.
--b0-json b0 JSON sidecar.
-h, -help, --help Prints the help menu, then exits.
USAGE
exit 1
}
#######################################
# Checks the dimensions of an input DWI file
# and a corresponding reverse phase encoded
# sbref/b0.
# Globals:
# log
# err
# Args:
# dwi: Input DWI file.
# b0: Reversed phase encoded b0 (or single band reference).
# Returns
# 0 if no errors, non-zero on error.
#######################################
check_dim(){
# Parse arguments
while [[ ${#} -gt 0 ]]; do
case "${1}" in
--dwi) shift; local dwi=${1} ;;
--b0) shift; local b0=${1} ;;
-*) echo_red "$(basename ${0}) | check_dim: Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Set dimension variables
local x1=$(fslval ${dwi} dim1)
local y1=$(fslval ${dwi} dim2)
local z1=$(fslval ${dwi} dim3)
local x2=$(fslval ${b0} dim1)
local y2=$(fslval ${b0} dim2)
local z2=$(fslval ${b0} dim3)
# Check dimensions
[[ ${x1} -ne ${x2} ]] && log "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI x: ${x1} rpe_sbref x: ${x2}" && exit_error "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI x: ${x1} rpe_sbref x: ${x2}"
[[ ${y1} -ne ${y2} ]] && log "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI y: ${y1} rpe_sbref y: ${y2}" && exit_error "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI y: ${y1} rpe_sbref y: ${y2}"
[[ ${z1} -ne ${z2} ]] && log "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI z: ${z1} rpe_sbref z: ${z2}" && exit_error "ERROR | check_dim: Input DWI and sbref are of different dimensions - DWI z: ${z1} rpe_sbref z: ${z2}"
}
#######################################
# Writes the index file for use with FSL's
# topup, and eddy.
#
# NOTE: This function assumes uniform phase-encoding
# for the DWI/dMRI. The output index file is just
# a sequence 1's.
# Required Args:
# dwi: Input DWI file.
# out-idx: Slice phase encoding index.
# Returns
# 0 if no errors, non-zero on error.
#######################################
write_idx(){
# Parse arguments
while [[ ${#} -gt 0 ]]; do
case "${1}" in
--dwi) shift; local dwi=${1} ;;
--out-idx) shift; local out_idx=${1} ;;
-*) echo_red "$(basename ${0}) | write_idx: Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Number of volumes/dynamics
local nvols=( $( echo $(seq 1 1 $(fslval ${dwi} dim4)) ) )
log "LOG | write_idx: Writing DWI/dMRI index file. NOTE: this assumes uniform phase encoding in the dMRI."
for i in ${nvols[@]}; do
echo "1" >> "${out_idx}"
done
}
#######################################
# Imports relevant information such as slice
# order, phase encoding index, and acquisition
# parameters.
# Globals:
# log
# err
# Required Args:
# slspec: Slice order specification file.
# acqp: Acquisition parameter file.
# out-slspec: Output filename of the slice order specification file.
# out-acqp: Output filename of the acquisition parameter file.
# Optional Args:
# idx: Slice phase encoding index.
# out-idx: Output filename of the slice phase encoding index.
# dwi: Input DWI file.
# Returns
# 0 if no errors, non-zero on error.
#######################################
import_info(){
# Set defaults
local idx=""
local dwi=""
# Parse arguments
while [[ ${#} -gt 0 ]]; do
case "${1}" in
--slspec) shift; local slspec=${1} ;;
--idx) shift; local idx=${1} ;;
--acqp) shift; local acqp=${1} ;;
--dwi) shift; local dwi=${1} ;;
--out-slspec) shift; local out_slspec=${1} ;;
--out-idx) shift; local out_idx=${1} ;;
--out-acqp) shift; local out_acqp=${1} ;;
-*) echo_red "$(basename ${0}) | import_info: Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
if [[ ! -f ${idx} ]] && [[ -f ${dwi} ]] && [[ ! -z ${out_idx} ]]; then
run write_idx --dwi ${dwi} --out-idx ${out_idx}
local idx=${out_idx}
else
run cp ${idx} ${out_idx}
fi
# Check input arguments
[[ -z ${slspec} ]] || [[ ! -f ${slspec} ]] && log "ERROR | import_info: Slice specification order file required." && exit_error "ERROR | import_info: Slice specification order file required."
[[ -z ${idx} ]] || [[ ! -f ${idx} ]] && log "ERROR | import_info: Slice index file required." && exit_error "ERROR | import_info: Slice index file required."
[[ -z ${acqp} ]] || [[ ! -f ${acqp} ]] && log "ERROR | import_info: Acquisition parameters file required." && exit_error "ERROR | import_info: Acquisition parameters file required."
[[ -z ${out_slspec} ]] && log "ERROR | import_info: Slice specification order output filename required." && exit_error "ERROR | import_info: Slice specification order output filename required."
[[ -z ${out_idx} ]] && log "ERROR | import_info: Slice index output filename required." && exit_error "ERROR | import_info: Slice index output filename required."
[[ -z ${out_acqp} ]] && log "ERROR | import_info: Acquisition parameters output filename required." && exit_error "ERROR | import_info: Acquisition parameters output filename required."
# Import info
run cp ${slspec} ${out_slspec}
run cp ${acqp} ${out_acqp}
}
# SCRIPT MAIN BODY
# Parse arguments
[[ ${#} -eq 0 ]] && Usage;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; dwi=${1} ;;
-b|--bval) shift; bval=${1} ;;
-e|--bvec) shift; bvec=${1} ;;
-b0|--b0) shift; b0=${1} ;;
--slspec) shift; slspec=${1} ;;
-mb|--multiband-factor) shift; mb=${1} ;;
--idx) shift; idx=${1} ;;
--acqp) shift; acqp=${1} ;;
--dwi-json) shift; dwi_json=${1} ;;
--b0-json) shift; b0_json=${1} ;;
--data-dir) shift; data_dir=${1} ;;
--echo-spacing) shift; echo_spacing=${1} ;;
-h|-help|--help) shift; Usage; ;;
-*) echo_red "$(basename ${0}): Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# variable info
sub_id=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $1}' | sed "s@sub-@@g")
run_id=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $4}' | sed "s@run-@@g")
dwi_PE=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $3}' | sed "s@dir-@@g")
bshell=$(echo $(remove_ext $(basename ${dwi})) | sed "s@_@ @g" | awk '{print $2}' | sed "s@acq-@@g")
outdir=${data_dir}/sub-${sub_id}/${bshell}/run-${run_id}
# Check (required) input arguments
if [[ -z ${data_dir} ]]; then
log "ERROR | import_data: Output data directory was not specified."
exit_error "ERROR | import_data: Output data directory was not specified."
fi
if [[ -z ${dwi} ]] || [[ ! -f ${dwi} ]]; then
log "ERROR | import_data: Input DWI file was not specified or does not exist."
exit_error "ERROR | import_data: Input DWI file was not specified or does not exist."
else
dwi=$(realpath ${dwi})
fi
if [[ -z ${bval} ]] || [[ ! -f ${bval} ]]; then
log "ERROR | import_data: Input bval file was not specified or does not exist."
exit_error "ERROR | import_data: Input bval file was not specified or does not exist."
else
bval=$(realpath ${bval})
fi
if [[ -z ${bvec} ]] || [[ ! -f ${bvec} ]]; then
log "ERROR | import_data: Input bvec file was not specified or does not exist."
exit_error "ERROR | import_data: Input bvec file was not specified or does not exist."
else
bvec=$(realpath ${bvec})
fi
if [[ -z ${b0} ]] || [[ ! -f ${b0} ]]; then
log "ERROR | import_data: Input reversed phase (rPE) b0 file was not specified or does not exist."
exit_error "ERROR | import_data: Input reversed phase (rPE) b0 file was not specified or does not exist."
else
b0=$(realpath ${b0})
fi
# Check (optional) arguments
if [[ ! -z ${dwi_json} ]]; then
if [[ -f ${dwi_json} ]]; then
dwi_json=$(realpath ${dwi_json})
else
log "ERROR | import_data: DWI JSON file specified, but it does not exist."
exit_error "ERROR | import_data: DWI JSON file specified, but it does not exist."
fi
fi
if [[ ! -z ${b0_json} ]]; then
if [[ -f ${b0_json} ]]; then
b0_json=$(realpath ${b0_json})
else
log "ERROR | import_data: Reversed phase (rPE) b0 JSON file specified, but it does not exist."
exit_error "ERROR | import_data: Reversed phase (rPE) b0 JSON file specified, but it does not exist."
fi
fi
# Declare global variables
cwd=${PWD}
log_dir=${outdir}/logs
log=${log_dir}/dwi.log
err=${log_dir}/dwi.err
# Import data
if [[ ! -d ${outdir}/import ]]; then
log "START: Import Data"
run mkdir -p ${log_dir}
run mkdir -p ${outdir}/import
check_dim --dwi ${dwi} --b0 ${b0}
# Temporary directory (variable)
tmp_dir=${outdir}/import/tmp_dir_${RANDOM}
# Check if acqp file was passed
if [[ -z ${acqp} ]] || [[ ! -f ${acqp} ]]; then
[[ ! -d ${tmp_dir} ]] && run mkdir -p ${tmp_dir}
if [[ -z ${echo_spacing} ]]; then
_echo_spacing=$(${dwinfo} read-bids --bids-nifti=${dwi} --bids-label=EchoSpacing)
echo_spacing=$(python -c "print(float('${_echo_spacing}')*100)")
fi
acqp=${tmp_dir}/params.acqp
echo "0 1 0 ${echo_spacing}" > ${acqp}
echo "0 -1 0 ${echo_spacing}" >> ${acqp}
fi
# Check if slice specification order file was passed
if [[ -z ${slspec} ]] || [[ ! -f ${slspec} ]]; then
[[ ! -d ${tmp_dir} ]] && run mkdir -p ${tmp_dir}
[[ -z ${mb} ]] && mb=$(${dwinfo} read-bids --bids-nifti=${dwi} --bids-label=MultibandAccelerationFactor)
slspec=${tmp_dir}/dwi.slice.order.txt
${dwinfo} sliceorder --bids-nifti=${dwi} --mb-factor=${mb} --output=${slspec} --interleaved
fi
run import_info --out-slspec ${outdir}/import/dwi.slice_order --out-acqp ${outdir}/import/dwi.params.acqp --dwi ${dwi} --out-idx ${outdir}/import/dwi.idx --slspec ${slspec} --idx ${idx} --acqp ${acqp}
run extract_b0 --dwi ${dwi} --bval ${bval} --bvec ${bvec} --out ${outdir}/import/sbref_pa.nii.gz
run fslmaths ${b0} -Tmean ${outdir}/import/sbref_ap &
run imcp ${dwi} ${outdir}/import/dwi &
# run imcp ${b0} ${outdir}/import/sbref_ap &
run cp ${bval} ${outdir}/import/dwi.bval
run cp ${bvec} ${outdir}/import/dwi.bvec
[[ ! -z ${dwi_json} ]] && run cp ${dwi_json} ${outdir}/import/dwi.json
[[ ! -z ${b0_json} ]] && run cp ${b0_json} ${outdir}/import/phase.json
rm -rf ${tmp_dir} &
wait
# Merge b0s
run cd ${outdir}
run fslmerge -t ${outdir}/import/phase ${outdir}/import/sbref_pa ${outdir}/import/sbref_ap
# Minor clean-up
run imrm ${outdir}/import/sbref_pa ${outdir}/import/sbref_ap
run cd ${cwd}
log "END: Import Data"
else
log "Import already completed."
fi
Topup (Stage 1)
Perform stage 1 of the preprocessing which includes FSL’s Topup.
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
# SET SCRIPT GLOBALS
scripts_dir=$(echo $(dirname $(realpath ${0})))
# SOURCE LOGGING FUNCTIONS
. ${scripts_dir}/lib.sh
#######################################
# Prints usage to the command line interface.
# Arguments:
# None
#######################################
Usage(){
cat << USAGE
Usage:
$(basename ${0}) <--options> [--options]
Performs dMR image distortion estimation of 4D reverse phase-encoded b0s.
Required arguments
-p, --phase 4D reverse phase-encoded b0s.
-a, --acqp Acquisition parameter file.
--out-dir Parent output directory.
Optional arguments
-c, --config Topup configuration file.
-h, -help, --help Prints the help menu, then exits.
USAGE
exit 1
}
# SCRIPT MAIN BODY
# Set defaults
config=${scripts_dir}/../misc/b02b0.cnf
# Parse arguments
[[ ${#} -eq 0 ]] && Usage;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-p|--phase) shift; phase=${1} ;;
-a|--acqp) shift; acqp=${1} ;;
-c|--config) shift; config=${1} ;;
--out-dir) shift; outdir=${1} ;;
-*) echo_red "$(basename ${0}): Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Log variabes
log_dir=${outdir}/logs
log=${log_dir}/dwi.log
err=${log_dir}/dwi.err
# Topup output dir
cwd=${PWD}
topup_dir=${outdir}/topup
if [[ ! -d ${topup_dir} ]]; then
run mkdir -p ${topup_dir}
cd ${topup_dir}
run imcp ${phase} phase && phase=${PWD}/phase
run cp ${acqp} params.acqp && acqp=params.acqp
log "START: TOPUP"
# Run topup
run topup --imain=${phase} --datain=${acqp} --config=${config} --fout=${topup_dir}/fieldmap --iout=${topup_dir}/topup_b0s --out=${topup_dir}/topup_results -v
# Run BET on topup output
run fslmaths ${topup_dir}/topup_b0s -Tmean ${topup_dir}/topup_hifib0
run bet ${topup_dir}/topup_hifib0 ${topup_dir}/nodif_brain -m -f 0.25 -R
cd ${cwd}
log "END: TOPUP"
else
log "TOPUP already completed."
fi
EDDY (Stage 2)
Perform stage 2 of the preprocessing which includes FSL’s EDDY.
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
# SET SCRIPT GLOBALS
scripts_dir=$(echo $(dirname $(realpath ${0})))
# SOURCE LOGGING FUNCTIONS
. ${scripts_dir}/lib.sh
dwinfo=$(realpath ${scripts_dir}/../pkgs/dwinfo/dwinfo.py)
#######################################
# Prints usage to the command line interface.
# Arguments:
# None
#######################################
Usage(){
cat << USAGE
Usage:
$(basename ${0}) <--options> [--options]
Performs eddy current correction, in addition to motion and distortion correction.
Additionally, slice-to-volume motion correction is also performed.
Required arguments
-d, --dwi Input DWI file.
-b, --bval Corresponding bval file.
-e, --bvec Corresponding bvec file.
--data-dir Output parent data directory.
--slspec Slice order specification file.
--acqp Acquisition parameter file.
--idx Slice phase encoding index file.
--outdir Parent output directory.
--topup-dir TOPUP output directory.
Optional arguments
--mporder Number of discrete cosine functions used to model slice-to-volume motion.
Set this parameter to 0 to disable slice-to-volume motion correction and
distortion correction. Otherwise, this parameter is automatically computed.
[default: automatically computed].
--factor Factor to divide the mporder by (if necessary). A factor division of 4
is recommended. [default: 0].
-h, -help, --help Prints the help menu, then exits.
USAGE
exit 1
}
# SCRIPT MAIN BODY
# Set defaults
mporder=""
factor=0
# Parse arguments
[[ ${#} -eq 0 ]] && Usage;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; dwi=${1} ;;
-b|--bval) shift; bval=${1} ;;
-e|--bvec) shift; bvec=${1} ;;
--slspec) shift; slspec=${1} ;;
--factor) shift; factor=${1} ;;
--idx) shift; idx=${1} ;;
--acqp) shift; acqp=${1} ;;
--outdir) shift; outdir=${1} ;;
--topup-dir) shift; topup_dir=${1} ;;
--mporder) shift; mporder=${1} ;;
-h|-help|--help) shift; Usage; ;;
-*) echo_red "$(basename ${0}): Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Log variabes
log_dir=${outdir}/logs
log=${log_dir}/dwi.log
err=${log_dir}/dwi.err
# Eddy output dir
cwd=${PWD}
eddy_dir=${outdir}/eddy
# bsub -q gpu-v100 -gpu "num=1" -M 1000 -W 500 -n 1 -J "bash" -R "span[hosts=1]" -Is bash
# Compute mporder
if [[ -z ${mporder} ]]; then
mporder=$(${dwinfo} mporder --bids-nifti ${dwi} --slice-order=${slspec} --factor-divide=${factor})
fi
if [[ ! -d ${eddy_dir} ]]; then
run mkdir -p ${eddy_dir}
log "START: EDDY"
# Run eddy
run eddy_cuda \
--imain=${dwi} \
--mask=${topup_dir}/nodif_brain_mask.nii.gz \
--index=${outdir}/import/dwi.idx \
--bvals=${bval} \
--bvecs=${bvec} \
--acqp=${acqp} \
--out=${eddy_dir}/eddy_corrected \
--very_verbose \
--niter=5 \
--fwhm=10,5,0,0,0 \
--nvoxhp=5000 \
--repol \
--ol_type=both \
--ol_nstd=3 \
--data_is_shelled \
--cnr_maps \
--residuals \
--dont_mask_output \
--slspec=${slspec} \
--s2v_niter=10 \
--mporder=${mporder} \
--s2v_interp=trilinear \
--s2v_lambda=1 \
--topup=${topup_dir}/topup_results \
--estimate_move_by_susceptibility \
--mbs_niter=20 \
--mbs_ksp=10 \
--mbs_lambda=10
dwi=${eddy_dir}/eddy_corrected.nii.gz
bvec=${eddy_dir}/eddy_corrected.eddy_rotated_bvecs
# Run BET on eddy output
run extract_b0 --dwi ${dwi} --bval ${bval} --bvec ${bvec} --out ${eddy_dir}/hifib0.nii.gz
run bet ${eddy_dir}/hifib0 ${eddy_dir}/nodif_brain -m -f 0.25 -R
log "END: EDDY"
else
log "EDDY already completed."
fi
Post-process (Stage 3)
Perform stage 3 of the preprocessing which includes:
Computing DTI (diffusion tensor imaging) metrics (e.g. FA, MD, RD, etc.)
QC (quality control, via EDDY QUAD)
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# DESCRIPTION:
#
#
# NOTE:
# Google shell style guide is used here for consistency. See the
# style guide here: https://google.github.io/styleguide/shellguide.html
#
# SET SCRIPT GLOBALS
scripts_dir=$(echo $(dirname $(realpath ${0})))
# SOURCE LOGGING FUNCTIONS
. ${scripts_dir}/lib.sh
#######################################
# Prints usage to the command line interface.
# Arguments:
# None
#######################################
Usage(){
cat << USAGE
Usage:
$(basename ${0}) <--options> [--options]
Performs post-processing, which includes dMR image quality control, and the diffusion model tensor fit.
Required arguments
-d, --dwi Input DWI file.
-b, --bval Corresponding bval file.
-e, --bvec Corresponding bvec file.
--dwi-json Corresponding dMRI JSON sidecar.
--outdir Output parent data directory.
--slspec Slice order specification file.
--acqp Acquisition parameter file.
--idx Slice phase encoding index file.
--topup-dir TOPUP output directory.
--eddy-dir EDDY output directory.
Optional arguments
-h, -help, --help Prints the help menu, then exits.
USAGE
exit 1
}
# SCRIPT MAIN BODY
# Parse arguments
[[ ${#} -eq 0 ]] && Usage;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
-d|--dwi) shift; dwi=${1} ;;
-b|--bval) shift; bval=${1} ;;
-e|--bvec) shift; bvec=${1} ;;
--dwi-json) shift; dwi_json=${1} ;;
--outdir) shift; outdir=${1} ;;
--eddy-dir) shift; eddy_dir=${1} ;;
--slspec) shift; slspec=${1} ;;
--idx) shift; idx=${1} ;;
--acqp) shift; acqp=${1} ;;
--topup-dir) shift; topup_dir=${1} ;;
-h|-help|--help) shift; Usage; ;;
-*) echo_red "$(basename ${0}): Unrecognized option ${1}" >&2; Usage; ;;
*) break ;;
esac
shift
done
# Log variabes
log_dir=${outdir}/logs
log=${log_dir}/dwi.log
err=${log_dir}/dwi.err
# Post-process output dir
cwd=${PWD}
preproc_dir=${outdir}/preprocessed_data
qc_dir=${outdir}/eddy.qc
log "START: POST-PROCESS"
if [[ ! -d ${preproc_dir} ]]; then
run mkdir -p ${preproc_dir}
run cd ${preproc_dir}
# Remove negative intensity values (caused by spline interpolation
# during preprocessing).
run fslmaths ${dwi} -thr 0 dwi
run cp ${bval} dwi.bval
run cp ${bvec} dwi.bvec
[[ -f ${dwi_json} ]] && run cp ${dwi_json} dwi.json
# Create brain mask
tmp_dir=${preproc_dir}/tmp_${RANDOM}
run mkdir -p ${tmp_dir}
run extract_b0 --dwi ${preproc_dir}/dwi.nii.gz --bval ${preproc_dir}/dwi.bval --bvec ${preproc_dir}/dwi.bvec --out ${tmp_dir}/b0.nii.gz
run bet ${tmp_dir}/b0.nii.gz ${preproc_dir}/nodif_brain -m -f 0.25 -R
rm -rf ${tmp_dir}
# DTIFIT
[[ ! -d ${preproc_dir}/dtifit ]] && run mkdir -p ${preproc_dir}/dtifit
run dtifit \
-k dwi \
-o ${preproc_dir}/dtifit/data \
-m ${preproc_dir}/nodif_brain_mask \
-r dwi.bvec \
-b dwi.bval \
--save_tensor
fi
if [[ ! -d ${qc_dir} ]]; then
run cd ${preproc_dir}
run eddy_quad ${eddy_dir}/eddy_corrected \
-idx ${idx} \
-par ${acqp} \
-m ${preproc_dir}/nodif_brain \
-b ${preproc_dir}/dwi.bval \
-g ${preproc_dir}/dwi.bvec \
-o ${qc_dir} \
-f ${topup_dir}/fieldmap \
-s ${slspec} \
-v
fi
log "END: POST-PROCESS"