diff --git a/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sam.tif b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sam.tif
new file mode 100644
index 0000000000000000000000000000000000000000..5b40c8cb58ca17168eb0b64bd9b258172603014f
--- /dev/null
+++ b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sam.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41ba89a71a9bb2af17ba1bc66f0e1197fb843b59a207a7eae6bffb44f2578dc1
+size 400
diff --git a/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid.tif b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid.tif
new file mode 100644
index 0000000000000000000000000000000000000000..6e4a1386201eff63effa649d782c66ce280c8f18
--- /dev/null
+++ b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e86975e7d76ce475074fef8fd6d276fd8bb3c5bc091b296402cbf146154dfb7f
+size 684
diff --git a/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid_measure.tif b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid_measure.tif
new file mode 100644
index 0000000000000000000000000000000000000000..68c604fc5baf6e60fbd81d6bc57ca8b24d68e4ca
--- /dev/null
+++ b/Data/Baseline/OTB/Images/apTvHySpectralAngleClassification_sid_measure.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c6285dfdf032e26076dfbde20b23d99931a49ca392a72b7e0671792d3dfa691
+size 6332
diff --git a/Documentation/Cookbook/Art/HyperspectralImages/classification.png b/Documentation/Cookbook/Art/HyperspectralImages/classification.png
new file mode 100644
index 0000000000000000000000000000000000000000..c84e24bea344beca46782e3dee9b9af5a29948e9
Binary files /dev/null and b/Documentation/Cookbook/Art/HyperspectralImages/classification.png differ
diff --git a/Documentation/Cookbook/rst/recipes/hyperspectral.rst b/Documentation/Cookbook/rst/recipes/hyperspectral.rst
index 9e89886fdf2fe302a99b22bee85e1650a60d712f..ef6fd1b451c757f5bface174383cd542542b7844 100644
--- a/Documentation/Cookbook/rst/recipes/hyperspectral.rst
+++ b/Documentation/Cookbook/rst/recipes/hyperspectral.rst
@@ -106,6 +106,68 @@ image is shown below:
 
    Resulting unmixed image, here the first three bands are displayed.
 
+Classification
+--------------
+
+A common task in hyperspectral image processing is to classify a data cube. For example
+one might want to match pixels to reference endmembers. The Spectral Angle Mapper (SAM) algorithm
+[1] does that by computing a spectral measure between each pixel and the references.
+
+The spectral angle of a pixel `x` with a reference pixel `r` is defined by : 
+
+.. math::
+    
+    sam[x, r] = cos^{-1}(\frac{<x,r>}{\|x\| * \|r\|  } )
+
+
+where `<x,r>` denotes the scalar product between x and r. 
+This is also called the spectral angle between `x` and `r`. 
+In SAM classification the spectral angle is computed for each pixel with a set of reference pixels, 
+the class associated with the pixel is the index of the reference pixel that has the lowest spectral angle.
+
+
+::
+
+    otbcli_SpectralAngleClassification -in inputImage.tif
+                                       -ie endmembers.tif
+                                       -out classification.tif
+                                       -mode sam
+
+The result of the classification is shown below : 
+
+
+.. figure:: ../Art/HyperspectralImages/classification.png
+   :width: 70%
+   :align: center
+    
+   Resulting unmixed classified image.
+
+
+Another algorithm is available in this application based on spectral information divergence [1],
+if we define the probability mass function p of a pixel x by :
+
+.. math::
+    x = [x_1, x_2 ,..., x_L ]^T \\
+
+    p = [p_1, p_2 ,..., p_L ]^T \\
+
+    p_i = \frac{x_i}{\sum_{j=1}^L x_j}
+
+
+The spectral information divergence between a pixel `x` and a reference `r` is defined by :
+
+.. math::
+    sid[x, r] = \sum_{j=1}^{L}  p_j  *log(\frac{p_j}{q_j}) + \sum_{j=1}^{L}  q_j * log(\frac{q_j}{p_j}) 
+
+
+where p and q are respectively the probability mass function of x and r. As with the SAM algorithm,
+the class associated with the pixel is the index of the reference pixel that has the lowest spectral information
+divergence.
+
+
+Note that the framework described in the
+:ref:`classification recipe<classif>` can also be applied to hyperspectral data. 
+
 
 Anomaly detection
 -----------------
@@ -183,6 +245,10 @@ The value of the threshold depends on how sensitive the anomaly detector should
    Left: Computed Rx score, right: detected anomalies (in red)
 
 
+*[1] Du, Yingzi & Chang, Chein-I & Ren, Hsuan & Chang, Chein-Chi & Jensen, James & D'Amico, Francis. (2004). 
+New Hyperspectral Discrimination Measure for Spectral Characterization. Optical Engineering - OPT ENG. 43.*
+
 .. _here: http://www.ehu.eus/ccwintco/index.php/Hyperspectral_Remote_Sensing_Scenes#Cuprite
 .. _AVIRIS: https://aviris.jpl.nasa.gov/
 .. _Pavia: http://www.ehu.eus/ccwintco/index.php/Hyperspectral_Remote_Sensing_Scenes#Pavia_University_scene
+
diff --git a/Documentation/Cookbook/rst/recipes/pbclassif.rst b/Documentation/Cookbook/rst/recipes/pbclassif.rst
index dbeeadd8abb8b9b775db2ffb3f65b834fbdea7b0..8a13d970b9ec03ff6d301b350060e1db8f166ab6 100644
--- a/Documentation/Cookbook/rst/recipes/pbclassif.rst
+++ b/Documentation/Cookbook/rst/recipes/pbclassif.rst
@@ -1,3 +1,5 @@
+.. _classif:
+
 Classification
 ==============
 
