Skip to content

Commit 36ec6a5

Browse files
committed
BUG: Restore registration interface failed multi-modal
Registration interface was failing to support multimodal input when they are given. ex) nipype: antsWF.inputs.fixed_image=['fixed_t1.nii.gz', 'fixed_t2.nii.gz'] antsWF.inputs.moving_image=['moving_t1.nii.gz', 'moving_t2.nii.gz'] corresponding correct commandline: --metric MI[\ fixed_t1.nii.gz,\ moving_t1.nii.gz,\ ... --metric MI[\ fixed_t2.nii.gz,\ moving_t2.nii.gz,\ ... Modifed test to be more clear about the multi-modal state. Modifed example to be more realistic use of metrics. Added comments to improve how metrics are used. Reported in issue: #1175
1 parent 1d6fea7 commit 36ec6a5

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Next release
22
============
33

4+
* BUG: ANTs Registration interface failed with multi-modal inputs
5+
(https://github.com/nipy/nipype/pull/1176) (https://github.com/nipy/nipype/issues/1175)
46
* FIX: Bug in XFibres5 (https://github.com/nipy/nipype/pull/1168)
57
* ENH: Attempt to use hard links for data sink.
68
(https://github.com/nipy/nipype/pull/1161)

nipype/interfaces/ants/registration.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,26 @@ class ANTSInputSpec(ANTSCommandInputSpec):
2626
desc=('image to apply transformation to (generally a coregistered '
2727
'functional)'))
2828

29+
# Not all metrics are appropriate for all modalities. Also, not all metrics
30+
# are efficeint or appropriate at all resolution levels, Some metrics perform
31+
# well for gross global registraiton, but do poorly for small changes (i.e.
32+
# Mattes), and some metrics do well for small changes but don't work well for
33+
# gross level changes (i.e. 'CC').
34+
#
35+
# This is a two stage registration. in the first stage
36+
# [ 'Mattes', .................]
37+
# ^^^^^^ <- First stage
38+
# Do a unimodal registration of the first elements of the fixed/moving input
39+
# list use the"CC" as the metric.
40+
#
41+
# In the second stage
42+
# [ ....., ['Mattes','CC'] ]
43+
# ^^^^^^^^^^^^^^^ <- Second stage
44+
# Do a multi-modal registration where the first elements of fixed/moving
45+
# input list use 'CC' metric and that is added to 'Mattes' metric result of
46+
# the second elements of the fixed/moving input.
47+
#
48+
# Cost = Sum_i ( metricweight[i] Metric_i ( fixedimage[i], movingimage[i]) )
2949
metric = traits.List(traits.Enum('CC', 'MI', 'SMI', 'PR', 'SSD',
3050
'MSQ', 'PSE'), mandatory=True, desc='')
3151

@@ -440,13 +460,15 @@ class Registration(ANTSCommand):
440460
441461
>>> # Test multiple metrics per stage
442462
>>> reg5 = copy.deepcopy(reg)
443-
>>> reg5.inputs.metric = ['CC', ['CC', 'Mattes']]
444-
>>> reg5.inputs.metric_weight = [1, [.5]*2]
445-
>>> reg5.inputs.radius_or_number_of_bins = [4, [32]*2]
463+
>>> reg5.inputs.fixed_image = [ 'fixed1.nii', 'fixed2.nii' ]
464+
>>> reg5.inputs.moving_image = [ 'moving1.nii', 'moving2.nii' ]
465+
>>> reg5.inputs.metric = ['Mattes', ['Mattes', 'CC']]
466+
>>> reg5.inputs.metric_weight = [1, [.5,.5]]
467+
>>> reg5.inputs.radius_or_number_of_bins = [32, [32,4] ]
446468
>>> reg5.inputs.sampling_strategy = ['Random', None] # use default strategy in second stage
447469
>>> reg5.inputs.sampling_percentage = [0.05, [0.05, 0.10]]
448470
>>> reg5.cmdline
449-
'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 ] --restore-state trans.mat --save-state trans.mat --transform Affine[ 2.0 ] --metric CC[ fixed1.nii, moving1.nii, 1, 4, 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 --transform SyN[ 0.25, 3.0, 0.0 ] --metric CC[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.1 ] --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'
471+
'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 ] --restore-state trans.mat --save-state trans.mat --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 --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] --metric CC[ fixed2.nii, moving2.nii, 0.5, 4, None, 0.1 ] --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'
450472
"""
451473
DEF_SAMPLING_STRATEGY = 'None'
452474
"""The default sampling strategy argument."""
@@ -466,14 +488,12 @@ def _formatMetric(self, index):
466488
----------
467489
index: the stage index
468490
"""
469-
# The common fixed image.
470-
fixed = self.inputs.fixed_image[0]
471-
# The common moving image.
472-
moving = self.inputs.moving_image[0]
473491
# The metric name input for the current stage.
474492
name_input = self.inputs.metric[index]
475493
# The stage-specific input dictionary.
476494
stage_inputs = dict(
495+
fixed_image=self.inputs.fixed_image[0],
496+
moving_image=self.inputs.moving_image[0],
477497
metric=name_input,
478498
weight=self.inputs.metric_weight[index],
479499
radius_or_bins=self.inputs.radius_or_number_of_bins[index],
@@ -499,19 +519,32 @@ def _formatMetric(self, index):
499519
if isinstance(name_input, list):
500520
items = stage_inputs.items()
501521
indexes = range(0, len(name_input))
502-
# dict-comprehension only works with python 2.7 and up
503-
#specs = [{k: v[i] for k, v in items} for i in indexes]
504-
specs = [dict([(k, v[i]) for k, v in items]) for i in indexes]
522+
specs = list()
523+
for i in indexes:
524+
temp = dict([(k, v[i]) for k, v in items])
525+
if i > len( self.inputs.fixed_image ):
526+
temp["fixed_image"] = self.inputs.fixed_image[0]
527+
else:
528+
temp["fixed_image"] = self.inputs.fixed_image[i]
529+
530+
if i > len( self.inputs.moving_image ):
531+
temp["moving_image"] = self.inputs.moving_image[0]
532+
else:
533+
temp["moving_image"] = self.inputs.moving_image[i]
534+
535+
specs.append( temp )
505536
else:
506537
specs = [stage_inputs]
507538

508539
# Format the --metric command line metric arguments, one per
509540
# specification.
510-
return [self._formatMetricArgument(fixed, moving, **spec) for spec in specs]
541+
return [self._formatMetricArgument(**spec) for spec in specs]
511542

512-
def _formatMetricArgument(self, fixed, moving, **kwargs):
543+
def _formatMetricArgument(self, **kwargs):
513544
retval = '%s[ %s, %s, %g, %d' % (kwargs['metric'],
514-
fixed, moving, kwargs['weight'],
545+
kwargs['fixed_image'],
546+
kwargs['moving_image'],
547+
kwargs['weight'],
515548
kwargs['radius_or_bins'])
516549

517550
# The optional sampling strategy.

0 commit comments

Comments
 (0)