diff --git a/CMake/OTBModuleHeaderTest.cmake b/CMake/OTBModuleHeaderTest.cmake
index 186c56d487f76374968d54fcd6bbfc8cef13e106..ca2f7cb5ef27ff712e52ed3d05d55d2c499d298b 100644
--- a/CMake/OTBModuleHeaderTest.cmake
+++ b/CMake/OTBModuleHeaderTest.cmake
@@ -46,7 +46,7 @@ if(NOT OTB_USE_OPENCV)
 	SET(BANNED_HEADERS "${BANNED_HEADERS} otbDecisionTreeMachineLearningModelFactory.h otbDecisionTreeMachineLearningModel.h otbKNearestNeighborsMachineLearningModelFactory.h otbKNearestNeighborsMachineLearningModel.h otbRandomForestsMachineLearningModelFactory.h otbRandomForestsMachineLearningModel.h otbSVMMachineLearningModelFactory.h otbSVMMachineLearningModel.h otbGradientBoostedTreeMachineLearningModelFactory.h otbGradientBoostedTreeMachineLearningModel.h otbBoostMachineLearningModelFactory.h otbBoostMachineLearningModel.h otbNeuralNetworkMachineLearningModelFactory.h otbNeuralNetworkMachineLearningModel.h otbNormalBayesMachineLearningModelFactory.h otbNormalBayesMachineLearningModel.h otbRequiresOpenCVCheck.h otbOpenCVUtils.h otbCvRTreesWrapper.h")
 endif()
 if(NOT OTB_USE_SHARK)
-  SET(BANNED_HEADERS "${BANNED_HEADERS} otbSharkRandomForestsMachineLearningModel.h otbSharkRandomForestsMachineLearningModel.txx otbSharkUtils.h otbRequiresSharkCheck.h otbSharkRandomForestsMachineLearningModelFactory.h")  
+  SET(BANNED_HEADERS "${BANNED_HEADERS} otbSharkRandomForestsMachineLearningModel.h otbSharkRandomForestsMachineLearningModel.txx otbSharkUtils.h otbRequiresSharkCheck.h otbSharkRandomForestsMachineLearningModelFactory.h  otbSharkKMeansMachineLearningModel.h otbSharkKMeansMachineLearningModel.txx otbSharkKMeansMachineLearningModelFactory.h otbSharkKMeansMachineLearningModelFactory.txx")
 endif()
 if(NOT OTB_USE_LIBSVM)
 	SET(BANNED_HEADERS "${BANNED_HEADERS} otbLibSVMMachineLearningModel.h otbLibSVMMachineLearningModelFactory.h")
@@ -64,7 +64,7 @@ endif()
 
 
 macro( otb_module_headertest _name )
