Extracting individual transforms from composite .h5 files (fmriprep)

Hi there,

I’m new to using .h5 files, which fmriprep uses to store (composite) transforms. I have been able to successfully propagate labels from an individual to the template space:

antsApplyTransforms -d 3 --float 1 --verbose 1 -i sub-0086_T1w_label-aseg_roi.nii.gz -o output/sub-0086_T1w_space-MNI152NLin2009cAsym_label-aseg_roi.nii.gz -r MNI2009c_T1w.nii.gz -t sub-0086_T1w_target-MNI152NLin2009cAsym_warp.h5 -n NearestNeighbor

(Hypothetically) if I had the individual transform files (sub-0086_T1w_target-MNI152NLin2009cAsym_warp.nii.gz and sub-0086_T1w_target-MNI152NLin2009cAsym_affine.mat), I could have gotten the same result with the following call:

antsApplyTransforms -d 3 --float 1 --verbose 1 -i sub-0086_T1w_label-aseg_roi.nii.gz -o output/sub-0086_T1w_space-MNI152NLin2009cAsym_label-aseg_roi.nii.gz -r MNI2009c_T1w.nii.gz -t sub-0086_T1w_target-MNI152NLin2009cAsym_warp.nii.gz -t sub-0086_T1w_target-MNI152NLin2009cAsym_affine.mat -n NearestNeighbor

I have a particular use case where having these transformations separated would be beneficial; specifically, because I want to propagate some 3D points (e.g. anterior and posterior commissure) using antsApplyTransformsToPoints:

antsApplyTransformsToPoints -d 3 -i sub-0086_T1w_label-ACPC_points.csv -o output/sub-0086_T1w_space-MNI152NLin2009cAsym_label-ACPC_points.csv -t [sub-0086_T1w_target-MNI152NLin2009cAsym_affine.mat,1] -t sub-0086_T1w_target-MNI152NLin2009cAsym_inversewarp.nii.gz

Note that the ordering of linear and nonlinear transformations is reversed for point transformation, and also requires the inverse deformation field as input. For what it’s worth I tried inputting both forward and backward composite transforms derived from fmriprep and the coordinates were nowhere close to the true location (MNI2009c_T1w_label-ACPC_points.csv)

This is somewhat related to a few previous posts here and on the ants forum:

  1. FMRIPREP ANTS .h5 composite transform file decomposition? How exactly the .h5 decomposition was performed is not described and the focus of discussion was more on capturing the rigid component from the linear transformation matrix. @ChenChiaLan
  2. FMRIPREP: inverse t1_to_mni warp describing modifications to include both forward and inverse .h5 files.
  3. https://sourceforge.net/p/advants/discussion/840260/thread/ff4587ef/?limit=25#c007 This covers the usage of antsApplyTransformsToPoints

Any thoughts on how to resolve this? I’ve provided all the files as a .tar.gz link for anyone able to take a closer look.

-jon


Also, hope to see some of you at the INCF Montreal meeting next week!

Tried digging a little deeper into this and found a thread on the ANTs forum using the python package called h5py to extract the affine transformation.

Alternatively is there an option in fmriprep that could allow storing the individual transforms rather than the composite h5?

Appreciate any thoughts on how to deal with this.

Do I understand correctly that antsApplyTransformsToPoints does not work correctly with an composite transform (h5) even though antsApplyTransforms works with the same transform? If so do the ANTs developers know about it?

Thanks for taking the time to respond! I think there may be two ways of considering the problem:

  1. antsApplyTransformsToPoints is not behaving as expected with the composite (h5) files; however, there is a solution that works if the linear and nonlinear warps are provided as separate files.
  2. fMRIPrep outputs two composite .h5 transforms (one forward and one backward). Was there a reason to adopt this format rather than store the output in each direction as a linear text file and nonlinear warp field? The latter seems more flexible to me for permitting different transform-based manipulations.

I decided to ask on neurostars first because some old threads on the ANTs forum suggested h5 was not being actively supported by the developers. However, I was fortunate to run into Nick Tustison this past week at the Neuroinformatics meeting. He was very receptive to finding a solution; some possibilities discussed include: (1) implementing a converter for decomposing the h5 files into component parts; (2) modifying antsApplyTransformsToPoints to handle .h5 files appropriately. Nick suggested I start a thread on the ANTs forum too, which I’ve just done: https://sourceforge.net/p/advants/discussion/840261/thread/ba6a10b0/

1 Like

