-
Notifications
You must be signed in to change notification settings - Fork 2
Update batch_single_subject.sh for the 2025 SCT Course
#33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5ca1c2a
bd48328
33fcc22
a6d80ca
06c0161
b31a5bd
bd6c4c1
0eb1aec
4ab8bbc
53665c3
3c42572
bfe9286
c98f50b
8812617
cd149da
60ebdeb
e51c8fb
dc8264a
aa88fb2
8719e48
fd6d63d
93cddf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| name: Compare SCT Commands | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| text_url: | ||
| description: "URL to text file (e.g. GitHub raw gist link)" | ||
| required: true | ||
| type: string | ||
|
|
||
| jobs: | ||
| compare: | ||
| runs-on: macos-latest | ||
| env: | ||
| YDIFF_OPTIONS: "--unified --pager=cat --color=always --width=120 --nowrap" | ||
|
|
||
| steps: | ||
| - name: Check out repo | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Install Python (for parsing script) | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Install `ydiff` # https://github.com/ymattw/ydiff | ||
| run: brew install ydiff | ||
|
|
||
| - name: Download remote text file | ||
| run: | | ||
| curl -L ${{ inputs.text_url }} -o remote.txt | ||
| echo "✅ Downloaded remote file:" | ||
| wc -l remote.txt | ||
| - name: Extract commands from remote file | ||
| run: | | ||
| python3 .github/workflows/scripts/extract_sct.py remote.txt -o remote_cmds.txt | ||
| sort -u remote_cmds.txt > remote_cmds_sorted.txt | ||
| echo "✅ Extracted $(wc -l < remote_cmds_sorted.txt) commands from remote file" | ||
| - name: Extract commands from local batch script | ||
| run: | | ||
| python3 .github/workflows/scripts/extract_sct.py single_subject/batch_single_subject.sh -o local_cmds.txt | ||
| sort -u local_cmds.txt > local_cmds_sorted.txt | ||
| echo "✅ Extracted $(wc -l < local_cmds_sorted.txt) commands from local script" | ||
| - name: Diff commands | ||
| run: | | ||
| echo "🔍 Diffing remote vs local..." | ||
| diff -u local_cmds_sorted.txt remote_cmds_sorted.txt > diff.txt || true | ||
| ydiff < diff.txt | ||
| - name: Upload results as artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: command-diff-output | ||
| path: | | ||
| remote_cmds_sorted.txt | ||
| local_cmds_sorted.txt | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import argparse | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def extract_sct_commands(paths, output=None): | ||
| results = [] | ||
|
|
||
| for path in paths: | ||
| with open(path, "r", encoding="utf-8") as f: | ||
| for line in f: | ||
| stripped = line.lstrip() | ||
| if stripped.startswith("# sct_"): | ||
| stripped = stripped[2:] | ||
| # Find relavent SCT commands to compare | ||
| if (stripped.startswith("sct_") | ||
| # sct commands must have command + arg + value (3) | ||
| # this excludes slide subtitles like "sct_slide ..." | ||
| and len(stripped.split(" ")) >= 3 | ||
| # exclude lines with <> which are likely placeholders | ||
| and not ("<" in stripped and ">" in stripped) | ||
| # exclude sct_download_data (data already present) | ||
| and not stripped.startswith("sct_download_data") | ||
| # exclude sct_run_batch (handled in .yml workflow) | ||
| and not stripped.startswith("sct_run_batch")): | ||
| results.append(stripped.rstrip()) | ||
|
|
||
| if output: | ||
| Path(output).write_text("\n".join(results), encoding="utf-8") | ||
| else: | ||
| print("\n".join(results)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| parser = argparse.ArgumentParser(description="Extract SCT commands " | ||
| "from TXT files.") | ||
| parser.add_argument("files", nargs="+", help="Input text files") | ||
| parser.add_argument("-o", "--output", help="Optional output file") | ||
| args = parser.parse_args() | ||
|
|
||
| extract_sct_commands(args.files, args.output) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,28 +63,32 @@ sct_deepseg -h | |
| # Vertebral labeling | ||
| # ====================================================================================================================== | ||
|
|
||
| # Vertebral labeling | ||
| sct_label_vertebrae -i t2.nii.gz -s t2_seg.nii.gz -c t2 -qc ~/qc_singleSubj | ||
| # Vertebral disc labeling | ||
| sct_deepseg spine -i t2.nii.gz -label-vert 1 -qc ~/qc_singleSubj | ||
|
|
||
| # Full spinal segmentation (Vertebrae, Intervertebral discs, Spinal cord and Spinal canal) | ||
| # Segment using totalspineseg | ||
| sct_deepseg spine -i t2.nii.gz -qc ~/qc_singleSubj | ||
| # Check results using FSLeyes | ||
| fsleyes t2.nii.gz -cm greyscale t2_step1_canal.nii.gz -cm YlOrRd -a 70.0 t2_step1_cord.nii.gz -cm YlOrRd -a 70.0 t2_totalspineseg_discs.nii.gz -cm subcortical -a 70.0 t2_step1_output.nii.gz -cm subcortical -a 70.0 t2_step2_output.nii.gz -cm subcortical -a 70.0 & | ||
| # Check QC report: Go to your browser and do "refresh". | ||
| # Note: Here, two files are output: t2_seg_labeled, which represents the labeled segmentation (i.e., the value | ||
| # corresponds to the vertebral level), and t2_seg_labeled_discs, which only has a single point for each | ||
| # inter-vertebral disc level. The convention is: Value 3 —> C2-C3 disc, Value 4 —> C3-C4 disc, etc. | ||
|
|
||
| # OPTIONAL: If automatic labeling did not work, you can initialize with manual identification of C2-C3 disc: | ||
| #sct_label_utils -i t2.nii.gz -create-viewer 3 -o label_c2c3.nii.gz -msg "Click at the posterior tip of C2/C3 inter-vertebral disc" | ||
| #sct_label_vertebrae -i t2.nii.gz -s t2_seg.nii.gz -c t2 -initlabel label_c2c3.nii.gz -qc ~/qc_singleSubj | ||
| # Optionally, you can use the generated disc labels to create a labeled segmentation | ||
| # Note: This approach is no longer recommended. Instead, use the disc labels directly in subsequent commands (e.g. `sct_process_segmentation`). | ||
| sct_label_vertebrae -i t2.nii.gz -s t2_seg.nii.gz -c t2 -discfile t2_totalspineseg_discs.nii.gz | ||
| # FIXME: Remove this command once the web tutorials are updated to no longer use labeled segmentations | ||
|
|
||
|
|
||
|
|
||
| # Shape-based analysis | ||
| # ====================================================================================================================== | ||
|
|
||
| # Compute cross-sectional area (CSA) of spinal cord and average it across levels C3 and C4 | ||
| sct_process_segmentation -i t2_seg.nii.gz -vert 3:4 -vertfile t2_seg_labeled.nii.gz -o csa_c3c4.csv | ||
| sct_process_segmentation -i t2_seg.nii.gz -vert 3:4 -discfile t2_totalspineseg_discs.nii.gz -o csa_c3c4.csv | ||
| # Aggregate CSA value per level | ||
| sct_process_segmentation -i t2_seg.nii.gz -vert 3:4 -vertfile t2_seg_labeled.nii.gz -perlevel 1 -o csa_perlevel.csv | ||
| sct_process_segmentation -i t2_seg.nii.gz -vert 3:4 -discfile t2_totalspineseg_discs.nii.gz -perlevel 1 -o csa_perlevel.csv | ||
| # Aggregate CSA value per slices | ||
| sct_process_segmentation -i t2_seg.nii.gz -z 30:35 -vertfile t2_seg_labeled.nii.gz -perslice 1 -o csa_perslice.csv | ||
| sct_process_segmentation -i t2_seg.nii.gz -z 30:35 -discfile t2_totalspineseg_discs.nii.gz -perslice 1 -o csa_perslice.csv | ||
|
|
||
| # A drawback of vertebral level-based CSA is that it doesn’t consider neck flexion and extension. | ||
| # To overcome this limitation, the CSA can instead be computed using the distance to a reference point. | ||
|
|
@@ -96,7 +100,7 @@ sct_process_segmentation -i t2_seg.nii.gz -pmj t2_pmj.nii.gz -pmj-distance 64 -p | |
|
|
||
| # The above commands will output the metrics in the subject space (with the original image's slice numbers) | ||
| # However, you can get the corresponding slice number in the PAM50 space by using the flag `-normalize-PAM50 1` | ||
| sct_process_segmentation -i t2_seg.nii.gz -vertfile t2_seg_labeled.nii.gz -perslice 1 -normalize-PAM50 1 -o csa_PAM50.csv | ||
| sct_process_segmentation -i t2_seg.nii.gz -discfile t2_totalspineseg_discs.nii.gz -perslice 1 -normalize-PAM50 1 -o csa_PAM50.csv | ||
|
|
||
|
|
||
|
|
||
|
|
@@ -127,24 +131,24 @@ sct_compute_compression -i t2_compressed_seg.nii.gz -vertfile t2_compressed_seg_ | |
| cd ../t2 | ||
|
|
||
| # Create labels at C3 and T2 mid-vertebral levels. These labels are needed for template registration. | ||
| sct_label_utils -i t2_seg_labeled.nii.gz -vert-body 3,9 -o t2_labels_vert.nii.gz | ||
| sct_label_utils -i t2_totalspineseg_discs.nii.gz -keep 3,9 -o t2_labels_vert.nii.gz | ||
| # Generate a QC report to visualize the two selected labels on the anatomical image | ||
| sct_qc -i t2.nii.gz -s t2_labels_vert.nii.gz -p sct_label_utils -qc ~/qc_singleSubj | ||
|
|
||
| # OPTIONAL: You might want to completely bypass sct_label_vertebrae and do the labeling manually. In that case, we | ||
| # provide a viewer to do so conveniently. In the example command below, we will create labels at the inter-vertebral | ||
| # discs C2-C3 (value=3), C3-C4 (value=4) and C4-C5 (value=5). | ||
| #sct_label_utils -i t2.nii.gz -create-viewer 3,4,5 -o labels_disc.nii.gz -msg "Place labels at the posterior tip of each inter-vertebral disc. E.g. Label 3: C2/C3, Label 4: C3/C4, etc." | ||
| # sct_label_utils -i t2.nii.gz -create-viewer 3,4,5 -o labels_disc.nii.gz -msg "Place labels at the posterior tip of each inter-vertebral disc. E.g. Label 3: C2/C3, Label 4: C3/C4, etc." | ||
|
|
||
| # Register t2->template. | ||
| sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -l t2_labels_vert.nii.gz -c t2 -qc ~/qc_singleSubj | ||
| sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -ldisc t2_labels_vert.nii.gz -c t2 -qc ~/qc_singleSubj | ||
| # Note: By default the PAM50 template is selected. You can also select your own template using flag -t. | ||
|
|
||
| # Register t2->template with modified parameters (advanced usage of `-param`) | ||
| sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -l t2_labels_vert.nii.gz -qc ~/qc_singleSubj -ofolder advanced_param -c t2 -param step=1,type=seg,algo=rigid:step=2,type=seg,metric=CC,algo=bsplinesyn,slicewise=1,iter=3:step=3,type=im,metric=CC,algo=syn,slicewise=1,iter=2 | ||
| sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -ldisc t2_labels_vert.nii.gz -qc ~/qc_singleSubj -ofolder advanced_param -c t2 -param step=1,type=seg,algo=rigid:step=2,type=seg,metric=CC,algo=bsplinesyn,slicewise=1,iter=3:step=3,type=im,metric=CC,algo=syn,slicewise=1,iter=2 | ||
|
|
||
| # Register t2->template with large FOV (e.g. C2-L1) using `-ldisc` option | ||
| # sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -ldisc t2_seg_labeled_discs.nii.gz -c t2 | ||
| # sct_register_to_template -i t2.nii.gz -s t2_seg.nii.gz -ldisc t2_totalspineseg_discs.nii.gz -c t2 | ||
|
Comment on lines
147
to
+151
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self re: spinalcordtoolbox/spinalcordtoolbox#5023 (comment): Not present in web tutorials? Add?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update: No. (This corresponds to the |
||
|
|
||
| # Register t2->template in compressed cord (example command) | ||
| # In case of highly compressed cord, the algo columnwise can be used, which allows for more deformation than bsplinesyn. | ||
|
|
@@ -226,6 +230,9 @@ sct_deepseg sc_lumbar_t2 -i t2_lumbar.nii.gz -qc ~/qc_singleSubj | |
| # sake of reproducing the results in the tutorial. | ||
| sct_label_utils -i t2_lumbar.nii.gz -create 27,76,187,17:27,79,80,60 -o t2_lumbar_labels.nii.gz -qc ~/qc_singleSubj | ||
|
|
||
| # generate a QC report for the lumbar labels | ||
| sct_qc -i t2_lumbar.nii.gz -s t2_lumbar_labels.nii.gz -p sct_label_utils -qc ~/qc_singleSubj | ||
|
|
||
| # Register the image to the template using segmentation and labels | ||
| sct_register_to_template -i t2_lumbar.nii.gz -s t2_lumbar_seg.nii.gz -ldisc t2_lumbar_labels.nii.gz -c t2 -qc ~/qc_singleSubj -param step=1,type=seg,algo=centermassrot:step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=3,slicewise=0:step=3,type=im,algo=syn,metric=CC,iter=3,slicewise=0 | ||
|
|
||
|
|
@@ -237,7 +244,7 @@ sct_register_to_template -i t2_lumbar.nii.gz -s t2_lumbar_seg.nii.gz -ldisc t2_l | |
| # Go to T2*-weighted data, which has good GM/WM contrast and high in-plane resolution | ||
| cd ../t2s | ||
| # Segment gray matter (check QC report afterwards) | ||
| sct_deepseg_gm -i t2s.nii.gz -qc ~/qc_singleSubj | ||
| sct_deepseg graymatter -i t2s.nii.gz -o t2s_gmseg.nii.gz -qc ~/qc_singleSubj | ||
| # Spinal cord segmentation | ||
| sct_deepseg spinalcord -i t2s.nii.gz -qc ~/qc_singleSubj | ||
| # Subtract GM segmentation from cord segmentation to obtain WM segmentation | ||
|
|
@@ -387,7 +394,7 @@ sct_smooth_spinalcord -i t1.nii.gz -s t1_seg.nii.gz | |
| # Tips: use flag "-sigma" to specify smoothing kernel size (in mm) | ||
|
|
||
| # Second-pass segmentation using the smoothed anatomical image | ||
| sct_deepseg_sc -i t1_smooth.nii.gz -c t1 -qc ~/qc_singleSubj | ||
| sct_deepseg spinalcord -i t1_smooth.nii.gz -qc ~/qc_singleSubj | ||
|
|
||
| # Align the spinal cord in the right-left direction using slice-wise translations. | ||
| sct_flatten_sagittal -i t1.nii.gz -s t1_seg.nii.gz | ||
|
|
@@ -414,23 +421,25 @@ sct_analyze_lesion -m t2_lesion_seg.nii.gz -s t2_sc_seg.nii.gz -qc ~/qc_singleSu | |
| # Lesion analysis using PAM50 (the -f flag is used to specify the folder containing the atlas/template) | ||
| # Note: You must go through the "Register to Template" steps (labeling, registration) first | ||
| # This is because `sct_warp_template` is required to generate the `label` folder used for `-f` | ||
| # sct_analyze_lesion -m t2_lesion_seg.nii.gz -s t2_sc_seg.nii.gz -f label -qc ~/qc_singleSubj | ||
| sct_warp_template -d t2.nii.gz -w ../t2/warp_template2anat.nii.gz | ||
| sct_analyze_lesion -m t2_lesion_seg.nii.gz -s t2_sc_seg.nii.gz -f label -qc ~/qc_singleSubj | ||
|
|
||
| # Segment the spinal cord on gradient echo EPI data | ||
| cd ../fmri/ | ||
| sct_deepseg sc_epi -i fmri_moco_mean.nii.gz -qc ~/qc_singleSubj | ||
| # Crop extraneous tissue using the t2-based mask generated earlier | ||
| sct_crop_image -i fmri_moco_mean.nii.gz -m mask_fmri.nii.gz -b 0 | ||
| # Segment the cord using the cropped image | ||
| sct_deepseg sc_epi -i fmri_moco_mean_crop.nii.gz -qc ~/qc_singleSubj | ||
|
|
||
| # Canal segmentation | ||
| cd ../t2 | ||
| sct_deepseg sc_canal_t2 -i t2.nii.gz -qc ~/qc_singleSubj | ||
| # Check results using FSLeyes | ||
| fsleyes t2.nii.gz -cm greyscale t2_canal_seg_seg.nii.gz -cm red -a 70.0 & | ||
|
|
||
| # Full spinal segmentation (Vertebrae, Intervertebral discs, Spinal cord and Spinal canal) | ||
| # Segment using totalspineseg | ||
| sct_deepseg totalspineseg -i t2.nii.gz -qc ~/qc_singleSubj | ||
| # Check results using FSLeyes | ||
| fsleyes t2.nii.gz -cm greyscale t2_step1_canal.nii.gz -cm YlOrRd -a 70.0 t2_step1_cord.nii.gz -cm YlOrRd -a 70.0 t2_step1_levels.nii.gz -cm subcortical -a 70.0 t2_step1_output.nii.gz -cm subcortical -a 70.0 t2_step2_output.nii.gz -cm subcortical -a 70.0 & | ||
| # Compute aSCOR (Adapted Spinal Cord Occupation Ratio) | ||
| # i.e. Spinal cord to canal ratio using the canal seg | ||
| sct_compute_ascor -i-SC t2_seg.nii.gz -i-canal t2_canal_seg.nii.gz -perlevel 1 -o ascor.csv | ||
|
Comment on lines
+440
to
+442
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self re: spinalcordtoolbox/spinalcordtoolbox#5023 (comment): Not present in web tutorials? Add? |
||
|
|
||
| # Segment the spinal nerve rootlets | ||
| sct_deepseg rootlets -i t2.nii.gz -qc ~/qc_singleSubj | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.