diff --git a/Modules/Applications/AppHyperspectral/app/CMakeLists.txt b/Modules/Applications/AppHyperspectral/app/CMakeLists.txt
index 062aecb339119c6aa2a0154f23b45900a75c0503..9ab5697e1ec3c38314903f24b0debdafb1bf9574 100644
--- a/Modules/Applications/AppHyperspectral/app/CMakeLists.txt
+++ b/Modules/Applications/AppHyperspectral/app/CMakeLists.txt
@@ -38,3 +38,8 @@ otb_create_application(
   SOURCES        otbEndmemberNumberEstimation.cxx
   LINK_LIBRARIES ${${otb-module}_LIBRARIES})
 
+otb_create_application(
+  NAME           SpectralAngleClassification
+  SOURCES        otbSpectralAngleClassification.cxx
+  LINK_LIBRARIES ${${otb-module}_LIBRARIES})
+
diff --git a/Modules/Applications/AppHyperspectral/app/otbSpectralAngleClassification.cxx b/Modules/Applications/AppHyperspectral/app/otbSpectralAngleClassification.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0755543feb9be40718321500af4b61bc2df8f5f0
--- /dev/null
+++ b/Modules/Applications/AppHyperspectral/app/otbSpectralAngleClassification.cxx
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2005-2020 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 "otbWrapperApplication.h"
+#include "otbWrapperApplicationFactory.h"
+
+#include "otbSpectralAngleFunctor.h"
+#include "otbFunctorImageFilter.h"
+#include "otbSpectralInformationDivergenceFunctor.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+class SpectralAngleClassification : public Application
+{
+public:
+  /** @name Standard class typedefs
+   * @{
+   */
+  using Self = SpectralAngleClassification;
+  using Superclass = Application;
+  using Pointer = itk::SmartPointer<Self>;
+  using ConstPointer = itk::SmartPointer<const Self>;
+  /** @} */
+
+  /** @name Standard macro
+   * @{
+   */
+  itkNewMacro(Self);
+  itkTypeMacro(SpectralAngleClassification, otb::Application);
+  /** @} */
+
+  using ValueType = float;
+  using ImageType = otb::VectorImage<ValueType>;
+  using PixelType = ImageType::PixelType;
+  using SAMFilterType = otb::FunctorImageFilter<otb::Functor::SpectralAngleMapperFunctor<PixelType, PixelType, PixelType>>;
+  using SIDFilterType = otb::FunctorImageFilter<otb::Functor::SpectralInformationDivergenceFunctor<PixelType, PixelType, PixelType>>;
+
+private:
+  void DoInit() override
+  {
+    SetName("SpectralAngleClassification");
+    SetDescription("Classifies an image using a spectral measure.");
+
+    // Documentation
+    SetDocLongDescription("This application computes a spectral measure on a hyperspectral data cube using a set of "
+                            "reference pixels. The image is then classified by finding for each pixel the index of its closest endmember, "
+                            "i.e. the endmember corresponding to the lowest measurement value. The output classification is "
+                            "labeled from 1 to L, L being the number of endmembers. A threshold can be set for the "
+                            "classification step, only measurement values lower than this threshold will be considered, other "
+                            "will be classified as `background values` (default value of 0). \n\n"
+                            "Two measures are available : the spectral angle mapper and the spectral information divergence. See [1] for details");
+    
+    SetDocLimitations("In sid mode, the pixels of the input image and the input endmembers should be strictly positive. \n"
+                      "The endmember image is fully loaded in memory.");
+    
+    SetDocAuthors("OTB-Team");
+    SetDocSeeAlso("VertexComponentAnalysis \n"
+      "[1] Du, Yingzi & Chang, Chein-I & Ren, Hsuan & Chang, Chein-Chi & Jensen, James & D'Amico, Francis. (2004). "
+      "New Hyperspectral Discrimination Measure for Spectral Characterization. Optical Engineering - OPT ENG. 43."
+      " 1777-1786. 10.1117/1.1766301. ");
+
+    AddDocTag(Tags::Hyperspectral);
+
+    AddParameter(ParameterType_InputImage, "in", "Input Image Filename");
+    SetParameterDescription("in", "The hyperspectral data cube input");
+
+    AddParameter(ParameterType_InputImage, "ie", "Input endmembers");
+    SetParameterDescription("ie",
+                            "The endmembers (estimated pure pixels) to "
+                            "use for unmixing. Must be stored as a multispectral image, where "
+                            "each pixel is interpreted as an endmember.");
+
+    AddParameter(ParameterType_OutputImage, "measure", "Output spectral angle values");
+    SetParameterDescription("measure",
+                            "Output image containing for each pixel from the input image the computed measure relative to each endmember");
+    MandatoryOff("measure");
+    
+    AddParameter(ParameterType_OutputImage, "out", "Output classified image");
+    SetParameterDescription("out",
+                            "Output classified image, classified pixels are labeled from 1 to L, L being the number of endmember in the image.");
+
+    MandatoryOff("out");
+    
+    AddParameter(ParameterType_Choice, "mode", "Measure used for classification");
+    SetParameterDescription("mode", "Measure used for classification");
+    MandatoryOff("mode");
+    
+    AddChoice("mode.sam", "Spectral angle mapper");
+    SetParameterDescription("mode.sam", "Spectral angle mapper (SAM) measure.");
+
+    AddChoice("mode.sid", "Spectral information divergence");
+    SetParameterDescription("mode.sid", "Spectral information divergence (SID) measure. "
+                                          "Input pixel values should be strictly positive.");
+
+
+    AddParameter(ParameterType_Float, "threshold", "Classification threshold");
+    SetParameterDescription("threshold",
+                            "Pixel with a measurement greater than this threshold relatively to "
+                            "a reference pixel are not classified. The same threshold is used for all classes.");
+    MandatoryOff("threshold");
+
+    AddParameter(ParameterType_Int, "bv", "Background value");
+    SetParameterDescription("bv",
+                            "Value of unclassified pixels in the classification image "
+                            "(this parameter is only used if a threshold is set).");
+    MandatoryOff("bv");
+    SetDefaultParameterInt("bv", 0);
+
+    AddRAMParameter();
+    SetMultiWriting(true);
+
+    // Doc example parameter settings
+    SetDocExampleParameterValue("in", "cupriteSubHsi.tif");
+    SetDocExampleParameterValue("ie", "cupriteEndmembers.tif");
+    SetDocExampleParameterValue("out", "classification.tif");
+    SetDocExampleParameterValue("measure", "measure.tif");
+    SetDocExampleParameterValue("mode", "sam");
+    SetDocExampleParameterValue("threshold", "0.1");
+
+    SetOfficialDocLink();
+  }
+
+  void DoUpdateParameters() override
+  {
+    // Nothing to do here : all parameters are independent
+  }
+
+  void DoExecute() override
+  {
+    auto inputEndmemberImage = GetParameterImage("ie");
+    inputEndmemberImage->Update();
+    itk::ImageRegionConstIterator<ImageType> it(inputEndmemberImage, inputEndmemberImage->GetLargestPossibleRegion());
+    std::vector<PixelType> endmembers;
+    
+    for (it.GoToBegin(); !it.IsAtEnd(); ++it)
+    {
+      endmembers.push_back(it.Get());
+    }
+    
+    // Process objects to keep references on the actual filter instanciated below and its output
+    itk::LightObject::Pointer filter;
+    ImageType::Pointer filterOutput;
+    
+    auto mode = GetParameterString("mode");
+    if (mode == "sam")
+    {
+      auto SAMFilter = SAMFilterType::New();
+      
+      SAMFilter->GetModifiableFunctor().SetReferencePixels(endmembers);
+      SAMFilter->SetInput(GetParameterImage("in"));
+      filter = SAMFilter;
+      filterOutput = SAMFilter->GetOutput();
+    }
+    else if (mode == "sid")
+    {
+      auto SIDFilter = SIDFilterType::New();
+      
+      SIDFilter->GetModifiableFunctor().SetReferencePixels(endmembers);
+      SIDFilter->SetInput(GetParameterImage("in"));
+      filter = SIDFilter;
+      filterOutput = SIDFilter->GetOutput();
+    }
+
+    if (HasValue("measure"))
+    {
+      SetParameterOutputImage("measure", filterOutput);
+    }
+
+    if (HasValue("out"))
+    {
+      auto threshold = HasValue("threshold") ? GetParameterFloat("threshold") 
+                                              : std::numeric_limits<ValueType>::max();
+      auto bv = GetParameterInt("bv");
+      
+      // This lambda return the index of the minimum value in a pixel, values above threshold are classified as background values.
+      auto minIndexLambda = [threshold, bv](PixelType const & pixel)
+      {
+        auto minElem = std::min_element(&pixel[0], &pixel[pixel.Size()]);
+        return static_cast<int>(*minElem < threshold ? std::distance(&pixel[0], minElem) + 1 : bv);
+      };
+      
+      auto classificationFilter = NewFunctorFilter(minIndexLambda);
+      classificationFilter->SetInput(filterOutput);
+      
+      SetParameterOutputImage("out", classificationFilter->GetOutput());
+      RegisterPipeline();
+    } 
+    else
+    {
+      RegisterPipeline();
+    }
+  }
+};
+}
+}
+
+OTB_APPLICATION_EXPORT(otb::Wrapper::SpectralAngleClassification)
diff --git a/Modules/Applications/AppHyperspectral/test/CMakeLists.txt b/Modules/Applications/AppHyperspectral/test/CMakeLists.txt
index 1a9b6ae1e8eaaff28027a0b93ca92c8b7057dbd7..7165de5422174e7f9d819cedafa11be0280c6658 100644
--- a/Modules/Applications/AppHyperspectral/test/CMakeLists.txt
+++ b/Modules/Applications/AppHyperspectral/test/CMakeLists.txt
@@ -75,3 +75,34 @@ otb_test_application(NAME  apTvHyLocalRxDetection
                               ${BASELINE}/apTvHyLocalRxDetection.tif
                               ${TEMP}/apTvHyLocalRxDetection.tif
 )
+
+
+#----------- SpectralAngleClassification TESTS ------------------------
+otb_test_application(NAME  apTvHySpectralAngleClassification_sam
+                     APP  SpectralAngleClassification
+                     OPTIONS -in ${OTB_DATA_ROOT}/Input/Hyperspectral/synthetic/hsi_cube.tif
+                             -ie ${INPUTDATA}/Hyperspectral/synthetic/endmembers.tif
+                             -out ${TEMP}/apTvHySpectralAngleClassification_sam.tif 
+                             -mode sam
+                     VALID   --compare-image ${EPSILON_9}
+                              ${BASELINE}/apTvHySpectralAngleClassification_sam.tif
+                              ${TEMP}/apTvHySpectralAngleClassification_sam.tif
+)
+
+
+#----------- LocalRxDetection TESTS ------------------------
+otb_test_application(NAME  apTvHySpectralAngleClassification_sid
+                     APP  SpectralAngleClassification
+                     OPTIONS -in ${OTB_DATA_ROOT}/Input/Hyperspectral/synthetic/hsi_cube.tif
+                             -ie ${INPUTDATA}/Hyperspectral/synthetic/endmembers.tif
+                             -out ${TEMP}/apTvHySpectralAngleClassification_sid.tif 
+                             -mode sid
+                             -measure ${TEMP}/apTvHySpectralAngleClassification_sid_measure.tif 
+                             -threshold 0.1 
+                             -bv -1
+                     VALID   --compare-n-images ${EPSILON_6} 2
+                              ${BASELINE}/apTvHySpectralAngleClassification_sid.tif
+                              ${TEMP}/apTvHySpectralAngleClassification_sid.tif
+                              ${BASELINE}/apTvHySpectralAngleClassification_sid_measure.tif
+                              ${TEMP}/apTvHySpectralAngleClassification_sid_measure.tif
+)
diff --git a/Modules/Filtering/ImageManipulation/include/otbBinarySpectralAngleFunctor.h b/Modules/Filtering/ImageManipulation/include/otbBinarySpectralAngleFunctor.h
index cf87c9b597712ceae6cd600920812a03dbd9a6c6..cc483ab61aad7b6cad8b711e828072afe1ebd660 100644
--- a/Modules/Filtering/ImageManipulation/include/otbBinarySpectralAngleFunctor.h
+++ b/Modules/Filtering/ImageManipulation/include/otbBinarySpectralAngleFunctor.h
@@ -23,12 +23,16 @@
 
 #include <algorithm>
 #include "otbMath.h"
