1
+ import itk
2
+
3
+ Dimension = 2
4
+ PixelType = itk .UC
5
+ Double = itk .D
6
+ Float = itk .F
7
+ ImageType = itk .Image [PixelType , Dimension ]
8
+
9
+ def main ():
10
+ # The transform that will map the fixed image into the moving image.
11
+ TransformType = itk .TranslationTransform [Double , Dimension ]
12
+
13
+ # An optimizer is required to explore the parameter space of the transform
14
+ # in search of optimal values of the metric.
15
+ OptimizerType = itk .RegularStepGradientDescentOptimizer
16
+
17
+ # The metric will compare how well the two images match each other. Metric
18
+ # types are usually parameterized by the image types as it can be seen in
19
+ # the following type declaration.
20
+ MetricType = itk .MeanSquaresImageToImageMetric [ImageType , ImageType ]
21
+
22
+ # Finally, the type of the interpolator is declared. The interpolator will
23
+ # evaluate the intensities of the moving image at non-grid positions.
24
+ InterpolatorType = itk .LinearInterpolateImageFunction [ImageType , Double ]
25
+
26
+ # The registration method type is instantiated using the types of the
27
+ # fixed and moving images. This class is responsible for interconnecting
28
+ # all the components that we have described so far.
29
+ RegistrationType = itk .ImageRegistrationMethod [ImageType , ImageType ]
30
+
31
+ # Create components
32
+ metric = MetricType .New ()
33
+ transform = TransformType .New ()
34
+ optimizer = OptimizerType .New ()
35
+ interpolator = InterpolatorType .New ()
36
+ registration = RegistrationType .New ()
37
+
38
+ # Each component is now connected to the instance of the registration method.
39
+ registration .SetMetric (metric )
40
+ registration .SetOptimizer (optimizer )
41
+ registration .SetTransform (transform )
42
+ registration .SetInterpolator (interpolator )
43
+
44
+ # Get the two images
45
+ fixedImage = ImageType .New ()
46
+ movingImage = ImageType .New ()
47
+
48
+ CreateSphereImage (fixedImage )
49
+ CreateEllipseImage (movingImage )
50
+
51
+ # Write the two synthetic inputs
52
+ itk .imwrite (fixedImage , "fixed.png" )
53
+ itk .imwrite (movingImage , "moving.png" )
54
+
55
+ # Set the registration inputs
56
+ registration .SetFixedImage (fixedImage )
57
+ registration .SetMovingImage (movingImage )
58
+
59
+ registration .SetFixedImageRegion (fixedImage .GetLargestPossibleRegion ())
60
+
61
+ # Initialize the transform
62
+ initialParameters = itk .OptimizerParameters [itk .D ](transform .GetNumberOfParameters ())
63
+
64
+ initialParameters [0 ] = 0.0 # Initial offset along X
65
+ initialParameters [1 ] = 0.0 # Initial offset along Y
66
+
67
+ registration .SetInitialTransformParameters (initialParameters )
68
+
69
+ optimizer .SetMaximumStepLength (4.00 )
70
+ optimizer .SetMinimumStepLength (0.01 )
71
+
72
+ # Set a stopping criterion
73
+ optimizer .SetNumberOfIterations (200 )
74
+
75
+ # Connect an observer
76
+ # surface = dict()
77
+ # def print_iteration():
78
+ # surface[tuple(optimizer.GetCurrentPosition())] = optimizer.GetValue()
79
+ # print(surface)
80
+
81
+ # optimizer.AddObserver(itk.IterationEvent(), print_iteration)
82
+
83
+
84
+ try :
85
+ registration .Update ()
86
+ except :
87
+ print ("ExceptionObject caught !" )
88
+ return
89
+
90
+ # The result of the registration process is an array of parameters that
91
+ # defines the spatial transformation in an unique way. This final result is
92
+ # obtained using the \code{GetLastTransformParameters()} method.
93
+
94
+ finalParameters = registration .GetLastTransformParameters ()
95
+
96
+ # In the case of the \doxygen{TranslationTransform}, there is a
97
+ # straightforward interpretation of the parameters. Each element of the
98
+ # array corresponds to a translation along one spatial dimension.
99
+
100
+ TranslationAlongX = finalParameters [0 ]
101
+ TranslationAlongY = finalParameters [1 ]
102
+
103
+ # The optimizer can be queried for the actual number of iterations
104
+ # performed to reach convergence. The \code{GetCurrentIteration()}
105
+ # method returns this value. A large number of iterations may be an
106
+ # indication that the maximum step length has been set too small, which
107
+ # is undesirable since it results in long computational times.
108
+
109
+ numberOfIterations = optimizer .GetCurrentIteration ()
110
+
111
+ # The value of the image metric corresponding to the last set of parameters
112
+ # can be obtained with the \code{GetValue()} method of the optimizer.
113
+
114
+ bestValue = optimizer .GetValue ()
115
+
116
+ # Print out results
117
+ print ("Result = " )
118
+ print (" Translation X = {}" .format (TranslationAlongX ))
119
+ print (" Translation Y = {}" .format (TranslationAlongY ))
120
+ print (" Iterations = {}" .format (numberOfIterations ))
121
+ print (" Metric value = {}" .format (bestValue ))
122
+
123
+ # It is common, as the last step of a registration task, to use the
124
+ # resulting transform to map the moving image into the fixed image space.
125
+ # This is easily done with the \doxygen{ResampleImageFilter}. Please
126
+ # refer to Section~\ref{sec:ResampleImageFilter} for details on the use
127
+ # of this filter. First, a ResampleImageFilter type is instantiated
128
+ # using the image types. It is convenient to use the fixed image type as
129
+ # the output type since it is likely that the transformed moving image
130
+ # will be compared with the fixed image.
131
+ ResampleFilterType = itk .ResampleImageFilter [ImageType , ImageType ]
132
+
133
+ # A resampling filter is created and the moving image is connected as its input.
134
+
135
+ resampler = ResampleFilterType .New ()
136
+ resampler .SetInput (movingImage )
137
+
138
+ # The Transform that is produced as output of the Registration method is
139
+ # also passed as input to the resampling filter. Note the use of the
140
+ # methods \code{GetOutput()} and \code{Get()}. This combination is needed
141
+ # here because the registration method acts as a filter whose output is a
142
+ # transform decorated in the form of a \doxygen{DataObject}. For details in
143
+ # this construction you may want to read the documentation of the
144
+ # \doxygen{DataObjectDecorator}.
145
+
146
+ resampler .SetTransform (registration .GetOutput ().Get ())
147
+
148
+ # As described in Section \ref{sec:ResampleImageFilter}, the
149
+ # ResampleImageFilter requires additional parameters to be specified, in
150
+ # particular, the spacing, origin and size of the output image. The default
151
+ # pixel value is also set to a distinct gray level in order to highlight
152
+ # the regions that are mapped outside of the moving image.
153
+
154
+ resampler .SetSize (itk .size (fixedImage ))
155
+ resampler .SetOutputOrigin (fixedImage .GetOrigin ())
156
+ resampler .SetOutputSpacing (fixedImage .GetSpacing ())
157
+ resampler .SetOutputDirection (fixedImage .GetDirection ())
158
+ resampler .SetDefaultPixelValue (100 )
159
+
160
+ # The output of the filter is passed to a writer that will store the
161
+ # image in a file. An \doxygen{CastImageFilter} is used to convert the
162
+ # pixel type of the resampled image to the final type used by the
163
+ # writer. The cast and writer filters are instantiated below.
164
+ CastFilterType = itk .CastImageFilter [ImageType , ImageType ]
165
+
166
+ caster = CastFilterType .New ()
167
+ caster .SetInput (resampler .GetOutput ())
168
+ itk .imwrite (caster .GetOutput (), "outputPython.png" )
169
+
170
+
171
+ # The fixed image and the transformed moving image can easily be compared
172
+ # using the \doxygen{SubtractImageFilter}. This pixel-wise filter computes
173
+ # the difference between homologous pixels of its two input images.
174
+
175
+
176
+ DifferenceFilterType = itk .SubtractImageFilter [
177
+ ImageType ,
178
+ ImageType ,
179
+ ImageType ]
180
+
181
+ difference = DifferenceFilterType .New ()
182
+ difference .SetInput1 ( fixedImage )
183
+ difference .SetInput2 ( resampler .GetOutput () )
184
+
185
+ return
186
+
187
+
188
+ def CreateEllipseImage (image ):
189
+ EllipseType = itk .EllipseSpatialObject [Dimension ]
190
+
191
+ SpatialObjectToImageFilterType = itk .SpatialObjectToImageFilter [itk .SpatialObject [2 ], ImageType ]
192
+
193
+ imageFilter = SpatialObjectToImageFilterType .New ()
194
+
195
+ size = itk .Size [Dimension ]()
196
+ size [0 ] = 100
197
+ size [1 ] = 100
198
+
199
+ imageFilter .SetSize (size )
200
+
201
+ spacing = [1 , 1 ]
202
+ imageFilter .SetSpacing (spacing )
203
+
204
+ ellipse = EllipseType .New ()
205
+ radiusArray = itk .Array [Float ]()
206
+ radiusArray .SetSize (Dimension )
207
+ radiusArray [0 ] = 10
208
+ radiusArray [1 ] = 20
209
+ ellipse .SetRadiusInObjectSpace (radiusArray )
210
+
211
+ TransformType = itk .AffineTransform [Double , Dimension ]
212
+ transform = TransformType .New ()
213
+ transform .SetIdentity ()
214
+
215
+ translation = itk .Vector [Float , Dimension ]()
216
+ translation [0 ] = 65
217
+ translation [1 ] = 45
218
+ transform .Translate (translation , False )
219
+
220
+ ellipse .SetObjectToParentTransform (transform )
221
+
222
+ imageFilter .SetInput (ellipse )
223
+
224
+ ellipse .SetDefaultInsideValue (255 )
225
+ ellipse .SetDefaultOutsideValue (0 )
226
+ imageFilter .SetUseObjectValue (True )
227
+ imageFilter .SetOutsideValue (0 )
228
+
229
+ imageFilter .Update ()
230
+ image .Graft (imageFilter .GetOutput ())
231
+
232
+
233
+ def CreateSphereImage (image ):
234
+ EllipseType = itk .EllipseSpatialObject [Dimension ]
235
+
236
+ SpatialObjectToImageFilterType = itk .SpatialObjectToImageFilter [itk .SpatialObject [2 ], itk .Image [itk .UC ,2 ]]
237
+
238
+ imageFilter = SpatialObjectToImageFilterType .New ()
239
+
240
+ size = itk .Size [Dimension ]()
241
+ size [0 ] = 100
242
+ size [1 ] = 100
243
+ imageFilter .SetSize (size )
244
+
245
+ spacing = [1 , 1 ]
246
+ imageFilter .SetSpacing (spacing )
247
+
248
+ ellipse = EllipseType .New ()
249
+ radiusArray = itk .Array [Float ]()
250
+ radiusArray .SetSize (Dimension )
251
+ radiusArray [0 ] = 10
252
+ radiusArray [1 ] = 10
253
+ ellipse .SetRadiusInObjectSpace (radiusArray )
254
+
255
+ TransformType = itk .AffineTransform [Double , Dimension ]
256
+ transform = TransformType .New ()
257
+ transform .SetIdentity ()
258
+
259
+ translation = itk .Vector [PixelType , Dimension ]()
260
+ translation [0 ] = 50
261
+ translation [1 ] = 50
262
+ transform .Translate (translation , False )
263
+
264
+ ellipse .SetObjectToParentTransform (transform )
265
+
266
+ imageFilter .SetInput (ellipse )
267
+
268
+ ellipse .SetDefaultInsideValue (255 )
269
+ ellipse .SetDefaultOutsideValue (0 )
270
+ imageFilter .SetUseObjectValue (True )
271
+ imageFilter .SetOutsideValue (0 )
272
+
273
+ imageFilter .Update ()
274
+ image .Graft (imageFilter .GetOutput ())
275
+
276
+ if __name__ == "__main__" :
277
+ main ()
0 commit comments