Skip to content

Commit 8b62ef2

Browse files
author
Natalie Johnston
committed
ENH: Add Python Example GlobalRegistrationOfTwoImages
1 parent 6f0e16b commit 8b62ef2

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

src/Registration/Common/GlobalRegistrationOfTwoImages/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ enable_testing()
2424

2525
add_test(NAME GlobalRegistrationOfTwoImagesTest
2626
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/GlobalRegistrationOfTwoImages)
27+
28+
if( ITK_WRAP_PYTHON )
29+
add_test( NAME GlobalRegistrationOfTwoImagesTestPython
30+
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Code.py
31+
)
32+
endif()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
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

Comments
 (0)