diff --git a/Modules/Applications/AppClassification/app/otbImageClassifier.cxx b/Modules/Applications/AppClassification/app/otbImageClassifier.cxx
index 47ba61e9b705da15b83794d55ecb3be5284a569c..1f94d2714355e37db5137cfd85d3de72b3927e57 100644
--- a/Modules/Applications/AppClassification/app/otbImageClassifier.cxx
+++ b/Modules/Applications/AppClassification/app/otbImageClassifier.cxx
@@ -50,7 +50,8 @@ public:
   itkTypeMacro(ImageClassifier, otb::Application);
 
   /** Filters typedef */
-  typedef UInt16ImageType                                                                      OutputImageType;
+  //typedef UInt16ImageType                                                                    OutputImageType;
+  typedef Int32ImageType                                                                       OutputImageType;
   typedef UInt8ImageType                                                                       MaskImageType;
   typedef itk::VariableLengthVector<FloatVectorImageType::InternalPixelType>                   MeasurementType;
   typedef otb::StatisticsXMLFileReader<MeasurementType>                                        StatisticsReader;
@@ -79,7 +80,7 @@ private:
 
     // Documentation
     SetDocName("Image Classification");
-    SetDocLongDescription("This application performs an image classification based on a model file produced by the TrainImagesClassifier application. Pixels of the output image will contain the class labels decided by the classifier (maximal class label = 65535). The input pixels can be optionally centered and reduced according to the statistics file produced by the ComputeImagesStatistics application. An optional input mask can be provided, in which case only input image pixels whose corresponding mask value is greater than 0 will be classified. The remaining of pixels will be given the label 0 in the output image.");
+    SetDocLongDescription("This application performs an image classification based on a model file produced by the TrainImagesClassifier application. Pixels of the output image will contain the class labels decided by the classifier (maximal class label = 65535). The input pixels can be optionally centered and reduced according to the statistics file produced by the ComputeImagesStatistics application. An optional input mask can be provided, in which case only input image pixels whose corresponding mask value is greater than 0 will be classified. By default, the remaining of pixels will be given the label 0 in the output image.");
 
     SetDocLimitations("The input image must have the same type, order and number of bands than the images used to produce the statistics file and the SVM model file. If a statistics file was used during training by the TrainImagesClassifier, it is mandatory to use the same statistics file for classification. If an input mask is used, its size must match the input image size.");
     SetDocAuthors("OTB-Team");
@@ -101,6 +102,15 @@ private:
     SetParameterDescription("imstat", "A XML file containing mean and standard deviation to center and reduce samples before classification (produced by ComputeImagesStatistics application).");
     MandatoryOff("imstat");
 
+    AddParameter(ParameterType_Int, "nodatalabel", "Label mask value");
+    SetParameterDescription("nodatalabel", "By default, "
+      "hidden pixels will have the assigned label 0 in the output image. "
+      "It's possible to define the label mask by another value, "
+      "but be careful to not take a label from another class (max. 65535).");
+
+    SetDefaultParameterInt("nodatalabel", 0);
+    MandatoryOff("nodatalabel");
+
     AddParameter(ParameterType_OutputImage, "out",  "Output Image");
     SetParameterDescription( "out", "Output image containing class labels");
     SetDefaultOutputPixelType( "out", ImagePixelType_uint8);
@@ -165,6 +175,8 @@ private:
     m_ClassificationFilter = ClassificationFilterType::New();
     m_ClassificationFilter->SetModel(m_Model);
 
+    m_ClassificationFilter->SetDefaultLabel(GetParameterInt("nodatalabel"));
+
     // Normalize input image if asked
     if(IsParameterEnabled("imstat")  )
       {
diff --git a/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx b/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx
index 36eeb318fadd983426556b41c0336d21a0366448..173daeec99126cd7cf288890d5c6d009b3a41b71 100644
--- a/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx
+++ b/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx
@@ -18,536 +18,500 @@
  * limitations under the License.
  */
 
-
-#include "otbWrapperApplication.h"
+#include "otbWrapperCompositeApplication.h"
 #include "otbWrapperApplicationFactory.h"
 
-#include "otbVectorImage.h"
-#include "otbStreamingTraits.h"
-#include "itkImageRegionConstIterator.h"
-#include "itkListSample.h"
-#include "itkWeightedCentroidKdTreeGenerator.h"
-#include "itkKdTreeBasedKmeansEstimator.h"
-#include "otbStreamingShrinkImageFilter.h"
-#include "otbChangeLabelImageFilter.h"
-#include "otbRAMDrivenStrippedStreamingManager.h"
-
-#include "otbChangeLabelImageFilter.h"
-#include "itkLabelToRGBImageFilter.h"
-#include "otbReliefColormapFunctor.h"
-#include "itkScalarToRGBColormapImageFilter.h"
-
+#include "otbOGRDataToSamplePositionFilter.h"
 
 namespace otb
 {
-
-
 namespace Wrapper
 {
 
-namespace Functor
-{
-template <class TSample, class TLabel> class KMeansFunctor
-{
-public:
-  /** operator */
-  TLabel operator ()(const TSample& sample) const
-  {
-    typename CentroidMapType::const_iterator it = m_CentroidsMap.begin();
-
-    if (it == m_CentroidsMap.end())
-      {
-      return 0;
-      }
-
-    TLabel resp = it->first;
-    double minDist = m_Distance->Evaluate(sample, it->second);
-    ++it;
-
-    while (it != m_CentroidsMap.end())
-      {
-      double dist = m_Distance->Evaluate(sample, it->second);
-
-      if (dist < minDist)
-        {
-        resp = it->first;
-        minDist = dist;
-        }
-      ++it;
-      }
-    return resp;
-  }
-
-  /** Add a new centroid */
-  void AddCentroid(const TLabel& label, const TSample& centroid)
-  {
-    m_CentroidsMap[label] = centroid;
-  }
-
-  /** Constructor */
-  KMeansFunctor() : m_CentroidsMap(), m_Distance()
-  {
-    m_Distance = DistanceType::New();
-  }
-
-  bool operator !=(const KMeansFunctor& other) const
-  {
-    return m_CentroidsMap != other.m_CentroidsMap;
-  }
-
-private:
-  typedef std::map<TLabel, TSample>                   CentroidMapType;
-  typedef itk::Statistics::EuclideanDistanceMetric<TSample> DistanceType;
-
-  CentroidMapType m_CentroidsMap;
-  typename DistanceType::Pointer m_Distance;
-};
-}
-
-
-typedef FloatImageType::PixelType PixelType;
-typedef UInt16ImageType   LabeledImageType;
-
-typedef UInt16VectorImageType       VectorImageType;
-typedef VectorImageType::PixelType  VectorPixelType;
-typedef UInt8RGBImageType           RGBImageType;
-typedef RGBImageType::PixelType     RGBPixelType;
-
-
-typedef LabeledImageType::PixelType LabelType;
-
-
-typedef FloatVectorImageType::PixelType                               SampleType;
-typedef itk::Statistics::ListSample<SampleType> ListSampleType;
-typedef itk::Statistics::WeightedCentroidKdTreeGenerator<ListSampleType> TreeGeneratorType;
-typedef TreeGeneratorType::KdTreeType TreeType;
-typedef itk::Statistics::KdTreeBasedKmeansEstimator<TreeType> EstimatorType;
-typedef RAMDrivenStrippedStreamingManager<FloatVectorImageType> RAMDrivenStrippedStreamingManagerType;
-
-
-typedef itk::ImageRegionConstIterator<FloatVectorImageType> IteratorType;
-typedef itk::ImageRegionConstIterator<UInt8ImageType> LabeledIteratorType;
-
-typedef otb::StreamingShrinkImageFilter<FloatVectorImageType,
-     FloatVectorImageType>              ImageSamplingFilterType;
-
-typedef otb::StreamingShrinkImageFilter<UInt8ImageType,
-    UInt8ImageType>              MaskSamplingFilterType;
-typedef Functor::KMeansFunctor<SampleType, LabelType> KMeansFunctorType;
-typedef itk::UnaryFunctorImageFilter<FloatVectorImageType,
-    LabeledImageType, KMeansFunctorType>     KMeansFilterType;
-
-
-// Manual label LUT
- typedef otb::ChangeLabelImageFilter
- <LabeledImageType, VectorImageType>    ChangeLabelFilterType;
-
- // Continuous LUT mapping
-  typedef itk::ScalarToRGBColormapImageFilter<LabeledImageType, RGBImageType>      ColorMapFilterType;
-
-
-  typedef otb::Functor::ReliefColormapFunctor
-   <LabelType, RGBPixelType>           ReliefColorMapFunctorType;
-
-  typedef otb::ImageMetadataInterfaceBase ImageMetadataInterfaceType;
-
-
-class KMeansClassification: public Application
+class KMeansApplicationBase : public CompositeApplication
 {
 public:
   /** Standard class typedefs. */
-  typedef KMeansClassification Self;
-  typedef Application Superclass;
+  typedef KMeansApplicationBase Self;
+  typedef CompositeApplication Superclass;
   typedef itk::SmartPointer<Self> Pointer;
   typedef itk::SmartPointer<const Self> ConstPointer;
 
   /** Standard macro */
-  itkNewMacro(Self);
+  itkTypeMacro( KMeansApplicationBase, Superclass )
 
-  itkTypeMacro(KMeansClassification, otb::Application);
-
-private:
-  void DoInit() ITK_OVERRIDE
+protected:
+  void InitKMParams()
   {
-    SetName("KMeansClassification");
-    SetDescription("Unsupervised KMeans image classification");
+    AddApplication("ImageEnvelope", "imgenvelop", "mean shift smoothing");
+    AddApplication("PolygonClassStatistics", "polystats", "Polygon Class Statistics");
+    AddApplication("SampleSelection", "select", "Sample selection");
+    AddApplication("SampleExtraction", "extraction", "Sample extraction");
+
+    AddApplication("TrainVectorClassifier", "training", "Model training");
+    AddApplication("ComputeImagesStatistics", "imgstats", "Compute Images second order statistics");
+    AddApplication("ImageClassifier", "classif", "Performs a classification of the input image");
+
+    ShareParameter("in", "imgenvelop.in");
+    ShareParameter("out", "classif.out");
+
+    InitKMSampling();
+    InitKMClassification();
+
+    // init at the end cleanup
+    AddParameter( ParameterType_Empty, "cleanup", "Temporary files cleaning" );
+    EnableParameter( "cleanup" );
+    SetParameterDescription( "cleanup",
+                           "If activated, the application will try to clean all temporary files it created" );
+    MandatoryOff( "cleanup" );
+  }
 
-    SetDocName("Unsupervised KMeans image classification");
-    SetDocLongDescription("Performs unsupervised KMeans image classification.");
-    SetDocLimitations("None");
-    SetDocAuthors("OTB-Team");
-    SetDocSeeAlso(" ");
+  void InitKMSampling()
+  {
+    AddParameter(ParameterType_Int, "nc", "Number of classes");
+    SetParameterDescription("nc", "Number of modes, which will be used to generate class membership.");
+    SetDefaultParameterInt("nc", 5);
 
-    AddDocTag(Tags::Learning);
-	AddDocTag(Tags::Segmentation);
-	
-    AddParameter(ParameterType_InputImage, "in", "Input Image");
-    SetParameterDescription("in", "Input image to classify.");
-    AddParameter(ParameterType_OutputImage, "out", "Output Image");
-    SetParameterDescription("out", "Output image containing the class indexes.");
-    SetDefaultOutputPixelType("out",ImagePixelType_uint8);
-
-    AddRAMParameter();
-
-    AddParameter(ParameterType_InputImage, "vm", "Validity Mask");
-    SetParameterDescription("vm", "Validity mask. Only non-zero pixels will be used to estimate KMeans modes.");
-    MandatoryOff("vm");
     AddParameter(ParameterType_Int, "ts", "Training set size");
     SetParameterDescription("ts", "Size of the training set (in pixels).");
     SetDefaultParameterInt("ts", 100);
     MandatoryOff("ts");
-    AddParameter(ParameterType_Int, "nc", "Number of classes");
-    SetParameterDescription("nc", "Number of modes, which will be used to generate class membership.");
-    SetDefaultParameterInt("nc", 5);
+
     AddParameter(ParameterType_Int, "maxit", "Maximum number of iterations");
     SetParameterDescription("maxit", "Maximum number of iterations for the learning step.");
     SetDefaultParameterInt("maxit", 1000);
     MandatoryOff("maxit");
-    AddParameter(ParameterType_Float, "ct", "Convergence threshold");
-    SetParameterDescription("ct", "Convergence threshold for class centroid  (L2 distance, by default 0.0001).");
-    SetDefaultParameterFloat("ct", 0.0001);
-    MandatoryOff("ct");
+
     AddParameter(ParameterType_OutputFilename, "outmeans", "Centroid filename");
     SetParameterDescription("outmeans", "Output text file containing centroid positions");
     MandatoryOff("outmeans");
 
-    AddRANDParameter();
-
-    // Doc example parameter settings
-    SetDocExampleParameterValue("in", "QB_1_ortho.tif");
-    SetDocExampleParameterValue("ts", "1000");
-    SetDocExampleParameterValue("nc", "5");
-    SetDocExampleParameterValue("maxit", "1000");
-    SetDocExampleParameterValue("ct", "0.0001");
-    SetDocExampleParameterValue("out", "ClassificationFilterOutput.tif");
-
-    SetOfficialDocLink();
+    ShareKMSamplingParameters();
+    ConnectKMSamplingParams();
   }
 
-  void DoUpdateParameters() ITK_OVERRIDE
+  void InitKMClassification()
   {
-    // test of input image //
-    if (HasValue("in"))
-      {
-      // input image
-      FloatVectorImageType::Pointer inImage = GetParameterImage("in");
+    ShareKMClassificationParams();
+    ConnectKMClassificationParams();
+  }
 
-      RAMDrivenStrippedStreamingManagerType::Pointer streamingManager = RAMDrivenStrippedStreamingManagerType::New();
-      int availableRAM = GetParameterInt("ram");
-      streamingManager->SetAvailableRAMInMB(availableRAM);
-      float bias = 1.5; // empirical value
-      streamingManager->SetBias(bias);
-      FloatVectorImageType::RegionType largestRegion = inImage->GetLargestPossibleRegion();
-      FloatVectorImageType::SizeType largestRegionSize = largestRegion.GetSize();
-      streamingManager->PrepareStreaming(inImage, largestRegion);
+  void ShareKMSamplingParameters()
+  {
+    ShareParameter("ram", "polystats.ram");
+    ShareParameter("sampler", "select.sampler");
+    ShareParameter("vm", "polystats.mask", "Validity Mask",
+      "Validity mask, only non-zero pixels will be used to estimate KMeans modes.");
+  }
 
-      unsigned long nbDivisions = streamingManager->GetNumberOfSplits();
-      unsigned long largestPixNb = largestRegionSize[0] * largestRegionSize[1];
+  void ShareKMClassificationParams()
+  {
+    ShareParameter("nodatalabel", "classif.nodatalabel", "Label mask value",
+      "By default, hidden pixels will have the assigned label 0 in the output image. "
+      "It's possible to define the label mask by another value, "
+      "but be careful to not take a label from another class. "
+      "This application initalize the labels from 0 to N-1, "
+      "N is the number of class (defined by 'nc' parameter).");
+  }
 
-      unsigned long maxPixNb = largestPixNb / nbDivisions;
+  void ConnectKMSamplingParams()
+  {
+    Connect("polystats.in", "imgenvelop.in");
 
-      if (GetParameterInt("ts") > static_cast<int> (maxPixNb))
-        {
-        otbAppLogWARNING("The available RAM is too small to process this sample size of " << GetParameterInt("ts") <<
-            " pixels. The sample size will be reduced to " << maxPixNb << " pixels." << std::endl);
-        this->SetParameterInt("ts",maxPixNb, false);
-        }
+    Connect("select.in", "polystats.in");
+    Connect("select.vec", "polystats.vec");
+    Connect("select.ram", "polystats.ram");
 
-      this->SetMaximumParameterIntValue("ts", maxPixNb);
-      }
+    Connect("extraction.in", "select.in");
+    Connect("extraction.field", "select.field");
+    Connect("extraction.vec", "select.out");
+    Connect("extraction.ram", "polystats.ram");
   }
 
-  void DoExecute() ITK_OVERRIDE
+  void ConnectKMClassificationParams()
   {
-    GetLogger()->Debug("Entering DoExecute\n");
+    Connect("training.cfield", "extraction.field");
+    Connect("training.io.stats","imgstats.out");
 
-    m_InImage = GetParameterImage("in");
-    m_InImage->UpdateOutputInformation();
-    UInt8ImageType::Pointer maskImage;
+    Connect("classif.in", "imgenvelop.in");
+    Connect("classif.model", "training.io.out");
+    Connect("classif.ram", "polystats.ram");
+    Connect("classif.imstat", "imgstats.out");
+  }
 
-    std::ostringstream message("");
+  void ConnectKMClassificationMask()
+  {
+    otbAppLogINFO("Using input mask ...");
+    Connect("select.mask", "polystats.mask");
+    Connect("classif.mask", "select.mask");
+  }
 
-    int nbsamples = GetParameterInt("ts");
-    const unsigned int nbClasses = GetParameterInt("nc");
+  void ComputeImageEnvelope(const std::string &vectorFileName)
+  {
+    GetInternalApplication("imgenvelop")->SetParameterString("out", vectorFileName, false);
+    GetInternalApplication("imgenvelop")->ExecuteAndWriteOutput();
+  }
 
-    /*******************************************/
-    /*           Sampling data                 */
-    /*******************************************/
+  void ComputeAddField(const std::string &vectorFileName,
+                       const std::string &fieldName)
+  {
+    otbAppLogINFO("add field in the layer ...");
+    otb::ogr::DataSource::Pointer ogrDS;
+    ogrDS = otb::ogr::DataSource::New(vectorFileName, otb::ogr::DataSource::Modes::Update_LayerUpdate);
+    otb::ogr::Layer layer = ogrDS->GetLayer(0);
+
+    OGRFieldDefn classField(fieldName.c_str(), OFTInteger);
+    classField.SetWidth(classField.GetWidth());
+    classField.SetPrecision(classField.GetPrecision());
+    ogr::FieldDefn classFieldDefn(classField);
+    layer.CreateField(classFieldDefn);
+
+    otb::ogr::Layer::const_iterator it = layer.cbegin();
+    otb::ogr::Layer::const_iterator itEnd = layer.cend();
+    for( ; it!=itEnd ; ++it)
+    {
+      ogr::Feature dstFeature(layer.GetLayerDefn());
+      dstFeature.SetFrom( *it, TRUE);
+      dstFeature.SetFID(it->GetFID());
+      dstFeature[fieldName].SetValue<int>(0);
+      layer.SetFeature(dstFeature);
+    }
+    const OGRErr err = layer.ogr().CommitTransaction();
+    if (err != OGRERR_NONE)
+      itkExceptionMacro(<< "Unable to commit transaction for OGR layer " << layer.ogr().GetName() << ".");
+    ogrDS->SyncToDisk();
+  }
 
-    otbAppLogINFO("-- SAMPLING DATA --"<<std::endl);
+  void ComputePolygonStatistics(const std::string &statisticsFileName,
+                                const std::string &fieldName)
+  {
+    std::vector<std::string> fieldList = {fieldName};
 
-    // Update input images information
-    m_InImage->UpdateOutputInformation();
+    GetInternalApplication("polystats")->SetParameterStringList("field", fieldList, false);
+    GetInternalApplication("polystats")->SetParameterString("out", statisticsFileName, false);
 
-    bool maskFlag = IsParameterEnabled("vm");
-    if (maskFlag)
-      {
-      otbAppLogINFO("sample choice using mask "<<std::endl);
-      maskImage = GetParameterUInt8Image("vm");
-      maskImage->UpdateOutputInformation();
-      if (m_InImage->GetLargestPossibleRegion() != maskImage->GetLargestPossibleRegion())
-        {
-        GetLogger()->Error("Mask image and input image have different sizes.");
-        return;
-        }
-      }
+    ExecuteInternal("polystats");
+  }
 
-    // Training sample lists
-    ListSampleType::Pointer sampleList = ListSampleType::New();
-    sampleList->SetMeasurementVectorSize(m_InImage->GetNumberOfComponentsPerPixel());
+  void SelectAndExtractSamples(const std::string &statisticsFileName,
+                               const std::string &fieldName,
+                               const std::string &sampleFileName,
+                               int NBSamples)
+  {
+    /* SampleSelection */
+    GetInternalApplication("select")->SetParameterString("out", sampleFileName, false);
 
-    //unsigned int init_means_index = 0;
+    UpdateInternalParameters("select");
+    GetInternalApplication("select")->SetParameterString("instats", statisticsFileName, false);
+    GetInternalApplication("select")->SetParameterString("field", fieldName, false);
 
-    // Sample dimension and max dimension
-    const unsigned int nbComp = m_InImage->GetNumberOfComponentsPerPixel();
-    unsigned int sampleSize = nbComp;
-    unsigned int totalSamples = 0;
+    GetInternalApplication("select")->SetParameterString("strategy", "constant", false);
+    GetInternalApplication("select")->SetParameterInt("strategy.constant.nb", NBSamples, false);
 
-    // sampleSize = std::min(nbComp, maxDim);
+    if( IsParameterEnabled("rand"))
+      GetInternalApplication("select")->SetParameterInt("rand", GetParameterInt("rand"), false);
 
-    EstimatorType::ParametersType initialMeans(nbComp * nbClasses);
-    initialMeans.Fill(0);
+    // select sample positions
+    ExecuteInternal("select");
 
-    // use image and mask shrink
+    /* SampleExtraction */
+    UpdateInternalParameters("extraction");
 
-    ImageSamplingFilterType::Pointer imageSampler = ImageSamplingFilterType::New();
-    imageSampler->SetInput(m_InImage);
+    GetInternalApplication("extraction")->SetParameterString("outfield", "prefix", false);
+    GetInternalApplication("extraction")->SetParameterString("outfield.prefix.name", "value_", false);
 
-    double theoricNBSamplesForKMeans = nbsamples;
+    // extract sample descriptors
+    GetInternalApplication("extraction")->ExecuteAndWriteOutput();
+  }
 
-    const double upperThresholdNBSamplesForKMeans = 1000 * 1000;
-    const double actualNBSamplesForKMeans = std::min(theoricNBSamplesForKMeans, upperThresholdNBSamplesForKMeans);
+  void TrainKMModel(FloatVectorImageType *image,
+                    const std::string &sampleTrainFileName,
+                    const std::string &modelFileName)
+  {
+    std::vector<std::string> extractOutputList = {sampleTrainFileName};
+    GetInternalApplication("training")->SetParameterStringList("io.vd", extractOutputList, false);
+    UpdateInternalParameters("training");
+
+    // set field names
+    std::string selectPrefix = GetInternalApplication("extraction")->GetParameterString("outfield.prefix.name");
+    unsigned int nbBands = image->GetNumberOfComponentsPerPixel();
+    std::vector<std::string> selectedNames;
+    for( unsigned int i = 0; i < nbBands; i++ )
+      {
+      std::ostringstream oss;
+      oss << i;
+      selectedNames.push_back( selectPrefix + oss.str() );
+      }
+    GetInternalApplication("training")->SetParameterStringList("feat", selectedNames, false);
 
-    otbAppLogINFO(<< actualNBSamplesForKMeans << " is the maximum sample size that will be used." << std::endl);
+    GetInternalApplication("training")->SetParameterString("classifier", "sharkkm", false);
+    GetInternalApplication("training")->SetParameterInt("classifier.sharkkm.maxiter",
+                                                        GetParameterInt("maxit"), false);
+    GetInternalApplication("training")->SetParameterInt("classifier.sharkkm.k",
+                                                        GetParameterInt("nc"), false);
 
-    const double shrinkFactor = vcl_floor(
-                                          vcl_sqrt(
-                                                   m_InImage->GetLargestPossibleRegion().GetNumberOfPixels()
-                                                       / actualNBSamplesForKMeans));
-    imageSampler->SetShrinkFactor(shrinkFactor);
-    imageSampler->Update();
+    if( IsParameterEnabled("rand"))
+      GetInternalApplication("training")->SetParameterInt("rand", GetParameterInt("rand"), false);
+    GetInternalApplication("training")->GetParameterByKey("v")->SetActive(false);
 
-    MaskSamplingFilterType::Pointer maskSampler;
-    LabeledIteratorType m_MaskIt;
-    if (maskFlag)
-      {
-      maskSampler = MaskSamplingFilterType::New();
-      maskSampler->SetInput(maskImage);
-      maskSampler->SetShrinkFactor(shrinkFactor);
-      maskSampler->Update();
-      m_MaskIt = LabeledIteratorType(maskSampler->GetOutput(), maskSampler->GetOutput()->GetLargestPossibleRegion());
-      m_MaskIt.GoToBegin();
-      }
-    // Then, build the sample list
+    GetInternalApplication("training")->SetParameterString("io.out", modelFileName, false);
 
-    IteratorType it(imageSampler->GetOutput(), imageSampler->GetOutput()->GetLargestPossibleRegion());
+    ExecuteInternal( "training" );
+    otbAppLogINFO("output model : " << GetInternalApplication("training")->GetParameterString("io.out"));
+  }
 
-    it.GoToBegin();
+  void ComputeImageStatistics(const std::string &imageFileName,
+                                               const std::string &imagesStatsFileName)
+  {
+    std::vector<std::string> imageFileNameList = {imageFileName};
+    GetInternalApplication("imgstats")->SetParameterStringList("il", imageFileNameList, false);
+    GetInternalApplication("imgstats")->SetParameterString("out", imagesStatsFileName, false);
 
-    SampleType min;
-    SampleType max;
-    SampleType sample;
-    //first sample
+    ExecuteInternal( "imgstats" );
+    otbAppLogINFO("image statistics file : " << GetInternalApplication("imgstats")->GetParameterString("out"));
+  }
 
-    itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen=itk::Statistics::MersenneTwisterRandomVariateGenerator::GetInstance();
 
-    //randGen->Initialize();
+  void KMeansClassif()
+  {
+    ExecuteInternal( "classif" );
+  }
 
-    if (maskFlag)
+  void CreateOutMeansFile(FloatVectorImageType *image,
+                          const std::string &modelFileName,
+                          unsigned int nbClasses)
+  {
+    if (IsParameterEnabled("outmeans"))
+    {
+      unsigned int nbBands = image->GetNumberOfComponentsPerPixel();
+      unsigned int nbElements = nbClasses * nbBands;
+      // get the line in model file that contains the centroids positions
+      std::ifstream infile(modelFileName);
+      if(!infile)
       {
-      while (!it.IsAtEnd() && !m_MaskIt.IsAtEnd() && (m_MaskIt.Get() <= 0))
-        {
-        ++it;
-        ++m_MaskIt;
-        }
-
-      // If the mask is empty after the subsampling
-      if (m_MaskIt.IsAtEnd())
-        {
-        GetLogger()->Error("The mask image is empty after subsampling. Please increase the training set size.");
-        return;
-        }
+        itkExceptionMacro(<< "File : " << modelFileName << " couldn't be opened");
       }
 
-    min = it.Get();
-    max = it.Get();
-    sample = it.Get();
+      // get the end line with the centroids
+      std::string line, centroidLine;
+      while(std::getline(infile,line))
+      {
+        if (!line.empty())
+          centroidLine = line;
+      }
 
-    sampleList->PushBack(sample);
+      std::vector<std::string> centroidElm;
+      boost::split(centroidElm,centroidLine,boost::is_any_of(" "));
 
-    ++it;
+      // remove the first elements, not the centroids positions
+      int nbWord = centroidElm.size();
+      int beginCentroid = nbWord-nbElements;
+      centroidElm.erase(centroidElm.begin(), centroidElm.begin()+beginCentroid);
 
-    if (maskFlag)
-      {
-      ++m_MaskIt;
-      }
+      // write in the output file
+      std::ofstream outfile;
+      outfile.open(GetParameterString("outmeans"));
 
-    totalSamples = 1;
-    bool selectSample;
-    while (!it.IsAtEnd())
+      for (unsigned int i = 0; i < nbClasses; i++)
       {
-      if (maskFlag)
+        for (unsigned int j = 0; j < nbBands; j++)
         {
-        selectSample = (m_MaskIt.Get() > 0);
-        ++m_MaskIt;
+          outfile << std::setw(8) << centroidElm[i * nbBands + j] << " ";
         }
-      else selectSample = true;
+        outfile << std::endl;
+      }
+    }
+  }
 
-      if (selectSample)
-        {
-        totalSamples++;
+  class KMeansFileNamesHandler
+    {
+    public :
+      KMeansFileNamesHandler(const std::string &outPath)
+      {
+        tmpVectorFile = outPath + "_imgEnvelope.shp";
+        polyStatOutput = outPath + "_polyStats.xml";
+        sampleOutput = outPath + "_sampleSelect.shp";
+        modelFile = outPath + "_model.txt";
+        imgStatOutput = outPath + "_imgstats.xml";
+      }
 
-        sample = it.Get();
+      void clear()
+      {
+        RemoveFile(tmpVectorFile);
+        RemoveFile(polyStatOutput);
+        RemoveFile(sampleOutput);
+        RemoveFile(modelFile);
+        RemoveFile(imgStatOutput);
+      }
 
-        sampleList->PushBack(sample);
+      std::string tmpVectorFile;
+      std::string polyStatOutput;
+      std::string sampleOutput;
+      std::string modelFile;
+      std::string imgStatOutput;
 
-        for (unsigned int i = 0; i < nbComp; ++i)
+    private:
+      bool RemoveFile(const std::string &filePath)
+      {
+        bool res = true;
+        if( itksys::SystemTools::FileExists( filePath.c_str() ) )
           {
-          if (min[i] > sample[i])
+          size_t posExt = filePath.rfind( '.' );
+          if( posExt != std::string::npos && filePath.compare( posExt, std::string::npos, ".shp" ) == 0 )
             {
-            min[i] = sample[i];
+            std::string shxPath = filePath.substr( 0, posExt ) + std::string( ".shx" );
+            std::string dbfPath = filePath.substr( 0, posExt ) + std::string( ".dbf" );
+            std::string prjPath = filePath.substr( 0, posExt ) + std::string( ".prj" );
+            RemoveFile( shxPath );
+            RemoveFile( dbfPath );
+            RemoveFile( prjPath );
             }
-          if (max[i] < sample[i])
+          res = itksys::SystemTools::RemoveFile( filePath.c_str() );
+          if( !res )
             {
-            max[i] = sample[i];
+            //otbAppLogINFO( <<"Unable to remove file  "<<filePath );
             }
           }
-        }
-      ++it;
+        return res;
       }
 
-    // Next, initialize centroids by random sampling in the generated
-    // list of samples
+    };
 
-    for (unsigned int classIndex = 0; classIndex < nbClasses; ++classIndex)
-      {
-      SampleType newCentroid = sampleList->GetMeasurementVector(randGen->GetIntegerVariate(sampleList->Size()-1));
+};
 
-      for (unsigned int compIndex = 0; compIndex < sampleSize; ++compIndex)
-        {
-        initialMeans[compIndex + classIndex * sampleSize] = newCentroid[compIndex];
-        }
-      }
-    otbAppLogINFO(<< totalSamples << " samples will be used as estimator input." << std::endl);
 
-    /*******************************************/
-    /*           Learning                      */
-    /*******************************************/
+class KMeansClassification: public KMeansApplicationBase
+{
+public:
+  /** Standard class typedefs. */
+  typedef KMeansClassification Self;
+  typedef KMeansApplicationBase Superclass;
+  typedef itk::SmartPointer<Self> Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
 
-    otbAppLogINFO("-- LEARNING --" << std::endl);
-    otbAppLogINFO("Initial centroids are: " << std::endl);
+  /** Standard macro */
+  itkNewMacro(Self);
 
-    message.str("");
-    message << std::endl;
-    for (unsigned int i = 0; i < nbClasses; i++)
-      {
-      message << "Class " << i << ": ";
-      for (unsigned int j = 0; j < sampleSize; j++)
-        {
-        message << std::setw(8) << initialMeans[i * sampleSize + j] << "   ";
-        }
-      message << std::endl;
-      }
-    message << std::endl;
-    GetLogger()->Info(message.str());
-    message.str("");
-    otbAppLogINFO("Starting optimization." << std::endl);
-    EstimatorType::Pointer estimator = EstimatorType::New();
+  itkTypeMacro(Self, Superclass);
 
-    TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New();
-    treeGenerator->SetSample(sampleList);
+private:
+  void DoInit() ITK_OVERRIDE
+  {
+    SetName("KMeansClassification");
+    SetDescription("Unsupervised KMeans image classification");
 
-    treeGenerator->SetBucketSize(10000);
-    treeGenerator->Update();
+    SetDocName("Unsupervised KMeans image classification");
+    SetDocLongDescription("Performs unsupervised KMeans image classification."
+      "KMeansClassification is a composite application, "
+      "using an existing training and classification application."
+      "The SharkKMeans model is used.\n"
+      "The steps of this composite application :\n"
+        "1) ImageEnveloppe : create a shapefile (1 polygon),\n"
+        "2) PolygonClassStatistics : compute the statistics,\n"
+        "3) SampleSelection : select the samples by constant strategy in the shapefile "
+            "(1000000 samples max),\n"
+        "4) SamplesExtraction : extract the samples descriptors (update of SampleSelection output file),\n"
+        "5) ComputeImagesStatistics : compute images second order statistics,\n"
+        "6) TrainVectorClassifier : train the SharkKMeans model,\n"
+        "7) ImageClassifier : performs the classification of the input image "
+            "according to a model file.\n\n"
+        "It's possible to choice random/periodic modes of the SampleSelection application.\n"
+        "If you want keep the temporary files (sample selected, model file, ...), "
+        "initialize cleanup parameter.\n"
+        "For more information on shark KMeans algorithm [1].");
 
-    estimator->SetParameters(initialMeans);
-    estimator->SetKdTree(treeGenerator->GetOutput());
-    int maxIt = GetParameterInt("maxit");
-    estimator->SetMaximumIteration(maxIt);
-    estimator->SetCentroidPositionChangesThreshold(GetParameterFloat("ct"));
-    estimator->StartOptimization();
+    SetDocLimitations("None");
+    SetDocAuthors("OTB-Team");
+    SetDocSeeAlso("ImageEnveloppe PolygonClassStatistics SampleSelection SamplesExtraction "
+      "PolygonClassStatistics TrainVectorClassifier ImageClassifier\n"
+      "[1] http://image.diku.dk/shark/sphinx_pages/build/html/rest_sources/tutorials/algorithms/kmeans.html");
 
-    EstimatorType::ParametersType estimatedMeans = estimator->GetParameters();
+    AddDocTag(Tags::Learning);
+    AddDocTag(Tags::Segmentation);
 
-    otbAppLogINFO("Optimization completed." );
-    if (estimator->GetCurrentIteration() == maxIt)
-      {
-      otbAppLogWARNING("The estimator reached the maximum iteration number." << std::endl);
-      }
-    message.str("");
-    message << "Estimated centroids are: " << std::endl;
-    message << std::endl;
-    for (unsigned int i = 0; i < nbClasses; i++)
-      {
-      message << "Class " << i << ": ";
-      for (unsigned int j = 0; j < sampleSize; j++)
-        {
-        message << std::setw(8) << estimatedMeans[i * sampleSize + j] << "   ";
-        }
-      message << std::endl;
-      }
+    // Perform initialization
+    ClearApplications();
 
-    message << std::endl;
-    message << "Learning completed." << std::endl;
-    message << std::endl;
-    GetLogger()->Info(message.str());
+    // initialisation parameters and synchronizes parameters
+    Superclass::InitKMParams();
 
-    /*******************************************/
-    /*           Classification                */
-    /*******************************************/
-    otbAppLogINFO("-- CLASSIFICATION --" << std::endl);
+    AddRANDParameter();
 
-    // Finally, update the KMeans filter
-    KMeansFunctorType functor;
+    // Doc example parameter settings
+    SetDocExampleParameterValue("in", "QB_1_ortho.tif");
+    SetDocExampleParameterValue("ts", "1000");
+    SetDocExampleParameterValue("nc", "5");
+    SetDocExampleParameterValue("maxit", "1000");
+    SetDocExampleParameterValue("out", "ClassificationFilterOutput.tif uint8");
 
-    for (unsigned int classIndex = 0; classIndex < nbClasses; ++classIndex)
-      {
-      SampleType centroid(sampleSize);
+    SetOfficialDocLink();
+  }
 
-      for (unsigned int compIndex = 0; compIndex < sampleSize; ++compIndex)
-        {
-        centroid[compIndex] = estimatedMeans[compIndex + classIndex * sampleSize];
-        }
-      functor.AddCentroid(classIndex, centroid);
-      }
+  void DoUpdateParameters() ITK_OVERRIDE
+  {
+  }
 
-    m_KMeansFilter = KMeansFilterType::New();
-    m_KMeansFilter->SetFunctor(functor);
-    m_KMeansFilter->SetInput(m_InImage);
+  void DoExecute() ITK_OVERRIDE
+  {
+    if (IsParameterEnabled("vm") && HasValue("vm")) Superclass::ConnectKMClassificationMask();
 
-    // optional saving option -> lut
+    KMeansFileNamesHandler fileNames(GetParameterString("out"));
 
-    if (IsParameterEnabled("outmeans"))
-      {
-      std::ofstream file;
-      file.open(GetParameterString("outmeans").c_str());
-      for (unsigned int i = 0; i < nbClasses; i++)
-        {
+    const std::string fieldName = "field";
 
-        for (unsigned int j = 0; j < sampleSize; j++)
-          {
-          file << std::setw(8) << estimatedMeans[i * sampleSize + j] << " ";
-          }
-        file << std::endl;
-        }
+    // Create an image envelope
+    Superclass::ComputeImageEnvelope(fileNames.tmpVectorFile);
+    // Add a new field at the ImageEnvelope output file
+    Superclass::ComputeAddField(fileNames.tmpVectorFile, fieldName);
 
-      file.close();
-      }
+    // Compute PolygonStatistics app
+    UpdateKMPolygonClassStatisticsParameters(fileNames.tmpVectorFile);
+    Superclass::ComputePolygonStatistics(fileNames.polyStatOutput, fieldName);
+
+    // Compute number of sample max for KMeans
+    const int theoricNBSamplesForKMeans = GetParameterInt("ts");
+    const int upperThresholdNBSamplesForKMeans = 1000 * 1000;
+    const int actualNBSamplesForKMeans = std::min(theoricNBSamplesForKMeans,
+                                                  upperThresholdNBSamplesForKMeans);
+    otbAppLogINFO(<< actualNBSamplesForKMeans << " is the maximum sample size that will be used." \
+                  << std::endl);
+
+    // Compute SampleSelection and SampleExtraction app
+    Superclass::SelectAndExtractSamples(fileNames.polyStatOutput, fieldName,
+                                        fileNames.sampleOutput,
+                                        actualNBSamplesForKMeans);
+
+    // Compute Images second order statistics
+    Superclass::ComputeImageStatistics(GetParameterString("in"), fileNames.imgStatOutput);
+
+    // Compute a train model with TrainVectorClassifier app
+    Superclass::TrainKMModel(GetParameterImage("in"), fileNames.sampleOutput,
+                             fileNames.modelFile);
+
+    // Compute a classification of the input image according to a model file
+    Superclass::KMeansClassif();
 
-    SetParameterOutputImage("out", m_KMeansFilter->GetOutput());
+    // Create the output text file containing centroids positions
+    Superclass::CreateOutMeansFile(GetParameterImage("in"), fileNames.modelFile, GetParameterInt("nc"));
 
+    // Remove all tempory files
+    if( IsParameterEnabled( "cleanup" ) )
+      {
+      otbAppLogINFO( <<"Final clean-up ..." );
+      fileNames.clear();
+      }
   }
 
-  // KMeans filter
-  KMeansFilterType::Pointer           m_KMeansFilter;
-  FloatVectorImageType::Pointer       m_InImage;
+  void UpdateKMPolygonClassStatisticsParameters(const std::string &vectorFileName)
+  {
+    GetInternalApplication( "polystats" )->SetParameterString( "vec", vectorFileName, false );
+    UpdateInternalParameters( "polystats" );
+  }
 
 };
 
-
 }
 }
 
 OTB_APPLICATION_EXPORT(otb::Wrapper::KMeansClassification)
 
-
diff --git a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
index 21d3b58552dcd0799d2849632d57093238a7d489..1d1d387c131d3c49fe9402f108e6f1880dd24792 100644
--- a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
+++ b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
@@ -110,9 +110,12 @@ protected:
     contingencyTableCalculator->Compute(performanceLabeledListSample->Begin(),
                                         performanceLabeledListSample->End(),predictedListSample->Begin(), predictedListSample->End());
 
-    otbAppLogINFO( "Training performances:" );
-
-    otbAppLogINFO(<<"Contingency table: reference labels (rows) vs. produced labels (cols)\n"<<contingencyTableCalculator->BuildContingencyTable());
+    if(IsParameterEnabled("v"))
+    {
+      otbAppLogINFO( "Training performances:" );
+      otbAppLogINFO(<<"Contingency table: reference labels (rows) vs. produced labels (cols)\n"
+        <<contingencyTableCalculator->BuildContingencyTable());
+    }
 
     return contingencyTableCalculator->BuildContingencyTable();
   }
