Importing SynthMorph transform/deformation field output (NIFTI) into 3D Slicer

Hi,

I am using SynthMorph via ‘mri_synthmorph’ command from Freesurfer to get the transform (deformation field) from image registration:

mri_synthmorph -m joint -t def.nii.gz moving.nii.gz fixed.nii.gz

when I use this ‘def.nii.gz’ file to transform the ‘moving.nii.gz’ using FreeSurfer’s ‘mri_convert’ command, it works as intended. However, when I try importing the file into 3D Slicer as a ‘Transform’, it does not work.

I have tried fixing the intent code and reshaping of the file as per:

This removes the intent error but does not solve the problem. I get a very weird artifact (shown in attachment).

Is there a way to correctly import the transform output from SynthMorph into 3D Slicer?

Kind regards

Andy

*additional header info ‘def.nii.gz’:

Shape: (256, 256, 256, 3)
<class ‘nibabel.nifti1.Nifti1Header’> object, endian=‘<’
sizeof_hdr : 348
data_type : np.bytes_(b’‘)
db_name : np.bytes_(b’‘)
extents : 0
session_error : 0
regular : np.bytes_(b’‘)
dim_info : 0
dim : [ 4 256 256 256 3 1 1 1]
intent_p1 : 0.0
intent_p2 : 0.0
intent_p3 : 0.0
intent_code : none
datatype : float32
bitpix : 32
slice_start : 0
pixdim : [-1. 1. 1. 1. 1. 1. 1. 1.]
vox_offset : 0.0
scl_slope : nan
scl_inter : nan
slice_end : 0
slice_code : unknown
xyzt_units : 10
cal_max : 0.0
cal_min : 0.0
slice_duration : 0.0
toffset : 0.0
glmax : 0
glmin : 0
descrip : np.bytes_(b’‘)
aux_file : np.bytes_(b’‘)
qform_code : scanner
sform_code : scanner
quatern_b : 0.0
quatern_c : -0.70710677
quatern_d : 0.70710677
qoffset_x : 127.5
qoffset_y : -127.5
qoffset_z : 127.5
srow_x : [ -1. -0. -0. 127.5]
srow_y : [ -0. -0. 1. -127.5]
srow_z : [ 0. -1. 0. 127.5]
intent_name : np.bytes_(b’‘)
magic : np.bytes_(b’n+1’)

I don’t think anyone has worked on this exact transform, but it should be possible to make it work with some coding. Basically a grid transform in Slicer is just a vector field in space where each vector tells how to map from a source image to a target image. the sampling grid is defined by the origin, directions, spacing, and dimensions of the grid transform’s image data. You can easily inspect this by getting the data as numpy arrays and you can use transform visualization options in Slicer to view them. To address the SynthMorph import you just need to understand the conventions they have used to represent the nifti file and map those into what Slicer expects.

There are several conventions that can be used to represent nonlinear transforms, and you just need to figure out which are used by SynthMorph. Maybe start by applying a known translation to sample data, run the registration, and inspect the resulting transform file.

1 Like

I’ve implemented displacement field storage in 4D NIFTI files in ITK and Slicer and it should work well, as long as the displacement field file is valid.

What do you mean by “does not work”? What did you do, what did you expect to happen, and what happened instead?

I don’t see any artifacts, it is a color image.

A 4D array may be interpreted as an RGB color image, so if you chose “Volume” when you loaded the data then this is the correct behavior (it would be nice to select “Transform” by default when you load a displacement field, but I don’t think it is implemented yet).

If you choose “Transform” in the “Add data” window then the file will be loaded as a displacement field transform.

One suspicious thing in the file header that you shared is that image orientation is specified with both sform and qform. Is that intentional? What are the image origin, spacing, axis directions of this image in RAS space? What does Volumes module / Volume information section show?

It is easy to create an incorrect file (intent field may be wrong, some software might write the vector values in voxel space, etc.), so check all details and report any problems to the developers of the software that created the incorrect field. If you believe that the file is correct then please share a set of data files (upload somewhere and post the link here) and I’ll have a look.

You may also choose to use NRRD format instead, which does not suffer from many of the issues and ambiguities of NIFTI files.

Most likely the violation of the NIFTI file format in your file header that prevents loading into Slicer is that the dimensions are incorrect. Dimensions are hardcoded in NIFTI. 4th dimension is always time: it is set to 3 in your file (correct value for a 3D displacement field is 1). 5th dimension is the dimensionality of the displacement vector: it is set to 1 in your header (correct value is 3 see
here: dim[5] must be the dimensionality of the displacment vector (e.g., 3 for spatial displacement...)

I’ve implemented this now - pull request submitted.

2 Likes

+1 on this comment :pray:

Thank you @pieper and @lassoan for looking into this. I managed to fix the issue following your suggestions.

I cross-checked the NIFTI header of a deformation field that was produced in 3D Slicer with the one produced by SynthMorph and found that the dimensions were different. I reshaped the SynthMorph NIFTI output to match the one produced by 3D Slicer and this worked.

Sorry, I used the wrong terminology. Usually when I import a displacement field as an image, I see a faint outline of how the image gets deformed. Additionally, when I hover over the voxels, I can read the vector. In this case, it did not look like this and the output when hovering over the voxels is only a scalar. But as you mentioned above, it has to do with the dimensions of the NIFTI file.

I updated the header with the correct dimensions which fixed the problem!

Thanks for working through this @andy97 and letting us know.

If SynthMorph / mri_convert create and support this kind of nifti file maybe it should be considered a “standard” variant and our reader should try to detect that and work with the files the way they are produced.

Or do we think this is a bug in SynthMorph and that it was meant to be producing “standard” deformation files? If you’re not sure we could contact the SynthMorph/Freesurfer team to clarify.

Yes, these are bugs in VoxelMorph. We described the exact steps (even provided Python code) to fix the standard violations in this issue: Aligning warp field output to ANTs SyN registration · Issue #438 · voxelmorph/voxelmorph · GitHub

@andy97 please add a comment to this issue that summarizes your findings. If many people will ask for the fix then that might motivate the developers.

1 Like