Sct_dmri_moco doesn't seem to correct motion

Hi,

I have a pediatric diffusion data acquired axially on the whole spine with very obvious motion that can be both cardiac and respiratory related (the subject is under sedation). I ran moco on the cropped data with default variables and couldn’t see difference on the corrected results. Then I followed the help message and increase the searching step to allow for more deformation (-param gradStep=5), but still no improvement. I wonder if you could give some suggestion on how to improve moco performance on this dataset? I attached a movie showing the motion from the sagittal view.

ThanksSpineDiffusionMotion.avi|attachment (469.0 KB)
Qiuting

Hi Qiuting,
I cannot visualize your avi file. Has it been properly uploaded?
Would you be able to share the DWI data so i can look for the best parameters for you?

Sure thing! Please see the link below for the movie and data. Thanks so much for the quick response.

https://iu.box.com/s/8nh5bm8i9jnfk4lth80fsmhy5eicv5ln

Hi,

This is a tricky one… it’s going to take a bit of time. I’ll come back to you with a good solution!

Cheers,
Julien

Sounds great.

Thanks much!
Qiuting

Hi Qiuting,

So, I’ve spent quite a bit of time doing a large refactoring of sct_dmri_moco and sct_fmri_moco, see Pull request #2634. The changes are not yet merged with the master branch (it is currently being reviewed), but if you would like to give it a try and give us feedback before this prototype branch is merged, please do so within the next 3-4 days (after that it will be merged).

To do the test, you will need to install the git version of SCT (if you are already running this version, you can skip to the git checkout line), and switch to the working branch:

git clone https://github.com/neuropoly/spinalcordtoolbox.git sct-dev
cd sct-dev
git checkout -b jca/2633-dmri-moco origin/jca/2633-dmri-moco
yes | install_sct

Then, open a new Terminal, and run this code for motion-correcting the data you sent me:

# Average DWI data
sct_dmri_separate_b0_and_dwi -i dmri_crop.nii.gz -bvec dmri.bvec
# Segment the cord roughly (just to get the centerline and create a mask)
sct_deepseg_sc -i dmri_crop_dwi_mean.nii.gz -c dwi -brain 0
# Get centerline (extrapolate on slices where segmentation is missing)
sct_get_centerline -i dmri_crop_dwi_mean_seg.nii.gz -c dwi -method optic -centerline-algo bspline
# Create a Gaussian mask around the spinal cord (to have the motion correction algorithm focus more on the cord)
sct_create_mask -i dmri_crop_dwi_mean.nii.gz -p centerline,dmri_crop_dwi_mean_seg_centerline.nii.gz -size 35mm -f gaussian -o dmri_crop_dwi_mean_seg_mask-gauss.nii.gz
# Run motion correction.
# Tips: Here we do not use grouping because adjacent volumes are highly different from each other due to respiratory-related AP displacements
sct_dmri_moco -i dmri_crop.nii.gz -bvec dmri.bvec -m dmri_crop_dwi_mean_seg_mask-gauss.nii.gz -g 1 -param poly=2,metric=MI,smooth=1

Here is a video of the results (only the first 40 DWI volumes). On the left: raw data, on the right: motion-corrected data:

moco_r55p

You will notice some adjacent-slice misalignment between axial slices ~20-28, which is due to the interleaved acquisition: due to subject’s breathing, when acquiring every other axial slices, the B0 field can drastically vary between the odd and even slices, causing these adjacent-slices misregistration effect. This effect cannot be properly corrected when using slice-regularized algorithm (which you want to use for robustness reasons due to the low SNR regime). So a suggestion would be to switch to ascending or descending mode for your next acquisitions, especially in the thoraco-lumbar region (strong effect of respiratory-related artifacts).

Cheers,
Julien

Hi Julien,

Thank you so much for the effort and quick solution. The motion correction looks great from the video. And your suggestion of switching interleave acquisition to ascending/descending makes great sense. I guess the only compromise to that is that we have to leave some gap between slices to avoid slice cross-talk. Thank you!

I installed the dev version and followed your command lines. It ran fine until the moco part. Please see below for the error message. The mask (dmri_crop_dwi_mean_seg_mask-gauss.nii.gz) generated from last step looks fine to me. Any idea what is going on?

Thanks!
Qiuting

--------------------------------------------------------
$ sct_dmri_moco -i dmri_crop.nii.gz -bvec dmri.bvec -m dmri_crop_dwi_mean_seg_mask-gauss.nii.gz -g 1 -param poly=2,metric=MI,smooth=1

----------------------------------------------------------------------

-------------------------------------------------------------------------------
  Estimating motion on b=0 images...
-------------------------------------------------------------------------------

