diff --git a/include/otbCacheLessLabelImageToVectorData.h b/include/otbCacheLessLabelImageToVectorData.h new file mode 100644 index 0000000000000000000000000000000000000000..d8fc339ed965780ce08cf6746025dc39aacca5c2 --- /dev/null +++ b/include/otbCacheLessLabelImageToVectorData.h @@ -0,0 +1,195 @@ +/*========================================================================= + + Copyright (c) Remi Cresson (IRSTEA). All rights reserved. + + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef MODULES_REMOTE_SIMPLEEXTRACTIONTOOLS_INCLUDE_OTBCACHELESSLABELIMAGETOVECTORDATA_H_ +#define MODULES_REMOTE_SIMPLEEXTRACTIONTOOLS_INCLUDE_OTBCACHELESSLABELIMAGETOVECTORDATA_H_ + +#include "otbImage.h" +#include "itkImageRegionIterator.h" +#include "itkImageRegionConstIterator.h" +#include "itkProcessObject.h" +#include "otbStreamingManager.h" +#include "otbLabelImageToVectorDataFilter.h" + +namespace otb +{ + +/** \class CacheLessLabelImageToVectorData + * \brief Produce a VectorData from an input pipeline. The pipeline is executed with + * explicit streaming and the resulting image is stored in the internal cache of the filter. + * This ensure that only the output image is cached, rather than all pipeline buffers. + * + * \ingroup SimpleExtractionTools + */ +template <class TInputImagePixel> +class ITK_EXPORT CacheLessLabelImageToVectorData : + public VectorDataSource< otb::VectorData<double> > +{ +public: + /** Standard class typedefs. */ + typedef CacheLessLabelImageToVectorData Self; + typedef VectorDataSource< VectorData<double> > Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(CacheLessLabelImageToVectorData, VectorDataSource); + + /** Some typedefs for the input. */ + typedef typename otb::Image<TInputImagePixel, 2> InputImageType; + typedef typename InputImageType::Pointer InputImagePointer; + typedef typename InputImageType::RegionType InputImageRegionType; + typedef typename InputImageType::PixelType InputImagePixelType; + typedef typename InputImageType::IndexType InputIndexType; + typedef itk::ImageRegionConstIterator<InputImageType> ConstIteratorType; + typedef itk::ImageRegionIterator<InputImageType> IteratorType; + + /** Definition of the output vector data. */ + typedef VectorData<double> VectorDataType; + typedef typename VectorDataType::Pointer VectorDataPointerType; + + typedef LabelImageToVectorDataFilter<InputImageType, double> LabelImageToVectorDataFilterType; + + /** Dimension of input image. */ + itkStaticConstMacro(InputImageDimension, unsigned int, + InputImageType::ImageDimension); + + /** Streaming manager base class pointer */ + typedef StreamingManager<InputImageType> StreamingManagerType; + typedef typename StreamingManagerType::Pointer StreamingManagerPointerType; + + /** Return the StreamingManager object responsible for dividing + * the region to write */ + StreamingManagerType* GetStreamingManager(void) + { + return m_StreamingManager; + } + + /** Set a user-specified implementation of StreamingManager + * used to divide the largest possible region in several divisions */ + void SetStreamingManager(StreamingManagerType* streamingManager) + { + m_StreamingManager = streamingManager; + } + + /** Set the streaming mode to 'stripped' and configure the number of strips + * which will be used to stream the image */ + void SetNumberOfDivisionsStrippedStreaming(unsigned int nbDivisions); + + /** Set the streaming mode to 'tiled' and configure the number of tiles + * which will be used to stream the image */ + void SetNumberOfDivisionsTiledStreaming(unsigned int nbDivisions); + + /** Set the streaming mode to 'stripped' and configure the number of strips + * which will be used to stream the image with respect to a number of line + * per strip */ + void SetNumberOfLinesStrippedStreaming(unsigned int nbLinesPerStrip); + + /** Set the streaming mode to 'stripped' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option. + * The bias parameter is a multiplier applied on the estimated memory size + * of the pipeline and can be used to fine tune the potential gap between + * estimated memory and actual memory used, which can happen because of + * composite filters for example */ + void SetAutomaticStrippedStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + /** Set the streaming mode to 'tiled' and configure the dimension of the tiles + * in pixels for each dimension (square tiles will be generated) */ + void SetTileDimensionTiledStreaming(unsigned int tileDimension); + + /** Set the streaming mode to 'tiled' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Tiles will be square. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option + * The bias parameter is a multiplier applied on the estimated memory size + * of the pipeline and can be used to fine tune the potential gap between + * estimated memory and actual memory used, which can happen because of + * composite filters for example */ + void SetAutomaticTiledStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + /** Set the streaming mode to 'adaptative' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Tiles will try to match the input file tile scheme. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option */ + void SetAutomaticAdaptativeStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + /** Set the only input of the writer */ + using Superclass::SetInput; + virtual void SetInput(const InputImageType *input); + + /** Get writer only input */ + const InputImageType* GetInput(); + + /** Override Update() from ProcessObject because this filter + * has no output. */ + void Update() ITK_OVERRIDE; + + void GenerateInputRequestedRegion() {}; + +protected: + CacheLessLabelImageToVectorData(); + ~CacheLessLabelImageToVectorData() ITK_OVERRIDE; + +private: + CacheLessLabelImageToVectorData(const CacheLessLabelImageToVectorData &); //purposely not implemented + void operator =(const CacheLessLabelImageToVectorData&); //purposely not implemented + + void ObserveSourceFilterProgress(itk::Object* object, const itk::EventObject & event ) + { + if (typeid(event) != typeid(itk::ProgressEvent)) + { + return; + } + + itk::ProcessObject* processObject = dynamic_cast<itk::ProcessObject*>(object); + if (processObject) + { + m_DivisionProgress = processObject->GetProgress(); + } + + this->UpdateFilterProgress(); + } + + void UpdateFilterProgress() + { + this->UpdateProgress( (m_DivisionProgress + m_CurrentDivision) / m_NumberOfDivisions ); + } + + unsigned int m_NumberOfDivisions; + unsigned int m_CurrentDivision; + float m_DivisionProgress; + + StreamingManagerPointerType m_StreamingManager; + + bool m_IsObserving; + unsigned long m_ObserverID; + + typename InputImageType::Pointer bufferedInputImage; + typename LabelImageToVectorDataFilterType::Pointer vectorizeFilter; +}; + +} // end namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbCacheLessLabelImageToVectorData.txx" +#endif + +#endif /* MODULES_REMOTE_SIMPLEEXTRACTIONTOOLS_INCLUDE_OTBCACHELESSLABELIMAGETOVECTORDATA_H_ */ diff --git a/include/otbCacheLessLabelImageToVectorData.txx b/include/otbCacheLessLabelImageToVectorData.txx new file mode 100644 index 0000000000000000000000000000000000000000..1f95319cc8b1e635b603b8731d523873f43842c3 --- /dev/null +++ b/include/otbCacheLessLabelImageToVectorData.txx @@ -0,0 +1,307 @@ +/*========================================================================= + + Copyright (c) Remi Cresson (IRSTEA). All rights reserved. + + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef MODULES_REMOTE_SIMPLEEXTRACTIONTOOLS_INCLUDE_OTBCACHELESSLABELIMAGETOVECTORDATA_txx_ +#define MODULES_REMOTE_SIMPLEEXTRACTIONTOOLS_INCLUDE_OTBCACHELESSLABELIMAGETOVECTORDATA_txx_ + +#include "otbCacheLessLabelImageToVectorData.h" + +#include "itkObjectFactoryBase.h" + +#include "itkImageRegionMultidimensionalSplitter.h" + +#include "itkImageRegionIterator.h" + +#include "otbNumberOfDivisionsStrippedStreamingManager.h" +#include "otbNumberOfDivisionsTiledStreamingManager.h" +#include "otbNumberOfLinesStrippedStreamingManager.h" +#include "otbRAMDrivenStrippedStreamingManager.h" +#include "otbTileDimensionTiledStreamingManager.h" +#include "otbRAMDrivenTiledStreamingManager.h" +#include "otbRAMDrivenAdaptativeStreamingManager.h" + +namespace otb +{ + +/** + * + */ +template <class TInputImagePixel> +CacheLessLabelImageToVectorData<TInputImagePixel> +::CacheLessLabelImageToVectorData() + : m_NumberOfDivisions(0), + m_CurrentDivision(0), + m_DivisionProgress(0.0), + m_IsObserving(true), + m_ObserverID(0) + { + + // By default, we use tiled streaming, with automatic tile size + // We don't set any parameter, so the memory size is retrieved from the OTB configuration options + this->SetAutomaticAdaptativeStreaming(); + } + +/** + * + */ +template <class TInputImagePixel> +CacheLessLabelImageToVectorData<TInputImagePixel> +::~CacheLessLabelImageToVectorData() +{ +} + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetNumberOfDivisionsStrippedStreaming(unsigned int nbDivisions) + { + typedef NumberOfDivisionsStrippedStreamingManager<InputImageType> NumberOfDivisionsStrippedStreamingManagerType; + typename NumberOfDivisionsStrippedStreamingManagerType::Pointer streamingManager = NumberOfDivisionsStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetNumberOfDivisionsTiledStreaming(unsigned int nbDivisions) + { + typedef NumberOfDivisionsTiledStreamingManager<InputImageType> NumberOfDivisionsTiledStreamingManagerType; + typename NumberOfDivisionsTiledStreamingManagerType::Pointer streamingManager = NumberOfDivisionsTiledStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetNumberOfLinesStrippedStreaming(unsigned int nbLinesPerStrip) + { + typedef NumberOfLinesStrippedStreamingManager<InputImageType> NumberOfLinesStrippedStreamingManagerType; + typename NumberOfLinesStrippedStreamingManagerType::Pointer streamingManager = NumberOfLinesStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfLinesPerStrip(nbLinesPerStrip); + + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetAutomaticStrippedStreaming(unsigned int availableRAM, double bias) + { + typedef RAMDrivenStrippedStreamingManager<InputImageType> RAMDrivenStrippedStreamingManagerType; + typename RAMDrivenStrippedStreamingManagerType::Pointer streamingManager = RAMDrivenStrippedStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetTileDimensionTiledStreaming(unsigned int tileDimension) + { + typedef TileDimensionTiledStreamingManager<InputImageType> TileDimensionTiledStreamingManagerType; + typename TileDimensionTiledStreamingManagerType::Pointer streamingManager = TileDimensionTiledStreamingManagerType::New(); + streamingManager->SetTileDimension(tileDimension); + + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetAutomaticTiledStreaming(unsigned int availableRAM, double bias) + { + typedef RAMDrivenTiledStreamingManager<InputImageType> RAMDrivenTiledStreamingManagerType; + typename RAMDrivenTiledStreamingManagerType::Pointer streamingManager = RAMDrivenTiledStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; + } + +template <class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetAutomaticAdaptativeStreaming(unsigned int availableRAM, double bias) + { + typedef RAMDrivenAdaptativeStreamingManager<InputImageType> RAMDrivenAdaptativeStreamingManagerType; + typename RAMDrivenAdaptativeStreamingManagerType::Pointer streamingManager = RAMDrivenAdaptativeStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; + } + +template<class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::SetInput(const InputImageType* input) + { + this->ProcessObject::SetNthInput(0,const_cast<InputImageType*>(input)); + } + +template<class TInputImagePixel> +const typename CacheLessLabelImageToVectorData<TInputImagePixel>::InputImageType* +CacheLessLabelImageToVectorData<TInputImagePixel> +::GetInput() + { + if (this->GetNumberOfInputs() < 1) + { + return ITK_NULLPTR; + } + + return static_cast<const InputImageType*>(this->ProcessObject::GetInput(0)); + } + +/** + * Update method : update output information of input and write to file + */ +template<class TInputImagePixel> +void +CacheLessLabelImageToVectorData<TInputImagePixel> +::Update() + { + // Update output information on input image + InputImagePointer inputPtr = + const_cast<InputImageType *>(this->GetInput()); + + // Make sure input is available + if ( inputPtr.IsNull() ) + { + itkExceptionMacro(<< "No input to writer"); + } + + this->SetAbortGenerateData(0); + this->SetProgress(0.0); + + /** + * Tell all Observers that the filter is starting + */ + this->InvokeEvent(itk::StartEvent()); + + /** + * Grab the input + */ + inputPtr->UpdateOutputInformation(); + InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion(); + + // Allocate the buffer image + bufferedInputImage = InputImageType::New(); + bufferedInputImage->SetRegions(inputRegion); + bufferedInputImage->Allocate(); + bufferedInputImage->SetMetaDataDictionary(inputPtr->GetMetaDataDictionary()); + bufferedInputImage->SetSpacing(inputPtr->GetSpacing()); + bufferedInputImage->SetOrigin (inputPtr->GetOrigin() ); + + + /** Compare the buffered region with the inputRegion which is the largest + * possible region or a user defined region through extended filename + * Not sure that if this modification is needed */ + if (inputPtr->GetBufferedRegion() == inputRegion) + { + otbMsgDevMacro(<< "Buffered region is the largest possible region, there is no need for streaming."); + this->SetNumberOfDivisionsStrippedStreaming(1); + } + m_StreamingManager->PrepareStreaming(inputPtr, inputRegion); + m_NumberOfDivisions = m_StreamingManager->GetNumberOfSplits(); + otbMsgDebugMacro(<< "Number Of Stream Divisions : " << m_NumberOfDivisions); + + /** + * Loop over the number of pieces, execute the upstream pipeline on each + * piece, and copy the results into the output image. + */ + InputImageRegionType streamRegion; + + this->UpdateProgress(0); + m_CurrentDivision = 0; + m_DivisionProgress = 0; + + // Get the source process object + itk::ProcessObject* source = inputPtr->GetSource(); + m_IsObserving = false; + m_ObserverID = 0; + + // Check if source exists + if(source) + { + typedef itk::MemberCommand<Self> CommandType; + typedef typename CommandType::Pointer CommandPointerType; + + CommandPointerType command = CommandType::New(); + command->SetCallbackFunction(this, &Self::ObserveSourceFilterProgress); + + m_ObserverID = source->AddObserver(itk::ProgressEvent(), command); + m_IsObserving = true; + } + else + { + itkWarningMacro(<< "Could not get the source process object. Progress report might be buggy"); + } + + for (m_CurrentDivision = 0; + m_CurrentDivision < m_NumberOfDivisions && !this->GetAbortGenerateData(); + m_CurrentDivision++, m_DivisionProgress = 0, this->UpdateFilterProgress()) + { + streamRegion = m_StreamingManager->GetSplit(m_CurrentDivision); + + inputPtr->SetRequestedRegion(streamRegion); + inputPtr->PropagateRequestedRegion(); + inputPtr->UpdateOutputData(); + + // Copy output in buffer + ConstIteratorType inIt(inputPtr, streamRegion); + IteratorType outIt(bufferedInputImage, streamRegion); + for (inIt.GoToBegin(), outIt.GoToBegin(); !inIt.IsAtEnd(); ++outIt, ++inIt) + { + outIt.Set(inIt.Get()); + } + + } + + // Vectorize the buffered image + vectorizeFilter = LabelImageToVectorDataFilterType::New(); + vectorizeFilter->SetInput(bufferedInputImage); + vectorizeFilter->SetInputMask(bufferedInputImage); + vectorizeFilter->Update(); + this->GraftOutput( vectorizeFilter->GetOutput() ); + + /** + * If we ended due to aborting, push the progress up to 1.0 (since + * it probably didn't end there) + */ + if (!this->GetAbortGenerateData()) + { + this->UpdateProgress(1.0); + } + + // Notify end event observers + this->InvokeEvent(itk::EndEvent()); + + if (m_IsObserving) + { + m_IsObserving = false; + source->RemoveObserver(m_ObserverID); + } + + /** + * Release any inputs if marked for release + */ + this->ReleaseInputs(); + + } + + +} // end namespace otb + +#endif