diff --git a/Modules/Core/Streaming/include/otbStreamingManager.h b/Modules/Core/Streaming/include/otbStreamingManager.h index cd57e83f9e7ddeda81282d35af4d048a4d837faa..0bffd7e95779c755c41e2a94a276a72360845e1d 100644 --- a/Modules/Core/Streaming/include/otbStreamingManager.h +++ b/Modules/Core/Streaming/include/otbStreamingManager.h @@ -67,6 +67,7 @@ public: typedef typename ImageType::InternalPixelType PixelType; typedef otb::PipelineMemoryPrintCalculator::MemoryPrintType MemoryPrintType; + typedef itk::ImageRegionSplitterBase AbstractSplitterType; /** Type macro */ itkTypeMacro(StreamingManager, itk::LightObject); @@ -74,6 +75,8 @@ public: /** Dimension of input image. */ itkStaticConstMacro(ImageDimension, unsigned int, ImageType::ImageDimension); + const AbstractSplitterType * GetSplitter() const; + /** Actually computes the stream divisions, according to the specified streaming mode, * eventually using the input parameter to estimate memory consumption */ virtual void PrepareStreaming(itk::DataObject * input, const RegionType ®ion) = 0; @@ -106,7 +109,6 @@ protected: RegionType m_Region; /** The splitter used to compute the different strips */ - typedef itk::ImageRegionSplitterBase AbstractSplitterType; typedef typename AbstractSplitterType::Pointer AbstractSplitterPointerType; AbstractSplitterPointerType m_Splitter; diff --git a/Modules/Core/Streaming/include/otbStreamingManager.txx b/Modules/Core/Streaming/include/otbStreamingManager.txx index 1d76ff1fe498007c5b9bb60fcc7fa80e040733b2..24e487a4e55998ee7d28b1be1a58cbda21ee8768 100644 --- a/Modules/Core/Streaming/include/otbStreamingManager.txx +++ b/Modules/Core/Streaming/include/otbStreamingManager.txx @@ -40,6 +40,13 @@ StreamingManager<TImage>::~StreamingManager() { } +template <class TImage> +const typename StreamingManager<TImage>::AbstractSplitterType * +StreamingManager<TImage>::GetSplitter() const +{ + return m_Splitter; +} + template <class TImage> typename StreamingManager<TImage>::MemoryPrintType StreamingManager<TImage>::GetActualAvailableRAMInBytes(MemoryPrintType availableRAMInMB) diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.h b/Modules/IO/ImageIO/include/otbImageFileWriter.h index cbc4c6c4e9a7fb49c3cdfe8821fec27ca84ea189..169e946c7579a241c7bed42424c561101d608912 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.h +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.h @@ -213,6 +213,9 @@ protected: /** Does the real work. */ void GenerateData(void) override; + /** Prepare the streaming and write the output information on disk */ + void GenerateOutputInformation(void) override; + private: ImageFileWriter(const ImageFileWriter &); //purposely not implemented void operator =(const ImageFileWriter&); //purposely not implemented diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.txx b/Modules/IO/ImageIO/include/otbImageFileWriter.txx index 2ddaf018bf89a48109c86a902d2c6fa57c88089b..6cfff64f8ae1df38f26344110537bb1c47d0336c 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.txx +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.txx @@ -263,13 +263,11 @@ ImageFileWriter<TInputImage> return static_cast<const InputImageType*>(this->ProcessObject::GetInput(0)); } -/** - * Update method : update output information of input and write to file - */ +/** Prepare everything and call m_ImageIO.WriteInformation() */ template<class TInputImage> void ImageFileWriter<TInputImage> -::Update() +::GenerateOutputInformation(void) { // Update output information on input image InputImagePointer inputPtr = @@ -398,14 +396,6 @@ ImageFileWriter<TInputImage> } } - this->SetAbortGenerateData(0); - this->SetProgress(0.0); - - /** - * Tell all Observers that the filter is starting - */ - this->InvokeEvent(itk::StartEvent()); - /** Prepare ImageIO : create ImageFactory */ if (m_FileName == "") @@ -477,7 +467,6 @@ ImageFileWriter<TInputImage> /** * Grab the input */ - inputPtr->UpdateOutputInformation(); InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion(); /** Parse region size modes */ @@ -541,12 +530,6 @@ ImageFileWriter<TInputImage> const auto firstSplitSize = m_StreamingManager->GetSplit(0).GetSize(); otbLogMacro(Info,<<"File "<<m_FileName<<" will be written in "<<m_NumberOfDivisions<<" blocks of "<<firstSplitSize[0]<<"x"<<firstSplitSize[1]<<" pixels"); - /** - * Loop over the number of pieces, execute the upstream pipeline on each - * piece, and copy the results into the output image. - */ - InputImageRegionType streamRegion; - // // Setup the ImageIO with information from inputPtr // @@ -586,12 +569,33 @@ ImageFileWriter<TInputImage> m_ImageIO->SetFileName(m_FileName.c_str()); m_ImageIO->WriteImageInformation(); +} +/** + * Update method : update output information of input and write to file + */ +template<class TInputImage> +void +ImageFileWriter<TInputImage> +::Update() +{ + this->UpdateOutputInformation(); + + this->SetAbortGenerateData(0); + this->SetProgress(0.0); + + /** + * Tell all Observers that the filter is starting + */ + this->InvokeEvent(itk::StartEvent()); + this->UpdateProgress(0); m_CurrentDivision = 0; m_DivisionProgress = 0; // Get the source process object + InputImagePointer inputPtr = + const_cast<InputImageType *>(this->GetInput()); itk::ProcessObject* source = inputPtr->GetSource(); m_IsObserving = false; m_ObserverID = 0; @@ -613,6 +617,12 @@ ImageFileWriter<TInputImage> otbLogMacro(Warning,<< "Could not get the source process object. Progress report might be buggy"); } + /** + * Loop over the number of pieces, execute the upstream pipeline on each + * piece, and copy the results into the output image. + */ + InputImageRegionType streamRegion; + for (m_CurrentDivision = 0; m_CurrentDivision < m_NumberOfDivisions && !this->GetAbortGenerateData(); m_CurrentDivision++, m_DivisionProgress = 0, this->UpdateFilterProgress()) diff --git a/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..0d2a1fc8aff8655f6f07b0af42794fe3516b1541 --- /dev/null +++ b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 otbMultiImageFileWriter_h +#define otbMultiImageFileWriter_h + +#include "otbImageFileWriter.h" +#include "otbImage.h" +#include "itkImageBase.h" +#include "itkProcessObject.h" +#include "itkImageIOBase.h" + +#include <boost/shared_ptr.hpp> + +namespace otb +{ + +/** \class MultiImageFileWriter + * \brief Streams a pipeline with multiple outputs. + * It writes each output to a file. Inputs + * are connected to the writer using the AddInputImage method. + * The type of streaming can be chosen. Each output may have a different size. + * When the user gives a number of lines per strip or a tile size, the value + * is interpreted on the first input to deduce the number of streams. This + * number of streams is then used to split the other inputs. + */ +class MultiImageFileWriter: public itk::ProcessObject +{ +public: + /** Standard class typedefs. */ + typedef MultiImageFileWriter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + itkNewMacro(Self); + + itkTypeMacro(MultiImageFileWriter, itk::ProcessObject); + + /** Public typedefs */ + typedef itk::ImageBase<2> ImageBaseType; + typedef ImageBaseType::RegionType RegionType; + typedef ImageBaseType::IndexType IndexType; + typedef ImageBaseType::IndexValueType IndexValueType; + typedef ImageBaseType::SizeType SizeType; + typedef ImageBaseType::SizeValueType SizeValueType; + + /** 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); + + virtual void UpdateOutputData(itk::DataObject * itkNotUsed(output)); + + /** Connect a new input to the multi-writer. Only the input pointer is + * required. If the filename list is empty, + * streaming will occur without writing. It the filename list contains more + * than one element, then the output will be divided into this number of + * granule files. The resolution factor specifies the ratio between the height of this image and the + * height of a reference image. The number of lines per strip class parameter will be modified according to this factor, so + * that images with different resolutions can be streamed together. For example, use 1.0 for a 10m pixels image, 0.5 for a 20m + * pixels image, and specify the number of lines per strip according to the 10m pixels image. + * You may specify top and bottom margins that will be removed from the input image, according to its largest possible region. + */ + template <class TImage> + void AddInputImage(const TImage* inputPtr, const std::string & fileName) + { + Sink<TImage> * sink = new Sink<TImage>(inputPtr, fileName); + m_SinkList.push_back(SinkBase::Pointer(sink)); + unsigned int size = m_SinkList.size(); + this->SetNthInput(size - 1, const_cast<itk::DataObject*>(dynamic_cast<const itk::DataObject*>(inputPtr))); + } + + /** Add a new ImageFileWriter to the multi-writer. This is an alternative method + * when you already have an instanciated writer. + */ + template <class TWriter> + void AddInputWriter(const TWriter* writer) + { + Sink<typename TWriter::InputImageType > * sink = + new Sink<typename TWriter::InputImageType >(writer); + m_SinkList.push_back(SinkBase::Pointer(sink)); + unsigned int size = m_SinkList.size(); + this->SetNthInput(size - 1, const_cast<itk::DataObject*>(dynamic_cast<const itk::DataObject*>(writer->GetInput()))); + } + + virtual void UpdateOutputInformation(); + + virtual void Update() + { + this->UpdateOutputInformation(); + this->UpdateOutputData(NULL); + } + +protected: + /** SetInput is changed to protected. Use AddInputImage to connect the + * pipeline to the writer + */ + virtual void SetInput(const itk::ProcessObject::DataObjectIdentifierType & key, itk::DataObject* image) + { this->Superclass::SetInput(key, image); } + + /** SetNthInput is changed to protected. Use AddInputImage to connect the + * pipeline to the writer + */ + virtual void SetNthInput(itk::ProcessObject::DataObjectPointerArraySizeType i, itk::DataObject* image) + { this->Superclass::SetNthInput(i, image); } + + MultiImageFileWriter(); + virtual ~MultiImageFileWriter() {} + + /** GenerateData calls the Write method for each connected input */ + virtual void GenerateData(void); + + /** GenerateInputRequestedRegion can predict approximate input regions + * based on the requested region of the fake output. Only usefull for + * pipeline memory print estimation + */ + virtual void GenerateInputRequestedRegion(); + + /** Computes the number of divisions */ + virtual void InitializeStreaming(); + + /** Goes up the pipeline starting at imagePtr, resetting all requested regions + * to a null region. This may be usefull when mixing inputs of different + * resolutions. */ + void ResetAllRequestedRegions(ImageBaseType* imagePtr); + + /** Returns the current stream region of the given input */ + virtual RegionType GetStreamRegion(int inputIndex); + + void operator =(const MultiImageFileWriter&); //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); + } + +private: + typedef otb::Image<unsigned char, 2> FakeOutputType; + typedef StreamingManager<FakeOutputType> StreamingManagerType; + /** Streaming manager used for the N inputs */ + StreamingManagerType::Pointer m_StreamingManager; + + //Division parameters + unsigned int m_NumberOfDivisions; + unsigned int m_CurrentDivision; + float m_DivisionProgress; + + bool m_IsObserving; + unsigned long m_ObserverID; + + /** Internal base wrapper class to handle each ImageFileWriter */ + class SinkBase + { + public: + SinkBase() {} + SinkBase(ImageBaseType::ConstPointer inputImage) : + m_InputImage(inputImage) + {} + virtual ~SinkBase() {} + virtual ImageBaseType::ConstPointer GetInput() const { return m_InputImage; } + virtual ImageBaseType::Pointer GetInput() { return const_cast<ImageBaseType*>(m_InputImage.GetPointer()); } + virtual void WriteImageInformation() = 0; + virtual void Write(const RegionType & streamRegion) = 0; + virtual bool CanStreamWrite() = 0; + typedef boost::shared_ptr<SinkBase> Pointer; + protected: + /** The image on which streaming is performed */ + ImageBaseType::ConstPointer m_InputImage; + }; + + /** \class Sink + * Wrapper class for each ImageFileWriter + */ + template <class TImage> + class Sink : public SinkBase + { + public: + Sink() {} + Sink(typename TImage::ConstPointer inputImage, + const std::string & filename); + Sink(typename otb::ImageFileWriter<TImage>::ConstPointer writer); + + virtual ~Sink() {} + + virtual void WriteImageInformation(); + virtual void Write(const RegionType & streamRegion); + virtual bool CanStreamWrite(); + typedef boost::shared_ptr<Sink> Pointer; + private: + /** Actual writer for this image */ + typename otb::ImageFileWriter<TImage>::Pointer m_Writer; + + /** An ImageIO used to actually write data to a file */ + otb::ImageIOBase::Pointer m_ImageIO; + }; + + /** The list of inputs and their associated parameters, built using AddInput */ + typedef std::vector<boost::shared_ptr<SinkBase> > SinkListType; + SinkListType m_SinkList; + + std::vector<RegionType> m_StreamRegionList; +}; + +} // end of namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbMultiImageFileWriter.txx" +#endif + +#endif // otbMultiImageFileWriter_h diff --git a/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx new file mode 100644 index 0000000000000000000000000000000000000000..5e6d6304dc52aa3dc95a497b45264ff2cc15d277 --- /dev/null +++ b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx @@ -0,0 +1,89 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 otbMultiImageFileWriter_txx +#define otbMultiImageFileWriter_txx + +#include "otbMultiImageFileWriter.h" +#include "otbImageIOFactory.h" +#include "otbMacro.h" + +namespace otb +{ + +template <class TImage> +MultiImageFileWriter::Sink<TImage> +::Sink(typename TImage::ConstPointer inputImage, + const std::string & fileName): + SinkBase(dynamic_cast<const ImageBaseType*>(inputImage.GetPointer())), + m_Writer(otb::ImageFileWriter<TImage>::New()), + m_ImageIO(NULL) +{ + m_Writer->SetFileName(fileName); + m_Writer->SetInput(inputImage); +} + +template <class TImage> +MultiImageFileWriter::Sink<TImage> +::Sink(typename otb::ImageFileWriter<TImage>::ConstPointer writer): + SinkBase(dynamic_cast<const ImageBaseType*>(writer->GetInput()->GetPointer())), + m_Writer(writer), + m_ImageIO(NULL) +{ +} + +template <class TImage> +bool +MultiImageFileWriter::Sink<TImage> +::CanStreamWrite() +{ + if (m_ImageIO.IsNull()) + return false; + return m_ImageIO->CanStreamWrite(); +} + +template <class TImage> +void +MultiImageFileWriter::Sink<TImage> +::WriteImageInformation() +{ + m_Writer->UpdateOutputInformation(); + m_ImageIO = m_Writer->GetImageIO(); +} + +template <class TImage> +void +MultiImageFileWriter::Sink<TImage> +::Write(const RegionType & streamRegion) +{ + // Write the image stream + itk::ImageIORegion ioRegion(TImage::ImageDimension); + for (unsigned int i = 0; i < TImage::ImageDimension; ++i) + { + ioRegion.SetSize(i, streamRegion.GetSize(i)); + ioRegion.SetIndex(i, streamRegion.GetIndex(i)); + } + m_ImageIO->SetIORegion(ioRegion); + m_Writer->UpdateOutputData(nullptr); +} + +} // end of namespace otb + +#endif // otbMultiImageFileWriter_txx diff --git a/Modules/IO/ImageIO/src/CMakeLists.txt b/Modules/IO/ImageIO/src/CMakeLists.txt index 2d580c6541b14e92fb19e500276f5146103257e0..e604942a7cd4b8473e595bd457dfbdac05523938 100644 --- a/Modules/IO/ImageIO/src/CMakeLists.txt +++ b/Modules/IO/ImageIO/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(OTBImageIO_SRC otbImageIOFactory.cxx + otbMultiImageFileWriter.cxx ) add_library(OTBImageIO ${OTBImageIO_SRC}) diff --git a/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx b/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a3f32a8d36bd404a271c2fd58d61b94488faf981 --- /dev/null +++ b/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx @@ -0,0 +1,443 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "otbMultiImageFileWriter.h" +#include "otbImageIOFactory.h" + +namespace otb +{ + +MultiImageFileWriter +::MultiImageFileWriter() : + 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(); + // add a fake output to drive memory estimation + this->SetNthOutput(0, FakeOutputType::New()); +} + +void +MultiImageFileWriter +::SetNumberOfDivisionsStrippedStreaming(unsigned int nbDivisions) +{ + typedef NumberOfDivisionsStrippedStreamingManager<FakeOutputType> NumberOfDivisionsStrippedStreamingManagerType; + NumberOfDivisionsStrippedStreamingManagerType::Pointer streamingManager = + NumberOfDivisionsStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetNumberOfDivisionsTiledStreaming(unsigned int nbDivisions) +{ + typedef NumberOfDivisionsTiledStreamingManager<FakeOutputType> NumberOfDivisionsTiledStreamingManagerType; + NumberOfDivisionsTiledStreamingManagerType::Pointer streamingManager = + NumberOfDivisionsTiledStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetNumberOfLinesStrippedStreaming(unsigned int nbLinesPerStrip) +{ + typedef NumberOfLinesStrippedStreamingManager<FakeOutputType> NumberOfLinesStrippedStreamingManagerType; + NumberOfLinesStrippedStreamingManagerType::Pointer streamingManager = + NumberOfLinesStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfLinesPerStrip(nbLinesPerStrip); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticStrippedStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenStrippedStreamingManager<FakeOutputType> RAMDrivenStrippedStreamingManagerType; + RAMDrivenStrippedStreamingManagerType::Pointer streamingManager = + RAMDrivenStrippedStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetTileDimensionTiledStreaming(unsigned int tileDimension) +{ + typedef TileDimensionTiledStreamingManager<FakeOutputType> TileDimensionTiledStreamingManagerType; + TileDimensionTiledStreamingManagerType::Pointer streamingManager = + TileDimensionTiledStreamingManagerType::New(); + streamingManager->SetTileDimension(tileDimension); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticTiledStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenTiledStreamingManager<FakeOutputType> RAMDrivenTiledStreamingManagerType; + RAMDrivenTiledStreamingManagerType::Pointer streamingManager = + RAMDrivenTiledStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticAdaptativeStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenAdaptativeStreamingManager<FakeOutputType> RAMDrivenAdaptativeStreamingManagerType; + RAMDrivenAdaptativeStreamingManagerType::Pointer streamingManager = + RAMDrivenAdaptativeStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::InitializeStreaming() +{ +// const ImageBaseType* inputPtr = this->GetInput(0); + if(m_SinkList.size() == 0) + itkExceptionMacro("At least one input must be connected to the writer\n"); + const ImageBaseType* inputPtr = m_SinkList[0]->GetInput(); + if(!inputPtr) + itkExceptionMacro("At least one input must be connected to the writer\n"); + + /** Control if the ImageIO is CanStreamWrite */ + m_NumberOfDivisions = 1; + bool canStream = true; + bool isBuffered = true; + for (unsigned int inputIndex = 0; inputIndex < m_SinkList.size(); ++inputIndex) + { + if (!m_SinkList[inputIndex]->CanStreamWrite()) + { + canStream = false; + } + if (m_SinkList[inputIndex]->GetInput()->GetBufferedRegion() != + m_SinkList[inputIndex]->GetInput()->GetLargestPossibleRegion()) + { + isBuffered = false; + } + } + if (canStream == false) + { + otbWarningMacro( + << "One of the selected ImageIO does not support streaming."); + this->SetNumberOfDivisionsStrippedStreaming(m_NumberOfDivisions); + } + + /** 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 */ + else if (isBuffered) + { + otbMsgDevMacro(<< "Buffered region is the largest possible region, there is" + " no need for streaming."); + this->SetNumberOfDivisionsStrippedStreaming(m_NumberOfDivisions); + } + else + { + /** + * Determine of number of pieces to divide the input. This will be the + * first estimated on the fake output, which has the same size as the + * first input. Then there is a check that each input can be split into + * this number of pieces. + */ + FakeOutputType * fakeOut = static_cast<FakeOutputType *>( + this->itk::ProcessObject::GetOutput(0)); + RegionType region = fakeOut->GetLargestPossibleRegion(); + m_StreamingManager->PrepareStreaming(fakeOut, region); + m_NumberOfDivisions = m_StreamingManager->GetNumberOfSplits(); + // Check this number of division is compatible with all inputs + bool nbDivValid = false; + while ( (!nbDivValid) && 1 < m_NumberOfDivisions) + { + unsigned int smallestNbDiv = m_NumberOfDivisions; + for (unsigned int i = 0; i < m_SinkList.size(); ++i) + { + unsigned int div = m_StreamingManager->GetSplitter()->GetNumberOfSplits( + m_SinkList[i]->GetInput()->GetLargestPossibleRegion(), + m_NumberOfDivisions); + smallestNbDiv = std::min(div, smallestNbDiv); + } + if (smallestNbDiv == m_NumberOfDivisions) + { + nbDivValid = true; + } + else + { + m_NumberOfDivisions = smallestNbDiv; + } + } + if (m_NumberOfDivisions == 1) + { + otbWarningMacro("Can't find a common split scheme between all inputs, streaming disabled\n"); + } + otbMsgDebugMacro(<< "Number Of Stream Divisions : " << m_NumberOfDivisions); + } +} + +void +MultiImageFileWriter +::ResetAllRequestedRegions(ImageBaseType* imagePtr) +{ + RegionType nullRegion = imagePtr->GetLargestPossibleRegion(); + nullRegion.SetSize(0, 0); + nullRegion.SetSize(1, 0); + + imagePtr->SetRequestedRegion(nullRegion); + if(imagePtr->GetSource()) + { + itk::ProcessObject::DataObjectPointerArray inputs = imagePtr->GetSource()->GetInputs(); + for( itk::ProcessObject::DataObjectPointerArray::iterator + it = inputs.begin(); + it != inputs.end(); + ++it ) + { + ImageBaseType * inputImagePtr = dynamic_cast<ImageBaseType*>(it->GetPointer()); + if(inputImagePtr != NULL) + { + ResetAllRequestedRegions(inputImagePtr); + } + } + } +} + +void +MultiImageFileWriter +::UpdateOutputInformation() +{ + for(unsigned int inputIndex = 0; inputIndex < m_SinkList.size(); ++inputIndex) + { + m_SinkList[inputIndex]->WriteImageInformation(); + } + this->GenerateOutputInformation(); +} + +void +MultiImageFileWriter +::UpdateOutputData(itk::DataObject * itkNotUsed(output)) +{ + /** + * prevent chasing our tail + */ + if (this->m_Updating) + { + return; + } + + // Initialize streaming + this->InitializeStreaming(); + + this->SetAbortGenerateData(0); + this->SetProgress(0.0); + this->m_Updating = true; + + /** + * Tell all Observers that the filter is starting + */ + this->InvokeEvent(itk::StartEvent()); + + this->UpdateProgress(0); + m_CurrentDivision = 0; + m_DivisionProgress = 0; + + /** Loop over the number of pieces, set and propagate requested regions then + * update pipeline upstream + */ + int numInputs = m_SinkList.size(); //this->GetNumberOfInputs(); + m_StreamRegionList.resize(numInputs); + itkDebugMacro( "Number of streaming divisions: " << m_NumberOfDivisions); + + // Add observer only to first input + if(numInputs > 0) + { + m_IsObserving = false; + m_ObserverID = 0; + + typedef itk::MemberCommand<Self> CommandType; + typedef CommandType::Pointer CommandPointerType; + + CommandPointerType command = CommandType::New(); + command->SetCallbackFunction(this, &Self::ObserveSourceFilterProgress); + + itk::ProcessObject* src = this->GetInput(0)->GetSource(); + m_ObserverID = src->AddObserver(itk::ProgressEvent(), command); + m_IsObserving = true; + } + + for (m_CurrentDivision = 0; m_CurrentDivision < m_NumberOfDivisions && !this->GetAbortGenerateData(); + m_CurrentDivision++, m_DivisionProgress = 0, this->UpdateFilterProgress()) + { + // Update all stream regions + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + m_StreamRegionList[inputIndex] = GetStreamRegion(inputIndex); + } + + // NOTE : this reset was probably designed to work with the next section + // Where the final requested region is the "union" between the computed + // requested region and the current requested region. + + // Reset requested regions for all images + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + ResetAllRequestedRegions(m_SinkList[inputIndex]->GetInput()); + } + + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + ImageBaseType::Pointer inputPtr = m_SinkList[inputIndex]->GetInput(); + RegionType inputRequestedRegion = m_StreamRegionList[inputIndex]; + const RegionType & currentInputRequestedRegion = inputPtr->GetRequestedRegion(); + if( currentInputRequestedRegion != inputPtr->GetLargestPossibleRegion() + && currentInputRequestedRegion.GetNumberOfPixels() != 0) + { + IndexType startIndex = currentInputRequestedRegion.GetIndex(); + IndexType lastIndex = currentInputRequestedRegion.GetUpperIndex(); + startIndex[0] = std::min(startIndex[0], inputRequestedRegion.GetIndex(0)); + startIndex[1] = std::min(startIndex[1], inputRequestedRegion.GetIndex(1)); + lastIndex[0] = std::max(lastIndex[0], inputRequestedRegion.GetUpperIndex()[0]); + lastIndex[1] = std::max(lastIndex[1], inputRequestedRegion.GetUpperIndex()[1]); + inputRequestedRegion.SetIndex(startIndex); + inputRequestedRegion.SetUpperIndex(lastIndex); + } + + inputPtr->SetRequestedRegion(inputRequestedRegion); + inputPtr->PropagateRequestedRegion(); + } + + /** Call GenerateData to write streams to files if needed */ + this->GenerateData(); + } + + /** + * 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) + { + ImageBaseType::Pointer inputPtr = m_SinkList[0]->GetInput(); // const_cast<ImageBaseType *>(this->GetInput(0)); + itk::ProcessObject* source = inputPtr->GetSource(); + m_IsObserving = false; + source->RemoveObserver(m_ObserverID); + } + + /** + * Release any inputs if marked for release + */ + this->ReleaseInputs(); + + // Mark that we are no longer updating the data in this filter + this->m_Updating = false; +} + + +void +MultiImageFileWriter +::GenerateInputRequestedRegion() +{ + Superclass::GenerateInputRequestedRegion(); + + // Approximate conversion of output requested region into each input, + // but this is only to have a consistent pipeline memory estimation. + int numInputs = m_SinkList.size(); //this->GetNumberOfInputs(); + + FakeOutputType* fakeOut = static_cast<FakeOutputType *>( + this->itk::ProcessObject::GetOutput(0)); + + if (numInputs) + { + RegionType refLargest = fakeOut->GetLargestPossibleRegion(); + RegionType refRequest = fakeOut->GetRequestedRegion(); + IndexType idxLargest = refLargest.GetIndex(); + SizeType sizeLargest = refLargest.GetSize(); + IndexType idxRequest = refRequest.GetIndex(); + SizeType sizeRequest = refRequest.GetSize(); + for (int i = 0; i < numInputs; ++i) + { + ImageBaseType* inputPtr = m_SinkList[i]->GetInput(); + if(!inputPtr) + { + return; + } + RegionType region = inputPtr->GetLargestPossibleRegion(); + IndexType idx = region.GetIndex(); + SizeType size = region.GetSize(); + idx[0] += size[0] * (idxRequest[0] - idxLargest[0]) / sizeLargest[0]; + idx[1] += size[1] * (idxRequest[1] - idxLargest[1]) / sizeLargest[1]; + size[0] *= sizeRequest[0] / sizeLargest[0]; + size[1] *= sizeRequest[1] / sizeLargest[1]; + region.SetIndex(idx); + region.SetSize(size); + inputPtr->SetRequestedRegion(region); + } + } +} + +void +MultiImageFileWriter +::GenerateData() +{ + int numInputs = m_SinkList.size(); + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + m_SinkList[inputIndex]->Write(m_StreamRegionList[inputIndex]); + } +} + +MultiImageFileWriter::RegionType +MultiImageFileWriter +::GetStreamRegion(int inputIndex) +{ + const SinkBase::Pointer sink = m_SinkList[inputIndex]; + RegionType region = sink->GetInput()->GetLargestPossibleRegion(); + + m_StreamingManager->GetSplitter()->GetSplit( + m_CurrentDivision, + m_NumberOfDivisions, + region); + return region; +} + +} // end of namespace otb diff --git a/Modules/IO/ImageIO/test/CMakeLists.txt b/Modules/IO/ImageIO/test/CMakeLists.txt index aac49fcdeab48d245227689b5aeb6461f6d6dd07..110b25410e8bfd8f663a505fa202b4399d213609 100644 --- a/Modules/IO/ImageIO/test/CMakeLists.txt +++ b/Modules/IO/ImageIO/test/CMakeLists.txt @@ -76,6 +76,7 @@ otbImageIOFactoryNew.cxx otbCompareWritingComplexImage.cxx otbImageFileReaderOptBandTest.cxx otbImageFileWriterOptBandTest.cxx +otbMultiImageFileWriterTest.cxx ) add_executable(otbImageIOTestDriver ${OTBImageIOTests}) @@ -1339,3 +1340,31 @@ otb_add_test(NAME ioTvImageIOToWriterOptions_OptBandReorgTest COMMAND otbImageIO ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBandReorg.tif?bands=2,:,-3,2:-1 4 ) + +otb_add_test(NAME ioTvMultiImageFileWriter_SameSize + COMMAND otbImageIOTestDriver + --compare-n-images ${EPSILON_9} 2 + ${INPUTDATA}/GomaAvant.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize1.tif + ${INPUTDATA}/GomaApres.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize2.tif + otbMultiImageFileWriterTest + ${INPUTDATA}/GomaAvant.png + ${INPUTDATA}/GomaApres.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize1.tif + ${TEMP}/ioTvMultiImageFileWriter_SameSize2.tif + 50) + +otb_add_test(NAME ioTvMultiImageFileWriter_DiffSize + COMMAND otbImageIOTestDriver + --compare-n-images ${EPSILON_9} 2 + ${INPUTDATA}/GomaAvant.png + ${TEMP}/ioTvMultiImageFileWriter_DiffSize1.tif + ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize2.tif + otbMultiImageFileWriterTest + ${INPUTDATA}/GomaAvant.png + ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize1.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize2.tif + 25) diff --git a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx index fb2aa583aa73653ece1ffb9f523ae6caaec71d84..4a7ff9409a53d34ec7d404c6f6d56cc57c8917bf 100644 --- a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx +++ b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx @@ -154,4 +154,5 @@ void RegisterTests() REGISTER_TEST(otbCompareWritingComplexImageTest); REGISTER_TEST(otbImageFileReaderOptBandTest); REGISTER_TEST(otbImageFileWriterOptBandTest); + REGISTER_TEST(otbMultiImageFileWriterTest); } diff --git a/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx b/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..77d6ede243b19c34694ff18a74959457c060a0d0 --- /dev/null +++ b/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx @@ -0,0 +1,65 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "otbMultiImageFileWriter.h" +#include "otbImage.h" +#include "otbImageFileReader.h" + +int otbMultiImageFileWriterTest(int argc, char* argv[]) +{ + typedef unsigned short PixelType1; + typedef otb::Image<PixelType1, 2> ImageType1; + typedef otb::ImageFileReader<ImageType1> ReaderType1; + + typedef double PixelType2; + typedef otb::Image<PixelType2, 2> ImageType2; + typedef otb::ImageFileReader<ImageType2> ReaderType2; + + typedef otb::MultiImageFileWriter WriterType; + + if (argc < 6) + { + std::cout << "Usage: " << argv[0] << " inputImageFileName1 inputImageFileName2 outputImageFileName1 outputImageFileName2 numberOfLinesPerStrip\n"; + return EXIT_FAILURE; + } + + const char * inputImageFileName1 = argv[1]; + const char * inputImageFileName2 = argv[2]; + const std::string outputImageFileName1 = argv[3]; + const std::string outputImageFileName2 = argv[4]; + const int numberOfLinesPerStrip = atoi(argv[5]); + + ReaderType1::Pointer reader1 = ReaderType1::New(); + reader1->SetFileName( inputImageFileName1 ); + + ReaderType2::Pointer reader2 = ReaderType2::New(); + reader2->SetFileName( inputImageFileName2 ); + + WriterType::Pointer writer = WriterType::New(); + writer->AddInputImage( reader1->GetOutput(), outputImageFileName1); + writer->AddInputImage( reader2->GetOutput(), outputImageFileName2); + writer->SetNumberOfLinesStrippedStreaming( numberOfLinesPerStrip ); + + writer->Update(); + + std::cout << writer << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h index ecec379e6d0cdaab2a6b39817b61ccd1a533c524..1800dc62ae4a170c70f0039906c79f2b2093e40d 100644 --- a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h +++ b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h @@ -47,232 +47,102 @@ namespace otb { /** - *\brief Write image data to multiple files with MPI processus and add a VRT file. + * \class MPIVrtWriter * - * The image is divided into several pieces. - * Each pieces is distributed to a MPI processus. - * Each MPI processus write their pieces into a separate - * file. - * The master processus writes a VRT file (optional). + * \brief Write each tile of an image into a separate Tiff file and join them in a VRT * - *\param img Image - *\param output Output Filename - *\param availableRAM Available memory for streaming - *\param writeVRTFile Activate the VRT file writing + * \ingroup OTBMPIVrtWriter */ -template <typename TImage> void WriteMPI(TImage *img, const std::string &output, unsigned int availableRAM = 0, bool writeVRTFile=true) +template <typename TImage> +class MPIVrtWriter: public itk::ProcessObject { - typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); - - unsigned int myRank = mpiConfig->GetMyRank(); - unsigned int nbProcs = mpiConfig->GetNbProcs(); - - typedef otb::ImageFileWriter<TImage> WriterType; - typedef otb::NumberOfDivisionsTiledStreamingManager<TImage> StreamingManagerType; - typedef itk::RegionOfInterestImageFilter<TImage, TImage> ExtractFilterType; - - // First, update infomration from image to write - img->UpdateOutputInformation(); - - // Configure streaming manager - typename StreamingManagerType::Pointer streamingManager = StreamingManagerType::New(); - streamingManager->SetNumberOfDivisions(nbProcs); - streamingManager->PrepareStreaming(img,img->GetLargestPossibleRegion()); - unsigned int numberOfSplits = streamingManager->GetNumberOfSplits(); - - // This vector will hold all regions to write for current rank - std::vector<typename TImage::RegionType> regionsToWrite; - - // Handle both cases when there are much more (resp. less) region to - // write than NbProcs - if(myRank < numberOfSplits) - { - unsigned int splitIdx = myRank; - while(splitIdx < numberOfSplits) - { - typename TImage::RegionType currentRegion = streamingManager->GetSplit(splitIdx); - regionsToWrite.push_back(currentRegion); - splitIdx+=nbProcs; - } - } - - // Output prefix - std::string extension = itksys::SystemTools::GetFilenameExtension(output); - if (extension != ".vrt") - { - - // TODO: Maybe remove this - if (extension == "") - { - // Missing extension - mpiConfig->logInfo("Filename has no extension. Adding <.vrt> extension."); - } - else - { - // Bad extension - mpiConfig->logError("Filename must have .vrt extension!"); - mpiConfig->abort(EXIT_FAILURE); - } - } - std::vector<std::string> joins; - joins.push_back(itksys::SystemTools::GetFilenamePath(output).append("/")); - joins.push_back(itksys::SystemTools::GetFilenameWithoutExtension(output)); - std::string prefix = itksys::SystemTools::JoinPath(joins); - +public: + /** Standard class typedefs. */ + typedef MPIVrtWriter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; - // Data type - std::string dataTypeStr = "Float32"; - GDALImageIO::Pointer gdalImageIO; + typedef TImage InputImageType; - // Now write all the regions - for(typename std::vector<typename TImage::RegionType>::const_iterator it = regionsToWrite.begin(); - it!=regionsToWrite.end();++it) - { - typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); - extractFilter->SetInput(img); - extractFilter->SetRegionOfInterest(*it); - // Writer - // Output Filename - std::stringstream ss; - ss<<prefix<<"_"<<it->GetIndex()[0]<<"_"<<it->GetIndex()[1]<<"_"<<it->GetSize()[0]<<"_"<<it->GetSize()[1]<<".tif"; - typename WriterType::Pointer writer = WriterType::New(); - writer->SetFileName(ss.str()); - writer->SetInput(extractFilter->GetOutput()); - // Datatype - gdalImageIO = dynamic_cast<GDALImageIO *>(writer->GetImageIO()); - if(gdalImageIO.IsNotNull()) - { - dataTypeStr = gdalImageIO->GetGdalPixelTypeAsString(); - } + /** Method for creation through the object factory. */ + itkNewMacro(Self); - if (!availableRAM) - { - writer->SetNumberOfDivisionsTiledStreaming(0); - } - else - { - writer->SetAutomaticAdaptativeStreaming(availableRAM); - } + /** Run-time type information (and related methods). */ + itkTypeMacro(MPIVrtWriter, itk::ProcessObject); - // Pipeline execution - try - { - writer->Update(); - } - catch (itk::ExceptionObject& err) - { - std::stringstream message; - message << "ExceptionObject caught: " << err << std::endl; - mpiConfig->logError(message.str()); - mpiConfig->abort(EXIT_FAILURE); - } - } + using Superclass::SetInput; + virtual void SetInput(const InputImageType *input); - // MPI process synchronization - mpiConfig->barrier(); + /** Get writer only input */ + const InputImageType* GetInput(); - // Write VRT file - try - { - if (writeVRTFile && (myRank == 0)) - { - // VRT Filename - std::stringstream vrtfOut; - vrtfOut<< prefix << ".vrt"; + /** Does the real work. */ + virtual void Update() override; - // Data type - GDALDataType dataType; - dataType = GDALGetDataTypeByName(dataTypeStr.c_str()); + /** SimpleParallelTiffWriter Methods */ + virtual void SetFileName(const char* extendedFileName); + virtual void SetFileName(std::string extendedFileName); + virtual const char* GetFileName () const; - int imageSizeY = img->GetLargestPossibleRegion().GetSize()[1]; - int imageSizeX = img->GetLargestPossibleRegion().GetSize()[0]; - const unsigned int nbBands = img->GetNumberOfComponentsPerPixel(); + /** Specify the region to write. If left NULL, then the whole image + * is written. */ + void SetIORegion(const itk::ImageIORegion& region); + itkGetConstReferenceMacro(IORegion, itk::ImageIORegion); - // Get VRT driver - GDALAllRegister(); - GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); - if (driver == NULL) { - mpiConfig->logError("Error opening VRT driver."); - mpiConfig->abort(EXIT_FAILURE); - } + itkSetMacro(WriteVRT, bool); + itkGetMacro(WriteVRT, bool); - // Create output raster - GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), - imageSizeX, - imageSizeY, - 0, - dataType, - NULL); // No options - if (VRTOutput == NULL) { - mpiConfig->logError("driver->Create call failed.\n"); - mpiConfig->abort(EXIT_FAILURE); - } + itkSetMacro(AvailableRAM, unsigned int); + itkGetMacro(AvailableRAM, unsigned int); - // Set GeoTransform - double gt[6]; - gt[0] = img->GetOrigin()[0] - 0.5*img->GetSignedSpacing()[0]; - gt[1] = img->GetSignedSpacing()[0]; - gt[2] = 0.0; - gt[3] = img->GetOrigin()[1] - 0.5*img->GetSignedSpacing()[1]; - gt[4] = 0.0; - gt[5] = img->GetSignedSpacing()[1]; - VRTOutput->SetGeoTransform(gt); +protected: + MPIVrtWriter(); + virtual ~MPIVrtWriter(); + void PrintSelf(std::ostream& os, itk::Indent indent) const override; - // Set projection - OGRSpatialReference out_sr; - char *wkt = NULL; - out_sr.SetFromUserInput(img->GetProjectionRef().c_str()); - out_sr.exportToWkt(&wkt); - VRTOutput->SetProjection(wkt); +private: + MPIVrtWriter(const MPIVrtWriter &) = delete; + void operator =(const MPIVrtWriter&) = delete; - for(unsigned int band = 1; band<=nbBands;++band) - { - VRTOutput->AddBand(dataType, NULL); + unsigned int m_AvailableRAM; - typename TImage::RegionType currentRegion; - for(unsigned int id=0; id < numberOfSplits; ++id) - { - currentRegion = streamingManager->GetSplit(id); - int tileSizeX = currentRegion.GetSize()[0]; - int tileSizeY = currentRegion.GetSize()[1]; - int tileIndexX = currentRegion.GetIndex()[0]; - int tileIndexY = currentRegion.GetIndex()[1]; - std::stringstream tileFileName; - tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; - std::cout<<tileFileName.str()<<std::endl; + itk::ImageIORegion m_IORegion; - GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + std::string m_Filename; - VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); - VRTBand->AddSimpleSource(dataset->GetRasterBand(band), - 0, //xOffSrc - 0, //yOffSrc - tileSizeX, //xSizeSrc - tileSizeY, //ySizeSrc - tileIndexX, //xOffDest - tileIndexY, //yOffDest - tileSizeX, //xSizeDest - tileSizeY, //ySizeDest - "near", - VRT_NODATA_UNSET); - } + bool m_WriteVRT; - } - - // Close - CPLFree(wkt); - GDALClose(VRTOutput); - } - } - catch (itk::ExceptionObject& err) - { - std::stringstream message; - message << "ExceptionObject caught: " << err << std::endl; - mpiConfig->logError(message.str()); - mpiConfig->abort(EXIT_FAILURE); - } +}; +/** + *\brief Write image data to multiple files with MPI processus and add a VRT file. + * + * The image is divided into several pieces. + * Each pieces is distributed to a MPI processus. + * Each MPI processus write their pieces into a separate + * file. + * The master processus writes a VRT file (optional). + * + *\param img Image + *\param output Output Filename + *\param availableRAM Available memory for streaming + *\param writeVRTFile Activate the VRT file writing + */ +template <typename TImage> void WriteMPI(TImage *img, const std::string &output, unsigned int availableRAM = 0, bool writeVRTFile=true) +{ + typename MPIVrtWriter<TImage>::Pointer writer = MPIVrtWriter<TImage>::New(); + writer->SetInput(img); + writer->SetFileName(output); + writer->SetAvailableRAM(availableRAM); + writer->SetWriteVRT(writeVRTFile); + writer->Update(); } } // End namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbMPIVrtWriter.txx" +#endif + #endif //__otbMPIVrtWriter_h diff --git a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx new file mode 100644 index 0000000000000000000000000000000000000000..b57518ed73d613b37e33b86c59ba732ed0e44cc2 --- /dev/null +++ b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 otbMPIVrtWriter_txx +#define otbMPIVrtWriter_txx + +#include "otbMPIVrtWriter.h" +#include "otbMacro.h" + +namespace otb +{ + +template <typename TImage> +MPIVrtWriter<TImage>::MPIVrtWriter() + : m_AvailableRAM(0) + , m_IORegion() + , m_Filename("") + , m_WriteVRT(true) +{ +} + +template <typename TImage> +MPIVrtWriter<TImage>::~MPIVrtWriter() +{ +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetInput(const InputImageType *input) +{ + this->ProcessObject::SetNthInput(0,const_cast<InputImageType*>(input)); +} + +template <typename TImage> +const TImage* +MPIVrtWriter<TImage>::GetInput() +{ + if (this->GetNumberOfInputs() < 1) + { + return 0; + } + return static_cast<const InputImageType*>(this->ProcessObject::GetInput(0)); +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetFileName(const char* extendedFileName) +{ + if (m_Filename.compare(extendedFileName) != 0 ) + { + m_Filename = std::string(extendedFileName); + this->Modified(); + } +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetFileName(std::string extendedFileName) +{ + this->SetFileName(extendedFileName.c_str()); +} + +template <typename TImage> +const char* +MPIVrtWriter<TImage>::GetFileName () const +{ + return m_Filename.c_str(); +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetIORegion(const itk::ImageIORegion& region) +{ + if (m_IORegion != region) + { + m_IORegion = region; + this->Modified(); + } +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + os << indent << "File Name: "<< m_Filename << std::endl; + os << indent << "Available RAM: "<< m_AvailableRAM << std::endl; + os << indent << "Write VRT: "<< m_WriteVRT << std::endl; +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::Update() +{ + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + + unsigned int myRank = mpiConfig->GetMyRank(); + unsigned int nbProcs = mpiConfig->GetNbProcs(); + + typedef otb::ImageFileWriter<TImage> WriterType; + typedef otb::NumberOfDivisionsTiledStreamingManager<TImage> StreamingManagerType; + typedef itk::RegionOfInterestImageFilter<TImage, TImage> ExtractFilterType; + + // First, update infomration from image to write + UpdateOutputInformation(); + InputImageType* img = const_cast<InputImageType*>(GetInput()); + std::string output = GetFileName(); + + // Configure streaming manager + typename StreamingManagerType::Pointer streamingManager = StreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbProcs); + streamingManager->PrepareStreaming(img,img->GetLargestPossibleRegion()); + unsigned int numberOfSplits = streamingManager->GetNumberOfSplits(); + + // This vector will hold all regions to write for current rank + std::vector<typename TImage::RegionType> regionsToWrite; + + // Handle both cases when there are much more (resp. less) region to + // write than NbProcs + if(myRank < numberOfSplits) + { + unsigned int splitIdx = myRank; + while(splitIdx < numberOfSplits) + { + typename TImage::RegionType currentRegion = streamingManager->GetSplit(splitIdx); + regionsToWrite.push_back(currentRegion); + splitIdx+=nbProcs; + } + } + + // Output prefix + std::string extension = itksys::SystemTools::GetFilenameExtension(output); + if (extension != ".vrt") + { + + // TODO: Maybe remove this + if (extension == "") + { + // Missing extension + mpiConfig->logInfo("Filename has no extension. Adding <.vrt> extension."); + } + else + { + // Bad extension + mpiConfig->logError("Filename must have .vrt extension!"); + mpiConfig->abort(EXIT_FAILURE); + } + } + std::vector<std::string> joins; + itksys::SystemTools::SplitPath(output, joins); + joins.back() = itksys::SystemTools::GetFilenameWithoutExtension(output); + std::string prefix = itksys::SystemTools::JoinPath(joins); + + // Data type + std::string dataTypeStr = "Float32"; + GDALImageIO::Pointer gdalImageIO; + + // Now write all the regions + for(typename std::vector<typename TImage::RegionType>::const_iterator it = regionsToWrite.begin(); + it!=regionsToWrite.end();++it) + { + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetInput(img); + extractFilter->SetRegionOfInterest(*it); + // Writer + // Output Filename + std::stringstream ss; + ss<<prefix<<"_"<<it->GetIndex()[0]<<"_"<<it->GetIndex()[1]<<"_"<<it->GetSize()[0]<<"_"<<it->GetSize()[1]<<".tif"; + typename WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(ss.str()); + writer->SetInput(extractFilter->GetOutput()); + // Datatype + gdalImageIO = dynamic_cast<GDALImageIO *>(writer->GetImageIO()); + if(gdalImageIO.IsNotNull()) + { + dataTypeStr = gdalImageIO->GetGdalPixelTypeAsString(); + } + + if (!m_AvailableRAM) + { + writer->SetNumberOfDivisionsTiledStreaming(0); + } + else + { + writer->SetAutomaticAdaptativeStreaming(m_AvailableRAM); + } + + // Pipeline execution + try + { + writer->Update(); + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } + } + + // MPI process synchronization + mpiConfig->barrier(); + + // Write VRT file + try + { + if (m_WriteVRT && (myRank == 0)) + { + // VRT Filename + std::stringstream vrtfOut; + vrtfOut<< prefix << ".vrt"; + + // Data type + GDALDataType dataType; + dataType = GDALGetDataTypeByName(dataTypeStr.c_str()); + + int imageSizeY = img->GetLargestPossibleRegion().GetSize()[1]; + int imageSizeX = img->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int nbBands = img->GetNumberOfComponentsPerPixel(); + + // Get VRT driver + GDALAllRegister(); + GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); + if (driver == NULL) { + mpiConfig->logError("Error opening VRT driver."); + mpiConfig->abort(EXIT_FAILURE); + } + + // Create output raster + GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), + imageSizeX, + imageSizeY, + 0, + dataType, + NULL); // No options + if (VRTOutput == NULL) { + mpiConfig->logError("driver->Create call failed.\n"); + mpiConfig->abort(EXIT_FAILURE); + } + + // Set GeoTransform + double gt[6]; + gt[0] = img->GetOrigin()[0] - 0.5*img->GetSignedSpacing()[0]; + gt[1] = img->GetSignedSpacing()[0]; + gt[2] = 0.0; + gt[3] = img->GetOrigin()[1] - 0.5*img->GetSignedSpacing()[1]; + gt[4] = 0.0; + gt[5] = img->GetSignedSpacing()[1]; + VRTOutput->SetGeoTransform(gt); + + // Set projection + OGRSpatialReference out_sr; + char *wkt = NULL; + out_sr.SetFromUserInput(img->GetProjectionRef().c_str()); + out_sr.exportToWkt(&wkt); + VRTOutput->SetProjection(wkt); + + for(unsigned int band = 1; band<=nbBands;++band) + { + VRTOutput->AddBand(dataType, NULL); + + typename TImage::RegionType currentRegion; + for(unsigned int id=0; id < numberOfSplits; ++id) + { + currentRegion = streamingManager->GetSplit(id); + int tileSizeX = currentRegion.GetSize()[0]; + int tileSizeY = currentRegion.GetSize()[1]; + int tileIndexX = currentRegion.GetIndex()[0]; + int tileIndexY = currentRegion.GetIndex()[1]; + std::stringstream tileFileName; + tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; + otbDebugMacro(<<tileFileName.str()); + + GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + + VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); + VRTBand->AddSimpleSource(dataset->GetRasterBand(band), + 0, //xOffSrc + 0, //yOffSrc + tileSizeX, //xSizeSrc + tileSizeY, //ySizeSrc + tileIndexX, //xOffDest + tileIndexY, //yOffDest + tileSizeX, //xSizeDest + tileSizeY, //ySizeDest + "near", + VRT_NODATA_UNSET); + } + + } + + // Close + CPLFree(wkt); + GDALClose(VRTOutput); + } + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } +} + + +} // end of namespace otb + +#endif diff --git a/Modules/MPI/MPIVrtWriter/otb-module.cmake b/Modules/MPI/MPIVrtWriter/otb-module.cmake index 0e95d97f7cbe02b27ba37be548a0106b3d70ea9e..f98ab957d207a9bf02f6f13c27002ad054582897 100644 --- a/Modules/MPI/MPIVrtWriter/otb-module.cmake +++ b/Modules/MPI/MPIVrtWriter/otb-module.cmake @@ -22,8 +22,9 @@ set(DOCUMENTATION "Provides a template function for MPI writing to a VRT file") otb_module(OTBMPIVrtWriter DEPENDS - OTBMPIConfig - OTBPanSharpening + OTBCommon + OTBMPIConfig +# OTBPanSharpening OTBProjection OTBInterpolation OTBTestKernel diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h index b9bc451c3b3b9a59437ac306cf48929e9459d734..eac7df4cadad4e1c8376918f53401439748a6094 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h @@ -117,14 +117,8 @@ protected: /** Destructor */ ~OutputImageParameter() override; - template <class TInputVectorImageType> - void SwitchVectorImageWrite(); - - template <class TInputVectorImageType> - void SwitchRGBImageWrite(); - - template <class TInputVectorImageType> - void SwitchRGBAImageWrite(); + template <class TInput> + int SwitchInput(TInput *img); //FloatVectorImageType::Pointer m_Image; ImageBaseType::Pointer m_Image; @@ -132,46 +126,28 @@ protected: ImagePixelType m_PixelType; ImagePixelType m_DefaultPixelType; - typedef otb::ImageFileWriter<UInt8VectorImageType> VectorUInt8WriterType; - typedef otb::ImageFileWriter<Int16VectorImageType> VectorInt16WriterType; - typedef otb::ImageFileWriter<UInt16VectorImageType> VectorUInt16WriterType; - typedef otb::ImageFileWriter<Int32VectorImageType> VectorInt32WriterType; - typedef otb::ImageFileWriter<UInt32VectorImageType> VectorUInt32WriterType; - typedef otb::ImageFileWriter<FloatVectorImageType> VectorFloatWriterType; - typedef otb::ImageFileWriter<DoubleVectorImageType> VectorDoubleWriterType; - - typedef otb::ImageFileWriter<UInt8RGBAImageType> RGBAUInt8WriterType; - typedef otb::ImageFileWriter<UInt8RGBImageType> RGBUInt8WriterType; - - typedef otb::ImageFileWriter<ComplexInt16VectorImageType> ComplexVectorInt16WriterType; - typedef otb::ImageFileWriter<ComplexInt32VectorImageType> ComplexVectorInt32WriterType; - typedef otb::ImageFileWriter<ComplexFloatVectorImageType> ComplexVectorFloatWriterType; - typedef otb::ImageFileWriter<ComplexDoubleVectorImageType> ComplexVectorDoubleWriterType; - - VectorUInt8WriterType::Pointer m_VectorUInt8Writer; - VectorInt16WriterType::Pointer m_VectorInt16Writer; - VectorUInt16WriterType::Pointer m_VectorUInt16Writer; - VectorInt32WriterType::Pointer m_VectorInt32Writer; - VectorUInt32WriterType::Pointer m_VectorUInt32Writer; - VectorFloatWriterType::Pointer m_VectorFloatWriter; - VectorDoubleWriterType::Pointer m_VectorDoubleWriter; - - RGBUInt8WriterType::Pointer m_RGBUInt8Writer; - RGBAUInt8WriterType::Pointer m_RGBAUInt8Writer; - - ComplexVectorInt16WriterType::Pointer m_ComplexVectorInt16Writer; - ComplexVectorInt32WriterType::Pointer m_ComplexVectorInt32Writer; - ComplexVectorFloatWriterType::Pointer m_ComplexVectorFloatWriter; - ComplexVectorDoubleWriterType::Pointer m_ComplexVectorDoubleWriter; - private: OutputImageParameter(const Parameter &); //purposely not implemented void operator =(const Parameter&); //purposely not implemented unsigned int m_RAMValue; + itk::ProcessObject::Pointer m_Caster; + + itk::ProcessObject::Pointer m_Writer; + }; // End class OutputImage Parameter +// Declare specialisation for UInt8RGBAImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBAImageType *img); + +// Declare specialisation for UInt8RGBImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBImageType *img); + } // End namespace Wrapper } // End namespace otb diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx index 7aec4f87620aa6917040ee282d4c5e0110848d53..31f46f3a019e6fe335ebcae5535b4b6284bc2f4a 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx @@ -686,7 +686,6 @@ int Application::ExecuteAndWriteOutput() if(outputParam!=ITK_NULLPTR) { - outputParam->InitializeWriters(); std::string checkReturn = outputParam->CheckFileName(true); if (!checkReturn.empty()) { @@ -696,6 +695,7 @@ int Application::ExecuteAndWriteOutput() { outputParam->SetRAMValue(ram); } + outputParam->InitializeWriters(); std::ostringstream progressId; progressId << "Writing " << outputParam->GetFileName() << "..."; AddProcess(outputParam->GetWriter(), progressId.str()); diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx index 09396447765610f1b1f2dd903943b9a2cc116922..9e6e33efdac2c1223830dad9db754e7110777817 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx @@ -42,7 +42,9 @@ namespace Wrapper OutputImageParameter::OutputImageParameter() : m_PixelType(ImagePixelType_float), m_DefaultPixelType(ImagePixelType_float), - m_RAMValue(0) + m_RAMValue(0), + m_Caster(nullptr), + m_Writer(nullptr) { this->SetName("Output Image"); this->SetKey("out"); @@ -149,35 +151,102 @@ OutputImageParameter::ConvertStringToPixelType(const std::string &value, ImagePi void OutputImageParameter::InitializeWriters() { - m_VectorUInt8Writer = VectorUInt8WriterType::New(); - m_VectorInt16Writer = VectorInt16WriterType::New(); - m_VectorUInt16Writer = VectorUInt16WriterType::New(); - m_VectorInt32Writer = VectorInt32WriterType::New(); - m_VectorUInt32Writer = VectorUInt32WriterType::New(); - m_VectorFloatWriter = VectorFloatWriterType::New(); - m_VectorDoubleWriter = VectorDoubleWriterType::New(); - - m_RGBUInt8Writer = RGBUInt8WriterType::New(); - m_RGBAUInt8Writer = RGBAUInt8WriterType::New(); - - m_ComplexVectorInt16Writer = ComplexVectorInt16WriterType::New(); - m_ComplexVectorInt32Writer = ComplexVectorInt32WriterType::New(); - m_ComplexVectorFloatWriter = ComplexVectorFloatWriterType::New(); - m_ComplexVectorDoubleWriter = ComplexVectorDoubleWriterType::New(); + ImageBaseType* imgBase = m_Image.GetPointer(); + // Guess the image type + std::string className(m_Image->GetNameOfClass()); + if (className == "VectorImage") + { + UInt8VectorImageType* imgUInt8 = dynamic_cast<UInt8VectorImageType*>(imgBase); + if (imgUInt8 && SwitchInput(imgUInt8)) return; + + Int16VectorImageType* imgInt16 = dynamic_cast<Int16VectorImageType*>(imgBase); + if (imgInt16 && SwitchInput(imgInt16)) return; + + UInt16VectorImageType* imgUInt16 = dynamic_cast<UInt16VectorImageType*>(imgBase); + if (imgUInt16 && SwitchInput(imgUInt16)) return; + + Int32VectorImageType* imgInt32 = dynamic_cast<Int32VectorImageType*>(imgBase); + if (imgInt32 && SwitchInput(imgInt32)) return; + + UInt32VectorImageType* imgUInt32 = dynamic_cast<UInt32VectorImageType*>(imgBase); + if (imgUInt32 && SwitchInput(imgUInt32)) return; + + FloatVectorImageType* imgFloat = dynamic_cast<FloatVectorImageType*>(imgBase); + if (imgFloat && SwitchInput(imgFloat)) return; + + DoubleVectorImageType* imgDouble = dynamic_cast<DoubleVectorImageType*>(imgBase); + if (imgDouble && SwitchInput(imgDouble)) return; + + ComplexInt16VectorImageType* imgCInt16 = dynamic_cast<ComplexInt16VectorImageType*>(imgBase); + if (imgCInt16 && SwitchInput(imgCInt16)) return; + + ComplexInt32VectorImageType* imgCInt32 = dynamic_cast<ComplexInt32VectorImageType*>(imgBase); + if (imgCInt32 && SwitchInput(imgCInt32)) return; + + ComplexFloatVectorImageType* imgCFloat = dynamic_cast<ComplexFloatVectorImageType*>(imgBase); + if (imgCFloat && SwitchInput(imgCFloat)) return; + + ComplexDoubleVectorImageType* imgCDouble = dynamic_cast<ComplexDoubleVectorImageType*>(imgBase); + if (imgCDouble && SwitchInput(imgCDouble)) return; + } + else + { + UInt8ImageType* imgUInt8 = dynamic_cast<UInt8ImageType*>(imgBase); + if (imgUInt8 && SwitchInput(imgUInt8)) return; + + Int16ImageType* imgInt16 = dynamic_cast<Int16ImageType*>(imgBase); + if (imgInt16 && SwitchInput(imgInt16)) return; + + UInt16ImageType* imgUInt16 = dynamic_cast<UInt16ImageType*>(imgBase); + if (imgUInt16 && SwitchInput(imgUInt16)) return; + + Int32ImageType* imgInt32 = dynamic_cast<Int32ImageType*>(imgBase); + if (imgInt32 && SwitchInput(imgInt32)) return; + + UInt32ImageType* imgUInt32 = dynamic_cast<UInt32ImageType*>(imgBase); + if (imgUInt32 && SwitchInput(imgUInt32)) return; + + FloatImageType* imgFloat = dynamic_cast<FloatImageType*>(imgBase); + if (imgFloat && SwitchInput(imgFloat)) return; + + DoubleImageType* imgDouble = dynamic_cast<DoubleImageType*>(imgBase); + if (imgDouble && SwitchInput(imgDouble)) return; + + ComplexInt16ImageType* imgCInt16 = dynamic_cast<ComplexInt16ImageType*>(imgBase); + if (imgCInt16 && SwitchInput(imgCInt16)) return; + + ComplexInt32ImageType* imgCInt32 = dynamic_cast<ComplexInt32ImageType*>(imgBase); + if (imgCInt32 && SwitchInput(imgCInt32)) return; + + ComplexFloatImageType* imgCFloat = dynamic_cast<ComplexFloatImageType*>(imgBase); + if (imgCFloat && SwitchInput(imgCFloat)) return; + + ComplexDoubleImageType* imgCDouble = dynamic_cast<ComplexDoubleImageType*>(imgBase); + if (imgCDouble && SwitchInput(imgCDouble)) return; + + UInt8RGBImageType* imgRGB = dynamic_cast<UInt8RGBImageType*>(imgBase); + if (imgRGB && SwitchInput(imgRGB)) return; + + UInt8RGBAImageType* imgRGBA = dynamic_cast<UInt8RGBAImageType*>(imgBase); + if (imgRGBA && SwitchInput(imgRGBA)) return; + } + + itkExceptionMacro("Unknown image type"); } template <typename TInput, typename TOutput> -void -ClampAndWriteVectorImage( itk::ImageBase<2> * in , - otb::ImageFileWriter<TOutput> * writer , +std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> +ClampAndWriteVectorImage( TInput * in , const std::string & filename , const unsigned int & ramValue ) { + std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> ret; typedef ClampImageFilter < TInput , TOutput > ClampFilterType; typename ClampFilterType::Pointer clampFilter ( ClampFilterType::New() ); - clampFilter->SetInput( dynamic_cast<TInput*>(in)); + clampFilter->SetInput( in); + ret.first = clampFilter.GetPointer(); bool useStandardWriter = true; @@ -194,8 +263,14 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , if(extension == ".vrt") { - // Use the WriteMPI function - WriteMPI(clampFilter->GetOutput(),filename,ramValue); + // Use the MPIVrtWriter + typedef otb::MPIVrtWriter<TOutput> VRTWriterType; + + typename VRTWriterType::Pointer vrtWriter = VRTWriterType::New(); + vrtWriter->SetInput(clampFilter->GetOutput()); + vrtWriter->SetFileName(filename); + vrtWriter->SetAvailableRAM(ramValue); + ret.second = vrtWriter.GetPointer(); } #ifdef OTB_USE_SPTW else if (extension == ".tif") @@ -206,9 +281,8 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , typename SPTWriterType::Pointer sptWriter = SPTWriterType::New(); sptWriter->SetFileName(filename); sptWriter->SetInput(clampFilter->GetOutput()); - sptWriter->SetAutomaticAdaptativeStreaming(ramValue); sptWriter->GetStreamingManager()->SetDefaultRAM(ramValue); - sptWriter->Update(); + ret.second = sptWriter.GetPointer(); } #endif @@ -223,114 +297,110 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , if(useStandardWriter) { + typename otb::ImageFileWriter<TOutput>::Pointer writer = + otb::ImageFileWriter<TOutput>::New(); writer->SetFileName( filename ); writer->SetInput(clampFilter->GetOutput()); writer->GetStreamingManager()->SetDefaultRAM(ramValue); - writer->Update(); + ret.second = writer.GetPointer(); } + + return ret; } template <class TInput> -void -OutputImageParameter::SwitchVectorImageWrite() - { +int +OutputImageParameter::SwitchInput(TInput *img) +{ + if (! img) return 0; + + std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> ret; switch(m_PixelType ) { case ImagePixelType_uint8: { - ClampAndWriteVectorImage< TInput , UInt8VectorImageType > ( - m_Image , - m_VectorUInt8Writer , + ret = ClampAndWriteVectorImage< TInput , UInt8VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_int16: { - ClampAndWriteVectorImage< TInput , Int16VectorImageType > ( - m_Image , - m_VectorInt16Writer , + ret = ClampAndWriteVectorImage< TInput , Int16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_uint16: { - ClampAndWriteVectorImage< TInput , UInt16VectorImageType > ( - m_Image , - m_VectorUInt16Writer , + ret = ClampAndWriteVectorImage< TInput , UInt16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_int32: { - ClampAndWriteVectorImage< TInput , Int32VectorImageType > ( - m_Image , - m_VectorInt32Writer , + ret = ClampAndWriteVectorImage< TInput , Int32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_uint32: { - ClampAndWriteVectorImage< TInput , UInt32VectorImageType > ( - m_Image , - m_VectorUInt32Writer , + ret = ClampAndWriteVectorImage< TInput , UInt32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_float: { - ClampAndWriteVectorImage< TInput , FloatVectorImageType > ( - m_Image , - m_VectorFloatWriter , + ret = ClampAndWriteVectorImage< TInput , FloatVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_double: { - ClampAndWriteVectorImage< TInput , DoubleVectorImageType > ( - m_Image , - m_VectorDoubleWriter , + ret = ClampAndWriteVectorImage< TInput , DoubleVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cint16: { - ClampAndWriteVectorImage < TInput , ComplexInt16VectorImageType > ( - m_Image , - m_ComplexVectorInt16Writer , + ret = ClampAndWriteVectorImage < TInput , ComplexInt16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cint32: { - ClampAndWriteVectorImage < TInput , ComplexInt32VectorImageType > ( - m_Image , - m_ComplexVectorInt32Writer , + ret = ClampAndWriteVectorImage < TInput , ComplexInt32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cfloat: { - ClampAndWriteVectorImage < TInput , ComplexFloatVectorImageType > ( - m_Image , - m_ComplexVectorFloatWriter , + ret = ClampAndWriteVectorImage < TInput , ComplexFloatVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cdouble: { - ClampAndWriteVectorImage < TInput , ComplexDoubleVectorImageType > ( - m_Image , - m_ComplexVectorDoubleWriter , + ret = ClampAndWriteVectorImage < TInput , ComplexDoubleVectorImageType > ( + img , m_FileName , m_RAMValue ); break; @@ -338,256 +408,27 @@ OutputImageParameter::SwitchVectorImageWrite() default: break; } - } - -template <class TInputRGBAImageType> -void -OutputImageParameter::SwitchRGBAImageWrite() - { - if( m_PixelType == ImagePixelType_uint8 ) - { - m_RGBAUInt8Writer->SetFileName( this->GetFileName() ); - m_RGBAUInt8Writer->SetInput(dynamic_cast<UInt8RGBAImageType*>(m_Image.GetPointer()) ); - m_RGBAUInt8Writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); - m_RGBAUInt8Writer->Update(); - } - else - itkExceptionMacro("Unknown PixelType for RGBA Image. Only uint8 is supported."); - } - -template <class TInputRGBImageType> -void -OutputImageParameter::SwitchRGBImageWrite() - { - if( m_PixelType == ImagePixelType_uint8 ) - { - m_RGBUInt8Writer->SetFileName( this->GetFileName() ); - m_RGBUInt8Writer->SetInput(dynamic_cast<UInt8RGBImageType*>(m_Image.GetPointer()) ); - m_RGBUInt8Writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); - m_RGBUInt8Writer->Update(); - } - else - itkExceptionMacro("Unknown PixelType for RGB Image. Only uint8 is supported."); - } + // Save the caster and writer + m_Caster = ret.first; + m_Writer = ret.second; + return 1; +} void OutputImageParameter::Write() { - m_Image->UpdateOutputInformation(); + m_Writer->Update(); - if (dynamic_cast<UInt8ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt8ImageType>(); - } - else if (dynamic_cast<Int16ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int16ImageType>(); - } - else if (dynamic_cast<UInt16ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt16ImageType>(); - } - else if (dynamic_cast<Int32ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int32ImageType>(); - } - else if (dynamic_cast<UInt32ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt32ImageType>(); - } - else if (dynamic_cast<FloatImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<FloatImageType>(); - } - else if (dynamic_cast<DoubleImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<DoubleImageType>(); - } - else if (dynamic_cast<ComplexInt16ImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexInt16ImageType>(); - } - else if (dynamic_cast<ComplexInt32ImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexInt32ImageType>(); - } - else if (dynamic_cast<ComplexFloatImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexFloatImageType>(); - } - else if (dynamic_cast<ComplexDoubleImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexDoubleImageType>(); - } - else if (dynamic_cast<UInt8VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt8VectorImageType>(); - } - else if (dynamic_cast<Int16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int16VectorImageType>(); - } - else if (dynamic_cast<UInt16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt16VectorImageType>(); - } - else if (dynamic_cast<Int32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int32VectorImageType>(); - } - else if (dynamic_cast<UInt32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt32VectorImageType>(); - } - else if (dynamic_cast<FloatVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<FloatVectorImageType>(); - } - else if (dynamic_cast<DoubleVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<DoubleVectorImageType>(); - } - else if (dynamic_cast<ComplexInt16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexInt16VectorImageType>(); - } - else if (dynamic_cast<ComplexInt32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexInt32VectorImageType>(); - } - else if (dynamic_cast<ComplexFloatVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexFloatVectorImageType>(); - } - else if (dynamic_cast<ComplexDoubleVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexDoubleVectorImageType>(); - } - else if (dynamic_cast<UInt8RGBImageType*>(m_Image.GetPointer())) - { - SwitchRGBImageWrite<UInt8RGBImageType>(); - } - else if (dynamic_cast<UInt8RGBAImageType*>(m_Image.GetPointer())) - { - SwitchRGBAImageWrite<UInt8RGBAImageType>(); - } - else - { - itkExceptionMacro("Unknown image type"); - } - } + // Clean internal filters + m_Caster = nullptr; + m_Writer = nullptr; +} itk::ProcessObject* OutputImageParameter::GetWriter() { - int type = 1; - // 0 : image - // 1 : VectorImage - // 2 : RGBAImage - // 3 : RGBImage - itk::ProcessObject* writer = ITK_NULLPTR; - - if (dynamic_cast<UInt8RGBAImageType*> (m_Image.GetPointer())) - { - type = 2; - writer = m_RGBAUInt8Writer; - itkWarningMacro("UInt8RGBAImageType will be saved in UInt8 format."); - return writer; - } - else if (dynamic_cast<UInt8RGBImageType*> (m_Image.GetPointer())) - { - type = 3; - writer = m_RGBUInt8Writer; - itkWarningMacro("UInt8RGBImageType will be saved in UInt8 format."); - return writer; - } - - switch (GetPixelType()) - { - case ImagePixelType_uint8: - { - switch(type) - { - case 1: - writer = m_VectorUInt8Writer; - break; - case 2: - writer = m_RGBAUInt8Writer; - break; - default: - writer = m_RGBUInt8Writer; - break; - } - break; - } - case ImagePixelType_int16: - { - if (type == 1) - writer = m_VectorInt16Writer; - break; - } - case ImagePixelType_uint16: - { - if (type == 1) - writer = m_VectorUInt16Writer; - break; - } - case ImagePixelType_int32: - { - if (type == 1) - writer = m_VectorInt32Writer; - break; - } - case ImagePixelType_uint32: - { - if (type == 1) - writer = m_VectorUInt32Writer; - break; - } - case ImagePixelType_float: - { - if (type == 1) - writer = m_VectorFloatWriter; - break; - } - case ImagePixelType_double: - { - if (type == 1) - writer = m_VectorDoubleWriter; - break; - } - case ImagePixelType_cint16: - { - if( type == 1 ) - writer = m_ComplexVectorInt16Writer; - break; - } - case ImagePixelType_cint32: - { - if( type == 1 ) - writer = m_ComplexVectorInt32Writer; - break; - } - case ImagePixelType_cfloat: - { - if( type == 1 ) - writer = m_ComplexVectorFloatWriter; - break; - } - case ImagePixelType_cdouble: - { - if( type == 1 ) - writer = m_ComplexVectorDoubleWriter; - break; - } - } - if (ITK_NULLPTR == writer) - { - itkExceptionMacro("Unknown Writer type."); - } - - return writer; + return m_Writer; } ImageBaseType* @@ -651,5 +492,45 @@ OutputImageParameter::CheckFileName(bool fixMissingExtension) return ret; } +// Specialization for UInt8RGBAImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBAImageType *img) +{ + if (! img) return 0; + if( m_PixelType == ImagePixelType_uint8 ) + { + typename otb::ImageFileWriter<UInt8RGBAImageType>::Pointer writer = + otb::ImageFileWriter<UInt8RGBAImageType>::New(); + writer->SetFileName( this->GetFileName() ); + writer->SetInput(img); + writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); + m_Writer = writer.GetPointer(); + } + else + itkExceptionMacro("Unknown PixelType for RGBA Image. Only uint8 is supported."); + return 1; +} + +// Specialization for UInt8RGBImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBImageType *img) +{ + if (! img) return 0; + if( m_PixelType == ImagePixelType_uint8 ) + { + typename otb::ImageFileWriter<UInt8RGBImageType>::Pointer writer = + otb::ImageFileWriter<UInt8RGBImageType>::New(); + writer->SetFileName( this->GetFileName() ); + writer->SetInput(img); + writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); + m_Writer = writer.GetPointer(); + } + else + itkExceptionMacro("Unknown PixelType for RGB Image. Only uint8 is supported."); + return 1; +} + } }