Input parameters:
  Input file ............ b0.nii
  Reference file ........ data_T0000.nii.gz
  Polynomial degree ..... 2
  Smoothing kernel ...... 1
  Gradient step ......... 1
  Metric ................ MI
  Sampling .............. 0.2
  Todo .................. estimate_and_apply
  Mask  ................. mask.nii
  Output mat folder ..... mat_b0groups

Data dimensions:
  37 x 65 x 49 x 5

Copy file_target to a temporary file...
Error: make sure mask.nii is an image.
Traceback (most recent call last):
  File "/N/project/wulab/software/sct-dev/scripts/sct_dmri_moco.py", line 155, in <module>
    main()
  File "/N/project/wulab/software/sct-dev/scripts/sct_dmri_moco.py", line 150, in main
    moco_wrapper(param)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/moco.py", line 318, in moco_wrapper
    file_mat_b0, _ = moco(param_moco)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/moco.py", line 519, in moco
    im_mask = Image(param.fname_mask)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/image.py", line 243, in __init__
    self.loadFromPath(param, verbose)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/image.py", line 347, in loadFromPath
    self.data = self.im_file.get_data()
AttributeError: 'NoneType' object has no attribute 'get_data'

Not necessarily-- if the TR is high enough and the slice profile is sharp enough, you won’t notice much difference.

The mask is likely missing. If you copy/paste the entire Terminal I can figure out what the problem is.

Get it. I will try no gap first and see how it looks. Thanks. I copy the entire output from the command line. Does it help?

[wenq@i11 dmri_dev]$ sct_dmri_moco -i dmri_crop.nii.gz -bvec dmri.bvec -m dmri_crop_dwi_mean_seg_mask-gauss.nii.gz -g 1 -param poly=2,metric=MI,smooth=1

--
Spinal Cord Toolbox (git-jca/2633-dmri-moco-761e54f06302643734be3a24b4bdc8f7d73166b1)


Input parameters:
  Input file ............ dmri_crop.nii.gz
  Group size ............ 1

Create temporary folder (/tmp/sct-20200315150136.231904-moco-4fthc2qj)...

Copying input data to tmp folder and convert to nii...
sct_convert -i dmri_crop.nii.gz -o /tmp/sct-20200315150136.231904-moco-4fthc2qj/data.nii
cp dmri.bvec /tmp/sct-20200315150136.231904-moco-4fthc2qj/bvecs.txt
cp dmri_crop_dwi_mean_seg_mask-gauss.nii.gz /tmp/sct-20200315150136.231904-moco-4fthc2qj/mask.nii

Get dimensions of data...
  37 x 65 x 49

Data orientation: RPI
  Treated as axial

Identify b=0 and DWI images...
  WARNING: bvecs file is 3xn instead of nx3. Consider using sct_dmri_transpose_bvecs.
  Transpose bvecs...
  Number of b=0: 5 [0, 13, 26, 39, 52]
  Number of DWI: 60 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]

Split along T dimension...

