Skip to content

Commit 7f70d95

Browse files
committed
ENH: Future proof the API for new template building
A) Ants developers are working on a multi-modal registration extension to antsRegistration to JOINTLY! Register multiple image modalities while estimating the best transformation. This is a great improvement on template building in some cases. 2) The passive images that are part of template building are sometimes label maps instead of intensity images, in that case, we need to use Nearest Neighbor instead of the hard-coded linear. The API change necessary to accomplish both of these task could be done now, with no behavior benefits at the moment. B) currently in antsRegistrationBuildTemplate.py: inputSpec = pe.Node(interface=util.IdentityInterface( fields=['images', 'fixed_image','ListOfPassiveImagesDictionaries']), name='InputSpec') tbuilder.connect(initAvg, 'output_average_image', buildTemplateIteration1, 'InputSpec.fixed_image') tbuilder.connect(datasource, 'imageList', buildTemplateIteration1, 'InputSpec.images') tbuilder.connect(datasource, 'passiveImagesDictionariesList', buildTemplateIteration1, 'InputSpec.ListOfPassiveImagesDictionaries') C) I propose the following interface perhaps we should implement this now to avoid API change later: inputSpec = pe.Node(interface=util.IdentityInterface( fields=['ListOfImagesDictionaries', 'registrationImages','interpolationMap','fixed_image']), name='InputSpec') ListOfImagesDictionaries - a list of dictionaries where each dictionary is for one scan session, and the mappings in the dictionary are for all the co-aligned images for that one scan session [ {'T1':'subj1/T1.nii','T2':'subj1/T2.nii','labelmap':'subj1/labels.nii'}, {'T1':'subj2/T1.nii','T2':'subj2/T2.nii','labelmap':'subj2/labels.nii'}, … {'T1':'subjN/T1.nii','T2':'subjN/T2.nii','labelmap':'subjN/labels.nii'}, ] registrationImages - A list of the image types to be used actively during the estimation process of registration, any image type not in this list will be passively resampled with the estimated transforms. ['T1','T2'] interpolationMap - A map of image types to interpolation modes. If an image type is not listed, it will be linearly interpolated. { 'labelmap':'NearestNeighbor', 'FLAIR':'WindowedSinc' } tbuilder.connect(initAvg, 'output_average_image', buildTemplateIteration1, 'InputSpec.fixed_image') tbuilder.connect(datasource, 'imageList', buildTemplateIteration1, 'InputSpec.images') tbuilder.connect(datasource, 'passiveImagesDictionariesList', buildTemplateIteration1, 'InputSpec.ListOfPassiveImagesDictionaries') input_interpolation_dict={'INV_T1':'Linear','LABEL_MAP':'NearestNeighbor'}
1 parent 240fa25 commit 7f70d95

File tree

4 files changed

+142
-63
lines changed

4 files changed

+142
-63
lines changed

examples/smri_ants_build_template_new.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,32 @@
5353
else:
5454
print("File previously downloaded {0}".format(localFilename))
5555

