A few questions regarding the use of sct for Quantitative Analysis of Spinal Cord MRI

Hi,

  1. I’m planning to use sct_deepseg_sc + sct_label_vertebrae + sct_maths + sct_process_segmentation + sct_analyze_lesion to get the Spinal cord Cross-sectional area and volume separated to cervical and thoracic area. I have axial, sagittal and sagittal stir T2 scans. Which series is the best for getting the most reliable results?

  2. Is there an easy way to use the disks positions from sct_label_vertebrae results on cervical (or survey) scan, to initialize disks positions of the first thoracic vertebrae for using sct_label_vertebrae on thoracic scan, if the two scans were taken together?

  3. Similarly I want to use the above tools with sct_deepseg_gm to get the gray matter Cross-sectional area and volume separated to cervical and thoracic area. Is there a way to do this using my t2 MRI scans or it must be a t2*?

  4. Is there a way to use the -remove-small parameter with sct_deepseg_lesion (like in sct_deepseg) to filter out lesions smaller than 30 mm3?

Thanks a lot,
Juda

Hi @juda,

Thank you for reaching out!

I’m planning to use sct_deepseg_sc + sct_label_vertebrae + sct_maths + sct_process_segmentation + sct_analyze_lesion to get the Spinal cord Cross-sectional area and volume separated to cervical and thoracic area. I have axial, sagittal and sagittal stir T2 scans. Which series is the best for getting the most reliable results?

Axial scans are definitely better to compute cross-sectional area of the spinal cord, given the highest in-plane resolution of axial scans. However, vertebral labeling is better achieved with sagittal (or isotropic resolution) scans. So, one thing you could do is vertebral labeling on the sagittal scan, and then resample without registration (sct_register_multimodal with option -identity 1) the vertebral labeling onto the axial scan for subsequent quantification with sct_process_segmentation.

Moreover, if your goal is only to compute CSA, then you don’t need sct_maths and sct_analyze_lesion. See the batch_processing example as well as the tutorials to get some inspirations about the various possibilities.

Is there an easy way to use the disks positions from sct_label_vertebrae results on cervical (or survey) scan, to initialize disks positions of the first thoracic vertebrae for using sct_label_vertebrae on thoracic scan, if the two scans were taken together?

Great question. Yes: if your cervical and thoracic sagittal scans consistently overlap across your participants, let’s say at disc C7-T1, then you can create a disc labeling initialization file from the output of the labeling on the cervical scan, and use that initialization file for labeling the thoracic scan. It would look like this:

# Label cervical scan
sct_label_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -initlabel label_C7T1.nii.gz -c <CONTRAST>
# Only keep one disc label at C7-T1
sct_label_utils -i t2_seg_labeled_discs.nii.gz -keep 8 -o label_C7T1.nii.gz
# Label thoracic scan
sct_label_vertebrae -i sag_thoracic.nii.gz -s sag_thoracic_seg.nii.gz -initlabel label_C7T1.nii.gz -c <CONTRAST>

For reference, here is the convention for label values.

Similarly I want to use the above tools with sct_deepseg_gm to get the gray matter Cross-sectional area and volume separated to cervical and thoracic area. Is there a way to do this using my t2 MRI scans or it must be a t2*?

If there is sufficient contrast to see the gray matter with your naked eyes, then the deep learning algorithm should be able to do it as well. Give it a try!

Is there a way to use the -remove-small parameter with sct_deepseg_lesion (like in sct_deepseg) to filter out lesions smaller than 30 mm3?

There is currently no such option, however we are currently working on an upgraded version of sct_deepseg_lesion (which will soon be deprecated). This new function will have that option. One thing you can do for now is to filter out in your statistical analysis: sct_analyze_lesion gives you the volume of each lesion, so you can filter out rows of your output CSV file based on an arbitrary threshold.

Thank you very much @jcohenadad for the answers. I’ll implement it in my code and comment here if any complications.
Best regards,
Juda

1 Like

Hi @jcohenadad ,
I had a few unexpected results while I was trying to implement your code:

  1. When I run the following commands to get the vertebrae labels it has a problem detecting disk 5:
sct_deepseg_sc -i sag_cervical.nii.gz -c t2 -qc "$QCF"
sct_label_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -c t2 -qc "$QCF"

  1. When I tried to manually fix the labels with the following commands I got en error:
    command:
