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 &region) = 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;
+}
+
 }
 }