Convert fmriprep transformation txt file to h5 format

Hi everyone. I’d like to convert fmriprep’s *_from-T1w_to-fsnative_mode-image_xfm.txt file to *.h5 format so that I can apply the transformation using the Python implementation of ANTs’ function apply_to_image(). I’ve been trying to use the h5py package in Python to accomplish this, but I haven’t been able to get it working. Any suggestions? See below for details.

It fails at sub_to_fsnative = ants.read_transform(trans_h5) with the following error:


"Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/flutist4129/Library/Python/3.9/lib/python/site-packages/ants/core/ants_transform_io.py", line 330, in read_transform
    dimensionUse = libfn1(filename)
RuntimeError: /Users/admin/actions-runner/_work/ANTsPy/ANTsPy/itksource/Modules/IO/TransformBase/src/itkTransformFileReader.cxx:128:
ITK ERROR: TransformFileReaderTemplate(0x60004e7a2600): Could not create Transform IO object for reading file /Users/flutist4129/Documents/Northwestern/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/sub-MWMH212_ses-2_from-T1w_to-fsnative_mode-image_xfm.h5
  Tried to create one of the following:
    HDF5TransformIOTemplate
    HDF5TransformIOTemplate
    MatlabTransformIOTemplate
    MatlabTransformIOTemplate
    TxtTransformIOTemplate
    TxtTransformIOTemplate
  You probably failed to set a file suffix, or
    set the suffix to an unsupported type."
import os
import json
import sys, getopt
import argparse
import ants 
import numpy as np
import h5py

parser = argparse.ArgumentParser()
parser.add_argument('-i')
parser.add_argument('-o')
parser.add_argument('-s')
parser.add_argument('-ss')
parser.add_argument('-t', nargs='+') 
args = parser.parse_args()

indir = args.i 
outdir = args.o 
sub = args.s 
ses = args.ss 
tasks = args.t 

# Directory where preprocessed fMRI data is located
subindir = os.path.join(indir, sub)
sesindir = os.path.join(subindir, ses)
funcindir = os.path.join(sesindir, 'func')

# Directory where outputs should go
suboutdir = os.path.join(outdir, sub)
sesoutdir = os.path.join(suboutdir, ses)
os.makedirs(os.path.join(outdir, sub, ses), exist_ok=True)

# Get the number of sessions
numses = 0
for root, dirs, files in os.walk(subindir):
    for dir in dirs: 
        if dir.startswith('ses'):
            numses = numses + 1

# Load Freesurfer's T1w image (fsnative)
if numses == 1:
    fs_T1 = ants.image_read(f"{sesoutdir}/anat/fs_T1w.nii.gz")
elif numses == 2:
    fs_T1 = ants.image_read(f"{suboutdir}/anat/fs_T1w.nii.gz")
else:
    raise ValueError("There is some number of sessions other than 1 or 2.")

# Set up file paths to transformation txts
trans_txt = f"{sub}_{ses}_from-T1w_to-fsnative_mode-image_xfm.txt" #sub-{sub}_from-T1w_to-MNI152NLin6Asym_mode-image_xfm.h5
trans_h5 = f"{sub}_{ses}_from-T1w_to-fsnative_mode-image_xfm.h5"
if numses == 1:
    trans_txt = f"{sesindir}/anat/{trans_txt}"
    trans_h5 = f"{sesoutdir}/anat/{trans_h5}"
elif numses == 2:
    trans_txt = f"{subindir}/anat/{trans_txt}"
    trans_h5 = f"{suboutdir}/anat/{trans_h5}"
else:
    raise ValueError("There is some number of sessions other than 1 or 2.")

# Process the file to extract the transformation parameters
parameters = []
with open(fp_txt, 'r') as file:
    for line in file:
        if line.startswith('Parameters:'):
            # Extract numbers from this line
            numbers = line.split()[1:]  # Skip the "Parameters:" part
            parameters = [float(num) for num in numbers]
            break  # Assuming only one set of parameters is needed