+#include "otbSpectralAngleFunctor.h"
 
 namespace otb
 {
 /** \class BinarySpectralAngleFunctor
  *  \brief This functor computes the spectral angle between two pixels.
  *
+ *  If the pixels have different sizes, only the first components of the 
+ *  largest pixel will be considered.
+ *
  *  It can be used as a functor in a BinaryFunctorImageFilter to
  *  compute the pixel-by-pixel spectral angles.
  *
@@ -40,40 +44,24 @@ template <class TInput1, class TInput2, class TOutputValue>
 class BinarySpectralAngleFunctor
 {
 public:
-  BinarySpectralAngleFunctor()
-  {
-  }
+  BinarySpectralAngleFunctor() = default;
 
-  virtual ~BinarySpectralAngleFunctor()
-  {
-  }
+  virtual ~BinarySpectralAngleFunctor() = default;
 
   // Binary operator
-  inline TOutputValue operator()(const TInput1& a, const TInput2& b) const
+  TOutputValue operator()(const TInput1& in1, const TInput2& in2) const
   {
-    const double Epsilon      = 1E-10;
-    double       dist         = 0.0;
-    double       scalarProd   = 0.0;
-    double       norma        = 0.0;
-    double       normb        = 0.0;
-    double       sqrtNormProd = 0.0;
-    for (unsigned int i = 0; i < std::min(a.Size(), b.Size()); ++i)
+    // Compute norms.
+    auto in1Norm = 0;
+    auto in2Norm = 0;
+    auto nbIter = std::min(in1.Size(), in2.Size());
+    for (unsigned int i = 0; i < nbIter; ++i)
     {
-      scalarProd += a[i] * b[i];
-      norma += a[i] * a[i];
-      normb += b[i] * b[i];
+      in1Norm += in1[i] * in1[i];
+      in2Norm += in2[i] * in2[i];
     }
-    sqrtNormProd = std::sqrt(norma * normb);
-    if (std::abs(sqrtNormProd) < Epsilon || scalarProd / sqrtNormProd > 1)
-    {
-      dist = 0.0;
-    }
-    else
-    {
-      dist = std::acos(scalarProd / sqrtNormProd);
-    }
-
-    return static_cast<TOutputValue>(dist);
+    
+    return SpectralAngleDetails::ComputeSpectralAngle<TInput1, TInput2, TOutputValue>(in1, in1Norm, in2, in2Norm);
   }
 };
 
diff --git a/Modules/Filtering/ImageManipulation/include/otbSpectralAngleDistanceImageFilter.h b/Modules/Filtering/ImageManipulation/include/otbSpectralAngleDistanceImageFilter.h
index 5c3d09538ccc9eaf5b28293f171800e078113409..e22e7192d9cf36e2638d97e3def9810ff9045773 100644
--- a/Modules/Filtering/ImageManipulation/include/otbSpectralAngleDistanceImageFilter.h
+++ b/Modules/Filtering/ImageManipulation/include/otbSpectralAngleDistanceImageFilter.h
@@ -26,6 +26,7 @@
 namespace otb
 {
 /** \class SpectralAngleDistanceImageFilter
+ *  \deprecated
  *  \brief This filter implements the computation of the spectral angle
  * distance with respect to a reference pixel.
  *
diff --git a/Modules/Filtering/ImageManipulation/include/otbSpectralAngleFunctor.h b/Modules/Filtering/ImageManipulation/include/otbSpectralAngleFunctor.h
index 1ca9a87501aaa7f646b638b5b5384c92ecdf19af..6ad8f3a6733a522bc49eabfc0976edfafc0afd1b 100644
--- a/Modules/Filtering/ImageManipulation/include/otbSpectralAngleFunctor.h
+++ b/Modules/Filtering/ImageManipulation/include/otbSpectralAngleFunctor.h
@@ -23,16 +23,45 @@
 
 #include "otbMath.h"
 #include <algorithm>
+#include <vector>
+#include <numeric>
 
 namespace otb
 {
+namespace Functor
+{
+
+namespace SpectralAngleDetails
+{
+
+/** \fn
+ * \brief This function computes spectral angle between a pixel and a reference, the norm of these inputs.
+ * should also be given as parameter of this function.
+ * */
+template <class TInput, class TReference, class TOutput>
+TOutput ComputeSpectralAngle(TInput const & input, typename TInput ::ValueType const & inputNorm, 
+                              TReference const & reference, typename TReference::ValueType refNorm)
+{
+  auto minSize = std::min(input.Size(), reference.Size());
+  double scalarProduct = std::inner_product(&input[0], &input[minSize], &reference[0],0. );
+  auto normProd = inputNorm * refNorm;
+  if ((normProd < 1.e-12) || (scalarProduct / normProd > 1))
+  {
+    return static_cast<TOutput>(0.0);
+  }
+  else
+  {
+    return static_cast<TOutput>(std::acos(scalarProduct / normProd));
+  }
+}
+
+} // end namespace SpectralAngleDetails
+
 /** \class SpectralAngleFunctor
  *  \brief This functor computes the spectral angle according to a reference pixel.
  *
  * \ingroup OTBImageManipulation
  */