-  if( NOT ${_name}_THIRD_PARTY 
+  if( NOT ${_name}_THIRD_PARTY
       AND EXISTS ${${_name}_SOURCE_DIR}/include
       AND PYTHON_EXECUTABLE
       AND NOT (PYTHON_VERSION_STRING VERSION_LESS 2.6)
diff --git a/Modules/Applications/AppClassification/app/CMakeLists.txt b/Modules/Applications/AppClassification/app/CMakeLists.txt
index e28ab734b922b98a067cee9aac88ddc4c832a44f..0d1475994c11bb266b43ef15309394172fee38b3 100644
--- a/Modules/Applications/AppClassification/app/CMakeLists.txt
+++ b/Modules/Applications/AppClassification/app/CMakeLists.txt
@@ -70,7 +70,6 @@ otb_create_application(
   SOURCES        otbTrainVectorClassifier.cxx
   LINK_LIBRARIES ${${otb-module}_LIBRARIES})
 
-
 otb_create_application(
   NAME           ComputeConfusionMatrix
   SOURCES        otbComputeConfusionMatrix.cxx
diff --git a/Modules/Applications/AppClassification/app/otbTrainImagesClassifier.cxx b/Modules/Applications/AppClassification/app/otbTrainImagesClassifier.cxx
index 9797ec0fadd347f65f39ec561868e59074ac4f6b..2a84ff53e78b81d71c5eddc3f32967f5cd92fb0b 100644
--- a/Modules/Applications/AppClassification/app/otbTrainImagesClassifier.cxx
+++ b/Modules/Applications/AppClassification/app/otbTrainImagesClassifier.cxx
@@ -18,513 +18,210 @@
  * limitations under the License.
  */
 
-#include "otbWrapperCompositeApplication.h"
-#include "otbWrapperApplicationFactory.h"
-
-#include "otbOGRDataToSamplePositionFilter.h"
-#include "otbSamplingRateCalculator.h"
+#include "otbTrainImagesBase.h"
 
 namespace otb
 {
 namespace Wrapper
 {
 
-class TrainImagesClassifier: public CompositeApplication
+class TrainImagesClassifier : public TrainImagesBase
 {
 public:
-  /** Standard class typedefs. */
-  typedef TrainImagesClassifier Self;
-  typedef CompositeApplication Superclass;
-  typedef itk::SmartPointer<Self> Pointer;
+  typedef TrainImagesClassifier         Self;
+  typedef TrainImagesBase               Superclass;
+  typedef itk::SmartPointer<Self>       Pointer;
   typedef itk::SmartPointer<const Self> ConstPointer;
-
-  /** Standard macro */
-  itkNewMacro(Self)
-
-  itkTypeMacro(TrainImagesClassifier, otb::Wrapper::CompositeApplication)
-
-  /** filters typedefs*/
-  typedef otb::OGRDataToSamplePositionFilter<
-    FloatVectorImageType,
-    UInt8ImageType,
-    otb::PeriodicSampler>                           PeriodicSamplerType;
-
-  typedef otb::SamplingRateCalculator::MapRateType  MapRateType;
-
-protected:
-
-private:
-
-bool RemoveFile(std::string &filePath)
-{
-  bool res = true;
-  if(itksys::SystemTools::FileExists(filePath.c_str()))
-    {
-    size_t posExt = filePath.rfind('.');
-    if (posExt != std::string::npos &&
-        filePath.compare(posExt,std::string::npos,".shp") == 0)
+  itkNewMacro( Self )
+  itkTypeMacro( Self, Superclass )
+
+  void DoInit() ITK_OVERRIDE
+  {
+    SetName( "TrainImagesClassifier" );
+    SetDescription( "Train a classifier from multiple pairs of images and training vector data." );
+
+    // Documentation
+    SetDocName( "Train a classifier from multiple images" );
+    SetDocLongDescription(
+            "This application performs a classifier training from multiple pairs of input images and training vector data. "
+                    "Samples are composed of pixel values in each band optionally centered and reduced using an XML statistics file produced by "
+                    "the ComputeImagesStatistics application.\n The training vector data must contain polygons with a positive integer field "
+                    "representing the class label. The name of this field can be set using the \"Class label field\" parameter. Training and validation "
+                    "sample lists are built such that each class is equally represented in both lists. One parameter allows controlling the ratio "
+                    "between the number of samples in training and validation sets. Two parameters allow managing the size of the training and "
+                    "validation sets per class and per image.\n Several classifier parameters can be set depending on the chosen classifier. In the "
+                    "validation process, the confusion matrix is organized the following way: rows = reference labels, columns = produced labels. "
+                    "In the header of the optional confusion matrix output file, the validation (reference) and predicted (produced) class labels"
+                    " are ordered according to the rows/columns of the confusion matrix.\n This application is based on LibSVM and OpenCV Machine Learning "
+                    "(2.3.1 and later)." );
+    SetDocLimitations( "None" );
+    SetDocAuthors( "OTB-Team" );
+    SetDocSeeAlso( "OpenCV documentation for machine learning http://docs.opencv.org/modules/ml/doc/ml.html " );
+
+    AddDocTag( Tags::Learning );
+
+    // Perform initialization
+    ClearApplications();
+    InitIO();
+    InitSampling();
+    InitClassification();
+
+    AddDocTag( Tags::Learning );
+
+    // Doc example parameter settings
+    SetDocExampleParameterValue( "io.il", "QB_1_ortho.tif" );
+    SetDocExampleParameterValue( "io.vd", "VectorData_QB1.shp" );
+    SetDocExampleParameterValue( "io.imstat", "EstimateImageStatisticsQB1.xml" );
+    SetDocExampleParameterValue( "sample.mv", "100" );
+    SetDocExampleParameterValue( "sample.mt", "100" );
+    SetDocExampleParameterValue( "sample.vtr", "0.5" );
+    SetDocExampleParameterValue( "sample.vfn", "Class" );
+    SetDocExampleParameterValue( "classifier", "libsvm" );
+    SetDocExampleParameterValue( "classifier.libsvm.k", "linear" );
+    SetDocExampleParameterValue( "classifier.libsvm.c", "1" );
+    SetDocExampleParameterValue( "classifier.libsvm.opt", "false" );
+    SetDocExampleParameterValue( "io.out", "svmModelQB1.txt" );
+    SetDocExampleParameterValue( "io.confmatout", "svmConfusionMatrixQB1.csv" );
+  }
+
+  void DoUpdateParameters() ITK_OVERRIDE
+  {
+    if( HasValue( "io.vd" ) && IsParameterEnabled( "io.vd" ))
       {
-      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);
+      UpdatePolygonClassStatisticsParameters();
       }
-    res = itksys::SystemTools::RemoveFile(filePath.c_str());
-    if (!res)
+  }
+
+  /**
+   * Select and Extract samples for validation with computed statistics and rates.
+   * Validation samples could be empty if sample.vrt == 0 and if no dedicated validation are provided.
+   * If no dedicated validation is provided the training is split corresponding to the sample.vtr parameter,
+   * in this case if no vector data have been provided, the training rates and statistics are computed
+   * on the selection and extraction training result.
+   * fileNames.sampleOutputs contains training data and after an ExtractValidationData training data will
+   * be split to fileNames.sampleTrainOutputs.
+   * \param imageList
+   * \param fileNames
+   * \param validationVectorFileList
+   * \param rates
+   * \param HasInputVector
+   */
+  void ExtractValidationData(FloatVectorImageListType *imageList, TrainFileNamesHandler& fileNames,
+                             std::vector<std::string> validationVectorFileList,
+                             const SamplingRates& rates, bool itkNotUsed(HasInputVector) )
+  {
+    if( !validationVectorFileList.empty() ) // Compute class statistics and sampling rate of validation data if provided.
       {
-      otbAppLogINFO(<<"Unable to remove file  "<<filePath);
+      ComputePolygonStatistics( imageList, validationVectorFileList, fileNames.polyStatValidOutputs );
+      ComputeSamplingRate( fileNames.polyStatValidOutputs, fileNames.rateValidOut, rates.fmv );
+      SelectAndExtractValidationSamples( fileNames, imageList, validationVectorFileList );
+//    if( HasInputVector ) // if input vector is provided the sampleTrainOutputs is the previously extracted sampleOutputs
+      fileNames.sampleTrainOutputs = fileNames.sampleOutputs;
       }
-    }
-  return res;
-}
-
-void DoInit() ITK_OVERRIDE
-{
-  SetName("TrainImagesClassifier");
-  SetDescription(
-    "Train a classifier from multiple pairs of images and training vector data.");
-
-  // Documentation
-  SetDocName("Train a classifier from multiple images");
-  SetDocLongDescription(
-    "This application performs a classifier training from multiple pairs of input images and training vector data. "
-    "Samples are composed of pixel values in each band optionally centered and reduced using an XML statistics file produced by "
-    "the ComputeImagesStatistics application.\n The training vector data must contain polygons with a positive integer field "
-    "representing the class label. The name of this field can be set using the \"Class label field\" parameter. Training and validation "
-    "sample lists are built such that each class is equally represented in both lists. One parameter allows controlling the ratio "
-    "between the number of samples in training and validation sets. Two parameters allow managing the size of the training and "
-    "validation sets per class and per image.\n Several classifier parameters can be set depending on the chosen classifier. In the "
-    "validation process, the confusion matrix is organized the following way: rows = reference labels, columns = produced labels. "
-    "In the header of the optional confusion matrix output file, the validation (reference) and predicted (produced) class labels"
-    " are ordered according to the rows/columns of the confusion matrix.\n This application is based on LibSVM and OpenCV Machine Learning "
-    "(2.3.1 and later).");
-  SetDocLimitations("None");
-  SetDocAuthors("OTB-Team");
-  SetDocSeeAlso("OpenCV documentation for machine learning http://docs.opencv.org/modules/ml/doc/ml.html ");
-
-  AddDocTag(Tags::Learning);
-
-  ClearApplications();
-  AddApplication("PolygonClassStatistics", "polystat","Polygon analysis");
-  AddApplication("MultiImageSamplingRate", "rates", "Sampling rates");
-  AddApplication("SampleSelection", "select", "Sample selection");
-  AddApplication("SampleExtraction","extraction", "Sample extraction");
-  AddApplication("TrainVectorClassifier", "training", "Model training");
-
-  //Group IO
-  AddParameter(ParameterType_Group, "io", "Input and output data");
-  SetParameterDescription("io", "This group of parameters allows setting input and output data.");
-
-  AddParameter(ParameterType_InputImageList, "io.il", "Input Image List");
-  SetParameterDescription("io.il", "A list of input images.");
-  AddParameter(ParameterType_InputVectorDataList, "io.vd", "Input Vector Data List");
-  SetParameterDescription("io.vd", "A list of vector data to select the training samples.");
-
-  AddParameter(ParameterType_InputVectorDataList, "io.valid", "Validation Vector Data List");
-  SetParameterDescription("io.valid", "A list of vector data to select the training samples.");
-  MandatoryOff("io.valid");
-
-  ShareParameter("io.imstat","training.io.stats");
-  ShareParameter("io.confmatout","training.io.confmatout");
-  ShareParameter("io.out","training.io.out");
-
-  ShareParameter("elev","polystat.elev");
-
-  // Sampling settings
-  AddParameter(ParameterType_Group, "sample", "Training and validation samples parameters");
-  SetParameterDescription("sample",
-    "This group of parameters allows you to set training and validation sample lists parameters.");
-  AddParameter(ParameterType_Int, "sample.mt", "Maximum training sample size per class");
-  SetDefaultParameterInt("sample.mt", 1000);
-  SetParameterDescription("sample.mt", "Maximum size per class (in pixels) of "
-    "the training sample list (default = 1000) (no limit = -1). If equal to -1,"
-    " then the maximal size of the available training sample list per class "
-    "will be equal to the surface area of the smallest class multiplied by the"
-    " training sample ratio.");
-  AddParameter(ParameterType_Int, "sample.mv", "Maximum validation sample size per class");
-  SetDefaultParameterInt("sample.mv", 1000);
-  SetParameterDescription("sample.mv", "Maximum size per class (in pixels) of "
-    "the validation sample list (default = 1000) (no limit = -1). If equal to -1,"
-    " then the maximal size of the available validation sample list per class "
-    "will be equal to the surface area of the smallest class multiplied by the "
-    "validation sample ratio.");
-  AddParameter(ParameterType_Int, "sample.bm", "Bound sample number by minimum");
-  SetDefaultParameterInt("sample.bm", 1);
-  SetParameterDescription("sample.bm", "Bound the number of samples for each "
-    "class by the number of available samples by the smaller class. Proportions "
-    "between training and validation are respected. Default is true (=1).");
-  AddParameter(ParameterType_Float, "sample.vtr", "Training and validation sample ratio");
-  SetParameterDescription("sample.vtr",
-    "Ratio between training and validation samples (0.0 = all training, 1.0 = "
-    "all validation) (default = 0.5).");
-  SetParameterFloat("sample.vtr",0.5, false);
-  SetMaximumParameterFloatValue("sample.vtr",1.0);
-  SetMinimumParameterFloatValue("sample.vtr",0.0);
-
-  ShareParameter("sample.vfn","polystat.field");
-
-  // hide sampling parameters
-  //ShareParameter("sample.strategy","rates.strategy");
-  //ShareParameter("sample.mim","rates.mim");
-
-  // Classifier settings
-  ShareParameter("classifier","training.classifier");
-
-  ShareParameter("rand","training.rand");
-
-  // Synchronization between applications
-  Connect("select.field", "polystat.field");
-  Connect("select.layer", "polystat.layer");
-  Connect("select.elev",  "polystat.elev");
-
-  Connect("extraction.in",    "select.in");
-  Connect("extraction.vec",   "select.out");
-  Connect("extraction.field", "polystat.field");
-  Connect("extraction.layer", "polystat.layer");
-
-  Connect("training.cfield", "polystat.field");
-
-  ShareParameter("ram","polystat.ram");
-  Connect("select.ram", "polystat.ram");
-  Connect("extraction.ram", "polystat.ram");
-
-  Connect("select.rand", "training.rand");
-
-  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");
-
-  // Doc example parameter settings
-  SetDocExampleParameterValue("io.il", "QB_1_ortho.tif");
-  SetDocExampleParameterValue("io.vd", "VectorData_QB1.shp");
-  SetDocExampleParameterValue("io.imstat", "EstimateImageStatisticsQB1.xml");
-  SetDocExampleParameterValue("sample.mv", "100");
-  SetDocExampleParameterValue("sample.mt", "100");
-  SetDocExampleParameterValue("sample.vtr", "0.5");
-  SetDocExampleParameterValue("sample.vfn", "Class");
-  SetDocExampleParameterValue("classifier", "libsvm");
-  SetDocExampleParameterValue("classifier.libsvm.k", "linear");
-  SetDocExampleParameterValue("classifier.libsvm.c", "1");
-  SetDocExampleParameterValue("classifier.libsvm.opt", "false");
-  SetDocExampleParameterValue("io.out", "svmModelQB1.txt");
-  SetDocExampleParameterValue("io.confmatout", "svmConfusionMatrixQB1.csv");
-}
-
-void DoUpdateParameters() ITK_OVERRIDE
-{
-  if ( HasValue("io.vd") )
-    {
-    std::vector<std::string> vectorFileList = GetParameterStringList("io.vd");
-    GetInternalApplication("polystat")->SetParameterString("vec",vectorFileList[0], false);
-    UpdateInternalParameters("polystat");
-    }
-}
-
-void DoExecute() ITK_OVERRIDE
-{
-  FloatVectorImageListType* imageList = GetParameterImageList("io.il");
-  std::vector<std::string> vectorFileList = GetParameterStringList("io.vd");
-  unsigned int nbInputs = imageList->Size();
-  if (nbInputs > vectorFileList.size())
-    {
-    otbAppLogFATAL("Missing input vector data files to match number of images ("<<nbInputs<<").");
-    }
-
-  // check if validation vectors are given
-  std::vector<std::string> validationVectorFileList;
-  bool dedicatedValidation = false;
-  if (IsParameterEnabled("io.valid") && HasValue("io.valid"))
-    {
-    dedicatedValidation = true;
-    validationVectorFileList = GetParameterStringList("io.valid");
-    if (nbInputs > validationVectorFileList.size())
+    else if(GetParameterFloat("sample.vtr") != 0.0)// Split training data to validation
       {
-      otbAppLogFATAL("Missing validation vector data files to match number of images ("<<nbInputs<<").");
+//      if( !HasInputVector ) // Compute one class statistics and sampling rate for the generated vector.
+//        ComputePolygonStatistics( imageList, fileNames.sampleOutputs, fileNames.polyStatTrainOutputs );
+//        ComputeSamplingRate( fileNames.polyStatTrainOutputs, fileNames.rateTrainOut, rates.fmt );
+      SplitTrainingToValidationSamples( fileNames, imageList );
       }
-    }
-
-  // Prepare temporary file names
-  std::string outModel(GetParameterString("io.out"));
-  std::vector<std::string> polyStatTrainOutputs;
-  std::vector<std::string> polyStatValidOutputs;
-  std::vector<std::string> ratesTrainOutputs;
-  std::vector<std::string> ratesValidOutputs;
-  std::vector<std::string> sampleOutputs;
-  std::vector<std::string> sampleTrainOutputs;
-  std::vector<std::string> sampleValidOutputs;
-  std::string rateTrainOut;
-  if (dedicatedValidation)
-    {
-    rateTrainOut = outModel + "_ratesTrain.csv";
-    }
-  else
-    {
-    rateTrainOut = outModel + "_rates.csv";
-    }
-  std::string rateValidOut(outModel + "_ratesValid.csv");
-  for (unsigned int i=0 ; i<nbInputs ; i++)
-    {
-    std::ostringstream oss;
-    oss <<i+1;
-    std::string strIndex(oss.str());
-    if (dedicatedValidation)
+    else // nothing to do, except update fileNames
       {
-      polyStatTrainOutputs.push_back(outModel + "_statsTrain_" + strIndex + ".xml");
-      polyStatValidOutputs.push_back(outModel + "_statsValid_" + strIndex + ".xml");
-      ratesTrainOutputs.push_back(outModel + "_ratesTrain_" + strIndex + ".csv");
-      ratesValidOutputs.push_back(outModel + "_ratesValid_" + strIndex + ".csv");
-      sampleOutputs.push_back(outModel + "_samplesTrain_" + strIndex + ".shp");
+      fileNames.sampleTrainOutputs = fileNames.sampleOutputs;
       }
-    else
+  }
+
+  /**
+   * Extract Training data depending if input vector is provided
+   * \param imageList list of the image
+   * \param fileNames handler that contain filenames
+   * \param vectorFileList input vector file list (if provided
+   * \param rates
+   */
+  void ExtractTrainData(FloatVectorImageListType *imageList, const TrainFileNamesHandler& fileNames,
+                        std::vector<std::string> vectorFileList,
+                        const SamplingRates& rates)
+  {
+//    if( !vectorFileList.empty() ) // Select and Extract samples for training with computed statistics and rates
+//      {
+      ComputePolygonStatistics( imageList, vectorFileList, fileNames.polyStatTrainOutputs );
+      ComputeSamplingRate( fileNames.polyStatTrainOutputs, fileNames.rateTrainOut, rates.fmt );
+      SelectAndExtractTrainSamples( fileNames, imageList, vectorFileList, SamplingStrategy::CLASS );
+//      }
+//    else // Select training samples base on geometric sampling if no input vector is provided
+//      {
+//      SelectAndExtractTrainSamples( fileNames, imageList, vectorFileList, SamplingStrategy::GEOMETRIC, "fid" );
+//      }
+  }
+
+
+  void DoExecute()
+  {
+    TrainFileNamesHandler fileNames;
+    std::vector<std::string> vectorFileList;
+    FloatVectorImageListType *imageList = GetParameterImageList( "io.il" );
+    bool HasInputVector = IsParameterEnabled( "io.vd" ) && HasValue( "io.vd" );
+    if(HasInputVector)
+      vectorFileList = GetParameterStringList( "io.vd" );
+
+
+    unsigned long nbInputs = imageList->Size();
+
+    if( !HasInputVector )
       {
-      polyStatTrainOutputs.push_back(outModel + "_stats_" + strIndex + ".xml");
-      ratesTrainOutputs.push_back(outModel + "_rates_" + strIndex + ".csv");
-      sampleOutputs.push_back(outModel + "_samples_" + strIndex + ".shp");
+      otbAppLogFATAL( "Missing input vector data files" );
       }
-    sampleTrainOutputs.push_back(outModel + "_samplesTrain_" + strIndex + ".shp");
-    sampleValidOutputs.push_back(outModel + "_samplesValid_" + strIndex + ".shp");
-    }
 
-  // ---------------------------------------------------------------------------
-  // Polygons stats
-  for (unsigned int i=0 ; i<nbInputs ; i++)
-    {
-    GetInternalApplication("polystat")->SetParameterInputImage("in",imageList->GetNthElement(i));
-    GetInternalApplication("polystat")->SetParameterString("vec",vectorFileList[i], false);
-    GetInternalApplication("polystat")->SetParameterString("out",polyStatTrainOutputs[i], false);
-    ExecuteInternal("polystat");
-    // analyse polygons given for validation
-    if (dedicatedValidation)
+    if( !vectorFileList.empty() && nbInputs > vectorFileList.size() )
       {
-      GetInternalApplication("polystat")->SetParameterString("vec",validationVectorFileList[i], false);
-      GetInternalApplication("polystat")->SetParameterString("out",polyStatValidOutputs[i], false);
-      ExecuteInternal("polystat");
+      otbAppLogFATAL( "Missing input vector data files to match number of images (" << nbInputs << ")." );
       }
-    }
 
-  // ---------------------------------------------------------------------------
-  // Compute sampling rates
-  GetInternalApplication("rates")->SetParameterString("mim","proportional", false);
-  double vtr = GetParameterFloat("sample.vtr");
-  long mt = GetParameterInt("sample.mt");
-  long mv = GetParameterInt("sample.mv");
-  // compute final maximum training and final maximum validation
-  // By default take all samples (-1 means all samples)
-  long fmt = -1;
-  long fmv = -1;
-  if (GetParameterInt("sample.bm") == 0)
-    {
-    if (dedicatedValidation)
-      {
-      // fmt and fmv will be used separately
-      fmt = mt;
-      fmv = mv;
-      if (mt > -1 && mv <= -1 && vtr < 0.99999)
-        {
-        fmv = static_cast<long>((double) mt * vtr / (1.0 - vtr));
-        }
-      if (mt <= -1 && mv > -1 && vtr > 0.00001)
-        {
-        fmt = static_cast<long>((double) mv * (1.0 - vtr) / vtr);
-        }
-      }
-    else
+    // check if validation vectors are given
+    std::vector<std::string> validationVectorFileList;
+    bool dedicatedValidation = false;
+    if( IsParameterEnabled( "io.valid" ) && HasValue( "io.valid" ) )
       {
-      // only fmt will be used for both training and validation samples
-      // So we try to compute the total number of samples given input
-      // parameters mt, mv and vtr.
-      if (mt > -1 && vtr < 0.99999)
-        {
-        fmt = static_cast<long>((double) mt / (1.0 - vtr));
-        }
-      if (mv > -1 && vtr > 0.00001)
+      validationVectorFileList = GetParameterStringList( "io.valid" );
+      if( nbInputs > validationVectorFileList.size() )
         {
-        if (fmt > -1 )
-          {
-          fmt = std::min(fmt, static_cast<long>((double) mv / vtr));
-          }
-        else
-          {
-          fmt = static_cast<long>((double) mv / vtr);
-          }
+        otbAppLogFATAL( "Missing validation vector data files to match number of images (" << nbInputs << ")." );
         }
-      }
-    }
 
-  // Sampling rates for training
-  GetInternalApplication("rates")->SetParameterStringList("il",polyStatTrainOutputs, false);
-  GetInternalApplication("rates")->SetParameterString("out",rateTrainOut, false);
-  if (GetParameterInt("sample.bm") != 0)
-    {
-    GetInternalApplication("rates")->SetParameterString("strategy","smallest", false);
-    }
-  else
-    {
-    if (fmt > -1)
-      {
-      std::ostringstream oss;
-      oss << fmt;
-      GetInternalApplication("rates")->SetParameterString("strategy","constant", false);
-      GetInternalApplication("rates")->SetParameterString("strategy.constant.nb",oss.str());
-      }
-    else
-      {
-      GetInternalApplication("rates")->SetParameterString("strategy","all", false);
+      dedicatedValidation = true;
       }
-    }
-  ExecuteInternal("rates");
-  // Sampling rates for validation
-  if (dedicatedValidation)
-    {
-    GetInternalApplication("rates")->SetParameterStringList("il",polyStatValidOutputs, false);
-    GetInternalApplication("rates")->SetParameterString("out",rateValidOut, false);
-    if (GetParameterInt("sample.bm") != 0)
-      {
-      GetInternalApplication("rates")->SetParameterString("strategy","smallest", false);
-      }
-    else
-      {
-      if (fmv > -1)
-        {
-        std::ostringstream oss;
-        oss << fmv;
-        GetInternalApplication("rates")->SetParameterString("strategy","constant", false);
-        GetInternalApplication("rates")->SetParameterString("strategy.constant.nb",oss.str());
-        }
-      else
-        {
-        GetInternalApplication("rates")->SetParameterString("strategy","all", false);
-        }
-      }
-    ExecuteInternal("rates");
-    }
 
-  // ---------------------------------------------------------------------------
-  // Select & extract samples
-  GetInternalApplication("select")->SetParameterString("sampler", "periodic", false);
-  GetInternalApplication("select")->SetParameterInt("sampler.periodic.jitter",50);
-  GetInternalApplication("select")->SetParameterString("strategy","byclass", false);
-  GetInternalApplication("extraction")->SetParameterString("outfield", "prefix", false);
-  GetInternalApplication("extraction")->SetParameterString("outfield.prefix.name","value_", false);
-  for (unsigned int i=0 ; i<nbInputs ; i++)
-    {
-    GetInternalApplication("select")->SetParameterInputImage("in",imageList->GetNthElement(i));
-    GetInternalApplication("select")->SetParameterString("vec",vectorFileList[i], false);
-    GetInternalApplication("select")->SetParameterString("out",sampleOutputs[i], false);
-    GetInternalApplication("select")->SetParameterString("instats",polyStatTrainOutputs[i], false);
-    GetInternalApplication("select")->SetParameterString("strategy.byclass.in",ratesTrainOutputs[i], false);
-    // select sample positions
-    ExecuteInternal("select");
-    // extract sample descriptors
-    ExecuteInternal("extraction");
+    fileNames.CreateTemporaryFileNames( GetParameterString( "io.out" ), nbInputs, dedicatedValidation );
 
-    if (dedicatedValidation)
-      {
-      GetInternalApplication("select")->SetParameterString("vec",validationVectorFileList[i], false);
-      GetInternalApplication("select")->SetParameterString("out",sampleValidOutputs[i], false);
-      GetInternalApplication("select")->SetParameterString("instats",polyStatValidOutputs[i], false);
-      GetInternalApplication("select")->SetParameterString("strategy.byclass.in",ratesValidOutputs[i], false);
-      // select sample positions
-      ExecuteInternal("select");
-      // extract sample descriptors
-      ExecuteInternal("extraction");
-      }
-    else
-      {
-      // Split between training and validation
-      ogr::DataSource::Pointer source = ogr::DataSource::New(sampleOutputs[i], ogr::DataSource::Modes::Read);
-      ogr::DataSource::Pointer destTrain = ogr::DataSource::New(sampleTrainOutputs[i], ogr::DataSource::Modes::Overwrite);
-      ogr::DataSource::Pointer destValid = ogr::DataSource::New(sampleValidOutputs[i], ogr::DataSource::Modes::Overwrite);
-      // read sampling rates from ratesTrainOutputs[i]
-      SamplingRateCalculator::Pointer rateCalculator = SamplingRateCalculator::New();
-      rateCalculator->Read(ratesTrainOutputs[i]);
-      // Compute sampling rates for train and valid
-      const MapRateType &inputRates = rateCalculator->GetRatesByClass();
-      MapRateType trainRates;
-      MapRateType validRates;
-      otb::SamplingRateCalculator::TripletType tpt;
-      for (MapRateType::const_iterator it = inputRates.begin() ;
-           it != inputRates.end() ;
-           ++it)
-        {
-        unsigned long total = std::min(it->second.Required,it->second.Tot );
-        unsigned long neededValid = static_cast<unsigned long>((double) total * vtr );
-        unsigned long neededTrain = total - neededValid;
-        tpt.Tot = total;
-        tpt.Required = neededTrain;
-        tpt.Rate = (1.0 - vtr);
-        trainRates[it->first] = tpt;
-        tpt.Tot = neededValid;
-        tpt.Required = neededValid;
-        tpt.Rate = 1.0;
-        validRates[it->first] = tpt;
-        }
+    // Compute final maximum sampling rates for both training and validation samples
+    SamplingRates rates = ComputeFinalMaximumSamplingRates( dedicatedValidation );
+
+    ExtractTrainData(imageList, fileNames, vectorFileList, rates);
+    ExtractValidationData(imageList, fileNames, validationVectorFileList, rates, HasInputVector);
 
-      // Use an otb::OGRDataToSamplePositionFilter with 2 outputs
-      PeriodicSamplerType::SamplerParameterType param;
-      param.Offset = 0;
-      param.MaxJitter = 0;
-      PeriodicSamplerType::Pointer splitter = PeriodicSamplerType::New();
-      splitter->SetInput(imageList->GetNthElement(i));
-      splitter->SetOGRData(source);
-      splitter->SetOutputPositionContainerAndRates(destTrain, trainRates, 0);
-      splitter->SetOutputPositionContainerAndRates(destValid, validRates, 1);
-      splitter->SetFieldName(this->GetParameterStringList("sample.vfn")[0]);
-      splitter->SetLayerIndex(0);
-      splitter->SetOriginFieldName(std::string(""));
-      splitter->SetSamplerParameters(param);
-      splitter->GetStreamer()->SetAutomaticTiledStreaming(this->GetParameterInt("ram"));
-      AddProcess(splitter->GetStreamer(),"Split samples between training and validation...");
-      splitter->Update();
+    // Then train the model with extracted samples
+    TrainModel( imageList, fileNames.sampleTrainOutputs, fileNames.sampleValidOutputs );
+
+    // cleanup
+    if( IsParameterEnabled( "cleanup" ) )
+      {
+      otbAppLogINFO( <<"Final clean-up ..." );
+      fileNames.clear();
       }
-    }
+  }
 
-  // ---------------------------------------------------------------------------
-  // Train model
-  GetInternalApplication("training")->SetParameterStringList("io.vd",sampleTrainOutputs, false);
-  if( vtr!=0.0 && !sampleValidOutputs.empty() )
-    GetInternalApplication("training")->SetParameterStringList("valid.vd",sampleValidOutputs, false);
-  UpdateInternalParameters("training");
-  // set field names
-  FloatVectorImageType::Pointer image = imageList->GetNthElement(0);
-  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("value_"+oss.str());
-    }
-  GetInternalApplication("training")->SetParameterStringList("feat",selectedNames, false);
-  ExecuteInternal("training");
+private :
 
-  // cleanup
-  if(IsParameterEnabled("cleanup"))
-    {
-    otbAppLogINFO(<<"Final clean-up ...");
-    for(unsigned int i=0 ; i<polyStatTrainOutputs.size() ; i++)
-      RemoveFile(polyStatTrainOutputs[i]);
-    for(unsigned int i=0 ; i<polyStatValidOutputs.size() ; i++)
-      RemoveFile(polyStatValidOutputs[i]);
-    for(unsigned int i=0 ; i<ratesTrainOutputs.size() ; i++)
-      RemoveFile(ratesTrainOutputs[i]);
-    for(unsigned int i=0 ; i<ratesValidOutputs.size() ; i++)
-      RemoveFile(ratesValidOutputs[i]);
-    for(unsigned int i=0 ; i<sampleOutputs.size() ; i++)
-      RemoveFile(sampleOutputs[i]);
-    for(unsigned int i=0 ; i<sampleTrainOutputs.size() ; i++)
-      RemoveFile(sampleTrainOutputs[i]);
-    for(unsigned int i=0 ; i<sampleValidOutputs.size() ; i++)
-      RemoveFile(sampleValidOutputs[i]);
-    }
-}
+  void UpdatePolygonClassStatisticsParameters()
+  {
+    std::vector<std::string> vectorFileList = GetParameterStringList( "io.vd" );
+    GetInternalApplication( "polystat" )->SetParameterString( "vec", vectorFileList[0], false );
+    UpdateInternalParameters( "polystat" );
+  }
 
 };
 
 } // end namespace Wrapper
 } // end namespace otb
 
-OTB_APPLICATION_EXPORT(otb::Wrapper::TrainImagesClassifier)
+OTB_APPLICATION_EXPORT( otb::Wrapper::TrainImagesClassifier )
\ No newline at end of file
diff --git a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
index f9acb6fbe0cafd499c68c234d3a976fa223cd47a..b39c6fae197b4c1dddd0e08156b221668fb883a2 100644
--- a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
+++ b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx
@@ -18,60 +18,30 @@
  * limitations under the License.
  */
 
-#include "otbWrapperApplication.h"
-#include "otbWrapperApplicationFactory.h"
-
-#include "otbLearningApplicationBase.h"
-
-#include "otbOGRDataSourceWrapper.h"
-#include "otbOGRFeatureWrapper.h"
-#include "otbStatisticsXMLFileWriter.h"
-
-#include "itkVariableLengthVector.h"
-#include "otbStatisticsXMLFileReader.h"
-
-#include "itkListSample.h"
-#include "otbShiftScaleSampleListFilter.h"
+#include "otbTrainVectorBase.h"
 
 // Validation
 #include "otbConfusionMatrixCalculator.h"
 
-#include <algorithm>
-#include <locale>
-
 namespace otb
 {
 namespace Wrapper
 {
 
-/** Utility function to negate std::isalnum */
-bool IsNotAlphaNum(char c)
-  {
-  return !std::isalnum(c);
-  }
-
-class TrainVectorClassifier : public LearningApplicationBase<float,int>
+class TrainVectorClassifier : public TrainVectorBase
 {
 public:
   typedef TrainVectorClassifier Self;
-  typedef LearningApplicationBase<float, int> Superclass;
+  typedef TrainVectorBase Superclass;
   typedef itk::SmartPointer<Self> Pointer;
   typedef itk::SmartPointer<const Self> ConstPointer;
-  itkNewMacro(Self)
-
-  itkTypeMacro(Self, Superclass)
-
-  typedef Superclass::SampleType              SampleType;
-  typedef Superclass::ListSampleType          ListSampleType;
-  typedef Superclass::TargetListSampleType    TargetListSampleType;
-  typedef Superclass::SampleImageType         SampleImageType;
-  
-  typedef double ValueType;
-  typedef itk::VariableLengthVector<ValueType> MeasurementType;
+  itkNewMacro( Self )
 
-  typedef otb::StatisticsXMLFileReader<SampleType> StatisticsReader;
+  itkTypeMacro( Self, Superclass )
 
-  typedef otb::Statistics::ShiftScaleSampleListFilter<ListSampleType, ListSampleType> ShiftScaleFilterType;
+  typedef Superclass::SampleType SampleType;
+  typedef Superclass::ListSampleType ListSampleType;
+  typedef Superclass::TargetListSampleType TargetListSampleType;
 
   // Estimate performance on validation sample
   typedef otb::ConfusionMatrixCalculator<TargetListSampleType, TargetListSampleType> ConfusionMatrixCalculatorType;
@@ -79,503 +49,257 @@ public:
   typedef ConfusionMatrixCalculatorType::MapOfIndicesType MapOfIndicesType;
   typedef ConfusionMatrixCalculatorType::ClassLabelType ClassLabelType;
 
-private:
+protected:
   void DoInit()
   {
-    SetName("TrainVectorClassifier");
-    SetDescription("Train a classifier based on labeled geometries and a list of features to consider.");
-
-    SetDocName("Train Vector Classifier");
-    SetDocLongDescription("This application trains a classifier based on "
-      "labeled geometries and a list of features to consider for classification.");
-    SetDocLimitations(" ");
-    SetDocAuthors("OTB Team");
-    SetDocSeeAlso(" ");
-   
-    //Group IO
-    AddParameter(ParameterType_Group, "io", "Input and output data");
-    SetParameterDescription("io", "This group of parameters allows setting input and output data.");
-
-    AddParameter(ParameterType_InputVectorDataList, "io.vd", "Input Vector Data");
-    SetParameterDescription("io.vd", "Input geometries used for training (note : all geometries from the layer will be used)");
-
-    AddParameter(ParameterType_InputFilename, "io.stats", "Input XML image statistics file");
-    MandatoryOff("io.stats");
-    SetParameterDescription("io.stats", "XML file containing mean and variance of each feature.");
-
-    AddParameter(ParameterType_OutputFilename, "io.confmatout", "Output confusion matrix");
-    SetParameterDescription("io.confmatout", "Output file containing the confusion matrix (.csv format).");
-    MandatoryOff("io.confmatout");
-
-    AddParameter(ParameterType_OutputFilename, "io.out", "Output model");
-    SetParameterDescription("io.out", "Output file containing the model estimated (.txt format).");
-
-    AddParameter(ParameterType_ListView,  "feat", "Field names for training features.");
-    SetParameterDescription("feat","List of field names in the input vector data to be used as features for training.");
-
-    AddParameter(ParameterType_ListView,"cfield","Field containing the class id for supervision");
-    SetParameterDescription("cfield","Field containing the class id for supervision. "
-      "Only geometries with this field available will be taken into account.");
-    SetListViewSingleSelectionMode("cfield",true);
-      
-    AddParameter(ParameterType_Int, "layer", "Layer Index");
-    SetParameterDescription("layer", "Index of the layer to use in the input vector file.");
-    MandatoryOff("layer");
-    SetDefaultParameterInt("layer",0);
-
-    AddParameter(ParameterType_Group, "valid", "Validation data");
-    SetParameterDescription("valid", "This group of parameters defines validation data.");
-
-    AddParameter(ParameterType_InputVectorDataList, "valid.vd", "Validation Vector Data");
-    SetParameterDescription("valid.vd", "Geometries used for validation "
-      "(must contain the same fields used for training, all geometries from the layer will be used)");
-    MandatoryOff("valid.vd");
-
-    AddParameter(ParameterType_Int, "valid.layer", "Layer Index");
-    SetParameterDescription("valid.layer", "Index of the layer to use in the validation vector file.");
-    MandatoryOff("valid.layer");
-    SetDefaultParameterInt("valid.layer",0);
-
-    // Add parameters for the classifier choice
-    Superclass::DoInit();
+    SetName( "TrainVectorClassifier" );
+    SetDescription( "Train a classifier based on labeled geometries and a list of features to consider." );
+
+    SetDocName( "Train Vector Classifier" );
+    SetDocLongDescription( "This application trains a classifier based on "
+                                   "labeled geometries and a list of features to consider for classification." );
+    SetDocLimitations( " " );
+    SetDocAuthors( "OTB Team" );
+    SetDocSeeAlso( " " );
 
-    AddRANDParameter();
-    // Doc example parameter settings
-    SetDocExampleParameterValue("io.vd", "vectorData.shp");
-    SetDocExampleParameterValue("io.stats", "meanVar.xml");
-    SetDocExampleParameterValue("io.out", "svmModel.svm");
-    SetDocExampleParameterValue("feat", "perimeter  area  width");
-    SetDocExampleParameterValue("cfield", "predicted");
+    Superclass::DoInit();
   }
 
   void DoUpdateParameters()
   {
-    if ( HasValue("io.vd") )
+    Superclass::DoUpdateParameters();
+  }
+
+  void DoExecute()
+  {
+    // Enforce the need of class field name in supervised mode
+    if (GetClassifierCategory() == Supervised)
       {
-      std::vector<std::string> vectorFileList = GetParameterStringList("io.vd");
-      ogr::DataSource::Pointer ogrDS =
-        ogr::DataSource::New(vectorFileList[0], ogr::DataSource::Modes::Read);
-      ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer"));
-      ogr::Feature feature = layer.ogr().GetNextFeature();
-
-      ClearChoices("feat");
-      ClearChoices("cfield");
-      
-      for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
+      m_FeaturesInfo.SetClassFieldNames( GetChoiceNames( "cfield" ), GetSelectedItems( "cfield" ) );
+
+      if( m_FeaturesInfo.m_SelectedCFieldIdx.empty() )
         {
-        std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
-        key = item;
-        std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
-        std::transform(key.begin(), end, key.begin(), tolower);
-        
-        OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
-        
-        if(fieldType == OFTInteger ||  ogr::version_proxy::IsOFTInteger64(fieldType) || fieldType == OFTReal)
-          {
-          std::string tmpKey="feat."+key.substr(0, end - key.begin());
-          AddChoice(tmpKey,item);
-          }
-        if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
-          {
-          std::string tmpKey="cfield."+key.substr(0, end - key.begin());
-          AddChoice(tmpKey,item);
-          }
+        otbAppLogFATAL( << "No field has been selected for data labelling!" );
         }
       }
+
+      Superclass::DoExecute();
+
+      if (GetClassifierCategory() == Supervised)
+        {
+        ConfusionMatrixCalculatorType::Pointer confMatCalc = ComputeConfusionMatrix( m_PredictedList,
+                                                                                     m_ClassificationSamplesWithLabel.labeledListSample );
+        WriteConfusionMatrix( confMatCalc );
+        }
+      else
+        {
+        // TODO Compute Contingency Table
+        }
   }
 
 
-void LogConfusionMatrix(ConfusionMatrixCalculatorType* confMatCalc)
-{
-  ConfusionMatrixCalculatorType::ConfusionMatrixType matrix = confMatCalc->GetConfusionMatrix();
 
-  // Compute minimal width
-  size_t minwidth = 0;
+  ConfusionMatrixCalculatorType::Pointer
+  ComputeConfusionMatrix(const TargetListSampleType::Pointer &predictedListSample,
+                         const TargetListSampleType::Pointer &performanceLabeledListSample)
+  {
+    ConfusionMatrixCalculatorType::Pointer confMatCalc = ConfusionMatrixCalculatorType::New();
 
-  for (unsigned int i = 0; i < matrix.Rows(); i++)
-    {
-    for (unsigned int j = 0; j < matrix.Cols(); j++)
+    otbAppLogINFO( "Predicted list size : " << predictedListSample->Size() );
+    otbAppLogINFO( "ValidationLabeledListSample size : " << performanceLabeledListSample->Size() );
+    confMatCalc->SetReferenceLabels( performanceLabeledListSample );
+    confMatCalc->SetProducedLabels( predictedListSample );
+    confMatCalc->Compute();
+
+    otbAppLogINFO( "training performances" );
+    LogConfusionMatrix( confMatCalc );
+
+    for( unsigned int itClasses = 0; itClasses < confMatCalc->GetNumberOfClasses(); itClasses++ )
       {
-      std::ostringstream os;
-      os << matrix(i, j);
-      size_t size = os.str().size();
+      ConfusionMatrixCalculatorType::ClassLabelType classLabel = confMatCalc->GetMapOfIndices()[itClasses];
 
-      if (size > minwidth)
-        {
-        minwidth = size;
-        }
+      otbAppLogINFO( "Precision of class [" << classLabel << "] vs all: " << confMatCalc->GetPrecisions()[itClasses] );
+      otbAppLogINFO( "Recall of class    [" << classLabel << "] vs all: " << confMatCalc->GetRecalls()[itClasses] );
+      otbAppLogINFO(
+              "F-score of class   [" << classLabel << "] vs all: " << confMatCalc->GetFScores()[itClasses] << "\n" );
       }
-    }
+    otbAppLogINFO( "Global performance, Kappa index: " << confMatCalc->GetKappaIndex() );
+    return confMatCalc;
+  }
 
-  MapOfIndicesType mapOfIndices = confMatCalc->GetMapOfIndices();
+  /**
+   * Write the confidence matrix into a file if output is provided.
+   * \param confMatCalc the input matrix to write.
+   */
+  void WriteConfusionMatrix(const ConfusionMatrixCalculatorType::Pointer &confMatCalc)
+  {
+    if( this->HasValue( "io.confmatout" ) )
+      {
+      // Writing the confusion matrix in the output .CSV file
 
-  MapOfIndicesType::const_iterator it = mapOfIndices.begin();
-  MapOfIndicesType::const_iterator end = mapOfIndices.end();
+      MapOfIndicesType::iterator itMapOfIndicesValid, itMapOfIndicesPred;
+      ClassLabelType labelValid = 0;
 
-  for (; it != end; ++it)
-    {
-    std::ostringstream os;
-    os << "[" << it->second << "]";
+      ConfusionMatrixType confusionMatrix = confMatCalc->GetConfusionMatrix();
+      MapOfIndicesType mapOfIndicesValid = confMatCalc->GetMapOfIndices();
 
-    size_t size = os.str().size();
-    if (size > minwidth)
-      {
-      minwidth = size;
-      }
-    }
+      unsigned long nbClassesPred = mapOfIndicesValid.size();
 
-  // Generate matrix string, with 'minwidth' as size specifier
-  std::ostringstream os;
+      /////////////////////////////////////////////
+      // Filling the 2 headers for the output file
+      const std::string commentValidStr = "#Reference labels (rows):";
+      const std::string commentPredStr = "#Produced labels (columns):";
+      const char separatorChar = ',';
+      std::ostringstream ossHeaderValidLabels, ossHeaderPredLabels;
 
-  // Header line
-  for (size_t i = 0; i < minwidth; ++i)
-    os << " ";
-  os << " ";
-
-  it = mapOfIndices.begin();
-  end = mapOfIndices.end();
-  for (; it != end; ++it)
-    {
-    os << "[" << it->second << "]" << " ";
-    }
-
-  os << std::endl;
-
-  // Each line of confusion matrix
-  for (unsigned int i = 0; i < matrix.Rows(); i++)
-    {
-    ConfusionMatrixCalculatorType::ClassLabelType label = mapOfIndices[i];
-    os << "[" << std::setw(minwidth - 2) << label << "]" << " ";
-    for (unsigned int j = 0; j < matrix.Cols(); j++)
-      {
-      os << std::setw(minwidth) << matrix(i, j) << " ";
-      }
-    os << std::endl;
-    }
+      // Filling ossHeaderValidLabels and ossHeaderPredLabels for the output file
+      ossHeaderValidLabels << commentValidStr;
+      ossHeaderPredLabels << commentPredStr;
 
-  otbAppLogINFO("Confusion matrix (rows = reference labels, columns = produced labels):\n" << os.str());
-}
+      itMapOfIndicesValid = mapOfIndicesValid.begin();
 
+      while( itMapOfIndicesValid != mapOfIndicesValid.end() )
+        {
+        // labels labelValid of mapOfIndicesValid are already sorted in otbConfusionMatrixCalculator
+        labelValid = itMapOfIndicesValid->second;
 
-void DoExecute()
-  {
-  typedef int LabelPixelType;
-  typedef itk::FixedArray<LabelPixelType,1> LabelSampleType;
-  typedef itk::Statistics::ListSample <LabelSampleType> LabelListSampleType;
-
-  // Prepare selected field names (their position may change between two inputs)
-  std::vector<int> selectedIdx = GetSelectedItems("feat");
-  std::vector<int> selectedCFieldIdx = GetSelectedItems("cfield");
-  
-  if(selectedIdx.empty())
-    {
-    otbAppLogFATAL(<<"No features have been selected to train the classifier on!");
-    }
-  
-  if(selectedCFieldIdx.empty())
-    {
-    otbAppLogFATAL(<<"No field has been selected for data labelling!");
-    }
-
-  const unsigned int nbFeatures = selectedIdx.size();
-  std::vector<std::string> fieldNames = GetChoiceNames("feat");
-  std::vector<std::string> cFieldNames = GetChoiceNames("cfield");
-  std::vector<std::string> selectedNames(nbFeatures);
-  for (unsigned int i=0 ; i<nbFeatures ; i++)
-    {
-    selectedNames[i] = fieldNames[selectedIdx[i]];
-    }
-
-  std::string selectedCFieldName = cFieldNames[selectedCFieldIdx.front()];
-
-  std::vector<int> featureFieldIndex(nbFeatures, -1);
-  int cFieldIndex = -1;
-
-  // Statistics for shift/scale
-  MeasurementType meanMeasurementVector;
-  MeasurementType stddevMeasurementVector;
-  if (HasValue("io.stats") && IsParameterEnabled("io.stats"))
-    {
-    StatisticsReader::Pointer statisticsReader = StatisticsReader::New();
-    std::string XMLfile = GetParameterString("io.stats");
-    statisticsReader->SetFileName(XMLfile);
-    meanMeasurementVector = statisticsReader->GetStatisticVectorByName("mean");
-    stddevMeasurementVector = statisticsReader->GetStatisticVectorByName("stddev");
-    }
-  else
-    {
-    meanMeasurementVector.SetSize(nbFeatures);
-    meanMeasurementVector.Fill(0.);
-    stddevMeasurementVector.SetSize(nbFeatures);
-    stddevMeasurementVector.Fill(1.);
-    }
-
-  ListSampleType::Pointer input = ListSampleType::New();
-  LabelListSampleType::Pointer target = LabelListSampleType::New();
-  input->SetMeasurementVectorSize(nbFeatures);
-
-  std::vector<std::string> vectorFileList = GetParameterStringList("io.vd");
-  for (unsigned int k=0 ; k<vectorFileList.size() ; k++)
-    {
-    otbAppLogINFO("Reading input vector file "<<k+1<<"/"<<vectorFileList.size());
-    ogr::DataSource::Pointer source = ogr::DataSource::New(vectorFileList[k], ogr::DataSource::Modes::Read);
-    ogr::Layer layer = source->GetLayer(this->GetParameterInt("layer"));
-    ogr::Feature feature = layer.ogr().GetNextFeature();
-    bool goesOn = feature.addr() != 0;
-    if (!goesOn)
-      {
-      otbAppLogWARNING("The layer "<<GetParameterInt("layer")<<" of "
-        <<vectorFileList[k]<<" is empty, input is skipped.");
-      continue;
-      }
+        otbAppLogINFO( "mapOfIndicesValid[" << itMapOfIndicesValid->first << "] = " << labelValid );
 
-    // Check all needed fields are present :
-    //   - check class field
-    cFieldIndex = feature.ogr().GetFieldIndex(selectedCFieldName.c_str());
-    if (cFieldIndex < 0)
-      otbAppLogFATAL("The field name for class label ("<<selectedCFieldName
-        <<") has not been found in the input vector file "<<vectorFileList[k]);
-    //   - check feature fields
-    for (unsigned int i=0 ; i<nbFeatures ; i++)
-      {
-      featureFieldIndex[i] = feature.ogr().GetFieldIndex(selectedNames[i].c_str());
-      if (featureFieldIndex[i] < 0)
-        otbAppLogFATAL("The field name for feature "<<selectedNames[i]
-        <<" has not been found in the input vector file "<<vectorFileList[k]);
-      }
+        ossHeaderValidLabels << labelValid;
+        ossHeaderPredLabels << labelValid;
 
-    while(goesOn)
-      {
-      if(feature.ogr().IsFieldSet(cFieldIndex))
-        {
-        MeasurementType mv;
-        mv.SetSize(nbFeatures);
-        for(unsigned int idx=0; idx < nbFeatures; ++idx)
-          mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]);
+        ++itMapOfIndicesValid;
 
-        input->PushBack(mv);
-        target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex));
-        }
-      feature = layer.ogr().GetNextFeature();
-      goesOn = feature.addr() != 0;
-      }
-    }
-
-  ShiftScaleFilterType::Pointer trainingShiftScaleFilter = ShiftScaleFilterType::New();
-  trainingShiftScaleFilter->SetInput(input);
-  trainingShiftScaleFilter->SetShifts(meanMeasurementVector);
-  trainingShiftScaleFilter->SetScales(stddevMeasurementVector);
-  trainingShiftScaleFilter->Update();
-
-  ListSampleType::Pointer trainingListSample= trainingShiftScaleFilter->GetOutput();
-  TargetListSampleType::Pointer trainingLabeledListSample = target;
-
-  //--------------------------
-  // Estimate model
-  //--------------------------
-  this->Train(trainingListSample,trainingLabeledListSample,GetParameterString("io.out"));
-
-  //--------------------------
-  // Performances estimation
-  //--------------------------
-  ListSampleType::Pointer validationListSample=ListSampleType::New();
-  TargetListSampleType::Pointer validationLabeledListSample = TargetListSampleType::New();
-
-  // Import validation data
-  if (HasValue("valid.vd") && IsParameterEnabled("valid.vd"))
-    {
-    input = ListSampleType::New();
-    target = LabelListSampleType::New();
-    input->SetMeasurementVectorSize(nbFeatures);
-
-    std::vector<std::string> validFileList = this->GetParameterStringList("valid.vd");
-    for (unsigned int k=0 ; k<validFileList.size() ; k++)
-      {
-      otbAppLogINFO("Reading validation vector file "<<k+1<<"/"<<validFileList.size());
-      ogr::DataSource::Pointer source = ogr::DataSource::New(validFileList[k], ogr::DataSource::Modes::Read);
-      ogr::Layer layer = source->GetLayer(this->GetParameterInt("valid.layer"));
-      ogr::Feature feature = layer.ogr().GetNextFeature();
-      bool goesOn = feature.addr() != 0;
-      if (!goesOn)
-        {
-        otbAppLogWARNING("The layer "<<GetParameterInt("valid.layer")<<" of "
-          <<validFileList[k]<<" is empty, input is skipped.");
-        continue;
+        if( itMapOfIndicesValid != mapOfIndicesValid.end() )
+          {
+          ossHeaderValidLabels << separatorChar;
+          ossHeaderPredLabels << separatorChar;
+          }
+        else
+          {
+          ossHeaderValidLabels << std::endl;
+          ossHeaderPredLabels << std::endl;
+          }
         }
 
-      // Check all needed fields are present :
-      //   - check class field
-      cFieldIndex = feature.ogr().GetFieldIndex(selectedCFieldName.c_str());
-      if (cFieldIndex < 0)
-        otbAppLogFATAL("The field name for class label ("<<selectedCFieldName
-          <<") has not been found in the validation vector file "<<validFileList[k]);
-      //   - check feature fields
-      for (unsigned int i=0 ; i<nbFeatures ; i++)
-        {
-        featureFieldIndex[i] = feature.ogr().GetFieldIndex(selectedNames[i].c_str());
-        if (featureFieldIndex[i] < 0)
-          otbAppLogFATAL("The field name for feature "<<selectedNames[i]
-          <<" has not been found in the validation vector file "<<validFileList[k]);
-        }
+      std::ofstream outFile;
+      outFile.open( this->GetParameterString( "io.confmatout" ).c_str() );
+      outFile << std::fixed;
+      outFile.precision( 10 );
+
+      /////////////////////////////////////
+      // Writing the 2 headers
+      outFile << ossHeaderValidLabels.str();
+      outFile << ossHeaderPredLabels.str();
+      /////////////////////////////////////
+
+      unsigned int indexLabelValid = 0, indexLabelPred = 0;
 
-      while(goesOn)
+      for( itMapOfIndicesValid = mapOfIndicesValid.begin();
+           itMapOfIndicesValid != mapOfIndicesValid.end(); ++itMapOfIndicesValid )
         {
-        if(feature.ogr().IsFieldSet(cFieldIndex))
-          {
-          MeasurementType mv;
-          mv.SetSize(nbFeatures);
-          for(unsigned int idx=0; idx < nbFeatures; ++idx)
-            mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]);
+        indexLabelPred = 0;
 
-          input->PushBack(mv);
-          target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex));
+        for( itMapOfIndicesPred = mapOfIndicesValid.begin();
+             itMapOfIndicesPred != mapOfIndicesValid.end(); ++itMapOfIndicesPred )
+          {
+          // Writing the confusion matrix (sorted in otbConfusionMatrixCalculator) in the output file
+          outFile << confusionMatrix( indexLabelValid, indexLabelPred );
+          if( indexLabelPred < ( nbClassesPred - 1 ) )
+            {
+            outFile << separatorChar;
+            }
+          else
+            {
+            outFile << std::endl;
+            }
+          ++indexLabelPred;
           }
-        feature = layer.ogr().GetNextFeature();
-        goesOn = feature.addr() != 0;
+
+        ++indexLabelValid;
         }
+
+      outFile.close();
       }
+  }
+
+  /**
+   * Display the log of the confusion matrix computed with
+   * \param confMatCalc the input confusion matrix to display
+   */
+  void LogConfusionMatrix(ConfusionMatrixCalculatorType *confMatCalc)
+  {
+    ConfusionMatrixCalculatorType::ConfusionMatrixType matrix = confMatCalc->GetConfusionMatrix();
+
+    // Compute minimal width
+    size_t minwidth = 0;
 
-    ShiftScaleFilterType::Pointer validShiftScaleFilter = ShiftScaleFilterType::New();
-    validShiftScaleFilter->SetInput(input);
-    validShiftScaleFilter->SetShifts(meanMeasurementVector);
-    validShiftScaleFilter->SetScales(stddevMeasurementVector);
-    validShiftScaleFilter->Update();
-
-    validationListSample = validShiftScaleFilter->GetOutput();
-    validationLabeledListSample = target;
-    }
- 
-  //Test the input validation set size
-  TargetListSampleType::Pointer predictedList = TargetListSampleType::New();
-  ListSampleType::Pointer performanceListSample;
-  TargetListSampleType::Pointer performanceLabeledListSample;
-  if(validationLabeledListSample->Size() != 0)
-    {
-    performanceListSample = validationListSample;
-    performanceLabeledListSample = validationLabeledListSample;
-    }
-  else
-    {
-    otbAppLogWARNING("The validation set is empty. The performance estimation is done using the input training set in this case.");
-    performanceListSample = trainingListSample;
-    performanceLabeledListSample = trainingLabeledListSample;
-    }
-
-  this->Classify(performanceListSample, predictedList, GetParameterString("io.out"));
-
-  ConfusionMatrixCalculatorType::Pointer confMatCalc = ConfusionMatrixCalculatorType::New();
-
-  otbAppLogINFO("Predicted list size : " << predictedList->Size());
-  otbAppLogINFO("ValidationLabeledListSample size : " << performanceLabeledListSample->Size());
-  confMatCalc->SetReferenceLabels(performanceLabeledListSample);
-  confMatCalc->SetProducedLabels(predictedList);
-  confMatCalc->Compute();
-
-  otbAppLogINFO("training performances");
-  LogConfusionMatrix(confMatCalc);
-
-  for (unsigned int itClasses = 0; itClasses < confMatCalc->GetNumberOfClasses(); itClasses++)
-    {
-    ConfusionMatrixCalculatorType::ClassLabelType classLabel = confMatCalc->GetMapOfIndices()[itClasses];
-
-    otbAppLogINFO("Precision of class [" << classLabel << "] vs all: " << confMatCalc->GetPrecisions()[itClasses]);
-    otbAppLogINFO("Recall of class    [" << classLabel << "] vs all: " << confMatCalc->GetRecalls()[itClasses]);
-    otbAppLogINFO(
-      "F-score of class   [" << classLabel << "] vs all: " << confMatCalc->GetFScores()[itClasses] << "\n");
-    }
-  otbAppLogINFO("Global performance, Kappa index: " << confMatCalc->GetKappaIndex());
-
-
-  if (this->HasValue("io.confmatout"))
-    {
-    // Writing the confusion matrix in the output .CSV file
-
-    MapOfIndicesType::iterator itMapOfIndicesValid, itMapOfIndicesPred;
-    ClassLabelType labelValid = 0;
-
-    ConfusionMatrixType confusionMatrix = confMatCalc->GetConfusionMatrix();
-    MapOfIndicesType mapOfIndicesValid = confMatCalc->GetMapOfIndices();
-
-    unsigned int nbClassesPred = mapOfIndicesValid.size();
-
-    /////////////////////////////////////////////
-    // Filling the 2 headers for the output file
-    const std::string commentValidStr = "#Reference labels (rows):";
-    const std::string commentPredStr = "#Produced labels (columns):";
-    const char separatorChar = ',';
-    std::ostringstream ossHeaderValidLabels, ossHeaderPredLabels;
-
-    // Filling ossHeaderValidLabels and ossHeaderPredLabels for the output file
-    ossHeaderValidLabels << commentValidStr;
-    ossHeaderPredLabels << commentPredStr;
-
-    itMapOfIndicesValid = mapOfIndicesValid.begin();
-
-    while (itMapOfIndicesValid != mapOfIndicesValid.end())
+    for( unsigned int i = 0; i < matrix.Rows(); i++ )
       {
-      // labels labelValid of mapOfIndicesValid are already sorted in otbConfusionMatrixCalculator
-      labelValid = itMapOfIndicesValid->second;
+      for( unsigned int j = 0; j < matrix.Cols(); j++ )
+        {
+        std::ostringstream os;
+        os << matrix( i, j );
+        size_t size = os.str().size();
 
-      otbAppLogINFO("mapOfIndicesValid[" << itMapOfIndicesValid->first << "] = " << labelValid);
+        if( size > minwidth )
+          {
+          minwidth = size;
+          }
+        }
+      }
 
-      ossHeaderValidLabels << labelValid;
-      ossHeaderPredLabels << labelValid;
+    MapOfIndicesType mapOfIndices = confMatCalc->GetMapOfIndices();
 
-      ++itMapOfIndicesValid;
+    MapOfIndicesType::const_iterator it = mapOfIndices.begin();
+    MapOfIndicesType::const_iterator end = mapOfIndices.end();
 
-      if (itMapOfIndicesValid != mapOfIndicesValid.end())
-        {
-        ossHeaderValidLabels << separatorChar;
-        ossHeaderPredLabels << separatorChar;
-        }
-      else
+    for( ; it != end; ++it )
+      {
+      std::ostringstream os;
+      os << "[" << it->second << "]";
+
+      size_t size = os.str().size();
+      if( size > minwidth )
         {
-        ossHeaderValidLabels << std::endl;
-        ossHeaderPredLabels << std::endl;
+        minwidth = size;
         }
       }
 
-    std::ofstream outFile;
-    outFile.open(this->GetParameterString("io.confmatout").c_str());
-    outFile << std::fixed;
-    outFile.precision(10);
-
-    /////////////////////////////////////
-    // Writing the 2 headers
-    outFile << ossHeaderValidLabels.str();
-    outFile << ossHeaderPredLabels.str();
-    /////////////////////////////////////
+    // Generate matrix string, with 'minwidth' as size specifier
+    std::ostringstream os;
 
-    unsigned int indexLabelValid = 0, indexLabelPred = 0;
+    // Header line
+    for( size_t i = 0; i < minwidth; ++i )
+      os << " ";
+    os << " ";
 
-    for (itMapOfIndicesValid = mapOfIndicesValid.begin(); itMapOfIndicesValid != mapOfIndicesValid.end(); ++itMapOfIndicesValid)
+    it = mapOfIndices.begin();
+    end = mapOfIndices.end();
+    for( ; it != end; ++it )
       {
-      indexLabelPred = 0;
+      os << "[" << it->second << "]" << " ";
+      }
 
-      for (itMapOfIndicesPred = mapOfIndicesValid.begin(); itMapOfIndicesPred != mapOfIndicesValid.end(); ++itMapOfIndicesPred)
+    os << std::endl;
+
+    // Each line of confusion matrix
+    for( unsigned int i = 0; i < matrix.Rows(); i++ )
+      {
+      ConfusionMatrixCalculatorType::ClassLabelType label = mapOfIndices[i];
+      os << "[" << std::setw( minwidth - 2 ) << label << "]" << " ";
+      for( unsigned int j = 0; j < matrix.Cols(); j++ )
         {
-        // Writing the confusion matrix (sorted in otbConfusionMatrixCalculator) in the output file
-        outFile << confusionMatrix(indexLabelValid, indexLabelPred);
-        if (indexLabelPred < (nbClassesPred - 1))
-          {
-          outFile << separatorChar;
-          }
-        else
-          {
-          outFile << std::endl;
-          }
-        ++indexLabelPred;
+        os << std::setw( minwidth ) << matrix( i, j ) << " ";
         }
-
-      ++indexLabelValid;
+      os << std::endl;
       }
 
-    outFile.close();
-    } // END if (this->HasValue("io.confmatout"))
+    otbAppLogINFO( "Confusion matrix (rows = reference labels, columns = produced labels):\n" << os.str() );
   }
 
 };
 }
 }
 