sct_label_utils -i sag_cervical.nii.gz -create-viewer 3,4,5 -o labels_disc.nii.gz -qc "$QCF" -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_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -c t2 -qc "$QCF" -initlabel labels_disc.nii.gz

I got the error:

Get z and disc values from straight label...
.. [273, 0]
Look for template...
Path template: /home/yael/sct_5.4/data/PAM50
Open template and vertebral levels...
Disc values from template: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Z-values for each disc: [963, 938, 907, 870, 833, 800, 769, 735, 692, 646, 600, 551, 500, 449, 396, 342, 289, 231, 168, 104, 79]
Distances between discs (in voxel): [25.0, 31.0, 37.0, 37.0, 33.0, 31.0, 34.0, 43.0, 46.0, 46.0, 49.0, 51.0, 51.0, 53.0, 54.0, 53.0, 58.0, 63.0, 64.0, 25.0]
Detect intervertebral discs...
Current disc: -1 (z=273). Direction: superior
.. Peak found: z=8 (correlation = 0.2358752778730007)
Traceback (most recent call last):
  File "/home/yael/sct_5.4/spinalcordtoolbox/vertebrae/core.py", line 206, in vertebral_detection
    approx_distance_to_next_disc = list_distance[list_disc_value_template.index(current_disc - 1)]
ValueError: -2 is not in list

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/yael/sct_5.4/spinalcordtoolbox/scripts/sct_label_vertebrae.py", line 485, in <module>
    main(sys.argv[1:])
  File "/home/yael/sct_5.4/spinalcordtoolbox/scripts/sct_label_vertebrae.py", line 422, in main
    verbose=verbose, path_template=path_template, path_output=path_output, scale_dist=scale_dist)
  File "/home/yael/sct_5.4/spinalcordtoolbox/vertebrae/core.py", line 208, in vertebral_detection
    logger.warning('Disc value not included in template. Using previously-calculated distance: %s', approx_distance_to_next_disc)
UnboundLocalError: local variable 'approx_distance_to_next_disc' referenced before assignment

Same happened when I was trying to get the vertebrae labels for t2 sagittal stir scan:

sct_deepseg_sc -i sag_stir_cervical.nii.gz -c t2 -qc "$QCF"
sct_label_vertebrae -i sag_stir_cervical.nii.gz -s sag_stir_cervical_seg.nii.gz -c t2 -qc "$QCF" -initlabel labels_disc.nii.gz

I got the error:

Get z and disc values from straight label...
.. [295, 0]
Look for template...
Path template: /home/yael/sct_5.4/data/PAM50
Open template and vertebral levels...
Disc values from template: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Z-values for each disc: [963, 938, 907, 870, 833, 800, 769, 735, 692, 646, 600, 551, 500, 449, 396, 342, 289, 231, 168, 104, 79]
Distances between discs (in voxel): [25.0, 31.0, 37.0, 37.0, 33.0, 31.0, 34.0, 43.0, 46.0, 46.0, 49.0, 51.0, 51.0, 53.0, 54.0, 53.0, 58.0, 63.0, 64.0, 25.0]
Detect intervertebral discs...
Current disc: -1 (z=295). Direction: superior
.. Peak found: z=-5 (correlation = 0.2045552821852658)
Traceback (most recent call last):
  File "/home/yael/sct_5.4/spinalcordtoolbox/vertebrae/core.py", line 206, in vertebral_detection
    approx_distance_to_next_disc = list_distance[list_disc_value_template.index(current_disc - 1)]
ValueError: -2 is not in list

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/yael/sct_5.4/spinalcordtoolbox/scripts/sct_label_vertebrae.py", line 485, in <module>
    main(sys.argv[1:])
  File "/home/yael/sct_5.4/spinalcordtoolbox/scripts/sct_label_vertebrae.py", line 422, in main
    verbose=verbose, path_template=path_template, path_output=path_output, scale_dist=scale_dist)
  File "/home/yael/sct_5.4/spinalcordtoolbox/vertebrae/core.py", line 208, in vertebral_detection
    logger.warning('Disc value not included in template. Using previously-calculated distance: %s', approx_distance_to_next_disc)
UnboundLocalError: local variable 'approx_distance_to_next_disc' referenced before assignment

  1. When I was trying to run sct_register_multimodal it seems like it is in the right direction but the results still need some improvements:
sct_register_multimodal -i sag_cervical_seg_labeled.nii.gz -d ax_cervical.nii.gz -identity 1 -o ax_cervical_seg_labeled.nii.gz