-namespace Functor
-{
 template <class TInput, class TOutputValue>
 class SpectralAngleFunctor
 {
@@ -43,64 +72,82 @@ public:
     m_ReferencePixel.Fill(1);
   }
 
-  virtual ~SpectralAngleFunctor()
-  {
-  }
+  ~SpectralAngleFunctor() = default;
+
   // Binary operator
-  inline TOutputValue operator()(const TInput& inPix) const
+  inline TOutputValue operator()(TInput const & inPix) const
   {
-    return this->Evaluate(inPix);
+    return SpectralAngleDetails::ComputeSpectralAngle<TInput, TInput, TOutputValue>(inPix, inPix.GetNorm(), m_ReferencePixel, m_RefNorm);
   }
 
-  void SetReferencePixel(TInput ref)
+  void SetReferencePixel(TInput const & ref)
   {
     m_ReferencePixel = ref;
-    m_RefNorm        = 0.0;
-    for (unsigned int i = 0; i < ref.Size(); ++i)
-    {
-      m_RefNorm += ref[i] * ref[i];
-    }
-    m_RefNorm = std::sqrt(static_cast<double>(m_RefNorm));
+    m_RefNorm = ref.GetNorm();
   }
+  
   TInput GetReferencePixel() const
   {
     return m_ReferencePixel;
   }
 
-protected:
-  // This method can be reimplemented in subclasses to actually
-  // compute the index value
-  virtual TOutputValue Evaluate(const TInput& inPix) const
+private :
+  TInput m_ReferencePixel;
+  double m_RefNorm;
+};
+
+/** \class SpectralAngleMapperFunctor
+ *  \brief This functor computes the spectral angle according to a vector of reference pixel.
+ *
+ * \ingroup OTBImageManipulation
+ */
+template <class TInput, class TReference, class TOutput>
+class SpectralAngleMapperFunctor
+{
+public:
+  SpectralAngleMapperFunctor() = default;
+  virtual ~SpectralAngleMapperFunctor() = default;
+  
+  // Binary operator
+  inline TOutput operator()(const TInput& inPix) const
   {
-    TOutputValue out;
-
-    double dist         = 0.0;
-    double scalarProd   = 0.0;
-    double normProd     = 0.0;
-    double normProd1    = 0.0;
-    double sqrtNormProd = 0.0;
-    for (unsigned int i = 0; i < std::min(inPix.Size(), m_ReferencePixel.Size()); ++i)
-    {
-      scalarProd += inPix[i] * m_ReferencePixel[i];
-      normProd1 += inPix[i] * inPix[i];
-    }
-    normProd     = normProd1 * m_RefNorm * m_RefNorm;
-    sqrtNormProd = std::sqrt(normProd);
-    if ((sqrtNormProd == 0.0) || (scalarProd / sqrtNormProd > 1))
+    TOutput res(m_ReferencePixels.size());
+    
+    auto inputNorm = inPix.GetNorm();
+    
+    for (unsigned int i = 0; i< m_ReferencePixels.size(); i++)
     {
-      dist = 0.0;
+      res[i] = SpectralAngleDetails::ComputeSpectralAngle<TInput, TInput, typename TOutput::ValueType>
+                                      (inPix, inputNorm, m_ReferencePixels[i], m_ReferenceNorm[i]);
     }
-    else
+
+    return res;
+  }
+
+  size_t OutputSize(...) const
+  {
+    return m_ReferencePixels.size();
+  }
+
+  void SetReferencePixels(std::vector<TReference> ref)
+  {
+    m_ReferencePixels = std::move(ref);
+    m_ReferenceNorm.clear();
+    // Precompute the norm of reference pixels
+    for (auto const & pixel : m_ReferencePixels)
     {
-      dist = std::acos(scalarProd / sqrtNormProd);
+      m_ReferenceNorm.push_back(pixel.GetNorm());
     }
-
-    out = static_cast<TOutputValue>(dist);
-    return out;
+  }
+  
+  std::vector<TReference> const & GetReferencePixels() const
+  {
+    return m_ReferencePixels;
   }
 
-  TInput m_ReferencePixel;
-  double m_RefNorm;
+private:
+  std::vector<TReference> m_ReferencePixels;
+  std::vector<double> m_ReferenceNorm;
 };
 
 } // end namespace functor