diff --git a/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx b/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx
index 0046cfde6bcb56bbfd843a5b1dd24fe90b2ef9dc..2c3575c2ead20381438f9f66d5cd9c65c1723132 100644
--- a/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx
+++ b/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx
@@ -91,6 +91,10 @@ void TrainVectorBase::DoInit()
     "The contingency table is output when we unsupervised algorithms is used otherwise the confusion matrix is output." );
   MandatoryOff( "io.confmatout" );
 
+  AddParameter(ParameterType_Empty, "v", "Verbose mode");
+  EnableParameter("v");
+  SetParameterDescription("v", "Verbose mode, display the contingency table result.");
+  MandatoryOff("v");
 
   // Doc example parameter settings
   SetDocExampleParameterValue( "io.vd", "vectorData.shp" );
diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt
index aa7e33d744d5eda1191e327bcc5ea8afe13da102..fae1474266db959a03a6accd01b14b43f00f4ec0 100644
--- a/Modules/Applications/AppClassification/test/CMakeLists.txt
+++ b/Modules/Applications/AppClassification/test/CMakeLists.txt
@@ -629,20 +629,24 @@ if(OTB_DATA_USE_LARGEINPUT)
 endif()
 
 #----------- KMeansClassification TESTS ----------------
-otb_test_application(NAME apTvClKMeansImageClassification
-  APP  KMeansClassification
-  OPTIONS -in ${INPUTDATA}/qb_RoadExtract.img
-  -vm ${INPUTDATA}/qb_RoadExtract_mask.png
-  -ts 30000
-  -nc 5
-  -maxit 10000
-  -ct 0.0000001
-  -rand 121212
-  -out ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif
-  VALID   --compare-image ${NOTOL}
-  ${OTBAPP_BASELINE}/apTvClKMeansImageClassificationFilterOutput.tif
-  ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif )
-
+if(OTB_USE_SHARK)
+  otb_test_application(NAME apTvClKMeansImageClassification_composite
+    APP  KMeansClassification
+    OPTIONS -in ${INPUTDATA}/qb_RoadExtract.img
+    -vm ${INPUTDATA}/qb_RoadExtract_mask_binary.png
+    -ts 30000
+    -nc 5
+    -maxit 10000
+    -sampler periodic
+    -rand 121212
+    -nodatalabel 255
+    -outmeans ${TEMP}/apTvClKMeansImageClassificationFilterOutMeans.txt
+    -out ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif uint8
+    -cleanup 0
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/apTvClKMeansImageClassificationFilterOutput.tif
+    ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif )
+endif()
 
 #----------- TrainImagesClassifier TESTS ----------------
 if(OTB_USE_LIBSVM)
diff --git a/Modules/Learning/Sampling/include/otbImageSampleExtractorFilter.txx b/Modules/Learning/Sampling/include/otbImageSampleExtractorFilter.txx
index 8e3850392e50d3cf6ca64eae74d49d4e1dd0133c..d5faa1064cb5c5a589dba2d64f5909d07007ff24 100644
--- a/Modules/Learning/Sampling/include/otbImageSampleExtractorFilter.txx
+++ b/Modules/Learning/Sampling/include/otbImageSampleExtractorFilter.txx
@@ -175,6 +175,7 @@ PersistentImageSampleExtractorFilter<TInputImage>
   IndexType imgIndex;
   PixelType imgPixel;
   double imgComp;
+
   ogr::Layer::const_iterator featIt = layerForThread.begin();
   for(; featIt!=layerForThread.end(); ++featIt)
     {