56-
input_images=[
57-
os.path.join(mydatadir,'01_T1_half.nii.gz'),
58-
os.path.join(mydatadir,'02_T1_half.nii.gz'),
59-
os.path.join(mydatadir,'03_T1_half.nii.gz')
60-
]
61-
input_passive_images=[
62-
{'INV_T1':os.path.join(mydatadir,'01_T1_inv_half.nii.gz')},
63-
{'INV_T1':os.path.join(mydatadir,'02_T1_inv_half.nii.gz')},
64-
{'INV_T1':os.path.join(mydatadir,'03_T1_inv_half.nii.gz')}
56+
57+
"""
58+
ListOfImagesDictionaries - a list of dictionaries where each dictionary is
59+
for one scan session, and the mappings in the dictionary are for all the
60+
co-aligned images for that one scan session
61+
"""
62+
ListOfImagesDictionaries=[
63+
{'T1':os.path.join(mydatadir,'01_T1_half.nii.gz'),'INV_T1':os.path.join(mydatadir,'01_T1_inv_half.nii.gz'),'LABEL_MAP':os.path.join(mydatadir,'01_T1_inv_half.nii.gz')},
64+
{'T1':os.path.join(mydatadir,'02_T1_half.nii.gz'),'INV_T1':os.path.join(mydatadir,'02_T1_inv_half.nii.gz'),'LABEL_MAP':os.path.join(mydatadir,'02_T1_inv_half.nii.gz')},
65+
{'T1':os.path.join(mydatadir,'03_T1_half.nii.gz'),'INV_T1':os.path.join(mydatadir,'03_T1_inv_half.nii.gz'),'LABEL_MAP':os.path.join(mydatadir,'03_T1_inv_half.nii.gz')}
6566
]
6667

68+
"""
69+
registrationImageTypes - A list of the image types to be used actively during
70+
the estimation process of registration, any image type not in this list
71+
will be passively resampled with the estimated transforms.
72+
['T1','T2']
73+
"""
74+
registrationImageTypes=['T1']
75+
76+
"""
77+
interpolationMap - A map of image types to interpolation modes. If an
78+
image type is not listed, it will be linearly interpolated.
79+
{ 'labelmap':'NearestNeighbor', 'FLAIR':'WindowedSinc' }
80+
"""
81+
interpolationMapping={'INV_T1':'LanczosWindowedSinc','LABEL_MAP':'NearestNeighbor','T1':'Linear'}
6782

6883
"""
6984
3. Define the workflow and its working directory
@@ -74,44 +89,50 @@
7489
"""
7590
4. Define data sources. In real life these would be replace by DataGrabbers
7691
"""
92+
InitialTemplateInputs=[ mdict['T1'] for mdict in ListOfImagesDictionaries ]
93+
7794
datasource = pe.Node(interface=util.IdentityInterface(fields=
78-
['imageList', 'passiveImagesDictionariesList']),
95+
['InitialTemplateInputs', 'ListOfImagesDictionaries',
96+
'registrationImageTypes','interpolationMapping']),
7997
run_without_submitting=True,
8098
name='InputImages' )
81-
datasource.inputs.imageList=input_images
82-
datasource.inputs.passiveImagesDictionariesList=input_passive_images
99+
datasource.inputs.InitialTemplateInputs=InitialTemplateInputs
100+
datasource.inputs.ListOfImagesDictionaries=ListOfImagesDictionaries
101+
datasource.inputs.registrationImageTypes=registrationImageTypes
102+
datasource.inputs.interpolationMapping=interpolationMapping
83103

84104
"""
85-
5. Template is initialized by a simple average
105+
5. Template is initialized by a simple average in this simple example,
106+
any reference image could be used (i.e. a previously created template)
86107
"""
87108
initAvg = pe.Node(interface=ants.AverageImages(), name ='initAvg')
88109
initAvg.inputs.dimension = 3
89110
initAvg.inputs.normalize = True
90111

91-
tbuilder.connect(datasource, "imageList", initAvg, "images")
112+
tbuilder.connect(datasource, "InitialTemplateInputs", initAvg, "images")
92113

93114
"""
94115
6. Define the first iteration of template building
95116
"""
96117

97118
buildTemplateIteration1=antsRegistrationTemplateBuildSingleIterationWF('iteration01')
98119
tbuilder.connect(initAvg, 'output_average_image', buildTemplateIteration1, 'InputSpec.fixed_image')
99-
tbuilder.connect(datasource, 'imageList', buildTemplateIteration1, 'InputSpec.images')
100-
tbuilder.connect(datasource, 'passiveImagesDictionariesList', buildTemplateIteration1, 'InputSpec.ListOfPassiveImagesDictionaries')
120+
tbuilder.connect(datasource, 'ListOfImagesDictionaries', buildTemplateIteration1, 'InputSpec.ListOfImagesDictionaries')
121+
tbuilder.connect(datasource, 'registrationImageTypes', buildTemplateIteration1, 'InputSpec.registrationImageTypes')
122+
tbuilder.connect(datasource, 'interpolationMapping', buildTemplateIteration1, 'InputSpec.interpolationMapping')
101123

102124
"""
103125
7. Define the second iteration of template building
104126
"""
105-
106127
buildTemplateIteration2 = antsRegistrationTemplateBuildSingleIterationWF('iteration02')
107128
tbuilder.connect(buildTemplateIteration1, 'OutputSpec.template', buildTemplateIteration2, 'InputSpec.fixed_image')
108-
tbuilder.connect(datasource, 'imageList', buildTemplateIteration2, 'InputSpec.images')
109-
tbuilder.connect(datasource, 'passiveImagesDictionariesList', buildTemplateIteration2, 'InputSpec.ListOfPassiveImagesDictionaries')
129+
tbuilder.connect(datasource, 'ListOfImagesDictionaries', buildTemplateIteration2, 'InputSpec.ListOfImagesDictionaries')
130+
tbuilder.connect(datasource, 'registrationImageTypes', buildTemplateIteration2, 'InputSpec.registrationImageTypes')
131+
tbuilder.connect(datasource, 'interpolationMapping', buildTemplateIteration2, 'InputSpec.interpolationMapping')
110132

111133
"""
112134
8. Move selected files to a designated results folder
113135
"""
114-
115136
datasink = pe.Node(io.DataSink(), name="datasink")
116137
datasink.inputs.base_directory = os.path.join(requestedPath, "results")
117138

examples/smri_ants_registration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@
7272
reg.inputs.shrink_factors = [[2,1], [3,2,1]]
7373
reg.inputs.use_estimate_learning_rate_once = [True, True]
7474
reg.inputs.use_histogram_matching = [True, True] # This is the default
75-
reg.inputs.output_transform_prefix = "t1_average_BRAINSABC_To_template_t1_clipped"
76-
reg.inputs.output_warped_image = 't1_average_BRAINSABC_To_template_t1_clipped_INTERNAL_WARPED.nii.gz'
75+
reg.inputs.output_transform_prefix = 'thisTransform'
76+
reg.inputs.output_warped_image = 'INTERNAL_WARPED.nii.gz'
7777
reg.cmdline
7878

7979

nipype/interfaces/ants/registration.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,17 @@ class RegistrationInputSpec(ANTSCommandInputSpec):
193193

194194
use_estimate_learning_rate_once = traits.List(traits.Bool(), desc='')
195195
use_histogram_matching = traits.List(traits.Bool(argstr='%s'), default=True, usedefault=True)
196+
# Interpolation flag
197+
interpolation = traits.Enum('Linear',
198+
'NearestNeighbor',
199+
'CosineWindowedSinc',
200+
'WelchWindowedSinc',
201+
'HammingWindowedSinc',
202+
'LanczosWindowedSinc',
203+
# 'MultiLabel',
204+
# 'Gaussian',
205+
# 'BSpline',
206+
argstr='%s', usedefault = True)
196207
# Transform flags
197208
write_composite_transform = traits.Bool(argstr='--write-composite-transform %d', default=False, usedefault=True, desc='')
198209
transforms = traits.List(traits.Enum('Rigid', 'Affine', 'CompositeAffine',
@@ -324,6 +335,9 @@ def _format_arg(self, opt, spec, val):
324335
return '--initial-moving-transform [ %s, 1 ]' % self.inputs.initial_moving_transform
325336
else:
326337
return '--initial-moving-transform [ %s, 0 ]' % self.inputs.initial_moving_transform
338+
elif opt == 'interpolation':
339+
# TODO: handle multilabel, gaussian, and bspline options
340+
return '--interpolation %s' % self.inputs.interpolation
327341
elif opt == 'output_transform_prefix':
328342
if isdefined(self.inputs.output_inverse_warped_image) and self.inputs.output_inverse_warped_image:
329343
return '--output [ %s, %s, %s ]' % (self.inputs.output_transform_prefix, self.inputs.output_warped_image, self.inputs.output_inverse_warped_image )

0 commit comments

Comments
 (0)