diff --git a/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h b/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e52e0b4a23d9b0e3d61bc633aaf78606c0901ab
--- /dev/null
+++ b/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005-2020 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 otbSpectralInformationDivergenceFunctor_h
+#define otbSpectralInformationDivergenceFunctor_h
+
+#include "otbMath.h"
+#include <algorithm>
+#include <vector>
+#include <numeric>
+
+namespace otb
+{
+namespace Functor
+{
+
+/** \class SpectralAngleFunctor
+ *  \brief This functor computes the spectral information divergence according to a reference pixel.
+ *
+ *  Du, Yingzi & Chang, Chein-I & Ren, Hsuan & Chang, Chein-Chi & Jensen, James & D'Amico, Francis. (2004). "
+      "New Hyperspectral Discrimination Measure for Spectral Characterization. Optical Engineering - OPT ENG. 43."
+      " 1777-1786. 10.1117/1.1766301.
+ * 
+ * \ingroup OTBImageManipulation
+ */
+template <class TInput, class TReference, class TOutput>
+class SpectralInformationDivergenceFunctor
+{
+public:
+  SpectralInformationDivergenceFunctor() = default;
+  virtual ~SpectralInformationDivergenceFunctor() = default;
+  
+  using OutputValueType = typename TOutput::ValueType;
+  
+  // Binary operator
+  inline TOutput operator()(const TInput& input) const
+  {
+    TOutput res;
+    res.SetSize(m_ReferenceProbabilities.size());
+    
+    auto inputProbability = ComputeProbabilityMassFunction(input);
+    
+    for (unsigned int i = 0; i< m_ReferenceProbabilities.size(); i++)
+    {
+      res[i] = ComputeSpectralInformationDivergence(inputProbability, m_ReferenceProbabilities[i]);
+    }
+
+    return res;
+  }
+
+  size_t OutputSize(...) const
+  {
+    return m_ReferenceProbabilities.size();
+  }
+
+  void SetReferencePixels(std::vector<TReference> const & ref)
+  {
+    // We only store the probability mass function associated with the reference pixels, are the latter are not needed
+    // in the sid computation.
+    m_ReferenceProbabilities.clear();
+    for (auto const & pixel : ref)
+    {
+      m_ReferenceProbabilities.push_back(ComputeProbabilityMassFunction(pixel));
+    }
+  }
+  
+private:
+  inline TInput ComputeProbabilityMassFunction(TInput const & input) const
+  {
+    for (unsigned int i = 0; i < input.Size(); i++)
+    {
+      // Input pixel should be non negative (e.g. reflectance, radiance)
+      if (input[i] <= 0) 
+      {
+        throw std::runtime_error("Input pixels of the spectral information divergence algorithm should be strictly positive.");
+      }
+    }
+    
+    return input / std::accumulate(&input[0], &input[input.Size()], 0.0);
+  }
+
+  inline OutputValueType ComputeSpectralInformationDivergence(TInput const & p, TInput const & q) const
+  {
+    assert(p.Size() == q.Size());
+    OutputValueType sid = 0.0;
+    for (unsigned int i = 0; i < p.Size(); i++)
+    {
+      // Compute SID : p[i] * std::log(p[i]/q[i]) + q[i] * std::log(q[i]/p[i]);
+      sid += (p[i] - q[i]) * std::log(p[i]/q[i]);
+    }
+    return sid;
+  }
+
+  /** Probability mass function associated with the reference pixel */ 
+  std::vector<TReference> m_ReferenceProbabilities;
+};
+
+} // end namespace functor
+} // end namespace otb
+
+#endif //otbSpectralInformationDivergenceFunctor_h
diff --git a/Modules/Filtering/ImageManipulation/include/otbSqrtSpectralAngleFunctor.h b/Modules/Filtering/ImageManipulation/include/otbSqrtSpectralAngleFunctor.h
index e5b2350c335f4a39be909e96b964469faac87d71..b708ba2ee32d7af937de34785ba8b779f720e047 100644
--- a/Modules/Filtering/ImageManipulation/include/otbSqrtSpectralAngleFunctor.h
+++ b/Modules/Filtering/ImageManipulation/include/otbSqrtSpectralAngleFunctor.h
@@ -35,25 +35,44 @@ namespace Functor
  *
  * \ingroup OTBImageManipulation
  */
-template <class TInputVectorPixel, class TOutputPixel>
-class SqrtSpectralAngleFunctor : public SpectralAngleFunctor<TInputVectorPixel, TOutputPixel>
+
+/** \class SpectralAngleFunctor
+ *  \brief This functor computes the spectral angle according to a reference pixel.
+ *
+ * \ingroup OTBImageManipulation
+ */
+template <class TInput, class TOutputValue>
+class SqrtSpectralAngleFunctor
 {
 public:
-  typedef SqrtSpectralAngleFunctor Self;
-  typedef SpectralAngleFunctor<TInputVectorPixel, TOutputPixel> Superclass;
-
   SqrtSpectralAngleFunctor()
   {
+    m_ReferencePixel.SetSize(4);
+    m_ReferencePixel.Fill(1);
   }
-  ~SqrtSpectralAngleFunctor() override
+
+  virtual ~SqrtSpectralAngleFunctor() = default;
+
+  // Binary operator
+  inline TOutputValue operator()(TInput const & inPix) const
   {
+    return std::sqrt(SpectralAngleDetails::ComputeSpectralAngle<TInput, TInput, TOutputValue>(inPix, inPix.GetNorm(), m_ReferencePixel, m_RefNorm));
   }
 
-protected:
-  TOutputPixel Evaluate(const TInputVectorPixel& inPix) const override
+  void SetReferencePixel(TInput const & ref)
   {
-    return static_cast<TOutputPixel>(std::sqrt(Superclass::Evaluate(inPix)));
+    m_ReferencePixel = ref;
+    m_RefNorm = ref.GetNorm();
   }
+  
+  TInput GetReferencePixel() const
+  {
+    return m_ReferencePixel;
+  }
+
+private :
+  TInput m_ReferencePixel;
+  double m_RefNorm;
 };
 
 } // end namespace Functor
