Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions etc/config_process_data_dcm-zurich.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"path_data" : "/home/GRAMES.POLYMTL.CA/p118175/extrassd1/janvalosek/data/dcm-zurich",
"path_output" : "/home/GRAMES.POLYMTL.CA/p118175/extrassd1/janvalosek/dcm-zurich_2023-05-17_persex",
"script" : "/home/GRAMES.POLYMTL.CA/p118175/code/dcm_metric_normalization/scripts/process_data_dcm-zurich.sh",
"path_data" : "/Users/kahina/NeuroPoly/data/dcm-zurich",
"path_output" : "/Users/kahina/NeuroPoly/results/first_tests_sept25",
"script" : "/Users/kahina/NeuroPoly/scr/dcm-metric-normalization/scripts/process_data_dcm-zurich.sh",
"exclude_list" : ["sub-817811", "sub-834907", "sub-842197", "sub-294305", "sub-796358", "sub-561017", "sub-250791", "sub-895299"],
"jobs" : 32
"jobs" : 4
}
172 changes: 147 additions & 25 deletions scripts/process_data_dcm-zurich.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ segment_if_does_not_exist() {
local contrast="$2"
# Update global variable with segmentation file name
FILESEG="${file}_label-SC_mask"
# TODO - change to FILESEG="${file}_label-SC_seg", once https://github.com/neuropoly/data-management/issues/225 is done
FILESEGMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${FILESEG}-manual.nii.gz"
# Getting the path for manual segmentation for each session
FILESEGMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${FILESEG}-manual.nii.gz"
echo
echo "Looking for manual segmentation: $FILESEGMANUAL"
if [[ -e $FILESEGMANUAL ]]; then
echo "Found! Using manual segmentation."
rsync -avzh $FILESEGMANUAL ${FILESEG}.nii.gz
sct_qc -i ${file}.nii.gz -s ${FILESEG}.nii.gz -p sct_deepseg_sc -qc ${PATH_QC} -qc-subject ${SUBJECT}
sct_qc -i ${file}.nii.gz -s ${FILESEG}.nii.gz -p sct_deepseg_sc -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
else
echo "Not found. Proceeding with automatic segmentation."
# Segment spinal cord
sct_deepseg_sc -i ${file}.nii.gz -o ${FILESEG}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}
sct_deepseg spinalcord -i ${file}.nii.gz -o ${FILESEG}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
fi
}

Expand All @@ -64,25 +64,99 @@ label_if_does_not_exist(){
local contrast="$3"
# Update global variable with segmentation file name
FILELABEL="${file}_labels"
# TODO - change to FILELABEL="${file}_label-disc", once https://github.com/neuropoly/data-management/issues/225 is done
FILELABELMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${FILELABEL}-manual.nii.gz"
FILELABELMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${FILELABEL}-manual.nii.gz"
echo "Looking for manual disc labels: $FILELABELMANUAL"
if [[ -e $FILELABELMANUAL ]]; then
echo "Found! Using manual disc labels."
rsync -avzh $FILELABELMANUAL ${FILELABEL}.nii.gz
# Generate labeled segmentation from manual disc labels
sct_label_vertebrae -i ${file}.nii.gz -s ${file_seg}.nii.gz -discfile ${FILELABEL}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}
sct_label_vertebrae -i ${file}.nii.gz -s ${file_seg}.nii.gz -discfile ${FILELABEL}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
else
echo "Not found. Proceeding with automatic labeling."
# Generate labeled segmentation automatically (no manual disc labels provided)
sct_label_vertebrae -i ${file}.nii.gz -s ${file_seg}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}
sct_label_vertebrae -i ${file}.nii.gz -s ${file_seg}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
fi
# Generate QC to access disc labels created by sct_label_vertebrae
sct_qc -i ${file}.nii.gz -s ${file_seg}_labeled_discs.nii.gz -p sct_label_utils -qc ${PATH_QC} -qc-subject ${SUBJECT}
sct_qc -i ${file}.nii.gz -s ${file_seg}_labeled_discs.nii.gz -p sct_label_utils -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
}

