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