diff --git a/Modules/Radiometry/Indices/include/otbWaterSqrtSpectralAngleImageFilter.h b/Modules/Radiometry/Indices/include/otbWaterSqrtSpectralAngleImageFilter.h
index ed9e8e56f4f767962a6ea4fff8358702467a5a00..57b9c94febaf66dc08d9726be7e91fdce8161970 100644
--- a/Modules/Radiometry/Indices/include/otbWaterSqrtSpectralAngleImageFilter.h
+++ b/Modules/Radiometry/Indices/include/otbWaterSqrtSpectralAngleImageFilter.h
@@ -23,6 +23,7 @@
 
 #include "otbSqrtSpectralAngleFunctor.h"
 #include "itkUnaryFunctorImageFilter.h"
+#include "otbRadiometricIndex.h"
 
 namespace otb
 {
@@ -38,100 +39,102 @@ namespace Functor
  *
  * \ingroup OTBIndices
  */
-template <class TInputVectorPixel, class TOutputPixel>
-class WaterSqrtSpectralAngleFunctor : public SqrtSpectralAngleFunctor<TInputVectorPixel, TOutputPixel>
+template <class TInput, class TOutput>
+class WaterSqrtSpectralAngleFunctor : public RadiometricIndex<TInput, TOutput>
 {
 public:
-  typedef WaterSqrtSpectralAngleFunctor Self;
-  typedef SqrtSpectralAngleFunctor<TInputVectorPixel, TOutputPixel> Superclass;
-  typedef TInputVectorPixel InputVectorPixelType;
-  WaterSqrtSpectralAngleFunctor()
-  {
+  using typename RadiometricIndex<TInput, TOutput>::PixelType;
 
-    // Set the channels indices
-    m_BlueIndex  = 0;
-    m_GreenIndex = 1;
-    m_RedIndex   = 2;
-    m_NIRIndex   = 3;
-
-    // Set reference water value
-    InputVectorPixelType reference;
-    reference.SetSize(4);
-    reference[0] = 136.0;
-    reference[1] = 132.0;
-    reference[2] = 47.0;
-    reference[3] = 24.0;
-    this->SetReferenceWaterPixel(reference);
+  WaterSqrtSpectralAngleFunctor() 
+        : RadiometricIndex<TInput, TOutput>({CommonBandNames::BLUE, CommonBandNames::GREEN, CommonBandNames::RED, CommonBandNames::NIR} )
+  {
+    // Set default reference water value
+    m_ReferencePixel.SetSize(4);
+    m_ReferencePixel[0] = 136.0;
+    m_ReferencePixel[1] = 132.0;
+    m_ReferencePixel[2] = 47.0;
+    m_ReferencePixel[3] = 24.0;
+    m_RefNorm = m_ReferencePixel.GetNorm();
   }
-  ~WaterSqrtSpectralAngleFunctor() override
+  
+  virtual ~WaterSqrtSpectralAngleFunctor() = default;
+
+  /** Set Reference Pixel 
+   * \param ref : The new reference pixel, the band indices will be used.
+   * */
+  void SetReferenceWaterPixel(PixelType ref)
   {
+    assert(m_ReferencePixel.Size() == 4);
+    m_ReferencePixel[0] = this->Value(CommonBandNames::BLUE, ref);
+    m_ReferencePixel[1] = this->Value(CommonBandNames::GREEN, ref);
+    m_ReferencePixel[2] = this->Value(CommonBandNames::RED, ref);
+    m_ReferencePixel[3] = this->Value(CommonBandNames::NIR, ref);
+    m_RefNorm = m_ReferencePixel.GetNorm();
   }
 
-  /** Set Reference Pixel */
-  void SetReferenceWaterPixel(InputVectorPixelType ref)
+  // Binary operator
+  inline TOutput operator()(PixelType const & inPix) const override
   {
-    if (ref.GetSize() != 4)
-    {
-    }
-    InputVectorPixelType reference;
-    reference.SetSize(4);
-    reference[m_BlueIndex]  = ref[0];
-    reference[m_GreenIndex] = ref[1];
-    reference[m_RedIndex]   = ref[2];
-    reference[m_NIRIndex]   = ref[3];
-    this->SetReferencePixel(reference);
+    PixelType pix(4);
+    pix[0] = this->Value(CommonBandNames::BLUE, inPix);
+    pix[1] = this->Value(CommonBandNames::GREEN, inPix);
+    pix[2] = this->Value(CommonBandNames::RED, inPix);
+    pix[3] = this->Value(CommonBandNames::NIR, inPix);
+
+    return std::sqrt(SpectralAngleDetails::ComputeSpectralAngle<PixelType, PixelType, TOutput>
+                                                                  (pix, pix.GetNorm(),
+                                                                   m_ReferencePixel, m_RefNorm));
   }
 
-  /** Getters and setters */
+  /**@{*/ 
+  /** Legacy getters and setters (use SetBandIndex/GetBandIndex instead)
+   * \deprecated
+   * */
   void SetBlueChannel(unsigned int channel)
   {
-    m_BlueIndex = channel;
+    this->SetBandIndex(CommonBandNames::BLUE, channel + 1);
   }
   unsigned int GetBlueChannel() const
   {
-    return m_BlueIndex;
+    return this->GetBandIndex(CommonBandNames::BLUE) - 1;
   }
   void SetGreenChannel(unsigned int channel)
   {
-    m_GreenIndex = channel;
+    this->SetBandIndex(CommonBandNames::GREEN, channel + 1);
   }
   unsigned int GetGreenChannel() const
   {
-    return m_GreenIndex;
+    return this->GetBandIndex(CommonBandNames::GREEN) - 1;
   }
   void SetRedChannel(unsigned int channel)
   {
-    m_RedIndex = channel;
+    this->SetBandIndex(CommonBandNames::RED, channel + 1);
   }
   unsigned int GetRedChannel() const
   {
-    return m_RedIndex;
+     return this->GetBandIndex(CommonBandNames::RED) - 1;
   }
   void SetNIRChannel(unsigned int channel)
   {
-    m_NIRIndex = channel;
+    this->SetBandIndex(CommonBandNames::NIR, channel + 1);
   }
   unsigned int GetNIRChannel() const
   {
-    return m_NIRIndex;
+    return this->GetBandIndex(CommonBandNames::NIR) - 1;
   }
+  /**@}*/
+  
 
-protected:
-  inline TOutputPixel Evaluate(const TInputVectorPixel& inPix) const override
-  {
-    return static_cast<TOutputPixel>(Superclass::Evaluate(inPix));
-  }
+private:
 
-  /** Channels */
-  int m_BlueIndex;
-  int m_GreenIndex;
-  int m_RedIndex;
-  int m_NIRIndex;
+  PixelType m_ReferencePixel;
+  double m_RefNorm;
 };
 } // End namespace Functor
 
 
 /** \class WaterSqrtSpectralAngleImageFilter
+ *  \deprecated
  *  \brief Compute a radiometric water indice
  *
  *  This filter calculates a pixel wise water indice by calculating
@@ -147,11 +150,11 @@ protected:
  *  probability of water.
  *
  *  \sa SpectralAngleDistanceImageFilter
- *
+ * 
  * \ingroup OTBIndices
  */
 template <class TInputVectorImage, class TOutputImage,
-          class TFunction = Functor::WaterSqrtSpectralAngleFunctor<typename TInputVectorImage::PixelType, typename TOutputImage::PixelType>>
+          class TFunction = Functor::WaterSqrtSpectralAngleFunctor<double, typename TOutputImage::PixelType>>
 class ITK_EXPORT WaterSqrtSpectralAngleImageFilter : public itk::UnaryFunctorImageFilter<TInputVectorImage, TOutputImage, TFunction>
 {
 public:
diff --git a/Modules/Radiometry/Indices/test/otbWaterSqrtSpectralAngleImageFilter.cxx b/Modules/Radiometry/Indices/test/otbWaterSqrtSpectralAngleImageFilter.cxx
index 589a168fd9c12e9b6ac7826e670384efc9f87111..83f81eebade31d3016f5e4bbb85f34e3be27c1b1 100644
--- a/Modules/Radiometry/Indices/test/otbWaterSqrtSpectralAngleImageFilter.cxx
+++ b/Modules/Radiometry/Indices/test/otbWaterSqrtSpectralAngleImageFilter.cxx
@@ -39,6 +39,7 @@ int otbWaterSqrtSpectralAngleImageFilter(int itkNotUsed(argc), char* argv[])
 
   // Instantiating objects
   WaterSqrtSpectralAngleImageFilterType::Pointer filter = WaterSqrtSpectralAngleImageFilterType::New();
+  
   ReaderType::Pointer                            reader = ReaderType::New();
   WriterType::Pointer                            writer = WriterType::New();
 
@@ -60,7 +61,7 @@ int otbWaterSqrtSpectralAngleImageFilter(int itkNotUsed(argc), char* argv[])
 
   filter->SetInput(reader->GetOutput());
   writer->SetInput(filter->GetOutput());
+  
   writer->Update();
-
   return EXIT_SUCCESS;
 }
diff --git a/Modules/Segmentation/CCOBIA/include/otbConnectedComponentMuParserFunctor.h b/Modules/Segmentation/CCOBIA/include/otbConnectedComponentMuParserFunctor.h
index 26e6b2034bfbda61e6e1235f0d05a6d489142d1b..f9e3ed36cb2e022746c436403bc136e4e216ff72 100644
--- a/Modules/Segmentation/CCOBIA/include/otbConnectedComponentMuParserFunctor.h
+++ b/Modules/Segmentation/CCOBIA/include/otbConnectedComponentMuParserFunctor.h
@@ -25,7 +25,7 @@
 
 #include "otbParser.h"
 #include "otbMacro.h"
-
+#include "otbBinarySpectralAngleFunctor.h"
 
 #include <vnl/algo/vnl_lsqr.h>
 #include <vnl/vnl_sparse_matrix_linear_system.h>
@@ -115,31 +115,9 @@ public:
     m_IntensityP2 = m_IntensityP2 / (static_cast<double>(m_NbOfBands));
 
     m_Distance = std::sqrt(m_Distance);
-
-    // compute spectralAngle
-    double scalarProd = 0.0;
-    double normProd   = 0.0;
-    double normProd1  = 0.0;
-    double normProd2  = 0.0;
-
-    for (unsigned int i = 0; i < p1.Size(); ++i)
-    {
-      scalarProd += p1[i] * p2[i];
-      normProd1 += p1[i] * p1[i];
-      normProd2 += p2[i] * p2[i];
-    }
-    normProd = normProd1 * normProd2;
-
-    if (normProd == 0.0)
-    {
-      m_SpectralAngle = 0.0;
-    }
-    else
-    {
-      m_SpectralAngle = std::acos(scalarProd / std::sqrt(normProd));
-    }
-
-    //
+    
+    BinarySpectralAngleFunctor<TInput, TInput, double> spectralAngleFunctor;
+    m_SpectralAngle = spectralAngleFunctor(p1, p2);
 
     value = m_Parser->Eval();