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