/*========================================================================= * * 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 itkImageSink_hxx #define itkImageSink_hxx #include "itkProgressTransformer.h" #include "itkInputDataObjectConstIterator.h" #include "itkMultiThreaderBase.h" namespace itk { template ImageSink::ImageSink() { // create default region splitter m_RegionSplitter = ImageRegionSplitterSlowDimension::New(); // Modify superclass default values, can be overridden by subclasses this->SetNumberOfRequiredInputs(1); } template void ImageSink::SetInput(const InputImageType * input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast(input)); } template auto ImageSink::GetInput() const -> const InputImageType * { return itkDynamicCastInDebugMode(this->ProcessObject::GetPrimaryInput()); } template auto ImageSink::GetInput(unsigned int idx) const -> const InputImageType * { const auto * in = dynamic_cast(this->ProcessObject::GetInput(idx)); if (in == nullptr && this->ProcessObject::GetInput(idx) != nullptr) { itkWarningMacro("Unable to convert input number " << idx << " to type " << typeid(InputImageType).name()); } return in; } template auto ImageSink::GetInput(const DataObjectIdentifierType & key) const -> const InputImageType * { const auto * in = dynamic_cast(this->ProcessObject::GetInput(key)); if (in == nullptr && this->ProcessObject::GetInput(key) != nullptr) { itkWarningMacro("Unable to convert input \"" << key << "\" to type " << typeid(InputImageType).name()); } return in; } template void ImageSink::UpdateLargestPossibleRegion() { this->Update(); } template void ImageSink::Update() { this->UpdateOutputInformation(); // if output 1, the just call it // this->PropagateRequestedRegion( nullptr ); this->UpdateOutputData(nullptr); } template void ImageSink::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "NumberOfStreamDivisions: " << this->m_NumberOfStreamDivisions << std::endl; os << indent << "RegionSplitter: " << this->m_RegionSplitter << std::endl; os << indent << "CoordinateTolerance: " << this->m_CoordinateTolerance << std::endl; os << indent << "DirectionTolerance: " << this->m_DirectionTolerance << std::endl; } template unsigned int ImageSink::GetNumberOfInputRequestedRegions() { const InputImageType * inputPtr = const_cast(this->GetInput()); InputImageRegionType inputImageRegion = inputPtr->GetLargestPossibleRegion(); return this->GetRegionSplitter()->GetNumberOfSplits(inputImageRegion, this->m_NumberOfStreamDivisions); } template void ImageSink::GenerateNthInputRequestedRegion(unsigned int inputRequestedRegionNumber) { Superclass::GenerateInputRequestedRegion(); auto * inputPtr = const_cast(this->GetInput()); InputImageRegionType inputImageRegion = inputPtr->GetLargestPossibleRegion(); this->GetRegionSplitter()->GetSplit( inputRequestedRegionNumber, this->GetNumberOfInputRequestedRegions(), inputImageRegion); m_CurrentInputRegion = inputImageRegion; itkDebugMacro("Generating " << inputRequestedRegionNumber << " chunk as " << m_CurrentInputRegion); for (auto & inputName : this->GetInputNames()) { if (this->ProcessObject::GetInput(inputName)) { // Check whether the input is an image of the appropriate // dimension (use ProcessObject's version of the GetInput() // method since it returns the input as a pointer to a // DataObject as opposed to the subclass version which // static_casts the input to an TInputImage). using ImageBaseType = ImageBase; auto * input = dynamic_cast(this->ProcessObject::GetInput(inputName)); // If not an image, skip it. A subclass can override this method // for particular input types. if (input == nullptr) { continue; } // copy the requested region of the first input to the others input->SetRequestedRegion(m_CurrentInputRegion); } } } template void ImageSink::VerifyInputInformation() ITKv5_CONST { using ImageBaseType = const ImageBase; ImageBaseType * inputPtr1 = nullptr; InputDataObjectConstIterator it(this); for (; !it.IsAtEnd(); ++it) { // Check whether the output is an image of the appropriate // dimension (use ProcessObject's version of the GetInput() // method since it returns the input as a pointer to a // DataObject as opposed to the subclass version which // static_casts the input to an TInputImage). inputPtr1 = dynamic_cast(it.GetInput()); if (inputPtr1) { break; } } for (; !it.IsAtEnd(); ++it) { auto * inputPtrN = dynamic_cast(it.GetInput()); // Physical space computation only matters if we're using two // images, and not an image and a constant. if (inputPtrN) { // check that the image occupy the same physical space, and that // each index is at the same physical location // tolerance for origin and spacing depends on the size of pixel // tolerance for directions a fraction of the unit cube. const SpacePrecisionType coordinateTol = itk::Math::abs(this->m_CoordinateTolerance * inputPtr1->GetSpacing()[0]); // use first dimension spacing if (!inputPtr1->GetOrigin().GetVnlVector().is_equal(inputPtrN->GetOrigin().GetVnlVector(), coordinateTol) || !inputPtr1->GetSpacing().GetVnlVector().is_equal(inputPtrN->GetSpacing().GetVnlVector(), coordinateTol) || !inputPtr1->GetDirection().GetVnlMatrix().is_equal(inputPtrN->GetDirection().GetVnlMatrix(), this->m_DirectionTolerance)) { std::ostringstream originString, spacingString, directionString; if (!inputPtr1->GetOrigin().GetVnlVector().is_equal(inputPtrN->GetOrigin().GetVnlVector(), coordinateTol)) { originString.setf(std::ios::scientific); originString.precision(7); originString << "InputImage Origin: " << inputPtr1->GetOrigin() << ", InputImage" << it.GetName() << " Origin: " << inputPtrN->GetOrigin() << std::endl; originString << "\tTolerance: " << coordinateTol << std::endl; } if (!inputPtr1->GetSpacing().GetVnlVector().is_equal(inputPtrN->GetSpacing().GetVnlVector(), coordinateTol)) { spacingString.setf(std::ios::scientific); spacingString.precision(7); spacingString << "InputImage Spacing: " << inputPtr1->GetSpacing() << ", InputImage" << it.GetName() << " Spacing: " << inputPtrN->GetSpacing() << std::endl; spacingString << "\tTolerance: " << coordinateTol << std::endl; } if (!inputPtr1->GetDirection().GetVnlMatrix().is_equal(inputPtrN->GetDirection().GetVnlMatrix(), this->m_DirectionTolerance)) { directionString.setf(std::ios::scientific); directionString.precision(7); directionString << "InputImage Direction: " << inputPtr1->GetDirection() << ", InputImage" << it.GetName() << " Direction: " << inputPtrN->GetDirection() << std::endl; directionString << "\tTolerance: " << this->m_DirectionTolerance << std::endl; } itkExceptionMacro("Inputs do not occupy the same physical space! " << std::endl << originString.str() << spacingString.str() << directionString.str()); } } } } template void ImageSink::StreamedGenerateData(unsigned int inputRequestedRegionNumber) { this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); // calculate the progress range for this streamed chunk const ThreadIdType total = this->GetNumberOfInputRequestedRegions(); const float oldProgress = static_cast(inputRequestedRegionNumber) / (total); const float newProgress = static_cast(inputRequestedRegionNumber + 1) / (total); ProgressTransformer pt(oldProgress, newProgress, this); this->GetMultiThreader()->template ParallelizeImageRegion( this->m_CurrentInputRegion, [this](const InputImageRegionType & inputRegionForThread) { this->ThreadedStreamedGenerateData(inputRegionForThread); }, pt.GetProcessObject()); } } // namespace itk #endif // itkImageSink_hxx