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 itkVideoSource_hxx
19 #define itkVideoSource_hxx
20 
21 #include "itkVideoSource.h"
22 
23 namespace itk
24 {
25 
26 //-CONSTRUCTOR PRINT-----------------------------------------------------------
27 
28 //
29 // Constructor
30 //
31 template<typename TOutputVideoStream>
VideoSource()32 VideoSource<TOutputVideoStream>::VideoSource()
33 {
34   typename OutputVideoStreamType::Pointer output =
35     static_cast< OutputVideoStreamType* >( this->MakeOutput(0).GetPointer() );
36   this->ProcessObject::SetNumberOfRequiredOutputs(1);
37   this->ProcessObject::SetNthOutput( 0, output.GetPointer() );
38 }
39 
40 //
41 // PrintSelf
42 //
43 template<typename TOutputVideoStream>
44 void
45 VideoSource<TOutputVideoStream>
PrintSelf(std::ostream & os,Indent indent) const46 ::PrintSelf(std::ostream & os, Indent indent) const
47 {
48   Superclass::PrintSelf(os, indent);
49 }
50 
51 //-PUBLIC METHODS--------------------------------------------------------------
52 
53 //
54 // GetOutput()
55 //
56 template<typename TOutputVideoStream>
57 typename VideoSource< TOutputVideoStream >::OutputVideoStreamType*
GetOutput()58 VideoSource<TOutputVideoStream>::GetOutput()
59 {
60   // Make sure there is at least 1 output
61   if (this->GetNumberOfOutputs() < 1)
62     {
63     itkWarningMacro("No outputs set");
64     return nullptr;
65     }
66 
67   // Return the output
68   return this->GetOutput(0);
69 }
70 
71 //
72 // GetOutput(idx)
73 //
74 template<typename TOutputVideoStream>
75 TOutputVideoStream*
GetOutput(unsigned int idx)76 VideoSource<TOutputVideoStream>::GetOutput(unsigned int idx)
77 {
78   auto * out = dynamic_cast< OutputVideoStreamType* > (this->TemporalProcessObject::GetOutput(idx) );
79 
80   // Make sure there is at least 1 output
81   if (out == nullptr)
82     {
83     itkWarningMacro("dynamic_cast to output type failed");
84     }
85 
86   return out;
87 }
88 
89 //
90 // GraftOutput
91 //
92 template<typename TOutputVideoStream>
93 void
GraftOutput(TOutputVideoStream * graft)94 VideoSource<TOutputVideoStream>::GraftOutput(TOutputVideoStream* graft)
95 {
96   this->GraftNthOutput(0, graft);
97 }
98 
99 //
100 // GraftNthOutput
101 //
102 template<typename TOutputVideoStream>
103 void
104 VideoSource<TOutputVideoStream>::
GraftNthOutput(unsigned int idx,TOutputVideoStream * graft)105 GraftNthOutput(unsigned int idx, TOutputVideoStream* graft)
106 {
107   if (idx >= this->GetNumberOfOutputs() )
108     {
109     itkExceptionMacro(<< "Requested to graft output " << idx
110                       << " but this VideoSource only has "
111                       << this->GetNumberOfOutputs() << " Outputs.");
112     }
113   if (!graft)
114     {
115     itkExceptionMacro("Cannot graft from a nullptr pointer");
116     }
117 
118   // we use the process object method since all our outputs may not be of the
119   // same type
120   DataObject* output = this->ProcessObject::GetOutput(idx);
121   output->Graft(graft);
122 }
123 
124 //
125 // MakeOutput
126 //
127 template<typename TOutputVideoStream>
128 DataObject::Pointer
MakeOutput(DataObjectPointerArraySizeType itkNotUsed (idx))129 VideoSource<TOutputVideoStream>::MakeOutput(DataObjectPointerArraySizeType itkNotUsed(idx) )
130 {
131   return OutputVideoStreamType::New().GetPointer();
132 }
133 
134 //-PROTECTED METHODS-----------------------------------------------------------
135 
136 //
137 // GenerateOutputRequestedTemporalRegion
138 //
139 template<typename TOutputVideoStream>
140 void
141 VideoSource<TOutputVideoStream>::
GenerateOutputRequestedTemporalRegion(TemporalDataObject * output)142 GenerateOutputRequestedTemporalRegion(TemporalDataObject* output)
143 {
144   // Check if requested temporal region unset
145   bool           resetNumFrames = false;
146   TemporalRegion outputRequest = output->GetRequestedTemporalRegion();
147 
148   if (!outputRequest.GetFrameDuration() )
149     {
150     resetNumFrames = true;
151     }
152 
153   // Call superclass's version - this will set the requested temporal region
154   Superclass::GenerateOutputRequestedTemporalRegion(this->GetOutput() );
155 
156   // Make sure the output has enough buffers available for the entire output
157   // only if this request has just been matched to the largest possible spatial
158   // region. This should only happen for filters at the end of the pipeline
159   // since mid-pipeline filters will have their outputs' requested temporal
160   // regions set automatically.
161   SizeValueType requestDuration =
162     this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration();
163   if (resetNumFrames && this->GetOutput()->GetNumberOfBuffers() < requestDuration)
164     {
165     this->GetOutput()->SetNumberOfBuffers(requestDuration);
166     }
167 
168   // If requested temporal region was just set to largest possible, set the
169   // spatial regions for every frame to the largest possible as well
170   if (resetNumFrames)
171     {
172     SizeValueType frameStart =
173       this->GetOutput()->GetRequestedTemporalRegion().GetFrameStart();
174     SizeValueType numFrames =
175       this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration();
176     for (SizeValueType i = frameStart; i < frameStart + numFrames; ++i)
177       {
178       //this->GetOutput()->SetFrameRequestedSpatialRegion(i,
179       //  this->GetOutput()->GetFrameLargestPossibleSpatialRegion(i));
180       OutputVideoStreamType* out = this->GetOutput();
181       out->GetFrameLargestPossibleSpatialRegion(i);
182       }
183     }
184 }
185 
186 //
187 // AllocateOutputs
188 //
189 template<typename TOutputVideoStream>
190 void
AllocateOutputs()191 VideoSource<TOutputVideoStream>::AllocateOutputs()
192 {
193   // Get the output
194   OutputVideoStreamType* output = this->GetOutput();
195 
196   // Get a list of unbuffered requested frames
197   TemporalRegion unbufferedRegion = output->GetUnbufferedRequestedTemporalRegion();
198 
199   // We don't touch the buffered temporal region here because that is handled
200   // by the default implementation of GenerateData in TemporalProcessObject
201 
202   // If there are no unbuffered frames, return now
203   SizeValueType numFrames = unbufferedRegion.GetFrameDuration();
204 
205   if (numFrames == 0)
206     {
207     return;
208     }
209 
210   // Initialize any empty frames (which will set region values from cache)
211   output->InitializeEmptyFrames();
212 
213   // Loop through the unbuffered frames and set the buffered spatial region to
214   // match the requested spatial region then allocate the data
215   SizeValueType startFrame = unbufferedRegion.GetFrameStart();
216   for (SizeValueType i = startFrame; i < startFrame + numFrames; ++i)
217     {
218     output->SetFrameBufferedSpatialRegion(i, output->GetFrameRequestedSpatialRegion(i) );
219     typename OutputFrameType::Pointer frame = output->GetFrame(i);
220     frame->SetBufferedRegion(output->GetFrameRequestedSpatialRegion(i) );
221     frame->Allocate();
222     }
223 }
224 
225 //
226 // TemporalStreamingGenerateData
227 //
228 template<typename TOutputVideoStream>
229 void
230 VideoSource<TOutputVideoStream>::
TemporalStreamingGenerateData()231 TemporalStreamingGenerateData()
232 {
233   // Call a method that can be overriden by a subclass to allocate
234   // memory for the filter's outputs
235   this->AllocateOutputs();
236 
237   // Call a method that can be overridden by a subclass to perform
238   // some calculations prior to splitting the main computations into
239   // separate threads
240   this->BeforeThreadedGenerateData();
241 
242   // Set up the multithreaded processing
243   ThreadStruct str;
244   str.Filter = this;
245 
246   this->GetMultiThreader()->SetNumberOfWorkUnits( this->GetNumberOfWorkUnits() );
247   this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str);
248 
249   // multithread the execution
250   this->GetMultiThreader()->SingleMethodExecute();
251 
252   // Call a method that can be overridden by a subclass to perform
253   // some calculations after all the threads have completed
254   this->AfterThreadedGenerateData();
255 }
256 
257 //
258 // ThreadedGenerateData
259 //
260 template<typename TOutputVideoStream>
261 void
262 VideoSource<TOutputVideoStream>::
ThreadedGenerateData(const typename TOutputVideoStream::SpatialRegionType & itkNotUsed (outputRegionForThread),int itkNotUsed (threadId))263 ThreadedGenerateData(
264   const typename TOutputVideoStream::SpatialRegionType& itkNotUsed(outputRegionForThread),
265   int itkNotUsed(threadId) )
266 {
267   itkExceptionMacro( << "itk::ERROR: " << this->GetNameOfClass()
268                      << "(" << this << "): "
269                      << "Subclass should override this method!!!");
270 }
271 
272 //
273 // SplitRequestedSpatialRegion -- Copied mostly from ImageSource
274 //
275 // Note: This implementation bases the spatial region split on the requested
276 // spatial region for the current Head frame of the output. This could
277 // potentially cause issues if frames are different sized.
278 //
279 template<typename TOutputVideoStream>
280 int
281 VideoSource<TOutputVideoStream>::
SplitRequestedSpatialRegion(int i,int num,typename TOutputVideoStream::SpatialRegionType & splitRegion)282 SplitRequestedSpatialRegion(int i, int num,
283                             typename TOutputVideoStream::SpatialRegionType& splitRegion)
284 {
285   // Get the output pointer and a pointer to the first output frame
286   OutputVideoStreamType* outputPtr = this->GetOutput();
287   SizeValueType          currentFrame = outputPtr->GetRequestedTemporalRegion().GetFrameStart();
288   OutputFrameType*       framePtr = outputPtr->GetFrame(currentFrame);
289 
290   const typename TOutputVideoStream::SizeType & requestedRegionSize =
291     framePtr->GetRequestedRegion().GetSize();
292 
293   int splitAxis;
294   typename TOutputVideoStream::IndexType splitIndex;
295   typename TOutputVideoStream::SizeType splitSize;
296 
297   // Initialize the splitRegion to the output requested region
298   splitRegion = framePtr->GetRequestedRegion();
299   splitIndex = splitRegion.GetIndex();
300   splitSize = splitRegion.GetSize();
301 
302   // split on the outermost dimension available
303   splitAxis = framePtr->GetImageDimension() - 1;
304   while ( requestedRegionSize[splitAxis] == 1 )
305     {
306     --splitAxis;
307     if ( splitAxis < 0 )
308       { // cannot split
309       itkDebugMacro("  Cannot Split");
310       return 1;
311       }
312     }
313 
314   // determine the actual number of pieces that will be generated
315   typename TOutputVideoStream::SizeType::SizeValueType range = requestedRegionSize[splitAxis];
316 
317   int valuesPerThread;
318   int maxThreadIdUsed;
319   if (range == 0)
320     {
321     valuesPerThread = 0;
322     maxThreadIdUsed = 0;
323     }
324   else
325     {
326     valuesPerThread = Math::Ceil< int >(range / (double)num);
327     maxThreadIdUsed = Math::Ceil< int >(range / (double)valuesPerThread) - 1;
328     }
329 
330   // Split the region
331   if ( i < maxThreadIdUsed )
332     {
333     splitIndex[splitAxis] += i * valuesPerThread;
334     splitSize[splitAxis] = valuesPerThread;
335     }
336   if ( i == maxThreadIdUsed )
337     {
338     splitIndex[splitAxis] += i * valuesPerThread;
339     // last thread needs to process the "rest" dimension being split
340     splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread;
341     }
342 
343   // set the split region ivars
344   splitRegion.SetIndex(splitIndex);
345   splitRegion.SetSize(splitSize);
346 
347   itkDebugMacro("  Split Piece: " << splitRegion);
348 
349   return maxThreadIdUsed + 1;
350 }
351 
352 //
353 // ThreaderCallback -- Copied from ImageSource
354 //
355 template<typename TOutputVideoStream>
356 ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION
357 VideoSource<TOutputVideoStream>::
ThreaderCallback(void * arg)358 ThreaderCallback(void* arg)
359 {
360   ThreadStruct *str;
361   int           total, threadId, threadCount;
362 
363   threadId = ( (MultiThreaderBase::WorkUnitInfo *)( arg ) )->WorkUnitID;
364   threadCount = ( (MultiThreaderBase::WorkUnitInfo *)( arg ) )->NumberOfWorkUnits;
365 
366   str = (ThreadStruct *)( ( (MultiThreaderBase::WorkUnitInfo *)( arg ) )->UserData );
367 
368   // execute the actual method with appropriate output region
369   // first find out how many pieces extent can be split into.
370   typename TOutputVideoStream::SpatialRegionType splitRegion;
371   total = str->Filter->SplitRequestedSpatialRegion(threadId, threadCount, splitRegion);
372 
373   if ( threadId < total )
374     {
375     str->Filter->ThreadedGenerateData(splitRegion, threadId);
376     }
377   // else
378   //   {
379   //   otherwise don't use this thread. Sometimes the threads dont
380   //   break up very well and it is just as efficient to leave a
381   //   few threads idle.
382   //   }
383 
384   return ITK_THREAD_RETURN_DEFAULT_VALUE;
385 }
386 
387 } // end namespace itk
388 
389 #endif
390