Skip to content

Commit

Permalink
Fixed get_lib_fun when upgrading ANTs; added quick SyN to norma…
Browse files Browse the repository at this point in the history
…lization
  • Loading branch information
dipterix committed Nov 22, 2024
1 parent f018fc1 commit 592c2b6
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 16 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: rpyANTs
Title: An Alternative Advanced Normalization Tools ('ANTs')
Version: 0.0.3.9006
Version: 0.0.3.9007
Authors@R:
person("Zhengjia", "Wang", email = "[email protected]",
role = c("aut", "cre"))
Expand Down
1 change: 1 addition & 0 deletions inst/rpyants/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ mkdocs==1.5.2
mkdocs-material==9.2.3
mkdocstrings==0.22.0
mkdocstrings-python==1.5.2
nibabel>=5.0.1
1 change: 1 addition & 0 deletions inst/rpyants/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
antspyx>=0.3.8
nibabel>=5.0.1
6 changes: 3 additions & 3 deletions inst/rpyants/rpyants/registration/halpern.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import ants
from ..utils.paths import normalize_path
from ..utils.internals import get_lib_fn, ants_process_arguments

def halpern_coregister_ct_mri(fixed_path, moving_path, outprefix, verbose=True):
'''
Expand All @@ -17,8 +18,7 @@ def halpern_coregister_ct_mri(fixed_path, moving_path, outprefix, verbose=True):
moving_path = normalize_path(moving_path, sep="/")
outprefix = normalize_path(outprefix, sep="/")
verbose = "1" if verbose else "0"
from ants import utils
libfn = utils.get_lib_fn("antsRegistration")
libfn = get_lib_fn("antsRegistration")
# Build arguments, see https://github.com/ANTsX/ANTsPy/blob/78dad33ca4e12ae605e80d8d990ec73a52abe273/ants/registration/interface.py
# The difference is we fit coreg twice
args = [
Expand All @@ -42,7 +42,7 @@ def halpern_coregister_ct_mri(fixed_path, moving_path, outprefix, verbose=True):
"-v", verbose
]
# Clean args
processed_args = utils._int_antsProcessArguments(args)
processed_args = ants_process_arguments(args)
reg_exit = libfn(processed_args)
if verbose == "1":
print("---- Call arguments --------------------------------")
Expand Down
93 changes: 93 additions & 0 deletions inst/rpyants/rpyants/registration/normalization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#########################################################
# Custom Normalization scripts
#########################################################

import ants
from ..utils.paths import normalize_path
from ..utils.internals import get_lib_fn, ants_process_arguments

# from rpyants.registration.normalization import normalize_to_template_syn
# fixed_path="/Users/dipterix/rave_data/raw_dir/PAV044/rave-imaging/normalization/anat/MNI152NLin2009bAsym_orig.nii.gz"
# moving_path="/Users/dipterix/rave_data/raw_dir/Precision001/rave-imaging/inputs/MRI/MRI_RAW.nii"
# outprefix="/Users/dipterix/rave_data/raw_dir/Precision001/rave-imaging/mornalize_mni152b_"
# normalize_to_template_syn(fixed_path, moving_path, outprefix, verbose=True)

def normalize_to_template_syn(fixed_path, moving_path, outprefix, verbose=True):
'''
Ridig-body coregistration for CT and MRI for Casey Halpern's lab.
'''
# use forward slash to avoid escaping issues on windows
# Do not use memory pointers!!!
fixed_path = normalize_path(fixed_path, sep="/")
moving_path = normalize_path(moving_path, sep="/")
outprefix = normalize_path(outprefix, sep="/")
verbose = "1" if verbose else "0"
libfn = get_lib_fn("antsRegistration")
# Build arguments, see https://github.com/ANTsX/ANTsPy/blob/78dad33ca4e12ae605e80d8d990ec73a52abe273/ants/registration/interface.py
args = [
"--dimensionality", "3",
"--float", "1",
"-o", "[%s,%s,%s]" % (outprefix, outprefix + "Warped.nii.gz", outprefix + "InverseWarped.nii.gz"),
"--interpolation", "Linear",
"--use-histogram-matching", "1",

# Rigid Transformation
# --transform Rigid[0.1] \
# --metric MI[fixedImage, movingImage, 1, 32, Random, 0.25] \
# --convergence [1000x500x250x0,1e-7,10] \
# --shrink-factors 12x8x4x1 \
# --smoothing-sigmas 5x4x3x1vox \
"--transform", "Rigid[2.0]",
"--metric", "MI[%s,%s,1,32,Random,0.25]" % (fixed_path, moving_path),
"--convergence", "[1000x500x250x0,1e-7,10]",
"--shrink-factors", "12x8x4x1",
"--smoothing-sigmas", "5x4x3x1vox",

# Affine Transformation
# --transform Affine[0.1] \
# --metric MI[/path/to/MNI152b.nii.gz,/path/to/subject_T1w.nii.gz,1,32,Random,0.25] \
# --convergence [1000x500x250x0,1e-7,10] \
# --shrink-factors 12x8x4x1 \
# --smoothing-sigmas 5x4x3x1vox \
"--transform", "Affine[0.1]",
"--metric", "MI[%s,%s,1,32,Random,0.25]" % (fixed_path, moving_path),
"--convergence", "[1000x500x250x0,1e-7,10]",
"--shrink-factors", "12x8x4x1",
"--smoothing-sigmas", "5x4x3x1vox",

# SyN Transformation
# --transform SyN[0.3,4,3] \
# --metric MI[/path/to/MNI152b.nii.gz,/path/to/subject_T1w.nii.gz,1,32,Random,0.25] \
# --convergence [1000x500x500x0,1e-7,7] \
# --shrink-factors 8x4x4x1 \
# --smoothing-sigmas 4x3x1x1vox
"--transform", "SyN[0.3,4.0,3.0]",
"--metric", "MI[%s,%s,1,32,Random,0.25]" % (fixed_path, moving_path),
"--convergence", "[1000x500x500x0,1e-7,7]",
"--shrink-factors", "8x4x4x1",
"--smoothing-sigmas", "4x3x1x1vox",

# subcortical refine stage
"--transform", "SyN[0.3,4,3]",
"--metric", "MI[%s,%s,1,32,Random,0.25]" % (fixed_path, moving_path),
"--convergence", "[200x50x10x0,1e-6,7]",
"--shrink-factors", "4x4x2x1",
"--smoothing-sigmas", "2x2x1x1vox",

"-v", verbose
]
# Clean args
processed_args = ants_process_arguments(args)
reg_exit = libfn(processed_args)
if verbose == "1":
print("---- Call arguments --------------------------------")
print("antsRegistration %s" % " ".join(processed_args))
if not reg_exit == 0:
raise RuntimeError(f"Registration failed with error code {reg_exit}")
return {
"outprefix" : outprefix,
"warpedmovout" : outprefix + "Warped.nii.gz",
"warpedfixout" : outprefix + "InverseWarped.nii.gz",
"transforms" : [outprefix + "0GenericAffine.mat"]
}

50 changes: 38 additions & 12 deletions inst/rpyants/rpyants/registration/yael.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class YAELPreprocess():
def work_path(self):
return self._work_path

def __init__(self, subject_code : str, work_path : str):
def __init__(self, subject_code : str, work_path : str, image_types : list | tuple = ["CT", "T1w", "T2w", "FLAIR", "preopCT", "T1wContrast", "fGATIR", "DWI"] ):
'''
Initialize a YAELPreprocess object.
Expand All @@ -35,6 +35,9 @@ def __init__(self, subject_code : str, work_path : str):
@param work_path: The path to the working directory, for example, `rave-imaging/`.
@type work_path: str
@param image_types: Image types allowed to query
@type image_types: list or tuple
'''

# List of allowed image types
Expand All @@ -45,7 +48,7 @@ def __init__(self, subject_code : str, work_path : str):
# preopCT: preop CT (for showing blood vessels)
# T1wContrast: preop T1w MRI with contrast (for showing blood vessels)
# fGATIR: preop fast Gray Matter Acquisition T1 Inversion Recovery
self.allowed_image_types = ["CT", "T1w", "T2w", "FLAIR", "preopCT", "T1wContrast", "fGATIR"]
self.allowed_image_types = [ *image_types ]
# Each item is a dictionary returned by `parse_bids_filename`
self._images = {}

Expand All @@ -65,7 +68,12 @@ def _fix_image_path(self, parsed : dict, name = None) -> dict:
if parsed['ext'] == "nii":
parsed['ext'] = "nii.gz"
if parsed['components'].get('ses', None) is None:
if parsed['type'] in self.allowed_image_types:
image_type = parsed['type']
if image_type.startswith("preop"):
parsed['components']['ses'] = 'preop'
elif image_type.startswith("preop"):
parsed['components']['ses'] = 'postop'
elif image_type in self.allowed_image_types:
if parsed['type'] == "CT":
parsed['components']['ses'] = 'postop'
else:
Expand Down Expand Up @@ -191,10 +199,11 @@ def set_image(self, type : str, path : str, **kwargs):
* overwrite : If True, overwrite the existing image. Otherwise throw an error if image exists.
* Other argument keys are BIDS components (e.g., `sub`, `ses`, `space`, `rec`, `desc`)
'''
if type not in self.allowed_image_types:
raise ValueError(f"Invalid image type: {type}. Must be one of {self.allowed_image_types}")
if not os.path.exists(path):
raise FileNotFoundError(f"Invalid image path: {path}")
if type not in self.allowed_image_types:
# raise ValueError(f"Invalid image type: {type}. Must be one of {self.allowed_image_types}")
self.allowed_image_types.append(type)
overwrite = kwargs.get('overwrite', False)
existing_path = self.input_image_path(type)
if not overwrite and existing_path is not None:
Expand Down Expand Up @@ -431,11 +440,19 @@ def map_to_template(self,
native_mask = ants.image_read(native_mask_path)
else:
native_mask = None
affine_reg = ants.registration(fixed = fix_img, moving = mov_img,
affine_reg = ants.registration(
fixed = fix_img, moving = mov_img,
mask = template_mask, moving_mask = native_mask,
outprefix = file_path(ants_outputdir, "affine_"), type_of_transform = "antsRegistrationSyN[a]",
aff_metric = "CC", syn_metric = "CC", write_composite_transform = False,
aff_sampling = 2, syn_sampling = 2, verbose = verbose)
outprefix = file_path(ants_outputdir, "affine_"),
type_of_transform = "antsRegistrationSyN[a]",
write_composite_transform = False,
aff_metric = "mattes",
aff_random_sampling_rate=0.25,
aff_iterations=(1000, 500, 250, 0),
aff_shrink_factors=(12, 8, 4, 1),
aff_smoothing_sigmas=(5, 4, 3, 1),
verbose = verbose
)
# save the registration result
transform_from_template_prefix = to_bids_prefix({ 'sub': self._subject_code, 'from': template_name, 'to': native_type, 'desc': 'affine+SyN', })
transform_to_template_prefix = to_bids_prefix({ 'sub': self._subject_code, 'from': native_type, 'to': template_name, 'desc': 'affine+SyN', })
Expand All @@ -453,9 +470,18 @@ def map_to_template(self,
deformable_reg = ants.registration(
fixed = fix_img, moving = affine_reg['warpedmovout'],
mask = template_mask, moving_mask = native_mask,
outprefix = file_path(ants_outputdir, "deformable_"), type_of_transform = "antsRegistrationSyN[b]",
aff_metric = "CC", syn_metric = "CC", write_composite_transform = False,
aff_sampling = 2, syn_sampling = 2, verbose = verbose)
outprefix = file_path(ants_outputdir, "deformable_"),
type_of_transform = "antsRegistrationSyNQuick[s]",
write_composite_transform = False,
aff_metric = "mattes",
aff_random_sampling_rate=0.25,
aff_iterations=(1000, 500, 250, 0),
aff_shrink_factors=(12, 8, 4, 1),
aff_smoothing_sigmas=(5, 4, 3, 1),
syn_metric="mattes",
reg_iterations=(1000, 500, 500, 0),
verbose = verbose
)
# save forward (mov -> fix) transforms
# simple experiment to check the composition of the transforms: the following two are the same
# np.matmul(ants_AffineTransform_to_m44(affine_1), ants_AffineTransform_to_m44(affine_0))
Expand Down
19 changes: 19 additions & 0 deletions inst/rpyants/rpyants/utils/internals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

def get_lib_fn(fn):
try:
from ants import utils
libfn = utils.get_lib_fn(fn)
except:
from ants.internal import get_lib_fn
libfn = get_lib_fn(fn)
return libfn


def ants_process_arguments(args):
try:
from ants import utils
ret = utils._int_antsProcessArguments(args)
except:
from ants.internal import process_arguments
ret = process_arguments(args)
return ret

0 comments on commit 592c2b6

Please sign in to comment.