Merge and average b=0 data...
Merge within groups:  63%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b       | 38/60 [00:00<00:00, 49.18iterMerge within groups:  73%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b     | 44/60 [00:00<00:00,Merge within groups:  83%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b   | 50/60 [00Merge within groups:  93%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b |Merge within groups: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 60/60 [00:01<00:00, 49.76iter/s]

Merge across groups...

-------------------------------------------------------------------------------
  Estimating motion on b=0 images...
-------------------------------------------------------------------------------

Input parameters:
  Input file ............ b0.nii
  Reference file ........ data_T0000.nii.gz
  Polynomial degree ..... 2
  Smoothing kernel ...... 1
  Gradient step ......... 1
  Metric ................ MI
  Sampling .............. 0.2
  Todo .................. estimate_and_apply
  Mask  ................. mask.nii
  Output mat folder ..... mat_b0groups

Data dimensions:
  37 x 65 x 49 x 5

Copy file_target to a temporary file...
Error: make sure mask.nii is an image.
Traceback (most recent call last):
  File "/N/project/wulab/software/sct-dev/scripts/sct_dmri_moco.py", line 155, in <module>
    main()
  File "/N/project/wulab/software/sct-dev/scripts/sct_dmri_moco.py", line 150, in main
    moco_wrapper(param)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/moco.py", line 318, in moco_wrapper
    file_mat_b0, _ = moco(param_moco)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/moco.py", line 519, in moco
    im_mask = Image(param.fname_mask)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/image.py", line 243, in __init__
    self.loadFromPath(param, verbose)
  File "/N/project/wulab/software/sct-dev/spinalcordtoolbox/image.py", line 347, in loadFromPath
    self.data = self.im_file.get_data()
AttributeError: 'NoneType' object has no attribute 'get_data'

yes it does help. I see you are using commit 761e54f06302643734be3a24b4bdc8f7d73166b1. I noticed a bug yesterday which I forgot to push (commit: 9d794f3bf4c7536bcb85254d07c3b5b98069ede9). It is now pushed, so you can go to the SCT folder, run git pull, and then re-run the command and it should work.

Great. It works on my end now. I can see that moco works on dwi volumes pretty well - awesome! Does it correct motion for b=0 volumes? There are 5 b=0 volumes in this dataset and I noticed that they stayed almost unchanged after moco, although there seems to be motion across these volumes. I separated b0 volumes and attached a video. http://forum.spinalcordmri.org/uploads/default/original/1X/11cd016461ff6ecd80836fc8a1a73c2f40418605.mp4

the reason the performance of moco seems poor is because of the interleaved acquisition mode. I’ve opened a feature request to accommodate moco for interleaved acquisition, but in the meantime you can try to use -param poly=0 (for no slicewise regularization, although it might result in worst motion correction due to the lack of through-slice regularization), or ideally acquire without interleaved mode.

Get it. Once I have a non-interleaved data I will update this post. Thank much!

Qiuting

1 Like

I am finally able to get a non-interleaved baby spine diffusion data after the long pause due to Covid19. I ran the pipeline as you suggest in the previous thread with the _dev version. Please see the video below. The motion correction does not seem to improve on my end? Could you please help take a look? Also, the cropping seem to get a bit too close to the spinal cord. Is there a way to relax it? Please use the below link for the dataset:
https://iu.box.com/s/t0i7g5t920h5x05gcnpmtwwlmj6b433g

Thank you.
Qiuting

hi

thank you for your feedback. I will have a look at your issue early next week.

Hi Qiuting,

So, I had a look at your data, and although the presence of short-distance scan-to-scan non-linear motion makes it difficult for the polynomial-regularized slicereg algorithm, so I decided to run it without regularization (p=0) and results are fairly good. Because the automatic centerline detection did not work very on this dataset, I also made a manual centerline by adding a few points along the cord centerline, as shown in the figure below:

The code I used is the following:

# Average DWI data
sct_dmri_separate_b0_and_dwi -i dmri.nii.gz -bvec dmri.bvec -a 1
# Get centerline (semi-manual)
sct_get_centerline -i dmri_dwi_mean.nii.gz -method viewer 
# Create a Gaussian mask around the spinal cord (to have the motion correction algorithm focus more on the cord)
sct_create_mask -i dmri_dwi_mean.nii.gz -p centerline,dmri_dwi_mean_centerline.nii.gz -size 35mm -f gaussian -o dmri_dwi_mean_mask-gauss.nii.gz
# Run motion correction.
sct_dmri_moco -i dmri.nii.gz -bvec dmri.bvec -m dmri_dwi_mean_mask-gauss.nii.gz -g 1 -param poly=0,metric=MI,smooth=1

And here are the results (left: uncorrected, right: corrected):

Note that in order to generate the same results, you will need to install the latest development version of SCT from github (instructions here).

I followed your steps and was able to produce the same results. Thank you so much!! I would like to give some feedback and one related question:

  1. Feedback on the centerline generation. The semi-manual code doesn’t work on my end, with some Qt flatform plugin issue. So I used b0_mean to get the centerline, which seems to be more robust than using dwi_mean. The code I used is:
    sct_get_centerline -i dmri_b0_mean.nii.gz -c dwi -method optic -centerline-algo bspline

  2. Feeback on version issue: I installed the most recent development version following the instructions in the link you attached (version 4.3). However, this version doesn’t produce the same moco results (i.e. motion persists). Instead, my previous version sct_dev (checked out in March, version 4.2.2) was able to reproduce the same results as you have.

  3. Error with other datasets: I processed a few other subjects (with sct_dev 4.2.2). Some of them run through with good results, but some of them had error messages with “No good transformation exist.”. Could you help take a look? Please see below for the data link/ code/output/data link. Thanks ahead of time!

data link:
https://iu.box.com/s/7xntv0nc3ejb60eh64u3f73b2siw458t

Code I used:

Average DWI data

sct_dmri_separate_b0_and_dwi -i dmri.nii.gz -bvec dmri.bvec -a 1

get centerline auto in b0 image

sct_get_centerline -i dmri_b0_mean.nii.gz -c dwi -method optic -centerline-algo bspline
sct_create_mask -i dmri_b0_mean.nii.gz -p centerline,dmri_b0_mean_centerline.nii.gz -size 35mm -f gaussian -o dmri_b0_mean_mask-gauss.nii.gz

Run motion correction.

sct_dmri_moco -i dmri.nii.gz -bvec dmri.bvec -m dmri_b0_mean_mask-gauss.nii.gz -g 1 -param poly=0,metric=MI,smooth=1

Error message:
Spinal Cord Toolbox (4.2.2)

Create temporary folder (/tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv)…

Copying input data to tmp folder and convert to nii…
sct_convert -i /geode2/projects/p060_040/G1/dMRI/DrHo/Spine/20200819_2yo_1.5T_1/processing/dmri/dmri.nii.gz -o /tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv/dmri.nii
cp /geode2/projects/p060_040/G1/dMRI/DrHo/Spine/20200819_2yo_1.5T_1/processing/dmri/dmri.bvec /tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv/bvecs.txt
cp /geode2/projects/p060_040/G1/dMRI/DrHo/Spine/20200819_2yo_1.5T_1/processing/dmri/dmri_b0_mean_mask-gauss.nii.gz /tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv/mask.nii.gz

Get dimensions of data…
100 x 100 x 50

Identify b=0 and DWI images…
WARNING: bvecs file is 3xn instead of nx3. Consider using sct_dmri_transpose_bvecs.
Transpose bvecs…
Number of b=0: 3 [0, 13, 26]
Number of DWI: 36 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38]

Split along T dimension…

Merge b=0…
File created: b0.nii

Average b=0…
sct_maths -i b0.nii -o b0_mean.nii -mean t # in /tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv
Merge within groups: 100%|####################| 36/36 [00:50<00:00, 1.40s/iter]

Merging DW files…

Averaging all DW images…
sct_maths -i dwi_averaged_groups.nii -o dwi_averaged_groups.nii_mean.nii.gz -mean t # in /tmp/sct-20200829132202.350591-dmri_moco-_g3td_kv


Estimating motion on b=0 images…

Input parameters:
Input file …b0.nii
Reference file …dmri_T0000.nii.gz
Polynomial degree …1
Smoothing kernel …1
Gradient step …1
Metric …MI
Sampling …0.2
Todo …estimate
Mask …mask.nii.gz
Output mat folder …mat_b0groups

Data dimensions:
100 x 100 x 50 x 3

Copy file_target to a temporary file…
sct_convert -i dmri_T0000.nii.gz -o target.nii.gz
sct_convert -i mask.nii.gz -o mask.nii

Register. Loop across Z (note: there is only one Z if orientation is axial
Z=0/0: 0%| | 0/3 [00:00<?, ?iter/s]WARNING in msct_moco.py: No output. Maybe related to improper calculation of mutual information. Either the mask you provided is too small, or the subject moved a lot. If you see too many messages like this try with a bigger mask. Using previous transformation for this volume (if itexists).
Z=0/0: 33%|############ | 1/3 [00:00<00:00, 3.11iter/s]WARNING in msct_moco.py: No output. Maybe related to improper calculation of mutual information. Either the mask you provided is too small, or the subject moved a lot. If you see too many messages like this try with a bigger mask. Using previous transformation for this volume (if itexists).
Z=0/0: 67%|######################## | 2/3 [00:00<00:00, 3.13iter/s]WARNING in msct_moco.py: No output. Maybe related to improper calculation of mutual information. Either the mask you provided is too small, or the subject moved a lot. If you see too many messages like this try with a bigger mask. Using previous transformation for this volume (if itexists).
Z=0/0: 100%|####################################| 3/3 [00:01<00:00, 2.73iter/s]

ERROR in msct_moco.py: No good transformation exist. Exit program.

  • Feedback on the centerline generation. The semi-manual code doesn’t work on my end, with some Qt flatform plugin issue. So I used b0_mean to get the centerline, which seems to be more robust than using dwi_mean. The code I used is:
    sct_get_centerline -i dmri_b0_mean.nii.gz -c dwi -method optic -centerline-algo bspline

Could you please open a new ticket, specifically about this Qt platform issue? We could try to solve it.

  • Feeback on version issue: I installed the most recent development version following the instructions in the link you attached (version 4.3). However, this version doesn’t produce the same moco results (i.e. motion persists). Instead, my previous version sct_dev (checked out in March, version 4.2.2) was able to reproduce the same results as you have.

As I mentioned in my previous post, you need to install the latest development version of SCT (instructions here).

  • Error with other datasets: I processed a few other subjects (with sct_dev 4.2.2). Some of them run through with good results, but some of them had error messages with “No good transformation exist.”. Could you help take a look? Please see below for the data link/ code/output/data link. Thanks ahead of time!

Please try with the latest version of SCT and if you have issues, post the entire Terminal output.

Hi Julien,

I have now installed the development version,which solves the “No good transformation” problem in my previous post. Moco works well on all datasets I’ve collected so far. Thank you so much for your help with our baby data!

Best,
Qiuting

1 Like