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 itkStreamingImageFilter_hxx
19 #define itkStreamingImageFilter_hxx
20 #include "itkStreamingImageFilter.h"
21 #include "itkCommand.h"
22 #include "itkImageAlgorithm.h"
23 #include "itkImageRegionSplitterSlowDimension.h"
24 
25 namespace itk
26 {
27 /**
28  *
29  */
30 template< typename TInputImage, typename TOutputImage >
31 StreamingImageFilter< TInputImage, TOutputImage >
StreamingImageFilter()32 ::StreamingImageFilter()
33 {
34   // default to 10 divisions
35   m_NumberOfStreamDivisions = 10;
36 
37   // create default region splitter
38   m_RegionSplitter = ImageRegionSplitterSlowDimension::New();
39 }
40 
41 /**
42  *
43  */
44 template< typename TInputImage, typename TOutputImage >
45 void
46 StreamingImageFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const47 ::PrintSelf(std::ostream & os, Indent indent) const
48 {
49   Superclass::PrintSelf(os, indent);
50 
51   os << indent << "Number of stream divisions: " << m_NumberOfStreamDivisions
52      << std::endl;
53 
54   itkPrintSelfObjectMacro( RegionSplitter );
55 }
56 
57 /**
58  *
59  */
60 template< typename TInputImage, typename TOutputImage >
61 void
62 StreamingImageFilter< TInputImage, TOutputImage >
PropagateRequestedRegion(DataObject * output)63 ::PropagateRequestedRegion(DataObject *output)
64 {
65   /**
66    * check flag to avoid executing forever if there is a loop
67    */
68   if ( this->m_Updating )
69     {
70     return;
71     }
72 
73   /**
74    * Give the subclass a chance to indicate that it will provide
75    * more data than required for the output. This can happen, for
76    * example, when a source can only produce the whole output.
77    * Although this is being called for a specific output, the source
78    * may need to enlarge all outputs.
79    */
80   this->EnlargeOutputRequestedRegion(output);
81 
82   /**
83    * Give the subclass a chance to define how to set the requested
84    * regions for each of its outputs, given this output's requested
85    * region.  The default implementation is to make all the output
86    * requested regions the same.  A subclass may need to override this
87    * method if each output is a different resolution.
88    */
89   this->GenerateOutputRequestedRegion(output);
90 
91   // we don't call GenerateInputRequestedRegion since the requested
92   // regions are manage when the pipeline is execute
93 
94   // we don't call inputs PropagateRequestedRegion either
95   // because the pipeline managed later
96 }
97 
98 /**
99  *
100  */
101 template< typename TInputImage, typename TOutputImage >
102 void
103 StreamingImageFilter< TInputImage, TOutputImage >
UpdateOutputData(DataObject * itkNotUsed (output))104 ::UpdateOutputData( DataObject *itkNotUsed(output) )
105 {
106 
107   /**
108    * prevent chasing our tail
109    */
110   if ( this->m_Updating )
111     {
112     return;
113     }
114 
115   /**
116    * Prepare all the outputs. This may deallocate previous bulk data.
117    */
118   this->PrepareOutputs();
119 
120   /**
121    * Make sure we have the necessary inputs
122    */
123   const itk::ProcessObject::DataObjectPointerArraySizeType &ninputs = this->GetNumberOfValidRequiredInputs();
124   if ( ninputs < this->GetNumberOfRequiredInputs() )
125     {
126     itkExceptionMacro(
127       << "At least " << static_cast< unsigned int >( this->GetNumberOfRequiredInputs() )
128       << " inputs are required but only " << ninputs << " are specified.");
129     return;
130     }
131 
132   /**
133    * Tell all Observers that the filter is starting, before emiting
134    * the 0.0 Progress event
135    */
136   this->InvokeEvent( StartEvent() );
137 
138 
139   this->SetAbortGenerateData(0);
140   this->UpdateProgress(0.0);
141   this->m_Updating = true;
142 
143 
144   /**
145    * Allocate the output buffer.
146    */
147   OutputImageType      *outputPtr = this->GetOutput(0);
148   const OutputImageRegionType outputRegion = outputPtr->GetRequestedRegion();
149   outputPtr->SetBufferedRegion(outputRegion);
150   outputPtr->Allocate();
151 
152   /**
153    * Grab the input
154    */
155   auto * inputPtr = const_cast< InputImageType * >( this->GetInput(0) );
156 
157   /**
158    * Determine of number of pieces to divide the input.  This will be the
159    * minimum of what the user specified via SetNumberOfStreamDivisions()
160    * and what the Splitter thinks is a reasonable value.
161    */
162   unsigned int numDivisions, numDivisionsFromSplitter;
163 
164   numDivisions = m_NumberOfStreamDivisions;
165   numDivisionsFromSplitter =
166     m_RegionSplitter
167     ->GetNumberOfSplits(outputRegion, m_NumberOfStreamDivisions);
168   if ( numDivisionsFromSplitter < numDivisions )
169     {
170     numDivisions = numDivisionsFromSplitter;
171     }
172 
173   /**
174    * Loop over the number of pieces, execute the upstream pipeline on each
175    * piece, and copy the results into the output image.
176    */
177   unsigned int         piece=0;
178   for (;
179        piece < numDivisions && !this->GetAbortGenerateData();
180        piece++ )
181     {
182     InputImageRegionType streamRegion = outputRegion;
183     m_RegionSplitter->GetSplit(piece, numDivisions, streamRegion);
184 
185     inputPtr->SetRequestedRegion(streamRegion);
186     inputPtr->PropagateRequestedRegion();
187     inputPtr->UpdateOutputData();
188 
189     // copy the result to the proper place in the output. the input
190     // requested region determined by the RegionSplitter (as opposed
191     // to what the pipeline might have enlarged it to) is used to
192     // copy the regions from the input to output
193     ImageAlgorithm::Copy( inputPtr, outputPtr, streamRegion, streamRegion );
194 
195 
196     this->UpdateProgress( static_cast<float>(piece) / static_cast<float>(numDivisions) );
197     }
198 
199   /**
200    * If we ended due to aborting, push the progress up to 1.0 (since
201    * it probably didn't end there)
202    */
203   if ( !this->GetAbortGenerateData() )
204     {
205     this->UpdateProgress(1.0);
206     }
207 
208   // Notify end event observers
209   this->InvokeEvent( EndEvent() );
210 
211   /**
212    * Now we have to mark the data as up to data.
213    */
214   for (auto &outputName : this->GetOutputNames() )
215     {
216     if (this->ProcessObject::GetOutput(outputName))
217       {
218       this->ProcessObject::GetOutput(outputName)->DataHasBeenGenerated();
219       }
220     }
221 
222   /**
223    * Release any inputs if marked for release
224    */
225   this->ReleaseInputs();
226 
227   // Mark that we are no longer updating the data in this filter
228   this->m_Updating = false;
229 }
230 } // end namespace itk
231 
232 #endif
233