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