-
Notifications
You must be signed in to change notification settings - Fork 2
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
Obtain spinal levels from nerve rootlets segmentation #13
Comments
Raphaëlle Schlienger noticed that some subjects have nerve rootlets segmentation quite far from the spinal cord, which thus leads to "short" spinal levels. |
@jcohenadad proposed an idea to take the highest Z value (i.e., in superior-inferior direction) found for each spinal nerve rootlet level. Given that rootlets are oriented caudally as they go away from the cord (see image below), the most rostral point of the segmented rootlets should be the closest to the actual spinal level.
But, this might not work for C1 and C2 as these rootlets are "upside down" compared to the other rootlets. |
This means you would be taking the highest Z-value to define the start of the spinal level. Hence some activity linked to that spinal level may occur above the highest Z-value observed for the corresponding rootlet. And with a precision of 0.8mm iso as in this T2 image used as reference (on the left), we can't be sure we are ultimately observing the highest point of entry. Maybe the spinal levels should thus start a bit above this highest Z-value to account for this uncertainty? |
Yes, I agree. This is precisely the reasoning for selecting the highest z-value (vs. doing the dilation). Even though the "highest z-value" is not perfect because as you pointed out, we don't exactly know where the dorsal entries synapse inside the cord, this is the closest "bet" we can make given the coarse resolution of MRI.
Sure, but what is "a bit above"? We would need some quantitative evidence to come up with a model that would systematically "shift up" the suggested spinal levels. Without this quantitative evidence, IMHO the best we can do is to rely on what we "see" on the MRI. Maybe the Mendez et al., 2021 paper (see spinalcordtoolbox/PAM50#3) includes such useful information? E.g. exact location of spinal levels based on the visualization of the nerve rootlets visible before they enter the spinal cord. @sandrinebedard might have some insights about this |
In the Mendez we have Width Across Dorsal and Ventral Columns (between the nerve root entry) and you have the angles of rostral and caudal rootlets per level, maybe with that you could compute the extra z you should add! However, you need to consider that the angles of the rootlets changes with flexion/extension of the head, so it may not be accurate. |
Brilliant idea! That would be very cool indeed. Though it might not be super robust to estimate an angle based on a very noisy segmentation. What might be much more robust would be to fit a geometric model of the rootlets to the segmented labels. For example, we could segment the rootlets in the PAM50 space. Then, for every new subject, do:
|
I am working on the solution described in the previous comment. So far, I have:
file_t2=sub-007_ses-headNormal_T2w
# Segment spinal cord
sct_deepseg_sc -i ${file_t2}.nii.gz -c t2 -qc qc -qc-subject ${file_t2}
# Create mid-vertebral labels in the cord for vertebrae C3 and C5
sct_label_vertebrae -i ${file_t2}.nii.gz -s ${file_t2}_seg.nii.gz -c t2 -qc qc -qc-subject ${file_t2}
sct_label_utils -i ${file_t2}_seg_labeled.nii.gz -vert-body 3,5 -o ${file_t2}_seg_labeled_vertbody_35.nii.gz -qc qc -qc-subject ${file_t2}
# Register T2-w image to PAM50 template
sct_register_to_template -i ${file_t2}.nii.gz -s ${file_t2}_seg.nii.gz -l ${file_t2}_seg_labeled_vertbody_35.nii.gz -c t2 -param step=1,type=seg,algo=centermassrot:step=2,type=seg,algo=syn,slicewise=1,smooth=0,iter=5:step=3,type=im,algo=syn,slicewise=1,smooth=0,iter=3 -qc qc -qc-subject ${file_t2}
# Rename output for clarity
mv anat2template.nii.gz ${file_t2}_reg.nii.gz
# Bring rootlets segmentation from native space to PAM50 space
# Note: Nearest-neighbor interpolation (-x nn) must be used to preserve the level-wise rootlets segmentation values
sct_apply_transfo -i ${file_t2}_label-rootlet_staple.nii.gz -d $SCT_DIR/data/PAM50/template/PAM50_t2.nii.gz -w warp_anat2template.nii.gz -x nn Legend: red - PAM50 rootlets, blue - native rootlets registered to PAM50 Now, I am working on the label-wise registration between PAM50 rootlets and subject rootlets. TODO - add details. |
I also think this should be debated during SCT's course "discussion period" I've added a "discussion needed" in spinalcordtoolbox/spinalcordtoolbox#4186 |
Following up on my previous comment, I am currently working on:
Since file_t2=sub-007_ses-headNormal_T2w
$SCT_DIR/bin/isct_antsRegistration --dimensionality 3 --float 0 \
--output [registration2_,${file_t2}_label-rootlet_staple_reg_reg.nii.gz] --interpolation nearestNeighbor --verbose 1 \
--transform Affine[5] --metric MeanSquares[PAM50_t2_label-rootlet.nii.gz,${file_t2}_label-rootlet_staple_reg.nii.gz,1,32] --convergence 20x10 --shrink-factors 2x1 --smoothing-sigmas 0x0mm --shrink-factors 1x1 --restrict-deformation 0x0x1 \
--transform BSplineSyN[0.1,3,0] --metric MeanSquares[PAM50_t2_label-rootlet.nii.gz,${file_t2}_label-rootlet_staple_reg.nii.gz,1,32] --convergence 10x5 --shrink-factors 2x1 --smoothing-sigmas 0x0mm --shrink-factors 1x1 --restrict-deformation 0x0x1 Notice the shift in S-I direction between the blue (before the Tz-only registration) and green (after the Tz-only registration). The green is now much closer to red (PAM50 rootlets)! Legend: red - PAM50 rootlets, blue - native rootlets registered to PAM50, green - native rootlets registered to PAM50 after the second (Tz-only) registration |
Continuing:
file_t2=sub-007_ses-headNormal_T2w
sct_concat_transfo -d ${file_t2}.nii.gz -w registration2_1InverseWarp.nii.gz warp_template2anat.nii.gz -o warp_final.nii.gz
cd ~/code/PAM50
git pull
git checkout jca/16-spinal-levels
sct_apply_transfo -i ~/code/PAM50/template/PAM50_spinal_levels.nii.gz -d ${file_t2}.nii.gz -w warp_final.nii.gz -x nn Resulting native image and native rootlets with registered |
omg this is amazing, great job @valosekj 🏅 👏!!! I could watch that video for hours. Let's make a similar one for SCT course where you go back-and-forth along AP in the coronal plane. |
erratum to my previous point about warping field concatenation:
I realized that I was not using the affine matrix Now, when manually including the affine in the # Note that `-winv` is used to invert the affine transform
$ sct_concat_transfo -d ${file_t2}.nii.gz -w registration2_0GenericAffine.mat registration2_1InverseWarp.nii.gz warp_template2anat.nii.gz -o warp_final_including_affine.nii.gz -winv registration2_0GenericAffine.mat And warping PAM50 spinal levels ( $ sct_apply_transfo -i ~/code/PAM50/template/PAM50_spinal_levels.nii.gz -d ${file_t2}.nii.gz -w warp_final_including_affine.nii.gz -x nn -o PAM50_spinal_levels_reg_with_affine.nii.gz The warped PAM50 levels ( But it seems to me that the spinal levels are now too high relative to the vertebrae. See, for example, the C4 level (pink color with the cursor). After the shift up, the C4 level is at the level of disc C2/C3. But it should instead be located at the level of vertebra C3; see illustration here. |
Why do you include two times
I would do:
|
Based on the following
This is also confirmed by the $ sct_concat_transfo -d ${file_t2}.nii.gz -w registration2_0GenericAffine.mat registration2_1InverseWarp.nii.gz warp_template2anat.nii.gz -o warp_final_including_affine.nii.gz -winv registration2_0GenericAffine.mat
...
Parse list of warping fields...
Check file existence...
OK: sub-007_ses-headNormal_T2w.nii.gz
OK: registration2_0GenericAffine.mat
OK: registration2_1InverseWarp.nii.gz
OK: warp_template2anat.nii.gz
/Users/valosek/code/sct_latest/bin/isct_ComposeMultiTransform 3 warp_final.nii.gz -R sub-007_ses-headNormal_T2w.nii.gz warp_template2anat.nii.gz registration2_1InverseWarp.nii.gz -i registration2_0GenericAffine.mat # in /Users/valosek/data/data.neuro.polymtl.ca/rootlets_seg/register_to_PAM50
When trying this command, I got an error about missing $ sct_concat_transfo -d ${file_t2}.nii.gz -winv registration2_0GenericAffine.mat -w registration2_1InverseWarp.nii.gz warp_template2anat.nii.gz -o warp_final_including_affine_fixed.nii.gz However, ...
Parse list of warping fields...
Check file existence...
OK: sub-007_ses-headNormal_T2w.nii.gz
OK: registration2_1InverseWarp.nii.gz
OK: warp_template2anat.nii.gz
/Users/valosek/code/sct_latest/bin/isct_ComposeMultiTransform 3 warp_final.nii.gz -R sub-007_ses-headNormal_T2w.nii.gz warp_template2anat.nii.gz registration2_1InverseWarp.nii.gz # in /Users/valosek/data/data.neuro.polymtl.ca/rootlets_seg/register_to_PAM50 |
Hey Jan, these advances are really exciting ! I also think you have to add
Have you tried : Let me know how this goes ! |
Thank you, Raphaelle!
Yes, I have tried! And indeed, this is the right command to concatenate all transformations. In other words, For clarity, I put all the commands discussed in this thread into a bash script (see here). However, I noticed that the label-wise Tz-only registration does not work well in some subjects --> I'm investigating that! |
Closing this issue as the follow-up investigation continues in #78. |
UPDATE by @valosekj -- I updated the issue description to be in line with #18.
The model predicts spinal nerve rootlets segmentation; see here. The predicted spinal nerve rootlets can be used to obtain the spinal levels using the utilities/rootlets_to_spinal_levels.py script using the following procedure:
-dilate
input argument)Individual steps are illustrated in this Google Slides presentation.
To validate the result, we compared the results of this prediction with Cadotte et al., 2017 values using the same MRI images.
Cadotte data have labeled images with PMJ (pontomedullary junction), vertebrae and DREZ (dorsal root entry zone) labeled in the same file. I used separate_cadotte_label.py to create 3 files: one for the PMJ, one for the DREZ, and one for vertebrae.
I've used cadotte_value notebook to create cadotte_dist.csv
The results of the prediction on Cadotte are not really good #11.
**Cadotte paper graph**
**Reproduced graph**
Legend: Left = Cadotte DREZ label, Middle = Cadotte label vertebrae, Right = M3 prediction (conversion with dilate = 2)
The text was updated successfully, but these errors were encountered: