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 "itkFEMRegistrationFilter.h"
20 #include "itkImageFileWriter.h"
21 #include "itkTestingMacros.h"
22 
23 #include <fstream>
24 
25 
26 // Typedefs used for registration
27 constexpr unsigned int ImageDimension = 3;
28 
29 using InputImagePixelType = unsigned char;
30 using DeformationFieldPixelType = float;
31 
32 using InputImageType = itk::Image< InputImagePixelType, ImageDimension >;
33 using DeformationFieldVectorType = itk::Vector< DeformationFieldPixelType, ImageDimension >;
34 using DeformationFieldImageType = itk::Image< DeformationFieldVectorType, ImageDimension >;
35 
36 using ElementType = itk::fem::Element3DC0LinearHexahedronMembrane;
37 
38 
39 // Template function to fill in an image with a value.
40 template< typename TImage >
FillImage(TImage * image,typename TImage::PixelType value)41 void FillImage( TImage * image, typename TImage::PixelType value )
42 {
43   using Iterator = itk::ImageRegionIteratorWithIndex< TImage >;
44   Iterator it( image, image->GetBufferedRegion() );
45 
46   for( it.GoToBegin(); !it.IsAtEnd(); ++it )
47     {
48     it.Set( value );
49     }
50 }
51 
52 // Template function to fill in an image with a circle.
53 template< typename TImage >
FillWithCircle(TImage * image,double * center,double radius,typename TImage::PixelType foregnd,typename TImage::PixelType backgnd)54 void FillWithCircle( TImage * image, double * center, double radius,
55   typename TImage::PixelType foregnd, typename TImage::PixelType backgnd )
56 {
57   using Iterator = itk::ImageRegionIteratorWithIndex< TImage >;
58   Iterator it( image, image->GetBufferedRegion() );
59 
60   typename TImage::IndexType index;
61   double r2 = itk::Math::sqr( radius );
62   for( it.GoToBegin(); !it.IsAtEnd(); ++it )
63     {
64     index = it.GetIndex();
65     double distance = 0;
66     for( unsigned int j = 0; j < TImage::ImageDimension; ++j )
67       {
68       distance += itk::Math::sqr( (double) index[j] - center[j] );
69       }
70     if( distance <= r2 )
71       {
72       it.Set( foregnd );
73       }
74     else
75       {
76       it.Set( backgnd );
77       }
78     }
79 }
80 
81 
itkFEMRegistrationFilterTest(int argc,char * argv[])82 int itkFEMRegistrationFilterTest( int argc, char *argv[] )
83 {
84   using IndexType = InputImageType::IndexType;
85   using SizeType = InputImageType::SizeType;
86   using RegionType = InputImageType::RegionType;
87 
88 
89   // Generate input images and initial deformation field
90 
91   InputImageType::SizeValueType sizeArray[ImageDimension];
92   for(auto & i : sizeArray)
93     {
94     i = 32;
95     }
96 
97   SizeType size;
98   size.SetSize( sizeArray );
99 
100   IndexType index;
101   index.Fill( 0 );
102 
103   RegionType region;
104   region.SetSize( size );
105   region.SetIndex( index );
106 
107   InputImageType::Pointer movingImage = InputImageType::New();
108   InputImageType::Pointer fixedImage = InputImageType::New();
109 
110   DeformationFieldImageType::Pointer initField = DeformationFieldImageType::New();
111 
112   movingImage->SetLargestPossibleRegion( region );
113   movingImage->SetBufferedRegion( region );
114   movingImage->Allocate();
115 
116   fixedImage->SetLargestPossibleRegion( region );
117   fixedImage->SetBufferedRegion( region );
118   fixedImage->Allocate();
119 
120   initField->SetLargestPossibleRegion( region );
121   initField->SetBufferedRegion( region );
122   initField->Allocate();
123 
124   double center[ImageDimension];
125   double radius;
126   InputImagePixelType fgnd = 250;
127   InputImagePixelType bgnd = 15;
128 
129   // Set the circle center
130   for(double & i : center)
131     {
132     i = 16;
133     }
134 
135 
136   // Fill fixed image with a circle
137   radius = 8;
138   FillWithCircle< InputImageType >( fixedImage, center, radius, fgnd, bgnd );
139 
140   // Fill moving image with a circle
141   radius = 5;
142   FillWithCircle< InputImageType >( movingImage, center, radius, fgnd, bgnd );
143 
144   // Fill initial deformation with zero vectors
145   DeformationFieldVectorType zeroVec;
146   zeroVec.Fill( 0.0 );
147   FillImage< DeformationFieldImageType >( initField, zeroVec );
148 
149 
150   using FEMObjectType = itk::fem::FEMObject<ImageDimension>;
151   using RegistrationType = itk::fem::FEMRegistrationFilter< InputImageType,
152                                           InputImageType,
153                                           FEMObjectType >;
154 
155   // Run registration and warp moving
156   for( unsigned int met = 0; met < 4; ++met )
157     {
158     RegistrationType::Pointer registrator = RegistrationType::New();
159 
160     EXERCISE_BASIC_OBJECT_METHODS( registrator, FEMRegistrationFilter,
161       ImageToImageFilter );
162 
163     registrator->SetFixedImage( fixedImage );
164     TEST_SET_GET_VALUE( fixedImage, registrator->GetFixedImage() );
165 
166     registrator->SetMovingImage( movingImage );
167     TEST_SET_GET_VALUE( movingImage, registrator->GetMovingImage() );
168 
169     unsigned int maxLevel = 1;
170     registrator->SetMaxLevel( maxLevel );
171     TEST_SET_GET_VALUE( maxLevel, registrator->GetMaxLevel() );
172 
173     bool useNormalizedGradient = true;
174     TEST_SET_GET_BOOLEAN( registrator, UseNormalizedGradient, useNormalizedGradient );
175 
176     registrator->ChooseMetric( met );
177 
178     unsigned int numberOfMaxIterations = 5;
179     registrator->SetMaximumIterations( numberOfMaxIterations, 0 );
180 
181     RegistrationType::Float elasticity = 10;
182     registrator->SetElasticity( elasticity, 0 );
183     TEST_SET_GET_VALUE( elasticity, registrator->GetElasticity() );
184 
185     RegistrationType::Float rho = 1;
186     registrator->SetRho( rho, 0 );
187     //TEST_SET_GET_VALUE( rho, registrator->GetRho() );
188 
189     RegistrationType::Float gamma = 1.;
190     registrator->SetGamma( gamma, 0 );
191     //TEST_SET_GET_VALUE( gamma, registrator->GetGamma() );
192 
193     RegistrationType::Float alpha = 1.;
194     registrator->SetAlpha( alpha );
195     TEST_SET_GET_VALUE( alpha, registrator->GetAlpha() );
196 
197     registrator->SetMeshPixelsPerElementAtEachResolution( 4, 0 );
198 
199     unsigned int widthOfMetricRegion;
200     if( met == 0 || met == 3 )
201       {
202       widthOfMetricRegion = 0;
203       }
204     else
205       {
206       widthOfMetricRegion = 1;
207       }
208     registrator->SetWidthOfMetricRegion( widthOfMetricRegion, 0 );
209     TEST_SET_GET_VALUE( widthOfMetricRegion,
210       registrator->GetWidthOfMetricRegion() );
211 
212     registrator->SetNumberOfIntegrationPoints(2, 0);
213 
214     RegistrationType::Float timeStep = 1.;
215     registrator->SetTimeStep( timeStep );
216     TEST_SET_GET_VALUE( timeStep, registrator->GetTimeStep() );
217 
218     unsigned int doLineSearchOnImageEnergy;
219     unsigned int employRegridding;
220     if( met == 0 )
221       {
222       doLineSearchOnImageEnergy = 2;
223       employRegridding = true;
224       }
225     else
226       {
227       doLineSearchOnImageEnergy = 0;
228       employRegridding = false;
229       }
230 
231     registrator->SetDoLineSearchOnImageEnergy( doLineSearchOnImageEnergy );
232     TEST_SET_GET_VALUE( doLineSearchOnImageEnergy,
233       registrator->GetDoLineSearchOnImageEnergy() );
234 
235     registrator->SetEmployRegridding( employRegridding );
236     TEST_SET_GET_VALUE( employRegridding, registrator->GetEmployRegridding() );
237 
238     bool useLandmarks = false;
239     TEST_SET_GET_BOOLEAN( registrator, UseLandmarks, useLandmarks );
240 
241     bool useMassMatrix = true;
242     TEST_SET_GET_BOOLEAN( registrator, UseMassMatrix, useMassMatrix );
243 
244     RegistrationType::Float energyReductionFactor = 0.0;
245     registrator->SetEnergyReductionFactor( energyReductionFactor );
246     TEST_SET_GET_VALUE( energyReductionFactor,
247       registrator->GetEnergyReductionFactor() );
248 
249     unsigned int lineSearchMaximumIterations = 100;
250     registrator->SetLineSearchMaximumIterations( lineSearchMaximumIterations );
251     TEST_SET_GET_VALUE( lineSearchMaximumIterations,
252       registrator->GetLineSearchMaximumIterations() );
253 
254     bool createMeshFromImage = true;
255     TEST_SET_GET_BOOLEAN( registrator, CreateMeshFromImage, createMeshFromImage );
256 
257     double standardDeviation = 0.5;
258     registrator->SetStandardDeviations( standardDeviation );
259     //TEST_SET_GET_VALUE( standardDeviations, registrator->GetStandardDeviations() );
260 
261     standardDeviation = 1.0;
262     RegistrationType::StandardDeviationsType standardDeviations;
263     standardDeviations.Fill( standardDeviation );
264     registrator->SetStandardDeviations( standardDeviations );
265     //TEST_SET_GET_VALUE( standardDeviations, registrator->GetStandardDeviations() );
266 
267     unsigned int maximumKernelWidth = 30;
268     registrator->SetMaximumKernelWidth( maximumKernelWidth );
269     TEST_SET_GET_VALUE( maximumKernelWidth, registrator->GetMaximumKernelWidth() );
270 
271     double maximumError = 0.1;
272     registrator->SetMaximumError( maximumError );
273     TEST_SET_GET_VALUE( maximumError, registrator->GetMaximumError() );
274 
275 
276     itk::fem::MaterialLinearElasticity::Pointer material =
277       itk::fem::MaterialLinearElasticity::New();
278     material->SetGlobalNumber( 0 );
279     material->SetYoungsModulus( registrator->GetElasticity() );
280     material->SetCrossSectionalArea( 1.0 );
281     material->SetThickness( 1.0 );
282     material->SetMomentOfInertia( 1.0 );
283     material->SetPoissonsRatio( 0. ); // DON'T CHOOSE 1.0!!
284     material->SetDensityHeatProduct( 1.0 );
285 
286     // Create the element type
287     ElementType::Pointer element1 = ElementType::New();
288     element1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*material ) );
289     registrator->SetElement( &*element1 );
290     registrator->SetMaterial( material );
291 
292     // Register the images
293 
294     try
295       {
296       // Register the images
297       registrator->RunRegistration();
298       }
299     catch( ::itk::ExceptionObject & err )
300       {
301         std::cerr << "ITK exception detected: "  << err;
302         std::cout << "Test FAILED" << std::endl;
303         return EXIT_FAILURE;
304       }
305     catch( ... )
306       {
307       // fixme - changes to femparray cause it to fail : old version works
308       std::cout << "Caught an exception: " << std::endl;
309       return EXIT_FAILURE;
310       //    std::cout << err << std::endl;
311       // throw err;
312       }
313 
314     if( argc == 2 )
315       {
316       std::string outFileName = argv[1];
317       std::stringstream ss;
318       ss << met;
319       outFileName += ss.str();
320       outFileName += ".mhd";
321       using ImageWriterType = itk::ImageFileWriter< RegistrationType::FieldType >;
322       ImageWriterType::Pointer writer = ImageWriterType::New();
323       writer->SetFileName( outFileName );
324       writer->SetInput( registrator->GetDisplacementField() );
325       writer->Update();
326       }
327 
328     if( argc == 3 )
329       {
330       std::string outFileName = argv[2];
331       std::stringstream ss;
332       ss << met;
333       outFileName += ss.str();
334       outFileName += ".mhd";
335       using ImageWriterType = itk::ImageFileWriter< InputImageType >;
336       ImageWriterType::Pointer writer = ImageWriterType::New();
337       writer->SetFileName( outFileName );
338       writer->SetInput( registrator->GetWarpedImage() );
339       writer->Update();
340       }
341     }
342 
343   /*
344   // get warped reference image
345   // ---------------------------------------------------------
346   std::cout << "Compare warped moving and fixed." << std::endl;
347 
348   // compare the warp and fixed images
349   itk::ImageRegionIterator<ImageType> fixedIter( fixed,
350       fixed->GetBufferedRegion() );
351   itk::ImageRegionIterator<ImageType> warpedIter( registrator->GetWarpedImage(),
352       fixed->GetBufferedRegion() );
353 
354   unsigned int numPixelsDifferent = 0;
355   while( !fixedIter.IsAtEnd() )
356     {
357     if( fixedIter.Get() != warpedIter.Get() )
358       {
359       numPixelsDifferent++;
360       }
361     ++fixedIter;
362     ++warpedIter;
363     }
364 
365   std::cout << "Number of pixels different: " << numPixelsDifferent;
366   std::cout << std::endl;
367 
368   if( numPixelsDifferent > 400 )
369     {
370     std::cout << "Test failed - too many pixels different." << std::endl;
371     return EXIT_FAILURE;
372     }
373 
374   std::cout << "Test passed" << std::endl;
375   */
376 
377   std::cout << "Test finished." << std::endl;
378   return EXIT_SUCCESS;
379 }
380