From 9885b547c16f77dedbcfd29c641d75aa51d6e19e Mon Sep 17 00:00:00 2001
From: Ludovic Hussonnois <ludovic.hussonnois@c-s.fr>
Date: Mon, 20 Feb 2017 10:29:54 +0100
Subject: [PATCH] ENH: Create Learning/Unsupervised Module and move
 corresponding files.

Move commons files for both supervised and unsupervised into
LearningBase Module.
Also update Test and includes.
---
 .../AppClassification/otb-module.cmake        |   1 +
 .../include/otbMachineLearningModel.h         |   0
 .../include/otbMachineLearningModel.txx       |   0
 .../otbMachineLearningModelFactoryBase.h      |   0
 .../otbMachineLearningModelFactory.txx        |  10 --
 Modules/Learning/Supervised/otb-module.cmake  |   2 +
 .../test/otbMachineLearningModelCanRead.cxx   |  30 ----
 .../test/otbSupervisedTestDriver.cxx          |   3 -
 .../test/otbTrainMachineLearningModel.cxx     |  80 ---------
 .../Supervised/test/tests-shark.cmake         |  27 ---
 Modules/Learning/Unsupervised/CMakeLists.txt  |   4 +
 ...otbMachineLearningClusteringModelFactory.h |  81 +++++++++
 ...bMachineLearningClusteringModelFactory.txx | 134 ++++++++++++++
 .../otbSharkKMeansMachineLearningModel.h      |   9 +-
 .../otbSharkKMeansMachineLearningModel.txx    |   0
 ...tbSharkKMeansMachineLearningModelFactory.h |   2 +-
 ...SharkKMeansMachineLearningModelFactory.txx |   0
 .../Learning/Unsupervised/otb-module.cmake    |  24 +++
 .../Learning/Unsupervised/test/CMakeLists.txt |  17 ++
 ...bMachineLearningClusteringModelCanRead.cxx |  62 +++++++
 ...otbTrainMachineLearningClusteringModel.cxx | 170 ++++++++++++++++++
 .../test/otbUnsupervisedTestDriver.cxx        |  10 ++
 .../Unsupervised/test/tests-shark.cmake       |  25 +++
 23 files changed, 536 insertions(+), 155 deletions(-)
 rename Modules/Learning/{Supervised => LearningBase}/include/otbMachineLearningModel.h (100%)
 rename Modules/Learning/{Supervised => LearningBase}/include/otbMachineLearningModel.txx (100%)
 rename Modules/Learning/{Supervised => LearningBase}/include/otbMachineLearningModelFactoryBase.h (100%)
 create mode 100644 Modules/Learning/Unsupervised/CMakeLists.txt
 create mode 100644 Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.h
 create mode 100644 Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.txx
 rename Modules/Learning/{Supervised => Unsupervised}/include/otbSharkKMeansMachineLearningModel.h (97%)
 rename Modules/Learning/{Supervised => Unsupervised}/include/otbSharkKMeansMachineLearningModel.txx (100%)
 rename Modules/Learning/{Supervised => Unsupervised}/include/otbSharkKMeansMachineLearningModelFactory.h (98%)
 rename Modules/Learning/{Supervised => Unsupervised}/include/otbSharkKMeansMachineLearningModelFactory.txx (100%)
 create mode 100644 Modules/Learning/Unsupervised/otb-module.cmake
 create mode 100644 Modules/Learning/Unsupervised/test/CMakeLists.txt
 create mode 100644 Modules/Learning/Unsupervised/test/otbMachineLearningClusteringModelCanRead.cxx
 create mode 100644 Modules/Learning/Unsupervised/test/otbTrainMachineLearningClusteringModel.cxx
 create mode 100644 Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
 create mode 100644 Modules/Learning/Unsupervised/test/tests-shark.cmake