-OTB_APPLICATION_EXPORT(otb::Wrapper::TrainVectorClassifier)
+OTB_APPLICATION_EXPORT( otb::Wrapper::TrainVectorClassifier )
diff --git a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.h b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.h
index 79767a1c02509c581fe62e8fe5cb25eba89e19ba..991ee9aa892137f12ee07bdb3b89d15bdbb26d4a 100644
--- a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.h
+++ b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.h
@@ -102,7 +102,22 @@ public:
   typedef typename ModelType::TargetSampleType      TargetSampleType;
   typedef typename ModelType::TargetListSampleType  TargetListSampleType;
   typedef typename ModelType::TargetValueType       TargetValueType;
-  
+
+  itkGetConstReferenceMacro(SupervisedClassifier, std::vector<std::string>);
+  itkGetConstReferenceMacro(UnsupervisedClassifier, std::vector<std::string>);
+
+  enum ClassifierCategory{
+    Supervised,
+    Unsupervised
+  };
+
+  /**
+   * Retrieve the classifier category (supervisde or unsupervised)
+   * based on the select algorithm from the classifier choice.
+   * @return ClassifierCategory the classifier category
+   */
+  ClassifierCategory GetClassifierCategory();
+
 protected:
   LearningApplicationBase();
 
@@ -120,15 +135,23 @@ protected:
                 std::string modelPath);
 
   /** Init method that creates all the parameters for machine learning models */
-  void DoInit();
+  void DoInit() ITK_OVERRIDE;
 
   /** Flag to switch between classification and regression mode.
    * False by default, child classes may change it in their constructor */
   bool m_RegressionFlag;
 
 private:
-
   /** Specific Init and Train methods for each machine learning model */
+
+  /** Init Parameters for Supervised Classifier */
+  void InitSupervisedClassifierParams();
+  std::vector<std::string> m_SupervisedClassifier;
+
+  /** Init Parameters for Unsupervised Classifier */
+  void InitUnsupervisedClassifierParams();
+  std::vector<std::string> m_UnsupervisedClassifier;
+
   //@{
 #ifdef OTB_USE_LIBSVM 
   void InitLibSVMParams();
@@ -179,6 +202,10 @@ private:
   void TrainSharkRandomForests(typename ListSampleType::Pointer trainingListSample,
                                typename TargetListSampleType::Pointer trainingLabeledListSample,
                                std::string modelPath);
+  void InitSharkKMeansParams();
+  void TrainSharkKMeans(typename ListSampleType::Pointer trainingListSample,
+                        typename TargetListSampleType::Pointer trainingLabeledListSample,
+                        std::string modelPath);
 #endif
   //@}
 };
@@ -203,6 +230,7 @@ private:
 #endif
 #ifdef OTB_USE_SHARK
 #include "otbTrainSharkRandomForests.txx"
+#include "otbTrainSharkKMeans.txx"
 #endif
 #endif
 
diff --git a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.txx b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.txx
index c0a66443a8066cebc3bb906cdcb1f68d68981b0d..a40d0e505e3af53a3625976691aa402b1d59b827 100644
--- a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.txx
+++ b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.txx
@@ -54,8 +54,33 @@ LearningApplicationBase<TInputValue,TOutputValue>
   AddParameter(ParameterType_Choice, "classifier", "Classifier to use for the training");
   SetParameterDescription("classifier", "Choice of the classifier to use for the training.");
 
+  InitSupervisedClassifierParams();
+  m_SupervisedClassifier = GetChoiceKeys("classifier");
+
+  InitUnsupervisedClassifierParams();
+  std::vector<std::string> allClassifier = GetChoiceKeys("classifier");
+  m_UnsupervisedClassifier.assign(allClassifier.begin() + m_SupervisedClassifier.size(), allClassifier.end());
+}
+
+template <class TInputValue, class TOutputValue>
+typename LearningApplicationBase<TInputValue,TOutputValue>::ClassifierCategory
+LearningApplicationBase<TInputValue,TOutputValue>
+::GetClassifierCategory()
+{
+  bool foundUnsupervised =
+          std::find(m_UnsupervisedClassifier.begin(), m_UnsupervisedClassifier.end(),
+                    GetParameterString("classifier")) != m_UnsupervisedClassifier.end();
+  return foundUnsupervised ? Unsupervised : Supervised;
+}
+
+template <class TInputValue, class TOutputValue>
+void
+LearningApplicationBase<TInputValue,TOutputValue>
+::InitSupervisedClassifierParams()
+{
+
   //Group LibSVM
-#ifdef OTB_USE_LIBSVM 
+#ifdef OTB_USE_LIBSVM
   InitLibSVMParams();
 #endif
 
@@ -81,7 +106,16 @@ LearningApplicationBase<TInputValue,TOutputValue>
 #ifdef OTB_USE_SHARK
   InitSharkRandomForestsParams();
 #endif
-  
+}
+
+template <class TInputValue, class TOutputValue>
+void
+LearningApplicationBase<TInputValue,TOutputValue>
+::InitUnsupervisedClassifierParams()
+{
+#ifdef OTB_USE_SHARK
+  InitSharkKMeansParams();
+#endif
 }
 
 template <class TInputValue, class TOutputValue>
@@ -151,6 +185,14 @@ LearningApplicationBase<TInputValue,TOutputValue>
     otbAppLogFATAL("Module SharkLearning is not installed. You should consider turning OTB_USE_SHARK on during cmake configuration.");
     #endif
     }
