Registration of lumbar data to PAM50

Hi everyone

Some of this I discussed directly with Julien and I’m putting it here if somebody else has a similar problem and to further the discussion. I’m registering lumbosacral ME-GRE images to the PAM50 template. So far I did this using a single label (label_body 19) which is close to the LSE. This is of course not optimal and Julien suggested to manually create labels as described in this previous topic.

First I created a new LSE label (ID 98) at the slice with the highest CSA (164)

# Create LSE label (#98)
sct_label_utils \
  -i $SCT_DIR/data/PAM50/template/PAM50_label_body.nii.gz \
  -create-add 70,70,164,98 \
  -o $SCT_DIR/data/PAM50/template/PAM50_label_body.nii.gz

Then I wanted to similarly create a new tip (conus medullaris) label (ID 99) at the end of the spinal cord. Until now the cord mask was missing the most caudal bit. However this seems to hopefully change soon as per this pull request. I’m now using the last slice of this new mask as the tip (40)

# Create tip label (#99)
sct_label_utils \
  -i $SCT_DIR/data/PAM50/template/PAM50_label_body.nii.gz \
  -create-add 70,70,40,99 \
  -o $SCT_DIR/data/PAM50/template/PAM50_label_body.nii.gz

I’m now in the process of updating each subjects ME-GRE label and questioning how exact I need to position my labels. Is it necessary to place the label in the spot where the tip of the cord is (axially speaking) or is it sufficient to label the slice? In the description of sct_register_to_template it is said that it has to be located at the center of the spinal cord. However so far I created the LSE/19 label automated placing them at the middle of the slice, which can be decently in the middle of the segmentation but depending on the tilt of the cord also outside of it. I didn’t have problems with this, so I’m wondering if I just got lucky so far.

And similarly did I get that right that the label has to be on a slice with segmentation (see also this response). We usually define the tip in the first slice below the last one we were able to confidently segment. So this would mean that I need to add a segmented “tip”-voxel to this slice for it to work properly? (I would just use the center of the last segmented slice. Or, depending on the previous paragraph, I would also need to place my label in this voxel.)

While working with the labels I saw that the PAM_label_body usually places the label at 70/70/xx but labels 15:18,20 are at 70/71/xx, so one voxel more anterior. Maybe this is intentional but it looked a bit unusual.

(I’m still using 5.8 since everything else was processed on this release but replaced the cord mask from the mentioned PR)

Hi @kuech,

Thank you for reaching out and for taking the time to clearly explain your experiments.

First of all, if you are registering axial ME-GRE, which I assume are acquired with highly anisotropic resolution, then I definitely recommend using the flag sct_register_to_template -ref subject to avoid straightening (which would introduce large interpolation error).

Is it necessary to place the label in the spot where the tip of the cord is (axially speaking) or is it sufficient to label the slice?

It depends how you define the steps for your registration. Step=0 corresponds to the label-based alignment. So, assuming axial orientation, if you place your labels correctly for z and incorrectly for x-y (eg: in the middle of your slice), then step=0 will correctly align and scale the cord along the superior-inferior direction, but will wrongly position the cord along the x-y dimensions. However, if you then add a step that fixes x-y orientation at step 1 (eg: step=1,type=seg,algo=centermassrot), then you’re fine.

And similarly did I get that right that the label has to be on a slice with segmentation (see also this response).

Yup! You got it right.

We usually define the tip in the first slice below the last one we were able to confidently segment. So this would mean that I need to add a segmented “tip”-voxel to this slice for it to work properly? (I would just use the center of the last segmented slice. Or, depending on the previous paragraph, I would also need to place my label in this voxel.)

Yup!

While working with the labels I saw that the PAM_label_body usually places the label at 70/70/xx but labels 15:18,20 are at 70/71/xx, so one voxel more anterior. Maybe this is intentional but it looked a bit unusual.

It is indeed intentional (location based on the center of mass), although I agree this could be confusing from a user standpoint. I’ve opened an issue for further discussion: Inconsistent X-Y location of labels for `PAM50_label_body.nii.gz` · Issue #22 · spinalcordtoolbox/PAM50 · GitHub. Thank you for letting us know!

Thank you for the quick answer.

I tested some subjects and was partially successful. Interestingly it doesn’t work when I add the labels to the PAM50_label_body.nii.gz file and use the -l argument during registration. I get the following error:

Generate labels from template vertebral labeling

Check if provided labels are available in the template

ERROR: Wrong landmarks input. Labels must have correspondence in template space.
Label max provided: 99.0
Label max from template: 20

It works fine when I added them to the PAM50_label_disc.nii.gz file and used the -ldisc argument during registration. However I realized that the disc labels 19 (slice 39 instead of 40) and 21 (slice 169 instead of 164) are pretty close to the new labels and therefore I might simply use them. So far so good.

We usually define the tip in the first slice below the last one we were able to confidently segment. So this would mean that I need to add a segmented “tip”-voxel to this slice for it to work properly? (I would just use the center of the last segmented slice. Or, depending on the previous paragraph, I would also need to place my label in this voxel.)

