Skip to content

antsRegistrationSyNQuick (continuation of #1392 and #2347) #2453

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 13 commits into from
Feb 23, 2018
Merged
3 changes: 2 additions & 1 deletion nipype/interfaces/ants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"""Top-level namespace for ants."""

# Registraiton programs
from .registration import ANTS, Registration, MeasureImageSimilarity
from .registration import (ANTS, Registration, RegistrationSynQuick,
MeasureImageSimilarity)

# Resampling Programs
from .resampling import (ApplyTransforms, ApplyTransformsToPoints,
Expand Down
106 changes: 105 additions & 1 deletion nipype/interfaces/ants/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from ...utils.filemanip import filename_to_list
from ..base import TraitedSpec, File, Str, traits, InputMultiPath, isdefined
from .base import ANTSCommand, ANTSCommandInputSpec
from .base import ANTSCommand, ANTSCommandInputSpec, LOCAL_DEFAULT_NUMBER_OF_THREADS


class ANTSInputSpec(ANTSCommandInputSpec):
Expand Down Expand Up @@ -1505,3 +1505,107 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None):
stdout = runtime.stdout.split('\n')
outputs.similarity = float(stdout[0])
return outputs


class RegistrationSynQuickInputSpec(ANTSCommandInputSpec):
dimension = traits.Enum(3, 2, argstr='-d %d',
usedefault=True, desc='image dimension (2 or 3)')
fixed_image = InputMultiPath(File(exists=True), mandatory=True, argstr='-f %s...',
desc='Fixed image or source image or reference image')
moving_image = InputMultiPath(File(exists=True), mandatory=True, argstr='-m %s...',
desc='Moving image or target image')
output_prefix = Str("transform", usedefault=True, argstr='-o %s',
desc="A prefix that is prepended to all output files")
num_threads = traits.Int(default_value=LOCAL_DEFAULT_NUMBER_OF_THREADS, usedefault=True,
desc='Number of threads (default = 1)', argstr='-n %d')

transform_type = traits.Enum('s', 't', 'r', 'a', 'sr', 'b', 'br', argstr='-t %s',
desc="""
transform type
t: translation
r: rigid
a: rigid + affine
s: rigid + affine + deformable syn (default)
sr: rigid + deformable syn
b: rigid + affine + deformable b-spline syn
br: rigid + deformable b-spline syn""",
usedefault=True)

use_histogram_matching = traits.Bool(False, argstr='-j %d',
desc='use histogram matching')
histogram_bins = traits.Int(default_value=32, argstr='-r %d',
desc='histogram bins for mutual information in SyN stage \
(default = 32)')
spline_distance = traits.Int(default_value=26, argstr='-s %d',
desc='spline distance for deformable B-spline SyN transform \
(default = 26)')
precision_type = traits.Enum('double', 'float', argstr='-p %s',
desc='precision type (default = double)', usedefault=True)


class RegistrationSynQuickOutputSpec(TraitedSpec):
warped_image = File(exists=True, desc="Warped image")
inverse_warped_image = File(exists=True, desc="Inverse warped image")
out_matrix = File(exists=True, desc='Affine matrix')
forward_warp_field = File(exists=True, desc='Forward warp field')
inverse_warp_field = File(exists=True, desc='Inverse warp field')


class RegistrationSynQuick(ANTSCommand):
"""
Registration using a symmetric image normalization method (SyN).
You can read more in Avants et al.; Med Image Anal., 2008
(https://www.ncbi.nlm.nih.gov/pubmed/17659998).

Examples
--------

>>> from nipype.interfaces.ants import RegistrationSynQuick
>>> reg = RegistrationSynQuick()
>>> reg.inputs.fixed_image = 'fixed1.nii'
>>> reg.inputs.moving_image = 'moving1.nii'
>>> reg.inputs.num_threads = 2
>>> reg.cmdline
'antsRegistrationSynQuick.sh -d 3 -f fixed1.nii -m moving1.nii -n 2 -o transform -p d -t s'
>>> reg.run() # doctest: +SKIP
Copy link
Member

Choose a reason for hiding this comment

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

From the help:

     NB:  Multiple image pairs can be specified for registration during the SyN stage.
          Specify additional images using the '-m' and '-f' options.  Note that image
          pair correspondence is given by the order specified on the command line.
          Only the first fixed and moving image pair is used for the linear resgitration
          stages.

We should add a doctest to make sure that if fixed_image and moving_image are lists, that we see -f fixed1.nii -f fixed2.nii -m moving1.nii -m moving2.nii (or similar).

It may be more trouble than it's worth to verify that the lists are of the same length.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it gives -f fixed1.nii fixed2.nii -m moving1.nii moving2.nii, but i haven't checked if it is properly propagated to the main Registration (couldn't find also examples that confirms that this is a proper command)

Copy link
Member

Choose a reason for hiding this comment

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

Okay, then I think we want to change the argstrs for fixed_image and moving_image to -f %s... and -m %s..., respectively. (The ... will change -f A B to -f A -f B.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I pushed before reading this comment.

But do you know this is correct syntax of command line? If yes, I can change it, but just wasn't sure what is the correct syntax

Copy link
Member

Choose a reason for hiding this comment

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

Yes. -f A B will fail, while -f A -f B will succeed.


example for multiple images

>>> from nipype.interfaces.ants import RegistrationSynQuick
>>> reg = RegistrationSynQuick()
>>> reg.inputs.fixed_image = ['fixed1.nii', 'fixed2.nii']
>>> reg.inputs.moving_image = ['moving1.nii', 'moving2.nii']
>>> reg.inputs.num_threads = 2
>>> reg.cmdline
'antsRegistrationSynQuick.sh -d 3 -f fixed1.nii -f fixed2.nii -m moving1.nii -m moving2.nii -n 2 -o transform -p d -t s'
>>> reg.run() # doctest: +SKIP
"""


_cmd = 'antsRegistrationSynQuick.sh'
input_spec = RegistrationSynQuickInputSpec
output_spec = RegistrationSynQuickOutputSpec

Copy link
Member

Choose a reason for hiding this comment

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

Just to avoid being affected by upstream changes, we should override _num_threads_update() to do nothing.

def _num_threads_update(self):
    """antsRegistrationSynQuick.sh ignores environment variables, so override environment update"""

def _num_threads_update(self):
"""
antsRegistrationSynQuick.sh ignores environment variables,
so override environment update frm ANTSCommand class
"""
pass

def _format_arg(self, name, spec, value):
if name == 'precision_type':
return spec.argstr % value[0]
return super(RegistrationSynQuick, self)._format_arg(name, spec, value)

def _list_outputs(self):
outputs = self.output_spec().get()
out_base = os.path.abspath(self.inputs.output_prefix)
outputs['warped_image'] = out_base + 'Warped.nii.gz'
outputs['inverse_warped_image'] = out_base + 'InverseWarped.nii.gz'
outputs['out_matrix'] = out_base + '0GenericAffine.mat'

if self.inputs.transform_type not in ('t', 'r', 'a'):
outputs['forward_warp_field'] = out_base + '1Warp.nii.gz'
outputs['inverse_warp_field'] = out_base + '1InverseWarp.nii.gz'
return outputs
71 changes: 71 additions & 0 deletions nipype/interfaces/ants/tests/test_auto_RegistrationSynQuick.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from __future__ import unicode_literals
from ..registration import RegistrationSynQuick


def test_RegistrationSynQuick_inputs():
input_map = dict(
args=dict(argstr='%s', ),
dimension=dict(
argstr='-d %d',
usedefault=True,
),
environ=dict(
nohash=True,
usedefault=True,
),
fixed_image=dict(
argstr='-f %s...',
mandatory=True,
),
histogram_bins=dict(argstr='-r %d', ),
ignore_exception=dict(
deprecated='1.0.0',
nohash=True,
usedefault=True,
),
moving_image=dict(
argstr='-m %s...',
mandatory=True,
),
num_threads=dict(
argstr='-n %d',
usedefault=True,
),
output_prefix=dict(
argstr='-o %s',
usedefault=True,
),
precision_type=dict(
argstr='-p %s',
usedefault=True,
),
spline_distance=dict(argstr='-s %d', ),
terminal_output=dict(
deprecated='1.0.0',
nohash=True,
),
transform_type=dict(
argstr='-t %s',
usedefault=True,
),
use_histogram_matching=dict(argstr='-j %d', ),
)
inputs = RegistrationSynQuick.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_RegistrationSynQuick_outputs():
output_map = dict(
forward_warp_field=dict(),
inverse_warp_field=dict(),
inverse_warped_image=dict(),
out_matrix=dict(),
warped_image=dict(),
)
outputs = RegistrationSynQuick.output_spec()

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