Skip to content

[ENH] Support for multiple initial-moving-transforms for ants.Registration and ComposeMultiTransforms #2187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Oct 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion nipype/interfaces/ants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@

# Utility Programs
from .utils import (AverageAffineTransform, AverageImages, MultiplyImages,
CreateJacobianDeterminantImage, AffineInitializer)
CreateJacobianDeterminantImage, AffineInitializer,
ComposeMultiTransform)
329 changes: 239 additions & 90 deletions nipype/interfaces/ants/registration.py

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions nipype/interfaces/ants/tests/test_auto_ComposeMultiTransform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from __future__ import unicode_literals
from ..utils import ComposeMultiTransform


def test_ComposeMultiTransform_inputs():
input_map = dict(args=dict(argstr='%s',
),
dimension=dict(argstr='%d',
position=0,
usedefault=True,
),
environ=dict(nohash=True,
usedefault=True,
),
ignore_exception=dict(nohash=True,
usedefault=True,
),
num_threads=dict(nohash=True,
usedefault=True,
),
output_transform=dict(argstr='%s',
keep_ext=True,
name_source=['transforms'],
name_template='%s_composed',
position=1,
),
reference_image=dict(argstr='%s',
position=2,
),
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
transforms=dict(argstr='%s',
mandatory=True,
position=3,
),
)
inputs = ComposeMultiTransform.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value


def test_ComposeMultiTransform_outputs():
output_map = dict(output_transform=dict(),
)
outputs = ComposeMultiTransform.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
1 change: 1 addition & 0 deletions nipype/interfaces/ants/tests/test_auto_Registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def test_Registration_inputs():
usedefault=True,
),
initial_moving_transform=dict(argstr='%s',
exists=True,
xor=['initial_moving_transform_com'],
),
initial_moving_transform_com=dict(argstr='%s',
Expand Down
79 changes: 61 additions & 18 deletions nipype/interfaces/ants/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

import os

from ...utils.filemanip import split_filename
from ..base import (TraitedSpec, File, traits, isdefined, InputMultiPath,
CommandLine, CommandLineInputSpec)
from ..base import TraitedSpec, File, traits, InputMultiPath
from .base import ANTSCommand, ANTSCommandInputSpec


Expand Down Expand Up @@ -59,12 +57,16 @@ def _list_outputs(self):
class AverageImagesInputSpec(ANTSCommandInputSpec):
dimension = traits.Enum(3, 2, argstr='%d', mandatory=True,
position=0, desc='image dimension (2 or 3)')
output_average_image = File("average.nii", argstr='%s', position=1, desc='the name of the resulting image.',
usedefault=True, hash_files=False)
normalize = traits.Bool(argstr="%d", mandatory=True, position=2, desc='Normalize: if true, the 2nd image' +
'is divided by its mean. This will select the largest image to average into.')
images = InputMultiPath(File(exists=True), argstr='%s', mandatory=True, position=3,
desc='image to apply transformation to (generally a coregistered functional)')
output_average_image = File(
"average.nii", argstr='%s', position=1, usedefault=True, hash_files=False,
desc='the name of the resulting image.')
normalize = traits.Bool(
argstr="%d", mandatory=True, position=2,
desc='Normalize: if true, the 2nd image is divided by its mean. '
'This will select the largest image to average into.')
images = InputMultiPath(
File(exists=True), argstr='%s', mandatory=True, position=3,
desc='image to apply transformation to (generally a coregistered functional)')


class AverageImagesOutputSpec(TraitedSpec):
Expand Down Expand Up @@ -101,9 +103,11 @@ def _list_outputs(self):
class MultiplyImagesInputSpec(ANTSCommandInputSpec):
dimension = traits.Enum(3, 2, argstr='%d', usedefault=False, mandatory=True, position=0,
desc='image dimension (2 or 3)')
first_input = File(argstr='%s', exists=True, mandatory=True, position=1, desc='image 1')
second_input = traits.Either(File(exists=True), traits.Float, argstr='%s', mandatory=True, position=2,
desc='image 2 or multiplication weight')
first_input = File(argstr='%s', exists=True,
mandatory=True, position=1, desc='image 1')
second_input = traits.Either(
File(exists=True), traits.Float, argstr='%s', mandatory=True, position=2,
desc='image 2 or multiplication weight')
output_product_image = File(argstr='%s', mandatory=True, position=3,
desc='Outputfname.nii.gz: the name of the resulting image.')

Expand Down Expand Up @@ -138,22 +142,25 @@ def _list_outputs(self):
self.inputs.output_product_image)
return outputs


class CreateJacobianDeterminantImageInputSpec(ANTSCommandInputSpec):
imageDimension = traits.Enum(3, 2, argstr='%d', usedefault=False, mandatory=True,
position=0, desc='image dimension (2 or 3)')
position=0, desc='image dimension (2 or 3)')
deformationField = File(argstr='%s', exists=True, mandatory=True,
position=1, desc='deformation transformation file')
position=1, desc='deformation transformation file')
outputImage = File(argstr='%s', mandatory=True,
position=2,
desc='output filename')
position=2,
desc='output filename')
doLogJacobian = traits.Enum(0, 1, argstr='%d', position=3,
desc='return the log jacobian')
desc='return the log jacobian')
useGeometric = traits.Enum(0, 1, argstr='%d', position=4,
desc='return the geometric jacobian')
desc='return the geometric jacobian')


class CreateJacobianDeterminantImageOutputSpec(TraitedSpec):
jacobian_image = File(exists=True, desc='jacobian image')


class CreateJacobianDeterminantImage(ANTSCommand):
"""
Examples
Expand Down Expand Up @@ -203,6 +210,7 @@ class AffineInitializerInputSpec(ANTSCommandInputSpec):
desc=' determines if a local optimization is run at each search point for the set '
'number of iterations')


class AffineInitializerOutputSpec(TraitedSpec):
out_file = File(desc='output transform file')

Expand All @@ -225,3 +233,38 @@ class AffineInitializer(ANTSCommand):

def _list_outputs(self):
return {'out_file': os.path.abspath(self.inputs.out_file)}


class ComposeMultiTransformInputSpec(ANTSCommandInputSpec):
dimension = traits.Enum(3, 2, argstr='%d', usedefault=True, position=0,
desc='image dimension (2 or 3)')
output_transform = File(argstr='%s', position=1, name_source=['transforms'],
name_template='%s_composed', keep_ext=True,
desc='the name of the resulting transform.')
reference_image = File(argstr='%s', position=2,
desc='Reference image (only necessary when output is warpfield)')
transforms = InputMultiPath(File(exists=True), argstr='%s', mandatory=True,
position=3, desc='transforms to average')


class ComposeMultiTransformOutputSpec(TraitedSpec):
output_transform = File(exists=True, desc='Composed transform file')


class ComposeMultiTransform(ANTSCommand):
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a one-line description of what ComposeMultiTransform does.

Take a set of transformations and convert them to a single transformation matrix/warpfield.

Examples
--------
>>> from nipype.interfaces.ants import ComposeMultiTransform
>>> compose_transform = ComposeMultiTransform()
>>> compose_transform.inputs.dimension = 3
>>> compose_transform.inputs.transforms = ['struct_to_template.mat', 'func_to_struct.mat']
>>> compose_transform.cmdline # doctest: +ALLOW_UNICODE
'ComposeMultiTransform 3 struct_to_template_composed struct_to_template.mat func_to_struct.mat'

"""
_cmd = 'ComposeMultiTransform'
input_spec = ComposeMultiTransformInputSpec
output_spec = ComposeMultiTransformOutputSpec
Empty file.