I’ll appreciate your further help regarding those commands.
I’ll send you the original files by email.

Thanks a lot,
Juda

Hi,

In the message above I advised to create only one label, with the flag -initlabel. If you wish to use more than one label (as you did above), then you should label all visible discs and use the flag -discfile. The usage of sct_label_vertebrae shows:

  -initlabel <file>    Initialize vertebral labeling by providing a nifti file that has a single
                       disc label. An example of such file is a single voxel with value '3', which
                       would be located at the posterior tip of C2-C3 disc. Such label file can be
                       created using: sct_label_utils -i IMAGE_REF -create-viewer 3 ; or by using
                       the Python module 'detect_c2c3' implemented in
                       'spinalcordtoolbox/vertebrae/detect_c2c3.py'.
  -discfile <file>     File with disc labels, which will be used to transform the input segmentation
                       into a vertebral level file. In that case, there will be no disc detection.
                       The convention for disc labels is the following: value=3 -> disc C2/C3,
                       value=4 -> disc C3/C4, etc.

So, in your case, I suggest you manually label all visible discs (probably easier/more reliable) and use flag -discfile, instead of only labeling one disc (less reliable) and use the flag -initlabel.

@joshuacwnewton we should probably consider getting rid of either -initlabel or -discfile to avoid confusion.

1 Like

I’ve opened an issue for this here: Fold `-initlabel` functionality into the `-discfile` option · Issue #3528 · spinalcordtoolbox/spinalcordtoolbox · GitHub :slightly_smiling_face:

1 Like

Thank you very much @jcohenadad for the answers. regarding my 3’rd question, am I doing something wrong, or is there a way to improve the results?

Thank you so much,
Juda

It is very difficult for me to assess what’s going on if I cannot see the data. Maybe you can share the input images (sag_cervical_seg_labeled, ax_cervical)?

Hi @jcohenadad
I forgot to mention that sag_cervical_seg_labeled is the results of running:

sct_deepseg_sc -i sag_cervical.nii.gz -c t2 -qc "$QCF"
sct_label_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -c t2 -qc "$QCF"

sag_cervical.nii.gz and ax_cervical.nii.gz is the files I sent you by email. I’ll send you all the files including sag_cervical_seg_labeled when I’ll be in the lab.

Thanks a lot,
Juda

Hi,

Sorry, I receive too many emails and I didn’t make the connection between your name here (juda) and your name from the email (Yehouda). So, based on the files you sent and my understanding of what you would like to do, below is a series of commands, showing their respective outputs.

# Segment spinal cord on the sagittal image
sct_deepseg_sc -i sag_cervical.nii.gz -c t2 -qc qc
# Manually label discs
sct_label_utils -i sag_cervical.nii.gz -create-viewer 2:11 -o labels_disc.nii.gz -qc qc -msg "Place labels at the posterior tip of each inter-vertebral disc. E.g. Label 3: C2/C3, Label 4: C3/C4, etc."
# Label vertebrae using disc file
sct_label_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -c t2 -discfile labels_disc.nii.gz -qc qc

Here is the manual labeling window:

Here is the output of the labeled vertebrae:

# Create mask around spinal cord
sct_create_mask -i sag_cervical.nii.gz -p centerline,sag_cervical_seg.nii.gz -size 35mm -f cylinder -o mask_cord.nii.gz

# Bring the mask in the axial space
sct_register_multimodal -i mask_cord.nii.gz -d ax_cervical.nii.gz -identity 1 -x nn
# Register the sagittal onto the axial image, using the mask and the slicereg algorithm
sct_register_multimodal -i sag_cervical.nii.gz -d ax_cervical.nii.gz -m mask_cord_reg.nii.gz -param step=1,type=im,algo=slicereg,metric=CC,poly=2,smooth=1

anim

:information_source: ‎ The spinal cord registration is not perfect, but in this case this is not a problem because the purpose is only to bring the vertebral levels to the axial space.

# Apply warping field to vertebral labeling
sct_apply_transfo -i sag_cervical_seg_labeled.nii.gz -d ax_cervical.nii.gz -w warp_sag_cervical2ax_cervical.nii.gz -x nn 

# Segment spinal cord on the axial image
sct_deepseg_sc -i ax_cervical.nii.gz -c t2 -qc qc

