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 #ifndef itkDiscreteGaussianDerivativeImageFilter_hxx
19 #define itkDiscreteGaussianDerivativeImageFilter_hxx
20
21 #include "itkDiscreteGaussianDerivativeImageFilter.h"
22 #include "itkNeighborhoodOperatorImageFilter.h"
23 #include "itkGaussianDerivativeOperator.h"
24 #include "itkImageRegionIterator.h"
25 #include "itkProgressAccumulator.h"
26 #include "itkStreamingImageFilter.h"
27
28 namespace itk
29 {
30 template< typename TInputImage, typename TOutputImage >
31 void
32 DiscreteGaussianDerivativeImageFilter< TInputImage, TOutputImage >
GenerateInputRequestedRegion()33 ::GenerateInputRequestedRegion()
34 {
35 // call the superclass' implementation of this method. this should
36 // copy the output requested region to the input requested region
37 Superclass::GenerateInputRequestedRegion();
38
39 // get pointers to the input and output
40 typename Superclass::InputImagePointer inputPtr =
41 const_cast< TInputImage * >( this->GetInput() );
42
43 if ( !inputPtr )
44 {
45 return;
46 }
47
48 // Build an operator so that we can determine the kernel size
49 GaussianDerivativeOperator< OutputPixelType, ImageDimension > oper;
50 typename TInputImage::SizeType radius;
51
52 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
53 {
54 // Determine the size of the operator in this dimension. Note that the
55 // Gaussian is built as a 1D operator in each of the specified directions.
56 oper.SetDirection(i);
57 if ( m_UseImageSpacing == true )
58 {
59 oper.SetSpacing(this->GetInput()->GetSpacing()[i]);
60 }
61
62 // GaussianDerivativeOperator modifies the variance when setting image
63 // spacing
64 oper.SetVariance(m_Variance[i]);
65 oper.SetMaximumError(m_MaximumError[i]);
66 oper.SetMaximumKernelWidth(m_MaximumKernelWidth);
67 oper.CreateDirectional();
68
69 radius[i] = oper.GetRadius(i);
70 }
71
72 // get a copy of the input requested region (should equal the output
73 // requested region)
74 typename TInputImage::RegionType inputRequestedRegion;
75 inputRequestedRegion = inputPtr->GetRequestedRegion();
76
77 // pad the input requested region by the operator radius
78 inputRequestedRegion.PadByRadius(radius);
79
80 // crop the input requested region at the input's largest possible region
81 if ( inputRequestedRegion.Crop( inputPtr->GetLargestPossibleRegion() ) )
82 {
83 inputPtr->SetRequestedRegion(inputRequestedRegion);
84 return;
85 }
86 else
87 {
88 // Couldn't crop the region (requested region is outside the largest
89 // possible region). Throw an exception.
90
91 // store what we tried to request (prior to trying to crop)
92 inputPtr->SetRequestedRegion(inputRequestedRegion);
93
94 // build an exception
95 InvalidRequestedRegionError e(__FILE__, __LINE__);
96 e.SetLocation(ITK_LOCATION);
97 e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
98 e.SetDataObject(inputPtr);
99 throw e;
100 }
101 }
102
103 template< typename TInputImage, typename TOutputImage >
104 void
105 DiscreteGaussianDerivativeImageFilter< TInputImage, TOutputImage >
GenerateData()106 ::GenerateData()
107 {
108 typename TOutputImage::Pointer output = this->GetOutput();
109
110 output->SetBufferedRegion( output->GetRequestedRegion() );
111 output->Allocate();
112
113 // Create an internal image to protect the input image's metdata
114 // (e.g. RequestedRegion). The StreamingImageFilter changes the
115 // requested region as poart of its normal provessing.
116 typename TInputImage::Pointer localInput = TInputImage::New();
117 localInput->Graft( this->GetInput() );
118
119 // Type of the pixel to use for intermediate results
120 using RealOutputPixelType = typename NumericTraits< OutputPixelType >::RealType;
121 using RealOutputImageType = Image< OutputPixelType, ImageDimension >;
122
123 // Type definition for the internal neighborhood filter
124 //
125 // First filter convolves and changes type from input type to real type
126 // Middle filters convolves from real to real
127 // Last filter convolves and changes type from real type to output type
128 // Streaming filter forces the mini-pipeline to run in chunks
129 using FirstFilterType = NeighborhoodOperatorImageFilter< InputImageType,
130 RealOutputImageType, RealOutputPixelType >;
131 using IntermediateFilterType = NeighborhoodOperatorImageFilter< RealOutputImageType,
132 RealOutputImageType, RealOutputPixelType >;
133 using LastFilterType = NeighborhoodOperatorImageFilter< RealOutputImageType,
134 OutputImageType, RealOutputPixelType >;
135 using SingleFilterType = NeighborhoodOperatorImageFilter< InputImageType,
136 OutputImageType, RealOutputPixelType >;
137 using StreamingFilterType =
138 StreamingImageFilter< OutputImageType, OutputImageType >;
139
140 using FirstFilterPointer = typename FirstFilterType::Pointer;
141 using IntermediateFilterPointer = typename IntermediateFilterType::Pointer;
142 using LastFilterPointer = typename LastFilterType::Pointer;
143 using SingleFilterPointer = typename SingleFilterType::Pointer;
144 using StreamingFilterPointer = typename StreamingFilterType::Pointer;
145
146 // Create a series of operators
147 using OperatorType = GaussianDerivativeOperator< RealOutputPixelType, ImageDimension >;
148 std::vector< OperatorType > oper;
149 oper.resize(ImageDimension);
150
151 // Create a process accumulator for tracking the progress of minipipeline
152 ProgressAccumulator::Pointer progress = ProgressAccumulator::New();
153 progress->SetMiniPipelineFilter(this);
154
155 // Set up the operators
156 for ( unsigned int i = 0; i < ImageDimension; ++i )
157 {
158 // we reverse the direction to minimize computation while, because
159 // the largest dimension will be split slice wise for streaming.
160 //
161 // This is to say oper[0] = Z, oper[1] = Y, oper[2] = X for the
162 // 3D case.
163 const unsigned int reverse_i = ImageDimension - i - 1;
164
165 // Set up the operator for this dimension
166 oper[reverse_i].SetDirection(i);
167 oper[reverse_i].SetOrder(m_Order[i]);
168 if ( m_UseImageSpacing == true )
169 {
170 // convert the variance from physical units to pixels
171 double s = localInput->GetSpacing()[i];
172 s = s * s;
173 oper[reverse_i].SetVariance(m_Variance[i] / s);
174 }
175 else
176 {
177 oper[reverse_i].SetVariance(m_Variance[i]);
178 }
179
180 oper[reverse_i].SetMaximumKernelWidth(m_MaximumKernelWidth);
181 oper[reverse_i].SetMaximumError(m_MaximumError[i]);
182 oper[reverse_i].SetNormalizeAcrossScale(m_NormalizeAcrossScale);
183 oper[reverse_i].CreateDirectional();
184 }
185
186 // Create a chain of filters
187 if ( ImageDimension == 1 )
188 {
189 // Use just a single filter
190 SingleFilterPointer singleFilter = SingleFilterType::New();
191 singleFilter->SetOperator(oper[0]);
192 singleFilter->SetInput(localInput);
193 progress->RegisterInternalFilter(singleFilter, 1.0f / ImageDimension);
194
195 // Graft this filters output onto the mini-pipeline so the mini-pipeline
196 // has the correct region ivars and will write to this filters bulk data
197 // output.
198 singleFilter->GraftOutput(output);
199
200 // Update the filter
201 singleFilter->Update();
202
203 // Graft the last output of the mini-pipeline onto this filters output so
204 // the final output has the correct region ivars and a handle to the final
205 // bulk data
206 this->GraftOutput(output);
207 }
208 else
209 {
210 // Setup a full mini-pipeline and stream the data through the
211 // pipeline.
212 unsigned int numberOfStages = ImageDimension * this->GetInternalNumberOfStreamDivisions() + 1;
213
214 // First filter convolves and changes type from input type to real type
215 FirstFilterPointer firstFilter = FirstFilterType::New();
216 firstFilter->SetOperator(oper[0]);
217 firstFilter->ReleaseDataFlagOn();
218 firstFilter->SetInput(localInput);
219 progress->RegisterInternalFilter(firstFilter, 1.0f / numberOfStages);
220
221 // Middle filters convolves from real to real
222 std::vector< IntermediateFilterPointer > intermediateFilters;
223 if ( ImageDimension > 2 )
224 {
225 const unsigned int max_dim = ImageDimension - 1;
226 for ( unsigned int i = 1; i != max_dim; ++i )
227 {
228 IntermediateFilterPointer f = IntermediateFilterType::New();
229 f->SetOperator(oper[i]);
230 f->ReleaseDataFlagOn();
231 progress->RegisterInternalFilter(f, 1.0f / numberOfStages);
232
233 if ( i == 1 )
234 {
235 f->SetInput( firstFilter->GetOutput() );
236 }
237 else
238 {
239 // note: first filter in vector (zeroth element) is for i==1
240 f->SetInput( intermediateFilters[i - 2]->GetOutput() );
241 }
242 intermediateFilters.push_back(f);
243 }
244 }
245
246 // Last filter convolves and changes type from real type to output type
247 LastFilterPointer lastFilter = LastFilterType::New();
248 lastFilter->SetOperator(oper[ImageDimension - 1]);
249 lastFilter->ReleaseDataFlagOn();
250 if ( ImageDimension > 2 )
251 {
252 const unsigned int temp_dim = ImageDimension - 3;
253 lastFilter->SetInput( intermediateFilters[temp_dim]->GetOutput() );
254 }
255 else
256 {
257 lastFilter->SetInput( firstFilter->GetOutput() );
258 }
259 progress->RegisterInternalFilter(lastFilter, 1.0f / numberOfStages);
260
261 // Put in a StreamingImageFilter so the mini-pipeline is processed
262 // in chunks to minimize memory usage
263 StreamingFilterPointer streamingFilter = StreamingFilterType::New();
264 streamingFilter->SetInput( lastFilter->GetOutput() );
265 streamingFilter->SetNumberOfStreamDivisions( this->GetInternalNumberOfStreamDivisions() );
266 progress->RegisterInternalFilter(streamingFilter, 1.0f / numberOfStages);
267
268 // Graft this filters output onto the mini-pipeline so the mini-pipeline
269 // has the correct region ivars and will write to this filters bulk data
270 // output.
271 streamingFilter->GraftOutput(output);
272
273 // Update the last filter in the chain
274 streamingFilter->Update();
275
276 // Graft the last output of the mini-pipeline onto this filters output so
277 // the final output has the correct region ivars and a handle to the final
278 // bulk data
279 this->GraftOutput(output);
280 }
281 }
282
283 template< typename TInputImage, typename TOutputImage >
284 void
285 DiscreteGaussianDerivativeImageFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const286 ::PrintSelf(std::ostream & os, Indent indent) const
287 {
288 Superclass::PrintSelf(os, indent);
289
290 os << indent << "Order: " << m_Order << std::endl;
291 os << indent << "Variance: " << m_Variance << std::endl;
292 os << indent << "MaximumError: " << m_MaximumError << std::endl;
293 os << indent << "MaximumKernelWidth: " << m_MaximumKernelWidth << std::endl;
294 os << indent << "UseImageSpacing: " << m_UseImageSpacing << std::endl;
295 os << indent << "InternalNumberOfStreamDivisions: " << m_InternalNumberOfStreamDivisions << std::endl;
296 os << indent << "NormalizeAcrossScale: " << m_NormalizeAcrossScale << std::endl;
297 }
298
299 } // end namespace itk
300
301 #endif
302