1 /*=========================================================================
2 *
3 * Copyright Insight Software Consortium
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18
19 //
20 // This example illustrates the use of \code{SpatialObject}s as masks for selecting the
21 // pixels that should contribute to the computation of Image Metrics. This
22 // example is almost identical to ImageRegistration6 with the exception that
23 // the \code{SpatialObject} masks are created and passed to the image metric.
24 //
25 //
26
27 #include "itkImageRegistrationMethod.h"
28 #include "itkMeanSquaresImageToImageMetric.h"
29 #include "itkRegularStepGradientDescentOptimizer.h"
30
31 #include "itkCenteredRigid2DTransform.h"
32 #include "itkCenteredTransformInitializer.h"
33
34 #include "itkImageFileReader.h"
35 #include "itkImageFileWriter.h"
36
37 #include "itkResampleImageFilter.h"
38 #include "itkCastImageFilter.h"
39 #include "itkSquaredDifferenceImageFilter.h"
40
41 //
42 // The most important header in this example is the one corresponding to the
43 // \doxygen{ImageMaskSpatialObject} class.
44 //
45 // \index{itk::ImageMaskSpatialObject!header}
46 //
47
48 #include "itkImageMaskSpatialObject.h"
49
50 //
51 // The following section of code implements a command observer
52 // that will monitor the evolution of the registration process.
53 //
54 #include "itkCommand.h"
55 class CommandIterationUpdate : public itk::Command
56 {
57 public:
58 using Self = CommandIterationUpdate;
59 using Superclass = itk::Command;
60 using Pointer = itk::SmartPointer<Self>;
61 itkNewMacro( Self );
62
63 protected:
64 CommandIterationUpdate() = default;
65
66 public:
67 using OptimizerType = itk::RegularStepGradientDescentOptimizer;
68 using OptimizerPointer = const OptimizerType *;
69
Execute(itk::Object * caller,const itk::EventObject & event)70 void Execute(itk::Object *caller, const itk::EventObject & event) override
71 {
72 Execute( (const itk::Object *)caller, event);
73 }
74
Execute(const itk::Object * object,const itk::EventObject & event)75 void Execute(const itk::Object * object, const itk::EventObject & event) override
76 {
77 auto optimizer = static_cast< OptimizerPointer >( object );
78 if( ! itk::IterationEvent().CheckEvent( &event ) )
79 {
80 return;
81 }
82 std::cout << optimizer->GetCurrentIteration() << " ";
83 std::cout << optimizer->GetValue() << " ";
84 std::cout << optimizer->GetCurrentPosition() << std::endl;
85 }
86 };
87
88 #include "itkTestDriverIncludeRequiredIOFactories.h"
main(int argc,char * argv[])89 int main( int argc, char *argv[] )
90 {
91 RegisterRequiredFactories();
92 if( argc < 5 )
93 {
94 std::cerr << "Missing Parameters " << std::endl;
95 std::cerr << "Usage: " << argv[0];
96 std::cerr << " fixedImageFile movingImageFile fixedImageMaskFile";
97 std::cerr << " outputImagefile [differenceOutputfile] ";
98 std::cerr << " [differenceBeforeRegistration] "<< std::endl;
99 return EXIT_FAILURE;
100 }
101
102 constexpr unsigned int Dimension = 2;
103 using PixelType = float;
104
105 using FixedImageType = itk::Image< PixelType, Dimension >;
106 using MovingImageType = itk::Image< PixelType, Dimension >;
107
108 using TransformType = itk::CenteredRigid2DTransform< double >;
109
110 using OptimizerType = itk::RegularStepGradientDescentOptimizer;
111 using MetricType = itk::MeanSquaresImageToImageMetric<
112 FixedImageType,
113 MovingImageType >;
114 using InterpolatorType = itk:: LinearInterpolateImageFunction<
115 MovingImageType,
116 double >;
117 using RegistrationType = itk::ImageRegistrationMethod<
118 FixedImageType,
119 MovingImageType >;
120
121 MetricType::Pointer metric = MetricType::New();
122 OptimizerType::Pointer optimizer = OptimizerType::New();
123 InterpolatorType::Pointer interpolator = InterpolatorType::New();
124 RegistrationType::Pointer registration = RegistrationType::New();
125
126 registration->SetMetric( metric );
127 registration->SetOptimizer( optimizer );
128 registration->SetInterpolator( interpolator );
129
130 TransformType::Pointer transform = TransformType::New();
131 registration->SetTransform( transform );
132
133 using FixedImageReaderType = itk::ImageFileReader< FixedImageType >;
134 using MovingImageReaderType = itk::ImageFileReader< MovingImageType >;
135 FixedImageReaderType::Pointer fixedImageReader = FixedImageReaderType::New();
136 MovingImageReaderType::Pointer movingImageReader = MovingImageReaderType::New();
137
138 fixedImageReader->SetFileName( argv[1] );
139 movingImageReader->SetFileName( argv[2] );
140
141 registration->SetFixedImage( fixedImageReader->GetOutput() );
142 registration->SetMovingImage( movingImageReader->GetOutput() );
143 fixedImageReader->Update();
144
145 registration->SetFixedImageRegion(
146 fixedImageReader->GetOutput()->GetBufferedRegion() );
147
148 using TransformInitializerType = itk::CenteredTransformInitializer<
149 TransformType,
150 FixedImageType,
151 MovingImageType >;
152 TransformInitializerType::Pointer initializer = TransformInitializerType::New();
153
154 initializer->SetTransform( transform );
155 initializer->SetFixedImage( fixedImageReader->GetOutput() );
156 initializer->SetMovingImage( movingImageReader->GetOutput() );
157
158 initializer->MomentsOn();
159
160 initializer->InitializeTransform();
161
162 transform->SetAngle( 0.0 );
163
164 registration->SetInitialTransformParameters( transform->GetParameters() );
165
166 using OptimizerScalesType = OptimizerType::ScalesType;
167 OptimizerScalesType optimizerScales( transform->GetNumberOfParameters() );
168 const double translationScale = 1.0 / 1000.0;
169
170 optimizerScales[0] = 1.0;
171 optimizerScales[1] = translationScale;
172 optimizerScales[2] = translationScale;
173 optimizerScales[3] = translationScale;
174 optimizerScales[4] = translationScale;
175
176 optimizer->SetScales( optimizerScales );
177
178 optimizer->SetMaximumStepLength( 0.1 );
179 optimizer->SetMinimumStepLength( 0.001 );
180 optimizer->SetNumberOfIterations( 200 );
181
182 // Create the Command observer and register it with the optimizer.
183 //
184 CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New();
185 optimizer->AddObserver( itk::IterationEvent(), observer );
186
187 //
188 // Here we instantiate the type of the \doxygen{ImageMaskSpatialObject}
189 // using the same dimension of the images to be registered.
190 //
191 // \index{itk::ImageMaskSpatialObject!Instantiation}
192 //
193
194 using MaskType = itk::ImageMaskSpatialObject< Dimension >;
195
196 //
197 // Then we use the type for creating the spatial object mask that will
198 // restrict the registration to a reduced region of the image.
199 //
200 // \index{itk::ImageMaskSpatialObject!New}
201 // \index{itk::ImageMaskSpatialObject!Pointer}
202 //
203
204 MaskType::Pointer spatialObjectMask = MaskType::New();
205
206 //
207 // The mask in this case is read from a binary file using the
208 // \code{ImageFileReader} instantiated for an \code{unsigned char} pixel
209 // type.
210 //
211
212 using ImageMaskType = itk::Image< unsigned char, Dimension >;
213
214 using MaskReaderType = itk::ImageFileReader< ImageMaskType >;
215
216 //
217 // The reader is constructed and a filename is passed to it.
218 //
219
220 MaskReaderType::Pointer maskReader = MaskReaderType::New();
221
222 maskReader->SetFileName( argv[3] );
223
224 //
225 // As usual, the reader is triggered by invoking its \code{Update()} method.
226 // Since this may eventually throw an exception, the call must be placed in
227 // a \code{try/catch} block. Note that a full fledged application will place
228 // this \code{try/catch} block at a much higher level, probably under the
229 // control of the GUI.
230 //
231
232 try
233 {
234 maskReader->Update();
235 }
236 catch( itk::ExceptionObject & err )
237 {
238 std::cerr << "ExceptionObject caught !" << std::endl;
239 std::cerr << err << std::endl;
240 return EXIT_FAILURE;
241 }
242
243 //
244 // The output of the mask reader is connected as input to the
245 // \code{ImageMaskSpatialObject}.
246 //
247 // \index{itk::ImageMaskSpatialObject!SetImage()}
248 //
249
250 spatialObjectMask->SetImage( maskReader->GetOutput() );
251
252 //
253 // Finally, the spatial object mask is passed to the image metric.
254 //
255 // \index{itk::ImageToImageMetric!SetFixedImage()}
256 //
257
258 metric->SetFixedImageMask( spatialObjectMask );
259
260 try
261 {
262 registration->Update();
263 std::cout << "Optimizer stop condition = "
264 << registration->GetOptimizer()->GetStopConditionDescription()
265 << std::endl;
266 }
267 catch( itk::ExceptionObject & err )
268 {
269 std::cerr << "ExceptionObject caught !" << std::endl;
270 std::cerr << err << std::endl;
271 return EXIT_FAILURE;
272 }
273
274 OptimizerType::ParametersType finalParameters =
275 registration->GetLastTransformParameters();
276
277 const double finalAngle = finalParameters[0];
278 const double finalRotationCenterX = finalParameters[1];
279 const double finalRotationCenterY = finalParameters[2];
280 const double finalTranslationX = finalParameters[3];
281 const double finalTranslationY = finalParameters[4];
282
283 const unsigned int numberOfIterations = optimizer->GetCurrentIteration();
284 const double bestValue = optimizer->GetValue();
285
286 // Print out results
287 //
288 const double finalAngleInDegrees = finalAngle * 45.0 / std::atan(1.0);
289
290 std::cout << "Result = " << std::endl;
291 std::cout << " Angle (radians) " << finalAngle << std::endl;
292 std::cout << " Angle (degrees) " << finalAngleInDegrees << std::endl;
293 std::cout << " Center X = " << finalRotationCenterX << std::endl;
294 std::cout << " Center Y = " << finalRotationCenterY << std::endl;
295 std::cout << " Translation X = " << finalTranslationX << std::endl;
296 std::cout << " Translation Y = " << finalTranslationY << std::endl;
297 std::cout << " Iterations = " << numberOfIterations << std::endl;
298 std::cout << " Metric value = " << bestValue << std::endl;
299
300 //
301 // Let's execute this example over some of the images provided in
302 // \code{Examples/Data}, for example:
303 //
304 // \begin{itemize}
305 // \item \code{BrainProtonDensitySliceBorder20.png}
306 // \item \code{BrainProtonDensitySliceR10X13Y17.png}
307 // \end{itemize}
308 //
309 // The second image is the result of intentionally rotating the first
310 // image by $10$ degrees and shifting it $13mm$ in $X$ and $17mm$ in
311 // $Y$. Both images have unit-spacing and are shown in Figure
312 // \ref{fig:FixedMovingImageRegistration5}.
313 //
314
315 transform->SetParameters( finalParameters );
316
317 TransformType::MatrixType matrix = transform->GetMatrix();
318 TransformType::OffsetType offset = transform->GetOffset();
319
320 std::cout << "Matrix = " << std::endl << matrix << std::endl;
321 std::cout << "Offset = " << std::endl << offset << std::endl;
322
323 //
324 // Now we resample the moving image using the transform resulting from the
325 // registration process.
326 //
327
328 using ResampleFilterType = itk::ResampleImageFilter<
329 MovingImageType,
330 FixedImageType >;
331 TransformType::Pointer finalTransform = TransformType::New();
332
333 finalTransform->SetParameters( finalParameters );
334 finalTransform->SetFixedParameters( transform->GetFixedParameters() );
335
336 ResampleFilterType::Pointer resample = ResampleFilterType::New();
337
338 resample->SetTransform( finalTransform );
339 resample->SetInput( movingImageReader->GetOutput() );
340
341 FixedImageType::Pointer fixedImage = fixedImageReader->GetOutput();
342
343 resample->SetSize( fixedImage->GetLargestPossibleRegion().GetSize() );
344 resample->SetOutputOrigin( fixedImage->GetOrigin() );
345 resample->SetOutputSpacing( fixedImage->GetSpacing() );
346 resample->SetOutputDirection( fixedImage->GetDirection() );
347 resample->SetDefaultPixelValue( 100 );
348
349 using OutputPixelType = unsigned char;
350
351 using OutputImageType = itk::Image< OutputPixelType, Dimension >;
352
353 using CastFilterType = itk::CastImageFilter<
354 FixedImageType,
355 OutputImageType >;
356
357 using WriterType = itk::ImageFileWriter< OutputImageType >;
358
359 WriterType::Pointer writer = WriterType::New();
360 CastFilterType::Pointer caster = CastFilterType::New();
361
362 writer->SetFileName( argv[4] );
363
364 caster->SetInput( resample->GetOutput() );
365 writer->SetInput( caster->GetOutput() );
366 writer->Update();
367
368 using DifferenceFilterType = itk::SquaredDifferenceImageFilter<
369 FixedImageType,
370 FixedImageType,
371 OutputImageType >;
372
373 DifferenceFilterType::Pointer difference = DifferenceFilterType::New();
374
375 WriterType::Pointer writer2 = WriterType::New();
376 writer2->SetInput( difference->GetOutput() );
377
378 // Compute the difference image between the
379 // fixed and resampled moving image.
380 if( argc >= 6 )
381 {
382 difference->SetInput1( fixedImageReader->GetOutput() );
383 difference->SetInput2( resample->GetOutput() );
384 writer2->SetFileName( argv[5] );
385 writer2->Update();
386 }
387
388 // Compute the difference image between the
389 // fixed and moving image before registration.
390 if( argc >= 7 )
391 {
392 writer2->SetFileName( argv[6] );
393 difference->SetInput1( fixedImageReader->GetOutput() );
394 difference->SetInput2( movingImageReader->GetOutput() );
395 writer2->Update();
396 }
397
398 return EXIT_SUCCESS;
399 }
400