+  else if(modelName == "sharkkm")
+    {
+    #ifdef OTB_USE_SHARK
+    TrainSharkKMeans( trainingListSample, trainingLabeledListSample, modelPath );
+    #else
+    otbAppLogFATAL("Module SharkLearning is not installed. You should consider turning OTB_USE_SHARK on during cmake configuration.");
+    #endif
+    }
   else if (modelName == "svm")
     {
     #ifdef OTB_USE_OPENCV
diff --git a/Modules/Applications/AppClassification/include/otbTrainImagesBase.h b/Modules/Applications/AppClassification/include/otbTrainImagesBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..a01eb6c430fffc3d588d109d0f41f1bf677783b5
--- /dev/null
+++ b/Modules/Applications/AppClassification/include/otbTrainImagesBase.h
@@ -0,0 +1,306 @@
+/*
+ * 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 otbTrainImagesBase_h
+#define otbTrainImagesBase_h
+
+#include "otbVectorDataFileWriter.h"
+#include "otbWrapperCompositeApplication.h"
+#include "otbWrapperApplicationFactory.h"
+
+#include "otbStatisticsXMLFileWriter.h"
+#include "otbImageToEnvelopeVectorDataFilter.h"
+#include "otbSamplingRateCalculator.h"
+#include "otbOGRDataToSamplePositionFilter.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+/** \class TrainImagesBase
+ * \brief Base class for the TrainImagesClassifier
+ *
+ * This class intends to hold common input/output parameters and
+ * composite application connection for both supervised and unsupervised
+ * model training.
+ *
+ * \ingroup OTBAppClassification
+ */
+class TrainImagesBase : public CompositeApplication
+{
+public:
+  /** Standard class typedefs. */
+  typedef TrainImagesBase Self;
+  typedef CompositeApplication Superclass;
+  typedef itk::SmartPointer<Self> Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+  /** Standard macro */
+  itkTypeMacro( TrainImagesBase, Superclass )
+
+  /** filters typedefs*/
+  typedef otb::OGRDataToSamplePositionFilter<FloatVectorImageType, UInt8ImageType, otb::PeriodicSampler> PeriodicSamplerType;
+
+  typedef otb::SamplingRateCalculator::MapRateType MapRateType;
+
+protected:
+
+  enum SamplingStrategy
+  {
+    CLASS, GEOMETRIC
+  };
+  struct SamplingRates;
+  class TrainFileNamesHandler;
+
+  /**
+   * Initialize all the input and output parameter used for the train images
+   */
+  void InitIO();
+
+  /**
+   * Initialize sampling related application and parameters
+   */
+  void InitSampling();
+
+  void ShareSamplingParameters();
+  void ConnectSamplingParameters();
+  void InitClassification();
+  void ShareClassificationParams();
+  void ConnectClassificationParams();
+
+  /**
+   * Compute polygon statistics given provided strategy with PolygonClassStatistics class
+   * \param imageList list of input images
+   * \param vectorFileNames list of input vector file names
+   * \param statisticsFileNames list of out
+   */
+  void ComputePolygonStatistics(FloatVectorImageListType *imageList, const std::vector<std::string> &vectorFileNames,
+                                const std::vector<std::string> &statisticsFileNames);
+
+  /**
+   * Compute final maximum training and validation
+   * \param dedicatedValidation
+   * \return SamplingRates final maximum training and final maximum validation
+   */
+  SamplingRates ComputeFinalMaximumSamplingRates(bool dedicatedValidation);
+
+
+  /**
+   * Compute rates using MultiImageSamplingRate application
+   * \param statisticsFileNames
+   * \param ratesFileName
+   * \param maximum final maximum value computed by ComputeFinalMaximumSamplingRates
+   * \sa ComputeFinalMaximumSamplingRates
+   */
+  void ComputeSamplingRate(const std::vector<std::string> &statisticsFileNames,
+                           const std::string &ratesFileName,
+                           long maximum);
+  /**
+   * Train the model with training and optional validation data samples
+   * \param imageList list of input images
+   * \param sampleTrainFileNames files names of the training samples
+   * \param sampleValidationFileNames file names of the validation sample
+   */
+  void TrainModel(FloatVectorImageListType *imageList, const std::vector<std::string> &sampleTrainFileNames,
+                  const std::vector<std::string> &sampleValidationFileNames);
+
+  /**
+   * Select samples by class or by geographic strategy
+   * \param image
+   * \param vectorFileName
+   * \param sampleFileName
+   * \param statisticsFileName
+   * \param ratesFileName
+   * \param strategy
+   */
+  void SelectAndExtractSamples(FloatVectorImageType *image, std::string vectorFileName, std::string sampleFileName,
+                               std::string statisticsFileName, std::string ratesFileName, SamplingStrategy strategy,
+                               std::string selectedField = "");
+  /**
+   * Select and extract samples with the SampleSelection and SampleExtraction application.
+   * \param fileNames
+   * \param imageList
+   * \param vectorFileNames
+   * \param strategy the strategy used for selection (by class or with geometry)
+   * \param selectedFieldName
+   */
+  void SelectAndExtractTrainSamples(const TrainFileNamesHandler &fileNames, FloatVectorImageListType *imageList,
+                                    std::vector<std::string> vectorFileNames, SamplingStrategy strategy,
+                                    std::string selectedFieldName = "");
+
+
+  /**
+   * Function used to select validation samples based on a defined strategy (geometric in unsupervised mode)
+   * and extract them. With dedicated validation the 'by class' sampling strategy and statistics are used.
+   * Otherwise this function split training to validation samples corresponding to sample.vtr percentage.
+   * or do nothing if this percentage is == 0
+   * \param fileNames
+   * \param imageList
+   * \param validationVectorFileList optional validation vector file for each images
+   */
+  void SelectAndExtractValidationSamples(const TrainFileNamesHandler &fileNames, FloatVectorImageListType *imageList,
+                                         const std::vector<std::string> &validationVectorFileList = std::vector<std::string>());
+
+  /**
+   * Function used to split all training samples from all images in a set of training and validation.
+   * \param fileNames
+   * \param imageList
+   * \sa SplitTrainingAndValidationSamples
+   */
+  void SplitTrainingToValidationSamples(const TrainFileNamesHandler &fileNames, FloatVectorImageListType *imageList);
+
+private:
+
+  /**
+   * Function used to split training samples in set of training and validation.
+   * \param image input image
+   * \param sampleFileName the input sample file name
+   * \param sampleTrainFileName the input training file name
+   * \param sampleValidFileName the input validation file name
+   * \param ratesTrainFileName the rates file name
+   */
+  void SplitTrainingAndValidationSamples(FloatVectorImageType *image, std::string sampleFileName,
+                                         std::string sampleTrainFileName, std::string sampleValidFileName,
+                                         std::string ratesTrainFileName);
+
+
+protected:
+
+  struct SamplingRates
+  {
+    long int fmt;
+    long int fmv;
+  };
+
+  /**
+   * \class TrainFileNamesHandler
+   * This class is used to store file names requires for the application's input and output.
+   * And to clear temporary files generated by the applications
+   * \ingroup OTBAppClassification
+   */
+  class TrainFileNamesHandler
+  {
+  public :
+    void CreateTemporaryFileNames(std::string outModel, size_t nbInputs, bool dedicatedValidation)
+    {
+
+      if( dedicatedValidation )
+        {
+        rateTrainOut = outModel + "_ratesTrain.csv";
+        }
+      else
+        {
+        rateTrainOut = outModel + "_rates.csv";
+        }
+
+      rateValidOut = outModel + "_ratesValid.csv";
+      for( unsigned int i = 0; i < nbInputs; i++ )
+        {
+        std::ostringstream oss;
+        oss << i + 1;
+        std::string strIndex( oss.str() );
+        if( dedicatedValidation )
+          {
+          polyStatTrainOutputs.push_back( outModel + "_statsTrain_" + strIndex + ".xml" );
+          polyStatValidOutputs.push_back( outModel + "_statsValid_" + strIndex + ".xml" );
+          ratesTrainOutputs.push_back( outModel + "_ratesTrain_" + strIndex + ".csv" );
+          ratesValidOutputs.push_back( outModel + "_ratesValid_" + strIndex + ".csv" );
+          sampleOutputs.push_back( outModel + "_samplesTrain_" + strIndex + ".shp" );
+          }
+        else
+          {
+          polyStatTrainOutputs.push_back( outModel + "_stats_" + strIndex + ".xml" );
+          ratesTrainOutputs.push_back( outModel + "_rates_" + strIndex + ".csv" );
+          sampleOutputs.push_back( outModel + "_samples_" + strIndex + ".shp" );
+          }
+        sampleTrainOutputs.push_back( outModel + "_samplesTrain_" + strIndex + ".shp" );
+        sampleValidOutputs.push_back( outModel + "_samplesValid_" + strIndex + ".shp" );
+        }
+
+    }
+
+    void clear()
+    {
+      for( unsigned int i = 0; i < polyStatTrainOutputs.size(); i++ )
+        RemoveFile( polyStatTrainOutputs[i] );
+      for( unsigned int i = 0; i < polyStatValidOutputs.size(); i++ )
+        RemoveFile( polyStatValidOutputs[i] );
+      for( unsigned int i = 0; i < ratesTrainOutputs.size(); i++ )
+        RemoveFile( ratesTrainOutputs[i] );
+      for( unsigned int i = 0; i < ratesValidOutputs.size(); i++ )
+        RemoveFile( ratesValidOutputs[i] );
+      for( unsigned int i = 0; i < sampleOutputs.size(); i++ )
+        RemoveFile( sampleOutputs[i] );
+      for( unsigned int i = 0; i < sampleTrainOutputs.size(); i++ )
+        RemoveFile( sampleTrainOutputs[i] );
+      for( unsigned int i = 0; i < sampleValidOutputs.size(); i++ )
+        RemoveFile( sampleValidOutputs[i] );
+      for( unsigned int i = 0; i < tmpVectorFileList.size(); i++ )
+        RemoveFile( tmpVectorFileList[i] );
+    }
+
+  public:
+    std::vector<std::string> polyStatTrainOutputs;
+    std::vector<std::string> polyStatValidOutputs;
+    std::vector<std::string> ratesTrainOutputs;
+    std::vector<std::string> ratesValidOutputs;
+    std::vector<std::string> sampleOutputs;
+    std::vector<std::string> sampleTrainOutputs;
+    std::vector<std::string> sampleValidOutputs;
+    std::vector<std::string> tmpVectorFileList;
+    std::string rateValidOut;
+    std::string rateTrainOut;
+
+  private:
+    bool RemoveFile(std::string &filePath)
+    {
+      bool res = true;
+      if( itksys::SystemTools::FileExists( filePath.c_str() ) )
+        {
+        size_t posExt = filePath.rfind( '.' );
+        if( posExt != std::string::npos && filePath.compare( posExt, std::string::npos, ".shp" ) == 0 )
+          {
+          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 );
+          }
+        res = itksys::SystemTools::RemoveFile( filePath.c_str() );
+        if( !res )
+          {
+          //otbAppLogINFO( <<"Unable to remove file  "<<filePath );
+          }
+        }
+      return res;
+    }
+  };
+
+};
+
+} // end namespace Wrapper
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbTrainImagesBase.txx"
+#endif
+
+#endif //otbTrainImagesBase_h
diff --git a/Modules/Applications/AppClassification/include/otbTrainImagesBase.txx b/Modules/Applications/AppClassification/include/otbTrainImagesBase.txx
new file mode 100644
index 0000000000000000000000000000000000000000..deb34bfc735224c66b64ac8a4d3d15d0b98ed7b3
--- /dev/null
+++ b/Modules/Applications/AppClassification/include/otbTrainImagesBase.txx
@@ -0,0 +1,407 @@
+/*
+ * 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 otbTrainImagesBase_txx
+#define otbTrainImagesBase_txx
+
+#include "otbTrainImagesBase.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+void TrainImagesBase::InitIO()
+{
+  //Group IO
+  AddParameter( ParameterType_Group, "io", "Input and output data" );
+  SetParameterDescription( "io", "This group of parameters allows setting input and output data." );
+
+  AddParameter( ParameterType_InputImageList, "io.il", "Input Image List" );
+  SetParameterDescription( "io.il", "A list of input images." );
+  AddParameter( ParameterType_InputVectorDataList, "io.vd", "Input Vector Data List" );
+  SetParameterDescription( "io.vd", "A list of vector data to select the training samples." );
+  MandatoryOn( "io.vd" );
+
+  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" );
+}
+
+void TrainImagesBase::InitSampling()
+{
+  AddApplication( "PolygonClassStatistics", "polystat", "Polygon analysis" );
+  AddApplication( "MultiImageSamplingRate", "rates", "Sampling rates" );
+  AddApplication( "SampleSelection", "select", "Sample selection" );
+  AddApplication( "SampleExtraction", "extraction", "Sample extraction" );
+
+  // Sampling settings
+  AddParameter( ParameterType_Group, "sample", "Training and validation samples parameters" );
+  SetParameterDescription( "sample",
+                           "This group of parameters allows you to set training and validation sample lists parameters." );
+  AddParameter( ParameterType_Int, "sample.mt", "Maximum training sample size per class" );
+  SetDefaultParameterInt( "sample.mt", 1000 );
+  SetParameterDescription( "sample.mt", "Maximum size per class (in pixels) of "
+          "the training sample list (default = 1000) (no limit = -1). If equal to -1,"
+          " then the maximal size of the available training sample list per class "
+          "will be equal to the surface area of the smallest class multiplied by the"
+          " training sample ratio." );
+  AddParameter( ParameterType_Int, "sample.mv", "Maximum validation sample size per class" );
+  SetDefaultParameterInt( "sample.mv", 1000 );
+  SetParameterDescription( "sample.mv", "Maximum size per class (in pixels) of "
+          "the validation sample list (default = 1000) (no limit = -1). If equal to -1,"
+          " then the maximal size of the available validation sample list per class "
+          "will be equal to the surface area of the smallest class multiplied by the "
+          "validation sample ratio." );
+  AddParameter( ParameterType_Int, "sample.bm", "Bound sample number by minimum" );
+  SetDefaultParameterInt( "sample.bm", 1 );
+  SetParameterDescription( "sample.bm", "Bound the number of samples for each "
+          "class by the number of available samples by the smaller class. Proportions "
+          "between training and validation are respected. Default is true (=1)." );
+  AddParameter( ParameterType_Float, "sample.vtr", "Training and validation sample ratio" );
+  SetParameterDescription( "sample.vtr", "Ratio between training and validation samples (0.0 = all training, 1.0 = "
+          "all validation) (default = 0.5)." );
+  SetParameterFloat( "sample.vtr", 0.5, false );
+  SetMaximumParameterFloatValue( "sample.vtr", 1.0 );
+  SetMinimumParameterFloatValue( "sample.vtr", 0.0 );
+
+//  AddParameter( ParameterType_Float, "sample.percent", "Percentage of sample extract from images" );
+//  SetParameterDescription( "sample.percent", "Percentage of sample extract from images for "
+//          "training and validation when only images are provided." );
+//  SetDefaultParameterFloat( "sample.percent", 1.0 );
+//  SetMinimumParameterFloatValue( "sample.percent", 0.0 );
+//  SetMaximumParameterFloatValue( "sample.percent", 1.0 );
+
+  ShareSamplingParameters();
+  ConnectSamplingParameters();
+}
+
+void TrainImagesBase::ShareSamplingParameters()
+{
+  // hide sampling parameters
+  //ShareParameter("sample.strategy","rates.strategy");
+  //ShareParameter("sample.mim","rates.mim");
+  ShareParameter( "ram", "polystat.ram" );
+  ShareParameter( "elev", "polystat.elev" );
+  ShareParameter( "sample.vfn", "polystat.field" );
+}
+
+void TrainImagesBase::ConnectSamplingParameters()
+{
+  Connect( "extraction.field", "polystat.field" );
+  Connect( "extraction.layer", "polystat.layer" );
+
+  Connect( "select.ram", "polystat.ram" );
+  Connect( "extraction.ram", "polystat.ram" );
+
+  Connect( "select.field", "polystat.field" );
+  Connect( "select.layer", "polystat.layer" );
+  Connect( "select.elev", "polystat.elev" );
+
+  Connect( "extraction.in", "select.in" );
+  Connect( "extraction.vec", "select.out" );
+}
+
+void TrainImagesBase::InitClassification()
+{
+  AddApplication( "TrainVectorClassifier", "training", "Model training" );
+
+  AddParameter( ParameterType_InputVectorDataList, "io.valid", "Validation Vector Data List" );
+  SetParameterDescription( "io.valid", "A list of vector data to select the training samples." );
+  MandatoryOff( "io.valid" );
+
+  ShareClassificationParams();
+  ConnectClassificationParams();
+};
+
+void TrainImagesBase::ShareClassificationParams()
+{
+  ShareParameter( "io.imstat", "training.io.stats" );
+  ShareParameter( "io.out", "training.io.out" );
+
+  ShareParameter( "classifier", "training.classifier" );
+  ShareParameter( "rand", "training.rand" );
+
+  ShareParameter( "io.confmatout", "training.io.confmatout" );
+}
+
+void TrainImagesBase::ConnectClassificationParams()
+{
+  Connect( "training.cfield", "polystat.field" );
+  Connect( "select.rand", "training.rand" );
+}
+
+void TrainImagesBase::ComputePolygonStatistics(FloatVectorImageListType *imageList,
+                                               const std::vector<std::string> &vectorFileNames,
+                                               const std::vector<std::string> &statisticsFileNames)
+{
+  unsigned int nbImages = static_cast<unsigned int>(imageList->Size());
+  for( unsigned int i = 0; i < nbImages; i++ )
+    {
+    GetInternalApplication( "polystat" )->SetParameterInputImage( "in", imageList->GetNthElement( i ) );
+    GetInternalApplication( "polystat" )->SetParameterString( "vec", vectorFileNames[i], false );
+    GetInternalApplication( "polystat" )->SetParameterString( "out", statisticsFileNames[i], false );
+    ExecuteInternal( "polystat" );
+    }
+}
+
+
+TrainImagesBase::SamplingRates TrainImagesBase::ComputeFinalMaximumSamplingRates(bool dedicatedValidation)
+{
+  SamplingRates rates;
+  GetInternalApplication( "rates" )->SetParameterString( "mim", "proportional", false );
+  double vtr = GetParameterFloat( "sample.vtr" );
+  long mt = GetParameterInt( "sample.mt" );
+  long mv = GetParameterInt( "sample.mv" );
+  // compute final maximum training and final maximum validation
+  // By default take all samples (-1 means all samples)
+  rates.fmt = -1;
+  rates.fmv = -1;
+  if( GetParameterInt( "sample.bm" ) == 0 )
+    {
+    if( dedicatedValidation )
+      {
+      // fmt and fmv will be used separately
+      rates.fmt = mt;
+      rates.fmv = mv;
+      if( mt > -1 && mv <= -1 && vtr < 0.99999 )
+        {
+        rates.fmv = static_cast<long>(( double ) mt * vtr / ( 1.0 - vtr ));
+        }
+      if( mt <= -1 && mv > -1 && vtr > 0.00001 )
+        {
+        rates.fmt = static_cast<long>(( double ) mv * ( 1.0 - vtr ) / vtr);
+        }
+      }
+    else
+      {
+      // only fmt will be used for both training and validation samples
+      // So we try to compute the total number of samples given input
+      // parameters mt, mv and vtr.
+      if( mt > -1 && vtr < 0.99999 )
+        {
+        rates.fmt = static_cast<long>(( double ) mt / ( 1.0 - vtr ));
+        }
+      if( mv > -1 && vtr > 0.00001 )
+        {
+        if( rates.fmt > -1 )
+          {
+          rates.fmt = std::min( rates.fmt, static_cast<long>(( double ) mv / vtr) );
+          }
+        else
+          {
+          rates.fmt = static_cast<long>(( double ) mv / vtr);
+          }
+        }
+      }
+    }
+  return rates;
+}
+
+
+void TrainImagesBase::ComputeSamplingRate(const std::vector<std::string> &statisticsFileNames,
+                                          const std::string &ratesFileName, long maximum)
+{
+  // Sampling rates
+  GetInternalApplication( "rates" )->SetParameterStringList( "il", statisticsFileNames, false );
+  GetInternalApplication( "rates" )->SetParameterString( "out", ratesFileName, false );
+  if( GetParameterInt( "sample.bm" ) != 0 )
+    {
+    GetInternalApplication( "rates" )->SetParameterString( "strategy", "smallest", false );
+    }
+  else
+    {
+    if( maximum > -1 )
+      {
+      std::ostringstream oss;
+      oss << maximum;
+      GetInternalApplication( "rates" )->SetParameterString( "strategy", "constant", false );
+      GetInternalApplication( "rates" )->SetParameterString( "strategy.constant.nb", oss.str(), false );
+      }
+    else
+      {
+      GetInternalApplication( "rates" )->SetParameterString( "strategy", "all", false );
+      }
+    }
+  ExecuteInternal( "rates" );
+}
+
+void
+TrainImagesBase::TrainModel(FloatVectorImageListType *imageList, const std::vector<std::string> &sampleTrainFileNames,
+                            const std::vector<std::string> &sampleValidationFileNames)
+{
+  GetInternalApplication( "training" )->SetParameterStringList( "io.vd", sampleTrainFileNames, false );
+  if( !sampleValidationFileNames.empty() )
+    GetInternalApplication( "training" )->SetParameterStringList( "valid.vd", sampleValidationFileNames, false );
+
+  UpdateInternalParameters( "training" );
+  // set field names
+  FloatVectorImageType::Pointer image = imageList->GetNthElement( 0 );
+  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( "value_" + oss.str() );
+    }
+  GetInternalApplication( "training" )->SetParameterStringList( "feat", selectedNames, false );
+  ExecuteInternal( "training" );
+}
+
+void TrainImagesBase::SelectAndExtractSamples(FloatVectorImageType *image, std::string vectorFileName,
+                                              std::string sampleFileName, std::string statisticsFileName,
+                                              std::string ratesFileName, SamplingStrategy strategy,
+                                              std::string selectedField)
+{
+  GetInternalApplication( "select" )->SetParameterInputImage( "in", image );
+  GetInternalApplication( "select" )->SetParameterString( "out", sampleFileName, false );
+
+  // Change the selection strategy based on selected sampling strategy
+  switch( strategy )
+    {
+//    case GEOMETRIC:
+//      GetInternalApplication( "select" )->SetParameterString( "sampler", "random", false );
+//      GetInternalApplication( "select" )->SetParameterString( "strategy", "percent", false );
+//      GetInternalApplication( "select" )->SetParameterFloat( "strategy.percent.p",
+//                                                             GetParameterFloat( "sample.percent" ), false );
+//      break;
+    case CLASS:
+    default:
+      GetInternalApplication( "select" )->SetParameterString( "vec", vectorFileName, false );
+      GetInternalApplication( "select" )->SetParameterString( "instats", statisticsFileName, false );
+      GetInternalApplication( "select" )->SetParameterString( "sampler", "periodic", false );
+      GetInternalApplication( "select" )->SetParameterInt( "sampler.periodic.jitter", 50 );
+      GetInternalApplication( "select" )->SetParameterString( "strategy", "byclass", false );
+      GetInternalApplication( "select" )->SetParameterString( "strategy.byclass.in", ratesFileName, false );
+      break;
+    }
+
+  // select sample positions
+  ExecuteInternal( "select" );
+
+  GetInternalApplication( "extraction" )->SetParameterString( "vec", sampleFileName, false );
+  UpdateInternalParameters( "extraction" );
+  if( !selectedField.empty() )
+    GetInternalApplication( "extraction" )->SetParameterString( "field", selectedField, false );
+
+  GetInternalApplication( "extraction" )->SetParameterString( "outfield", "prefix", false );
+  GetInternalApplication( "extraction" )->SetParameterString( "outfield.prefix.name", "value_", false );
+
+  // extract sample descriptors
+  ExecuteInternal( "extraction" );
+}
+
+
+void TrainImagesBase::SelectAndExtractTrainSamples(const TrainFileNamesHandler &fileNames,
+                                                   FloatVectorImageListType *imageList,
+                                                   std::vector<std::string> vectorFileNames, SamplingStrategy strategy,
+                                                   std::string selectedFieldName)
+{
+
+  for( unsigned int i = 0; i < imageList->Size(); ++i )
+    {
+    std::string vectorFileName = vectorFileNames.empty() ? "" : vectorFileNames[i];
+    SelectAndExtractSamples( imageList->GetNthElement( i ), vectorFileName, fileNames.sampleOutputs[i],
+                             fileNames.polyStatTrainOutputs[i], fileNames.ratesTrainOutputs[i], strategy,
+                             selectedFieldName );
+    }
+}
+
+
+void TrainImagesBase::SelectAndExtractValidationSamples(const TrainFileNamesHandler &fileNames,
+                                                        FloatVectorImageListType *imageList,
+                                                        const std::vector<std::string> &validationVectorFileList)
+{
+  for( unsigned int i = 0; i < imageList->Size(); ++i )
+    {
+    SelectAndExtractSamples( imageList->GetNthElement( i ), validationVectorFileList[i],
+                             fileNames.sampleValidOutputs[i], fileNames.polyStatValidOutputs[i],
+                             fileNames.ratesValidOutputs[i], SamplingStrategy::CLASS );
+    }
+}
+
+void TrainImagesBase::SplitTrainingToValidationSamples(const TrainFileNamesHandler &fileNames,
+                                                       FloatVectorImageListType *imageList)
+{
+  for( unsigned int i = 0; i < imageList->Size(); ++i )
+    {
+    SplitTrainingAndValidationSamples( imageList->GetNthElement( i ), fileNames.sampleOutputs[i],
+                                       fileNames.sampleTrainOutputs[i], fileNames.sampleValidOutputs[i],
+                                       fileNames.ratesTrainOutputs[i] );
+    }
+}
+
+void TrainImagesBase::SplitTrainingAndValidationSamples(FloatVectorImageType *image, std::string sampleFileName,
+                                                        std::string sampleTrainFileName,
+                                                        std::string sampleValidFileName,
+                                                        std::string ratesTrainFileName)
+
+{
+  // Split between training and validation
+  ogr::DataSource::Pointer source = ogr::DataSource::New( sampleFileName, ogr::DataSource::Modes::Read );
+  ogr::DataSource::Pointer destTrain = ogr::DataSource::New( sampleTrainFileName, ogr::DataSource::Modes::Overwrite );
+  ogr::DataSource::Pointer destValid = ogr::DataSource::New( sampleValidFileName, ogr::DataSource::Modes::Overwrite );
+  // read sampling rates from ratesTrainOutputs
+  SamplingRateCalculator::Pointer rateCalculator = SamplingRateCalculator::New();
+  rateCalculator->Read( ratesTrainFileName );
+  // Compute sampling rates for train and valid
+  const MapRateType &inputRates = rateCalculator->GetRatesByClass();
+  MapRateType trainRates;
+  MapRateType validRates;
+  otb::SamplingRateCalculator::TripletType tpt;
+  for( MapRateType::const_iterator it = inputRates.begin(); it != inputRates.end(); ++it )
+    {
+    double vtr = GetParameterFloat( "sample.vtr" );
+    unsigned long total = std::min( it->second.Required, it->second.Tot );
+    unsigned long neededValid = static_cast<unsigned long>(( double ) total * vtr );
+    unsigned long neededTrain = total - neededValid;
+    tpt.Tot = total;
+    tpt.Required = neededTrain;
+    tpt.Rate = ( 1.0 - vtr );
+    trainRates[it->first] = tpt;
+    tpt.Tot = neededValid;
+    tpt.Required = neededValid;
+    tpt.Rate = 1.0;
+    validRates[it->first] = tpt;
+    }
+
+  // Use an otb::OGRDataToSamplePositionFilter with 2 outputs
+  PeriodicSamplerType::SamplerParameterType param;
+  param.Offset = 0;
+  param.MaxJitter = 0;
+  PeriodicSamplerType::Pointer splitter = PeriodicSamplerType::New();
+  splitter->SetInput( image );
+  splitter->SetOGRData( source );
+  splitter->SetOutputPositionContainerAndRates( destTrain, trainRates, 0 );
+  splitter->SetOutputPositionContainerAndRates( destValid, validRates, 1 );
+  splitter->SetFieldName( this->GetParameterStringList( "sample.vfn" )[0] );
+  splitter->SetLayerIndex( 0 );
+  splitter->SetOriginFieldName( std::string( "" ) );
+  splitter->SetSamplerParameters( param );
+  splitter->GetStreamer()->SetAutomaticTiledStreaming( static_cast<unsigned int>(this->GetParameterInt( "ram" )) );
+  AddProcess( splitter->GetStreamer(), "Split samples between training and validation..." );
+  splitter->Update();
+}
+}
+}
+
+#endif
diff --git a/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.txx b/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.txx
new file mode 100644
index 0000000000000000000000000000000000000000..ca03ea33b2d8ee3b3085db3d0f112447eebaf77b
--- /dev/null
+++ b/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.txx
@@ -0,0 +1,76 @@
+/*
+ * 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 otbTrainSharkKMeans_txx
+#define otbTrainSharkKMeans_txx
+
+#include "otbLearningApplicationBase.h"
+#include "otbSharkKMeansMachineLearningModel.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+template<class TInputValue, class TOutputValue>
+void LearningApplicationBase<TInputValue, TOutputValue>::InitSharkKMeansParams()
+{
+  AddChoice( "classifier.sharkkm", "Shark kmeans classifier" );
+  SetParameterDescription( "classifier.sharkkm",
+                           "This group of parameters allows setting Shark kMeans classifier parameters. "
+                                   "See complete documentation here "
+                                   "\\url{http://image.diku.dk/shark/sphinx_pages/build/html/rest_sources/tutorials/algorithms/kmeans.html}.\n " );
+  //MaxNumberOfIterations
+  AddParameter( ParameterType_Int, "classifier.sharkkm.maxiter",
+                "Maximum number of iteration for the kmeans algorithm." );
+  SetParameterInt( "classifier.sharkkm.maxiter", 10 );
+  SetMinimumParameterIntValue( "classifier.sharkkm.maxiter", 0 );
+  SetParameterDescription( "classifier.sharkkm.maxiter",
+                           "The maximum number of iteration for the kmeans algorithm. 0=unlimited" );
+
+  //MaxNumberOfIterations
+  AddParameter( ParameterType_Int, "classifier.sharkkm.k", "The number of class used for the kmeans algorithm." );
+  SetParameterInt( "classifier.sharkkm.k", 2 );
+  SetParameterDescription( "classifier.sharkkm.k",
+                           "The number of class used for the kmeans algorithm. Default set to 2 class" );
+  SetMinimumParameterIntValue( "classifier.sharkkm.k", 2 );
+}
+
+template<class TInputValue, class TOutputValue>
+void LearningApplicationBase<TInputValue, TOutputValue>::TrainSharkKMeans(
+        typename ListSampleType::Pointer trainingListSample,
+        typename TargetListSampleType::Pointer trainingLabeledListSample, std::string modelPath)
+{
+  unsigned int nbMaxIter = static_cast<unsigned int>(abs( GetParameterInt( "classifier.sharkkm.maxiter" ) ));
+  unsigned int k = static_cast<unsigned int>(abs( GetParameterInt( "classifier.sharkkm.k" ) ));
+
+  typedef otb::SharkKMeansMachineLearningModel<InputValueType, OutputValueType> SharkKMeansType;
+  typename SharkKMeansType::Pointer classifier = SharkKMeansType::New();
+  classifier->SetRegressionMode( this->m_RegressionFlag );
+  classifier->SetInputListSample( trainingListSample );
+  classifier->SetTargetListSample( trainingLabeledListSample );
+  classifier->SetK( k );
+  classifier->SetMaximumNumberOfIterations( nbMaxIter );
+  classifier->Train();
+  classifier->Save( modelPath );
+}
+
+} //end namespace wrapper
+} //end namespace otb
+
+#endif
diff --git a/Modules/Applications/AppClassification/include/otbTrainVectorBase.h b/Modules/Applications/AppClassification/include/otbTrainVectorBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..3e1d27b94902a067dfc992a4aefd1f854ffa4177
--- /dev/null
+++ b/Modules/Applications/AppClassification/include/otbTrainVectorBase.h
@@ -0,0 +1,196 @@
+/*
+ * 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 otbTrainVectorBase_h
+#define otbTrainVectorBase_h
+
+#include "otbLearningApplicationBase.h"
+#include "otbWrapperApplication.h"
+#include "otbWrapperApplicationFactory.h"
+
+#include "otbOGRDataSourceWrapper.h"
+#include "otbOGRFeatureWrapper.h"
+#include "otbStatisticsXMLFileWriter.h"
+
+#include "itkVariableLengthVector.h"
+#include "otbStatisticsXMLFileReader.h"
+
+#include "itkListSample.h"
+#include "otbShiftScaleSampleListFilter.h"
+
+#include <algorithm>
+#include <locale>
+
+namespace otb
+{
+namespace Wrapper
+{
+
+/** Utility function to negate std::isalnum */
+bool IsNotAlphaNum(char c)
+{
+  return !std::isalnum( c );
+}
+
+class TrainVectorBase : public LearningApplicationBase<float, int>
+{
+public:
+  /** Standard class typedefs. */
+  typedef TrainVectorBase Self;
+  typedef LearningApplicationBase<float, int> Superclass;
+  typedef itk::SmartPointer <Self> Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+  /** Standard macro */
+  itkTypeMacro(Self, Superclass)
+
+  typedef Superclass::SampleType SampleType;
+  typedef Superclass::ListSampleType ListSampleType;
+  typedef Superclass::TargetListSampleType TargetListSampleType;
+
+  typedef double ValueType;
+  typedef itk::VariableLengthVector <ValueType> MeasurementType;
+
+  typedef otb::StatisticsXMLFileReader<SampleType> StatisticsReader;
+
+  typedef otb::Statistics::ShiftScaleSampleListFilter<ListSampleType, ListSampleType> ShiftScaleFilterType;
+
+protected:
+
+  /** Class used to store statistics Measurment (mean/stddev) */
+  class ShiftScaleParameters
+  {
+  public:
+    MeasurementType meanMeasurementVector;
+    MeasurementType stddevMeasurementVector;
+  };
+
+  /** Class used to store a list of sample and the corresponding label */
+  class SamplesWithLabel
+  {
+  public:
+    ListSampleType::Pointer listSample;
+    TargetListSampleType::Pointer labeledListSample;
+    SamplesWithLabel()
+    {
+      listSample = ListSampleType::New();
+      labeledListSample = TargetListSampleType::New();
+    }
+  };
+
+  /**
+   * Features information class used to store informations
+   * about the field and class name/id of an input vector
+   */
+  class FeaturesInfo
+  {
+  public:
+    /** Selected Index */
+    std::vector<int> m_SelectedIdx;
+    /** Index for class field */
+    std::vector<int> m_SelectedCFieldIdx;
+    /** Selected class field name */
+    std::string m_SelectedCFieldName;
+    /** Selected names */
+    std::vector <std::string> m_SelectedNames;
+    unsigned int m_NbFeatures;
+
+    void SetFieldNames(std::vector <std::string> fieldNames, std::vector<int> selectedIdx)
+    {
+      m_SelectedIdx = selectedIdx;
+      m_NbFeatures = static_cast<unsigned int>(selectedIdx.size());
+      m_SelectedNames = std::vector<std::string>( m_NbFeatures );
+      for( unsigned int i = 0; i < m_NbFeatures; ++i )
+        {
+        m_SelectedNames[i] = fieldNames[selectedIdx[i]];
+        }
+    }
+    void SetClassFieldNames(std::vector<std::string> cFieldNames, std::vector<int> selectedCFieldIdx)
+    {
+      m_SelectedCFieldIdx = selectedCFieldIdx;
+      // Handle only one class field name, if several are provided only the first one is used.
+      m_SelectedCFieldName = cFieldNames[selectedCFieldIdx.front()];
+    }
+  };
+
+
+protected:
+
+  /**
+   * Function which extract and store all samples for Training and Classification.
+   * \param measurement statics measurement (mean/stddev)
+   * \param featuresInfo information about the features
+   */
+  virtual void ExtractAllSamples(const ShiftScaleParameters &measurement);
+
+  /**
+  * Extract the training sample list
+  * \param measurement statics measurement (mean/stddev)
+  * \param featuresInfo information about the features
+  * \return sample list used for training
+  */
+  virtual SamplesWithLabel ExtractTrainingSamplesWithLabel(const ShiftScaleParameters &measurement);
+
+  /**
+   * Extract classification the sample list
+   * \param measurement statics measurement (mean/stddev)
+   * \param featuresInfo information about the features
+   * \return sample list used for classification
+   */
+  virtual SamplesWithLabel ExtractClassificationSamplesWithLabel(const ShiftScaleParameters &measurement);
+
+
+  /** Extract samples from input file for corresponding field name
+   *
+   * \param parameterName the name of the input file option in the input application parameters
+   * \param parameterLayer the name of the layer option in the input application parameters
+   * \param measurement statics measurement (mean/stddev)
+   * \param nbFeatures the number of features.
+   * \return the list of samples and their corresponding labels.
+   */
+  SamplesWithLabel
+  ExtractSamplesWithLabel(std::string parameterName, std::string parameterLayer, const ShiftScaleParameters &measurement);
+
+
+  /**
+   * Retrieve statistics mean and standard deviation if input statistics are provided.
+   * Otherwise mean is set to 0 and standard deviation to 1 for each Features.
+   * \param nbFeatures
+   */
+  ShiftScaleParameters GetStatistics(unsigned int nbFeatures);
+
+  SamplesWithLabel m_TrainingSamplesWithLabel;
+  SamplesWithLabel m_ClassificationSamplesWithLabel;
+  TargetListSampleType::Pointer m_PredictedList;
+  FeaturesInfo m_FeaturesInfo;
+
+  void DoInit() ITK_OVERRIDE;
+  void DoUpdateParameters() ITK_OVERRIDE;
+  void DoExecute() ITK_OVERRIDE;
+
+};
+
+}
+}
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbTrainVectorBase.txx"
+#endif
+
+#endif
diff --git a/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx b/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx
new file mode 100644
index 0000000000000000000000000000000000000000..57554baccf5174c0ad084a1e80b99a0d93e5f56c
--- /dev/null
+++ b/Modules/Applications/AppClassification/include/otbTrainVectorBase.txx
@@ -0,0 +1,305 @@
+/*
+ * 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 otbTrainVectorBase_txx
+#define otbTrainVectorBase_txx
+
+#include "otbTrainVectorBase.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+void TrainVectorBase::DoInit()
+{
+  // Common Parameters for all Learning Application
+  AddParameter( ParameterType_Group, "io", "Input and output data" );
+  SetParameterDescription( "io", "This group of parameters allows setting input and output data." );
+
+  AddParameter( ParameterType_InputVectorDataList, "io.vd", "Input Vector Data" );
+  SetParameterDescription( "io.vd",
+                           "Input geometries used for training (note : all geometries from the layer will be used)" );
+
+  AddParameter( ParameterType_InputFilename, "io.stats", "Input XML image statistics file" );
+  MandatoryOff( "io.stats" );
+  SetParameterDescription( "io.stats", "XML file containing mean and variance of each feature." );
+
+  AddParameter( ParameterType_OutputFilename, "io.out", "Output model" );
+  SetParameterDescription( "io.out", "Output file containing the model estimated (.txt format)." );
+
+  AddParameter( ParameterType_Int, "layer", "Layer Index" );
+  SetParameterDescription( "layer", "Index of the layer to use in the input vector file." );
+  MandatoryOff( "layer" );
+  SetDefaultParameterInt( "layer", 0 );
+
+  AddParameter(ParameterType_ListView,  "feat", "Field names for training features.");
+  SetParameterDescription("feat","List of field names in the input vector data to be used as features for training.");
+
+  // Add validation data used to compute confusion matrix or contingency table
+  AddParameter( ParameterType_Group, "valid", "Validation data" );
+  SetParameterDescription( "valid", "This group of parameters defines validation data." );
+
+  AddParameter( ParameterType_InputVectorDataList, "valid.vd", "Validation Vector Data" );
+  SetParameterDescription( "valid.vd", "Geometries used for validation "
+          "(must contain the same fields used for training, all geometries from the layer will be used)" );
+  MandatoryOff( "valid.vd" );
+
+  AddParameter( ParameterType_Int, "valid.layer", "Layer Index" );
+  SetParameterDescription( "valid.layer", "Index of the layer to use in the validation vector file." );
+  MandatoryOff( "valid.layer" );
+  SetDefaultParameterInt( "valid.layer", 0 );
+
+  // Add class field if we used validation
+  AddParameter( ParameterType_ListView, "cfield", "Field containing the class id for supervision" );
+  SetParameterDescription( "cfield", "Field containing the class id for supervision. "
+          "Only geometries with this field available will be taken into account." );
+  SetListViewSingleSelectionMode( "cfield", true );
+
+  // Add a new parameter to compute confusion matrix / contingency table
+  AddParameter( ParameterType_OutputFilename, "io.confmatout", "Output confusion matrix or contingency table" );
+  SetParameterDescription( "io.confmatout", "Output file containing the confusion matrix or contingency table (.csv format)."
+          "The contingency table is ouput when we unsupervised algorithms is used otherwise the confusion matrix is output." );
+  MandatoryOff( "io.confmatout" );
+
+
+  // Doc example parameter settings
+  SetDocExampleParameterValue( "io.vd", "vectorData.shp" );
+  SetDocExampleParameterValue( "io.stats", "meanVar.xml" );
+  SetDocExampleParameterValue( "io.out", "svmModel.svm" );
+  SetDocExampleParameterValue( "feat", "perimeter  area  width" );
+  SetDocExampleParameterValue( "cfield", "predicted" );
+
+
+  // Add parameters for the classifier choice
+  Superclass::DoInit();
+
+  AddRANDParameter();
+}
+
+void TrainVectorBase::DoUpdateParameters()
+{
+  // if vector data is present and updated then reload fields
+  if( HasValue( "io.vd" ) )
+    {
+    std::vector<std::string> vectorFileList = GetParameterStringList( "io.vd" );
+    ogr::DataSource::Pointer ogrDS = ogr::DataSource::New( vectorFileList[0], ogr::DataSource::Modes::Read );
+    ogr::Layer layer = ogrDS->GetLayer( static_cast<size_t>( this->GetParameterInt( "layer" ) ) );
+    ogr::Feature feature = layer.ogr().GetNextFeature();
+
+    ClearChoices( "feat" );
+    ClearChoices( "cfield" );
+
+    for( int iField = 0; iField < feature.ogr().GetFieldCount(); iField++ )
+      {
+      std::string key, item = feature.ogr().GetFieldDefnRef( iField )->GetNameRef();
+      key = item;
+      std::string::iterator end = std::remove_if( key.begin(), key.end(), IsNotAlphaNum );
+      std::transform( key.begin(), end, key.begin(), tolower );
+
+      OGRFieldType fieldType = feature.ogr().GetFieldDefnRef( iField )->GetType();
+
+      if( fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64( fieldType ) || fieldType == OFTReal )
+        {
+        std::string tmpKey = "feat." + key.substr( 0, static_cast<unsigned long>( end - key.begin() ) );
+        AddChoice( tmpKey, item );
+        }
+      if( fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64( fieldType ) )
+        {
+        std::string tmpKey = "cfield." + key.substr( 0, static_cast<unsigned long>( end - key.begin() ) );
+        AddChoice( tmpKey, item );
+        }
+      }
+    }
+}
+
+void TrainVectorBase::DoExecute()
+{
+  m_FeaturesInfo.SetFieldNames( GetChoiceNames( "feat" ), GetSelectedItems( "feat" ));
+
+  // Check input parameters
+  if( m_FeaturesInfo.m_SelectedIdx.empty() )
+    {
+    otbAppLogFATAL( << "No features have been selected to train the classifier on!" );
+    }
+
+  ShiftScaleParameters measurement = GetStatistics( m_FeaturesInfo.m_NbFeatures );
+  ExtractAllSamples( measurement );
+
+  this->Train( m_TrainingSamplesWithLabel.listSample, m_TrainingSamplesWithLabel.labeledListSample, GetParameterString( "io.out" ) );
+
+  m_PredictedList = TargetListSampleType::New();
+  this->Classify( m_ClassificationSamplesWithLabel.listSample, m_PredictedList, GetParameterString( "io.out" ) );
+}
+
+
+void TrainVectorBase::ExtractAllSamples(const ShiftScaleParameters &measurement)
+{
+  m_TrainingSamplesWithLabel = ExtractTrainingSamplesWithLabel(measurement);
+  m_ClassificationSamplesWithLabel = ExtractClassificationSamplesWithLabel(measurement);
+}
+
+TrainVectorBase::SamplesWithLabel
+TrainVectorBase::ExtractTrainingSamplesWithLabel(const ShiftScaleParameters &measurement)
+{
+  return ExtractSamplesWithLabel( "io.vd", "layer", measurement);
+}
+
+TrainVectorBase::SamplesWithLabel
+TrainVectorBase::ExtractClassificationSamplesWithLabel(const ShiftScaleParameters &measurement)
+{
+  if(GetClassifierCategory() == Supervised)
+    {
+    SamplesWithLabel tmpSamplesWithLabel;
+    SamplesWithLabel validationSamplesWithLabel = ExtractSamplesWithLabel( "valid.vd", "valid.layer", measurement );
+    //Test the input validation set size
+    if( validationSamplesWithLabel.labeledListSample->Size() != 0 )
+      {
+      tmpSamplesWithLabel.listSample = validationSamplesWithLabel.listSample;
+      tmpSamplesWithLabel.labeledListSample = validationSamplesWithLabel.labeledListSample;
+      }
+    else
+      {
+      otbAppLogWARNING(
+              "The validation set is empty. The performance estimation is done using the input training set in this case." );
+      tmpSamplesWithLabel.listSample = m_TrainingSamplesWithLabel.listSample;
+      tmpSamplesWithLabel.labeledListSample = m_TrainingSamplesWithLabel.labeledListSample;
+      }
+
+    return tmpSamplesWithLabel;
+    }
+  else
+    {
+    return m_TrainingSamplesWithLabel;
+    }
+}
+
+
+TrainVectorBase::ShiftScaleParameters
+TrainVectorBase::GetStatistics(unsigned int nbFeatures)
+{
+  ShiftScaleParameters measurement = ShiftScaleParameters();
+  if( HasValue( "io.stats" ) && IsParameterEnabled( "io.stats" ) )
+    {
+    StatisticsReader::Pointer statisticsReader = StatisticsReader::New();
+    std::string XMLfile = GetParameterString( "io.stats" );
+    statisticsReader->SetFileName( XMLfile.c_str() );
+    measurement.meanMeasurementVector = statisticsReader->GetStatisticVectorByName( "mean" );
+    measurement.stddevMeasurementVector = statisticsReader->GetStatisticVectorByName( "stddev" );
+    }
+  else
+    {
+    measurement.meanMeasurementVector.SetSize( nbFeatures );
+    measurement.meanMeasurementVector.Fill( 0. );
+    measurement.stddevMeasurementVector.SetSize( nbFeatures );
+    measurement.stddevMeasurementVector.Fill( 1. );
+    }
+  return measurement;
+}
+
+
+TrainVectorBase::SamplesWithLabel
+TrainVectorBase::ExtractSamplesWithLabel(std::string parameterName, std::string parameterLayer,
+                                    const ShiftScaleParameters &measurement)
+{
+  SamplesWithLabel samplesWithLabel;
+  if( HasValue( parameterName ) && IsParameterEnabled( parameterName ) )
+    {
+    ListSampleType::Pointer input = ListSampleType::New();
+    TargetListSampleType::Pointer target = TargetListSampleType::New();
+    input->SetMeasurementVectorSize( m_FeaturesInfo.m_NbFeatures );
+
+    std::vector<std::string> fileList = this->GetParameterStringList( parameterName );
+    for( unsigned int k = 0; k < fileList.size(); k++ )
+      {
+      otbAppLogINFO( "Reading vector file " << k + 1 << "/" << fileList.size() );
+      ogr::DataSource::Pointer source = ogr::DataSource::New( fileList[k], ogr::DataSource::Modes::Read );
+      ogr::Layer layer = source->GetLayer( static_cast<size_t>(this->GetParameterInt( parameterLayer )) );
+      ogr::Feature feature = layer.ogr().GetNextFeature();
+      bool goesOn = feature.addr() != 0;
+      if( !goesOn )
+        {
+        otbAppLogWARNING( "The layer " << GetParameterInt( parameterLayer ) << " of " << fileList[k]
+                                       << " is empty, input is skipped." );
+        continue;
+        }
+
+      // Check all needed fields are present :
+      //   - check class field if we use supervised classification or if class field name is not empty
+      int cFieldIndex = feature.ogr().GetFieldIndex( m_FeaturesInfo.m_SelectedCFieldName.c_str() );
+      if( cFieldIndex < 0 && !m_FeaturesInfo.m_SelectedCFieldName.empty())
+        {
+        otbAppLogFATAL( "The field name for class label (" << m_FeaturesInfo.m_SelectedCFieldName
+                                                           << ") has not been found in the vector file "
+                                                           << fileList[k] );
+        }
+
+      //   - check feature fields
+      std::vector<int> featureFieldIndex( m_FeaturesInfo.m_NbFeatures, -1 );
+      for( unsigned int i = 0; i < m_FeaturesInfo.m_NbFeatures; i++ )
+        {
+        featureFieldIndex[i] = feature.ogr().GetFieldIndex( m_FeaturesInfo.m_SelectedNames[i].c_str() );
+        if( featureFieldIndex[i] < 0 )
+          otbAppLogFATAL( "The field name for feature " << m_FeaturesInfo.m_SelectedNames[i]
+                                                        << " has not been found in the vector file "
+                                                        << fileList[k] );
+        }
+
+
+      while( goesOn )
+        {
+        // Retrieve all the features for each field in the ogr layer.
+        MeasurementType mv;
+        mv.SetSize( m_FeaturesInfo.m_NbFeatures );
+        for( unsigned int idx = 0; idx < m_FeaturesInfo.m_NbFeatures; ++idx )
+          mv[idx] = feature.ogr().GetFieldAsDouble( featureFieldIndex[idx] );
+
+        input->PushBack( mv );
+
+        if( feature.ogr().IsFieldSet( cFieldIndex ) && cFieldIndex != -1 )
+          target->PushBack( feature.ogr().GetFieldAsInteger( cFieldIndex ) );
+        else
+          target->PushBack( 0 );
+
+        feature = layer.ogr().GetNextFeature();
+        goesOn = feature.addr() != 0;
+        }
+      }
+
+
+
+    ShiftScaleFilterType::Pointer shiftScaleFilter = ShiftScaleFilterType::New();
+    shiftScaleFilter->SetInput( input );
+    shiftScaleFilter->SetShifts( measurement.meanMeasurementVector );
+    shiftScaleFilter->SetScales( measurement.stddevMeasurementVector );
+    shiftScaleFilter->Update();
+
+    samplesWithLabel.listSample = shiftScaleFilter->GetOutput();
+    samplesWithLabel.labeledListSample = target;
+    samplesWithLabel.listSample->DisconnectPipeline();
+    }
+
+  return samplesWithLabel;
+}
+
+
+}
+}
+
+#endif
diff --git a/Modules/Applications/AppClassification/otb-module.cmake b/Modules/Applications/AppClassification/otb-module.cmake
index 07b121f77de3512038be604d8f9e67f9ffa0985e..ca0c9281275d233b7dcb441d667fb295d5580755 100644
--- a/Modules/Applications/AppClassification/otb-module.cmake
+++ b/Modules/Applications/AppClassification/otb-module.cmake
@@ -31,7 +31,9 @@ otb_module(OTBAppClassification
     OTBMajorityVoting
     OTBVectorDataIO
     OTBSOM
+    OTBLearningBase
     OTBSupervised
+    OTBUnsupervised
     OTBApplicationEngine
     OTBIndices
     OTBMathParser
diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt
index 3a340346d8e549420ba3904edbfe9b99377dee12..3b4e213470625f57238c635383b4bacf22e93e7c 100644
--- a/Modules/Applications/AppClassification/test/CMakeLists.txt
+++ b/Modules/Applications/AppClassification/test/CMakeLists.txt
@@ -21,46 +21,46 @@
 otb_module_test()
 #----------- ComputeOGRLayersFeaturesStatistics TESTS ----------------
 otb_test_application(NAME apTvClComputeOGRLayersFeaturesStatistics
-                     APP  ComputeOGRLayersFeaturesStatistics
-                     OPTIONS -inshp ${INPUTDATA}/Classification/apTvClLabeledVector.shp
-                             -feat meanB0 meanB1 meanB2 meanB3 varB0 varB1 varB2 varB3
-                             -outstats ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/apTvClComputeOGRLayersFeaturesStatistics.xml
-                             ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml)
+  APP  ComputeOGRLayersFeaturesStatistics
+  OPTIONS -inshp ${INPUTDATA}/Classification/apTvClLabeledVector.shp
+  -feat meanB0 meanB1 meanB2 meanB3 varB0 varB1 varB2 varB3
+  -outstats ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvClComputeOGRLayersFeaturesStatistics.xml
+  ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml)
 
 #----------- SOMClassification TESTS ----------------
 otb_test_application(NAME apTvClSOMClassificationSmall
-                     APP  SOMClassification
-                     OPTIONS -in ${INPUTDATA}/poupees_sub.png
-                             -rand 121212
-                             -out ${TEMP}/apTvClSOMClassificationSmall.tif uint16
-                     VALID   --compare-image ${NOTOL}
-                             ${BASELINE}/apTvClSOMClassificationSmall.tif
-                             ${TEMP}/apTvClSOMClassificationSmall.tif)
+  APP  SOMClassification
+  OPTIONS -in ${INPUTDATA}/poupees_sub.png
+  -rand 121212
+  -out ${TEMP}/apTvClSOMClassificationSmall.tif uint16
+  VALID   --compare-image ${NOTOL}
+  ${BASELINE}/apTvClSOMClassificationSmall.tif
+  ${TEMP}/apTvClSOMClassificationSmall.tif)
 
 otb_test_application(NAME apTvClSOMClassificationFull
-                     APP  SOMClassification
-                     OPTIONS -in  ${INPUTDATA}/poupees_sub.png
-                             -out ${TEMP}/apTvClSOMClassificationFull.tif uint16
-                             -vm  ${INPUTDATA}/poupees_sub_c1.png
-                             -tp  0.8
-                             -ts  13000
-                             -som ${TEMP}/apTvClSOMClassificationMap.hdr
-                             -sx  30
-                             -sy  30
-                             -nx  9
-                             -ny  9
-                             -ni  5
-                             -bi  1.0
-                             -bf  0.1
-                             -iv  0
-                             -rand 121212
-                     VALID   --compare-n-images ${NOTOL} 2
-                             ${BASELINE}/apTvClSOMClassificationFull.tif
-                             ${TEMP}/apTvClSOMClassificationFull.tif
-                             ${BASELINE}/apTvClSOMClassificationMap.hdr
-                             ${TEMP}/apTvClSOMClassificationMap.hdr)
+  APP  SOMClassification
+  OPTIONS -in  ${INPUTDATA}/poupees_sub.png
+  -out ${TEMP}/apTvClSOMClassificationFull.tif uint16
+  -vm  ${INPUTDATA}/poupees_sub_c1.png
+  -tp  0.8
+  -ts  13000
+  -som ${TEMP}/apTvClSOMClassificationMap.hdr
+  -sx  30
+  -sy  30
+  -nx  9
+  -ny  9
+  -ni  5
+  -bi  1.0
+  -bf  0.1
+  -iv  0
+  -rand 121212
+  VALID   --compare-n-images ${NOTOL} 2
+  ${BASELINE}/apTvClSOMClassificationFull.tif
+  ${TEMP}/apTvClSOMClassificationFull.tif
+  ${BASELINE}/apTvClSOMClassificationMap.hdr
+  ${TEMP}/apTvClSOMClassificationMap.hdr)
 
 
 #----------- ImageClassifier TESTS ----------------
@@ -96,6 +96,7 @@ set(bayes_output_format ".bayes")
 set(rf_output_format ".rf")
 set(knn_output_format ".knn")
 set(sharkrf_output_format ".txt")
+set(sharkkm_output_format ".txt")
 
 # Training algorithms parameters
 set(libsvm_parameters "-classifier.libsvm.opt" "true" "-classifier.libsvm.prob" "true")
@@ -108,6 +109,7 @@ set(bayes_parameters "")
 set(rf_parameters "")
 set(knn_parameters "")
 set(sharkrf_parameters "")
+set(sharkkm_parameters "")
 
 # Validation depending on mode
 set(ascii_comparison --compare-ascii ${EPSILON_6})
@@ -120,7 +122,7 @@ set(raster_ref_path ${OTBAPP_BASELINE})
 
 set(classifierList)
 #if(OTB_USE_LIBSVM)
-  #list(APPEND classifierList "LIBSVM")
+#list(APPEND classifierList "LIBSVM")
 #endif()
 if(OTB_USE_OPENCV)
   #list(APPEND classifierList "SVM" "BOOST" "DT" "GBT" "ANN" "BAYES" "RF" "KNN")
@@ -130,14 +132,14 @@ if(OTB_USE_OPENCV)
   endif()
 endif()
 if(OTB_USE_SHARK)
-  list(APPEND classifierList "SHARKRF")
+  list(APPEND classifierList "SHARKRF" "SHARKKM")
 endif()
 
 set(classifier_with_confmap "LIBSVM" "BOOST" "KNN" "ANN" "RF")
 
 # This is a black list for classifier that can not have a baseline
 # because they are using randomness and seed can not be set
-set(classifier_without_baseline "SHARKRF")
+set(classifier_without_baseline "SHARKRF" "SHARKKM")
 
 # Loop on classifiers
 foreach(classifier ${classifierList})
@@ -154,21 +156,21 @@ foreach(classifier ${classifierList})
   else()
     set(valid "")
   endif()
-  
+
   otb_test_application(
-                       NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1
-                       APP      TrainImagesClassifier
-                       OPTIONS  -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
-                                -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
-                                -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
-                                -classifier ${lclassifier}
-                                ${${lclassifier}_parameters}
-                                -io.out ${TEMP}/${OUTMODELFILE}
-                                -sample.vfn Class
-                                -rand 121212
-
-                       VALID    ${valid}
-                       )
+    NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1
+    APP      TrainImagesClassifier
+    OPTIONS  -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
+    -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
+    -classifier ${lclassifier}
+    ${${lclassifier}_parameters}
+    -io.out ${TEMP}/${OUTMODELFILE}
+    -sample.vfn Class
+    -rand 121212
+
+    VALID    ${valid}
+  )
 
   if(${_classifier_has_baseline} EQUAL -1)
     set(valid ${ascii_comparison} ${ascii_ref_path}/${OUTMODELFILE} ${TEMP}/OutXML1_${OUTMODELFILE})
@@ -177,20 +179,20 @@ foreach(classifier ${classifierList})
   endif()
 
   otb_test_application(
-                       NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1_OutXML1
-                       APP      TrainImagesClassifier
-                       OPTIONS  -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
-                                -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
-                                -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
-                                -classifier ${lclassifier}
-                                ${${lclassifier}_parameters}
-                                -io.out ${TEMP}/OutXML1_${OUTMODELFILE}
-                                -rand 121212
-                                -sample.vfn Class
-                                -outxml ${TEMP}/cl${classifier}_OutXML1.xml
-
-                       VALID ${valid}
-                       )
+    NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1_OutXML1
+    APP      TrainImagesClassifier
+    OPTIONS  -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
+    -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
+    -classifier ${lclassifier}
+    ${${lclassifier}_parameters}
+    -io.out ${TEMP}/OutXML1_${OUTMODELFILE}
+    -rand 121212
+    -sample.vfn Class
+    -outxml ${TEMP}/cl${classifier}_OutXML1.xml
+
+    VALID ${valid}
+  )
 
   if(${_classifier_has_baseline} EQUAL -1)
     set(valid ${ascii_comparison} ${ascii_ref_path}/${OUTMODELFILE} ${TEMP}/OutXML2_${OUTMODELFILE})
@@ -199,744 +201,746 @@ foreach(classifier ${classifierList})
   endif()
 
   otb_test_application(
-                       NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1_InXML1
-                       APP      TrainImagesClassifier
-                       OPTIONS  -inxml ${INPUTDATA}/cl${classifier}_OutXML1.xml
-                                -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
-                                -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
-                                -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
-                                -io.out ${TEMP}/OutXML2_${OUTMODELFILE}
-                                -sample.vfn Class
-                       VALID    ${valid}
-                       )
-
-                     #set_tests_properties(apTvClTrainMethod${classifier}ImagesClassifierQB1_InXML1 PROPERTIES DEPENDS apTvClTrainMethod${classifier}ImagesClassifierQB1_OutXML1)
+    NAME     apTvClTrainMethod${classifier}ImagesClassifierQB1_InXML1
+    APP      TrainImagesClassifier
+    OPTIONS  -inxml ${INPUTDATA}/cl${classifier}_OutXML1.xml
+    -io.il ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
+    -io.vd ${INPUTDATA}/Classification/VectorData_${${lclassifier}_input}QB1${vector_input_format}
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
+    -io.out ${TEMP}/OutXML2_${OUTMODELFILE}
+    -sample.vfn Class
+    VALID    ${valid}
+  )
+
+  #set_tests_properties(apTvClTrainMethod${classifier}ImagesClassifierQB1_InXML1 PROPERTIES DEPENDS apTvClTrainMethod${classifier}ImagesClassifierQB1_OutXML1)
 
   list(FIND classifier_with_confmap ${classifier} _classifier_has_confmap)
   if(${_classifier_has_confmap} EQUAL -1)
     otb_test_application(
-                       NAME     apTvClMethod${classifier}ImageClassifierQB1
-                       APP      ImageClassifier
-                       OPTIONS  -in ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
-                                -model ${INPUTDATA}/Classification/${OUTMODELFILE}
-                                -imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
-                                -out ${TEMP}/${OUTRASTER} ${raster_output_option}
-
-                       VALID    ${raster_comparison}
-                                ${raster_ref_path}/${OUTRASTER}
-                                ${TEMP}/${OUTRASTER}
-                       )
+      NAME     apTvClMethod${classifier}ImageClassifierQB1
+      APP      ImageClassifier
+      OPTIONS  -in ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
+      -model ${INPUTDATA}/Classification/${OUTMODELFILE}
+      -imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
+      -out ${TEMP}/${OUTRASTER} ${raster_output_option}
+
+      VALID    ${raster_comparison}
+      ${raster_ref_path}/${OUTRASTER}
+      ${TEMP}/${OUTRASTER}
+    )
   else()
     otb_test_application(
-                       NAME     apTvClMethod${classifier}ImageClassifierQB1
-                       APP      ImageClassifier
-                       OPTIONS  -in ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
-                                -model ${INPUTDATA}/Classification/${OUTMODELFILE}
-                                -imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
-                                -out ${TEMP}/${OUTRASTER} ${raster_output_option}
-                                -confmap ${TEMP}/${OUTCONFMAP}
-
-                       VALID    ${raster_comparison_two}
-                                ${raster_ref_path}/${OUTRASTER}
-                                ${TEMP}/${OUTRASTER}
-                                ${raster_ref_path}/${OUTCONFMAP}
-                                ${TEMP}/${OUTCONFMAP}
-                       )
+      NAME     apTvClMethod${classifier}ImageClassifierQB1
+      APP      ImageClassifier
+      OPTIONS  -in ${INPUTDATA}/Classification/QB_1_ortho${raster_input_format}
+      -model ${INPUTDATA}/Classification/${OUTMODELFILE}
+      -imstat ${INPUTDATA}/Classification/clImageStatisticsQB1${stat_input_format}
+      -out ${TEMP}/${OUTRASTER} ${raster_output_option}
+      -confmap ${TEMP}/${OUTCONFMAP}
+
+      VALID    ${raster_comparison_two}
+      ${raster_ref_path}/${OUTRASTER}
+      ${TEMP}/${OUTRASTER}
+      ${raster_ref_path}/${OUTCONFMAP}
+      ${TEMP}/${OUTCONFMAP}
+    )
   endif()
 
 endforeach()
 
+#----------- LIBSVM Classifier TESTS ----------------
+
 if(OTB_USE_LIBSVM)
   otb_test_application(NAME apTvClImageSVMClassifierQB2
-                      APP  ImageClassifier
-                      OPTIONS -in      ${INPUTDATA}/Classification/QB_2_ortho.tif
-                              -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
-                              -out     ${TEMP}/clLabeledImageQB2.tif
-                      VALID   --compare-image ${NOTOL}
-                              ${OTBAPP_BASELINE}/clLabeledImageQB2.tif
-                              ${TEMP}/clLabeledImageQB2.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_2_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
+    -out     ${TEMP}/clLabeledImageQB2.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB2.tif
+    ${TEMP}/clLabeledImageQB2.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB3
-                      APP  ImageClassifier
-                      OPTIONS -in      ${INPUTDATA}/Classification/QB_3_ortho.tif
-                              -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
-                              -out     ${TEMP}/clLabeledImageQB3.tif
-                      VALID   --compare-image ${NOTOL}
-                              ${OTBAPP_BASELINE}/clLabeledImageQB3.tif
-                              ${TEMP}/clLabeledImageQB3.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_3_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
+    -out     ${TEMP}/clLabeledImageQB3.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB3.tif
+    ${TEMP}/clLabeledImageQB3.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB1
-                      APP  ImageClassifier
-                      OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
-                              -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
-                              -out     ${TEMP}/clLabeledImageQB1.tif
-                      VALID   --compare-image ${NOTOL}
-                              ${OTBAPP_BASELINE}/clLabeledImageQB1.tif
-                              ${TEMP}/clLabeledImageQB1.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB1.svm
+    -out     ${TEMP}/clLabeledImageQB1.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB1.tif
+    ${TEMP}/clLabeledImageQB1.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB456_6
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_6_extract.tif
-                               -mask    ${INPUTDATA}/Classification/QB_6_mask.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
-                               -out     ${TEMP}/clLabeledImageQB456_6.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB456_6.tif
-                               ${TEMP}/clLabeledImageQB456_6.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_6_extract.tif
+    -mask    ${INPUTDATA}/Classification/QB_6_mask.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
+    -out     ${TEMP}/clLabeledImageQB456_6.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB456_6.tif
+    ${TEMP}/clLabeledImageQB456_6.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB456_4
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_4_extract.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
-                               -out     ${TEMP}/clLabeledImageQB456_4.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB456_4.tif
-                               ${TEMP}/clLabeledImageQB456_4.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_4_extract.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
+    -out     ${TEMP}/clLabeledImageQB456_4.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB456_4.tif
+    ${TEMP}/clLabeledImageQB456_4.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB456_5
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_5_extract.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
-                               -out     ${TEMP}/clLabeledImageQB456_5.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB456_5.tif
-                               ${TEMP}/clLabeledImageQB456_5.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_5_extract.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
+    -out     ${TEMP}/clLabeledImageQB456_5.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB456_5.tif
+    ${TEMP}/clLabeledImageQB456_5.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB123_6
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_6_extract.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
-                               -out     ${TEMP}/clLabeledImageQB123_6.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB123_6.tif
-                               ${TEMP}/clLabeledImageQB123_6.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_6_extract.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
+    -out     ${TEMP}/clLabeledImageQB123_6.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB123_6.tif
+    ${TEMP}/clLabeledImageQB123_6.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB456_1
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
-                               -out     ${TEMP}/clLabeledImageQB456_1.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB456_1.tif
-                               ${TEMP}/clLabeledImageQB456_1.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB456.svm
+    -out     ${TEMP}/clLabeledImageQB456_1.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB456_1.tif
+    ${TEMP}/clLabeledImageQB456_1.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB123_3
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_3_ortho.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
-                               -out     ${TEMP}/clLabeledImageQB123_3.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB123_3.tif
-                               ${TEMP}/clLabeledImageQB123_3.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_3_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
+    -out     ${TEMP}/clLabeledImageQB123_3.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB123_3.tif
+    ${TEMP}/clLabeledImageQB123_3.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB123_2
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_2_ortho.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
-                               -out     ${TEMP}/clLabeledImageQB123_2.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB123_2.tif
-                               ${TEMP}/clLabeledImageQB123_2.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_2_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
+    -out     ${TEMP}/clLabeledImageQB123_2.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB123_2.tif
+    ${TEMP}/clLabeledImageQB123_2.tif)
 
   otb_test_application(NAME apTvClImageSVMClassifierQB123_1
-                       APP  ImageClassifier
-                       OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
-                               -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
-                               -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
-                               -out     ${TEMP}/clLabeledImageQB123_1.tif
-                       VALID   --compare-image ${NOTOL}
-                               ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
-                               ${TEMP}/clLabeledImageQB123_1.tif)
+    APP  ImageClassifier
+    OPTIONS -in      ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -imstat  ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
+    -model   ${INPUTDATA}/Classification/clsvmModelQB123.svm
+    -out     ${TEMP}/clLabeledImageQB123_1.tif
+    VALID   --compare-image ${NOTOL}
+    ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
+    ${TEMP}/clLabeledImageQB123_1.tif)
 
 endif()
 
 #----------- TrainOGRLayersClassifier TESTS ----------------
 otb_test_application(NAME apTvClTrainOGRLayersClassifier
-                     APP  TrainOGRLayersClassifier
-                     OPTIONS -inshp ${INPUTDATA}/Classification/apTvClLabeledVector.shp
-                             -feat meanB0 meanB1 meanB2 meanB3 varB0 varB1 varB2 varB3
-                             -instats ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml
-                             -outsvm ${TEMP}/apTvClModel.svm
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/apTvClModel.svm
-                             ${TEMP}/apTvClModel.svm)
+  APP  TrainOGRLayersClassifier
+  OPTIONS -inshp ${INPUTDATA}/Classification/apTvClLabeledVector.shp
+  -feat meanB0 meanB1 meanB2 meanB3 varB0 varB1 varB2 varB3
+  -instats ${TEMP}/apTvClComputeOGRLayersFeaturesStatistics.xml
+  -outsvm ${TEMP}/apTvClModel.svm
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvClModel.svm
+  ${TEMP}/apTvClModel.svm)
 
 set_tests_properties(apTvClTrainOGRLayersClassifier PROPERTIES DEPENDS apTvClComputeOGRLayersFeaturesStatistics)
 
 #----------- ComputeConfusionMatrix TESTS ----------------
 otb_test_application(NAME apTvComputeConfusionMatrixV
-                     APP  ComputeConfusionMatrix
-                     OPTIONS -in ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
-                             -ref vector
-                             -ref.vector.in ${INPUTDATA}/Classification/VectorData_QB1_ter.shp
-                             -ref.vector.field Class
-                             -out ${TEMP}/apTvComputeConfusionMatrixTconfusionVOut.csv
-                     VALID   --compare-ascii ${NOTOL}
-                     ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixTconfusionVOut.csv
-                     ${TEMP}/apTvComputeConfusionMatrixTconfusionVOut.csv)
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
+  -ref vector
+  -ref.vector.in ${INPUTDATA}/Classification/VectorData_QB1_ter.shp
+  -ref.vector.field Class
+  -out ${TEMP}/apTvComputeConfusionMatrixTconfusionVOut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixTconfusionVOut.csv
+  ${TEMP}/apTvComputeConfusionMatrixTconfusionVOut.csv)
 
 otb_test_application(NAME apTvComputeConfusionMatrixExtraReferenceLabelsV
-                     APP  ComputeConfusionMatrix
-                     OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho_C7.tif
-                             -ref vector
-                             -ref.vector.in ${INPUTDATA}/Classification/VectorData_QB1_ter.shp
-                             -ref.vector.field Class
-                             -out ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv
-                     VALID   --compare-ascii ${NOTOL}
-                     ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv
-                     ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv)
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho_C7.tif
+  -ref vector
+  -ref.vector.in ${INPUTDATA}/Classification/VectorData_QB1_ter.shp
+  -ref.vector.field Class
+  -out ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv
+  ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsVOut.csv)
 
 otb_test_application(NAME apTvComputeConfusionMatrixExtraReferenceLabelsR
-                     APP  ComputeConfusionMatrix
-                     OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho_C7.tif
-                             -ref raster
-                             -ref.raster.in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
-                             -nodatalabel 255
-                             -out ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv
-                     VALID   --compare-ascii ${NOTOL}
-                     ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv
-                     ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv)
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho_C7.tif
+  -ref raster
+  -ref.raster.in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
+  -nodatalabel 255
+  -out ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv
+  ${TEMP}/apTvComputeConfusionMatrixExtraRefLabelsROut.csv)
 
 otb_test_application(NAME apTvComputeConfusionMatrixR
-                     APP  ComputeConfusionMatrix
-                     OPTIONS -in ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
-                             -ref raster
-                             -ref.raster.in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
-                             -nodatalabel 255
-                             -out ${TEMP}/apTvComputeConfusionMatrixTconfusionROut.csv
-                     VALID   --compare-ascii ${NOTOL}
-                     ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixTconfusionROut.csv
-                     ${TEMP}/apTvComputeConfusionMatrixTconfusionROut.csv)
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
+  -ref raster
+  -ref.raster.in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
+  -nodatalabel 255
+  -out ${TEMP}/apTvComputeConfusionMatrixTconfusionROut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixTconfusionROut.csv
+  ${TEMP}/apTvComputeConfusionMatrixTconfusionROut.csv)
 
 otb_test_application(NAME apTvComputeConfusionMatrixExtraProducedLabelsR
-                     APP  ComputeConfusionMatrix
-                     OPTIONS -in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
-                             -ref raster
-                             -ref.raster.in ${INPUTDATA}/Classification/QB_1_ortho_C8.tif
-                             -nodatalabel 255
-                             -out ${TEMP}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv
-                     VALID   --compare-ascii ${NOTOL}
-                     ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv
-                     ${TEMP}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv)
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
+  -ref raster
+  -ref.raster.in ${INPUTDATA}/Classification/QB_1_ortho_C8.tif
+  -nodatalabel 255
+  -out ${TEMP}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv
+  ${TEMP}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv)
 
 
 #----------- FusionOfClassifications TESTS ----------------
 otb_test_application(NAME apTvFusionOfClassificationsDSPrecision6Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
-                             -method dempstershafer
-                             -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
-                             -method.dempstershafer.mob precision
-                             -nodatalabel 10
-                             -undecidedlabel 7
-                             -out ${TEMP}/apTvFusionOfClassificationsDS6InputsPrecisionOutput.tif uint8
-                     VALID   --compare-image ${NOTOL}
-                     ${BASELINE}/QB_1_ortho_DS_FUSED_PRECISION.tif
-                     ${TEMP}/apTvFusionOfClassificationsDS6InputsPrecisionOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
+  -method dempstershafer
+  -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
+  -method.dempstershafer.mob precision
+  -nodatalabel 10
+  -undecidedlabel 7
+  -out ${TEMP}/apTvFusionOfClassificationsDS6InputsPrecisionOutput.tif uint8
+  VALID   --compare-image ${NOTOL}
+  ${BASELINE}/QB_1_ortho_DS_FUSED_PRECISION.tif
+  ${TEMP}/apTvFusionOfClassificationsDS6InputsPrecisionOutput.tif)
 
 otb_test_application(NAME apTvFusionOfClassificationsMV3Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il ${INPUTDATA}/Classification/clLabeledImageQB123_1.tif
-                                 ${INPUTDATA}/Classification/clLabeledImageQB456_1.tif
-                                 ${INPUTDATA}/Classification/clLabeledImageQB1.tif
-                             -method majorityvoting
-                             -undecidedlabel 100
-                             -out ${TEMP}/apTvFusionOfClassificationsMV3InputsOutput.tif uint16
-                     VALID   --compare-image ${NOTOL}
-                     ${OTBAPP_BASELINE}/apTvFusionOfClassifications3InputsOutput.tif
-                     ${TEMP}/apTvFusionOfClassificationsMV3InputsOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il ${INPUTDATA}/Classification/clLabeledImageQB123_1.tif
+  ${INPUTDATA}/Classification/clLabeledImageQB456_1.tif
+  ${INPUTDATA}/Classification/clLabeledImageQB1.tif
+  -method majorityvoting
+  -undecidedlabel 100
+  -out ${TEMP}/apTvFusionOfClassificationsMV3InputsOutput.tif uint16
+  VALID   --compare-image ${NOTOL}
+  ${OTBAPP_BASELINE}/apTvFusionOfClassifications3InputsOutput.tif
+  ${TEMP}/apTvFusionOfClassificationsMV3InputsOutput.tif)
 
 otb_test_application(NAME apTvFusionOfClassificationsDSKappa6Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
-                             -method dempstershafer
-                             -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
-                             -method.dempstershafer.mob kappa
-                             -nodatalabel 10
-                             -undecidedlabel 7
-                             -out ${TEMP}/apTvFusionOfClassificationsDS6InputsKappaOutput.tif uint8
-                     VALID   --compare-image ${NOTOL}
-                     ${BASELINE}/QB_1_ortho_DS_FUSED_KAPPA.tif
-                     ${TEMP}/apTvFusionOfClassificationsDS6InputsKappaOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
+  -method dempstershafer
+  -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
+  -method.dempstershafer.mob kappa
+  -nodatalabel 10
+  -undecidedlabel 7
+  -out ${TEMP}/apTvFusionOfClassificationsDS6InputsKappaOutput.tif uint8
+  VALID   --compare-image ${NOTOL}
+  ${BASELINE}/QB_1_ortho_DS_FUSED_KAPPA.tif
+  ${TEMP}/apTvFusionOfClassificationsDS6InputsKappaOutput.tif)
 
 otb_test_application(NAME apTvFusionOfClassificationsDSRecall6Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
-                             -method dempstershafer
-                             -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
-                             -method.dempstershafer.mob recall
-                             -nodatalabel 10
-                             -undecidedlabel 7
-                             -out ${TEMP}/apTvFusionOfClassificationsDS6InputsRecallOutput.tif uint8
-                     VALID   --compare-image ${NOTOL}
-                     ${BASELINE}/QB_1_ortho_DS_FUSED_RECALL.tif
-                     ${TEMP}/apTvFusionOfClassificationsDS6InputsRecallOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
+  -method dempstershafer
+  -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
+  -method.dempstershafer.mob recall
+  -nodatalabel 10
+  -undecidedlabel 7
+  -out ${TEMP}/apTvFusionOfClassificationsDS6InputsRecallOutput.tif uint8
+  VALID   --compare-image ${NOTOL}
+  ${BASELINE}/QB_1_ortho_DS_FUSED_RECALL.tif
+  ${TEMP}/apTvFusionOfClassificationsDS6InputsRecallOutput.tif)
 
 otb_test_application(NAME apTvFusionOfClassificationsDSAccuracy6Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
-                                  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
-                             -method dempstershafer
-                             -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
-                                                         ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
-                             -method.dempstershafer.mob accuracy
-                             -nodatalabel 10
-                             -undecidedlabel 7
-                             -out ${TEMP}/apTvFusionOfClassificationsDS6InputsAccuracyOutput.tif uint8
-                     VALID   --compare-image ${NOTOL}
-                     ${BASELINE}/QB_1_ortho_DS_FUSED_ACCURACY.tif
-                     ${TEMP}/apTvFusionOfClassificationsDS6InputsAccuracyOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il  ${INPUTDATA}/Classification/QB_1_ortho_C1.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.tif
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.tif
+  -method dempstershafer
+  -method.dempstershafer.cmfl ${INPUTDATA}/Classification/QB_1_ortho_C1.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C2.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C3.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C4.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C5.csv
+  ${INPUTDATA}/Classification/QB_1_ortho_C6.csv
+  -method.dempstershafer.mob accuracy
+  -nodatalabel 10
+  -undecidedlabel 7
+  -out ${TEMP}/apTvFusionOfClassificationsDS6InputsAccuracyOutput.tif uint8
+  VALID   --compare-image ${NOTOL}
+  ${BASELINE}/QB_1_ortho_DS_FUSED_ACCURACY.tif
+  ${TEMP}/apTvFusionOfClassificationsDS6InputsAccuracyOutput.tif)
 
 otb_test_application(NAME apTvFusionOfClassificationsMV2Inputs
-                     APP  FusionOfClassifications
-                     OPTIONS -il ${INPUTDATA}/Classification/clLabeledImageQB123_1.tif
-                                 ${INPUTDATA}/Classification/clLabeledImageQB456_1.tif
-                             -method majorityvoting
-                             -undecidedlabel 100
-                             -out ${TEMP}/apTvFusionOfClassificationsMV2InputsOutput.tif uint16
-                     VALID   --compare-image ${NOTOL}
-                     ${OTBAPP_BASELINE}/apTvFusionOfClassifications2InputsOutput.tif
-                     ${TEMP}/apTvFusionOfClassificationsMV2InputsOutput.tif)
+  APP  FusionOfClassifications
+  OPTIONS -il ${INPUTDATA}/Classification/clLabeledImageQB123_1.tif
+  ${INPUTDATA}/Classification/clLabeledImageQB456_1.tif
+  -method majorityvoting
+  -undecidedlabel 100
+  -out ${TEMP}/apTvFusionOfClassificationsMV2InputsOutput.tif uint16
+  VALID   --compare-image ${NOTOL}
+  ${OTBAPP_BASELINE}/apTvFusionOfClassifications2InputsOutput.tif
+  ${TEMP}/apTvFusionOfClassificationsMV2InputsOutput.tif)
 
 
 #----------- ComputePolylineFeatureFromImage TESTS ----------------
 otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
-                             -vd LARGEINPUT{DEMPSTER-SHAFER/wrongroads.shp}
-                             -expr  "(b1 > 0.4)"
-                             -field "NONDVI"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr.shp)
+  APP  ComputePolylineFeatureFromImage
+  OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
+  -vd LARGEINPUT{DEMPSTER-SHAFER/wrongroads.shp}
+  -expr  "(b1 > 0.4)"
+  -field "NONDVI"
+  -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr.shp)
 
 # FIXME cascade dependencies to largeinput repository
 if(OTB_DATA_USE_LARGEINPUT)
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication.shp
-                             -expr  "(b1 > 0.25)"
-                             -field "ROADSA"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr.shp
-                             -expr  "(b1 > 0.25)"
-                             -field "ROADSA"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr.shp
-                             -expr  "(b1 == 0)"
-                             -field "NOBUIL"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
-                     VALID   --compare-ogr 0.0
-                             ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
-                             ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
-                             -vd LARGEINPUT{DEMPSTER-SHAFER/road_extraction.shp}
-                             -expr  "(b1 > 0.4)"
-                             -field "NONDVI"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication.shp)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_gt
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
-                             -vd LARGEINPUT{DEMPSTER-SHAFER/roads_ground_truth.shp}
-                             -expr  "(b1 > 0.4)"
-                             -field NONDVI
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage.shp)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage.shp
-                             -expr  "(b1 > 0.25)"
-                             -field "ROADSA"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_gt)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt.shp
-                             -expr  "(b1 == 0)"
-                             -field "NOBUIL"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
-                     VALID   --compare-ogr 0.0
-                             ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
-                             ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt)
-
-otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication
-                     APP  ComputePolylineFeatureFromImage
-                     OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
-                             -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication.shp
-                             -expr  "(b1 == 0)"
-                             -field "NOBUIL"
-                             -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
-                     VALID   --compare-ogr 0.0
-                             ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
-                             ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp)
-
-set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication)
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication.shp
+    -expr  "(b1 > 0.25)"
+    -field "ROADSA"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr.shp
+    -expr  "(b1 > 0.25)"
+    -field "ROADSA"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_wr)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr.shp
+    -expr  "(b1 == 0)"
+    -field "NOBUIL"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
+    VALID   --compare-ogr 0.0
+    ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
+    ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_wr PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_wr)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
+    -vd LARGEINPUT{DEMPSTER-SHAFER/road_extraction.shp}
+    -expr  "(b1 > 0.4)"
+    -field "NONDVI"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_RoadExtractionApplication.shp)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_gt
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/NDVI.TIF}
+    -vd LARGEINPUT{DEMPSTER-SHAFER/roads_ground_truth.shp}
+    -expr  "(b1 > 0.4)"
+    -field NONDVI
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage.shp)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/SpectralAngleRoad.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage.shp
+    -expr  "(b1 > 0.25)"
+    -field "ROADSA"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_NONDVI_gt)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt.shp
+    -expr  "(b1 == 0)"
+    -field "NOBUIL"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
+    VALID   --compare-ogr 0.0
+    ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
+    ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_gt PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_gt)
+
+  otb_test_application(NAME apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication
+    APP  ComputePolylineFeatureFromImage
+    OPTIONS -in LARGEINPUT{DEMPSTER-SHAFER/BUILDINGS.TIF}
+    -vd ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication.shp
+    -expr  "(b1 == 0)"
+    -field "NOBUIL"
+    -out ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
+    VALID   --compare-ogr 0.0
+    ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
+    ${TEMP}/apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp)
+
+  set_tests_properties(apTvCdbComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication PROPERTIES DEPENDS apTvCdbComputePolylineFeatureFromImage_LI_ROADSA_RoadExtractionApplication)
 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 )
+  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 )
 
 
 #----------- TrainImagesClassifier TESTS ----------------
 if(OTB_USE_LIBSVM)
-otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_allOpt_InXML
-                      APP  TrainImagesClassifier
-                      OPTIONS
-                              -inxml ${INPUTDATA}/clsvmModelQB1_OutXML.xml
-                              -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                              -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
-                              -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -sample.mv 100
-                              -sample.mt 100
-                              -sample.vtr 0.5
-                              -sample.vfn Class
-                              -classifier.libsvm.opt true
-                              -rand 121212
-                              -io.out ${TEMP}/clsvmModelQB1_allOpt_InXML.svm
-                      VALID   ${ascii_comparison}
-                              ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
-                              ${TEMP}/clsvmModelQB1_allOpt_InXML.svm)
-
-otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_OutXML
-                      APP  TrainImagesClassifier
-                      OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                              -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
-                              -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -sample.vfn Class
-                              -classifier libsvm
-                              -classifier.libsvm.opt true
-                              -io.out ${TEMP}/clsvmModelQB1_OutXML.svm
-                              -rand 121212
-                              -outxml ${TEMP}/clsvmModelQB1_OutXML.xml
-                      VALID   ${ascii_comparison}
-                              ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
-                              ${TEMP}/clsvmModelQB1_OutXML.svm)
+  otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_allOpt_InXML
+    APP  TrainImagesClassifier
+    OPTIONS
+    -inxml ${INPUTDATA}/clsvmModelQB1_OutXML.xml
+    -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -sample.mv 100
+    -sample.mt 100
+    -sample.vtr 0.5
+    -sample.vfn Class
+    -classifier.libsvm.opt true
+    -rand 121212
+    -io.out ${TEMP}/clsvmModelQB1_allOpt_InXML.svm
+    VALID   ${ascii_comparison}
+    ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
+    ${TEMP}/clsvmModelQB1_allOpt_InXML.svm)
+
+  otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_OutXML
+    APP  TrainImagesClassifier
+    OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -sample.vfn Class
+    -classifier libsvm
+    -classifier.libsvm.opt true
+    -io.out ${TEMP}/clsvmModelQB1_OutXML.svm
+    -rand 121212
+    -outxml ${TEMP}/clsvmModelQB1_OutXML.xml
+    VALID   ${ascii_comparison}
+    ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
+    ${TEMP}/clsvmModelQB1_OutXML.svm)
 
   otb_test_application(NAME apTvClTrainSVMImagesClassifierQB123
-                       APP  TrainImagesClassifier
-                       OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                                      ${INPUTDATA}/Classification/QB_2_ortho.tif
-                                      ${INPUTDATA}/Classification/QB_3_ortho.tif
-                               -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
-                                      ${INPUTDATA}/Classification/VectorData_QB2.shp
-                                      ${INPUTDATA}/Classification/VectorData_QB3.shp
-                               -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
-                               -sample.vfn Class
-                               -classifier libsvm
-                               -classifier.libsvm.opt true
-                               -io.out ${TEMP}/clsvmModelQB123.svm
-                               -rand 121212)
-# RK: 09/2016. same reason explained for leTvSVMImageModelEstimatorTrainWithOptimization test
-#                       VALID   --compare-ascii ${NOTOL}
-#                               ${OTBAPP_BASELINE_FILES}/clsvmModelQB123.svm
-#                               ${TEMP}/clsvmModelQB123.svm
-
-otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1
-                      APP  TrainImagesClassifier
-                      OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                              -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
-                              -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -classifier libsvm
-                              -classifier.libsvm.opt true
-                              -sample.vfn Class
-                              -io.out ${TEMP}/clsvmModelQB1.svm
-                              -rand 121212
-                      VALID   ${ascii_comparison}
-                              ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
-                              ${TEMP}/clsvmModelQB1.svm)
+    APP  TrainImagesClassifier
+    OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
+    ${INPUTDATA}/Classification/QB_2_ortho.tif
+    ${INPUTDATA}/Classification/QB_3_ortho.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
+    ${INPUTDATA}/Classification/VectorData_QB2.shp
+    ${INPUTDATA}/Classification/VectorData_QB3.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB123.xml
+    -sample.vfn Class
+    -classifier libsvm
+    -classifier.libsvm.opt true
+    -io.out ${TEMP}/clsvmModelQB123.svm
+    -rand 121212)
+  # RK: 09/2016. same reason explained for leTvSVMImageModelEstimatorTrainWithOptimization test
+  #                       VALID   --compare-ascii ${NOTOL}
+  #                               ${OTBAPP_BASELINE_FILES}/clsvmModelQB123.svm
+  #                               ${TEMP}/clsvmModelQB123.svm
+
+  otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1
+    APP  TrainImagesClassifier
+    OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -classifier libsvm
+    -classifier.libsvm.opt true
+    -sample.vfn Class
+    -io.out ${TEMP}/clsvmModelQB1.svm
+    -rand 121212
+    VALID   ${ascii_comparison}
+    ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
+    ${TEMP}/clsvmModelQB1.svm)
 
   otb_test_application(NAME apTvClTrainSVMImagesClassifierQB456
-                       APP  TrainImagesClassifier
-                       OPTIONS -io.il ${INPUTDATA}/Classification/QB_4_extract.tif
-                                      ${INPUTDATA}/Classification/QB_5_extract.tif
-                                      ${INPUTDATA}/Classification/QB_6_extract.tif
-                               -io.vd ${INPUTDATA}/Classification/VectorData_QB4.shp
-                                      ${INPUTDATA}/Classification/VectorData_QB5.shp
-                                      ${INPUTDATA}/Classification/VectorData_QB6.shp
-                               -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
-                               -sample.vfn Class
-                               -classifier libsvm
-                               -classifier.libsvm.opt true
-                               -io.out ${TEMP}/clsvmModelQB456.svm
-                               -rand 121212)
-
-# RK: 09/2016. same reason explained for leTvSVMImageModelEstimatorTrainWithOptimization test
-#                       VALID   --compare-ascii ${NOTOL}
-#                               ${OTBAPP_BASELINE_FILES}/clsvmModelQB456.svm
-#                               ${TEMP}/clsvmModelQB456.svm
-
-otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_allOpt
-                      APP  TrainImagesClassifier
-                      OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                              -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
-                              -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
-                              -classifier libsvm
-                              -sample.mv 100
-                              -sample.mt 100
-                              -sample.vtr 0.5
-                              -sample.vfn Class
-                              -classifier.libsvm.opt true
-                              -rand 121212
-                              -io.out ${TEMP}/clsvmModelQB1_allOpt.svm
-                      VALID   ${ascii_comparison}
-                              ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
-                              ${TEMP}/clsvmModelQB1_allOpt.svm)
+    APP  TrainImagesClassifier
+    OPTIONS -io.il ${INPUTDATA}/Classification/QB_4_extract.tif
+    ${INPUTDATA}/Classification/QB_5_extract.tif
+    ${INPUTDATA}/Classification/QB_6_extract.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB4.shp
+    ${INPUTDATA}/Classification/VectorData_QB5.shp
+    ${INPUTDATA}/Classification/VectorData_QB6.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB456.xml
+    -sample.vfn Class
+    -classifier libsvm
+    -classifier.libsvm.opt true
+    -io.out ${TEMP}/clsvmModelQB456.svm
+    -rand 121212)
+
+  # RK: 09/2016. same reason explained for leTvSVMImageModelEstimatorTrainWithOptimization test
+  #                       VALID   --compare-ascii ${NOTOL}
+  #                               ${OTBAPP_BASELINE_FILES}/clsvmModelQB456.svm
+  #                               ${TEMP}/clsvmModelQB456.svm
+
+  otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_allOpt
+    APP  TrainImagesClassifier
+    OPTIONS -io.il ${INPUTDATA}/Classification/QB_1_ortho.tif
+    -io.vd ${INPUTDATA}/Classification/VectorData_QB1.shp
+    -io.imstat ${INPUTDATA}/Classification/clImageStatisticsQB1.xml
+    -classifier libsvm
+    -sample.mv 100
+    -sample.mt 100
+    -sample.vtr 0.5
+    -sample.vfn Class
+    -classifier.libsvm.opt true
+    -rand 121212
+    -io.out ${TEMP}/clsvmModelQB1_allOpt.svm
+    VALID   ${ascii_comparison}
+    ${OTBAPP_BASELINE_FILES}/clsvmModelQB1.svm
+    ${TEMP}/clsvmModelQB1_allOpt.svm)
 endif()
 
 #----------- DSFuzzyModelEstimation TESTS ----------------
 otb_test_application(NAME apTvCdbDSFuzzyModelEstimation_LI_autoInit
-                     APP  DSFuzzyModelEstimation
-                     OPTIONS -psin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
-                             -nsin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
-                             -belsup "ROADSA"
-                             -plasup "NONDVI" "ROADSA" "NOBUIL"
-                             -desclist "NONDVI" "ROADSA" "NOBUIL"
-                             -maxnbit 4
-                             -optobs true
-                             -out ${TEMP}/apTvCdbDSFuzzyModelEstimatorOutput_LI_autoInit.xml)
+  APP  DSFuzzyModelEstimation
+  OPTIONS -psin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
+  -nsin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
+  -belsup "ROADSA"
+  -plasup "NONDVI" "ROADSA" "NOBUIL"
+  -desclist "NONDVI" "ROADSA" "NOBUIL"
+  -maxnbit 4
+  -optobs true
+  -out ${TEMP}/apTvCdbDSFuzzyModelEstimatorOutput_LI_autoInit.xml)
 
 otb_test_application(NAME apTvCdbDSFuzzyModelEstimation_LI
-                     APP  DSFuzzyModelEstimation
-                     OPTIONS -psin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
-                             -nsin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
-                             -belsup "ROADSA"
-                             -plasup "NONDVI" "ROADSA" "NOBUIL"
-                             -initmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel_Init.xml
-                             -maxnbit 4
-                             -optobs true
-                             -out ${TEMP}/apTvCdbDSFuzzyModelEstimatorOutput_LI.xml)
+  APP  DSFuzzyModelEstimation
+  OPTIONS -psin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
+  -nsin ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
+  -belsup "ROADSA"
+  -plasup "NONDVI" "ROADSA" "NOBUIL"
+  -initmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel_Init.xml
+  -maxnbit 4
+  -optobs true
+  -out ${TEMP}/apTvCdbDSFuzzyModelEstimatorOutput_LI.xml)
 
 
 #----------- ClassificationMapRegularization TESTS ----------------
 otb_test_application(NAME apTvClassificationMapRegularization
-                     APP  ClassificationMapRegularization
-                     OPTIONS -io.in  ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
-                             -io.out ${TEMP}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif
-                             -ip.radius 2
-                             -ip.suvbool true
-                             -ip.nodatalabel 10
-                             -ip.undecidedlabel 7
-                     VALID   --compare-image ${NOTOL}
-                             ${OTBAPP_BASELINE}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif
-                             ${TEMP}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif)
+  APP  ClassificationMapRegularization
+  OPTIONS -io.in  ${OTBAPP_BASELINE}/clLabeledImageQB123_1.tif
+  -io.out ${TEMP}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif
+  -ip.radius 2
+  -ip.suvbool true
+  -ip.nodatalabel 10
+  -ip.undecidedlabel 7
+  VALID   --compare-image ${NOTOL}
+  ${OTBAPP_BASELINE}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif
+  ${TEMP}/clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif)
 
 
 #----------- OGRLayerClassifier TESTS ----------------
 
 #----------- ComputeImagesStatistics TESTS ----------------
 otb_test_application(NAME apTvClComputeImagesStatisticsQB1
-                     APP  ComputeImagesStatistics
-                     OPTIONS -il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                             -out ${TEMP}/apTvClEstimateImageStatisticsQB1.xml
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB1.xml
-                             ${TEMP}/apTvClEstimateImageStatisticsQB1.xml)
+  APP  ComputeImagesStatistics
+  OPTIONS -il ${INPUTDATA}/Classification/QB_1_ortho.tif
+  -out ${TEMP}/apTvClEstimateImageStatisticsQB1.xml
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB1.xml
+  ${TEMP}/apTvClEstimateImageStatisticsQB1.xml)
 
 otb_test_application(NAME apTvClComputeImagesStatisticsQB456
-                     APP  ComputeImagesStatistics
-                     OPTIONS -il ${INPUTDATA}/Classification/QB_4_extract.tif
-                                 ${INPUTDATA}/Classification/QB_5_extract.tif
-                                 ${INPUTDATA}/Classification/QB_6_extract.tif
-                             -out ${TEMP}/apTvClEstimateImageStatisticsQB456.xml
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB456.xml
-                             ${TEMP}/apTvClEstimateImageStatisticsQB456.xml)
+  APP  ComputeImagesStatistics
+  OPTIONS -il ${INPUTDATA}/Classification/QB_4_extract.tif
+  ${INPUTDATA}/Classification/QB_5_extract.tif
+  ${INPUTDATA}/Classification/QB_6_extract.tif
+  -out ${TEMP}/apTvClEstimateImageStatisticsQB456.xml
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB456.xml
+  ${TEMP}/apTvClEstimateImageStatisticsQB456.xml)
 
 otb_test_application(NAME apTvClComputeImagesStatisticsQB123
-                     APP  ComputeImagesStatistics
-                     OPTIONS -il ${INPUTDATA}/Classification/QB_1_ortho.tif
-                                 ${INPUTDATA}/Classification/QB_2_ortho.tif
-                                 ${INPUTDATA}/Classification/QB_3_ortho.tif
-                             -out ${TEMP}/apTvClEstimateImageStatisticsQB123.xml
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB123.xml
-                             ${TEMP}/apTvClEstimateImageStatisticsQB123.xml)
+  APP  ComputeImagesStatistics
+  OPTIONS -il ${INPUTDATA}/Classification/QB_1_ortho.tif
+  ${INPUTDATA}/Classification/QB_2_ortho.tif
+  ${INPUTDATA}/Classification/QB_3_ortho.tif
+  -out ${TEMP}/apTvClEstimateImageStatisticsQB123.xml
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/clImageStatisticsQB123.xml
+  ${TEMP}/apTvClEstimateImageStatisticsQB123.xml)
 
 
 #----------- VectorDataDSValidation TESTS ----------------
 otb_test_application(NAME cdbTvVectorDataDSValidationGroundTruth_LI
-                     APP  VectorDataDSValidation
-                     OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
-                             -belsup "ROADSA"
-                             -plasup "NONDVI" "ROADSA" "NOBUIL"
-                             -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
-                             -out ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp
-                    VALID    --compare-ogr 0.0
-                             ${OTBAPP_BASELINE_FILES}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp
-                             ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp)
+  APP  VectorDataDSValidation
+  OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_gt.shp
+  -belsup "ROADSA"
+  -plasup "NONDVI" "ROADSA" "NOBUIL"
+  -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
+  -out ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp
+  VALID    --compare-ogr 0.0
+  ${OTBAPP_BASELINE_FILES}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp
+  ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_gt.shp)
 
 otb_test_application(NAME apTvCdbVectorDataDSValidationRoadExtractionApplication_LI
-                     APP  VectorDataDSValidation
-                     OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
-                             -belsup "ROADSA"
-                             -plasup "NONDVI" "ROADSA" "NOBUIL"
-                             -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
-                             -out ${TEMP}/apTvCdbVectorDataDSValidationOutpout_LI_RoadExtractionApplication.shp)
+  APP  VectorDataDSValidation
+  OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_RoadExtractionApplication.shp
+  -belsup "ROADSA"
+  -plasup "NONDVI" "ROADSA" "NOBUIL"
+  -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
+  -out ${TEMP}/apTvCdbVectorDataDSValidationOutpout_LI_RoadExtractionApplication.shp)
 
 otb_test_application(NAME cdbTvVectorDataDSValidationWrongRoads_LI
-                     APP  VectorDataDSValidation
-                     OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
-                             -belsup "ROADSA"
-                             -plasup "NONDVI" "ROADSA" "NOBUIL"
-                             -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
-                             -out ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp
-                    VALID    --compare-ogr 0.0
-                             ${OTBAPP_BASELINE_FILES}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp
-                             ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp)
+  APP  VectorDataDSValidation
+  OPTIONS -in ${OTBAPP_BASELINE_FILES}/cdbTvComputePolylineFeatureFromImage_LI_NOBUIL_wr.shp
+  -belsup "ROADSA"
+  -plasup "NONDVI" "ROADSA" "NOBUIL"
+  -descmod ${OTB_DATA_ROOT}/Input/Dempster-Shafer/DSFuzzyModel.xml
+  -out ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp
+  VALID    --compare-ogr 0.0
+  ${OTBAPP_BASELINE_FILES}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp
+  ${TEMP}/cdbTvVectorDataDSValidationOutpout_LI_wr.shp)
 
 if(OTB_USE_OPENCV)
-#----------- TrainRegression TESTS ----------------
-# y = 0.01*x^2 + 1.5*x - 300
-otb_test_application(NAME apTvClTrainRegressionTest_monovar
-                     APP  TrainRegression
-                     OPTIONS -io.il ${INPUTDATA}/QB_Toulouse_Ortho_regression.tif
-                             -io.imstat ${INPUTDATA}/QB_Toulouse_Ortho_regression.xml
-                             -io.out ${TEMP}/apTvClTrainRegressionTest_monovar.rf
-                             -sample.mt 20000
-                             -sample.mv 20000
-                             -sample.vtr 0.5
-                             -rand 121212
-                             -classifier rf
-                             -classifier.rf.ra 0.0001
-                             -classifier.rf.max 6
-                             -classifier.rf.acc 0.0005
-                    VALID    ${ascii_comparison}
-                             ${OTBAPP_BASELINE_FILES}/apTvClTrainRegressionTest_monovar.rf
-                             ${TEMP}/apTvClTrainRegressionTest_monovar.rf)
-
-#----------- PredictRegression TESTS ----------------
-otb_test_application(NAME apTvClPredictRegressionTest_monovar
-                     APP PredictRegression
-                     OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif
-                             -model ${OTBAPP_BASELINE_FILES}/apTvClTrainRegressionTest_monovar.rf
-                             -imstat ${INPUTDATA}/QB_Toulouse_Ortho_regression.xml
-                             -out ${TEMP}/apTvClPredictRegressionTest_monovar.tif
-                    VALID    --compare-image 1
-                             ${OTBAPP_BASELINE}/apTvClPredictRegressionTest_monovar.tif
-                             ${TEMP}/apTvClPredictRegressionTest_monovar.tif)
+  #----------- TrainRegression TESTS ----------------
+  # y = 0.01*x^2 + 1.5*x - 300
+  otb_test_application(NAME apTvClTrainRegressionTest_monovar
+    APP  TrainRegression
+    OPTIONS -io.il ${INPUTDATA}/QB_Toulouse_Ortho_regression.tif
+    -io.imstat ${INPUTDATA}/QB_Toulouse_Ortho_regression.xml
+    -io.out ${TEMP}/apTvClTrainRegressionTest_monovar.rf
+    -sample.mt 20000
+    -sample.mv 20000
+    -sample.vtr 0.5
+    -rand 121212
+    -classifier rf
+    -classifier.rf.ra 0.0001
+    -classifier.rf.max 6
+    -classifier.rf.acc 0.0005
+    VALID    ${ascii_comparison}
+    ${OTBAPP_BASELINE_FILES}/apTvClTrainRegressionTest_monovar.rf
+    ${TEMP}/apTvClTrainRegressionTest_monovar.rf)
+
+  #----------- PredictRegression TESTS ----------------
+  otb_test_application(NAME apTvClPredictRegressionTest_monovar
+    APP PredictRegression
+    OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif
+    -model ${OTBAPP_BASELINE_FILES}/apTvClTrainRegressionTest_monovar.rf
+    -imstat ${INPUTDATA}/QB_Toulouse_Ortho_regression.xml
+    -out ${TEMP}/apTvClPredictRegressionTest_monovar.tif
+    VALID    --compare-image 1
+    ${OTBAPP_BASELINE}/apTvClPredictRegressionTest_monovar.tif
+    ${TEMP}/apTvClPredictRegressionTest_monovar.tif)
 
 endif()
 
 #----------- PolygonClassStatistics TESTS ----------------
 otb_test_application(NAME apTvClPolygonClassStatisticsTest
-                     APP PolygonClassStatistics
-                     OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
-                             -vec ${INPUTDATA}/Classification/VectorData_QB1.shp
-                             -field Class
-                             -out ${TEMP}/apTvClPolygonClassStatisticsOut.xml
-                     VALID   --compare-ascii ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/apTvClPolygonClassStatisticsOut.xml
-                             ${TEMP}/apTvClPolygonClassStatisticsOut.xml)
+  APP PolygonClassStatistics
+  OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
+  -vec ${INPUTDATA}/Classification/VectorData_QB1.shp
+  -field Class
+  -out ${TEMP}/apTvClPolygonClassStatisticsOut.xml
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvClPolygonClassStatisticsOut.xml
+  ${TEMP}/apTvClPolygonClassStatisticsOut.xml)
 
 #----------- SampleSelection TESTS -----------------------
 otb_test_application(NAME apTvClSampleSelection
-                     APP SampleSelection
-                     OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
-                             -vec ${INPUTDATA}/Classification/VectorData_QB1.shp
-                             -field Class
-                             -out ${TEMP}/apTvClSampleSelectionOut.sqlite
-                             -instats ${OTBAPP_BASELINE_FILES}/apTvClPolygonClassStatisticsOut.xml
-                             -outrates ${TEMP}/apTvClSampleSelectionOutRates.txt
-                             -strategy byclass
-                             -strategy.byclass.in ${INPUTDATA}/Classification/sampling_required.csv
-                             -sampler periodic
-                     VALID   --compare-ogr ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/apTvClSampleSelectionOut.sqlite
-                             ${TEMP}/apTvClSampleSelectionOut.sqlite)
+  APP SampleSelection
+  OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
+  -vec ${INPUTDATA}/Classification/VectorData_QB1.shp
+  -field Class
+  -out ${TEMP}/apTvClSampleSelectionOut.sqlite
+  -instats ${OTBAPP_BASELINE_FILES}/apTvClPolygonClassStatisticsOut.xml
+  -outrates ${TEMP}/apTvClSampleSelectionOutRates.txt
+  -strategy byclass
+  -strategy.byclass.in ${INPUTDATA}/Classification/sampling_required.csv
+  -sampler periodic
+  VALID   --compare-ogr ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvClSampleSelectionOut.sqlite
+  ${TEMP}/apTvClSampleSelectionOut.sqlite)
 
 #----------- SampleExtraction TESTS -----------------------
 otb_test_application(NAME apTvClSampleExtraction
-                     APP SampleExtraction
-                     OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
-                             -vec ${INPUTDATA}/Classification/apTvClSampleSelectionOut.sqlite
-                             -field class
-                             -out ${TEMP}/apTvClSampleExtractionOut.sqlite
-                     VALID   --compare-ogr ${NOTOL}
-                             ${OTBAPP_BASELINE_FILES}/apTvClSampleExtractionOut.sqlite
-                             ${TEMP}/apTvClSampleExtractionOut.sqlite)
+  APP SampleExtraction
+  OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
+  -vec ${INPUTDATA}/Classification/apTvClSampleSelectionOut.sqlite
+  -field class
+  -out ${TEMP}/apTvClSampleExtractionOut.sqlite
+  VALID   --compare-ogr ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvClSampleExtractionOut.sqlite
+  ${TEMP}/apTvClSampleExtractionOut.sqlite)
 
 #----------- TrainVectorClassifier TESTS ----------------
 if(OTB_USE_OPENCV)
@@ -953,6 +957,24 @@ if(OTB_USE_OPENCV)
     ${TEMP}/apTvClTrainVectorClassifierModel.rf)
 endif()
 
+#----------- TrainVectorClassifier unsupervised TESTS ----------------
+if(OTB_USE_SHARK)
+  otb_test_application(NAME apTvClTrainVectorUnsupervised
+    APP  TrainVectorClassifier
+    OPTIONS -io.vd ${INPUTDATA}/Classification/apTvClSampleExtractionOut.sqlite
+    -feat value_0 value_1 value_2 value_3
+    -classifier sharkkm
+    -io.out ${TEMP}/apTvClTrainVectorClusteringModel.txt)
+
+  otb_test_application(NAME apTvClTrainVectorUnsupervisedWithClass
+    APP  TrainVectorClassifier
+    OPTIONS -io.vd ${INPUTDATA}/Classification/apTvClSampleExtractionOut.sqlite
+    -feat value_0 value_1 value_2 value_3
+    -cfield class
+    -classifier sharkkm
+    -io.out ${TEMP}/apTvClTrainVectorClusteringModelWithClass.txt)
+endif()
+
 #------------ MultiImageSamplingRate TESTS ----------------
 otb_test_application(
   NAME apTvClMultiImageSamplingRate
@@ -971,4 +993,4 @@ otb_test_application(
   ${TEMP}/apTvClMultiImageSamplingRate_out_2.csv
   ${OTBAPP_BASELINE_FILES}/apTvClMultiImageSamplingRate_out_3.csv
   ${TEMP}/apTvClMultiImageSamplingRate_out_3.csv
-  )
+)
diff --git a/Modules/Learning/Supervised/include/otbImageClassificationFilter.h b/Modules/Learning/LearningBase/include/otbImageClassificationFilter.h
similarity index 99%
rename from Modules/Learning/Supervised/include/otbImageClassificationFilter.h
rename to Modules/Learning/LearningBase/include/otbImageClassificationFilter.h
index 35804bc535ab0ce0d2607f2cb14ec5a806a8ae42..620177e5af42420b3a58a0bced7db1abfe82b4ac 100644
--- a/Modules/Learning/Supervised/include/otbImageClassificationFilter.h
+++ b/Modules/Learning/LearningBase/include/otbImageClassificationFilter.h
@@ -37,7 +37,7 @@ namespace otb
  * \ingroup Streamed
  * \ingroup Threaded
  *
- * \ingroup OTBSupervised
+ * \ingroup OTBLearningBase
  */
 template <class TInputImage, class TOutputImage, class TMaskImage = TOutputImage>
 class ITK_EXPORT ImageClassificationFilter
@@ -90,7 +90,7 @@ public:
   itkSetMacro(BatchMode, bool);
   itkGetMacro(BatchMode, bool);
   itkBooleanMacro(BatchMode);
-  
+
   /**
    * If set, only pixels within the mask will be classified.
    * All pixels with a value greater than 0 in the mask, will be classified.
diff --git a/Modules/Learning/Supervised/include/otbImageClassificationFilter.txx b/Modules/Learning/LearningBase/include/otbImageClassificationFilter.txx
similarity index 100%
rename from Modules/Learning/Supervised/include/otbImageClassificationFilter.txx
rename to Modules/Learning/LearningBase/include/otbImageClassificationFilter.txx
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModel.h b/Modules/Learning/LearningBase/include/otbMachineLearningModel.h
similarity index 98%
rename from Modules/Learning/Supervised/include/otbMachineLearningModel.h
rename to Modules/Learning/LearningBase/include/otbMachineLearningModel.h
index a71d819eff893cee01c60d411b1116c2351aa6df..12e0e4dbf924b15a02c17a302443edb5855d9205 100644
--- a/Modules/Learning/Supervised/include/otbMachineLearningModel.h
+++ b/Modules/Learning/LearningBase/include/otbMachineLearningModel.h
@@ -60,10 +60,11 @@ namespace otb
  * \sa NormalBayesMachineLearningModel
  * \sa NeuralNetworkMachineLearningModel
  * \sa SharkRandomForestsMachineLearningModel
+ * \sa SharkKMeansMachineLearningModel
  * \sa ImageClassificationFilter
  *
  *
- * \ingroup OTBSupervised
+ * \ingroup OTBLearningBase
  */
 template <class TInputValue, class TTargetValue, class TConfidenceValue = double >
 class ITK_EXPORT MachineLearningModel