# Check if manual canal segmentation file already exists. If it does, copy it locally.
# If it doesn't, perform automatic canal segmentation
segment_canal_if_does_not_exist() {
local file="$1"
local contrast="$2"
# Update global variable with segmentation file name
FILESEG="${file}_label-canal_seg"
FILESEGMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${FILESEG}.nii.gz"
echo
echo "Looking for manual canal segmentation: $FILESEGMANUAL"
if [[ -e $FILESEGMANUAL ]]; then
echo "Found! Using manual canal segmentation."
rsync -avzh $FILESEGMANUAL ${FILESEG}.nii.gz
sct_qc -i ${file}.nii.gz -s ${FILESEG}.nii.gz -p sct_deepseg_sc -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
else
echo "Not found. Proceeding with automatic segmentation."
# Segment canal
sct_deepseg sc_canal_t2 -i ${file}.nii.gz -o ${FILESEG}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
fi
}

# Check if manual lesion segmentation file already exists. If it does, copy it locally.
# If it doesn't, perform automatic lesion segmentation
segment_lesion_if_does_not_exist() {
local file="$1"
local contrast="$2"
# Update global variable with segmentation file name
FILESEG="${file}"
FILESEGMANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${file}_label-lesion_seg.nii.gz"
echo
echo "Looking for manual lesion segmentation: $FILESEGMANUAL"
if [[ -e $FILESEGMANUAL ]]; then
echo "Found! Using manual lesion segmentation."
rsync -avzh $FILESEGMANUAL ${file}_lesion_seg.nii.gz
sct_qc -i ${file}.nii.gz -s ${file}_lesion_seg.nii.gz -p sct_deepseg_lesion -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
else
echo "Not found. Proceeding with automatic segmentation."
# Segment lesions
sct_deepseg lesion_sci_t2 -i ${file}.nii.gz -o ${file}.nii.gz -c ${contrast} -qc ${PATH_QC} -qc-subject ${SUBJECT}_${SESSION}
fi
}

# Retrieve input params and other params
SUBJECT=$1
SUBJECT_INPUT=$1
# Adapting the script to handle new folder organization where each subject has multiple sessions
SESSION_INPUT=${2:-ses-M0} # Default to ses-M0 if not provided

# Handle sct_run_batch format where SUBJECT might be "sub-001/ses-M0"
if [[ "$SUBJECT_INPUT" == *"/"* ]]; then
# Extract subject and session from combined format (sct_run_batch style)
SUBJECT=$(echo "$SUBJECT_INPUT" | cut -d'/' -f1)
SESSION=$(echo "$SUBJECT_INPUT" | cut -d'/' -f2)
echo "Detected sct_run_batch format: $SUBJECT_INPUT -> SUBJECT=$SUBJECT, SESSION=$SESSION"
else
# Use separate parameters (direct script call)
SUBJECT=$SUBJECT_INPUT
SESSION=$SESSION_INPUT
fi

# Validate parameters
if [[ -z "$SUBJECT" ]]; then
echo "ERROR: SUBJECT parameter is required"
echo " $0 <SUBJECT/SESSION> (sct_run_batch format)"
echo "Example: $0 sub-001 (uses default ses-M0)"
echo " $0 sub-001/ses-M0 (sct_run_batch format)"
exit 1
fi

# Verify the session directory exists (only if PATH_DATA is set)
if [[ -n "${PATH_DATA}" && ! -d "${PATH_DATA}/${SUBJECT}/${SESSION}" ]]; then
echo "ERROR: Session directory ${PATH_DATA}/${SUBJECT}/${SESSION} does not exist"
echo "Available sessions for ${SUBJECT}:"
find ${PATH_DATA}/${SUBJECT} -maxdepth 1 -type d -name "ses-*" 2>/dev/null | sort || echo " No sessions found or subject doesn't exist"
exit 1
fi

echo "Processing ${SUBJECT} - ${SESSION}"

# get starting time:
start=`date +%s`
Expand All @@ -104,17 +178,17 @@ fi

# Copy source T2w images
# Note: we use '/./' in order to include the sub-folder 'ses-0X'
rsync -Ravzh ${PATH_DATA}/./${SUBJECT}/anat/${SUBJECT}_*T2w.* .
rsync -Ravzh ${PATH_DATA}/./${SUBJECT}/${SESSION}/anat/${SUBJECT}_${SESSION}*T2w.* .

# Go to subject folder for source images
cd ${SUBJECT}/anat
cd ${SUBJECT}/${SESSION}/anat

