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