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 #include "itkKappaStatisticImageToImageMetric.h"
20 #include "itkNearestNeighborInterpolateImageFunction.h"
21 #include "itkTranslationTransform.h"
22 #include "itkMath.h"
23 #include "itkTestingMacros.h"
24 
25 /**
26  *  This test exercised the various methods in the
27  *  itkKappaStatisticImageToImageMetric class.  Two binary images are
28  *  created for testing purposes -- one of a square and another of the
29  *  same square translated in both x and y.
30  *
31  */
32 
itkKappaStatisticImageToImageMetricTest(int,char * [])33 int itkKappaStatisticImageToImageMetricTest(int, char* [] )
34 {
35 
36   constexpr unsigned int Dimension = 2;
37 
38   using FixedImagePixelType = unsigned char;
39   using MovingImagePixelType = unsigned char;
40 
41   using CoordRepPixelType = double;
42 
43   using GradientPixelType = double;
44 
45   using FixedImageType = itk::Image< FixedImagePixelType, Dimension >;
46   using MovingImageType = itk::Image< MovingImagePixelType, Dimension >;
47   using GradientImageType = itk::Image< GradientPixelType, Dimension >;
48 
49   using MetricType = itk::KappaStatisticImageToImageMetric< FixedImageType, MovingImageType >;
50   using FixedImageIteratorType = itk::ImageRegionIteratorWithIndex< FixedImageType >;
51   using MovingImageIteratorType = itk::ImageRegionIteratorWithIndex< MovingImageType >;
52 
53   using GradientImageIteratorType = itk::ImageRegionIteratorWithIndex< GradientImageType >;
54   using TransformType = itk::TranslationTransform< CoordRepPixelType, Dimension >;
55   using InterpolatorType =
56       itk::NearestNeighborInterpolateImageFunction< MovingImageType, CoordRepPixelType >;
57 
58 
59   double epsilon = 0.000001;
60 
61   TransformType::Pointer    transform    = TransformType::New();
62   InterpolatorType::Pointer interpolator = InterpolatorType::New();
63 
64   FixedImageType::SizeType fixedImageSize;
65   fixedImageSize.Fill( 128 );
66 
67   // Create fixed image
68   FixedImageType::Pointer fixedImage = FixedImageType::New();
69   fixedImage->SetRegions( fixedImageSize );
70   fixedImage->Allocate( true ); // initialize buffer to zero
71   fixedImage->Update();
72 
73   FixedImageIteratorType fixedIt( fixedImage, fixedImage->GetBufferedRegion() );
74   for( fixedIt.GoToBegin(); !fixedIt.IsAtEnd(); ++fixedIt )
75     {
76     FixedImageType::IndexType index = fixedIt.GetIndex();
77     if( index[0] >= 48 && index[0] <= 80 && index[1] >= 48 && index[1] <= 80 )
78       {
79       fixedIt.Set(255);
80       }
81     }
82 
83   MovingImageType::SizeType movingImageSize;
84   movingImageSize.Fill( 128 );
85 
86   // Create moving image
87   MovingImageType::Pointer movingImage = MovingImageType::New();
88   movingImage->SetRegions( movingImageSize );
89   movingImage->Allocate( true ); // initialize buffer to zero
90   movingImage->Update();
91 
92   MovingImageIteratorType movingIt( movingImage, movingImage->GetBufferedRegion() );
93   for( movingIt.GoToBegin(); !movingIt.IsAtEnd(); ++movingIt )
94     {
95     MovingImageType::IndexType index = movingIt.GetIndex();
96     if( index[0] >= 55 && index[0] <= 87 && index[1] >= 55 && index[1] <= 87 )
97       {
98       movingIt.Set(255);
99       }
100     }
101 
102   MetricType::Pointer metric = MetricType::New();
103 
104   EXERCISE_BASIC_OBJECT_METHODS( metric, KappaStatisticImageToImageMetric,
105     ImageToImageMetric );
106 
107   MetricType::RealType foregroundValue = 255;
108   metric->SetForegroundValue( foregroundValue );
109   TEST_SET_GET_VALUE( foregroundValue, metric->GetForegroundValue() );
110 
111   bool useComplement = false;
112   metric->SetComplement( useComplement );
113   TEST_SET_GET_VALUE( useComplement, metric->GetComplement() );
114 
115   metric->ComplementOff();
116   TEST_SET_GET_VALUE( false, metric->GetComplement() );
117 
118   transform->SetIdentity();
119   metric->SetTransform( transform );
120 
121   TransformType::ParametersType parameters = transform->GetParameters();
122 
123   // Test error conditions
124   //
125   TRY_EXPECT_EXCEPTION( metric->GetValue( parameters ) );
126 
127   metric->SetFixedImage( fixedImage );
128 
129   TRY_EXPECT_EXCEPTION( metric->GetValue( parameters ) );
130 
131   metric->SetMovingImage( movingImage );
132   metric->SetInterpolator( interpolator );
133   metric->SetFixedImageRegion( fixedImage->GetBufferedRegion() );
134 
135   MetricType::DerivativeType derivative;
136   TRY_EXPECT_EXCEPTION( metric->GetDerivative( parameters, derivative ) );
137 
138   TRY_EXPECT_NO_EXCEPTION( metric->Initialize() );
139 
140   metric->SetFixedImage( nullptr );
141   TRY_EXPECT_EXCEPTION( metric->GetDerivative( parameters, derivative ) );
142 
143   metric->SetFixedImage( fixedImage );
144 
145   // Test the GetValue method
146   //
147 
148   // The value 0.620753 was computed by hand for these two images
149   double expectedMatchMeasure = 0.620753;
150   MetricType::MeasureType value = metric->GetValue( parameters );
151   if( !itk::Math::FloatAlmostEqual( (double)value, expectedMatchMeasure, 10, epsilon ) )
152     {
153       std::cerr << "Error !" << std::endl;
154       std::cerr << "Expected: " << expectedMatchMeasure << " but got "
155         << static_cast< double >( value ) << std::endl;
156       std::cerr << "Test failed" << std::endl;
157     return EXIT_FAILURE;
158     }
159 
160 
161   // Test the ComputeGradient method
162   //
163   metric->ComputeGradient();
164 
165   GradientImageType::Pointer xGradImage = GradientImageType::New();
166   xGradImage->SetRegions( movingImageSize );
167   xGradImage->Allocate( true ); // initialize buffer to zero
168   xGradImage->Update();
169 
170   GradientImageType::Pointer yGradImage = GradientImageType::New();
171   yGradImage->SetRegions( movingImageSize );
172   yGradImage->Allocate( true ); // initialize buffer to zero
173   yGradImage->Update();
174 
175   GradientImageIteratorType xGradIt( xGradImage, xGradImage->GetBufferedRegion() );
176   GradientImageIteratorType yGradIt( yGradImage, yGradImage->GetBufferedRegion() );
177 
178   xGradIt.GoToBegin();
179   yGradIt.GoToBegin();
180 
181   // Construct the gradient images explicitly based on what we know
182   // they should be and use them to validate metric's version
183   while( !xGradIt.IsAtEnd() )
184     {
185     GradientImageType::IndexType index = xGradIt.GetIndex();
186 
187     if( ( index[0] == 54 || index[0] == 55 ) && index[1] >= 55 && index[1] <= 87 )
188       {
189       xGradIt.Set(1);
190       }
191     if( ( index[0] == 87 || index[0] == 88  ) && index[1] >= 55 && index[1] <= 87 )
192       {
193       xGradIt.Set(-1);
194       }
195     if( ( index[1] == 54 || index[1] == 55  ) && index[0] >= 55 && index[0] <= 87 )
196       {
197       yGradIt.Set(1);
198       }
199     if( ( index[1] == 87 || index[1] == 88  ) &&(index[0] >= 55) && index[0] <= 87)
200       {
201       yGradIt.Set(-1);
202       }
203 
204     ++xGradIt;
205     ++yGradIt;
206     }
207 
208   using GradIteratorType = itk::ImageRegionIteratorWithIndex< const MetricType::GradientImageType >;
209   GradIteratorType gradIt( metric->GetGradientImage(), metric->GetGradientImage()->GetBufferedRegion() );
210   gradIt.GoToBegin();
211   xGradIt.GoToBegin();
212   yGradIt.GoToBegin();
213   while( !gradIt.IsAtEnd() )
214     {
215     if( itk::Math::NotAlmostEquals( ( gradIt.Get())[0], xGradIt.Get() ) ||
216         itk::Math::NotAlmostEquals( ( gradIt.Get())[1], yGradIt.Get() ) )
217       {
218       std::cerr << "Error !" << std::endl;
219       std::cerr << "Expected: [" << static_cast< double >( gradIt.Get()[0] )
220         << ", " << static_cast< double >( gradIt.Get()[1] ) << "], but got ["
221         << static_cast< double >( xGradIt.Get() ) << ", "
222         << static_cast< double >( yGradIt.Get() ) << "]"
223         << std::endl;
224       std::cerr << "Test failed" << std::endl;
225       return EXIT_FAILURE;
226       }
227 
228     ++gradIt;
229     ++xGradIt;
230     ++yGradIt;
231     }
232 
233 
234   // Test the GetDerivative method
235   //
236   metric->GetDerivative( parameters, derivative );
237 
238   // The value 0.0477502 was computed by hand
239   double expectedDerivativeMeasure = -0.0477502;
240   for( unsigned int i = 0; i < derivative.size(); ++i )
241     {
242     if( !itk::Math::FloatAlmostEqual( (double)derivative[i], expectedDerivativeMeasure, 10, epsilon ) )
243       {
244         std::cerr << "Error !" << std::endl;
245         std::cerr << "Expected: " << expectedDerivativeMeasure << " but got "
246           << static_cast< double >( derivative[i] )
247           << " at index [" << i << "]" << std::endl;
248         std::cerr << "Test failed" << std::endl;
249       return EXIT_FAILURE;
250       }
251     }
252 
253 
254   // Test the GetValueAndDerivative method
255   //
256   metric->GetValueAndDerivative( parameters, value, derivative );
257 
258   if( !itk::Math::FloatAlmostEqual( (double)value, expectedMatchMeasure, 10, epsilon ) )
259     {
260       std::cerr << "Error !" << std::endl;
261       std::cerr << "Expected: " << expectedMatchMeasure << " but got "
262         << static_cast< double >( value ) << std::endl;
263       std::cerr << "Test failed" << std::endl;
264     return EXIT_FAILURE;
265     }
266   for( unsigned int i = 0; i < derivative.size(); ++i )
267     {
268     if( !itk::Math::FloatAlmostEqual( (double)derivative[i], expectedDerivativeMeasure, 10, epsilon ) )
269       {
270         std::cerr << "Error !" << std::endl;
271         std::cerr << "Expected: " << expectedDerivativeMeasure << " but got "
272           << static_cast< double >( derivative[i] )
273           << " at index [" << i << "]" << std::endl;
274         std::cerr << "Test failed" << std::endl;
275       return EXIT_FAILURE;
276       }
277     }
278 
279 
280   // Test with Complement set to true
281   //
282   useComplement = true;
283   metric->SetComplement( useComplement );
284   TEST_SET_GET_VALUE( useComplement, metric->GetComplement() );
285 
286   metric->ComplementOn();
287   TEST_SET_GET_VALUE( true, metric->GetComplement() );
288 
289   // The value 0.379247 was computed by hand
290   expectedMatchMeasure = 0.379247;
291   value = metric->GetValue( parameters );
292   if( !itk::Math::FloatAlmostEqual( (double)value, expectedMatchMeasure, 10, epsilon ) )
293     {
294       std::cerr << "Error !" << std::endl;
295       std::cerr << "Expected: " << expectedMatchMeasure << " but got "
296         << static_cast< double >( value ) << std::endl;
297       std::cerr << "Test failed" << std::endl;
298     return EXIT_FAILURE;
299     }
300 
301   return EXIT_SUCCESS;
302 }
303