# Compute CSA across vertebral levels between C3 and C7
sct_process_segmentation -i ax_cervical_seg.nii.gz -vertfile sag_cervical_seg_labeled_reg.nii.gz -perlevel 1 -vert 3:7 -o csa.csv
Slice (I->S) VertLevel DistancePMJ MEAN(area)
12:14 7 53.55502841008330
15:17 6 62.566087988167400
18:20 5 66.00845696353210
21:23 4 61.46203804828000
24:26 3 57.50141604927090

csa.csv (2.6 KB)

Thank you so much @jcohenadad for your answers.
The results are much better after implementing your suggestions. I make a little change in an attempt to achieve better resolution for the vertebrae labels in the axial image. I applied the warping field transformation to the disks labels (‘sag_cervical_seg_labeled_discs.nii.gz’ file) instead of the vertebrae labels from the sagittal image (‘sag_cervical_seg_labeled.nii.gz’ file). Then I used sct_label_vertebrae to get the vertebrae labels. I changed also the parameter ‘x’ in sct_apply_transfo to ‘label’ accordingly. I’m posting here the code:

# Segment spinal cord on the sagittal image
sct_deepseg_sc -i sag_cervical.nii.gz -c t2 -qc "$QC"
# Manually label discs
sct_label_utils -i sag_cervical.nii.gz -create-viewer 1:9 -o sag_cervical_discfile.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."
# Label vertebrae using disc file
sct_label_vertebrae -i sag_cervical.nii.gz -s sag_cervical_seg.nii.gz -c t2 -discfile sag_cervical_discfile.nii.gz -qc "$QC"
# Create mask around spinal cord
sct_create_mask -i sag_cervical.nii.gz -p centerline,sag_cervical_seg.nii.gz -size 35mm -f cylinder -o sag_cervical_mask_cord.nii.gz
# Bring the mask in the axial space
sct_register_multimodal -i sag_cervical_mask_cord.nii.gz -d ax_cervical.nii.gz -identity 1 -x nn
# Register the sagittal onto the axial image, using the mask and the slicereg algorithm
sct_register_multimodal -i sag_cervical.nii.gz -d ax_cervical.nii.gz -m sag_cervical_mask_cord_reg.nii.gz -param step=1,type=im,algo=slicereg,metric=CC,poly=2,smooth=1
# Apply warping field to sagittal labeled discs
sct_apply_transfo -i sag_cervical_seg_labeled_discs.nii.gz -d ax_cervical.nii.gz -w warp_sag_cervical2ax_cervical.nii.gz -x label -o ax_cervical_discfile.nii.gz
# Segment spinal cord on the axial image
sct_deepseg_sc -i ax_cervical.nii.gz -c t2 -qc "$QC"
# Label vertebrae using disc file
sct_label_vertebrae -i ax_cervical.nii.gz -s ax_cervical_seg.nii.gz -c t2 -discfile ax_cervical_discfile.nii.gz -qc "$QC"
# Compute CSA across vertebral levels between C3 and C7
sct_process_segmentation -i ax_cervical_seg.nii.gz -vertfile ax_cervical_seg_labeled.nii.gz -perlevel 1 -vert 3:7 -o ax_cervical_csa.csv