# ------------------------------------------------------------------------------
# T2w Sagittal
# ------------------------------------------------------------------------------
# Define variables
# We do a substitution '/' --> '_' in case there is a subfolder 'ses-0X/'
file_t2_sag="${SUBJECT//[\/]/_}"_acq-sagittal_T2w
file_t2_sag="${SUBJECT//[\/]/_}"_"${SESSION}"_acq-sagittal_T2w
# Check if file_t2_sag exists
if [[ ! -e ${file_t2_sag}.nii.gz ]]; then
echo "File ${file_t2_sag}.nii.gz does not exist" >> ${PATH_LOG}/missing_files.log
Expand All @@ -131,7 +205,7 @@ fi
# ------------------------------------------------------------------------------
# Define variables
# We do a substitution '/' --> '_' in case there is a subfolder 'ses-0X/'
file_t2_ax="${SUBJECT//[\/]/_}"_acq-axial_T2w
file_t2_ax="${SUBJECT//[\/]/_}"_"${SESSION}"_acq-axial_T2w
# Check if file_t2_ax exists.
# Note: some subjects do not have T2w axial images. In this case, analysis will be stop after processing of
# T2w sagittal image.
Expand All @@ -140,16 +214,21 @@ if [[ ! -e ${file_t2_ax}.nii.gz ]]; then
echo "ERROR: File ${file_t2_ax}.nii.gz does not exist. Exiting."
exit 1
else
# -------------
# Segment SC (if SC segmentation file already exists under derivatives folder, it will be copied)
# -------------
segment_if_does_not_exist ${file_t2_ax} 't2'
file_t2_ax_seg=$FILESEG