A single file is more compact and less error prone than two files (especially considering order of applying transforms. Most users, just want to apply transforms rather than modify them.

I like option (2). Glad you are working with Nick to figure this out!

1 Like

I’m exploring option 2 in https://github.com/nipy/nibabel/pull/656

1 Like

Thanks for looking into this, @oesteban!

When you have a chance, can you expand on how the PR in nibabel would be considered a solution to option 2? I want to make sure I’m able to recap on the different possible options (once a solution is found).

Update from the parallel thread on the ants forum: I’m testing out the use of the CompositeTransformUtil --disassemble tool (recommended by Nick). Will report back once I’ve sorted out whether this works.

I realize that some of the functionality I’m interested in is beyond the scope of the typical user, but what about the possibility of having those intermediate files stored in a working/scratch directory? Also one of the reasons I ask is that I don’t see any mention of the .h5 format in BEP14: the BIDS derivative document on transformations. The transform derivatives are described as separate linear _affine.txt and _warp.nii.gz files.

At this point, that pull request has no functionality for that. But it is the idea to implement it some time soon.

Okay specifically by option 2, you mean modifying antsApplyTransformsToPoints to handle .h5 files? Using nibabel seems like it would be a different solution.

(The numbering of options was a bit ambiguous initially – which is why I plan to recap at some point.)

The implementation of a module that reads and writes transforms from whatever neuroimaging software would cover h5 files when it comes to transforms from ANTs.

I could give a command line interface for resamplings and transform conversions. In that case, then the conversions command would have a decompose option or similar.

1 Like

Okay turns out antsApplyTransformsToPoints works just fine with the .h5 composite transforms provided as output by fMRIPrep. One of my problems stemmed from the points being in RAS anatomical coordinate space (they were placed using 3D Slicer) rather than LPS space. Anyone interested in this issue can look at the link here.

If your goal like mine is to transform a set of points from native space to those output by fMRIPrep the following solutions work:

(1) using the .h5 inverse composite transform:

antsApplyTransformsToPoints -d 3 -i sub-0086_T1w_label-ACPC_points_LPS.csv -o subject_to_template_composite.csv -t sub-0086_T1w_space-MNI152NLin2009cAsym_target-T1w_warp.h5

(2) disassembling the .h5 transforms first using the ANTs CompositeTransformUtil --disassemble then running antsApplyTransformsToPoints:

CompositeTransformUtil --disassemble sub-0086_T1w_space-MNI152NLin2009cAsym_target-T1w_warp.h5 sub-0086_T1w_space-MNI152NLin2009cAsym_target-T1w_warp
CompositeTransformUtil --disassemble sub-0086_T1w_target-MNI152NLin2009cAsym_warp.h5 sub-0086_T1w_target-MNI152NLin2009cAsym_warp
antsApplyTransformsToPoints -d 3 -i sub-0086_T1w_label-ACPC_points.csv -o subject_to_template_disassembled.csv -t [00_sub-0086_T1w_target-MNI152NLin2009cAsym_warp_AffineTransform.mat,1] -t 00_sub-0086_T1w_space-MNI152NLin2009cAsym_target-T1w_warp_DisplacementFieldTransform.nii.gz

@oesteban is working on a module for reading and writing transforms between neuroimaging software in nibabel. Could this perhaps take advantage of the CompositeTransformUtil --disassemble function?

2 Likes

Hi, I’m facing a pretty similar issue which is not yet solved by the (very useful) information in this thread. I want to transform MNI Atlas coordinates into subject (T1w) space. However, if I use antsApplyTransformsToPoints I get incorrect results.

As an example, lets take this voxel in MNI space:

I then used:

antsApplyTransformsToPoints -d 3 -i coord.csv -o coord_transformed.csv -t sub-001_from-MNI152Lin_to-T1w_mode-image_xfm.h5

The results are the same if I use CompositeTransformUtil --disassemble to split the composite into individual transforms:

antsApplyTransformsToPoints -d 3 -i coord.csv -o coord_transformed.csv -t affine.mat -t field.nii.gz

However, if I create an empty nifti in MNI space and mark the single voxel in it to use ‘antsApplyTransforms’, I get a correct result:

antsApplyTransforms -e 3 -d 3 -i coord.nii.gz -r ref_target.nii.gz -o coord_transformed.nii.gz -t sub-001_from-MNI152Lin_to-T1w_mode-image_xfm.h5

Am I missing something here? Any help would be greatly appreciated. Thank you!

@mibur sorry for pinging you months later but wanted to provide an answer in case you were still wondering.

I think you might be applying the incorrect transform when running the function. From the help menu of antsApplyTransformsToPoints:

Points are transformed in the OPPOSITE direction of images, therefore you should pass the inverse of what is needed to warp the images. Eg if the image is warped by Affine.mat, you should pass the inverse of Affine.mat to transform points defined in the same space as the image.

So in your situation, you are trying to get the points into patient space from MNI. However, you are passing sub-001_from-MNI152Lin_to-T1w_mode-image_xfm.h5. Have you tried using the opposite transform sub-001_from-T1w_to-MNI152Lin_mode-image_xfm.h5? When you pass this transform make sure you put the transform within square brackets and indicate if you want to use the inverse (0 or 1):

antsApplyTransformsToPoints -d 3 -i coord.csv -o coord_transformed.csv -t [ sub-001_from-T1w_to-MNI152Lin_mode-image_xfm.h5 , 0 ]

When decomposing the .h5 composite transform you obtain the affine .mat and the displacement .nii.gz, which are just the components of the original transform. So it’s actually good you obtain the same results. You would need to decompose the opposite transform sub-001_from-T1w_to-MNI152Lin_mode-image_xfm.h5 to get the points into MNI space.

antsApplyTransformsToPoints -d 3 -i coord.csv -o coord_transformed.csv -t [ 01_sub-001_from-T1w_to-MNI152Lin_mode-image_xfm_AffineTransform.mat , 0 ] -t 00_sub-087_from-T1w_to-MNI152NLin_mode-image_xfm_DisplacementFieldTransform

Note: when passing the components of the transform the order matters, the process is noncommutative.

So,

-t [ 01_sub-001_from-T1w_to-MNI152Lin_mode-image_xfm_AffineTransform.mat , 0 ] -t 00_sub-087_from-T1w_to-MNI152NLin_mode-image_xfm_DisplacementFieldTransform

is not the same as

-t 00_sub-087_from-T1w_to-MNI152NLin_mode-image_xfm_DisplacementFieldTransform -t [ 01_sub-001_from-T1w_to-MNI152Lin_mode-image_xfm_AffineTransform.mat , 0 ]

You also need to make sure your points are defined in LPS and not RAS. To move from RAS to LPS you need to multiply X,Y by -1.

Hope that helps in some way,

Greydon

2 Likes

Thank you so much for this elaborate response!

I was able to figure it out eventually but forgot about this thread (and failed to report it here, sorry about that). Your explanation is spot on though, and might assist other inexperienced people like me in the future :slight_smile:

1 Like

glad to hear it all worked out! :+1:

1 Like