Skip to content

Commit 5040260

Browse files
authored
Merge pull request #2078 from effigies/fix/ants_reg_import
ENH: Enable antsRegistration 2.2.0+ masking
2 parents 218e591 + b743f54 commit 5040260

File tree

4 files changed

+101
-17
lines changed

4 files changed

+101
-17
lines changed

docker/base.Dockerfile

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ RUN apt-get update && \
7777
fsl-core \
7878
fsl-mni152-templates \
7979
afni \
80+
ants \
8081
bzip2 \
8182
xvfb \
8283
git \
@@ -103,23 +104,16 @@ ENV FSLDIR=/usr/share/fsl/5.0 \
103104
AFNI_IMSAVE_WARNINGS=NO \
104105
AFNI_TTATLAS_DATASET=/usr/share/afni/atlases \
105106
AFNI_PLUGINPATH=/usr/lib/afni/plugins \
106-
PATH=/usr/lib/fsl/5.0:/usr/lib/afni/bin:$PATH
107-
108-
# Installing and setting up ANTs
109-
RUN mkdir -p /opt/ants && \
110-
curl -sSL "https://dl.dropbox.com/s/2f4sui1z6lcgyek/ANTs-Linux-centos5_x86_64-v2.2.0-0740f91.tar.gz?dl=0" \
111-
| tar -zx -C /opt
112-
113-
ENV ANTSPATH=/opt/ants \
114-
PATH=$ANTSPATH:$PATH
107+
ANTSPATH=/usr/lib/ants
108+
ENV PATH=/usr/lib/fsl/5.0:/usr/lib/afni/bin:$ANTSPATH:$PATH
115109

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

121-
ENV C3DPATH=/opt/c3d/ \
122-
PATH=$C3DPATH/bin:$PATH
115+
ENV C3DPATH=/opt/c3d/
116+
ENV PATH=$C3DPATH/bin:$PATH
123117

124118
# Install fake-S3
125119
ENV GEM_HOME /usr/lib/ruby/gems/2.3

nipype/interfaces/ants/base.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
"""The ants module provides basic functions for interfacing with ANTS tools."""
55
from __future__ import print_function, division, unicode_literals, absolute_import
66
from builtins import str
7+
8+
import os
9+
import subprocess
10+
711
# Local imports
8-
from ... import logging
12+
from ... import logging, LooseVersion
913
from ..base import CommandLine, CommandLineInputSpec, traits, isdefined
1014
logger = logging.getLogger('interface')
1115

@@ -25,6 +29,39 @@
2529
ALT_ITKv4_THREAD_LIMIT_VARIABLE = 'ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS'
2630

2731

