/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkVideoSource_hxx #define itkVideoSource_hxx #include "itkMultiThreaderBase.h" namespace itk { //-CONSTRUCTOR PRINT----------------------------------------------------------- // // Constructor // template VideoSource::VideoSource() { typename OutputVideoStreamType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); this->ProcessObject::SetNumberOfRequiredOutputs(1); this->ProcessObject::SetNthOutput(0, output.GetPointer()); } // // PrintSelf // template void VideoSource::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); } //-PUBLIC METHODS-------------------------------------------------------------- // // GetOutput() // template auto VideoSource::GetOutput() -> OutputVideoStreamType * { // Make sure there is at least 1 output if (this->GetNumberOfOutputs() < 1) { itkWarningMacro("No outputs set"); return nullptr; } // Return the output return this->GetOutput(0); } // // GetOutput(idx) // template TOutputVideoStream * VideoSource::GetOutput(unsigned int idx) { auto * out = dynamic_cast(this->TemporalProcessObject::GetOutput(idx)); // Make sure there is at least 1 output if (out == nullptr) { itkWarningMacro("dynamic_cast to output type failed"); } return out; } // // GraftOutput // template void VideoSource::GraftOutput(TOutputVideoStream * graft) { this->GraftNthOutput(0, graft); } // // GraftNthOutput // template void VideoSource::GraftNthOutput(unsigned int idx, TOutputVideoStream * graft) { if (idx >= this->GetNumberOfOutputs()) { itkExceptionMacro("Requested to graft output " << idx << " but this VideoSource only has " << this->GetNumberOfOutputs() << " Outputs."); } if (!graft) { itkExceptionMacro("Cannot graft from a nullptr pointer"); } // we use the process object method since all our outputs may not be of the // same type DataObject * output = this->ProcessObject::GetOutput(idx); output->Graft(graft); } // // MakeOutput // template DataObject::Pointer VideoSource::MakeOutput(DataObjectPointerArraySizeType itkNotUsed(idx)) { return OutputVideoStreamType::New().GetPointer(); } //-PROTECTED METHODS----------------------------------------------------------- // // GenerateOutputRequestedTemporalRegion // template void VideoSource::GenerateOutputRequestedTemporalRegion(TemporalDataObject * output) { // Check if requested temporal region unset bool resetNumFrames = false; TemporalRegion outputRequest = output->GetRequestedTemporalRegion(); if (!outputRequest.GetFrameDuration()) { resetNumFrames = true; } // Call superclass's version - this will set the requested temporal region Superclass::GenerateOutputRequestedTemporalRegion(this->GetOutput()); // Make sure the output has enough buffers available for the entire output // only if this request has just been matched to the largest possible spatial // region. This should only happen for filters at the end of the pipeline // since mid-pipeline filters will have their outputs' requested temporal // regions set automatically. SizeValueType requestDuration = this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration(); if (resetNumFrames && this->GetOutput()->GetNumberOfBuffers() < requestDuration) { this->GetOutput()->SetNumberOfBuffers(requestDuration); } // If requested temporal region was just set to largest possible, set the // spatial regions for every frame to the largest possible as well if (resetNumFrames) { SizeValueType frameStart = this->GetOutput()->GetRequestedTemporalRegion().GetFrameStart(); SizeValueType numFrames = this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration(); for (SizeValueType i = frameStart; i < frameStart + numFrames; ++i) { // this->GetOutput()->SetFrameRequestedSpatialRegion(i, // this->GetOutput()->GetFrameLargestPossibleSpatialRegion(i)); OutputVideoStreamType * out = this->GetOutput(); out->GetFrameLargestPossibleSpatialRegion(i); } } } // // AllocateOutputs // template void VideoSource::AllocateOutputs() { // Get the output OutputVideoStreamType * output = this->GetOutput(); // Get a list of unbuffered requested frames TemporalRegion unbufferedRegion = output->GetUnbufferedRequestedTemporalRegion(); // We don't touch the buffered temporal region here because that is handled // by the default implementation of GenerateData in TemporalProcessObject // If there are no unbuffered frames, return now SizeValueType numFrames = unbufferedRegion.GetFrameDuration(); if (numFrames == 0) { return; } // Initialize any empty frames (which will set region values from cache) output->InitializeEmptyFrames(); // Loop through the unbuffered frames and set the buffered spatial region to // match the requested spatial region then allocate the data SizeValueType startFrame = unbufferedRegion.GetFrameStart(); for (SizeValueType i = startFrame; i < startFrame + numFrames; ++i) { output->SetFrameBufferedSpatialRegion(i, output->GetFrameRequestedSpatialRegion(i)); typename OutputFrameType::Pointer frame = output->GetFrame(i); frame->SetBufferedRegion(output->GetFrameRequestedSpatialRegion(i)); frame->Allocate(); } } // // TemporalStreamingGenerateData // template void VideoSource::TemporalStreamingGenerateData() { // Call a method that can be overridden by a subclass to allocate // memory for the filter's outputs this->AllocateOutputs(); // Call a method that can be overridden by a subclass to perform // some calculations prior to splitting the main computations into // separate threads this->BeforeThreadedGenerateData(); // Set up the multithreaded processing ThreadStruct str; str.Filter = this; this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); this->GetMultiThreader()->SetSingleMethodAndExecute(this->ThreaderCallback, &str); // Call a method that can be overridden by a subclass to perform // some calculations after all the threads have completed this->AfterThreadedGenerateData(); } // // ThreadedGenerateData // template void VideoSource::ThreadedGenerateData( const typename TOutputVideoStream::SpatialRegionType & itkNotUsed(outputRegionForThread), int itkNotUsed(threadId)) { itkExceptionMacro("itk::ERROR: " << this->GetNameOfClass() << '(' << this << "): " << "Subclass should override this method!!!"); } // // SplitRequestedSpatialRegion -- Copied mostly from ImageSource // // Note: This implementation bases the spatial region split on the requested // spatial region for the current Head frame of the output. This could // potentially cause issues if frames are different sized. // template int VideoSource::SplitRequestedSpatialRegion( int i, int num, typename TOutputVideoStream::SpatialRegionType & splitRegion) { // Get the output pointer and a pointer to the first output frame OutputVideoStreamType * outputPtr = this->GetOutput(); SizeValueType currentFrame = outputPtr->GetRequestedTemporalRegion().GetFrameStart(); OutputFrameType * framePtr = outputPtr->GetFrame(currentFrame); const typename TOutputVideoStream::SizeType & requestedRegionSize = framePtr->GetRequestedRegion().GetSize(); int splitAxis; typename TOutputVideoStream::IndexType splitIndex; typename TOutputVideoStream::SizeType splitSize; // Initialize the splitRegion to the output requested region splitRegion = framePtr->GetRequestedRegion(); splitIndex = splitRegion.GetIndex(); splitSize = splitRegion.GetSize(); // split on the outermost dimension available splitAxis = framePtr->GetImageDimension() - 1; while (requestedRegionSize[splitAxis] == 1) { --splitAxis; if (splitAxis < 0) { // cannot split itkDebugMacro(" Cannot Split"); return 1; } } // determine the actual number of pieces that will be generated typename TOutputVideoStream::SizeType::SizeValueType range = requestedRegionSize[splitAxis]; int valuesPerThread; int maxThreadIdUsed; if (range == 0) { valuesPerThread = 0; maxThreadIdUsed = 0; } else { valuesPerThread = Math::Ceil(range / static_cast(num)); maxThreadIdUsed = Math::Ceil(range / static_cast(valuesPerThread)) - 1; } // Split the region if (i < maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; splitSize[splitAxis] = valuesPerThread; } if (i == maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; // last thread needs to process the "rest" dimension being split splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread; } // set the split region ivars splitRegion.SetIndex(splitIndex); splitRegion.SetSize(splitSize); itkDebugMacro(" Split Piece: " << splitRegion); return maxThreadIdUsed + 1; } // // ThreaderCallback -- Copied from ImageSource // template ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION VideoSource::ThreaderCallback(void * arg) { ThreadStruct * str; int total, workUnitID, threadCount; workUnitID = ((MultiThreaderBase::WorkUnitInfo *)(arg))->WorkUnitID; threadCount = ((MultiThreaderBase::WorkUnitInfo *)(arg))->NumberOfWorkUnits; str = (ThreadStruct *)(((MultiThreaderBase::WorkUnitInfo *)(arg))->UserData); // execute the actual method with appropriate output region // first find out how many pieces extent can be split into. typename TOutputVideoStream::SpatialRegionType splitRegion; total = str->Filter->SplitRequestedSpatialRegion(workUnitID, threadCount, splitRegion); if (workUnitID < total) { str->Filter->ThreadedGenerateData(splitRegion, workUnitID); } // else // { // otherwise don't use this thread. Sometimes the threads don't // break up very well and it is just as efficient to leave a // few threads idle. // } return ITK_THREAD_RETURN_DEFAULT_VALUE; } } // end namespace itk #endif