# Convert the list of parameters into a NumPy array
sub_to_fsnative = np.array(parameters).reshape(3, 4)  # Reshape according to your matrix shape, here it's assumed to be 3x4

# Create an HDF5 file and write the matrix to it 
with h5py.File(trans_h5, 'w') as hdf:
    hdf.create_dataset('sub_to_fsnative', data=sub_to_fsnative)

sub_to_fsnative = ants.read_transform(trans_h5)
          
for task in tasks:
    # Load input image 
    input_path = f"{funcindir}/{sub}_{ses}_{task}_space-T1w_desc-preproc_bold.nii.gz"
    input = ants.image_read(input_path)

    # Apply transform and write out transformed image
    output = sub_to_fsnative.apply_to_image(input, reference=fs_T1)
    output_path = f"{sesindir}/func/{sub}_{ses}_{task}_space-fsnative_desc-preproc_bold.nii.gz"
    ants.image_write(output, output_path)

Hi @butellyn,

I don’t think this is necessary. Those .txt files should be ITK/ANTs compatible, if I’m not mistaken. What kind of error do you get using those .txt files directly?

Best,
Steven

Hi @Steven! Thank you for always being so helpful and responsive :smile:

When I change the sub_to_fsnative = ants.read_transform(trans_h5) to sub_to_fsnative = ants.read_transform(trans_txt) (so using the *.txt file directly), I get the following error when I run output = sub_to_fsnative.apply_to_image(input, reference=fs_T1):

>>> output = sub_to_fsnative.apply_to_image(input, reference=fs_T1) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/flutist4129/Library/Python/3.9/lib/python/site-packages/ants/core/ants_transform.py", line 175, in apply_to_image tform_fn = utils.get_lib_fn('transformImage%s%s' % (self._libsuffix, image._libsuffix)) File "/Users/flutist4129/Library/Python/3.9/lib/python/site-packages/ants/utils/process_args.py", line 29, in get_lib_fn return lib.__dict__[string] KeyError: 'transformImageF3F4'

Have you seen this before?

Hi @butellyn,

You might have luck if you do something like

ConvertTransformFile 3 ${xfm}.txt ${xfm}.mat -m

and then use the .mat file in ANTs. Based on the documentation (ants.core.ants_transform_io — ANTsPy master documentation) it looks like mainly .mat is used in examples in the Python version.

ConvertTransformFile is an ANTs command.

Best,
Steve

Hi @Steven! Thanks for the advice. I tried out the function:

transin=${anatindir}/sub-${subid}_ses-${sesid}_from-T1w_to-fsnative_mode-image_xfm.txt
transout=${anatoutdir}/sub-${subid}_ses-${sesid}_from-T1w_to-fsnative_mode-image_xfm.mat
ConvertTransformFile 3 ${tranin} ${transout} -m

And got this error: " file /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/sub-MWMH212_ses-2_from-T1w_to-fsnative_mode-image_xfm.mat does not exist ."

The file does not exist, but it is the file I am trying to create, so it is not supposed to exist. I am not sure why this is an error.

I double checked the documentation, and it looks like the arguments are in the correct order:

I tried reversing the order of the input and output files, and got the same error (which actually makes sense in this direction). Any idea what is going on?

Hi @butellyn

When you say you got the same error, does that mean the second try with the switched arguments also resulted in an error message with the .mat message? Can you double check /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/ is typed correctly, because I could imagine that first error happening if the directory did not exist. Have you tried outputting to other directories (e.g., just writing to ~) as a test?

Best,
Steven

/projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/ exists:

I’m actually getting a different error when I switched in and outputs. Not sure what I did last time that I saw the same error.

When I try writing to my home directory, I get the same error:

What if you cd to /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/ and then remove the directory prefix from the file? So it’s:

ConvertTransformFile 3 sub-${subid}_ses-${sesid}_from-T1w_to-fsnative_mode-image_xfm.txt sub-${subid}_ses-${sesid}_from-T1w_to-fsnative_mode-image_xfm.mat -m

Same error:

Hi @butellyn,

In your ls output the txt file is not there, are you sure you’re in the right directory where the .txt transform file is?

Best,
Steven

Ah! I have them in different directories. Didn’t think about the command. Here’s them copied into the same place. It works now! I have no idea why it isn’t working when I give the full paths. Do you have a sense of why?

Omg. It was a typo. I refered to ${transin} as ${tranin} in my initial attempts. This is why I should always use echo. Sorry about this!

Okay. After I transform the *.txt file to *.mat, I tried running an antsApplyTransforms directly (because I had to get the CLI working for ANTs):

antsApplyTransforms \
    -d 4 \
    -i ${VolumefMRI} \
    -r ${anatoutdir}/fs_T1w.nii.gz \
    -t ${transout} \
    -o ${VolumefMRI_fs}

and got this error:

Description: itk::ERROR: itk::ERROR: TransformFileReaderTemplate(0x3b18b20): Transform IO: MatlabTransformIOTemplate

failed to read file: /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/sub-MWMH212_ses-2_from-T1w_to-fsnative_mode-image_xfm.mat:

Not sure what this is about, so I tried running antsApplyTransforms with the original *.txt:

antsApplyTransforms \
    -d 4 \
    -i ${VolumefMRI} \
    -r ${anatoutdir}/fs_T1w.nii.gz \
    -t ${transin} \
    -o ${VolumefMRI_fs}

I did not get an error doing this, but I didn’t get any output either, despite not getting any kind of warning message. I checked that the command is what I thought it was by echoing it:

antsApplyTransforms \
    -d 4 \
    -i /projects/b1108/studies/mwmh/data/processed/neuroimaging/fmriprep_23.1.4/sub-MWMH212/ses-2/func/sub-MWMH212_ses-2_task-rest_space-T1w_desc-preproc_bold.nii.gz \
    -r /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/fs_T1w.nii.gz \
    -t /projects/b1108/studies/mwmh/data/processed/neuroimaging/fmriprep_23.1.4/sub-MWMH212/ses-2/anat/sub-MWMH212_ses-2_from-T1w_to-fsnative_mode-image_xfm.txt \
    -o /projects/b1108/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/func/sub-MWMH212_ses-2_task-rest_space-fsnative_desc-preproc_bold.nii.gz

I also double checked that all of these files exist, and only the desired output was missing:

So I went back to my Python script to see if I could get the *.mat file working there:

trans_mat = f"{sub}_{ses}_from-T1w_to-fsnative_mode-image_xfm.mat"
trans_mat = f"{sesoutdir}/anat/{trans_mat}"
sub_to_fsnative = ants.read_transform(trans_mat)

But I got similar error to when I tried to use antsApplyTransforms:

ITK ERROR: TransformFileReaderTemplate(0x60004f7d1d80): Transform IO: MatlabTransformIOTemplate
   failed to read file: /Users/flutist4129/Documents/Northwestern/studies/mwmh/data/processed/neuroimaging/surf/sub-MWMH212/ses-2/anat/sub-MWMH212_ses-2_from-T1w_to-fsnative_mode-image_xfm.mat

Do you have any thoughts about what might be going on here?

I am not sure, perhaps try asking here? ITK

1 Like

Looks like it is about the number of TRs: antsApplyTransforms not able to read transform txt · Issue #1681 · ANTsX/ANTs · GitHub

Also realized I can select fsnative as an output space from fmriprep, circumventing this problem.

Hi @butellyn,

I don’t think those will be the same - fsnative will produce surface GIFTI outputs. What you were describing earlier in the post would align the volumetric BOLD sequence to the the FreeSurfer output brain.

Best,
Steven