@@ -93,7 +94,7 @@ public:
   //@}
 
   /**\name Confidence value typedef */
-  typedef TConfidenceValue                              ConfidenceValueType;
+  typedef TConfidenceValue                                  ConfidenceValueType;
   typedef itk::FixedArray<ConfidenceValueType,1>            ConfidenceSampleType;
   typedef itk::Statistics::ListSample<ConfidenceSampleType> ConfidenceListSampleType;
 
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModel.txx b/Modules/Learning/LearningBase/include/otbMachineLearningModel.txx
similarity index 100%
rename from Modules/Learning/Supervised/include/otbMachineLearningModel.txx
rename to Modules/Learning/LearningBase/include/otbMachineLearningModel.txx
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModelFactoryBase.h b/Modules/Learning/LearningBase/include/otbMachineLearningModelFactoryBase.h
similarity index 98%
rename from Modules/Learning/Supervised/include/otbMachineLearningModelFactoryBase.h
rename to Modules/Learning/LearningBase/include/otbMachineLearningModelFactoryBase.h
index 029b73d49787f4f339843c066c64781626ff6f25..0ca61f2c37483d0735c146f9355b4a4a8d4c5d35 100644
--- a/Modules/Learning/Supervised/include/otbMachineLearningModelFactoryBase.h
+++ b/Modules/Learning/LearningBase/include/otbMachineLearningModelFactoryBase.h
@@ -32,8 +32,7 @@ namespace otb
  * This class intends to hold the static attributes that can not be
  * part of a template class (ld error).
  *