# -------------
# Label SC
# TODO: consider moving the if statement inside a new function, e.g., `label_t2w_ax_if_does_not_exist`
# -------------
# Check if manual disc labels file already exists. If so, generate labeled segmentation from manual disc labels.
echo "Looking for manual disc labels: ${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${file_t2_ax}_labels-manual.nii.gz"
if [[ -e ${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${file_t2_ax}_labels-manual.nii.gz ]]; then
if [[ -e ${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${file_t2_ax}_labels-manual.nii.gz ]]; then
echo "Found! Using manual disc labels."
rsync -avzh ${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${file_t2_ax}_labels-manual.nii.gz ${file_t2_ax}_labels.nii.gz
rsync -avzh ${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${file_t2_ax}_labels-manual.nii.gz ${file_t2_ax}_labels.nii.gz

file_t2_ax_labels=${file_t2_ax}_labels
# If manual disc labels file does not exist, use disc labels from sagittal image
Expand All @@ -168,16 +247,60 @@ else
file_t2_ax_labels=${file_t2_sag_seg}_labeled_discs_reg
fi

# Label T2w axial spinal cord segmentation. Either using manual disc labels or using disc labels from sagittal image.
# Note: here we use sct_label_utils instead of sct_label_vertebrae to avoid SC straightening
# Context: https://github.com/spinalcordtoolbox/spinalcordtoolbox/pull/4072
sct_label_utils -i ${file_t2_ax_seg}.nii.gz -disc ${file_t2_ax_labels}.nii.gz -o ${file_t2_ax_seg}_labeled.nii.gz
# Label T2w axial spinal cord segmentation.
# Either using manual disc labels or using disc labels from sagittal image -- this is handled in the previous step.
# Note: we use `sct_label_vertebrae -discfile` to avoid cord straightening
# Details: https://github.com/spinalcordtoolbox/spinalcordtoolbox/pull/4896
sct_label_vertebrae -i ${file_t2_ax}.nii.gz -s ${file_t2_ax_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -c t2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migth also be worth testing using totalspineseg directly on the axial image and compare results!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that mean computing everything twice and comparing each metric obtained?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comparing getting the vertebral labels from the sagittal image vs getting it using totalspineseg directly on the axial, and doing a QC report comparing the quality of the labeling, is it more clear?

# Generate QC report to assess labeled segmentation
sct_qc -i ${file_t2_ax}.nii.gz -s ${file_t2_ax_seg}_labeled.nii.gz -p sct_label_vertebrae -qc ${PATH_QC} -qc-subject ${SUBJECT}

# Check if compression labels exists.
# -------------
# Compute spinal cord morphometrics across levels
# -------------
echo "Computing spinal cord morphometrics across vertebral levels..."
# Compute CSA, AP, RL, eccentricity and solidity across vertebral levels
sct_process_segmentation -i ${file_t2_ax_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -perlevel 1 -o ${PATH_RESULTS}/vertebral_level_metrics_cord.csv -append 1
# Normalized to PAM50
sct_process_segmentation -i ${file_t2_ax_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -normalize-PAM50 1 -perslice 1 -perlevel 1 -o ${PATH_RESULTS}/vertebral_level_metrics_cord_normalized.csv -append 1

# -------------
# Segment spinal canal if manual segmentation doesn't exists
# -------------
segment_canal_if_does_not_exist ${file_t2_ax} 't2'
file_t2_ax_canal_seg=$FILESEG

# -------------
# Compute spinal canal morphometrics across levels
# -------------
echo "Computing spinal canal morphometrics across vertebral levels..."
# Compute CSA, AP, RL across vertebral levels for canal segmentation
sct_process_segmentation -i ${file_t2_ax_canal_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -perlevel 1 -o ${PATH_RESULTS}/vertebral_level_metrics_canal.csv -append 1
# Normalized to PAM50
sct_process_segmentation -i ${file_t2_ax_canal_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -normalize-PAM50 1 -perslice 1 -perlevel 1 -o ${PATH_RESULTS}/vertebral_level_metrics_canal_normalized.csv -append 1

# -------------
# Compute aSCOR -- it needs both SC and canal segmentations
# -------------
sct_compute_ascor -i-SC ${file_t2_ax_seg}.nii.gz -i-canal ${file_t2_ax_canal_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -perlevel 1 -o ${PATH_RESULTS}/aSCOR_metrics.csv -append 1
# Normalized to PAM50
sct_compute_ascor -i-SC ${file_t2_ax_seg}.nii.gz -i-canal ${file_t2_ax_canal_seg}.nii.gz -discfile ${file_t2_ax_labels}.nii.gz -normalize-PAM50 1 -perslice 1 -perlevel 1 -o ${PATH_RESULTS}/aSCOR_metrics_normalized.csv -append 1

# -------------
# Segment intramedullary lesions if manual segmentation doesn't exists
# -------------
segment_lesion_if_does_not_exist ${file_t2_ax} 't2'
file_t2_ax_lesion_seg=$FILESEG
# Compute lesion metrics
echo "Computing lesion metrics..."
sct_analyze_lesion -m ${file_t2_ax_lesion_seg}_lesion_seg.nii.gz -s ${file_t2_ax_seg}.nii.gz -ofolder ${PATH_RESULTS}

# -------------
# Compute compression metrics
# -------------
# Check if file with compression labels exists.
file_compression="${file_t2_ax}_label-compression-manual"
FILE_COMPRESSION_MANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/anat/${file_compression}.nii.gz"
FILE_COMPRESSION_MANUAL="${PATH_DATA}/derivatives/labels/${SUBJECT}/${SESSION}/anat/${file_compression}.nii.gz"
if [[ ! -e ${FILE_COMPRESSION_MANUAL} ]]; then
echo "File ${FILE_COMPRESSION_MANUAL}.nii.gz does not exist" >> ${PATH_LOG}/missing_files.log
echo "ERROR: File ${FILE_COMPRESSION_MANUAL}.nii.gz does not exist. Exiting."
Expand All @@ -186,8 +309,8 @@ else
echo "Found! Using manual compression labels."
rsync -avzh $FILE_COMPRESSION_MANUAL ${file_compression}.nii.gz

# Fetch sex from participants.tsv file
sex=$(grep ${SUBJECT} ${PARTICIPANTS_PATH} | awk '{print $5}')
# Fetch sex from participants.tsv file (adaptated for new folder organization)
sex=$(grep ${SUBJECT} ${PARTICIPANTS_PATH} | awk '{print $4}')
echo "${SUBJECT}: ${sex}"

# TODO: test without angle correction too
Expand All @@ -204,7 +327,6 @@ else
sct_compute_compression -i ${file_t2_ax_seg}.nii.gz -vertfile ${file_t2_ax_seg}_labeled.nii.gz -l ${file_compression}.nii.gz -normalize-hc 1 -sex ${sex} -metric eccentricity -o ${PATH_RESULTS}/compression_metrics.csv
# solidity
sct_compute_compression -i ${file_t2_ax_seg}.nii.gz -vertfile ${file_t2_ax_seg}_labeled.nii.gz -l ${file_compression}.nii.gz -normalize-hc 1 -sex ${sex} -metric solidity -o ${PATH_RESULTS}/compression_metrics.csv

fi
fi
# ------------------------------------------------------------------------------
Expand Down