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 "itkSliceBySliceImageFilter.h"
20 #include "itkImageFileReader.h"
21 #include "itkImageFileWriter.h"
22 #include "itkSimpleFilterWatcher.h"
23 #include "itkPipelineMonitorImageFilter.h"
24 #include "itkTestingMacros.h"
25 #include "itkMedianImageFilter.h"
26 
27 
sliceCallBack(itk::Object * object,const itk::EventObject &,void *)28 void sliceCallBack(itk::Object* object, const itk::EventObject &, void*)
29 {
30   // the same type alias than in the main function - should be done in a nicer way
31   constexpr int Dimension = 3;
32   using PixelType = unsigned char;
33 
34   using ImageType = itk::Image< PixelType, Dimension >;
35   using FilterType = itk::SliceBySliceImageFilter< ImageType, ImageType >;
36   using MedianType = itk::MedianImageFilter< FilterType::InternalInputImageType,
37     FilterType::InternalOutputImageType >;
38 
39   // real stuff begins here
40   // get the slice by slice filter and the median filter
41   auto * filter = dynamic_cast< FilterType * >( object );
42   auto * median = dynamic_cast< MedianType * >( filter->GetModifiableInputFilter() );
43 
44   // std::cout << "callback! slice: " << filter->GetSliceIndex() << std::endl;
45 
46   // set half of the slice number as radius
47   MedianType::InputSizeType radius;
48   radius.Fill( filter->GetSliceIndex() / 2 );
49   median->SetRadius( radius );
50 }
51 
itkSliceBySliceImageFilterTest(int argc,char * argv[])52 int itkSliceBySliceImageFilterTest(int argc, char * argv[])
53 {
54 
55   if( argc != 4 )
56     {
57     std::cerr << "usage: " << itkNameOfTestExecutableMacro(argv) << " input output slicingDimension" << std::endl;
58     return EXIT_FAILURE;
59     }
60 
61   constexpr int Dimension = 3;
62   using PixelType = unsigned char;
63 
64   using ImageType = itk::Image< PixelType, Dimension >;
65 
66   using ReaderType = itk::ImageFileReader< ImageType >;
67 
68   ReaderType::Pointer reader = ReaderType::New();
69   reader->SetFileName( argv[1] );
70 
71   using FilterType = itk::SliceBySliceImageFilter< ImageType, ImageType >;
72 
73   FilterType::Pointer filter = FilterType::New();
74   filter->DebugOn();
75 
76   filter->SetInput( reader->GetOutput() );
77 
78   using MedianType = itk::MedianImageFilter< FilterType::InternalInputImageType,
79                                   FilterType::InternalOutputImageType >;
80 
81   MedianType::Pointer median = MedianType::New();
82   filter->SetFilter( median );
83 
84   using MonitorType = itk::PipelineMonitorImageFilter<FilterType::InternalOutputImageType>;
85   MonitorType::Pointer monitor = MonitorType::New();
86 
87   itk::CStyleCommand::Pointer command = itk::CStyleCommand::New();
88   command->SetCallback( *sliceCallBack );
89 
90   filter->AddObserver( itk::IterationEvent(), command );
91 
92   itk::SimpleFilterWatcher watcher(filter, "filter");
93 
94   using WriterType = itk::ImageFileWriter< ImageType >;
95 
96   WriterType::Pointer writer = WriterType::New();
97   writer->SetInput( filter->GetOutput() );
98   writer->SetFileName( argv[2] );
99 
100   unsigned int slicingDimension;
101   std::istringstream istrm( argv[3] );
102   istrm >> slicingDimension;
103   filter->SetDimension( slicingDimension );
104   std::cout << "Slicing dimension: " << slicingDimension << std::endl;
105   std::cout << "Slicing dimension: " << filter->GetDimension() << std::endl;
106 
107   try
108     {
109     writer->Update();
110     }
111   catch( itk::ExceptionObject & excp )
112     {
113     std::cerr << excp << std::endl;
114     return EXIT_FAILURE;
115     }
116 
117 
118   // set up a requested region of just one pixel and verify that was
119   // all that was produced.
120   std::cout << "Testing with requested region..." << std::endl;
121   ImageType::Pointer temp = filter->GetOutput();
122   temp->DisconnectPipeline();
123   temp = nullptr;
124 
125   ImageType::RegionType rr = reader->GetOutput()->GetLargestPossibleRegion();
126   for (unsigned int i = 0; i < ImageType::ImageDimension; ++i)
127     {
128     rr.SetIndex(i, rr.GetIndex(i)+rr.GetSize(i)/2);
129     rr.SetSize(i,1);
130     }
131 
132 
133   monitor->SetInput(median->GetOutput());
134   filter->SetOutputFilter(monitor);
135   filter->GetOutput()->SetRequestedRegion(rr);
136 
137 
138   try
139     {
140     filter->Update();
141     }
142   catch( itk::ExceptionObject & excp )
143     {
144     std::cerr << excp << std::endl;
145     return EXIT_FAILURE;
146     }
147 
148   // check that one slice executed is just one pixel and the input
149   // filter just update that region
150   TEST_EXPECT_EQUAL( monitor->GetNumberOfUpdates(), 1 );
151   TEST_EXPECT_EQUAL( monitor->GetOutputRequestedRegions()[0].GetNumberOfPixels(), 1 );
152   TEST_EXPECT_TRUE( monitor->VerifyAllInputCanStream(1) );
153 
154   //
155   // Test that a sliced version of the input image information is passed
156   // through to the internal filters, with proper origin and
157   // spacing. We are setting the input image to have a non-zero
158   // starting index.
159   //
160   ImageType::Pointer image = ImageType::New();
161   {
162   ImageType::RegionType region = reader->GetOutput()->GetLargestPossibleRegion();
163   region.SetIndex(0,10);
164   image->SetRegions(region);
165   image->Allocate(true);
166   }
167 
168   ImageType::SpacingType spacing;
169   ImageType::PointType origin;
170   for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i )
171     {
172     spacing[i] = i + 0.1;
173     origin[i] = i + 0.2;
174     }
175   image->SetSpacing(spacing);
176   image->SetOrigin(origin);
177 
178   filter->SetInput(image);
179   filter->Update();
180 
181   FilterType::InternalInputImageType::SpacingType expectedInternalSpacing;
182   FilterType::InternalInputImageType::PointType expectedInternalOrigin;
183   for ( unsigned int i = 0, internal_i = 0; internal_i < FilterType::InternalImageDimension; ++i, ++internal_i )
184     {
185     if ( i == slicingDimension )
186       {
187       ++i;
188       }
189 
190     expectedInternalSpacing[internal_i] = spacing[i];
191     expectedInternalOrigin[internal_i] = origin[i];
192     }
193   TEST_EXPECT_EQUAL( monitor->GetUpdatedOutputSpacing(), expectedInternalSpacing );
194   TEST_EXPECT_EQUAL( monitor->GetUpdatedOutputOrigin(), expectedInternalOrigin );
195 
196   //
197   // Exercise PrintSelf()
198   //
199   filter->Print( std::cout );
200 
201   //
202   // Exercise exceptions
203   //
204   bool caughtException;
205   FilterType::Pointer badFilter = FilterType::New();
206 
207   std::cout << "Testing with no filter set..." << std::endl;
208   badFilter->SetInput( reader->GetOutput() );
209   caughtException = false;
210   try
211     {
212     badFilter->Update();
213     }
214   catch( itk::ExceptionObject & excp )
215     {
216     std::cout << "Caught expected exception" << std::endl;
217     std::cout << excp << std::endl;
218     caughtException = true;
219     }
220   if (!caughtException)
221     {
222     return EXIT_FAILURE;
223     }
224 
225   std::cout << "Testing with no output filter set..." << std::endl;
226   badFilter->SetInput( reader->GetOutput() );
227   badFilter->SetInputFilter( median );
228   caughtException = false;
229   try
230     {
231     badFilter->Update();
232     }
233   catch( itk::ExceptionObject & excp )
234     {
235     std::cout << "Caught expected exception" << std::endl;
236     std::cout << excp << std::endl;
237     caughtException = true;
238     }
239   if (!caughtException)
240     {
241     return EXIT_FAILURE;
242     }
243 
244   // check nullptr input/output
245   TRY_EXPECT_EXCEPTION(badFilter->SetInputFilter(nullptr));
246   TRY_EXPECT_EXCEPTION(badFilter->SetOutputFilter(nullptr));
247 
248   return EXIT_SUCCESS;
249 }
250