Transform formats (rigid registration) in sct_register_multimodal

Description

Hi all,

I’m hoping that asking for algo=translation, rigid, or affine would be able to give me a transformation matrix - What format would this be in? Or is it even possible to save this matrix when performing sct_register_multimodal? I know that I can save full warps as NII with -owarp option, but when running affine/rigid/translation and asking for .mat or .txt I am getting “Cannot work out file type of cord_affine.txt”.

It looks like I can still ask for a NII image output (and it appears to be an linear transform applied to all coordinates), but I’d like to have the 4x4 matrix if possible (I’m moving diffusion data around before voxel-wise reconstruction and want to correct my b-vectors).

Thank you,
Kurt

Commands and terminal output (note simplified for readability)

sct_register_multimodal -i cord_dwi_mean.nii -iseg cord_mask.nii -d T1_masked.nii.gz -dseg T1_seg.nii -o cord_dwi_2_T1.nii -owarp cord_translate.txt -param step=1,type=seg,algo=translation,metric=MeanSquares:step=2,type=im,algo=translation,metric=MI,iter=5,shrink=2 

Full output looks good up until generating outputs files:

Generate output files...
  WARNING: File PROCESSED/cord_dwi_2_T1.nii already exists. Deleting it...
  File created: PROCESSED/cord_dwi_2_T1.nii
  WARNING: File PROCESSED/cord_dwi_2_T1_inv.nii.gz already exists. Deleting it...
  File created: PROCESSED/cord_dwi_2_T1_inv.nii.gz
Traceback (most recent call last):
  File "/Users/kurtschilling/sct_dev/scripts/sct_register_multimodal.py", line 372, in <module>
    main()
  File "/Users/kurtschilling/sct_dev/scripts/sct_register_multimodal.py", line 348, in main
    path_out=path_out)
  File "/Users/kurtschilling/sct_dev/scripts/msct_register.py", line 343, in register_wrapper
    sct.generate_output_file(os.path.join(path_tmp, "warp_src2dest.nii.gz"), fname_output_warp, param.verbose)
  File "/Users/kurtschilling/sct_dev/scripts/sct_utils.py", line 746, in generate_output_file
    convert(fname_in, fname_out, squeeze_data=squeeze_data, verbose=0)
  File "/Users/kurtschilling/sct_dev/scripts/sct_convert.py", line 89, in convert
    im.save(fname_out, mutable=True, verbose=verbose)
  File "/Users/kurtschilling/sct_dev/spinalcordtoolbox/image.py", line 488, in save
    nibabel.save(img, path)
  File "/Users/kurtschilling/sct_dev/python/envs/venv_sct/lib/python3.6/site-packages/nibabel/loadsave.py", line 134, in save
    filename)
nibabel.filebasedimages.ImageFileError: Cannot work out file type of "PROCESSED/cord_warp.txt"

sct_check_dependencies

Spinal Cord Toolbox (dev)

SCT info:

- version: dev

- path: /Users/kurtschilling/sct_dev

OS: osx (Darwin-20.3.0-x86_64-i386-64bit)

CPU cores: Available: 8, Used by SCT: 8

RAM: Primary memory available: 16.00 gigabytes

Check Python executable.............................[OK]

Using bundled python 3.6.10 |Anaconda, Inc.| (default, May 7 2020, 23:06:31)

[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] at /Users/kurtschilling/sct_dev/python/envs/venv_sct/bin/python

Check if data are installed.........................[OK]

Check if numpy is installed.........................[OK] (1.18.4)

Check if colored is installed.......................[OK] (1.4.2)

Check if cryptography is installed..................[OK] (2.9.2)

Check if dipy is installed..........................[OK] (1.1.1)

Check if futures is installed.......................[OK]

Check if h5py is installed..........................[OK] (2.10.0)

Check if ivadomed (1.2.1) is installed..............[OK] (1.2.1)

Check if Keras (2.1.5) is installed.................[OK] (2.1.5)

Check if matplotlib is installed....................[OK] (3.2.1)

Check if nibabel is installed.......................[OK] (3.1.0)

Check if pandas is installed........................[OK] (1.0.3)

Check if psutil is installed........................[OK] (5.7.0)

Check if pyqt5 (5.11.3) is installed................[OK] (5.11.3)

Check if pytest is installed........................[OK] (5.4.2)

Check if pytest-cov is installed....................[OK] (2.9.0)

Check if raven is installed.........................[OK]

Check if requests is installed......................[OK] (2.23.0)

Check if requirements-parser is installed...........[OK] (0.2.0)

Check if scipy is installed.........................[OK] (1.4.1)

Check if scikit-image is installed..................[OK] (0.17.2)

Check if scikit-learn is installed..................[OK] (0.23.1)

Check if tensorflow (1.5.0) is installed............[OK] (1.5.0)

Check if torch (1.5.0) is installed.................[OK] (1.5.0)

Check if torchvision (0.6.0) is installed...........[OK] (0.6.0)

Check if xlrd is installed..........................[OK] (1.2.0)

Check if xlutils is installed.......................[OK]

Check if xlwt is installed..........................[OK] (1.3.0)

Check if tqdm is installed..........................[OK] (4.46.0)

Check if transforms3d is installed..................[OK] (0.3.1)

Check if urllib3 is installed.......................[OK] (1.25.9)

Check if spinalcordtoolbox is installed.............[OK]