Yup!

Placing a single voxel on that cord mask-slice was not sufficient in some subjects. It still got lost during straightening but I can prevent it by using 3by3 voxels. Because I use the default -param for registration I’m usually fine with placing the labels in the middle of the image. For this case I also tried placing them inside the segmentation, but only the increase of voxels in that slice solved the problem.

First of all, if you are registering axial ME-GRE, which I assume are acquired with highly anisotropic resolution, then I definitely recommend using the flag sct_register_to_template -ref subject to avoid straightening (which would introduce large interpolation error).

Yes, 0.5x0.5x5mm. I tried this on a few subjects now, but it produces something wrong or gets stuck. I let it run for 20min before interrupting. Error happens during estimate transformation for step #2.
In one case the registration rotates the image by about 45° degrees. I added a folder containing this example with the code for reproduction (including the new label files). I guess the cost function gets stuck in a local minima or trapped without reaching exit-conditions? In which case the registration to the template seems to be more robust or even the only option. example_wcode.zip (804.1 KB)

edit: When using the -ref subject I get this rotation in all subjects, it varies from about 20° to 90°, always clockwise.

Hi @kuech, very sorry for the delay in answering-- for some reasons I didn’t get the notification of your answer-- I will look at it ASAP and come back to you

Hi @kuech,

Again, very sorry about the delayed response-- here are some answers:

Interestingly it doesn’t work when I add the labels to the PAM50_label_body.nii.gz file and use the -l argument during registration.

Indeed, this is strange. We have opened an issue about it and we will investigate: ERROR: Wrong landmarks input. Labels must have correspondence in template space. · Issue #4227 · spinalcordtoolbox/spinalcordtoolbox · GitHub

Placing a single voxel on that cord mask-slice was not sufficient in some subjects. It still got lost during straightening

