Fsnative to fmriprep fsnative space

I’ve noticed that the coordinates in the fsnative surfaces output from fmriprep differ from the freesurfer sourcedata fsnative surfaces.

I’m wondering what transformation is used to go from one to the other?

So far I’ve tried:

  1. mris_convert --to-scanner lh.pial lh.pial.surf.gii

Having mri_convert output in scanner space instead of FS tkr space gets you VERY close to the fmriprep output, but there is a slight offset.

  1. Subsequently using wb_command --surface-apply-affine to apply the “_from-fsnative_to-T1w_mode-image_xfm” transformation. This also doesn’t match the fmriprep surface coordinates.

Is there a different command or different transformation matrix output by fmriprep that could be used to do this successfully?

Thanks,
Nathan

For those who are curious, this seems to work, but I am not totally clear why. See below:

  1. mris_convert lh.pial lh.pial.surf.gii

# run code adapted from:
# https://fmriprep.org/en/1.0.3/_modules/fmriprep/interfaces/surf.html

import numpy as np
import nitransforms as nt
import nibabel as nb

in_surf = os.path.join(base_path,surf_path,'lh.pial.surf.gii')
tx_file = os.path.join(base_path,anat_path,'sub-XXX_ses-XX_acq-highresAP_from-fsnative_to-T1w_mode-image_xfm.txt')

transform = nt.io.itk.ITKLinearTransform.from_filename(tx_file).to_ras()
transform = np.linalg.inv(transform) 

img = nb.load(in_surf)

pointset = img.get_arrays_from_intent('NIFTI_INTENT_POINTSET')[0]
coords = pointset.data.T
c_ras_keys = ('VolGeomC_R', 'VolGeomC_A', 'VolGeomC_S')
ras = np.array([[float(pointset.metadata[key])]
                for key in c_ras_keys])
ones = np.ones((1, coords.shape[1]), dtype=coords.dtype)
# Apply C_RAS translation to coordinates, then transform
pointset.data = transform.dot(np.vstack((coords + ras, ones)))[:3].T.astype(coords.dtype)

secondary = nb.gifti.GiftiNVPairs('AnatomicalStructureSecondary', 'MidThickness')
geom_type = nb.gifti.GiftiNVPairs('GeometricType', 'Anatomical')
has_ass = has_geo = False
for nvpair in pointset.meta.data:
    # Remove C_RAS translation from metadata to avoid double-dipping in FreeSurfer
    if nvpair.name in c_ras_keys:
        nvpair.value = '0.000000'
    # Check for missing metadata
    elif nvpair.name == secondary.name:
        has_ass = True
    elif nvpair.name == geom_type.name:
        has_geo = True

img.to_filename(out_file)

In particular, it is not clear to me why one should need to invert the stored transform, since shouldn’t it already be from fsnative to T1w coords? But the code I based this off of uses the LTA transform, which I believe should be from T1w to fsnative coords (see: nitransforms.io.lta — nitransforms 24.1.2 documentation).

Am I confusing something here?

Thanks again,
Nathan