Description
Describe the bug
My prior analysis pipeline involved running commands in the terminal but I am now wanting to code this in Python with nipype so I can generate one script for easier distribution and implementation.
The terminal script:
ants antsRegistrationSyN.sh -d 3 -f MNI_template.nii.gz -m T1w_image.nii.gz -o T1w_to_MNI.nii.gz
ANTs version= 2.4.0
The output full command from "ants antsRegistrationSyN.sh -d 3 -f MNI_template.nii.gz -m T1w_image.nii.gz -o T1w_to_MNI.nii.gz"
antsRegistration --verbose 1 --dimensionality 3 --float 0 --collapse-output-transforms 1 --output [ test,testWarped.nii.gz,testInverseWarped.nii.gz ] --interpolation Linear --use-histogram-matching 0 --winsorize-image-intensities [ 0.005,0.995 ] --initial-moving-transform [ /data/project/CARDS/kcl_nm/test/mni_icbm152_t1_tal_nlin_sym_09c.nii,/data/project/CARDS/kcl_nm/test/sub-001_T1w_biascorr.nii.gz,1 ] --transform Rigid[ 0.1 ] --metric MI[ /data/project/CARDS/kcl_nm/test/mni_icbm152_t1_tal_nlin_sym_09c.nii,/data/project/CARDS/kcl_nm/test/sub-001_T1w_biascorr.nii.gz,1,32,Regular,0.25 ] --convergence [ 1000x500x250x100,1e-6,10 ] --shrink-factors 8x4x2x1 --smoothing-sigmas 3x2x1x0vox --transform Affine[ 0.1 ] --metric MI[ /data/project/CARDS/kcl_nm/test/mni_icbm152_t1_tal_nlin_sym_09c.nii,/data/project/CARDS/kcl_nm/test/sub-001_T1w_biascorr.nii.gz,1,32,Regular,0.25 ] --convergence [ 1000x500x250x100,1e-6,10 ] --shrink-factors 8x4x2x1 --smoothing-sigmas 3x2x1x0vox --transform SyN[ 0.1,3,0 ] --metric CC[ /data/project/CARDS/kcl_nm/test/mni_icbm152_t1_tal_nlin_sym_09c.nii,/data/project/CARDS/kcl_nm/test/sub-001_T1w_biascorr.nii.gz,1,4 ] --convergence [ 100x70x50x20,1e-6,10 ] --shrink-factors 8x4x2x1 --smoothing-sigmas 3x2x1x0vox
To reproduce
When I run the python code I have had to do this in a conda generated virtual environment which is the following:
(kcl_nm) [k1754339@login2 test]0% antsRegistration --version
ANTs Version: 2.5.4.dev1-ga8846f1
Compiled: Nov 27 2024 11:01:20
I then use the reg.inputs below but it is still a bit off:
def ants_normalization_to_MNI(T1_corrected_path, MNI_template_path, output_dir, subject_id, num_threads):
"""
Perform ANTs SyN-based normalization to align the T1-weighted image to the MNI template.
Parameters:
T1_corrected_path (str): Path to the bias-corrected T1-weighted image.
MNI_template_path (str): Path to the MNI template.
output_dir (str): Directory where output files will be saved.
subject_id (str): Subject ID to name the output files appropriately.
num_threads (int): Number of threads to use for parallel processing.
"""
# Define output file paths
output_prefix = os.path.join(output_dir, f"{subject_id}_T1w_space-MNI152NLin2009cSym")
warped_image_path = f"{output_prefix}_Warped.nii.gz"
inverse_warped_image_path = f"{output_prefix}_InverseWarped.nii.gz"
affine_transform_path = f"{output_prefix}_Affine.mat"
warp_transform_path = f"{output_prefix}_Warp.nii.gz"
inverse_warp_transform_path = f"{output_prefix}_InverseWarp.nii.gz"
# Set number of threads for ANTs processing
os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = str(num_threads)
# Print thread info for debugging
print(f"Using {num_threads} threads for ANTs processing.")
# Create a Registration object
reg = Registration()
reg.inputs.verbose = True # Enable verbose output
reg.inputs.num_threads = num_threads # Set number of threads
reg.inputs.dimension = 3 # Set image dimensionality
# Set fixed (MNI template) and moving (T1-weighted) images
reg.inputs.fixed_image = MNI_template_path
reg.inputs.moving_image = T1_corrected_path
# Output transform prefix
reg.inputs.output_transform_prefix = output_prefix
reg.inputs.output_warped_image = warped_image_path
reg.inputs.output_inverse_warped_image = inverse_warped_image_path
# Define transformations
reg.inputs.transforms = ["Rigid", "Affine", "SyN"]
reg.inputs.transform_parameters = [(0.1,), (0.1,), (0.1, 3, 0)]
# Convergence parameters
reg.inputs.number_of_iterations = [[1000, 500, 250, 100], [1000, 500, 250, 100], [100, 70, 50, 20]]
reg.inputs.convergence_threshold = [1e-6, 1e-6, 1e-6]
reg.inputs.convergence_window_size = [10, 10, 10]
# Shrink factors & smoothing sigmas
reg.inputs.shrink_factors = [[8, 4, 2, 1], [8, 4, 2, 1], [8, 4, 2, 1]]
reg.inputs.smoothing_sigmas = [[3, 2, 1, 0], [3, 2, 1, 0], [3, 2, 1, 0]]
reg.inputs.sigma_units = ["vox", "vox", "vox"]
# Similarity metric
reg.inputs.metric = ["MI", "MI", "CC"]
reg.inputs.metric_weight = [1, 1, 1]
reg.inputs.radius_or_number_of_bins = [32, 32, 4]
reg.inputs.sampling_strategy = ["Regular", "Regular", None]
reg.inputs.sampling_percentage = [0.25, 0.25, None]
# Histogram matching & intensity settings
reg.inputs.use_histogram_matching = [False, False, True] # SyN uses histogram matching
reg.inputs.winsorize_lower_quantile = 0.005
reg.inputs.winsorize_upper_quantile = 0.995
# Interpolation method
reg.inputs.interpolation = "Linear"
# Run the registration
print(f"Beginning T1-to-MNI normalization for subject {subject_id}")
reg.run()
print(f"T1-to-MNI normalization complete. Warped image saved at {warped_image_path}")
Expected behavior
A nearly identical image generated via the terminal and python methods
Help:
Please let me know if it is possible to ensure the parameters and methods are exactly the same between pipelines. I am happy to use a different nipype method if needed. Many thanks.