As mentioned above (Registration of lumbar data to PAM50 - #2 by jcohenadad), I strongly recommend not involving straightening in the registration process, precisely because of the issues you are observing (among other reasons).

Yes, 0.5x0.5x5mm. I tried this on a few subjects now, but it produces something wrong or gets stuck. I let it run for 20min before interrupting. Error happens during estimate transformation for step #2.
In one case the registration rotates the image by about 45° degrees. I added a folder containing this example with the code for reproduction (including the new label files). I guess the cost function gets stuck in a local minima or trapped without reaching exit-conditions? In which case the registration to the template seems to be more robust or even the only option. example_wcode.zip (804.1 KB)

Thank you for providing the data for reproducing. Trying the commands in your script, with comments below:

:information_source: I recommend using the -qc flag, especially when optimizing parameters. You can quickly have an overview of the result, while keeping track of the syntax for each combination of parameters.

-ref template

sct_register_to_template -i $anat -s $mask -ldisc $label -c t2 -ref template -ofolder reg_to_tem

anim

This produces reasonable registration (with room for improvements by tweaking the registration params), although as discussed above I would avoid straightening.

-ref template with flag -l

Comment from the SHELL script:

Using the -l argument: will not run!

I can reproduce, it does not run. More specifically, here is the full Terminal output:

Terminal output
julien-macbook:~/Desktop/example_wcode $ sct_register_to_template -i megre3d_cr.nii.gz -s megre3d_cr_msc.nii.gz -l megre3d_cr_label.nii.gz -c t2 -ref template -ofolder reg_to_tem_body

--
Spinal Cord Toolbox (git-master-76c27e16f13974de3ff8f81e25c92abe8f6d80ec)

sct_register_to_template -i megre3d_cr.nii.gz -s megre3d_cr_msc.nii.gz -l megre3d_cr_label.nii.gz -c t2 -ref template -ofolder reg_to_tem_body
--


Check template files...
  OK: /Users/julien/code/spinalcordtoolbox/data/PAM50/template/PAM50_t2.nii.gz
  OK: /Users/julien/code/spinalcordtoolbox/data/PAM50/template/PAM50_levels.nii.gz
  OK: /Users/julien/code/spinalcordtoolbox/data/PAM50/template/PAM50_cord.nii.gz

Check parameters:
  Data:                 megre3d_cr.nii.gz
  Landmarks:            megre3d_cr_label.nii.gz
  Segmentation:         megre3d_cr_msc.nii.gz
  Path template:        /Users/julien/code/spinalcordtoolbox/data/PAM50
  Remove temp files:    1

Check input labels...
Creating temporary folder (/var/folders/5f/6n99hhyj5_g5cv_1bbx1q7cr0000gn/T/sct_2023-09-25_10-27-45_register-to-template_n_sjkkp7)

Copying input data to tmp folder and convert to nii...

Generate labels from template vertebral labeling

Check if provided labels are available in the template
ERROR: Wrong landmarks input. Labels must have correspondence in template space. 
Label max provided: 99.0
Label max from template: 20

The program complains that the max value on the template is 20, even though I’ve edited the file to add labels 98 and 99. Proof:

**julien-macbook:**~/code/spinalcordtoolbox/data/PAM50/template $ sct_label_utils -i PAM50_label_body.nii.gz -display

--
Spinal Cord Toolbox (git-master-76c27e16f13974de3ff8f81e25c92abe8f6d80ec)

sct_label_utils -i PAM50_label_body.nii.gz -display

--
Position=(70,70,951) -- Value= 1
Position=(70,70,924) -- Value= 2
Position=(70,70,890) -- Value= 3
Position=(70,70,853) -- Value= 4
Position=(70,70,818) -- Value= 5
Position=(70,71,787) -- Value= 6
Position=(70,71,754) -- Value= 7
Position=(70,70,716) -- Value= 8
Position=(70,70,671) -- Value= 9
Position=(70,70,625) -- Value= 10
Position=(70,70,577) -- Value= 11
Position=(70,70,527) -- Value= 12
Position=(70,70,476) -- Value= 13
Position=(70,70,424) -- Value= 14
Position=(70,71,370) -- Value= 15
Position=(70,71,317) -- Value= 16
Position=(70,71,261) -- Value= 17
Position=(70,71,200) -- Value= 18
Position=(70,70,142) -- Value= 19
Position=(70,71,91) -- Value= 20
Position=(70,70,164) -- Value= 98
Position=(70,70,40) -- Value= 99

OK, I identified the cause of the problem. In fact, the file PAM50_label_body.nii.gz is not used by sct_register_to_template -l, but instead is generated on-the-fly from the file PAM50_levels.nii.gz. Issue opened: Confusing existence of `PAM50_label_body.nii.gz` · Issue #4229 · spinalcordtoolbox/spinalcordtoolbox · GitHub

Until this issue is resolved, I suggest to only use -ldisc with custom labels.

-ref subject

sct_register_to_template -i $anat -s $mask -ldisc $label -c t2 -ref subject -ofolder reg_to_sub

anim

As you noted, there is definitely an issue with excessive rotation, which might be caused by a wrong estimation of spinal cord orientation at step=1. This is not uncommon in the lumbar area, where the spinal cord is rounder (vs. in the cervical cord, where the cord is more ellipsoid). To overcome this issue, I tried ignoring the rotation by using the centermass algorithm instead:

sct_register_to_template -i $anat -s $mask -ldisc $label -c t2 -ref subject -ofolder reg_to_sub -param step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=3,smooth=1 -qc qc -v 2 -r 0

However it did not solve the rotation problem. So I had to dig further, investigating the intermediate files and labels for registering data at step=0. Indeed, step=0 seems to be the cause of the issue:

The label registration at step=0 seems to be working fine:

So maybe the issue is caused by how labels are defined? Normally, one label orthogonal to the two provided labels is created, in order to properly estimate an affine transform. However, from the screenshot above, only two labels (not three) are used.

Digging further, I noticed that the third label uses a dummy value 99:

Which is problematic, because 99 is also the value chosen by the user (!). This is quite unfortunate :sweat_smile:

So what we need to do:

  • SCT dev team: add a check to make sure that the input image label does not have the value 99.
  • For this particular case to work: modify the label value from 99–>97.

I replaced 99 by 97 in the following files:

  • megre3d_cr_label.nii.gz
  • PAM50_label_disc.nii.gz (which was previously modified by @kuech)

And then re-launched the registration:

sct_register_to_template -i $anat -s $mask -ldisc megre3d_cr_label_97-98.nii.gz -c t2 -ref subject -ofolder reg_to_sub -param step=1,type=seg,algo=centermass:step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=3,smooth=1 -qc qc -v 2 -r 0

Bingo! :tada: The rotation issue is now solved:

anim

And the label-based registration at step=0 (output with -v 2) confirms there are now three labels:

Next step:

Things I’ve tried to improve registration:

Parameters Results
centermassrot vs. centermass :-1:
type=imseg,algo=centermassrot,rot_method=pcahog :-1:
-c t2s :-1: (some slices from the T2s template are missing caudally)
step=3,type=im,algo=syn,metric=CC,iter=5,slicewise=1 :+1:

Suggested syntax:

sct_register_to_template -i $anat -s $mask -ldisc megre3d_cr_label_97-98.nii.gz -c t2 -ref subject -ofolder reg_to_sub -param step=1,type=imseg,algo=centermassrot,rot_method=pcahog:step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=3,smooth=1,slicewise=1:step=3,type=im,algo=syn,metric=CC,iter=5,slicewise=1 -qc qc

Results:
anim

QC reports of all my investigations: qc.zip (877.1 KB)

Thank you very much for the extensive help. I will implement the switch from -ref template to -ref subject in the near future. With your explanation it also makes sense seeing this rotation in all subjects since it was connected to the 99 label.

At that point I will also try to investigate the used parameters a bit more, so far I simply used the default ones. Your suggested synthax is greatly appreciated and I will report back if/when I run into further struggles.