forked from BRAINSia/BRAINSStandAlone
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBAWantsRegistrationBuildTemplate.py
381 lines (334 loc) · 24.4 KB
/
BAWantsRegistrationBuildTemplate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
#################################################################################
## Program: Build Template Parallel
## Language: Python
##
## Authors: Jessica Forbes, Grace Murray, and Hans Johnson, University of Iowa
##
## This software is distributed WITHOUT ANY WARRANTY; without even
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE.
##
#################################################################################
import nipype.pipeline.engine as pe
import nipype.interfaces.utility as util
from nipype.interfaces.utility import Function
from nipype.interfaces.ants import (
Registration,
ApplyTransforms,
AverageImages, MultiplyImages,
AverageAffineTransform)
def makeListOfOneElement(inputFile):
outputList = [inputFile]
return outputList
def GetFirstListElement(this_list):
return this_list[0]
def MakeTransformListWithGradientWarps(averageAffineTranform, gradientStepWarp):
return [averageAffineTranform, gradientStepWarp, gradientStepWarp, gradientStepWarp, gradientStepWarp]
def RenestDeformedPassiveImages(deformedPassiveImages, flattened_image_nametypes, interpolationMapping):
import os
""" Now make a list of lists of images where the outter list is per image type,
and the inner list is the same size as the number of subjects to be averaged.
In this case, the first element will be a list of all the deformed T2's, and
the second element will be a list of all deformed POSTERIOR_AIR, etc..
"""
all_images_size = len(deformedPassiveImages)
image_dictionary_of_lists = dict()
nested_imagetype_list = list()
outputAverageImageName_list = list()
image_type_list = list()
nested_interpolation_type = list()
## make empty_list, this is not efficient, but it works
for name in flattened_image_nametypes:
image_dictionary_of_lists[name] = list()
for index in range(0, all_images_size):
curr_name = flattened_image_nametypes[index]
curr_file = deformedPassiveImages[index]
image_dictionary_of_lists[curr_name].append(curr_file)
for image_type, image_list in image_dictionary_of_lists.items():
nested_imagetype_list.append(image_list)
outputAverageImageName_list.append('AVG_' + image_type + '.nii.gz')
image_type_list.append('WARP_AVG_' + image_type)
if image_type in interpolationMapping:
nested_interpolation_type.append(interpolationMapping[image_type])
else:
nested_interpolation_type.append('Linear') # Linear is the default.
print "\n" * 10
print "HACK: ", nested_imagetype_list
print "HACK: ", outputAverageImageName_list
print "HACK: ", image_type_list
print "HACK: ", nested_interpolation_type
return nested_imagetype_list, outputAverageImageName_list, image_type_list, nested_interpolation_type
def SplitAffineAndWarpComponents(list_of_transforms_lists):
### Nota bene: The outputs will include the initial_moving_transform from Registration (which depends on what
### the invert_initial_moving_transform is set to)
affine_component_list = []
warp_component_list = []
for transform in list_of_transforms_lists:
affine_component_list.append(transform[0])
warp_component_list.append(transform[1])
print "HACK ", affine_component_list, " ", warp_component_list
return affine_component_list, warp_component_list
## Flatten and return equal length transform and images lists.
def FlattenTransformAndImagesList(ListOfPassiveImagesDictionaries, transforms, invert_transform_flags, interpolationMapping):
import sys
print("HACK: DEBUG: ListOfPassiveImagesDictionaries\n{lpi}\n".format(lpi=ListOfPassiveImagesDictionaries))
subjCount = len(ListOfPassiveImagesDictionaries)
tranCount = len(transforms)
if subjCount != tranCount:
print "ERROR: subjCount must equal tranCount {0} != {1}".format(subjCount, tranCount)
sys.exit(-1)
invertTfmsFlagsCount = len(invert_transform_flags)
if subjCount != invertTfmsFlagsCount:
print "ERROR: subjCount must equal invertTfmsFlags {0} != {1}".format(subjCount, invertTfmsFlagsCount)
sys.exit(-1)
flattened_images = list()
flattened_image_nametypes = list()
flattened_transforms = list()
flattened_invert_transform_flags = list()
flattened_interpolation_type = list()
passiveImagesCount = len(ListOfPassiveImagesDictionaries[0])
for subjIndex in range(0, subjCount):
# if passiveImagesCount != len(ListOfPassiveImagesDictionaries[subjIndex]):
# print "ERROR: all image lengths must be equal {0} != {1}".format(passiveImagesCount,len(ListOfPassiveImagesDictionaries[subjIndex]))
# sys.exit(-1)
subjImgDictionary = ListOfPassiveImagesDictionaries[subjIndex]
subjToAtlasTransform = transforms[subjIndex]
subjToAtlasInvertFlags = invert_transform_flags[subjIndex]
for imgname, img in subjImgDictionary.items():
flattened_images.append(img)
flattened_image_nametypes.append(imgname)
flattened_transforms.append(subjToAtlasTransform)
flattened_invert_transform_flags.append(subjToAtlasInvertFlags)
if imgname in interpolationMapping:
flattened_interpolation_type.append(interpolationMapping[imgname])
else:
flattened_interpolation_type.append('Linear') # Linear is the default.
print("HACK: flattened images {0}\n".format(flattened_images))
print("HACK: flattened nametypes {0}\n".format(flattened_image_nametypes))
print("HACK: flattened txfms {0}\n".format(flattened_transforms))
print("HACK: flattened txfmsFlags{0}\n".format(flattened_invert_transform_flags))
return flattened_images, flattened_transforms, flattened_invert_transform_flags, flattened_image_nametypes, flattened_interpolation_type
def GetMovingImages(ListOfImagesDictionaries, registrationImageTypes, interpolationMapping):
""" This currently ONLY works when registrationImageTypes has
length of exactly 1. When the new multi-variate registration
is introduced, it will be expanded.
"""
if len(registrationImageTypes) != 1:
print("ERROR: Multivariate imageing not supported yet!")
return []
moving_images = [mdict[registrationImageTypes[0]] for mdict in ListOfImagesDictionaries]
moving_interpolation_type = interpolationMapping[registrationImageTypes[0]]
return moving_images, moving_interpolation_type
def GetPassiveImages(ListOfImagesDictionaries, registrationImageTypes):
if len(registrationImageTypes) != 1:
print("ERROR: Multivariate imageing not supported yet!")
return [dict()]
passive_images = list()
for mdict in ListOfImagesDictionaries:
ThisSubjectPassiveImages = dict()
for key, value in mdict.items():
if key not in registrationImageTypes:
ThisSubjectPassiveImages[key] = value
passive_images.append(ThisSubjectPassiveImages)
return passive_images
##
## NOTE: The modes can be either 'SINGLE_IMAGE' or 'MULTI'
## 'SINGLE_IMAGE' is quick shorthand when you are building an atlas with a single subject, then registration can
## be short-circuted
## any other string indicates the normal mode that you would expect and replicates the shell script build_template_parallel.sh
def BAWantsRegistrationTemplateBuildSingleIterationWF(iterationPhasePrefix=''):
"""
Inputs::
inputspec.images :
inputspec.fixed_image :
inputspec.ListOfPassiveImagesDictionaries :
inputspec.interpolationMapping :
Outputs::
outputspec.template :
outputspec.transforms_list :
outputspec.passive_deformed_templates :
"""
TemplateBuildSingleIterationWF = pe.Workflow(name='antsRegistrationTemplateBuildSingleIterationWF_' + str(iterationPhasePrefix))
inputSpec = pe.Node(interface=util.IdentityInterface(fields=[
'ListOfImagesDictionaries', 'registrationImageTypes',
#'maskRegistrationImageType',
'interpolationMapping', 'fixed_image']),
run_without_submitting=True,
name='inputspec')
## HACK: TODO: We need to have the AVG_AIR.nii.gz be warped with a default voxel value of 1.0
## HACK: TODO: Need to move all local functions to a common untility file, or at the top of the file so that
## they do not change due to re-indenting. Otherwise re-indenting for flow control will trigger
## their hash to change.
## HACK: TODO: REMOVE 'transforms_list' it is not used. That will change all the hashes
## HACK: TODO: Need to run all python files through the code beutifiers. It has gotten pretty ugly.
outputSpec = pe.Node(interface=util.IdentityInterface(fields=['template', 'transforms_list',
'passive_deformed_templates']),
run_without_submitting=True,
name='outputspec')
### NOTE MAP NODE! warp each of the original images to the provided fixed_image as the template
BeginANTS = pe.MapNode(interface=Registration(), name='BeginANTS', iterfield=['moving_image'])
BeginANTS.inputs.dimension = 3
""" This is the recommended set of parameters from the ANTS developers """
BeginANTS.inputs.output_transform_prefix = str(iterationPhasePrefix) + '_tfm'
BeginANTS.inputs.transforms = ["Rigid", "Similarity", "Affine", "SyN"]
BeginANTS.inputs.transform_parameters = [[0.1], [0.1], [0.1], [0.15, 3.0, 0.0]]
BeginANTS.inputs.metric = ['Mattes', 'Mattes', 'Mattes', 'CC']
BeginANTS.inputs.sampling_strategy = ['Regular', 'Regular', 'Regular', None]
BeginANTS.inputs.sampling_percentage = [1.0, 1.0, 1.0, 1.0]
BeginANTS.inputs.metric_weight = [1.0, 1.0, 1.0, 1.0]
BeginANTS.inputs.radius_or_number_of_bins = [32, 32, 32, 4]
BeginANTS.inputs.convergence_threshold = [5e-7, 5e-7, 5e-7, 5e-7]
BeginANTS.inputs.convergence_window_size = [25, 25, 25, 25]
BeginANTS.inputs.use_histogram_matching = [True, True, True, True]
BeginANTS.inputs.number_of_iterations = [[2000, 2000], [2000, 2000], [1000, 1000, 100], [10000, 500, 500, 200]]
BeginANTS.inputs.smoothing_sigmas = [[4, 2], [5, 2], [4, 2, 1], [5, 4, 2, 0]]
BeginANTS.inputs.shrink_factors = [[4, 2], [5, 2], [4, 2, 1], [5, 4, 2, 1]]
BeginANTS.inputs.use_estimate_learning_rate_once = [False, False, False, False]
BeginANTS.inputs.write_composite_transform = True
BeginANTS.inputs.collapse_output_transforms = True
BeginANTS.inputs.winsorize_lower_quantile = 0.025
BeginANTS.inputs.winsorize_upper_quantile = 0.975
BeginANTS.inputs.output_warped_image = 'atlas2subject.nii.gz'
BeginANTS.inputs.output_inverse_warped_image = 'subject2atlas.nii.gz'
GetMovingImagesNode = pe.Node(interface=util.Function(function=GetMovingImages,
input_names=['ListOfImagesDictionaries', 'registrationImageTypes', 'interpolationMapping'],
output_names=['moving_images', 'moving_interpolation_type']),
run_without_submitting=True,
name='99_GetMovingImagesNode')
TemplateBuildSingleIterationWF.connect(inputSpec, 'ListOfImagesDictionaries', GetMovingImagesNode, 'ListOfImagesDictionaries')
TemplateBuildSingleIterationWF.connect(inputSpec, 'registrationImageTypes', GetMovingImagesNode, 'registrationImageTypes')
TemplateBuildSingleIterationWF.connect(inputSpec, 'interpolationMapping', GetMovingImagesNode, 'interpolationMapping')
TemplateBuildSingleIterationWF.connect(GetMovingImagesNode, 'moving_images', BeginANTS, 'moving_image')
TemplateBuildSingleIterationWF.connect(GetMovingImagesNode, 'moving_interpolation_type', BeginANTS, 'interpolation')
TemplateBuildSingleIterationWF.connect(inputSpec, 'fixed_image', BeginANTS, 'fixed_image')
## Now warp all the input_images images
wimtdeformed = pe.MapNode(interface=ApplyTransforms(),
iterfield=['transforms', 'invert_transform_flags', 'input_image'],
name='wimtdeformed')
wimtdeformed.inputs.interpolation = 'Linear'
wimtdeformed.default_value = 0
# HACK: Should try using forward_composite_transform
TemplateBuildSingleIterationWF.connect(BeginANTS, 'forward_transforms', wimtdeformed, 'transforms')
TemplateBuildSingleIterationWF.connect(BeginANTS, 'forward_invert_flags', wimtdeformed, 'invert_transform_flags')
TemplateBuildSingleIterationWF.connect(GetMovingImagesNode, 'moving_images', wimtdeformed, 'input_image')
TemplateBuildSingleIterationWF.connect(inputSpec, 'fixed_image', wimtdeformed, 'reference_image')
## Shape Update Next =====
## Now Average All input_images deformed images together to create an updated template average
AvgDeformedImages = pe.Node(interface=AverageImages(), name='AvgDeformedImages')
AvgDeformedImages.inputs.dimension = 3
AvgDeformedImages.inputs.output_average_image = str(iterationPhasePrefix) + '.nii.gz'
AvgDeformedImages.inputs.normalize = True
TemplateBuildSingleIterationWF.connect(wimtdeformed, "output_image", AvgDeformedImages, 'images')
## Now average all affine transforms together
AvgAffineTransform = pe.Node(interface=AverageAffineTransform(), name='AvgAffineTransform')
AvgAffineTransform.inputs.dimension = 3
AvgAffineTransform.inputs.output_affine_transform = 'Avererage_' + str(iterationPhasePrefix) + '_Affine.h5'
SplitAffineAndWarpsNode = pe.Node(interface=util.Function(function=SplitAffineAndWarpComponents,
input_names=['list_of_transforms_lists'],
output_names=['affine_component_list', 'warp_component_list']),
run_without_submitting=True,
name='99_SplitAffineAndWarpsNode')
TemplateBuildSingleIterationWF.connect(BeginANTS, 'forward_transforms', SplitAffineAndWarpsNode, 'list_of_transforms_lists')
TemplateBuildSingleIterationWF.connect(SplitAffineAndWarpsNode, 'affine_component_list', AvgAffineTransform, 'transforms')
## Now average the warp fields togther
AvgWarpImages = pe.Node(interface=AverageImages(), name='AvgWarpImages')
AvgWarpImages.inputs.dimension = 3
AvgWarpImages.inputs.output_average_image = str(iterationPhasePrefix) + 'warp.nii.gz'
AvgWarpImages.inputs.normalize = True
TemplateBuildSingleIterationWF.connect(SplitAffineAndWarpsNode, 'warp_component_list', AvgWarpImages, 'images')
## Now average the images together
## TODO: For now GradientStep is set to 0.25 as a hard coded default value.
GradientStep = 0.25
GradientStepWarpImage = pe.Node(interface=MultiplyImages(), name='GradientStepWarpImage')
GradientStepWarpImage.inputs.dimension = 3
GradientStepWarpImage.inputs.second_input = -1.0 * GradientStep
GradientStepWarpImage.inputs.output_product_image = 'GradientStep0.25_' + str(iterationPhasePrefix) + '_warp.nii.gz'
TemplateBuildSingleIterationWF.connect(AvgWarpImages, 'output_average_image', GradientStepWarpImage, 'first_input')
## Now create the new template shape based on the average of all deformed images
UpdateTemplateShape = pe.Node(interface=ApplyTransforms(), name='UpdateTemplateShape')
UpdateTemplateShape.inputs.invert_transform_flags = [True]
UpdateTemplateShape.inputs.interpolation = 'Linear'
UpdateTemplateShape.default_value = 0
TemplateBuildSingleIterationWF.connect(AvgDeformedImages, 'output_average_image', UpdateTemplateShape, 'reference_image')
TemplateBuildSingleIterationWF.connect([(AvgAffineTransform, UpdateTemplateShape, [(('affine_transform', makeListOfOneElement), 'transforms')]), ])
TemplateBuildSingleIterationWF.connect(GradientStepWarpImage, 'output_product_image', UpdateTemplateShape, 'input_image')
ApplyInvAverageAndFourTimesGradientStepWarpImage = pe.Node(interface=util.Function(function=MakeTransformListWithGradientWarps,
input_names=['averageAffineTranform', 'gradientStepWarp'],
output_names=['TransformListWithGradientWarps']),
run_without_submitting=True,
name='99_MakeTransformListWithGradientWarps')
ApplyInvAverageAndFourTimesGradientStepWarpImage.inputs.ignore_exception = True
TemplateBuildSingleIterationWF.connect(AvgAffineTransform, 'affine_transform', ApplyInvAverageAndFourTimesGradientStepWarpImage, 'averageAffineTranform')
TemplateBuildSingleIterationWF.connect(UpdateTemplateShape, 'output_image', ApplyInvAverageAndFourTimesGradientStepWarpImage, 'gradientStepWarp')
ReshapeAverageImageWithShapeUpdate = pe.Node(interface=ApplyTransforms(), name='ReshapeAverageImageWithShapeUpdate')
ReshapeAverageImageWithShapeUpdate.inputs.invert_transform_flags = [True, False, False, False, False]
ReshapeAverageImageWithShapeUpdate.inputs.interpolation = 'Linear'
ReshapeAverageImageWithShapeUpdate.default_value = 0
ReshapeAverageImageWithShapeUpdate.inputs.output_image = 'ReshapeAverageImageWithShapeUpdate.nii.gz'
TemplateBuildSingleIterationWF.connect(AvgDeformedImages, 'output_average_image', ReshapeAverageImageWithShapeUpdate, 'input_image')
TemplateBuildSingleIterationWF.connect(AvgDeformedImages, 'output_average_image', ReshapeAverageImageWithShapeUpdate, 'reference_image')
TemplateBuildSingleIterationWF.connect(ApplyInvAverageAndFourTimesGradientStepWarpImage, 'TransformListWithGradientWarps', ReshapeAverageImageWithShapeUpdate, 'transforms')
TemplateBuildSingleIterationWF.connect(ReshapeAverageImageWithShapeUpdate, 'output_image', outputSpec, 'template')
######
######
###### Process all the passive deformed images in a way similar to the main image used for registration
######
######
######
##############################################
## Now warp all the ListOfPassiveImagesDictionaries images
FlattenTransformAndImagesListNode = pe.Node(Function(function=FlattenTransformAndImagesList,
input_names=['ListOfPassiveImagesDictionaries', 'transforms',
'invert_transform_flags', 'interpolationMapping'],
output_names=['flattened_images', 'flattened_transforms', 'flattened_invert_transform_flags',
'flattened_image_nametypes', 'flattened_interpolation_type']),
run_without_submitting=True, name="99_FlattenTransformAndImagesList")
GetPassiveImagesNode = pe.Node(interface=util.Function(function=GetPassiveImages,
input_names=['ListOfImagesDictionaries', 'registrationImageTypes'],
output_names=['ListOfPassiveImagesDictionaries']),
run_without_submitting=True,
name='99_GetPassiveImagesNode')
TemplateBuildSingleIterationWF.connect(inputSpec, 'ListOfImagesDictionaries', GetPassiveImagesNode, 'ListOfImagesDictionaries')
TemplateBuildSingleIterationWF.connect(inputSpec, 'registrationImageTypes', GetPassiveImagesNode, 'registrationImageTypes')
TemplateBuildSingleIterationWF.connect(GetPassiveImagesNode, 'ListOfPassiveImagesDictionaries', FlattenTransformAndImagesListNode, 'ListOfPassiveImagesDictionaries')
TemplateBuildSingleIterationWF.connect(inputSpec, 'interpolationMapping', FlattenTransformAndImagesListNode, 'interpolationMapping')
TemplateBuildSingleIterationWF.connect(BeginANTS, 'forward_transforms', FlattenTransformAndImagesListNode, 'transforms')
TemplateBuildSingleIterationWF.connect(BeginANTS, 'forward_invert_flags', FlattenTransformAndImagesListNode, 'invert_transform_flags')
wimtPassivedeformed = pe.MapNode(interface=ApplyTransforms(),
iterfield=['transforms', 'invert_transform_flags', 'input_image', 'interpolation'],
name='wimtPassivedeformed')
wimtPassivedeformed.default_value = 0
TemplateBuildSingleIterationWF.connect(AvgDeformedImages, 'output_average_image', wimtPassivedeformed, 'reference_image')
TemplateBuildSingleIterationWF.connect(FlattenTransformAndImagesListNode, 'flattened_interpolation_type', wimtPassivedeformed, 'interpolation')
TemplateBuildSingleIterationWF.connect(FlattenTransformAndImagesListNode, 'flattened_images', wimtPassivedeformed, 'input_image')
TemplateBuildSingleIterationWF.connect(FlattenTransformAndImagesListNode, 'flattened_transforms', wimtPassivedeformed, 'transforms')
TemplateBuildSingleIterationWF.connect(FlattenTransformAndImagesListNode, 'flattened_invert_transform_flags', wimtPassivedeformed, 'invert_transform_flags')
RenestDeformedPassiveImagesNode = pe.Node(Function(function=RenestDeformedPassiveImages,
input_names=['deformedPassiveImages', 'flattened_image_nametypes', 'interpolationMapping'],
output_names=['nested_imagetype_list', 'outputAverageImageName_list',
'image_type_list', 'nested_interpolation_type']),
run_without_submitting=True, name="99_RenestDeformedPassiveImages")
TemplateBuildSingleIterationWF.connect(inputSpec, 'interpolationMapping', RenestDeformedPassiveImagesNode, 'interpolationMapping')
TemplateBuildSingleIterationWF.connect(wimtPassivedeformed, 'output_image', RenestDeformedPassiveImagesNode, 'deformedPassiveImages')
TemplateBuildSingleIterationWF.connect(FlattenTransformAndImagesListNode, 'flattened_image_nametypes', RenestDeformedPassiveImagesNode, 'flattened_image_nametypes')
## Now Average All passive input_images deformed images together to create an updated template average
AvgDeformedPassiveImages = pe.MapNode(interface=AverageImages(),
iterfield=['images', 'output_average_image'],
name='AvgDeformedPassiveImages')
AvgDeformedPassiveImages.inputs.dimension = 3
AvgDeformedPassiveImages.inputs.normalize = False
TemplateBuildSingleIterationWF.connect(RenestDeformedPassiveImagesNode, "nested_imagetype_list", AvgDeformedPassiveImages, 'images')
TemplateBuildSingleIterationWF.connect(RenestDeformedPassiveImagesNode, "outputAverageImageName_list", AvgDeformedPassiveImages, 'output_average_image')
## -- TODO: Now neeed to reshape all the passive images as well
ReshapeAveragePassiveImageWithShapeUpdate = pe.MapNode(interface=ApplyTransforms(),
iterfield=['input_image', 'reference_image', 'output_image', 'interpolation'],
name='ReshapeAveragePassiveImageWithShapeUpdate')
ReshapeAveragePassiveImageWithShapeUpdate.inputs.invert_transform_flags = [True, False, False, False, False]
ReshapeAveragePassiveImageWithShapeUpdate.default_value = 0
TemplateBuildSingleIterationWF.connect(RenestDeformedPassiveImagesNode, 'nested_interpolation_type', ReshapeAveragePassiveImageWithShapeUpdate, 'interpolation')
TemplateBuildSingleIterationWF.connect(RenestDeformedPassiveImagesNode, 'outputAverageImageName_list', ReshapeAveragePassiveImageWithShapeUpdate, 'output_image')
TemplateBuildSingleIterationWF.connect(AvgDeformedPassiveImages, 'output_average_image', ReshapeAveragePassiveImageWithShapeUpdate, 'input_image')
TemplateBuildSingleIterationWF.connect(AvgDeformedPassiveImages, 'output_average_image', ReshapeAveragePassiveImageWithShapeUpdate, 'reference_image')
TemplateBuildSingleIterationWF.connect(ApplyInvAverageAndFourTimesGradientStepWarpImage, 'TransformListWithGradientWarps', ReshapeAveragePassiveImageWithShapeUpdate, 'transforms')
TemplateBuildSingleIterationWF.connect(ReshapeAveragePassiveImageWithShapeUpdate, 'output_image', outputSpec, 'passive_deformed_templates')
return TemplateBuildSingleIterationWF