Check ANTs compatibility with OS ...................[OK]

Check PropSeg compatibility with OS ................[OK]

Check if DISPLAY variable is set....................[OK]

Check if figure can be opened with PyQt.............[OK]

Hi @schillkg,

Thank you for reaching out!

Given that our users rarely do affine transformations only, we always output an ITK-compatible transformation field in NIfTI format. As you rightfully noted, the expected format of -owarp is “.nii.gz”.

If you absolutely want the affine matrix, you can find it in the temporary intermediate files. Example:

# dummy transformation. Note the "-r 0" at the end to retrieve temporary files
sct_register_multimodal -i mt0.nii.gz -d mt1.nii.gz -param step=1,type=im,algo=affine -r 0

Note the temporary folder, which is created at the beginning:

--
Spinal Cord Toolbox (git-jca/3336-display-warp-7de29b49e9979eb22409e8a11eb2c79eb0277281)

sct_register_multimodal -i mt0.nii.gz -d mt1.nii.gz -param step=1,type=im,algo=affine -r 0
--


Input parameters:
  Source .............. mt0.nii.gz
  Destination ......... mt1.nii.gz
  Init transfo ........ 
  Mask ................ 
  Output name ......... 
  Remove temp files ... 0
  Verbose ............. 1

Check if input data are 3D...
Creating temporary folder (/var/folders/s8/4qnm5q1n261ch35b5kkclsb00000gn/T/sct-20210407211058.464852-register-yl4lc5_9)  --> HERE!
[...]

Go to this folder after the registration is finished and see what’s in there:

cd /var/folders/s8/4qnm5q1n261ch35b5kkclsb00000gn/T/sct-20210407211058.464852-register-yl4lc5_9
ls -la

Output:

total 976
drwx------   13 julien  staff     416  7 Apr 21:11 .
drwx------@ 122 julien  staff    3904  7 Apr 21:10 ..
-rw-r--r--    1 julien  staff   16352  7 Apr 21:10 dest.nii
-rw-r--r--    1 julien  staff   16352  7 Apr 21:10 dest_RPI.nii
-rw-r--r--    1 julien  staff   48352  7 Apr 21:10 dest_RPI_pad.nii
-rw-r--r--    1 julien  staff   64352  7 Apr 21:10 dest_reg.nii
-rw-r--r--    1 julien  staff   16352  7 Apr 21:10 src.nii
-rw-r--r--    1 julien  staff   64352  7 Apr 21:10 src_reg.nii
-rw-r--r--    1 julien  staff   64352  7 Apr 21:10 src_regStep0.nii
-rw-r--r--    1 julien  staff  192352  7 Apr 21:10 src_reg_regStep1.nii
-rw-r--r--    1 julien  staff     337  7 Apr 21:10 warp_forward_0.nii.gz
-rw-r--r--    1 julien  staff     193  7 Apr 21:10 warp_forward_1.mat
-rw-r--r--    1 julien  staff     337  7 Apr 21:10 warp_inverse_0.nii.gz

Notice the affine matrix: warp_forward_1.mat. Note that it is in ITK format, so to visualize it, use ANTs tools:

antsTransformInfo warp_forward_1.mat 

Which gives you:

Transform file: warp_forward_1.mat
AffineTransform (0x7f9d5bd2c0e0)
  RTTI typeinfo:   itk::AffineTransform<double, 3u>
  Reference Count: 3
  Modified Time: 512
  Debug: Off
  Object Name: 
  Observers: 
    none
  Matrix: 
    0.996624 0.0167483 -0.00136067 
    -0.012089 1.00036 -0.00467956 
    0.000853096 0.000492122 0.99986 
  Offset: [0.961513, -0.522049, 0.0420784]
  Center: [0, 0, 0]
  Translation: [0.961513, -0.522049, 0.0420784]
  Inverse: 
    1.00318 -0.0167961 0.00128659 
    0.012119 0.999432 0.00469405 
    -0.000861896 -0.000477581 1.00014 
  Singular: 0

:information_source:‎ As you already know, correcting the b-matrix is only useful when you need the orientation, e.g. to perform tractography. If the goal is to compute metrics such as FA, MD, etc. there is no need in correcting the b-matrix.

I hope that helps!
Julien

1 Like

Perfect! This is exactly what I was looking for.

Thank you for the quick response.

Yes, in addition to scalar metrics, I’m playing around with orientation in different spaces and ‘think’ that the easiest approach in this specific case is just to get all raw data rigidly aligned (and adjust b-vectors) rather than dealing with re-orientation after converting to SH or other basis functions.

BTW - the sct_register_multimodal interface/wrapper is infinitely more intuitive than many other registration softwares! The only issue I had was pulling out the affine!

Thank you again,
Kurt

Yes, in addition to scalar metrics, I’m playing around with orientation in different spaces and ‘think’ that the easiest approach in this specific case is just to get all raw data rigidly aligned (and adjust b-vectors) rather than dealing with re-orientation after converting to SH or other basis functions.

Another possibility would be to derive a voxelwise b-matrix factor based on the NIfTI warping field itself (which is de facto a deformation per voxel). I am wondering if other people have done that.

BTW - the sct_register_multimodal interface/wrapper is infinitely more intuitive than many other registration softwares! The only issue I had was pulling out the affine!

Thank you for the kind words :heart: