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 #include <iostream>
19
20 #include "itkTemporalProcessObject.h"
21 #include "itkTemporalDataObject.h"
22
23 /** Set up dummy implementations of TemporalProcessObject and
24 * TemporalDataObject for testing
25 */
26 namespace itk
27 {
28 namespace TemporalProcessObjectTest
29 {
30
31 using SizeValueType = ::itk::SizeValueType;
32 using OffsetValueType = ::itk::OffsetValueType;
33 /** \class CallRecord
34 * Record of a start or end of a GenerateDataCall from a
35 * DummyTemporalProcessObject instance
36 */
37 class CallRecord
38 {
39 public:
40 enum RecordTypeEnum {START_CALL, END_CALL, MAX_RECORD_TYPE};
41 enum MethodTypeEnum {GENERATE_DATA, STREAMING_GENERATE_DATA, MAX_METHOD_TYPE};
42
43 /** Constructor that takes necessary info */
CallRecord(SizeValueType callerId,SizeValueType recordType,SizeValueType methodType)44 CallRecord(SizeValueType callerId, SizeValueType recordType, SizeValueType methodType)
45 {
46 if (recordType >= MAX_RECORD_TYPE || methodType >= MAX_METHOD_TYPE)
47 {
48 throw;
49 }
50 m_CallerId = callerId;
51 m_RecordType = recordType;
52 m_MethodType = methodType;
53 }
54
55 /** Access members */
GetCallerId() const56 SizeValueType GetCallerId() const
57 {
58 return m_CallerId;
59 }
GetRecordType() const60 SizeValueType GetRecordType() const
61 {
62 return m_RecordType;
63 }
GetMethodType() const64 SizeValueType GetMethodType() const
65 {
66 return m_MethodType;
67 }
68
69 /** Print out nicely */
Print()70 void Print()
71 {
72 std::cout << "ID: " << m_CallerId << " -> ";
73 if (m_MethodType == GENERATE_DATA)
74 {
75 std::cout << "GenerateData - ";
76 }
77 else if(m_MethodType == STREAMING_GENERATE_DATA)
78 {
79 std::cout << "TemporalStreamingGenerateData - ";
80 }
81
82 if (m_RecordType == START_CALL)
83 {
84 std::cout << " START";
85 }
86 else if(m_RecordType == END_CALL)
87 {
88 std::cout << " END";
89 }
90 std::cout << std::endl;
91 }
92
93 /** Comparison operators */
operator ==(const CallRecord & other) const94 bool operator==(const CallRecord& other) const
95 {
96 return (m_CallerId == other.GetCallerId() &&
97 m_RecordType == other.GetRecordType() &&
98 m_MethodType == other.GetMethodType() );
99 }
100
operator !=(const CallRecord & other) const101 bool operator!=(const CallRecord& other) const
102 {
103 return !(*this == other);
104 }
105
106 protected:
107 SizeValueType m_CallerId;
108 SizeValueType m_RecordType;
109 SizeValueType m_MethodType;
110 };
111
112 /**
113 * Static list of CallRecord items representing the stack trace of
114 * calls to GenerateData and TemporalStreamingGenerateData
115 */
116 std::vector<CallRecord> m_CallStack;
117
118 /** \class DummyTemporalDataObject
119 * Create TemporaDataObject subclass that does nothing, but overrides some
120 * methods to provide debug output
121 */
122 class DummyTemporalDataObject : public TemporalDataObject
123 {
124 public:
125
126 /** type alias */
127 using Self = DummyTemporalDataObject;
128 using Superclass = TemporalDataObject;
129 using Pointer = SmartPointer< Self >;
130 using ConstPointer = SmartPointer< const Self >;
131
132 /** Class macros */
133 itkNewMacro(Self);
134 itkTypeMacro(DummyTemporalDataObject, TemporalDataObject);
135
136 /** Override update for debug output */
Update()137 void Update() override
138 {
139 //std::cout << "Calling Update from temporal data object" << std::endl;
140 Superclass::Update();
141 }
142
143 /** Override UpdateOutputInformation for debug output */
UpdateOutputInformation()144 void UpdateOutputInformation() override
145 {
146 //std::cout << "Calling UpdateOutputInformation from temporal data object"
147 // << std::endl;
148 Superclass::UpdateOutputInformation();
149 }
150
151 /** Override PropagateRequestedRegion for debug output */
PropagateRequestedRegion()152 void PropagateRequestedRegion() throw (itk::InvalidRequestedRegionError) override
153 {
154 Superclass::PropagateRequestedRegion();
155 }
156
157 /** Override UpdateOutputData for debug output */
UpdateOutputData()158 void UpdateOutputData() override
159 {
160 std::cout << " UpdateOutputData from temporal data object" << std::endl;
161
162 //DEBUG
163 std::cout << "Buffered region outside: " << this->RequestedRegionIsOutsideOfTheBufferedRegion()
164 << std::endl;
165 Superclass::UpdateOutputData();
166 }
167
168 /** Fill buffer with X new frames */
SetBufferToXNewFrames(SizeValueType x)169 void SetBufferToXNewFrames(SizeValueType x)
170 {
171 // Set the internal number of buffers
172 m_DataObjectBuffer->SetNumberOfBuffers(x);
173
174 for (SizeValueType i = 0; i < x; ++i)
175 {
176 // Create a new DataObject
177 DataObject::Pointer obj = dynamic_cast<DataObject*>(DataObject::New().GetPointer() );
178
179 // Append to the end of the buffer
180 m_DataObjectBuffer->MoveHeadForward();
181 m_DataObjectBuffer->SetBufferContents(0, obj);
182 }
183
184 // Set buffered region info
185 m_BufferedTemporalRegion.SetFrameStart(0);
186 m_BufferedTemporalRegion.SetFrameDuration(x);
187 }
188
189 /** Append the supplied data object */
SetObjectAtFrame(SizeValueType frameNumber,DataObject * obj)190 void SetObjectAtFrame(SizeValueType frameNumber, DataObject* obj)
191 {
192 m_DataObjectBuffer->SetBufferContents(frameNumber, obj);
193 }
194
195 /** Get a bufferd frame */
GetFrame(SizeValueType frameNumber)196 DataObject::Pointer GetFrame(SizeValueType frameNumber)
197 {
198 // if nothing buffered, just fail
199 if (m_BufferedTemporalRegion.GetFrameDuration() == 0)
200 {
201 return nullptr;
202 }
203
204 // make sure we have the desired frame buffered
205 SizeValueType bufStart = m_BufferedTemporalRegion.GetFrameStart();
206 SizeValueType bufEnd = bufStart + m_BufferedTemporalRegion.GetFrameDuration() - 1;
207 if (frameNumber < bufStart || frameNumber > bufEnd)
208 {
209 return nullptr;
210 }
211
212 // If we can, fetch the desired frame
213 OffsetValueType frameOffset = frameNumber - bufEnd; // Should be negative
214 return m_DataObjectBuffer->GetBufferContents(frameOffset);
215 }
216
217 };
218
219 /** \class DummyTemporalProcessObject
220 * Create TemporalProcessObject subclass that does nothing, but implements
221 * New() and TemporalStreamingGenerateData()
222 */
223 class DummyTemporalProcessObject : public TemporalProcessObject
224 {
225 public:
226 /** type alias */
227 using Self = DummyTemporalProcessObject;
228 using Superclass = TemporalProcessObject;
229 using Pointer = SmartPointer< Self >;
230 using ConstPointer = SmartPointer< const Self >;
231
232 /** Class macros */
233 itkNewMacro(Self);
234 itkTypeMacro(DummyTemporalProcessObject, TemporalProcessObject);
235
236 /*-REQUIRED IMPLEMENTATIONS------------------------------------------------*/
237
238 /** TemporalStreamingGenerateData */
TemporalStreamingGenerateData()239 void TemporalStreamingGenerateData() override
240 {
241 // Create a START entry in the stack trace
242 m_CallStack.emplace_back(m_IdNumber,
243 CallRecord::START_CALL, CallRecord::STREAMING_GENERATE_DATA );
244
245 // Report
246 SizeValueType outputStart = this->GetOutput()->GetRequestedTemporalRegion().GetFrameStart();
247 std::cout << "**(ID = " << m_IdNumber << ") - TemporalStreamingGenerateData" << std::endl;
248 std::cout << " -> output requested from: " << outputStart << " to "
249 << this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() +
250 outputStart - 1
251 << std::endl;
252
253 SizeValueType inputStart = this->GetInput()->GetRequestedTemporalRegion().GetFrameStart();
254 SizeValueType inputEnd = inputStart +
255 this->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() - 1;
256 std::cout << " -> input requested from " << inputStart << " to " << inputEnd << std::endl;
257 std::cout << " -> input buffered from "
258 << this->GetInput()->GetBufferedTemporalRegion().GetFrameStart() << " to "
259 << this->GetInput()->GetBufferedTemporalRegion().GetFrameStart() +
260 this->GetInput()->GetBufferedTemporalRegion().GetFrameDuration() - 1
261 << std::endl;
262
263 // Get the list of unbuffered frames
264 TemporalRegion unbufferedRegion = this->GetOutput()->GetUnbufferedRequestedTemporalRegion();
265 std::cout << unbufferedRegion << std::endl;
266
267 // Make sure that the requested output duration matches the unit output
268 // duration
269 SizeValueType numFramesOut =
270 this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration();
271 if (numFramesOut != m_UnitOutputNumberOfFrames)
272 {
273 itkExceptionMacro("Requested non-unit number of output frames");
274 }
275
276 // Just pass frames from the input through to the output and add debug info
277 for (SizeValueType i = outputStart; i < outputStart + numFramesOut; ++i)
278 {
279 DataObject::Pointer newObj = dynamic_cast<DataObject*>(DataObject::New().GetPointer() );
280
281 // Set the output
282 this->GetOutput()->SetObjectAtFrame(i, newObj);
283 }
284
285 // Set the buffered region to match the requested region
286 //this->GetOutput()->SetBufferedTemporalRegion(this->GetOutput()->GetRequestedTemporalRegion());
287
288 // Create an END entry in the stack trace
289 m_CallStack.emplace_back(m_IdNumber,
290 CallRecord::END_CALL, CallRecord::STREAMING_GENERATE_DATA );
291
292 }
293
294 /** Allow the UnitInputNumberOfFrames to be set */
SetUnitInputNumberOfFrames(const SizeValueType numberOfFrames)295 void SetUnitInputNumberOfFrames( const SizeValueType numberOfFrames ) override
296 {
297 itkDebugMacro("setting UnitInputNumberOfFrames to " << numberOfFrames);
298 if ( this->m_UnitInputNumberOfFrames != numberOfFrames )
299 {
300 this->m_UnitInputNumberOfFrames = numberOfFrames;
301 this->Modified();
302 }
303 }
304
305 /** Allow the UnitOutputNumberOfFrames to be set */
SetUnitOutputNumberOfFrames(const SizeValueType numberOfFrames)306 void SetUnitOutputNumberOfFrames( const SizeValueType numberOfFrames ) override
307 {
308 itkDebugMacro("setting UnitOutputNumberOfFrames to " << numberOfFrames);
309 if ( this->m_UnitOutputNumberOfFrames != numberOfFrames )
310 {
311 this->m_UnitOutputNumberOfFrames = numberOfFrames;
312 this->Modified();
313 }
314 }
315
316 /** GetOutput will return the output on port 0 */
GetOutput()317 DummyTemporalDataObject::Pointer GetOutput()
318 {
319 return dynamic_cast<DummyTemporalDataObject*>(this->TemporalProcessObject::GetOutput(0) );
320 }
321
322 /** SetInput will set the 0th input */
323 using Superclass::SetInput;
SetInput(TemporalDataObject * tdo)324 void SetInput(TemporalDataObject* tdo)
325 {
326 this->ProcessObject::SetNthInput(0, tdo);
327 }
328
329 /** GetInput gets the 0th input as a DummyTemporalDataObject */
GetInput()330 DummyTemporalDataObject::Pointer GetInput()
331 {
332 return dynamic_cast<DummyTemporalDataObject*>(this->TemporalProcessObject::GetInput(0) );
333 }
334
335 /** Get/Set IdNumber */
336 itkSetMacro(IdNumber, SizeValueType);
337 itkGetMacro(IdNumber, SizeValueType);
338
339 /** Provide access to m_FrameSkipPerOutput */
SetFrameSkipPerOutput(const OffsetValueType frameSkip)340 void SetFrameSkipPerOutput ( const OffsetValueType frameSkip ) override
341 {
342 itkDebugMacro("setting FrameSkipPerOutput to " << frameSkip);
343 if ( this->m_FrameSkipPerOutput != frameSkip )
344 {
345 this->m_FrameSkipPerOutput = frameSkip;
346 this->Modified();
347 }
348 }
349
350 itkGetMacro(FrameSkipPerOutput, OffsetValueType);
351
352 /** Provide access to m_InputStencilCurrentFrameIndex */
SetInputStencilCurrentFrameIndex(const SizeValueType inputStencil)353 void SetInputStencilCurrentFrameIndex ( const SizeValueType inputStencil ) override
354 {
355 itkDebugMacro("setting InputStencilCurrentFrameIndex to " << inputStencil);
356 if ( this->m_InputStencilCurrentFrameIndex != inputStencil )
357 {
358 this->m_InputStencilCurrentFrameIndex = inputStencil;
359 this->Modified();
360 }
361 }
GetInputStencilCurrentFrameIndex()362 SizeValueType GetInputStencilCurrentFrameIndex() override
363 {
364 return this->m_InputStencilCurrentFrameIndex;
365 }
366
367 /*-DEBUG OVERRIDES---------------------------------------------------------*/
368
369 /** Override Update for debug output */
Update()370 void Update() override
371 {
372 std::cout << "(ID = " << m_IdNumber << ") - Update" << std::endl;
373 Superclass::Update();
374 }
375
376 /** Override UpdateOutputData for debug output */
UpdateOutputData(DataObject * dobj)377 void UpdateOutputData(DataObject* dobj) override
378 {
379 std::cout << "(ID = " << m_IdNumber << ") - UpdateOutputData" << std::endl;
380 Superclass::UpdateOutputData(dobj);
381 }
382
383 /** Override GenerateData for debug output */
GenerateData()384 void GenerateData() override
385 {
386 // Create a START entry in the stack trace
387 m_CallStack.emplace_back(m_IdNumber,
388 CallRecord::START_CALL, CallRecord::GENERATE_DATA );
389
390 std::cout << "*(ID = " << m_IdNumber << ") - GenerateData" << std::endl;
391 Superclass::GenerateData();
392
393 // Create an END entry in the stack trace
394 m_CallStack.emplace_back(m_IdNumber,
395 CallRecord::END_CALL, CallRecord::GENERATE_DATA );
396
397 }
398
399 /** Override EnlargeOutputRequestedTemporalRegion for debug output */
EnlargeOutputRequestedTemporalRegion(TemporalDataObject * output)400 void EnlargeOutputRequestedTemporalRegion(TemporalDataObject* output) override
401 {
402 std::cout << "(ID = " << m_IdNumber << ") - EnlargeOutputRequestedTemporalRegion" << std::endl;
403 Superclass::EnlargeOutputRequestedTemporalRegion(output);
404 }
405
406 /** Override GenerateInputRequestedTemporalRegion for debug output */
GenerateInputRequestedTemporalRegion()407 void GenerateInputRequestedTemporalRegion() override
408 {
409 std::cout << "(ID = " << m_IdNumber << ") - GenerateInputRequestedTemporalRegion" << std::endl;
410 Superclass::GenerateInputRequestedTemporalRegion();
411 }
412
413 protected:
414
415 /** Constructor */
DummyTemporalProcessObject()416 DummyTemporalProcessObject()
417
418 {
419 DummyTemporalDataObject::Pointer po = DummyTemporalDataObject::New();
420
421 this->SetNthOutput(0, po.GetPointer() );
422 }
423
424 private:
425
426 /** ID number used for debugging */
427 SizeValueType m_IdNumber{0};
428
429 };
430
431 } // end namespace TemporalProcessObjectTest
432 } // end namespace itk
433
434 /**
435 * Test functionality of itkTemporalProcessObject
436 */
itkTemporalProcessObjectTest(int,char * [])437 int itkTemporalProcessObjectTest( int ,
438 char* [] )
439 {
440
441 using SizeValueType = ::itk::SizeValueType;
442 using OffsetValueType = ::itk::OffsetValueType;
443 //////
444 // Set up pipeline
445 //////
446
447 // Create 3 new DummyTemporalProcessObjects
448 using TPOType = itk::TemporalProcessObjectTest::DummyTemporalProcessObject;
449 TPOType::Pointer tpo1 = TPOType::New();
450 tpo1->SetIdNumber(1);
451 TPOType::Pointer tpo2 = TPOType::New();
452 tpo2->SetIdNumber(2);
453 TPOType::Pointer tpo3 = TPOType::New();
454 tpo3->SetIdNumber(3);
455
456 // Set up the Process Objects in a pipeline
457 tpo2->SetInput(tpo1->GetOutput() );
458 tpo3->SetInput(tpo2->GetOutput() );
459
460 // Set up the Unit input/output numbers of frames
461 tpo1->SetUnitInputNumberOfFrames(3);
462 tpo1->SetUnitOutputNumberOfFrames(1);
463 tpo2->SetUnitInputNumberOfFrames(3);
464 tpo2->SetUnitOutputNumberOfFrames(3);
465 tpo3->SetUnitInputNumberOfFrames(2);
466 tpo3->SetUnitOutputNumberOfFrames(1);
467 tpo3->SetFrameSkipPerOutput(2);
468 tpo2->GetOutput()->SetNumberOfBuffers(6);
469
470 // Set up frame stencils
471 tpo1->SetInputStencilCurrentFrameIndex(1); // "current frame" centered in
472 // group of 3
473 tpo2->SetInputStencilCurrentFrameIndex(0); // "current frame" at start of
474 // group of 3
475 tpo3->SetInputStencilCurrentFrameIndex(1); // "current frame" at end of group
476 // of 2
477
478 // Create a new TemporalDataObject to pass through the pipeline
479 using TDOType = itk::TemporalProcessObjectTest::DummyTemporalDataObject;
480 TDOType::Pointer tdo = TDOType::New();
481 tpo1->SetInput(tdo);
482
483 // Set up regions for TemporalDataObject
484 itk::TemporalRegion largestRegion;
485 itk::TemporalRegion requestedRegion;
486 itk::TemporalRegion bufferedRegion;
487 largestRegion.SetFrameStart(0);
488 largestRegion.SetFrameDuration(20);
489 tdo->SetLargestPossibleTemporalRegion(largestRegion);
490 requestedRegion.SetFrameStart(0);
491 requestedRegion.SetFrameDuration(1);
492 tdo->SetRequestedTemporalRegion(requestedRegion);
493 bufferedRegion.SetFrameStart(0);
494 bufferedRegion.SetFrameDuration(0);
495 tdo->SetBufferedTemporalRegion(bufferedRegion);
496
497 // Fill the TemporalDataObject input with frames for the entire region
498 tdo->SetBufferToXNewFrames(largestRegion.GetFrameDuration() );
499
500 //////
501 // Test results of LargestTemporalRegion computation
502 //////
503
504 // Update to get largest possible temporal region information
505 tpo3->UpdateOutputInformation();
506
507 // Check largest possible temporal region after propagation
508 if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() != 18)
509 {
510 std::cerr << "tpo1 largest possible region duration not correct" << std::endl;
511 return EXIT_FAILURE;
512 }
513 if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameStart() != 1)
514 {
515 std::cerr << "tpo1 largest possible region start not correct" << std::endl;
516 return EXIT_FAILURE;
517 }
518 if (tpo2->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() != 48)
519 {
520 std::cerr << "tpo2 largest possible region duration not correct" << std::endl;
521 return EXIT_FAILURE;
522 }
523 if (tpo2->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameStart() != 1)
524 {
525 std::cerr << "tpo2 largest possible region start not correct" << std::endl;
526 return EXIT_FAILURE;
527 }
528 itk::TemporalRegion endLargestPossibleRegion =
529 tpo3->GetOutput()->GetLargestPossibleTemporalRegion();
530 if (endLargestPossibleRegion.GetFrameDuration() != 24)
531 {
532 std::cerr << "tpo3 largest possible region duration not correct" << std::endl;
533 return EXIT_FAILURE;
534 }
535 if (endLargestPossibleRegion.GetFrameStart() != 2)
536 {
537 std::cerr << "tpo3 largest possible region start not correct" << std::endl;
538 return EXIT_FAILURE;
539 }
540
541 //////
542 // Test results of requested region propagation
543 //////
544
545 // Set up requested region for the end of the pipeline
546 itk::TemporalRegion finalRequest;
547 finalRequest.SetFrameStart(endLargestPossibleRegion.GetFrameStart() );
548 finalRequest.SetFrameDuration(1);
549 itk::TemporalProcessObjectTest::DummyTemporalDataObject* finalOutput = tpo3->GetOutput();
550 finalOutput->SetRequestedTemporalRegion(finalRequest);
551
552 // Update to propagate the requested temporal region
553 finalOutput->PropagateRequestedRegion();
554
555 // Check requested region up the pipeline
556
557 // for tpo3, the requested input region should be size 3 because tpo2 can
558 // only output in groups of 3
559 if (tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 3)
560 {
561 std::cout << tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() << std::endl;
562 std::cerr << "tpo3 requested region duration not correct" << std::endl;
563 return EXIT_FAILURE;
564 }
565 if (tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 3)
566 {
567 std::cerr << tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
568 std::cerr << "tpo3 requested region start not correct" << std::endl;
569 return EXIT_FAILURE;
570 }
571
572 // tpo2 is 3->3, so an initial request of 2 gets enlarged to 3 which results
573 // in propagating a request for 3 to tpo1
574 if (tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 3)
575 {
576 std::cerr << "tpo2 requested region duration not correct" << std::endl;
577 return EXIT_FAILURE;
578 }
579 if (tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 3)
580 {
581 std::cerr << tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
582 std::cerr << "tpo2 requested region start not correct" << std::endl;
583 return EXIT_FAILURE;
584 }
585
586 // tpo1 is 3->1 and skips 1 frame for each output, so a request for 3
587 // requires 5 as input
588 if (tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 5)
589 {
590 std::cerr << "tpo1 requested region duration not correct" << std::endl;
591 return EXIT_FAILURE;
592 }
593 if (tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 2)
594 {
595 std::cerr << tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
596 std::cerr << "tpo1 requested region start not correct" << std::endl;
597 return EXIT_FAILURE;
598 }
599
600 //////
601 // Test Generation of data
602 //////
603
604 // Call update to execute the entire pipeline and track the call stack
605 itk::TemporalProcessObjectTest::m_CallStack.clear();
606 tpo3->Update();
607
608 // Print out duration of buffered output region
609 itk::TemporalProcessObjectTest::DummyTemporalDataObject::Pointer outputObject = tpo3->GetOutput();
610 OffsetValueType outputStart =
611 outputObject->GetBufferedTemporalRegion().GetFrameStart();
612 SizeValueType outputDuration =
613 outputObject->GetBufferedTemporalRegion().GetFrameDuration();
614 std::cout << "Buffered Output Region: "
615 << outputStart << "->" << outputStart + outputDuration - 1 << std::endl;
616
617 // Create a list of CallRecord items representing the correct
618 // stack trace
619 using RecordType = itk::TemporalProcessObjectTest::CallRecord;
620 std::vector<RecordType> correctCallStack;
621
622 // GenDat - START - obj 3
623 correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::GENERATE_DATA );
624
625 // GenDat - START - obj 2
626 correctCallStack.emplace_back(2, RecordType::START_CALL, RecordType::GENERATE_DATA );
627
628 // GenDat - START - obj 1
629 correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::GENERATE_DATA );
630
631 // TempStreamGenDat - START - obj 1
632 correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
633
634 // TempStreamGenDat - END - obj 1
635 correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
636
637 // TempStreamGenDat - START - obj 1
638 correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
639
640 // TempStreamGenDat - END - obj 1
641 correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
642
643 // TempStreamGenDat - START - obj 1
644 correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
645
646 // TempStreamGenDat - END - obj 1
647 correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
648
649 // GenDat - END - obj 1
650 correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::GENERATE_DATA );
651
652 // TempStreamGenDat - START - obj 2
653 correctCallStack.emplace_back(2, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
654
655 // TempStreamGenDat - END - obj 2
656 correctCallStack.emplace_back(2, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
657
658 // GenDat - END - obj 2
659 correctCallStack.emplace_back(2, RecordType::END_CALL, RecordType::GENERATE_DATA );
660
661 // TempStreamGenDat - START - obj 3
662 correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
663
664 // TempStreamGenDat - END - obj 3
665 correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
666
667 // GenDat - END - obj 3
668 correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::GENERATE_DATA );
669
670 // Check that correct number of calls made
671 if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
672 {
673 std::cerr << "Incorrect number of items in call stack" << std::endl;
674 return EXIT_FAILURE;
675 }
676
677 // Check that call lists match
678 std::cout << std::endl;
679 for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
680 {
681 std::cout << "Got: ";
682 itk::TemporalProcessObjectTest::m_CallStack[i].Print();
683 std::cout << "Expected: ";
684 correctCallStack[i].Print();
685 std::cout << std::endl;
686
687 if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
688 {
689 std::cerr << "Call stacks don't match" << std::endl;
690 return EXIT_FAILURE;
691 }
692 }
693
694 //////
695 // Test Generation of next output frame -- Since tpo3 skips two frames of
696 // input for every frame of output and tpo2 can only generate 3 outputs at a
697 // time, tpo2 must generate 6,7,8 (none of which are already buffered), so
698 // the entire pipeline runs again (so the call stack should be the same).
699 //////
700
701 // Set the requested region to the next output frame
702 finalRequest.SetFrameStart(finalRequest.GetFrameStart() + 1);
703 finalOutput = tpo3->GetOutput();
704 finalOutput->SetRequestedTemporalRegion(finalRequest);
705
706 // Call update to execute the entire pipeline and track the call stack
707 itk::TemporalProcessObjectTest::m_CallStack.clear();
708 tpo3->Update();
709
710 // Check that correct number of calls made
711 if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
712 {
713 std::cerr << "Incorrect number of items in call stack. Got: "
714 << itk::TemporalProcessObjectTest::m_CallStack.size() << " Expected: "
715 << correctCallStack.size() << std::endl;
716 return EXIT_FAILURE;
717 }
718
719 // Check that call lists match
720 std::cout << std::endl;
721 for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
722 {
723 std::cout << "Got: ";
724 itk::TemporalProcessObjectTest::m_CallStack[i].Print();
725 std::cout << "Expected: ";
726 correctCallStack[i].Print();
727 std::cout << std::endl;
728
729 if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
730 {
731 std::cerr << "Call stacks don't match" << std::endl;
732 return EXIT_FAILURE;
733 }
734 }
735
736 //////
737 // Call Update again and make sure that nothing happens except one call to
738 // GenerateData at the bottom which doesn't end up needing to do anything
739 //////
740 itk::TemporalProcessObjectTest::m_CallStack.clear();
741 tpo3->Update();
742
743 correctCallStack.clear();
744
745 // GenDat - START - obj 3
746 correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::GENERATE_DATA );
747
748 // GenDat - END - obj 3
749 correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::GENERATE_DATA );
750
751 // Check that correct number of calls made
752 if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
753 {
754 std::cerr << "Incorrect number of items in call stack. Got: "
755 << itk::TemporalProcessObjectTest::m_CallStack.size() << " Expected: "
756 << correctCallStack.size() << std::endl;
757 return EXIT_FAILURE;
758 }
759
760 // Check that call lists match
761 std::cout << std::endl;
762 for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
763 {
764 std::cout << "Got: ";
765 itk::TemporalProcessObjectTest::m_CallStack[i].Print();
766 std::cout << "Expected: ";
767 correctCallStack[i].Print();
768 std::cout << std::endl;
769
770 if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
771 {
772 std::cerr << "Call stacks don't match" << std::endl;
773 return EXIT_FAILURE;
774 }
775 }
776
777 //////
778 // Test that the requested temporal region for the output of a temporal
779 // process object gets set to the largest possible temporal region if no
780 // temporal region has been set
781 //////
782
783 // Reset tpo1 and the requsted temporal region of tdo
784 tpo1 = TPOType::New();
785 itk::TemporalRegion emptyRegion;
786 tdo->SetRequestedTemporalRegion(emptyRegion);
787 tpo1->SetInput(tdo);
788 tpo1->UpdateOutputInformation();
789
790 // Make sure the requested temporal region of tpo1's output is empty
791 if (tpo1->GetOutput()->GetRequestedTemporalRegion() != emptyRegion)
792 {
793 std::cerr << "tpo1's output's requested temporal region not empty before propagate" << std::endl;
794 return EXIT_FAILURE;
795 }
796
797 tpo1->PropagateRequestedRegion(tpo1->GetOutput() );
798 if (tpo1->GetOutput()->GetRequestedTemporalRegion() !=
799 tpo1->GetOutput()->GetLargestPossibleTemporalRegion() ||
800 tpo1->GetOutput()->GetRequestedTemporalRegion() == emptyRegion)
801 {
802 std::cerr << "tpo1's output's requested temporal region not set correctly after propagate" << std::endl;
803 return EXIT_FAILURE;
804 }
805
806 // Test that if largest possible temporal region has infinte duration,
807 // request gets set to duration 1
808 tpo1 = TPOType::New();
809 largestRegion = tdo->GetLargestPossibleTemporalRegion();
810 largestRegion.SetFrameDuration(ITK_INFINITE_FRAME_DURATION);
811 tdo->SetLargestPossibleTemporalRegion(largestRegion);
812 tpo1->SetInput(tdo);
813 tpo1->UpdateOutputInformation();
814 tpo1->PropagateRequestedRegion(tpo1->GetOutput() );
815 if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() !=
816 ITK_INFINITE_FRAME_DURATION ||
817 tpo1->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() != 1)
818 {
819 std::cerr << "tpo1's output's temporal regions not properly set for infinite input" << std::endl;
820 std::cerr << "Requested region duration: "
821 << tpo1->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() << std::endl;
822 return EXIT_FAILURE;
823 }
824
825 //////
826 // Return successfully
827 //////
828 return EXIT_SUCCESS;
829 }
830