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 "itkRecursiveMultiResolutionPyramidImageFilter.h"
19 
20 #include <iostream>
21 #include "itkMath.h"
22 #include "itkStreamingImageFilter.h"
23 
24 namespace
25 {
26 
27 /**
28  * This function defines the test image pattern.
29  * The pattern is a 3D gaussian in the middle
30  * and some directional pattern on the outside.
31  */
F(double x,double y,double z)32 double F( double x, double y, double z )
33 {
34   constexpr double s = 50;
35   double value = 200.0 * std::exp( - ( x*x + y*y + z*z )/(s*s) );
36   x -= 8; y += 3; z += 0;
37   double r = std::sqrt( x*x + y*y + z*z );
38   if( r > 35 )
39     {
40     value = 2 * ( itk::Math::abs( x ) +
41       0.8 * itk::Math::abs( y ) +
42       0.5 * itk::Math::abs( z ) );
43     }
44   if( r < 4 )
45     {
46     value = 400;
47     }
48 
49   return value;
50 
51 }
52 
53 
54 // The following three classes are used to support callbacks
55 // on the filter in the pipeline that follows later
56 class ShowProgressObject
57 {
58 public:
ShowProgressObject(itk::ProcessObject * o)59   ShowProgressObject(itk::ProcessObject* o)
60     {m_Process = o;}
ShowProgress()61   void ShowProgress()
62     {std::cout << "Progress " << m_Process->GetProgress() << std::endl;}
63   itk::ProcessObject::Pointer m_Process;
64 };
65 }
66 
itkRecursiveMultiResolutionPyramidImageFilterTest(int argc,char * argv[])67 int itkRecursiveMultiResolutionPyramidImageFilterTest(int argc, char* argv[] )
68 {
69 
70 //------------------------------------------------------------
71 // Create a simple image
72 //------------------------------------------------------------
73 
74   // Allocate Images
75   using PixelType = signed short;
76   using InputImageType = itk::Image<PixelType,3>;
77   using OutputImageType = itk::Image<float,3>;
78   enum { ImageDimension = InputImageType::ImageDimension };
79   bool useShrinkFilter(false);
80   if(argc > 1)
81     {
82     std::string s(argv[1]);
83     if(s == "Shrink")
84       {
85       useShrinkFilter = true;
86       std::cout << "true";
87       }
88     else
89       {
90       std::cout << "false";
91       }
92     std::cout << std::endl;
93     }
94 
95   InputImageType::SizeType size = {{100,100,40}};
96   InputImageType::IndexType index = {{0,0,0}};
97   InputImageType::RegionType region;
98   region.SetSize( size );
99   region.SetIndex( index );
100 
101   InputImageType::Pointer imgTarget = InputImageType::New();
102   imgTarget->SetLargestPossibleRegion( region );
103   imgTarget->SetBufferedRegion( region );
104   imgTarget->SetRequestedRegion( region );
105   imgTarget->Allocate();
106 
107   // Fill images with a 3D gaussian with some directional pattern
108   // in the background
109   using Iterator = itk::ImageRegionIterator<InputImageType>;
110 
111   itk::Point<double,3> center;
112   center[0] = (double)region.GetSize()[0]/2.0;
113   center[1] = (double)region.GetSize()[1]/2.0;
114   center[2] = (double)region.GetSize()[2]/2.0;
115 
116   itk::Point<double,3>  p;
117   itk::Vector<double,3> d;
118 
119   Iterator ti(imgTarget,region);
120 
121 
122   while(!ti.IsAtEnd())
123   {
124     p[0] = ti.GetIndex()[0];
125     p[1] = ti.GetIndex()[1];
126     p[2] = ti.GetIndex()[2];
127     d = p-center;
128     const double x = d[0];
129     const double y = d[1];
130     const double z = d[2];
131     ti.Set( (PixelType) F(x,y,z) );
132     ++ti;
133   }
134 
135   // set image origin to be center of the image
136   double transCenter[3];
137   for( unsigned int j = 0; j < 3; j++ )
138     {
139     transCenter[j] = -0.5 * double(size[j]);
140     }
141 
142   imgTarget->SetOrigin( transCenter );
143 
144 
145  /**
146   * Setup a multi-resolution pyramid
147   */
148   using PyramidType =
149       itk::RecursiveMultiResolutionPyramidImageFilter<InputImageType,OutputImageType>;
150   using ScheduleType = PyramidType::ScheduleType;
151   PyramidType::Pointer pyramid = PyramidType::New();
152   pyramid->SetUseShrinkImageFilter(useShrinkFilter);
153 
154   pyramid->SetInput( imgTarget );
155 
156   bool pass = true;
157   unsigned int numLevels;
158   itk::Vector<unsigned int,ImageDimension> factors;
159 
160   // set schedule by specifying the number of levels;
161   numLevels = 3;
162   factors.Fill( 1 << (numLevels - 1) );
163   pyramid->SetNumberOfLevels( numLevels );
164 
165   // check the schedule
166   ScheduleType schedule( numLevels, ImageDimension );
167   unsigned int j, k;
168 
169   for( k = 0; k < numLevels; k++ )
170     {
171     unsigned int denominator = 1 << k;
172     for( j = 0; j < ImageDimension; j++ )
173       {
174       schedule[k][j] = factors[j] / denominator;
175       if( schedule[k][j] == 0 )
176         {
177         schedule[k][j] = 1;
178         }
179       }
180     }
181 
182   if( schedule != pyramid->GetSchedule() )
183     {
184     pass = false;
185     std::cout << "Schedule should be: " << std::endl;
186     std::cout << schedule << std::endl;
187     std::cout << "instead of: " << std::endl;
188     std::cout << pyramid->GetSchedule();
189     }
190 
191   // set schedule by specifying the starting shrink factors
192   numLevels = 4;
193   factors[0] = 8; factors[1] = 4; factors[2] = 2;
194   pyramid->SetNumberOfLevels( numLevels );
195   pyramid->SetStartingShrinkFactors( factors.Begin() );
196 
197   // check the schedule;
198   schedule = ScheduleType( numLevels, ImageDimension );
199   for( k = 0; k < numLevels; k++ )
200     {
201     unsigned int denominator = 1 << k;
202     for( j = 0; j < ImageDimension; j++ )
203       {
204       schedule[k][j] = factors[j] / denominator;
205       if( schedule[k][j] == 0 )
206         {
207         schedule[k][j] = 1;
208         }
209       }
210     }
211 
212   if( schedule != pyramid->GetSchedule() )
213     {
214     pass = false;
215     std::cout << "Schedule should be: " << std::endl;
216     std::cout << schedule << std::endl;
217     std::cout << "instead of: " << std::endl;
218     std::cout << pyramid->GetSchedule();
219     }
220 
221   // test start factors
222   const unsigned int * ss = pyramid->GetStartingShrinkFactors();
223   for( j = 0; j < ImageDimension; j++ )
224     {
225     if( ss[j] != factors[j] )
226       {
227       pass = false;
228       std::cout << "Returned starting factors incorrect" << std::endl;
229       break;
230       }
231     }
232 
233   // test divisibility
234   if( !PyramidType::IsScheduleDownwardDivisible( pyramid->GetSchedule() ) )
235     {
236     pass = false;
237     std::cout << "Schedule should be downward divisible" << std::endl;
238     }
239 
240   // generate output at a level with progress
241   std::cout << "Run RecursiveMultiResolutionPyramidImageFilter in standalone mode with progress";
242   std::cout << std::endl;
243 
244   ShowProgressObject progressWatch(pyramid);
245   itk::SimpleMemberCommand<ShowProgressObject>::Pointer command;
246   command = itk::SimpleMemberCommand<ShowProgressObject>::New();
247   command->SetCallbackFunction(&progressWatch,
248                                &ShowProgressObject::ShowProgress);
249   pyramid->AddObserver(itk::ProgressEvent(), command);
250 
251   pyramid->Print( std::cout );
252 
253 //  update pyramid at a particular level
254   unsigned int testLevel = 2;
255   pyramid->GetOutput( testLevel )->Update();
256 
257 
258 // test output at another level
259   testLevel = 2;
260 
261   // check the output image information
262   InputImageType::SizeType inputSize =
263     pyramid->GetInput()->GetLargestPossibleRegion().GetSize();
264   const InputImageType::SpacingType& inputSpacing =
265     pyramid->GetInput()->GetSpacing();
266 
267   OutputImageType::SizeType outputSize =
268     pyramid->GetOutput( testLevel )->GetLargestPossibleRegion().GetSize();
269   const OutputImageType::SpacingType& outputSpacing =
270     pyramid->GetOutput( testLevel )->GetSpacing();
271 
272   for( j = 0; j < ImageDimension; j++ )
273     {
274     if( itk::Math::NotAlmostEquals( outputSpacing[j],
275       inputSpacing[j] * (double) schedule[testLevel][j] ) )
276       {
277       break;
278       }
279     unsigned int sz = inputSize[j] / schedule[testLevel][j];
280     if( sz == 0 ) sz = 1;
281     if( outputSize[j] != sz )
282       {
283       break;
284       }
285     }
286 
287   if( j != ImageDimension )
288     {
289     pass = false;
290     pyramid->GetInput()->Print(std::cout);
291     pyramid->GetOutput( testLevel )->Print(std::cout);
292     }
293 
294 
295   // run in streamed mode
296   std::cout << "Run ImagePyramid with streamer";
297   std::cout << std::endl;
298 
299   using CasterType = itk::CastImageFilter<InputImageType,InputImageType>;
300   CasterType::Pointer caster = CasterType::New();
301 
302   caster->SetInput( pyramid->GetInput() );
303 
304   PyramidType::Pointer pyramid2 = PyramidType::New();
305   pyramid2->SetInput( caster->GetOutput() );
306   pyramid2->SetUseShrinkImageFilter(useShrinkFilter);
307   pyramid2->SetNumberOfLevels( pyramid->GetNumberOfLevels() );
308   pyramid2->SetSchedule( pyramid->GetSchedule() );
309 
310   using StreamerType =
311       itk::StreamingImageFilter<OutputImageType,OutputImageType>;
312   StreamerType::Pointer streamer = StreamerType::New();
313   streamer->SetInput( pyramid2->GetOutput( testLevel ) );
314   streamer->Update();
315 
316   std::cout << "Compare standalone and streamed outputs" << std::endl;
317   using OutputIterator = itk::ImageRegionIterator<OutputImageType>;
318   OutputIterator iter1( pyramid->GetOutput( testLevel ),
319     pyramid->GetOutput( testLevel )->GetBufferedRegion() );
320   OutputIterator iter2( streamer->GetOutput(),
321     streamer->GetOutput()->GetBufferedRegion() );
322 
323   while( !iter1.IsAtEnd() )
324     {
325     if( !itk::Math::FloatAlmostEqual(iter1.Get(), iter2.Get(),2) )
326       {
327       std::cout << "Expected: " << iter1.Get() << " but got: " << iter2.Get() << std::endl;
328       std::cout << "\t@: " << iter1.GetIndex() << std::endl;
329       std::cout << "Streamed output is different!!!" << std::endl;
330       pass = false;
331       //break;
332       }
333     ++iter1;
334     ++iter2;
335     }
336 
337   if( !pass )
338     {
339     std::cout << "Test failed." << std::endl;
340     return EXIT_FAILURE;
341     }
342 
343   // Set schedule to all ones and update
344   schedule = pyramid->GetSchedule();
345   schedule.Fill( 1 );
346   pyramid->SetSchedule( schedule );
347   pyramid->Update();
348 
349   std::cout << "Test passed." << std::endl;
350   return EXIT_SUCCESS;
351 
352 }
353