diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt
index 7f38866ae252ad4207f2e48624f125772e5e50b2..94e558f6e59ac549e7d84ba6614027b607781661 100644
--- a/Modules/Applications/AppClassification/test/CMakeLists.txt
+++ b/Modules/Applications/AppClassification/test/CMakeLists.txt
@@ -527,6 +527,18 @@ otb_test_application(NAME apTvComputeConfusionMatrixExtraProducedLabelsR
   ${OTBAPP_BASELINE_FILES}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv
   ${TEMP}/apTvComputeConfusionMatrixExtraProdLabelsROut.csv)
 
+#----------- ComputeContingencyTable TESTS ----------------
+
+otb_test_application(NAME apTvComputeContingencyTableExtraProducedLabelsR
+  APP  ComputeConfusionMatrix
+  OPTIONS -in ${INPUTDATA}/Classification/clLabeledImageQB456_1_NoData_255.tif
+  -ref raster
+  -ref.raster.in ${INPUTDATA}/Classification/QB_1_ortho_C8.tif
+  -format contingencytable
+  -out ${TEMP}/apTvComputeContingencyTableExtraProdLabelsROut.csv
+  VALID   --compare-ascii ${NOTOL}
+  ${OTBAPP_BASELINE_FILES}/apTvComputeContingencyTableExtraProdLabelsROut.csv
+  ${TEMP}/apTvComputeContingencyTableExtraProdLabelsROut.csv)
 
 #----------- FusionOfClassifications TESTS ----------------
 otb_test_application(NAME apTvFusionOfClassificationsDSPrecision6Inputs
diff --git a/Modules/Learning/Unsupervised/test/otbContingencyTableCalculatorTest.cxx b/Modules/Learning/Unsupervised/test/otbContingencyTableCalculatorTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..41d2aa45a40766a9e5ffb981dba108b54f662130
--- /dev/null
+++ b/Modules/Learning/Unsupervised/test/otbContingencyTableCalculatorTest.cxx
@@ -0,0 +1,244 @@
+/*=========================================================================
+
+  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 "itkListSample.h"
+#include "otbContingencyTableCalculator.h"
+
+int otbContingencyTableCalculatorNew(int itkNotUsed(argc), char * itkNotUsed(argv) [])
+{
+
+  typedef int                                             ClassLabelType;
+  typedef otb::ContingencyTableCalculator<ClassLabelType> CalculatorType;
+
+  CalculatorType::Pointer calculator = CalculatorType::New();
+
+  return EXIT_SUCCESS;
+}
+
+
+
+int otbContingencyTableCalculatorSetListSamples(int argc, char* argv[])
+{
+
+  if (argc != 3)
+    {
+    std::cerr << "Usage: " << argv[0] << " nbSamples nbClasses " << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  typedef int                                             ClassLabelType;
+  typedef itk::VariableLengthVector<ClassLabelType>       PLabelType;
+  typedef itk::Statistics::ListSample<PLabelType>         PListLabelType;
+  typedef itk::FixedArray<ClassLabelType, 1>              RLabelType;
+  typedef itk::Statistics::ListSample<RLabelType>         RListLabelType;
+  typedef otb::ContingencyTableCalculator<ClassLabelType> CalculatorType;
+
+  CalculatorType::Pointer calculator = CalculatorType::New();
+
+  RListLabelType::Pointer refLabels = RListLabelType::New();
+  PListLabelType::Pointer prodLabels = PListLabelType::New();
+
+  // Set the measurement vector size for the list sample labels
+  refLabels->SetMeasurementVectorSize(1);
+  prodLabels->SetMeasurementVectorSize(1);
+
+  int nbSamples = atoi(argv[1]);
+  int nbClasses = atoi(argv[2]);
+
+  for (int i = 0; i < nbSamples; ++i)
+    {
+    ClassLabelType label = (i % nbClasses) + 1;
+    PLabelType plab;
+    plab.SetSize(1);
+    plab[0] = label;
+    refLabels->PushBack(label);
+    prodLabels->PushBack(plab);
+    }
+
+  calculator->Compute( refLabels->Begin(), refLabels->End(), prodLabels->Begin(), prodLabels->End() );
+
+  return EXIT_SUCCESS;
+}
+
+int otbContingencyTableCalculatorCompute(int argc, char* argv[])
+{
+
+  if (argc != 3)
+    {
+    std::cerr << "Usage: " << argv[0] << " nbSamples nbRefClasses " << std::endl;
+    return EXIT_FAILURE;
+    }
+
+
+  typedef int                                             ClassLabelType;
+  typedef itk::VariableLengthVector<ClassLabelType>       PLabelType;
+  typedef itk::Statistics::ListSample<PLabelType>         PListLabelType;
+  typedef itk::FixedArray<ClassLabelType, 1>              RLabelType;
+  typedef itk::Statistics::ListSample<RLabelType>         RListLabelType;
+  typedef otb::ContingencyTableCalculator<ClassLabelType> CalculatorType;
+  typedef CalculatorType::ContingencyTableType          ContingencyTableType;
+
+  CalculatorType::Pointer calculator = CalculatorType::New();
+
+  RListLabelType::Pointer refLabels = RListLabelType::New();
+  PListLabelType::Pointer prodLabels = PListLabelType::New();
+
+  // Set the measurement vector size for the list sample labels
+  refLabels->SetMeasurementVectorSize(1);
+  prodLabels->SetMeasurementVectorSize(1);
+
+  int nbSamples = atoi(argv[1]);
+  int nbRefClasses = atoi(argv[2]);
+  int nbProdClasses = nbSamples;
+
+  for (int i = 0; i < nbSamples; ++i)
+    {
+    int label;
+
+    label = (i % nbProdClasses) + 1;
+
+    PLabelType plab;
+    RLabelType rlab;
+    plab.SetSize(1);
+
+    plab[0] = label > nbProdClasses ? nbProdClasses : label;
+    rlab[0] = label > nbRefClasses ? nbRefClasses : label;
+
+    refLabels->PushBack(rlab);
+    prodLabels->PushBack(plab);
+    }
+
+  calculator->Compute( refLabels->Begin(), refLabels->End(), prodLabels->Begin(), prodLabels->End() );
+  ContingencyTableType confmat = calculator->GetContingencyTable();
+
+
+  if( static_cast<int>(calculator->GetNumberOfRefClasses()) != nbRefClasses )
+    {
+    std::cerr << "Wrong number of reference classes " << calculator->GetNumberOfRefClasses() << " != " << nbRefClasses
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  if( static_cast<int>(calculator->GetNumberOfProdClasses()) != nbProdClasses )
+    {
+    std::cerr << "Wrong number of produced classes " << calculator->GetNumberOfProdClasses() << " != " << nbProdClasses
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  if( static_cast<int>(calculator->GetNumberOfSamples()) != nbSamples )
+    {
+    std::cerr << "Wrong number of samples" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+
+  std::cout << "contingency table" << std::endl  << confmat << std::endl;
+
+  return EXIT_SUCCESS;
+}
+
+
+
+int otbContingencyTableCalculatorComputeWithBaseline(int itkNotUsed(argc), char* itkNotUsed(argv) [])
+{
+  typedef int ClassLabelType;
+  typedef itk::VariableLengthVector<ClassLabelType> PLabelType;
+  typedef itk::Statistics::ListSample<PLabelType> PListLabelType;
+  typedef itk::FixedArray<ClassLabelType, 1> RLabelType;
+  typedef itk::Statistics::ListSample<RLabelType> RListLabelType;
+  typedef otb::ContingencyTableCalculator<ClassLabelType> CalculatorType;
+
+  CalculatorType::Pointer calculator = CalculatorType::New();
+
+  RListLabelType::Pointer refLabels = RListLabelType::New();
+  PListLabelType::Pointer prodLabels = PListLabelType::New();
+
+  unsigned int nbSamples = 12;
+
+  // Reference samples: a b c d a b c d a b c d
+  // Classified reference samples: a c c d d b c d d d d c
+  std::vector<ClassLabelType> labelsUniverse, labelsClassified;
+
+  labelsUniverse.push_back( 'a' );
+  labelsUniverse.push_back( 'b' );
+  labelsUniverse.push_back( 'b' );
+  labelsUniverse.push_back( 'b' );
+  labelsUniverse.push_back( 'c' );
+  labelsUniverse.push_back( 'd' );
+  labelsUniverse.push_back( 'd' );
+  labelsUniverse.push_back( 'd' );
+  labelsUniverse.push_back( 'd' );
+  labelsUniverse.push_back( 'c' );
+  labelsUniverse.push_back( 'c' );
+  labelsUniverse.push_back( 'z' );
+
+  labelsClassified.push_back( 'a' );
+  labelsClassified.push_back( 'b' );
+  labelsClassified.push_back( 'c' );
+  labelsClassified.push_back( 'd' );
+  labelsClassified.push_back( 'd' );
+  labelsClassified.push_back( 'y' );
+  labelsClassified.push_back( 'c' );
+  labelsClassified.push_back( 'u' );
+  labelsClassified.push_back( 'd' );
+  labelsClassified.push_back( 'k' );
+  labelsClassified.push_back( 'd' );
+  labelsClassified.push_back( 'c' );
+
+  for( unsigned int i = 0; i < nbSamples; ++i )
+    {
+    ClassLabelType label = labelsUniverse[i];
+    PLabelType plab;
+    plab.SetSize( 1 );
+    plab[0] = labelsClassified[i];
+    if( i == 0 )
+      {
+      prodLabels->SetMeasurementVectorSize( itk::NumericTraits<PLabelType>::GetLength( plab ) );
+      }
+    refLabels->PushBack( label );
+    prodLabels->PushBack( plab );
+    }
+
+  calculator->Compute( refLabels->Begin(), refLabels->End(), prodLabels->Begin(), prodLabels->End() );
+  CalculatorType::ContingencyTableType confmat = calculator->GetContingencyTable();
+
+  std::cout << std::endl;
+  std::cout << "contingency Table" << std::endl << confmat << std::endl;
+  unsigned int nbRefClasses = 5;
+  unsigned int nbProdClasses = 7;
+
+  if( calculator->GetNumberOfRefClasses() != nbRefClasses )
+    {
+    std::cerr << "Wrong number of reference classes " << calculator->GetNumberOfRefClasses() << " != " << nbRefClasses
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  if( calculator->GetNumberOfProdClasses() != nbProdClasses )
+    {
+    std::cerr << "Wrong number of produced classes " << calculator->GetNumberOfProdClasses() << " != " << nbProdClasses
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  if( calculator->GetNumberOfSamples() != nbSamples )
+    {
+    std::cerr << "Wrong number of samples" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
+
diff --git a/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx b/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
index 14ca633f1cfaecbd96a348b26c7c37df1fc46f7c..30b0b20f1904a20aad0865ceae4e944d9c391dc8 100644
--- a/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
+++ b/Modules/Learning/Unsupervised/test/otbUnsupervisedTestDriver.cxx
@@ -1,6 +1,12 @@
 #include "otbTestMain.h"
 void RegisterTests()
 {
+
+  REGISTER_TEST(otbContingencyTableCalculatorNew);
+  REGISTER_TEST(otbContingencyTableCalculatorSetListSamples);
+  REGISTER_TEST(otbContingencyTableCalculatorCompute);
+  REGISTER_TEST(otbContingencyTableCalculatorComputeWithBaseline);
+
 #ifdef OTB_USE_SHARK
   REGISTER_TEST(otbSharkKMeansMachineLearningModelCanRead);
   REGISTER_TEST(otbSharkKMeansMachineLearningModelNew);