I would like to use the combine the output of AFNI 3dVolReg (the .aff12.1D file) with other FSL and ANTs derived transformation in a single interpolation step. Does anyone know of a tool or script for converting AFNI transformations to FSL or ITK formats?
From Bob Cox:
I have no idea about the other formats (FSL or ITK). However, I can tell you that AFNI transformation matrices are coordinate-based (not index-based), and
the coordinates are sign reversed in x and y from FSL — that is, AFNI uses DICOM order for xyz. So if you have an AFNI matrix
[ a b c d ]
T = [ e f g h ]
[ i j k l ]
[ 0 0 0 1 ] — this row is implicit, not in the file
then since x_FSL = [A] x_AFNI where
[ -1 0 0 0 ]
A = [ 0 -1 0 0 ]
[ 0 0 1 0 ]
[ 0 0 1 0 ] which is it’s own inverse, of course;
so the matrix that transforms x_FSL in the same way as T transforms x_AFNI is the matrix product T’ = [ A T A ], which is just
[ a b -c -d ]
T’ = [ e f -g -h ]
[ -i -j k l ]
[ 0 0 0 1 ]
I would also like to use such a script - if it exists. Are the visitors of this thread also looking for something similar?
I’ve seen a 2011 email about the same posted on the AFNI mailing list: https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=fsl;81d877a8.1101
And again this year (2017): https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=fsl;49ee0deb.1704
It seems that the translation matrix needs to be converted from neurological (DICOM/ AFNI) to neuroimaging (FSL/SPM) by swapping the order in x and y, and corrected for where the origin is placed (therefore, this calculation is volume dependent).
We made some progress on this topic in this PR: https://github.com/poldracklab/fmriprep/pull/1354
Have you made this function? I checked the link you provide but still very confused how to use fmriprep utility to do this. Can you give more insignts here?
We are streaming out all the conversions to a submodule in nibabel: https://github.com/nipy/nibabel/pull/656/files
@oesteban, Thanks so much. It seems that the transform module has not been released.
But I looked how you implement it. It seems that you follow Bob’s calculation, give an afni transform;
# afni xfm is in LPS+ space afni_LPS+_xfm = [[a, b, c, d], [e, f, g, h], [i, j, k, l], [0,0,0,1] # the corresponding xfm in RAS+ space RAS+_xfm = [[a, b, -c, -d], [e, f, -g, -h], [-i, -j, k, l], [0,0,0,1]
Have you tested whether this conversion is correct? Actually I did some testing. For instance, I aligned volume A to volume B using afni align_epi_anat.py program and obtained afni_B2A_xfm. Then I converted it to RAS+_B2A_xfm using the rationale above. Then I simply did
import nibabel as nib volB = nib.load('B.nii.gz') nib.save(volB._class__(volB.get_data(), RAS+_B2A_xfm.dot(B.affine), volB.header), 'Bal2A.nii.gz')
If the afni_B2A_xfm and the conversion above are correct, I can open any visualization tool and the Bal2A.nii.gz and A.nii.gz should aligned. However, it failed on my side.
I also manually created some RAS+_xfm and then converted them back into the afni format, it also failed to produce correct alignments.
I doubt that there are some additional processing performed internally in AFNI. If that is the case, the transformation conversion above (i.e., between LPS+ and RAS+ spaces) are somehow problematic.
Have you tested it or did I miss something here???
Hi, this is all the testing I’ve done this far: https://github.com/oesteban/BIDS-X5/blob/master/01%20-%20Preparing%20images.ipynb
AFNI conversions fail when the x-forms are not aligned with the canonical axes. Chris’ original implementation in fMRIPrep enforced this alignment in a previous processing node.
@oesteban, thanks. Yeah, I did run your testing code and I noticed that the afni parts fails. My situation is, I have some transformations generated by afni and I want to somehow use them in nibabel (i.e., directly change the sform/qform in header) without resampling the data. Do you have some suggestions? I am not familar with fmriprep, how can I enforce the alignment with the canonical axes?
I really appreciated your efforts as it is important to unify the transformation across software.
nibabel.funcs.as_closest_canonical is what you need here.