32+
class Info(object):
33+
_version = None
34+
35+
@property
36+
def version(self):
37+
if self._version is None:
38+
try:
39+
basedir = os.environ['ANTSPATH']
40+
except KeyError:
41+
return None
42+
43+
cmd = os.path.join(basedir, 'antsRegistration')
44+
try:
45+
res = subprocess.check_output([cmd, '--version']).decode('utf-8')
46+
except OSError:
47+
return None
48+
49+
for line in res.splitlines():
50+
if line.startswith('ANTs Version: '):
51+
self._version = line.split()[2]
52+
break
53+
else:
54+
return None
55+
56+
v_string, githash = self._version.split('-')
57+
58+
# 2.2.0-equivalent version string
59+
if 'post' in v_string and LooseVersion(v_string) >= LooseVersion('2.1.0.post789'):
60+
return '2.2.0'
61+
else:
62+
return '.'.join(v_string.split('.')[:3])
63+
64+
2865
class ANTSCommandInputSpec(CommandLineInputSpec):
2966
"""Base Input Specification for all ANTS Commands
3067
"""
@@ -84,3 +121,7 @@ def set_default_num_threads(cls, num_threads):
84121
<instance>.inputs.num_threads
85122
"""
86123
cls._num_threads = num_threads
124+
125+
@property
126+
def version(self):
127+
return Info().version

nipype/interfaces/ants/registration.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from builtins import range, str
1212
import os
1313

14+
from ...utils.filemanip import filename_to_list
1415
from ..base import TraitedSpec, File, Str, traits, InputMultiPath, isdefined
1516
from .base import ANTSCommand, ANTSCommandInputSpec
1617

@@ -219,12 +220,22 @@ class RegistrationInputSpec(ANTSCommandInputSpec):
219220
usedefault=True, desc='image dimension (2 or 3)')
220221
fixed_image = InputMultiPath(File(exists=True), mandatory=True,
221222
desc='image to apply transformation to (generally a coregistered functional)')
222-
fixed_image_mask = File(argstr='%s', exists=True,
223-
desc='mask used to limit metric sampling region of the fixed image')
223+
fixed_image_mask = File(
224+
exists=True, argstr='%s', max_ver='2.1.0', xor=['fixed_image_masks'],
225+
desc='mask used to limit metric sampling region of the fixed image')
226+
fixed_image_masks = InputMultiPath(
227+
traits.Either('NULL', File(exists=True)), min_ver='2.2.0', xor=['fixed_image_mask'],
228+
desc='mask used to limit metric sampling region of the fixed image '
229+
'(Use "NULL" to omit a mask at a given stage)')
224230
moving_image = InputMultiPath(File(exists=True), mandatory=True,
225231
desc='image to apply transformation to (generally a coregistered functional)')
226-
moving_image_mask = File(requires=['fixed_image_mask'],
227-
exists=True, desc='mask used to limit metric sampling region of the moving image')
232+
moving_image_mask = File(
233+
exists=True, requires=['fixed_image_mask'], max_ver='2.1.0', xor=['moving_image_masks'],
234+
desc='mask used to limit metric sampling region of the moving image')
235+
moving_image_masks = InputMultiPath(
236+
traits.Either('NULL', File(exists=True)), min_ver='2.2.0', xor=['moving_image_mask'],
237+
desc='mask used to limit metric sampling region of the moving image '
238+
'(Use "NULL" to omit a mask at a given stage)')
228239

229240
save_state = File(argstr='--save-state %s', exists=False,
230241
desc='Filename for saving the internal restorable state of the registration')
@@ -648,6 +659,20 @@ class Registration(ANTSCommand):
648659
--metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \
649660
--smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \
650661
--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'
662+
663+
>>> # Test masking
664+
>>> reg9 = copy.deepcopy(reg)
665+
>>> reg9.inputs.fixed_image_masks = ['NULL', 'fixed1.nii']
666+
>>> reg9.cmdline # doctest: +ALLOW_UNICODE
667+
'antsRegistration --collapse-output-transforms 0 --dimensionality 3 --initial-moving-transform [ trans.mat, 1 ] \
668+
--initialize-transforms-per-stage 0 --interpolation Linear --output [ output_, output_warped_image.nii.gz ] \
669+
--transform Affine[ 2.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32, Random, 0.05 ] \
670+
--convergence [ 1500x200, 1e-08, 20 ] --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 \
671+
--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --masks [ NULL, NULL ] \
672+
--transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \
673+
--convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \
674+
--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --masks [ fixed1.nii, NULL ] \
675+
--winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'
651676
"""
652677
DEF_SAMPLING_STRATEGY = 'None'
653678
"""The default sampling strategy argument."""
@@ -783,6 +808,20 @@ def _format_registration(self):
783808
if isdefined(self.inputs.restrict_deformation):
784809
retval.append('--restrict-deformation %s' %
785810
self._format_xarray(self.inputs.restrict_deformation[ii]))
811+
if any((isdefined(self.inputs.fixed_image_masks),
812+
isdefined(self.inputs.moving_image_masks))):
813+
if isdefined(self.inputs.fixed_image_masks):
814+
fixed_masks = filename_to_list(self.inputs.fixed_image_masks)
815+
fixed_mask = fixed_masks[ii if len(fixed_masks) > 1 else 0]
816+
else:
817+
fixed_mask = 'NULL'
818+
819+
if isdefined(self.inputs.moving_image_masks):
820+
moving_masks = filename_to_list(self.inputs.moving_image_masks)
821+
moving_mask = moving_masks[ii if len(moving_masks) > 1 else 0]
822+
else:
823+
moving_mask = 'NULL'
824+
retval.append('--masks [ %s, %s ]' % (fixed_mask, moving_mask))
786825
return " ".join(retval)
787826

788827
def _get_outputfilenames(self, inverse=False):

nipype/interfaces/ants/tests/test_auto_Registration.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ def test_Registration_inputs():
2424
fixed_image=dict(mandatory=True,
2525
),
2626
fixed_image_mask=dict(argstr='%s',
27+
max_ver='2.1.0',
28+
xor=['fixed_image_masks'],
29+
),
30+
fixed_image_masks=dict(min_ver='2.2.0',
31+
xor=['fixed_image_mask'],
2732
),
2833
float=dict(argstr='--float %d',
2934
),
@@ -58,7 +63,12 @@ def test_Registration_inputs():
5863
metric_weight_stage_trait=dict(),
5964
moving_image=dict(mandatory=True,
6065
),
61-
moving_image_mask=dict(requires=['fixed_image_mask'],
66+
moving_image_mask=dict(max_ver='2.1.0',
67+
requires=['fixed_image_mask'],
68+
xor=['moving_image_masks'],
69+
),
70+
moving_image_masks=dict(min_ver='2.2.0',
71+
xor=['moving_image_mask'],
6272
),
6373
num_threads=dict(nohash=True,
6474
usedefault=True,

0 commit comments

Comments
 (0)