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