|
1 | 1 | #!/bin/bash |
2 | 2 | # |
3 | | -# Process data. This script should be run within the subject's folder. |
| 3 | +# Process data. This script is designed to be run in the folder for a single subject, however 'sct_run_batch' can be |
| 4 | +# used to run this script multiple times in parallel across a multi-subject BIDS dataset. |
4 | 5 | # |
5 | 6 | # Usage: |
6 | | -# ./process_data.sh <SUBJECT> <FILEPARAM> |
| 7 | +# ./process_data.sh <SUBJECT> |
7 | 8 | # |
8 | 9 | # Example: |
9 | | -# ./process_data.sh sub-03 parameters.sh |
| 10 | +# ./process_data.sh sub-03 |
10 | 11 | # |
11 | 12 | # Author: Julien Cohen-Adad |
12 | 13 |
|
13 | | -# The following global variables are retrieved from parameters.sh but could be |
14 | | -# overwritten here: |
15 | | -# PATH_QC="~/qc" |
| 14 | +# BASH SETTINGS |
| 15 | +# ====================================================================================================================== |
16 | 16 |
|
17 | 17 | # Uncomment for full verbose |
18 | | -set -v |
| 18 | +# set -v |
19 | 19 |
|
20 | 20 | # Immediately exit if error |
21 | 21 | set -e |
22 | 22 |
|
23 | 23 | # Exit if user presses CTRL+C (Linux) or CMD+C (OSX) |
24 | 24 | trap "echo Caught Keyboard Interrupt within script. Exiting now.; exit" INT |
25 | 25 |
|
26 | | -# Retrieve input params |
27 | | -SUBJECT=$1 |
28 | | -FILEPARAM=$2 |
29 | | - |
30 | | - |
31 | | -# FUNCTIONS |
32 | | -# ============================================================================== |
| 26 | +# CONVENIENCE FUNCTIONS |
| 27 | +# ====================================================================================================================== |
33 | 28 |
|
34 | | -# Check if manual label already exists. If it does, copy it locally. If it does |
35 | | -# not, perform labeling. |
36 | | -label_if_does_not_exist(){ |
| 29 | +label_if_does_not_exist() { |
| 30 | + ### |
| 31 | + # This function checks if a manual label file already exists, then: |
| 32 | + # - If it does, copy it locally. |
| 33 | + # - If it doesn't, perform automatic labeling. |
| 34 | + # This allows you to add manual labels on a subject-by-subject basis without disrupting the pipeline. |
| 35 | + ### |
37 | 36 | local file="$1" |
38 | 37 | local file_seg="$2" |
39 | 38 | # Update global variable with segmentation file name |
40 | 39 | FILELABEL="${file}_labels" |
41 | 40 | if [ -e "${PATH_SEGMANUAL}/${file}_labels-manual.nii.gz" ]; then |
42 | 41 | echo "Found manual label: ${PATH_SEGMANUAL}/${file}_labels-manual.nii.gz" |
43 | | - rsync -avzh "${PATH_SEGMANUAL}/${file}_labels-manual.nii.gz" ${FILELABEL}.nii.gz |
| 42 | + rsync -avzh "${PATH_SEGMANUAL}/${file}_labels-manual.nii.gz" "${FILELABEL}.nii.gz" |
44 | 43 | else |
45 | 44 | # Generate labeled segmentation |
46 | | - sct_label_vertebrae -i ${file}.nii.gz -s ${file_seg}.nii.gz -c t2 -qc ${PATH_QC} -qc-subject ${SUBJECT} |
| 45 | + sct_label_vertebrae -i "${file}.nii.gz" -s "${file_seg}.nii.gz" -c t2 -qc "${PATH_QC}" -qc-subject "${SUBJECT}" |
47 | 46 | # Create labels in the cord at C3 and C5 mid-vertebral levels |
48 | | - sct_label_utils -i ${file_seg}_labeled.nii.gz -vert-body 3,5 -o ${FILELABEL}.nii.gz |
| 47 | + sct_label_utils -i "${file_seg}_labeled.nii.gz" -vert-body 3,5 -o "${FILELABEL}.nii.gz" |
49 | 48 | fi |
50 | 49 | } |
51 | 50 |
|
52 | | -# Check if manual segmentation already exists. If it does, copy it locally. If |
53 | | -# it does not, perform seg. |
54 | | -segment_if_does_not_exist(){ |
| 51 | +segment_if_does_not_exist() { |
| 52 | + ### |
| 53 | + # This function checks if a manual spinal cord segmentation file already exists, then: |
| 54 | + # - If it does, copy it locally. |
| 55 | + # - If it doesn't, perform automatic spinal cord segmentation. |
| 56 | + # This allows you to add manual segmentations on a subject-by-subject basis without disrupting the pipeline. |
| 57 | + ### |
55 | 58 | local file="$1" |
56 | 59 | local contrast="$2" |
57 | 60 | # Update global variable with segmentation file name |
58 | 61 | FILESEG="${file}_seg" |
59 | 62 | if [ -e "${PATH_SEGMANUAL}/${FILESEG}-manual.nii.gz" ]; then |
60 | 63 | echo "Found manual segmentation: ${PATH_SEGMANUAL}/${FILESEG}-manual.nii.gz" |
61 | | - rsync -avzh "${PATH_SEGMANUAL}/${FILESEG}-manual.nii.gz" ${FILESEG}.nii.gz |
62 | | - sct_qc -i ${file}.nii.gz -s ${FILESEG}.nii.gz -p sct_deepseg_sc -qc ${PATH_QC} -qc-subject ${SUBJECT} |
| 64 | + rsync -avzh "${PATH_SEGMANUAL}/${FILESEG}-manual.nii.gz" "${FILESEG}.nii.gz" |
| 65 | + sct_qc -i "${file}.nii.gz" -s "${FILESEG}.nii.gz" -p sct_deepseg_sc -qc "${PATH_QC}" -qc-subject "${SUBJECT}" |
63 | 66 | else |
64 | 67 | # Segment spinal cord |
65 | | - sct_deepseg_sc -i ${file}.nii.gz -c $contrast -qc ${PATH_QC} -qc-subject ${SUBJECT} |
| 68 | + sct_deepseg_sc -i "${file}.nii.gz" -c "${contrast}" -qc "${PATH_QC}" -qc-subject "${SUBJECT}" |
66 | 69 | fi |
67 | 70 | } |
68 | 71 |
|
69 | | - |
70 | 72 | # SCRIPT STARTS HERE |
71 | | -# ============================================================================== |
| 73 | +# ====================================================================================================================== |
| 74 | + |
| 75 | +# The following global variables are retrieved from config.yml but could be overwritten by uncommenting: |
| 76 | +# PATH_QC="~/qc" |
72 | 77 |
|
73 | | -source $FILEPARAM |
| 78 | +# Retrieve input params |
| 79 | +SUBJECT=$1 |
74 | 80 |
|
75 | 81 | # Go to results folder, where most of the outputs will be located |
76 | | -cd $PATH_RESULTS |
| 82 | +cd "${PATH_RESULTS}" |
77 | 83 |
|
78 | 84 | # Copy source images |
79 | 85 | mkdir -p data |
80 | 86 | cd data |
81 | | -cp -r $PATH_DATA/$SUBJECT . |
| 87 | +cp -r "${PATH_DATA}/${SUBJECT}" . |
82 | 88 |
|
83 | 89 | # T2w |
84 | | -# ============================================================================= |
85 | | -cd $SUBJECT/anat/ |
86 | | -file_t2=${SUBJECT}_T2w |
| 90 | +# ====================================================================================================================== |
| 91 | +cd "${SUBJECT}/anat/" |
| 92 | +file_t2="${SUBJECT}_T2w" |
87 | 93 | # Segment spinal cord (only if it does not exist) |
88 | | -segment_if_does_not_exist $file_t2 "t2" |
89 | | -file_t2_seg=$FILESEG |
| 94 | +segment_if_does_not_exist "${file_t2}" "t2" |
| 95 | +file_t2_seg="${FILESEG}" |
90 | 96 | # Create labels in the cord at C2 and C5 mid-vertebral levels (only if it does not exist) |
91 | | -label_if_does_not_exist $file_t2 $file_t2_seg |
92 | | -file_label=$FILELABEL |
| 97 | +label_if_does_not_exist "${file_t2}" "${file_t2_seg}" |
| 98 | +file_label="${FILELABEL}" |
93 | 99 | # Register to template |
94 | | -sct_register_to_template -i $file_t2.nii.gz -s $file_t2_seg.nii.gz -l $file_label.nii.gz -c t2 -param step=1,type=seg,algo=centermassrot:step=2,type=im,algo=syn,iter=5,slicewise=1,metric=CC,smooth=0 -qc $PATH_QC |
| 100 | +sct_register_to_template -i "${file_t2}.nii.gz" -s "${file_t2_seg}.nii.gz" -l "${file_label}.nii.gz" -c t2 \ |
| 101 | + -param step=1,type=seg,algo=centermassrot:step=2,type=im,algo=syn,iter=5,slicewise=1,metric=CC,smooth=0 \ |
| 102 | + -qc "${PATH_QC}" |
95 | 103 | # Warp template |
96 | 104 | # Note: we don't need the white matter atlas at this point, therefore use flag "-a 0" |
97 | | -sct_warp_template -d $file_t2.nii.gz -w warp_template2anat.nii.gz -a 0 -ofolder label_T2w -qc $PATH_QC |
| 105 | +sct_warp_template -d "${file_t2}.nii.gz" -w warp_template2anat.nii.gz -a 0 -ofolder label_T2w -qc "${PATH_QC}" |
98 | 106 | # Compute average CSA between C2 and C3 levels (append across subjects) |
99 | | -sct_process_segmentation -i $file_t2_seg.nii.gz -vert 2:3 -vertfile label_T2w/template/PAM50_levels.nii.gz -o $PATH_RESULTS/CSA.csv -append 1 -qc $PATH_QC |
| 107 | +sct_process_segmentation -i "${file_t2_seg}.nii.gz" -vert 2:3 -vertfile label_T2w/template/PAM50_levels.nii.gz \ |
| 108 | + -o "${PATH_RESULTS}/CSA.csv" -append 1 -qc "${PATH_QC}" |
100 | 109 |
|
101 | 110 | # MT |
102 | | -# ============================================================================= |
| 111 | +# ====================================================================================================================== |
103 | 112 | file_mt1="${SUBJECT}_acq-MTon_MTS" |
104 | 113 | file_mt0="${SUBJECT}_acq-MToff_MTS" |
105 | 114 | # Segment spinal cord |
106 | | -segment_if_does_not_exist $file_mt1 "t2s" |
| 115 | +segment_if_does_not_exist "${file_mt1}" "t2s" |
107 | 116 | file_mt1_seg=$FILESEG |
108 | 117 | # Create mask |
109 | | -sct_create_mask -i $file_mt1.nii.gz -p centerline,$file_mt1_seg.nii.gz -size 45mm |
| 118 | +sct_create_mask -i "${file_mt1}.nii.gz" -p centerline,"${file_mt1_seg}.nii.gz" -size 45mm |
110 | 119 | # Crop data for faster processing |
111 | | -sct_crop_image -i $file_mt1.nii.gz -m mask_$file_mt1.nii.gz -o ${file_mt1}_crop.nii.gz |
112 | | -sct_crop_image -i $file_mt1_seg.nii.gz -m mask_$file_mt1.nii.gz -o ${file_mt1}_crop_seg.nii.gz |
113 | | -file_mt1=${file_mt1}_crop |
| 120 | +sct_crop_image -i "${file_mt1}.nii.gz" -m "mask_${file_mt1}.nii.gz" -o "${file_mt1}_crop.nii.gz" |
| 121 | +sct_crop_image -i "${file_mt1_seg}.nii.gz" -m "mask_${file_mt1}.nii.gz" -o "${file_mt1}_crop_seg.nii.gz" |
| 122 | +file_mt1="${file_mt1}_crop" |
114 | 123 | # Register mt0->mt1 |
115 | 124 | # Tips: here we only use rigid transformation because both images have very |
116 | 125 | # similar sequence parameters. We don't want to use SyN/BSplineSyN to avoid |
117 | 126 | # introducing spurious deformations. |
118 | | -sct_register_multimodal -i $file_mt0.nii.gz -d $file_mt1.nii.gz -param step=1,type=im,algo=rigid,slicewise=1,metric=CC -x spline -qc $PATH_QC |
| 127 | +sct_register_multimodal -i "${file_mt0}.nii.gz" -d "${file_mt1}.nii.gz" \ |
| 128 | + -param step=1,type=im,algo=rigid,slicewise=1,metric=CC \ |
| 129 | + -x spline -qc "${PATH_QC}" |
119 | 130 | # Register template->mt1 |
120 | 131 | # Tips: here we only use the segmentations due to poor SC/CSF contrast at the bottom slice. |
121 | 132 | # Tips: First step: slicereg based on images, with large smoothing to capture |
122 | 133 | # potential motion between anat and mt, then at second step: bpslinesyn in order to |
123 | 134 | # adapt the shape of the cord to the mt modality (in case there are distortions between anat and mt). |
124 | | -sct_register_multimodal -i $SCT_DIR/data/PAM50/template/PAM50_t2.nii.gz -iseg $SCT_DIR/data/PAM50/template/PAM50_cord.nii.gz -d ${file_mt1}.nii.gz -dseg ${file_mt1}_seg.nii.gz -param step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,slicewise=1,iter=3 -initwarp warp_template2anat.nii.gz -initwarpinv warp_anat2template.nii.gz -qc $PATH_QC |
| 135 | +sct_register_multimodal -i "${SCT_DIR}/data/PAM50/template/PAM50_t2.nii.gz" \ |
| 136 | + -iseg "${SCT_DIR}/data/PAM50/template/PAM50_cord.nii.gz" \ |
| 137 | + -d "${file_mt1}.nii.gz" \ |
| 138 | + -dseg "${file_mt1}_seg.nii.gz" \ |
| 139 | + -param step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,slicewise=1,iter=3 \ |
| 140 | + -initwarp warp_template2anat.nii.gz \ |
| 141 | + -initwarpinv warp_anat2template.nii.gz \ |
| 142 | + -qc "${PATH_QC}" |
125 | 143 | # Rename warping fields for clarity |
126 | | -mv warp_PAM50_t22$file_mt1.nii.gz warp_template2mt.nii.gz |
127 | | -mv warp_${file_mt1}2PAM50_t2.nii.gz warp_mt2template.nii.gz |
| 144 | +mv "warp_PAM50_t22${file_mt1}.nii.gz" warp_template2mt.nii.gz |
| 145 | +mv "warp_${file_mt1}2PAM50_t2.nii.gz" warp_mt2template.nii.gz |
128 | 146 | # Warp template |
129 | | -sct_warp_template -d $file_mt1.nii.gz -w warp_template2mt.nii.gz -ofolder label_MT -qc $PATH_QC |
| 147 | +sct_warp_template -d "${file_mt1}.nii.gz" -w warp_template2mt.nii.gz -ofolder label_MT -qc "${PATH_QC}" |
130 | 148 | # Compute mtr |
131 | | -sct_compute_mtr -mt0 ${file_mt0}_reg.nii.gz -mt1 $file_mt1.nii.gz |
| 149 | +sct_compute_mtr -mt0 "${file_mt0}_reg.nii.gz" -mt1 "${file_mt1}.nii.gz" |
132 | 150 | # compute MTR in dorsal columns between levels C2 and C5 (append across subjects) |
133 | | -sct_extract_metric -i mtr.nii.gz -f label_MT/atlas -l 53 -vert 2:5 -vertfile label_MT/template/PAM50_levels.nii.gz -method map -o $PATH_RESULTS/MTR_in_DC.csv -append 1 |
| 151 | +sct_extract_metric -i mtr.nii.gz -f label_MT/atlas -l 53 -vert 2:5 -vertfile label_MT/template/PAM50_levels.nii.gz \ |
| 152 | + -method map -o "${PATH_RESULTS}/MTR_in_DC.csv" -append 1 |
134 | 153 |
|
135 | 154 | # Verify presence of output files and write log file if error |
136 | | -# ============================================================================= |
| 155 | +# ====================================================================================================================== |
137 | 156 | FILES_TO_CHECK=( |
138 | 157 | "$file_t2_seg.nii.gz" |
139 | 158 | "mtr.nii.gz" |
140 | 159 | ) |
141 | | -for file in ${FILES_TO_CHECK[@]}; do |
142 | | - if [ ! -e $file ]; then |
143 | | - echo "$SUBJECT/$file does not exist" >> $PATH_LOG/error.log |
| 160 | +for file in "${FILES_TO_CHECK[@]}"; do |
| 161 | + if [ ! -e "${file}" ]; then |
| 162 | + echo "${SUBJECT}/${file} does not exist" >> "${PATH_LOG}/error.log" |
144 | 163 | fi |
145 | 164 | done |
0 commit comments