diff --git a/Modules/Applications/AppClassification/otb-module.cmake b/Modules/Applications/AppClassification/otb-module.cmake
index 9c31eeede5..322ba7f9c4 100644
--- a/Modules/Applications/AppClassification/otb-module.cmake
+++ b/Modules/Applications/AppClassification/otb-module.cmake
@@ -12,6 +12,7 @@ otb_module(OTBAppClassification
     OTBVectorDataIO
     OTBSOM
     OTBSupervised
+    OTBUnsupervised
     OTBApplicationEngine
     OTBIndices
     OTBMathParser
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModel.h b/Modules/Learning/LearningBase/include/otbMachineLearningModel.h
similarity index 100%
rename from Modules/Learning/Supervised/include/otbMachineLearningModel.h
rename to Modules/Learning/LearningBase/include/otbMachineLearningModel.h
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 100%
rename from Modules/Learning/Supervised/include/otbMachineLearningModelFactoryBase.h
rename to Modules/Learning/LearningBase/include/otbMachineLearningModelFactoryBase.h
diff --git a/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx b/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
index 5e72ce37db..a99aa0f78e 100644
--- a/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
+++ b/Modules/Learning/Supervised/include/otbMachineLearningModelFactory.txx
@@ -37,7 +37,6 @@
 
 #ifdef OTB_USE_SHARK
 #include "otbSharkRandomForestsMachineLearningModelFactory.h"
-#include "otbSharkKMeansMachineLearningModelFactory.h"
 #endif
 
 #include "itkMutexLockHolder.h"
@@ -105,7 +104,6 @@ MachineLearningModelFactory<TInputValue,TOutputValue>
 
 #ifdef OTB_USE_SHARK
   RegisterFactory(SharkRandomForestsMachineLearningModelFactory<TInputValue,TOutputValue>::New());
-  RegisterFactory(SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>::New());
 #endif
   
 #ifdef OTB_USE_OPENCV
@@ -162,14 +160,6 @@ 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/otb-module.cmake b/Modules/Learning/Supervised/otb-module.cmake
index ebce0f334e..b46c75f574 100644
--- a/Modules/Learning/Supervised/otb-module.cmake
+++ b/Modules/Learning/Supervised/otb-module.cmake
@@ -9,6 +9,7 @@ ENABLE_SHARED
     OTBCommon
     OTBITK
     OTBImageBase
+    OTBLearningBase
 
   OPTIONAL_DEPENDS
     OTBOpenCV
@@ -19,6 +20,7 @@ ENABLE_SHARED
     OTBTestKernel
     OTBImageIO
     OTBImageBase
+    OTBLearningBase
     OTBBoost
 
   DESCRIPTION
diff --git a/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx b/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
index 9c03dcf2fb..e2358fcd15 100644
--- a/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
+++ b/Modules/Learning/Supervised/test/otbMachineLearningModelCanRead.cxx
@@ -319,35 +319,5 @@ int otbSharkRFMachineLearningModelCanRead(int argc, char* argv[])
   return EXIT_SUCCESS;
 }
 
-#include "otbSharkKMeansMachineLearningModel.h"
-
-int itbSharkKMeansMachineLearningModelCanRead(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> RFType;
-  RFType::Pointer classifier = RFType::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/Supervised/test/otbSupervisedTestDriver.cxx b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
index 6aba3c2fc3..825341047d 100644
--- a/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
+++ b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx
@@ -62,9 +62,6 @@ void RegisterTests()
   REGISTER_TEST(otbSharkRFMachineLearningModel);
   REGISTER_TEST(otbSharkRFMachineLearningModelCanRead);
   REGISTER_TEST(otbSharkImageClassificationFilter);
-  REGISTER_TEST(otbSharkKMeansMachineLearningModelNew);
-  REGISTER_TEST(otbSharkKMeansMachineLearningModelTrain);
-  REGISTER_TEST(otbSharkKMeansMachineLearningModelPredict);
 #endif
 
   REGISTER_TEST(otbImageClassificationFilterNew);
diff --git a/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx b/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
index 6221a7dcdc..fc1597c3f4 100644
--- a/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
+++ b/Modules/Learning/Supervised/test/otbTrainMachineLearningModel.cxx
@@ -1287,84 +1287,4 @@ int otbSharkRFMachineLearningModel(int argc, char * argv[])
 }
 
 
-#include "otbSharkKMeansMachineLearningModel.h"
-
-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";
-  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/Supervised/test/tests-shark.cmake b/Modules/Learning/Supervised/test/tests-shark.cmake
index 73706dda29..49ac03632b 100644
--- a/Modules/Learning/Supervised/test/tests-shark.cmake
+++ b/Modules/Learning/Supervised/test/tests-shark.cmake
@@ -60,30 +60,3 @@ otb_add_test(NAME leTvImageClassificationFilterSharkFastMask COMMAND  otbSupervi
   ${INPUTDATA}/Classification/QB_1_ortho_mask.tif
   )
 
