Skip to content

ENH: Enable antsRegistration 2.2.0+ masking #2078

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 6 commits into from
Jun 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 5 additions & 11 deletions docker/base.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ RUN apt-get update && \
fsl-core \
fsl-mni152-templates \
afni \
ants \
bzip2 \
xvfb \
git \
Expand All @@ -103,23 +104,16 @@ ENV FSLDIR=/usr/share/fsl/5.0 \
AFNI_IMSAVE_WARNINGS=NO \
AFNI_TTATLAS_DATASET=/usr/share/afni/atlases \
AFNI_PLUGINPATH=/usr/lib/afni/plugins \
PATH=/usr/lib/fsl/5.0:/usr/lib/afni/bin:$PATH

# Installing and setting up ANTs
RUN mkdir -p /opt/ants && \
curl -sSL "https://dl.dropbox.com/s/2f4sui1z6lcgyek/ANTs-Linux-centos5_x86_64-v2.2.0-0740f91.tar.gz?dl=0" \
| tar -zx -C /opt

ENV ANTSPATH=/opt/ants \
PATH=$ANTSPATH:$PATH
ANTSPATH=/usr/lib/ants
ENV PATH=/usr/lib/fsl/5.0:/usr/lib/afni/bin:$ANTSPATH:$PATH

# Installing and setting up c3d
RUN mkdir -p /opt/c3d && \
curl -sSL "http://downloads.sourceforge.net/project/c3d/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz" \
| tar -xzC /opt/c3d --strip-components 1

ENV C3DPATH=/opt/c3d/ \
PATH=$C3DPATH/bin:$PATH
ENV C3DPATH=/opt/c3d/
ENV PATH=$C3DPATH/bin:$PATH

# Install fake-S3
ENV GEM_HOME /usr/lib/ruby/gems/2.3
Expand Down
43 changes: 42 additions & 1 deletion nipype/interfaces/ants/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
"""The ants module provides basic functions for interfacing with ANTS tools."""
from __future__ import print_function, division, unicode_literals, absolute_import
from builtins import str

import os
import subprocess

# Local imports
from ... import logging
from ... import logging, LooseVersion
from ..base import CommandLine, CommandLineInputSpec, traits, isdefined
logger = logging.getLogger('interface')

Expand All @@ -25,6 +29,39 @@
ALT_ITKv4_THREAD_LIMIT_VARIABLE = 'ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS'


class Info(object):
_version = None

@property
def version(self):
if self._version is None:
try:
basedir = os.environ['ANTSPATH']
except KeyError:
return None

cmd = os.path.join(basedir, 'antsRegistration')
try:
res = subprocess.check_output([cmd, '--version']).decode('utf-8')
except OSError:
return None

for line in res.splitlines():
if line.startswith('ANTs Version: '):
self._version = line.split()[2]
break
else:
return None

v_string, githash = self._version.split('-')

# 2.2.0-equivalent version string
if 'post' in v_string and LooseVersion(v_string) >= LooseVersion('2.1.0.post789'):
return '2.2.0'
else:
return '.'.join(v_string.split('.')[:3])