# Segment spinal cord on the sagittal image
sct_deepseg_sc -i sag_thoracic.nii.gz -c t2 -qc "$QC"
# Only keep one disc label at T1-T2
sct_label_utils -i sag_cervical_seg_labeled_discs.nii.gz -keep 9 -o sag_cervical_label_T1T2.nii.gz
# Register the disc label at T1-T2 onto the thoracic image
sct_register_multimodal -i sag_cervical_label_T1T2.nii.gz -d sag_thoracic.nii.gz -identity 1 -x nn -o sag_thoracic_label_T1T2.nii.gz
# Manually label discs
sct_label_utils -i sag_thoracic.nii.gz -create-viewer 8:20 -ilabel sag_thoracic_label_T1T2.nii.gz -o sag_thoracic_discfile.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."
# Label thoracic scan using disc file
sct_label_vertebrae -i sag_thoracic.nii.gz -s sag_thoracic_seg.nii.gz -c t2 -discfile sag_thoracic_discfile.nii.gz -qc "$QC"
# sct_label_vertebrae -i sag_thoracic.nii.gz -s sag_thoracic_seg.nii.gz -initlabel sag_cervical_label_T1T2.nii.gz -c t2
# Create mask around spinal cord
sct_create_mask -i sag_thoracic.nii.gz -p centerline,sag_thoracic_seg.nii.gz -size 35mm -f cylinder -o sag_thoracic_mask_cord.nii.gz
# Bring the mask in the axial space
sct_register_multimodal -i sag_thoracic_mask_cord.nii.gz -d ax_thoracic.nii.gz -identity 1 -x nn
# Register the sagittal onto the axial image, using the mask and the slicereg algorithm
sct_register_multimodal -i sag_thoracic.nii.gz -d ax_thoracic.nii.gz -m sag_thoracic_mask_cord_reg.nii.gz -param step=1,type=im,algo=slicereg,metric=CC,poly=2,smooth=1
# Apply warping field to vertebral labeling
sct_apply_transfo -i sag_thoracic_seg_labeled_discs.nii.gz -d ax_thoracic.nii.gz -w warp_sag_thoracic2ax_thoracic.nii.gz -x label -o ax_thoracic_discfile.nii.gz
# Segment spinal cord on the axial image
sct_deepseg_sc -i ax_thoracic.nii.gz -c t2 -qc "$QC"
# Label vertebrae using disc file
sct_label_vertebrae -i ax_thoracic.nii.gz -s ax_thoracic_seg.nii.gz -c t2 -discfile ax_thoracic_discfile.nii.gz -qc "$QC"
# Compute CSA across vertebral levels between T1 and T12
sct_process_segmentation -i ax_thoracic_seg.nii.gz -vertfile ax_thoracic_seg_labeled.nii.gz -perlevel 1 -vert 8:19 -o ax_thoracic_csa.csv

This is the results before and after the change.
before:


after:

Thank you so much, I appreciate your help a lot,
Juda

1 Like

Dear @jcohenadad

After implementing your code and running on a few scans, I have a problem with one of the scans. When I run sct_label_vertebrae with discfile there is some voxels in the spinal cord segmentation, but not in the vertebrae label and there is some voxels in the vertebrae labels, that are not in the spinal cord segmentation. Is there a way to fix this issue? It cause the calculated volume and csa to be wrong.
I’ll send you the files via email.
Thank you very much,
Yehuda

Here is some examples:

this is my code:

sct_label_vertebrae -i "${SUBJECT}_t2_ax_cervical.nii.gz" -s "${SUBJECT}_t2_ax_cervical_seg.nii.gz" -c t2 -discfile "${SUBJECT}_t2_ax_cervical_discfile.nii.gz" -qc "${PATH_QC}"

This is the full sh file:
process_data.sh (17.2 KB)

Hi,

I’ve looked at the data you sent me, and the T2 axial image is highly corrupted/artifacted. As you can see below, the spinal cord is not ‘continuous’:

I suspect there was an extremely high amount of motion on this 2D scan, although I am surprised to not see more ghosting. Are you 100% certain these data were not processed after converting them from DICOM to NIfTI?

Best,
Julien

Thank you @jcohenadad for your help. I’ve checked today the original dicom files and there are some slices that are not continue with the one blow or above slice, as you said. I use dcm2niix to convert the dicom files.
Is there a way to get better CSA results on images like this? Is it possible to make the vertebrae labels output of sct_label_vertebrae match the vertebrae segmentation of all the slices that are between the corresponding labels from the diskfile (for the axial scan)?

Thanks a lot
Yehuda

For this particular subject, you could run the segmentation and compute CSA by selecting the slices you trust (you can ‘see’ the discs on the axial image so you can figure out what vertebral levels they correspond to)

1 Like

Dear @jcohenadad

I have another question about sct_analyze_lesion. I’m trying to use Is the column “length [mm]” for my research but lot of the values I get are negative. Am I doing something wrong?

For example here:

My code is

sct_analyze_lesion -m sub-56027_ses-20180207_t2_sag_cervical_lesionseg.nii.gz -s sub-56027_ses-20180207_t2_sag_cervical_seg.nii.gz -i sub-56027_ses-20180207_t2_sag_cervical.nii.gz -f sub-56027_ses-20180207_t2_sag_cervical_templates

I’ll send you the original files on Email.

Thanks a lot,
Yehuda

Hi Yehuda,

Could you please post this latest issue in another new thread, with the appropriate title (eg: Negative values with sct_analyze_lesion). Because having multiple unrelated issues under the same thread makes it harder for people to find answer to specific issues. Then you can indeed send me the files via email and I will look into it.

Thank you

1 Like

Thanks @jcohenadad you’re completely right. I posted a new thread here Negative values with sct_analyze_lesion .