- *
- * \ingroup OTBSupervised
+ * \ingroup OTBLearningBase
  */
 class OTBSupervised_EXPORT MachineLearningModelFactoryBase : public itk::Object
 {
diff --git a/Modules/Learning/Supervised/include/otbSharkUtils.h b/Modules/Learning/LearningBase/include/otbSharkUtils.h
similarity index 96%
rename from Modules/Learning/Supervised/include/otbSharkUtils.h
rename to Modules/Learning/LearningBase/include/otbSharkUtils.h
index 4774965cfa581ec8d733d1e04aa33857e021b159..9efcf948bdbbfd7e9a672068677f029ac10c7d39 100644
--- a/Modules/Learning/Supervised/include/otbSharkUtils.h
+++ b/Modules/Learning/LearningBase/include/otbSharkUtils.h
@@ -21,14 +21,16 @@
 #ifndef otbSharkUtils_h
 #define otbSharkUtils_h
 
-#include "otb_shark.h"
 #include "itkMacro.h"
 
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wsign-compare"
 #endif
-#include <shark/Data/Dataset.h>
+#include "otb_shark.h"
+#include "shark/Data/Dataset.h"
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
diff --git a/Modules/Learning/LearningBase/otb-module.cmake b/Modules/Learning/LearningBase/otb-module.cmake
index 6a94a9a00add529b41d2597acf248457356ea233..b4fab23bbaec200852575ff64dbb2424475542c6 100644
--- a/Modules/Learning/LearningBase/otb-module.cmake
+++ b/Modules/Learning/LearningBase/otb-module.cmake
@@ -25,6 +25,11 @@ otb_module(OTBLearningBase
   DEPENDS
     OTBCommon
     OTBITK
+    OTBImageIO
+    OTBImageBase
+
+  OPTIONAL_DEPENDS
+    OTBShark
 
   TEST_DEPENDS
     OTBTestKernel
diff --git a/Modules/Learning/Supervised/include/otbKNearestNeighborsMachineLearningModel.txx b/Modules/Learning/Supervised/include/otbKNearestNeighborsMachineLearningModel.txx
index 9ed1a25ddd076191852388ad7624036f74c77991..3b6dc52a25674819aff7ddb715e2be4b964c0c38 100644
--- a/Modules/Learning/Supervised/include/otbKNearestNeighborsMachineLearningModel.txx
+++ b/Modules/Learning/Supervised/include/otbKNearestNeighborsMachineLearningModel.txx
@@ -243,11 +243,21 @@ KNearestNeighborsMachineLearningModel<TInputValue,TTargetValue>
   //first line is the K parameter of this algorithm.
   std::string line;
   std::getline(ifs, line);
+  std::istringstream iss(line);
+  if( line.find( "K" ) == std::string::npos )
+    {
+    itkExceptionMacro( <<"Could not read file "<<filename );
+    }
   std::string::size_type pos = line.find_first_of("=", 0);
   std::string::size_type nextpos = line.find_first_of(" \n\r", pos+1);
   this->SetK(boost::lexical_cast<int>(line.substr(pos+1, nextpos-pos-1)));
+
   //second line is the IsRegression parameter
   std::getline(ifs, line);
+  if( line.find( "IsRegression" ) == std::string::npos )
+    {
+    itkExceptionMacro( <<"Could not read file "<<filename );
+    }
   pos = line.find_first_of("=", 0);
   nextpos = line.find_first_of(" \n\r", pos+1);
   this->SetRegressionMode(boost::lexical_cast<bool>(line.substr(pos+1, nextpos-pos-1)));
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx b/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
index 51d136a1ea3b8ad021c13ad04f7df1fa3cf83da0..760096feafeada28f0328d8a6fced9e3a7cf9397 100644
--- a/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
+++ b/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
@@ -43,6 +43,7 @@
 
 #ifdef OTB_USE_SHARK
 #include "otbSharkRandomForestsMachineLearningModelFactory.h"
+#include "otbSharkKMeansMachineLearningModelFactory.h"
 #endif
 
 #include "itkMutexLockHolder.h"
@@ -110,6 +111,7 @@ MachineLearningModelFactory<TInputValue,TOutputValue>
 
 #ifdef OTB_USE_SHARK
   RegisterFactory(SharkRandomForestsMachineLearningModelFactory<TInputValue,TOutputValue>::New());
+  RegisterFactory(SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>::New());
 #endif
   
 #ifdef OTB_USE_OPENCV
@@ -168,6 +170,14 @@ MachineLearningModelFactory<TInputValue,TOutputValue>
       itk::ObjectFactoryBase::UnRegisterFactory(sharkRFFactory);
       continue;
       }
+
+    SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue> *sharkKMeansFactory =
+            dynamic_cast<SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue> *>(*itFac);
+    if (sharkKMeansFactory)
+      {
+      itk::ObjectFactoryBase::UnRegisterFactory(sharkKMeansFactory);
+      continue;
+      }
 #endif
 
 #ifdef OTB_USE_OPENCV
diff --git a/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.h b/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.h
index 6efe3ecdf7d19220966904e7205115f52599382e..8d2c0c81233c5e5486ec8068f86298a2f7053ab8 100644
--- a/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.h
+++ b/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.h
@@ -21,8 +21,6 @@
 #ifndef otbSharkRandomForestsMachineLearningModel_h
 #define otbSharkRandomForestsMachineLearningModel_h
 
-#include "otb_shark.h"
-
 #include "itkLightObject.h"
 #include "otbMachineLearningModel.h"
 
@@ -36,6 +34,7 @@
 #pragma GCC diagnostic ignored "-Wcast-align"
 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
 #endif
+#include "otb_shark.h"
 #include "shark/Algorithms/Trainers/RFTrainer.h"
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
diff --git a/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.txx b/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.txx
index 6c90b26f3614398d5b567ad9be5df335722afca8..207f1abdd77e4b5cfffd9bc5d104c4b40232f853 100644
--- a/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.txx
+++ b/Modules/Learning/Supervised/include/otbSharkRandomForestsMachineLearningModel.txx
@@ -198,6 +198,8 @@ SharkRandomForestsMachineLearningModel<TInputValue,TOutputValue>
     {
     itkExceptionMacro(<< "Error opening " << filename.c_str() );
     }
+  // Add comment with model file name
+  ofs << "#" << m_RFModel.name() << std::endl;
   shark::TextOutArchive oa(ofs);
   m_RFModel.save(oa,0);
 }
@@ -208,8 +210,25 @@ SharkRandomForestsMachineLearningModel<TInputValue,TOutputValue>
 ::Load(const std::string & filename, const std::string & itkNotUsed(name))
 {
   std::ifstream ifs(filename.c_str());
-  shark::TextInArchive ia(ifs);
-  m_RFModel.load(ia,0);
+  if( ifs.good() )
+    {
+    // Check if the first line is a comment and verify the name of the model in this case.
+    std::string line;
+    getline( ifs, line );
+    if( line.at( 0 ) == '#' )
+      {
+      if( line.find( m_RFModel.name() ) == std::string::npos )
+        itkExceptionMacro( "The model file : " + filename + " cannot be read." );
+      }
+    else
+      {
+      // rewind if first line is not a comment
+      ifs.clear();
+      ifs.seekg( 0, std::ios::beg );
+      }
+    shark::TextInArchive ia( ifs );
+    m_RFModel.load( ia, 0 );
+    }
 }
 
 template <class TInputValue, class TOutputValue>
diff --git a/Modules/Learning/Supervised/otb-module.cmake b/Modules/Learning/Supervised/otb-module.cmake
index 32afd5841aeeb097608f5fc4ab3b4e772dac7c17..2c7c4ae51189889628694c0269c7a469a6d3205f 100644
--- a/Modules/Learning/Supervised/otb-module.cmake
+++ b/Modules/Learning/Supervised/otb-module.cmake
@@ -29,12 +29,14 @@ ENABLE_SHARED
     OTBCommon
     OTBITK
     OTBImageBase
+    OTBLearningBase
+    OTBUnsupervised
 
   OPTIONAL_DEPENDS
     OTBOpenCV
     OTBSVMLearning
     OTBShark
-    
+
   TEST_DEPENDS
     OTBTestKernel
     OTBImageIO
diff --git a/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx b/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
index df1eccdec10194e71256d0b5a3eb843b2f5eb7c8..49faadbebe6a450d48c1bf8c4e958f4b508fa65a 100644
--- a/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
+++ b/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
@@ -322,4 +322,5 @@ int otbSharkRFMachineLearningModelCanRead(int argc, char* argv[])
   return EXIT_SUCCESS;
 }
 
+
 #endif
diff --git a/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
index 7ba47e427e27a004f7a94b00fa4ebd311592d087..d96021c156c46b0e7d72ccac06f0d9006c0eb8b5 100644
--- a/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
+++ b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
@@ -92,7 +92,7 @@ void RegisterTests()
   REGISTER_TEST(otbSharkRFMachineLearningModelCanRead);
   REGISTER_TEST(otbSharkImageClassificationFilter);
 #endif
-  
+
   REGISTER_TEST(otbImageClassificationFilterNew);
   REGISTER_TEST(otbImageClassificationFilter);
 }
diff --git a/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx b/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
index 5fc1f79f1de5c6af752e37edad080555ae7e58e2..590fae174907817e0ed525cda4d56c1d3e78e2d0 100644
--- a/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
+++ b/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
@@ -1290,4 +1290,5 @@ int otbSharkRFMachineLearningModel(int argc, char * argv[])
    return EXIT_SUCCESS;
 }
 
+
 #endif
