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