class ANTSCommandInputSpec(CommandLineInputSpec):
"""Base Input Specification for all ANTS Commands
"""
Expand Down Expand Up @@ -84,3 +121,7 @@ def set_default_num_threads(cls, num_threads):
<instance>.inputs.num_threads
"""
cls._num_threads = num_threads

@property
def version(self):
return Info().version
47 changes: 43 additions & 4 deletions nipype/interfaces/ants/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from builtins import range, str
import os

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

Expand Down Expand Up @@ -219,12 +220,22 @@ class RegistrationInputSpec(ANTSCommandInputSpec):
usedefault=True, desc='image dimension (2 or 3)')
fixed_image = InputMultiPath(File(exists=True), mandatory=True,
desc='image to apply transformation to (generally a coregistered functional)')
fixed_image_mask = File(argstr='%s', exists=True,
desc='mask used to limit metric sampling region of the fixed image')
fixed_image_mask = File(
exists=True, argstr='%s', max_ver='2.1.0', xor=['fixed_image_masks'],
desc='mask used to limit metric sampling region of the fixed image')
fixed_image_masks = InputMultiPath(
traits.Either('NULL', File(exists=True)), min_ver='2.2.0', xor=['fixed_image_mask'],
desc='mask used to limit metric sampling region of the fixed image '
'(Use "NULL" to omit a mask at a given stage)')
moving_image = InputMultiPath(File(exists=True), mandatory=True,
desc='image to apply transformation to (generally a coregistered functional)')
moving_image_mask = File(requires=['fixed_image_mask'],
exists=True, desc='mask used to limit metric sampling region of the moving image')
moving_image_mask = File(
exists=True, requires=['fixed_image_mask'], max_ver='2.1.0', xor=['moving_image_masks'],
desc='mask used to limit metric sampling region of the moving image')
moving_image_masks = InputMultiPath(
traits.Either('NULL', File(exists=True)), min_ver='2.2.0', xor=['moving_image_mask'],
desc='mask used to limit metric sampling region of the moving image '
'(Use "NULL" to omit a mask at a given stage)')

save_state = File(argstr='--save-state %s', exists=False,
desc='Filename for saving the internal restorable state of the registration')
Expand Down Expand Up @@ -648,6 +659,20 @@ class Registration(ANTSCommand):
--metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \
--smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \
--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'

>>> # Test masking
>>> reg9 = copy.deepcopy(reg)
>>> reg9.inputs.fixed_image_masks = ['NULL', 'fixed1.nii']
>>> reg9.cmdline # doctest: +ALLOW_UNICODE
'antsRegistration --collapse-output-transforms 0 --dimensionality 3 --initial-moving-transform [ trans.mat, 1 ] \
--initialize-transforms-per-stage 0 --interpolation Linear --output [ output_, output_warped_image.nii.gz ] \
--transform Affine[ 2.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32, Random, 0.05 ] \
--convergence [ 1500x200, 1e-08, 20 ] --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 \
--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --masks [ NULL, NULL ] \
--transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \
--convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \
--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --masks [ fixed1.nii, NULL ] \
--winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'
"""
DEF_SAMPLING_STRATEGY = 'None'
"""The default sampling strategy argument."""
Expand Down Expand Up @@ -783,6 +808,20 @@ def _format_registration(self):
if isdefined(self.inputs.restrict_deformation):
retval.append('--restrict-deformation %s' %
self._format_xarray(self.inputs.restrict_deformation[ii]))
if any((isdefined(self.inputs.fixed_image_masks),
isdefined(self.inputs.moving_image_masks))):
if isdefined(self.inputs.fixed_image_masks):
fixed_masks = filename_to_list(self.inputs.fixed_image_masks)
fixed_mask = fixed_masks[ii if len(fixed_masks) > 1 else 0]
else:
fixed_mask = 'NULL'

if isdefined(self.inputs.moving_image_masks):
moving_masks = filename_to_list(self.inputs.moving_image_masks)
moving_mask = moving_masks[ii if len(moving_masks) > 1 else 0]
else:
moving_mask = 'NULL'
retval.append('--masks [ %s, %s ]' % (fixed_mask, moving_mask))
return " ".join(retval)

def _get_outputfilenames(self, inverse=False):
Expand Down
12 changes: 11 additions & 1 deletion nipype/interfaces/ants/tests/test_auto_Registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ def test_Registration_inputs():
fixed_image=dict(mandatory=True,
),
fixed_image_mask=dict(argstr='%s',
max_ver='2.1.0',
xor=['fixed_image_masks'],
),
fixed_image_masks=dict(min_ver='2.2.0',
xor=['fixed_image_mask'],
),
float=dict(argstr='--float %d',
),
Expand Down Expand Up @@ -58,7 +63,12 @@ def test_Registration_inputs():
metric_weight_stage_trait=dict(),
moving_image=dict(mandatory=True,
),
moving_image_mask=dict(requires=['fixed_image_mask'],
moving_image_mask=dict(max_ver='2.1.0',
requires=['fixed_image_mask'],
xor=['moving_image_masks'],
),
moving_image_masks=dict(min_ver='2.2.0',
xor=['moving_image_mask'],
),
num_threads=dict(nohash=True,
usedefault=True,
Expand Down