-
-
-# kMeans Shark related tests
-
-otb_add_test(NAME leTvSharkKMeansMachineLearningModelNew COMMAND otbSupervisedTestDriver
-  otbSharkKMeansMachineLearningModelNew
-  )
-
-otb_add_test(NAME leTvSharkKMeansMachineLearningModel COMMAND otbSupervisedTestDriver
-  otbSharkKMeansMachineLearningModelTrain
-  ${INPUTDATA}/letter.scale
-  ${TEMP}/shark_km_model.txt
-  )
-
-otb_add_test(NAME leTvSharkKMeansMachineLearningModelCanRead COMMAND otbSupervisedTestDriver
-  otbSharkKMeansMachineLearningModelPredict
-  ${INPUTDATA}/letter.scale
-  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
-  )
-
-otb_add_test(NAME leTvSharkKMeansMachineLearningModelCanReadFail COMMAND otbSupervisedTestDriver
-  otbSharkKMeansMachineLearningModelPredict
-  ${INPUTDATA}/letter.scale
-  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_RFmodel.txt
-  )
-
-set_property(TEST leTvSharkKMeansMachineLearningModelCanReadFail PROPERTY WILL_FAIL true)
diff --git a/Modules/Learning/Unsupervised/CMakeLists.txt b/Modules/Learning/Unsupervised/CMakeLists.txt
new file mode 100644
index 0000000000..e83c272cd6
--- /dev/null
+++ b/Modules/Learning/Unsupervised/CMakeLists.txt
@@ -0,0 +1,4 @@
+project(OTBUnsupervised)
+
+
+otb_module_impl()
diff --git a/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.h b/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.h
new file mode 100644
index 0000000000..62b0c6add6
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.h
@@ -0,0 +1,81 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef otbMachineLearningClusteringModelFactory_h
+#define otbMachineLearningClusteringModelFactory_h
+
+#include "otbMachineLearningModel.h"
+#include "otbMachineLearningModelFactoryBase.h"
+
+namespace otb
+{
+/** \class MachineLearningModelFactory
+ * \brief Creation of object instance using object factory.
+ *
+ * \ingroup OTBUnsupervised
+ */
+template <class TInputValue, class TOutputValue>
+class MachineLearningModelFactory : public MachineLearningModelFactoryBase
+{
+public:
+  /** Standard class typedefs. */
+  typedef MachineLearningModelFactory                Self;
+  typedef itk::Object           Superclass;
+  typedef itk::SmartPointer<Self>       Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+  /** Class Methods used to interface with the registered factories */
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(MachineLearningModelFactory, itk::Object);
+
+  /** Convenient typedefs. */
+  typedef otb::MachineLearningModel<TInputValue,TOutputValue> MachineLearningModelType;
+  typedef typename MachineLearningModelType::Pointer MachineLearningModelTypePointer;
+
+  /** Mode in which the files is intended to be used */
+  typedef enum { ReadMode, WriteMode } FileModeType;
+
+  /** Create the appropriate MachineLearningModel depending on the particulars of the file. */
+  static MachineLearningModelTypePointer CreateMachineLearningModel(const std::string& path, FileModeType mode);
+
+  static void CleanFactories();
+
+protected:
+  MachineLearningModelFactory();
+  ~MachineLearningModelFactory() ITK_OVERRIDE;
+
+private:
+  MachineLearningModelFactory(const Self &); //purposely not implemented
+  void operator =(const Self&); //purposely not implemented
+
+  /** Register Built-in factories */
+  static void RegisterBuiltInFactories();
+
+  /** Register a single factory, ensuring it has not been registered
+    * twice */
+  static void RegisterFactory(itk::ObjectFactoryBase * factory);
+
+};
+
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbMachineLearningClusteringModelFactory.txx"
+#endif
+
+#endif //otbMachineLearningClusteringModelFactory_h
diff --git a/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.txx b/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.txx
new file mode 100644
index 0000000000..9e06486f78
--- /dev/null
+++ b/Modules/Learning/Unsupervised/include/otbMachineLearningClusteringModelFactory.txx
@@ -0,0 +1,134 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef otbMachineLearningModelFactory_txx
+#define otbMachineLearningModelFactory_txx
+
+#include "otbMachineLearningClusteringModelFactory.h"
+#include "otbConfigure.h"
+
+#ifdef OTB_USE_SHARK
+#include "otbSharkKMeansMachineLearningModelFactory.h"
+#endif
+
+#include "itkMutexLockHolder.h"
+
+
+namespace otb
+{
+template <class TInputValue, class TOutputValue>
+typename MachineLearningModel<TInputValue,TOutputValue>::Pointer
+MachineLearningModelFactory<TInputValue,TOutputValue>
+::CreateMachineLearningModel(const std::string& path, FileModeType mode)
+{
+  RegisterBuiltInFactories();
+
+  std::list<MachineLearningModelTypePointer> possibleMachineLearningModel;
+  std::list<LightObject::Pointer> allobjects =
+          itk::ObjectFactoryBase::CreateAllInstance("otbMachineLearningModel");
+  for(std::list<LightObject::Pointer>::iterator i = allobjects.begin();
+      i != allobjects.end(); ++i)
+    {
+    MachineLearningModel<TInputValue,TOutputValue> * io = dynamic_cast<MachineLearningModel<TInputValue,TOutputValue>*>(i->GetPointer());
+    if(io)
+      {
+      possibleMachineLearningModel.push_back(io);
+      }
+    else
+      {
+      std::cerr << "Error MachineLearningModel Factory did not return an MachineLearningModel: "
+                << (*i)->GetNameOfClass()
+                << std::endl;
+      }
+    }
+  for(typename std::list<MachineLearningModelTypePointer>::iterator k = possibleMachineLearningModel.begin();
+      k != possibleMachineLearningModel.end(); ++k)
+    {
+    if( mode == ReadMode )
+      {
+      if((*k)->CanReadFile(path))
+        {
+        return *k;
+        }
+      }
+    else if( mode == WriteMode )
+      {
+      if((*k)->CanWriteFile(path))
+        {
+        return *k;
+        }
+
+      }
+    }
+  return ITK_NULLPTR;
+}
+
+template <class TInputValue, class TOutputValue>
+void
+MachineLearningModelFactory<TInputValue,TOutputValue>
+::RegisterBuiltInFactories()
+{
+  itk::MutexLockHolder<itk::SimpleMutexLock> lockHolder(mutex);
+
+#ifdef OTB_USE_SHARK
+  RegisterFactory(SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue>::New());
+#endif
+
+}
+
+template <class TInputValue, class TOutputValue>
+void
+MachineLearningModelFactory<TInputValue,TOutputValue>
+::RegisterFactory(itk::ObjectFactoryBase * factory)
+{
+  // Unregister any previously registered factory of the same class
+  // Might be more intensive but static bool is not an option due to
+  // ld error.
+  itk::ObjectFactoryBase::UnRegisterFactory(factory);
+  itk::ObjectFactoryBase::RegisterFactory(factory);
+}
+
+template <class TInputValue, class TOutputValue>
+void
+MachineLearningModelFactory<TInputValue,TOutputValue>
+::CleanFactories()
+{
+  itk::MutexLockHolder<itk::SimpleMutexLock> lockHolder(mutex);
+
+  std::list<itk::ObjectFactoryBase*> factories = itk::ObjectFactoryBase::GetRegisteredFactories();
+  std::list<itk::ObjectFactoryBase*>::iterator itFac;
+
+  for (itFac = factories.begin(); itFac != factories.end() ; ++itFac)
+  {
+
+#ifdef OTB_USE_SHARK
+    SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue> *sharkKMeansFactory =
+            dynamic_cast<SharkKMeansMachineLearningModelFactory<TInputValue,TOutputValue> *>(*itFac);
+    if (sharkKMeansFactory)
+      {
+      itk::ObjectFactoryBase::UnRegisterFactory(sharkKMeansFactory);
+      continue;
+      }
+#endif
+
+    }
+
+}
+
+} // end namespace otb
+
+#endif
diff --git a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModel.h b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h
similarity index 97%
rename from Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModel.h
rename to Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h
index c822b029d1..a31891e5d1 100644
--- a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModel.h
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h
@@ -18,9 +18,7 @@
 #ifndef otbSharkKMeansMachineLearningModel_h
 #define otbSharkKMeansMachineLearningModel_h
 
-#include <shark/Models/Clustering/HardClusteringModel.h>
-#include <shark/Models/Clustering/SoftClusteringModel.h>
-#include "otb_shark.h"
+
 
 #include "itkLightObject.h"
 #include "otbMachineLearningModel.h"
@@ -36,6 +34,9 @@
 #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"
@@ -57,7 +58,7 @@ using namespace shark;
  *  For more information, see
  *  http://image.diku.dk/shark/sphinx_pages/build/html/rest_sources/tutorials/algorithms/kmeans.html
  *
- *  \ingroup OTBSupervised
+ *  \ingroup OTBUnsupervised
  */
 namespace otb
 {
diff --git a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModel.txx b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.txx
similarity index 100%
rename from Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModel.txx
rename to Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.txx
diff --git a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModelFactory.h b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h
similarity index 98%
rename from Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModelFactory.h
rename to Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h
index 2d439f0b92..cf0c033eb5 100644
--- a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModelFactory.h
+++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.h
@@ -26,7 +26,7 @@ namespace otb
 /** \class SharkKMeansMachineLearningModelFactory
  * \brief Creation of an instance of a SharkKMeansMachineLearningModel object using the object factory
  *
- * \ingroup OTBSupervised
+ * \ingroup OTBUnsupervised
  */
 template <class TInputValue, class TTargetValue>
 class ITK_EXPORT SharkKMeansMachineLearningModelFactory : public itk::ObjectFactoryBase
diff --git a/Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModelFactory.txx b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.txx
similarity index 100%
rename from Modules/Learning/Supervised/include/otbSharkKMeansMachineLearningModelFactory.txx
rename to Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModelFactory.txx
diff --git a/Modules/Learning/Unsupervised/otb-module.cmake b/Modules/Learning/Unsupervised/otb-module.cmake
new file mode 100644
index 0000000000..d849a22611
--- /dev/null
+++ b/Modules/Learning/Unsupervised/otb-module.cmake
@@ -0,0 +1,24 @@
+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
+  OTBSupervised
+
+  OPTIONAL_DEPENDS
+  OTBShark
+
+  TEST_DEPENDS
+  OTBTestKernel
+  OTBImageIO
+  OTBImageBase
+  OTBLearningBase
+  OTBSupervised
+
+  DESCRIPTION
+  "${DOCUMENTATION}"
+  )
diff --git a/Modules/Learning/Unsupervised/test/CMakeLists.txt b/Modules/Learning/Unsupervised/test/CMakeLists.txt
new file mode 100644
index 0000000000..1880a72167
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/CMakeLists.txt
@@ -0,0 +1,17 @@
+otb_module_test()
+set(OTBUnsupervisedTests
+  otbUnsupervisedTestDriver.cxx
+  otbMachineLearningClusteringModelCanRead.cxx
+  otbTrainMachineLearningClusteringModel.cxx
+  )
+
+
+add_executable(otbUnsupervisedTestDriver ${OTBUnsupervisedTests})
+target_link_libraries(otbUnsupervisedTestDriver ${OTBUnsupervised-Test_LIBRARIES})
+otb_module_target_label(otbUnsupervisedTestDriver)
+
+# Tests Declaration
+
+if(OTB_USE_SHARK)
+  include(tests-shark.cmake)
+endif()
diff --git a/Modules/Learning/Unsupervised/test/otbMachineLearningClusteringModelCanRead.cxx b/Modules/Learning/Unsupervised/test/otbMachineLearningClusteringModelCanRead.cxx
new file mode 100644
index 0000000000..761c2716f5
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbMachineLearningClusteringModelCanRead.cxx
@@ -0,0 +1,62 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#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> RFType;
+  RFType::Pointer classifier = RFType::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/otbTrainMachineLearningClusteringModel.cxx b/Modules/Learning/Unsupervised/test/otbTrainMachineLearningClusteringModel.cxx
new file mode 100644
index 0000000000..7eae88216b
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbTrainMachineLearningClusteringModel.cxx
@@ -0,0 +1,170 @@
+#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";
+  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 0000000000..14ca633f1c
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
@@ -0,0 +1,10 @@
+#include "otbTestMain.h"
+void RegisterTests()
+{
+#ifdef OTB_USE_SHARK
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelCanRead);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelNew);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelTrain);
+  REGISTER_TEST(otbSharkKMeansMachineLearningModelPredict);
+#endif
+}
diff --git a/Modules/Learning/Unsupervised/test/tests-shark.cmake b/Modules/Learning/Unsupervised/test/tests-shark.cmake
new file mode 100644
index 0000000000..0635d94ec2
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/tests-shark.cmake
@@ -0,0 +1,25 @@
+# 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 leTvSharkKMeansMachineLearningModelCanRead COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelPredict
+  ${INPUTDATA}/letter.scale
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_KMeansmodel.txt
+  )
+
+otb_add_test(NAME leTvSharkKMeansMachineLearningModelCanReadFail COMMAND otbUnsupervisedTestDriver
+  otbSharkKMeansMachineLearningModelPredict
+  ${INPUTDATA}/letter.scale
+  ${INPUTDATA}/Classification/otbSharkImageClassificationFilter_RFmodel.txt
+  )
+
+set_property(TEST leTvSharkKMeansMachineLearningModelCanReadFail PROPERTY WILL_FAIL true)
-- 
GitLab