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 #include "itkDemonsImageToImageMetricv4.h"
19 #include "itkTranslationTransform.h"
20 #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransform.h"
21 #include "itkTestingMacros.h"
22 #include "itkMath.h"
23 
24 /* Simple test to verify that class builds and runs.
25  * Results are not verified. See ImageToImageMetricv4Test
26  * for verification of basic metric functionality.
27  *
28  * TODO Numerical verification.
29  */
30 
itkDemonsImageToImageMetricv4Test(int,char ** const)31 int itkDemonsImageToImageMetricv4Test(int, char ** const)
32 {
33 
34   constexpr unsigned int imageSize = 5;
35   constexpr unsigned int imageDimensionality = 3;
36   using ImageType = itk::Image< double, imageDimensionality >;
37 
38   ImageType::SizeType       size;
39   size.Fill( imageSize );
40   ImageType::IndexType      index;
41   index.Fill( 0 );
42   ImageType::RegionType     region;
43   region.SetSize( size );
44   region.SetIndex( index );
45   ImageType::SpacingType    spacing;
46   spacing.Fill(1.0);
47   ImageType::PointType      origin;
48   origin.Fill(0);
49   ImageType::DirectionType  direction;
50   direction.SetIdentity();
51 
52   /* Create simple test images. */
53   ImageType::Pointer fixedImage = ImageType::New();
54   fixedImage->SetRegions( region );
55   fixedImage->SetSpacing( spacing );
56   fixedImage->SetOrigin( origin );
57   fixedImage->SetDirection( direction );
58   fixedImage->Allocate();
59 
60   ImageType::Pointer movingImage = ImageType::New();
61   movingImage->SetRegions( region );
62   movingImage->SetSpacing( spacing );
63   movingImage->SetOrigin( origin );
64   movingImage->SetDirection( direction );
65   movingImage->Allocate();
66 
67   /* Fill images */
68   itk::ImageRegionIterator<ImageType> itFixed( fixedImage, region );
69   itFixed.GoToBegin();
70   unsigned int count = 1;
71   while( !itFixed.IsAtEnd() )
72     {
73     itFixed.Set( count*count );
74     count++;
75     ++itFixed;
76     }
77 
78   itk::ImageRegionIteratorWithIndex<ImageType> itMoving( movingImage, region );
79 
80   itMoving.GoToBegin();
81   count = 1;
82 
83   while( !itMoving.IsAtEnd() )
84     {
85     itMoving.Set( 1.0/(count*count) );
86     count++;
87     ++itMoving;
88     }
89 
90   /* Transforms */
91   using TranslationTransformType = itk::TranslationTransform<double,imageDimensionality>;
92   using DisplacementTransformType = itk::GaussianSmoothingOnUpdateDisplacementFieldTransform< double, imageDimensionality>;
93 
94   TranslationTransformType::Pointer translationTransform = TranslationTransformType::New();
95   translationTransform->SetIdentity();
96 
97   DisplacementTransformType::Pointer displacementTransform = DisplacementTransformType::New();
98   using DisplacementFieldType = DisplacementTransformType::DisplacementFieldType;
99   DisplacementFieldType::Pointer field = DisplacementFieldType::New();
100   field->SetRegions( fixedImage->GetLargestPossibleRegion() );
101   field->CopyInformation( fixedImage );
102   field->Allocate();
103   DisplacementTransformType::OutputVectorType zeroVector;
104   zeroVector.Fill( 0 );
105   field->FillBuffer( zeroVector );
106   displacementTransform->SetDisplacementField( field );
107   displacementTransform->SetGaussianSmoothingVarianceForTheUpdateField( 5 );
108   displacementTransform->SetGaussianSmoothingVarianceForTheTotalField( 6 );
109 
110   /* The metric */
111   using MetricType = itk::DemonsImageToImageMetricv4< ImageType, ImageType, ImageType >;
112 
113   MetricType::Pointer metric = MetricType::New();
114 
115   /* Assign images and transforms.
116    * By not setting a virtual domain image or virtual domain settings,
117    * the metric will use the fixed image for the virtual domain. */
118   metric->SetFixedImage( fixedImage );
119   metric->SetMovingImage( movingImage );
120   metric->SetFixedTransform( translationTransform );
121   metric->SetMovingTransform( displacementTransform );
122 
123   /* Test 1st with fixed image gradient source. This is the default */
124   metric->SetGradientSource( MetricType::GRADIENT_SOURCE_FIXED );
125   if( metric->GetGradientSource() != MetricType::GRADIENT_SOURCE_FIXED )
126     {
127     std::cerr << "Failed setting fixed image gradient source." << std::endl;
128     return EXIT_FAILURE;
129     }
130 
131   /* Initialize. */
132   try
133     {
134     std::cout << "Calling Initialize..." << std::endl;
135     metric->Initialize();
136     }
137   catch( itk::ExceptionObject & exc )
138     {
139     std::cerr << "Caught unexpected exception during Initialize: " << exc << std::endl;
140     return EXIT_FAILURE;
141     }
142 
143   // Evaluate with GetValueAndDerivative
144   MetricType::MeasureType valueReturn1, valueReturn2;
145   MetricType::DerivativeType derivativeReturn;
146 
147   try
148     {
149     std::cout << "Calling GetValueAndDerivative..." << std::endl;
150     metric->GetValueAndDerivative( valueReturn1, derivativeReturn );
151     }
152   catch( itk::ExceptionObject & exc )
153     {
154     std::cout << "Caught unexpected exception during GetValueAndDerivative: "
155               << exc;
156     return EXIT_FAILURE;
157     }
158 
159   /* Re-initialize. */
160   try
161     {
162     std::cout << "Calling Initialize..." << std::endl;
163     metric->Initialize();
164     }
165   catch( itk::ExceptionObject & exc )
166     {
167     std::cerr << "Caught unexpected exception during re-initialize: " << exc << std::endl;
168     return EXIT_FAILURE;
169     }
170 
171   try
172     {
173     std::cout << "Calling GetValue..." << std::endl;
174     valueReturn2 = metric->GetValue();
175     }
176   catch( itk::ExceptionObject & exc )
177     {
178     std::cout << "Caught unexpected exception during GetValue: "
179               << exc;
180     return EXIT_FAILURE;
181     }
182 
183   // Test same value returned by different methods
184   std::cout << "Check Value return values..." << std::endl;
185   if( itk::Math::NotExactlyEquals(valueReturn1, valueReturn2) )
186     {
187     std::cerr << "Results for Value don't match: " << valueReturn1
188               << ", " << valueReturn2 << std::endl;
189     }
190 
191   /* Test with moving image as gradient source. The default is
192    * to have the fixed image used for image gradients.  */
193   metric->SetFixedTransform( translationTransform );
194   metric->SetMovingTransform( displacementTransform );
195   metric->SetGradientSource( MetricType::GRADIENT_SOURCE_MOVING );
196   if( metric->GetGradientSource() != MetricType::GRADIENT_SOURCE_MOVING )
197     {
198     std::cerr << "Failed setting moving image gradient source." << std::endl;
199     return EXIT_FAILURE;
200     }
201 
202   try
203     {
204     std::cout << "Calling Initialize..." << std::endl;
205     metric->Initialize();
206     }
207   catch( itk::ExceptionObject & exc )
208     {
209     std::cerr << "Caught unexpected exception during initialize with moving "
210               << "image gradient source: " << exc << std::endl;
211     return EXIT_FAILURE;
212     }
213 
214   // Evaluate with GetValueAndDerivative
215   try
216     {
217     std::cout << "Calling GetValueAndDerivative..." << std::endl;
218     metric->GetValueAndDerivative( valueReturn1, derivativeReturn );
219     }
220   catch( itk::ExceptionObject & exc )
221     {
222     std::cout << "Caught unexpected exception during GetValueAndDerivative: "
223               << exc;
224     return EXIT_FAILURE;
225     }
226 
227   /* Re-initialize. */
228   try
229     {
230     std::cout << "Calling Initialize..." << std::endl;
231     metric->Initialize();
232     }
233   catch( itk::ExceptionObject & exc )
234     {
235     std::cerr << "Caught unexpected exception during re-initialize: " << exc << std::endl;
236     return EXIT_FAILURE;
237     }
238 
239   try
240     {
241     std::cout << "Calling GetValue..." << std::endl;
242     valueReturn2 = metric->GetValue();
243     }
244   catch( itk::ExceptionObject & exc )
245     {
246     std::cout << "Caught unexpected exception during GetValue: "
247               << exc;
248     return EXIT_FAILURE;
249     }
250 
251   // Test same value returned by different methods
252   std::cout << "Check Value return values..." << std::endl;
253   if( itk::Math::NotExactlyEquals(valueReturn1, valueReturn2) )
254     {
255     std::cerr << "Moving image gradient source: results for Value don't match: " << valueReturn1
256               << ", " << valueReturn2 << std::endl;
257     }
258 
259   /* Test expected exceptions when transform is not right type */
260   metric->SetMovingTransform( translationTransform );
261   TRY_EXPECT_EXCEPTION( metric->Initialize() );
262 
263   /* Exercise accessor method */
264   const auto testValue = static_cast<MetricType::InternalComputationValueType>(0.5);
265   metric->SetIntensityDifferenceThreshold( testValue );
266   if( itk::Math::NotExactlyEquals(metric->GetIntensityDifferenceThreshold(), testValue) )
267     {
268     std::cerr << "Set/GetIntensityDifferenceThreshold failed." << std::endl;
269     return EXIT_FAILURE;
270     }
271 
272   /* Print self */
273   metric->Print( std::cout );
274 
275   std::cout << "Test passed." << std::endl;
276   return EXIT_SUCCESS;
277 }
278