diff --git a/Modules/Learning/Supervised/test/tests-shark.cmake b/Modules/Learning/Supervised/test/tests-shark.cmake
index 2f7d6906c74b1be51e7f22260ef68bbb6309fd35..eeda8fff3f9f1413dcffe868c3fb5f8cb4f8f6d0 100644
--- a/Modules/Learning/Supervised/test/tests-shark.cmake
+++ b/Modules/Learning/Supervised/test/tests-shark.cmake
@@ -34,17 +34,17 @@ otb_add_test(NAME leTvSharkRFMachineLearningModelCanRead COMMAND otbSupervisedTe
 
 otb_add_test(NAME leTvSharkRFMachineLearningModelCanReadFail COMMAND otbSupervisedTestDriver
   otbSharkRFMachineLearningModelCanRead
-  ${INPUTDATA}/ROI_QB_MUL_4_svmModel.txt
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
   )
 set_property(TEST leTvSharkRFMachineLearningModelCanReadFail PROPERTY WILL_FAIL true)
 
 
 otb_add_test(NAME leTvImageClassificationFilterSharkFast COMMAND  otbSupervisedTestDriver
-  --compare-n-images ${NOTOL} 2 
+  --compare-n-images ${NOTOL} 2
   ${BASELINE}/leSharkImageClassificationFilterOutput.tif
   ${TEMP}/leSharkImageClassificationFilterOutput.tif
   ${BASELINE}/leSharkImageClassificationFilterConfidence.tif
-  ${TEMP}/leSharkImageClassificationFilterConfidence.tif   
+  ${TEMP}/leSharkImageClassificationFilterConfidence.tif
   otbSharkImageClassificationFilter
   ${INPUTDATA}/Classification/QB_1_ortho.tif
   ${TEMP}/leSharkImageClassificationFilterOutput.tif
@@ -66,11 +66,11 @@ otb_add_test(NAME leTvImageClassificationFilterSharkFast COMMAND  otbSupervisedT
 #   )
 
 otb_add_test(NAME leTvImageClassificationFilterSharkFastMask COMMAND  otbSupervisedTestDriver
-  --compare-n-images ${NOTOL} 2 
+  --compare-n-images ${NOTOL} 2
   ${BASELINE}/leSharkImageClassificationFilterWithMaskOutput.tif
   ${TEMP}/leSharkImageClassificationFilterWithMaskOutput.tif
   ${BASELINE}/leSharkImageClassificationFilterWithMaskConfidence.tif
-  ${TEMP}/leSharkImageClassificationFilterWithMaskConfidence.tif   
+  ${TEMP}/leSharkImageClassificationFilterWithMaskConfidence.tif
   otbSharkImageClassificationFilter
   ${INPUTDATA}/Classification/QB_1_ortho.tif
   ${TEMP}/leSharkImageClassificationFilterWithMaskOutput.tif
@@ -79,3 +79,4 @@ otb_add_test(NAME leTvImageClassificationFilterSharkFastMask COMMAND  otbSupervi
   ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_RFmodel.txt
   ${INPUTDATA}/Classification/QB_1_ortho_mask.tif
   )
+
diff --git a/Modules/Learning/Unsupervised/CMakeLists.txt b/Modules/Learning/Unsupervised/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e83c272cd617b09367dce57c8ed038757904d14c
--- /dev/null
+++ b/Modules/Learning/Unsupervised/CMakeLists.txt
@@ -0,0 +1,4 @@
+project(OTBUnsupervised)
+
+
+otb_module_impl()
diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..3084b2503e8680fe36b60c0cf70b979bc7656cc9
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h
@@ -0,0 +1,171 @@
+/*
+ * 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 otbSharkKMeansMachineLearningModel_h
+#define otbSharkKMeansMachineLearningModel_h
+
+#include "itkLightObject.h"
+#include "otbMachineLearningModel.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Woverloaded-virtual"
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wcast-align"
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#endif
+
+#include "otb_shark.h"
+#include "shark/Models/Clustering/HardClusteringModel.h"
+#include "shark/Models/Clustering/SoftClusteringModel.h"
+#include "shark/Models/Clustering/Centroids.h"
+#include "shark/Models/Clustering/ClusteringModel.h"
+#include "shark/Algorithms/KMeans.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+/** \class SharkKMeansMachineLearningModel
+ *  \brief Shark version of Random Forests algorithm
+ *
+ *  This is a specialization of MachineLearningModel class allowing to
+ *  use Shark implementation of the Random Forests algorithm.
+ *
+ *  It is noteworthy that training step is parallel.
+ *
+ *  For more information, see
+ *  http://image.diku.dk/shark/sphinx_pages/build/html/rest_sources/tutorials/algorithms/kmeans.html
+ *
+ *  \ingroup OTBUnsupervised
+ */
+namespace otb
+{
+template<class TInputValue, class TTargetValue>
+class ITK_EXPORT SharkKMeansMachineLearningModel : public MachineLearningModel<TInputValue, TTargetValue>
+{
+public:
+  /** Standard class typedefs. */
+  typedef SharkKMeansMachineLearningModel                 Self;
+  typedef MachineLearningModel<TInputValue, TTargetValue> Superclass;
+  typedef itk::SmartPointer<Self>                         Pointer;
+  typedef itk::SmartPointer<const Self>                   ConstPointer;
+
+  typedef typename Superclass::InputValueType             InputValueType;
+  typedef typename Superclass::InputSampleType            InputSampleType;
+  typedef typename Superclass::InputListSampleType        InputListSampleType;
+  typedef typename Superclass::TargetValueType            TargetValueType;
+  typedef typename Superclass::TargetSampleType           TargetSampleType;
+  typedef typename Superclass::TargetListSampleType       TargetListSampleType;
+  typedef typename Superclass::ConfidenceValueType        ConfidenceValueType;
+  typedef typename Superclass::ConfidenceSampleType       ConfidenceSampleType;
+  typedef typename Superclass::ConfidenceListSampleType   ConfidenceListSampleType;
+
+
+  typedef shark::HardClusteringModel<shark::RealVector>   ClusteringModelType;
+  typedef ClusteringModelType::OutputType                 ClusteringOutputType;
+
+  /** Run-time type information (and related methods). */
+  itkNewMacro( Self );
+  itkTypeMacro( SharkKMeansMachineLearningModel, MachineLearningModel );
+
+  /** Train the machine learning model */
+  virtual void Train() ITK_OVERRIDE;
+
+  /** Save the model to file */
+  virtual void Save(const std::string &filename, const std::string &name = "") ITK_OVERRIDE;
+
+  /** Load the model from file */
+  virtual void Load(const std::string &filename, const std::string &name = "") ITK_OVERRIDE;
+
+  /**\name Classification model file compatibility tests */
+  //@{
+  /** Is the input model file readable and compatible with the corresponding classifier ? */
+  virtual bool CanReadFile(const std::string &) ITK_OVERRIDE;
+
+  /** Is the input model file writable and compatible with the corresponding classifier ? */
+  virtual bool CanWriteFile(const std::string &) ITK_OVERRIDE;
+  //@}
+
+  /** Get the maximum number of iteration for the kMeans algorithm.*/
+  itkGetMacro( MaximumNumberOfIterations, unsigned );
+  /** Set the maximum number of iteration for the kMeans algorithm.*/
+  itkSetMacro( MaximumNumberOfIterations, unsigned );
+
+  /** Get the number of class for the kMeans algorithm.*/
+  itkGetMacro( K, unsigned );
+  /** Set the number of class for the kMeans algorithm.*/
+  itkSetMacro( K, unsigned );
+
+  /** If true, normalized input data sample list */
+  itkGetMacro( Normalized, bool );
+  itkSetMacro( Normalized, bool );
+
+protected:
+  /** Constructor */
+  SharkKMeansMachineLearningModel();
+
+  /** Destructor */
+  virtual ~SharkKMeansMachineLearningModel();
+
+  /** Predict values using the model */
+  virtual TargetSampleType
+  DoPredict(const InputSampleType &input, ConfidenceValueType *quality = ITK_NULLPTR) const ITK_OVERRIDE;
+
+
+  virtual void DoPredictBatch(const InputListSampleType *, const unsigned int &startIndex, const unsigned int &size,
+                              TargetListSampleType *, ConfidenceListSampleType * = ITK_NULLPTR) const ITK_OVERRIDE;
+
+  template<typename DataType>
+  DataType NormalizeData(const DataType &data) const;
+
+  /** PrintSelf method */
+  void PrintSelf(std::ostream &os, itk::Indent indent) const;
+
+private:
+  SharkKMeansMachineLearningModel(const Self &); //purposely not implemented
+  void operator=(const Self &); //purposely not implemented
+
+  // Parameters set by the user
+  bool m_Normalized;
+  unsigned int m_K;
+  unsigned int m_MaximumNumberOfIterations;
+  bool m_CanRead;
+
+
+  /** Centroids results form kMeans */
+  shark::Centroids m_Centroids;
+
+
+  /** shark Model could be SoftClusteringModel or HardClusteringModel */
+  boost::shared_ptr<ClusteringModelType> m_ClusteringModel;
+
+};
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+
+#include "otbSharkKMeansMachineLearningModel.txx"
+
+#endif
+
+#endif
diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.txx b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.txx
new file mode 100644
index 0000000000000000000000000000000000000000..267a676ae668a863928be6087146a1ca7dd61d6e
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.txx
@@ -0,0 +1,257 @@
+/*
+ * 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 otbSharkKMeansMachineLearningModel_txx
+#define otbSharkKMeansMachineLearningModel_txx
+
+#include <fstream>
+#include "itkMacro.h"
+#include "otbSharkKMeansMachineLearningModel.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Woverloaded-virtual"
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#endif
+
+#include "otb_shark.h"
+#include "otbSharkUtils.h"
+#include "shark/Algorithms/Trainers/NormalizeComponentsUnitVariance.h" //normalize
+#include "shark/Algorithms/KMeans.h" //k-means algorithm
+#include "shark/Models/Clustering/HardClusteringModel.h"
+#include "shark/Models/Clustering/SoftClusteringModel.h"
+#include "shark/Algorithms/Trainers/NormalizeComponentsUnitVariance.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+
+
+namespace otb
+{
+template<class TInputValue, class TOutputValue>
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::SharkKMeansMachineLearningModel() :
+        m_Normalized( false ), m_K(2), m_MaximumNumberOfIterations( 10 )
+{
+  // Default set HardClusteringModel
+  m_ClusteringModel = boost::shared_ptr<ClusteringModelType>(new ClusteringModelType( &m_Centroids ));
+}
+
+
+template<class TInputValue, class TOutputValue>
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::~SharkKMeansMachineLearningModel()
+{
+}
+
+/** Train the machine learning model */
+template<class TInputValue, class TOutputValue>
+void
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::Train()
+{
+  // Parse input data and convert to Shark Data
+  std::vector<shark::RealVector> vector_data;
+  otb::Shark::ListSampleToSharkVector( this->GetInputListSample(), vector_data );
+  shark::Data<shark::RealVector> data = shark::createDataFromRange( vector_data );
+
+  // Normalized input value if necessary
+  if( m_Normalized )
+    data = NormalizeData( data );
+
+  // Use a Hard Clustering Model for classification
+  shark::kMeans( data, m_K, m_Centroids, m_MaximumNumberOfIterations );
+  m_ClusteringModel = boost::shared_ptr<ClusteringModelType>(new ClusteringModelType( &m_Centroids ));
+}
+
+template<class TInputValue, class TOutputValue>
+template<typename DataType>
+DataType
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::NormalizeData(const DataType &data) const
+{
+  shark::Normalizer<> normalizer;
+  shark::NormalizeComponentsUnitVariance<> normalizingTrainer( true );//zero mean
+  normalizingTrainer.train( normalizer, data );
+  return normalizer( data );
+}
+
+template<class TInputValue, class TOutputValue>
+typename SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::TargetSampleType
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::DoPredict(const InputSampleType &value, ConfidenceValueType *quality) const
+{
+  shark::RealVector data( value.Size());
+  for( size_t i = 0; i < value.Size(); i++ )
+    {
+    data.push_back( value[i] );
+    }
+
+  // Change quality measurement only if SoftClustering or other clustering method is used.
+  if( quality != ITK_NULLPTR )
+    {
+    //unsigned int probas = (*m_ClusteringModel)( data );
+    ( *quality ) = ConfidenceValueType( 1.);
+    }
+
+  TargetSampleType target;
+  ClusteringOutputType predictedValue = (*m_ClusteringModel)( data );
+  target[0] = static_cast<TOutputValue>(predictedValue);
+  return target;
+}
+
+template<class TInputValue, class TOutputValue>
+void
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::DoPredictBatch(const InputListSampleType *input,
+                 const unsigned int &startIndex,
+                 const unsigned int &size,
+                 TargetListSampleType *targets,
+                 ConfidenceListSampleType *quality) const
+{
+
+  // Perform check on input values
+  assert( input != ITK_NULLPTR );
+  assert( targets != ITK_NULLPTR );
+
+  // input list sample and target list sample should be initialized and without
+  assert( input->Size() == targets->Size() && "Input sample list and target label list do not have the same size." );
+  assert( ( ( quality == ITK_NULLPTR ) || ( quality->Size() == input->Size() ) ) &&
+          "Quality samples list is not null and does not have the same size as input samples list" );
+  if( startIndex + size > input->Size() )
+    {
+    itkExceptionMacro(
+            <<"requested range ["<<startIndex<<", "<<startIndex+size<<"[ partially outside input sample list range.[0,"<<input->Size()<<"[" );
+    }
+
+  // Convert input list of features to shark data format
+  std::vector<shark::RealVector> features;
+  otb::Shark::ListSampleRangeToSharkVector( input, features, startIndex, size );
+  shark::Data<shark::RealVector> inputSamples = shark::createDataFromRange( features );
+
+  shark::Data<ClusteringOutputType> clusters;
+  try
+    {
+     clusters = ( *m_ClusteringModel )( inputSamples );
+    }
+  catch( ... )
+    {
+    itkExceptionMacro( "Failed to run clustering classification. "
+                               "The number of features of input samples and the model could differ.");
+    }
+
+  unsigned int id = startIndex;
+  for( const auto &p : clusters.elements() )
+    {
+    TargetSampleType target;
+    target[0] = static_cast<TOutputValue>(p);
+    targets->SetMeasurementVector( id, target );
+    ++id;
+    }
+
+  // Change quality measurement only if SoftClustering or other clustering method is used.
+  if( quality != ITK_NULLPTR )
+    {
+    for( unsigned int qid = startIndex; qid < size; ++qid )
+      {
+      quality->SetMeasurementVector( qid, static_cast<ConfidenceValueType>(1.) );
+      }
+    }
+}
+
+
+template<class TInputValue, class TOutputValue>
+void
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::Save(const std::string &filename, const std::string & itkNotUsed( name ))
+{
+  std::ofstream ofs( filename.c_str());
+  if( !ofs )
+    {
+    itkExceptionMacro( << "Error opening " << filename.c_str());
+    }
+  ofs << "#" << m_ClusteringModel->name() << std::endl;
+  shark::TextOutArchive oa( ofs );
+  m_ClusteringModel->save( oa, 1 );
+}
+
+template<class TInputValue, class TOutputValue>
+void
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::Load(const std::string &filename, const std::string & itkNotUsed( name ))
+{
+  m_CanRead = false;
+  std::ifstream ifs( filename.c_str());
+  if(ifs.good())
+    {
+    // Check if first line contains model name
+    std::string line;
+    std::getline(ifs, line);
+    m_CanRead = line.find(m_ClusteringModel->name()) != std::string::npos;
+    }
+
+  if(!m_CanRead)
+    return;
+
+  shark::TextInArchive ia( ifs );
+  m_ClusteringModel->load( ia, 0 );
+  ifs.close();
+}
+
+template<class TInputValue, class TOutputValue>
+bool
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::CanReadFile(const std::string &file)
+{
+  try
+    {
+    m_CanRead = true;
+    this->Load( file );
+    }
+  catch( ... )
+    {
+    return false;
+    }
+  return m_CanRead;
+}
+
+template<class TInputValue, class TOutputValue>
+bool
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::CanWriteFile(const std::string & itkNotUsed( file ))
+{
+  return true;
+}
+
+template<class TInputValue, class TOutputValue>
+void
+SharkKMeansMachineLearningModel<TInputValue, TOutputValue>
+::PrintSelf(std::ostream &os, itk::Indent indent) const
+{
+  // Call superclass implementation
+  Superclass::PrintSelf( os, indent );
+}
+} //end namespace otb
+
+#endif
diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h
new file mode 100644
index 0000000000000000000000000000000000000000..a072d5d71921f24cc483e0193d3f0d04a4ab41d2
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h
@@ -0,0 +1,76 @@
+/*
+ * 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 otbSharkKMeansMachineLearningModelFactory_h
+#define otbSharkKMeansMachineLearningModelFactory_h
+
+#include "itkObjectFactoryBase.h"
+#include "itkImageIOBase.h"
+
+namespace otb
+{
+/** \class SharkKMeansMachineLearningModelFactory
+ * \brief Creation of an instance of a SharkKMeansMachineLearningModel object using the object factory
+ *
+ * \ingroup OTBUnsupervised
+ */
+template <class TInputValue, class TTargetValue>
+class ITK_EXPORT SharkKMeansMachineLearningModelFactory : public itk::ObjectFactoryBase
+{
+public:
+  /** Standard class typedefs. */
+  typedef SharkKMeansMachineLearningModelFactory             Self;
+  typedef itk::ObjectFactoryBase        Superclass;
+  typedef itk::SmartPointer<Self>       Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+  /** Class methods used to interface with the registered factories. */
+  virtual const char* GetITKSourceVersion(void) const;
+  virtual const char* GetDescription(void) const;
+
+  /** Method for class instantiation. */
+  itkFactorylessNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(SharkKMeansMachineLearningModelFactory, itk::ObjectFactoryBase);
+
+  /** Register one factory of this type  */
+  static void RegisterOneFactory(void)
+  {
+    Pointer KMeansFactory = SharkKMeansMachineLearningModelFactory::New();
+    itk::ObjectFactoryBase::RegisterFactory(KMeansFactory);
+  }
+
+protected:
+  SharkKMeansMachineLearningModelFactory();
+  virtual ~SharkKMeansMachineLearningModelFactory();
+
+private:
+  SharkKMeansMachineLearningModelFactory(const Self &); //purposely not implemented
+  void operator =(const Self&); //purposely not implemented
+
+};
+
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbSharkKMeansMachineLearningModelFactory.txx"
+#endif
+
+#endif
diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.txx b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.txx
new file mode 100644
index 0000000000000000000000000000000000000000..a4fe5584e04249875de1193e365fd888a43cfa07
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.txx
@@ -0,0 +1,71 @@
+/*
+ * 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 otbSharkKMeansMachineLearningModelFactory_txx
+#define otbSharkKMeansMachineLearningModelFactory_txx
+
+#include "otbSharkKMeansMachineLearningModelFactory.h"
+
+#include "itkCreateObjectFunction.h"
+#include "otbSharkKMeansMachineLearningModel.h"
+#include "itkVersion.h"
+
+namespace otb
+{
+
+template <class TInputValue, class TOutputValue>
+SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>
+::SharkKMeansMachineLearningModelFactory()
+{
+
+  std::string classOverride = std::string("otbMachineLearningModel");
+  std::string subclass = std::string("otbSharkKMeansMachineLearningModel");
+
+  this->RegisterOverride(classOverride.c_str(),
+                         subclass.c_str(),
+                         "Shark KMeans Machine Learning Model",
+                         1,
+                         itk::CreateObjectFunction<SharkKMeansMachineLearningModel<TInputValue,TOutputValue> >::New());
+}
+
+template <class TInputValue, class TOutputValue>
+SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>
+::~SharkKMeansMachineLearningModelFactory()
+{
+}
+
+template <class TInputValue, class TOutputValue>
+const char*
+SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>
+::GetITKSourceVersion(void) const
+{
+  return ITK_SOURCE_VERSION;
+}
+
+template <class TInputValue, class TOutputValue>
+const char*
+SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>
+::GetDescription() const
+{
+  return "Shark KMeans unsupervised machine learning model factory";
+}
+
+} // end namespace otb
+
+#endif
diff --git a/Modules/Learning/Unsupervised/otb-module.cmake b/Modules/Learning/Unsupervised/otb-module.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..edaf52212e6bd849bf0b385bd3657bbc27b072c6
--- /dev/null
+++ b/Modules/Learning/Unsupervised/otb-module.cmake
@@ -0,0 +1,21 @@
+set(DOCUMENTATION "This module provides the Orfeo Toolbox unsupervised
+classification and regression framework, currently based on Shark")
+
+otb_module(OTBUnsupervised
+  DEPENDS
+  OTBCommon
+  OTBITK
+  OTBImageBase
+  OTBLearningBase
+
+  OPTIONAL_DEPENDS
+  OTBShark
+
+  TEST_DEPENDS
+  OTBTestKernel
+  OTBImageIO
+  OTBImageBase
+
+  DESCRIPTION
+  "${DOCUMENTATION}"
+  )
diff --git a/Modules/Learning/Unsupervised/test/CMakeLists.txt b/Modules/Learning/Unsupervised/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..57cdb01bd6d73755758b1ec3ba1110e93a2adc36
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/CMakeLists.txt
@@ -0,0 +1,17 @@
+otb_module_test()
+set(OTBUnsupervisedTests
+  otbUnsupervisedTestDriver.cxx
+  otbMachineLearningUnsupervisedModelCanRead.cxx
+  otbTrainMachineLearningUnsupervisedModel.cxx
+  )
+
+# Tests Declaration
+
+if(OTB_USE_SHARK)
+  set(OTBUnsupervisedTests ${OTBUnsupervisedTests} otbSharkUnsupervisedImageClassificationFilter.cxx)
+  include(tests-shark.cmake)
+endif()
+
+add_executable(otbUnsupervisedTestDriver ${OTBUnsupervisedTests})
+target_link_libraries(otbUnsupervisedTestDriver ${OTBUnsupervised-Test_LIBRARIES})
+otb_module_target_label(otbUnsupervisedTestDriver)
diff --git a/Modules/Learning/Unsupervised/test/otbMachineLearningUnsupervisedModelCanRead.cxx b/Modules/Learning/Unsupervised/test/otbMachineLearningUnsupervisedModelCanRead.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6856f0d0728327c26df9873af3afa016789c6d10
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbMachineLearningUnsupervisedModelCanRead.cxx
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <iostream>
+
+#include <otbConfigure.h>
+#include <otbMachineLearningModel.h>
+
+typedef otb::MachineLearningModel<float,short>         MachineLearningModelType;
+typedef MachineLearningModelType::InputValueType       InputValueType;
+typedef MachineLearningModelType::InputSampleType      InputSampleType;
+typedef MachineLearningModelType::InputListSampleType  InputListSampleType;
+typedef MachineLearningModelType::TargetValueType      TargetValueType;
+typedef MachineLearningModelType::TargetSampleType     TargetSampleType;
+typedef MachineLearningModelType::TargetListSampleType TargetListSampleType;
+
+#ifdef OTB_USE_SHARK
+
+#include "otbSharkKMeansMachineLearningModel.h"
+
+int otbSharkKMeansMachineLearningModelCanRead(int argc, char *argv[])
+{
+  if( argc != 2 )
+    {
+    std::cerr << "Usage: " << argv[0] << "<model>" << std::endl;
+    std::cerr << "Called here with " << argc << " arguments\n";
+    for( int i = 1; i < argc; ++i )
+      {
+      std::cerr << " - " << argv[i] << "\n";
+      }
+    return EXIT_FAILURE;
+    }
+  std::string filename( argv[1] );
+  typedef otb::SharkKMeansMachineLearningModel<InputValueType, TargetValueType> KMType;
+  KMType::Pointer classifier = KMType::New();
+  bool lCanRead = classifier->CanReadFile( filename );
+  if( !lCanRead )
+    {
+    std::cerr << "Error otb::SharkKMeansMachineLearningModel : impossible to open the file " << filename << "."
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/Modules/Learning/Unsupervised/test/otbSharkUnsupervisedImageClassificationFilter.cxx b/Modules/Learning/Unsupervised/test/otbSharkUnsupervisedImageClassificationFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7c101f7e2eb4ab7b6ec1d083c793b84523d4b25e
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbSharkUnsupervisedImageClassificationFilter.cxx
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+#include "otbVectorImage.h"
+#include "otbImageFileReader.h"
+#include "otbImageFileWriter.h"
+#include "otbImageClassificationFilter.h"
+#include "otbSharkKMeansMachineLearningModelFactory.h"
+
+#include <random>
+#include <chrono>
+
+
+const unsigned int Dimension = 2;
+typedef float PixelType;
+typedef unsigned short LabeledPixelType;
+
+typedef otb::VectorImage<PixelType, Dimension> ImageType;
+typedef otb::Image<LabeledPixelType, Dimension> LabeledImageType;
+typedef otb::ImageClassificationFilter<ImageType, LabeledImageType> ClassificationFilterType;
+typedef ClassificationFilterType::ModelType ModelType;
+typedef ClassificationFilterType::ValueType ValueType;
+typedef ClassificationFilterType::LabelType LabelType;
+typedef otb::SharkKMeansMachineLearningModelFactory<ValueType, LabelType> MachineLearningModelFactoryType;
+typedef otb::ImageFileReader<ImageType> ReaderType;
+typedef otb::ImageFileReader<LabeledImageType> MaskReaderType;
+typedef otb::ImageFileWriter<LabeledImageType> WriterType;
+
+typedef otb::SharkKMeansMachineLearningModel<PixelType,short unsigned int>         MachineLearningModelType;
+typedef MachineLearningModelType::InputValueType       LocalInputValueType;
+typedef MachineLearningModelType::InputSampleType      LocalInputSampleType;
+typedef MachineLearningModelType::InputListSampleType  LocalInputListSampleType;
+typedef MachineLearningModelType::TargetValueType      LocalTargetValueType;
+typedef MachineLearningModelType::TargetSampleType     LocalTargetSampleType;
+typedef MachineLearningModelType::TargetListSampleType LocalTargetListSampleType;
+
+void generateSamples(unsigned int num_classes, unsigned int num_samples,
+                     unsigned int num_features,
+                     LocalInputListSampleType * samples,
+                     LocalTargetListSampleType * labels)
+{
+  std::default_random_engine randomEngine;
+  std::uniform_int_distribution<int> label_distribution(1,num_classes);
+  std::uniform_int_distribution<int> feat_distribution(0,256);
+  for(size_t scount=0; scount<num_samples; ++scount)
+    {
+    LabeledPixelType label = label_distribution(randomEngine);
+    LocalInputSampleType sample(num_features);
+    for(unsigned int i=0; i<num_features; ++i)
+      sample[i]= feat_distribution(randomEngine);
+    samples->SetMeasurementVectorSize(num_features);
+    samples->PushBack(sample);
+    labels->PushBack(label);
+    }
+}
+
+void buildModel(unsigned int num_classes, unsigned int num_samples,
+                unsigned int num_features, std::string modelfname)
+{
+  LocalInputListSampleType::Pointer samples = LocalInputListSampleType::New();
+  LocalTargetListSampleType::Pointer labels = LocalTargetListSampleType::New();
+
+  std::cout << "Sample generation\n";
+  generateSamples(num_classes, num_samples, num_features, samples, labels);
+
+  MachineLearningModelType::Pointer classifier = MachineLearningModelType::New();
+  classifier->SetInputListSample(samples);
+  classifier->SetTargetListSample(labels);
+  classifier->SetRegressionMode(false);
+  classifier->SetK(3);
+
+  std::cout << "Training\n";
+  using TimeT = std::chrono::milliseconds;
+  auto start = std::chrono::system_clock::now();
+  classifier->Train();
+  auto duration = std::chrono::duration_cast< TimeT>
+          (std::chrono::system_clock::now() - start);
+  auto elapsed = duration.count();
+  std::cout << "Training took " << elapsed << " ms\n";
+  classifier->Save(modelfname);
+}
+
+int otbSharkUnsupervisedImageClassificationFilter(int argc, char * argv[])
+{
+  if(argc<5 || argc>7)
+    {
+    std::cout << "Usage: input_image output_image output_confidence batchmode [in_model_name] [mask_name]\n";
+    }
+  std::string imfname = argv[1];
+  std::string outfname = argv[2];
+  std::string conffname = argv[3];
+  bool batch = (std::string(argv[4])=="1");
+  std::string modelfname = "/tmp/rf_model.txt";
+  std::string maskfname{};
+
+  MaskReaderType::Pointer mask_reader = MaskReaderType::New();
+  ReaderType::Pointer reader = ReaderType::New();
+  reader->SetFileName(imfname);
+  reader->UpdateOutputInformation();
+
+  auto num_features = reader->GetOutput()->GetNumberOfComponentsPerPixel();
+
+  std::cout << "Image has " << num_features << " bands\n";
+
+  if(argc>5)
+    {
+    modelfname = argv[5];
+    }
+  else
+    {
+    buildModel(3, 1000, num_features, modelfname);
+    }
+
+  ClassificationFilterType::Pointer filter = ClassificationFilterType::New();
+
+  MachineLearningModelType::Pointer model = MachineLearningModelType::New();
+  if(!model->CanReadFile(modelfname))
+    {
+    std::cerr << "Unable to read the model : " << modelfname << "\n";
+    return EXIT_FAILURE;
+    }
+
+  filter->SetModel(model);
+  filter->SetInput(reader->GetOutput());
+  if(argc==7)
+    {
+    maskfname = argv[6];
+    mask_reader->SetFileName(maskfname);
+    filter->SetInputMask(mask_reader->GetOutput());
+    }
+
+  WriterType::Pointer writer = WriterType::New();
+  writer->SetInput(filter->GetOutput());
+  writer->SetFileName(outfname);
+  std::cout << "Classification\n";
+  filter->SetBatchMode(batch);
+  filter->SetUseConfidenceMap(true);
+  using TimeT = std::chrono::milliseconds;
+  auto start = std::chrono::system_clock::now();
+  writer->Update();
+  auto duration = std::chrono::duration_cast< TimeT>
+          (std::chrono::system_clock::now() - start);
+  auto elapsed = duration.count();
+  std::cout << "Classification took " << elapsed << " ms\n";
+
+  auto confWriter = otb::ImageFileWriter<ClassificationFilterType::ConfidenceImageType>::New();
+  confWriter->SetInput(filter->GetOutputConfidence());
+  confWriter->SetFileName(conffname);
+  confWriter->Update();
+
+  return EXIT_SUCCESS;
+}
diff --git a/Modules/Learning/Unsupervised/test/otbTrainMachineLearningUnsupervisedModel.cxx b/Modules/Learning/Unsupervised/test/otbTrainMachineLearningUnsupervisedModel.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a0805c29fb38defb049a9588a26e10167cb5920b
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbTrainMachineLearningUnsupervisedModel.cxx
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+#include <iostream>
+
+#include <otbConfigure.h>
+#include <otbMachineLearningModel.h>
+
+typedef otb::MachineLearningModel<float,short>         MachineLearningModelType;
+typedef MachineLearningModelType::InputValueType       InputValueType;
+typedef MachineLearningModelType::InputSampleType      InputSampleType;
+typedef MachineLearningModelType::InputListSampleType  InputListSampleType;
+typedef MachineLearningModelType::TargetValueType      TargetValueType;
+typedef MachineLearningModelType::TargetSampleType     TargetSampleType;
+typedef MachineLearningModelType::TargetListSampleType TargetListSampleType;
+
+typedef otb::MachineLearningModel<float,float>                   MachineLearningModelRegressionType;
+typedef MachineLearningModelRegressionType::InputValueType       InputValueRegressionType;
+typedef MachineLearningModelRegressionType::InputSampleType      InputSampleRegressionType;
+typedef MachineLearningModelRegressionType::InputListSampleType  InputListSampleRegressionType;
+typedef MachineLearningModelRegressionType::TargetValueType      TargetValueRegressionType;
+typedef MachineLearningModelRegressionType::TargetSampleType     TargetSampleRegressionType;
+typedef MachineLearningModelRegressionType::TargetListSampleType TargetListSampleRegressionType;
+
+
+#ifdef OTB_USE_SHARK
+#include "otbSharkKMeansMachineLearningModel.h"
+#include "otb_boost_string_header.h"
+#include <chrono>
+
+bool SharkReadDataFile(const std::string & infname, InputListSampleType * samples, TargetListSampleType * labels)
+{
+  std::ifstream ifs(infname.c_str());
+
+  if(!ifs)
+    {
+    std::cout<<"Could not read file "<<infname<<std::endl;
+    return false;
+    }
+
+  unsigned int nbfeatures = 0;
+
+  std::string line;
+  while (std::getline(ifs, line))
+    {
+    boost::algorithm::trim(line);
+
+    if(nbfeatures == 0)
+      {
+      nbfeatures = std::count(line.begin(),line.end(),' ');
+      }
+
+    if(line.size()>1)
+      {
+      InputSampleType sample(nbfeatures);
+      sample.Fill(0);
+
+      std::string::size_type pos = line.find_first_of(" ", 0);
+
+      // Parse label
+      TargetSampleType label;
+      label[0] = std::stoi(line.substr(0, pos).c_str());
+
+      bool endOfLine = false;
+      unsigned int id = 0;
+
+      while(!endOfLine)
+        {
+        std::string::size_type nextpos = line.find_first_of(" ", pos+1);
+
+        if(pos == std::string::npos)
+          {
+          endOfLine = true;
+          nextpos = line.size()-1;
+          }
+        else
+          {
+          std::string feature = line.substr(pos,nextpos-pos);
+          std::string::size_type semicolonpos = feature.find_first_of(":");
+          id = std::stoi(feature.substr(0,semicolonpos).c_str());
+          sample[id - 1] = atof(feature.substr(semicolonpos+1,feature.size()-semicolonpos).c_str());
+          pos = nextpos;
+          }
+
+        }
+      samples->SetMeasurementVectorSize(itk::NumericTraits<InputSampleType>::GetLength(sample));
+      samples->PushBack(sample);
+      labels->PushBack(label);
+      }
+    }
+
+  //std::cout<<"Retrieved "<<samples->Size()<<" samples"<<std::endl;
+  ifs.close();
+  return true;
+}
+
+
+int otbSharkKMeansMachineLearningModelNew(int itkNotUsed( argc ), char *itkNotUsed( argv )[])
+{
+  typedef otb::SharkKMeansMachineLearningModel<InputValueType, TargetValueType> SharkRFType;
+  SharkRFType::Pointer classifier = SharkRFType::New();
+  return EXIT_SUCCESS;
+}
+
+int otbSharkKMeansMachineLearningModelTrain(int argc, char *argv[])
+{
+  if( argc != 3 )
+    {
+    std::cout << "Wrong number of arguments " << std::endl;
+    std::cout << "Usage : sample file, output file " << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  typedef otb::SharkKMeansMachineLearningModel<InputValueType, TargetValueType> KMeansType;
+  InputListSampleType::Pointer samples = InputListSampleType::New();
+  TargetListSampleType::Pointer labels = TargetListSampleType::New();
+
+  if( !SharkReadDataFile( argv[1], samples, labels ) )
+    {
+    std::cout << "Failed to read samples file " << argv[1] << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  KMeansType::Pointer classifier = KMeansType::New();
+  classifier->SetInputListSample( samples );
+  classifier->SetTargetListSample( labels );
+  classifier->SetRegressionMode( false );
+  classifier->SetK( 3 );
+  classifier->SetMaximumNumberOfIterations( 0 );
+  std::cout << "Train\n";
+  classifier->Train();
+  std::cout << "Save\n";
+  classifier->Save( argv[2] );
+
+  return EXIT_SUCCESS;
+}
+
+
+int otbSharkKMeansMachineLearningModelPredict(int argc, char *argv[])
+{
+  if( argc != 3 )
+    {
+    std::cout << "Wrong number of arguments " << std::endl;
+    std::cout << "Usage : sample file, input model file " << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  typedef otb::SharkKMeansMachineLearningModel<InputValueType, TargetValueType> KMeansType;
+  InputListSampleType::Pointer samples = InputListSampleType::New();
+  TargetListSampleType::Pointer labels = TargetListSampleType::New();
+
+  if( !SharkReadDataFile( argv[1], samples, labels ) )
+    {
+    std::cout << "Failed to read samples file " << argv[1] << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  KMeansType::Pointer classifier = KMeansType::New();
+  std::cout << "Load\n";
+  if(!classifier->CanReadFile(argv[2]))
+    {
+    std::cerr << "Unable to read model file : " << argv[2] << std::endl;
+    return EXIT_FAILURE;
+    }
+  classifier->Load( argv[2] );
+  auto start = std::chrono::system_clock::now();
+  classifier->SetInputListSample( samples );
+  classifier->SetTargetListSample( labels );
+  std::cout << "Predict loaded\n";
+  classifier->PredictBatch( samples, NULL );
+  using TimeT = std::chrono::milliseconds;
+  auto duration = std::chrono::duration_cast<TimeT>( std::chrono::system_clock::now() - start );
+  auto elapsed = duration.count();
+  std::cout << "PredictAll took " << elapsed << " ms\n";
+
+  return EXIT_SUCCESS;
+}
+#endif
diff --git a/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx b/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..30a7194f52fb61c6370693fcd8b455b1f77ac7a2
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include "otbTestMain.h"
+void RegisterTests()
+{
+#ifdef OTB_USE_SHARK
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelCanRead);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelNew);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelTrain);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelPredict);
+  REGISTER_TEST(otbSharkUnsupervisedImageClassificationFilter);
+#endif
+}
diff --git a/Modules/Learning/Unsupervised/test/tests-shark.cmake b/Modules/Learning/Unsupervised/test/tests-shark.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..df61cb46479f9922e3355441458a1e261879c40c
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/tests-shark.cmake
@@ -0,0 +1,58 @@
+# kMeans Shark related tests
+
+otb_add_test(NAME leTvSharkKMeansMachineLearningModelNew COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelNew
+  )
+
+
+otb_add_test(NAME leTvSharkKMeansMachineLearningModel COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelTrain
+  ${INPUTDATA}/letter.scale
+  ${TEMP}/shark_km_model.txt
+  )
+
+otb_add_test(NAME otbSharkKMeansMachineLearningModelPredict COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelPredict
+  ${INPUTDATA}/letter.scale
+  ${INPUTDATA}/Classification/shark_km_model.txt
+  )
+
+otb_add_test(NAME leTvSharkKMeansMachineLearningModelCanRead COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelCanRead
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
+  )
+
+otb_add_test(NAME leTvSharkKMeansMachineLearningModelCanReadFail COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelCanRead
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_RFmodel.txt
+  )
+
+set_property(TEST leTvSharkKMeansMachineLearningModelCanReadFail PROPERTY WILL_FAIL true)
+
+
+
+otb_add_test(NAME leTvImageClassificationFilterSharkKMeans COMMAND  otbUnsupervisedTestDriver
+  --compare-n-images ${NOTOL} 1
+  ${BASELINE}/leSharkUnsupervisedImageClassificationFilterOutput.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterOutput.tif
+  otbSharkUnsupervisedImageClassificationFilter
+  ${INPUTDATA}/Classification/QB_1_ortho.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterOutput.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterConfidence.tif
+  1
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
+  )
+
+
+otb_add_test(NAME leTvImageClassificationFilterSharkKMeansMask COMMAND  otbUnsupervisedTestDriver
+  --compare-n-images ${NOTOL} 1
+  ${BASELINE}/leSharkUnsupervisedImageClassificationFilterWithMaskOutput.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterWithMaskOutput.tif
+  otbSharkUnsupervisedImageClassificationFilter
+  ${INPUTDATA}/Classification/QB_1_ortho.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterWithMaskOutput.tif
+  ${TEMP}/leSharkUnsupervisedImageClassificationFilterWithMaskConfidence.tif
+  1
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
+  ${INPUTDATA}/Classification/QB_1_ortho_mask.tif
+  )