From 90d4151db8197442a7263a5321bfabde65c96d4e Mon Sep 17 00:00:00 2001
From: Julien Michel <julien.michel@c-s.fr>
Date: Thu, 17 Jan 2008 16:43:43 +0000
Subject: [PATCH] =?UTF-8?q?Classification=20multi=C3=A9chelle=20des=20stru?=
 =?UTF-8?q?ctures=20convexes=20et=20concaves=20de=20l'image?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../otbQuaternaryFunctorImageFilter.h         | 171 ++++++++++++
 .../otbQuaternaryFunctorImageFilter.txx       | 178 +++++++++++++
 ...ScaleConvexOrConcaveClassificationFilter.h | 246 ++++++++++++++++++
 Testing/Code/MultiScale/CMakeLists.txt        |  25 ++
 ...aleConvexOrConcaveClassificationFilter.cxx | 111 ++++++++
 ...ConvexOrConcaveClassificationFilterNew.cxx |  40 +++
 .../Code/MultiScale/otbMultiScaleTests3.cxx   |   2 +
 ...ativeToMultiScaleCharacteristicsFilter.cxx |   2 +-
 8 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100755 Code/BasicFilters/otbQuaternaryFunctorImageFilter.h
 create mode 100755 Code/BasicFilters/otbQuaternaryFunctorImageFilter.txx
 create mode 100644 Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.h
 create mode 100644 Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.cxx
 create mode 100644 Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilterNew.cxx

diff --git a/Code/BasicFilters/otbQuaternaryFunctorImageFilter.h b/Code/BasicFilters/otbQuaternaryFunctorImageFilter.h
new file mode 100755
index 0000000000..6d9a140b0a
--- /dev/null
+++ b/Code/BasicFilters/otbQuaternaryFunctorImageFilter.h
@@ -0,0 +1,171 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+  Some parts of this code are derived from ITK. See ITKCopyright.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 __itkQuaternaryFunctorImageFilter_h
+#define __itkQuaternaryFunctorImageFilter_h
+
+#include "itkInPlaceImageFilter.h"
+#include "itkImageRegionIteratorWithIndex.h"
+
+namespace otb
+{
+  
+/** \class QuaternaryFunctorImageFilter
+ * \brief Implements pixel-wise generic operation of four images.
+ *
+ * This class is parameterized over the types of the four input images
+ * and the type of the output image.  It is also parameterized by the
+ * operation to be applied, using a Functor style.
+ * 
+ * \sa TernaryFunctorImageFilter,BinaryFunctorImageFilter UnaryFunctorImageFilter
+ *
+ * \ingroup IntensityImageFilters Multithreaded
+ */
+template <class TInputImage1, class TInputImage2, 
+          class TInputImage3, class TInputImage4,
+          class TOutputImage, class TFunction    >
+class ITK_EXPORT QuaternaryFunctorImageFilter :
+    public itk::InPlaceImageFilter<TInputImage1,TOutputImage> 
+{
+public:
+  /** Standard class typedefs. */
+  typedef QuaternaryFunctorImageFilter  Self;
+  typedef itk::InPlaceImageFilter<TInputImage1,TOutputImage>   Superclass;
+  typedef itk::SmartPointer<Self>        Pointer;
+  typedef itk::SmartPointer<const Self>  ConstPointer;
+
+  /** Method for creation through the object factory. */
+  itkNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(QuaternaryFunctorImageFilter, InPlaceImageFilter);
+
+  /** Some typedefs. */
+  typedef TFunction   FunctorType;
+  typedef TInputImage1 Input1ImageType;
+  typedef typename Input1ImageType::ConstPointer Input1ImagePointer;
+  typedef typename Input1ImageType::RegionType Input1ImageRegionType; 
+  typedef typename Input1ImageType::PixelType Input1ImagePixelType; 
+  typedef TInputImage2 Input2ImageType;
+  typedef typename Input2ImageType::ConstPointer Input2ImagePointer;
+  typedef typename Input2ImageType::RegionType Input2ImageRegionType; 
+  typedef typename Input2ImageType::PixelType Input2ImagePixelType; 
+  typedef TInputImage3 Input3ImageType;
+  typedef typename Input3ImageType::ConstPointer Input3ImagePointer;
+  typedef typename Input3ImageType::RegionType Input3ImageRegionType; 
+  typedef typename Input3ImageType::PixelType Input3ImagePixelType; 
+  typedef TInputImage4 Input4ImageType;
+  typedef typename Input4ImageType::ConstPointer Input4ImagePointer;
+  typedef typename Input4ImageType::RegionType Input4ImageRegionType; 
+  typedef typename Input4ImageType::PixelType Input4ImagePixelType; 
+  typedef TOutputImage OutputImageType;
+  typedef typename OutputImageType::Pointer OutputImagePointer;
+  typedef typename OutputImageType::RegionType OutputImageRegionType;
+  typedef typename OutputImageType::PixelType OutputImagePixelType;
+
+  /** Connect one of the operands for pixel-wise addition. */
+  void SetInput1( const TInputImage1 *image1);
+
+  /** Connect one of the operands for pixel-wise addition. */
+  void SetInput2( const TInputImage2 *image2);
+
+  /** Connect one of the operands for pixel-wise addition. */
+  void SetInput3( const TInputImage3 *image3);
+
+  /** Connect one of the operands for pixel-wise addition. */
+  void SetInput4( const TInputImage4 *image4);
+
+  /** Get the functor object.  The functor is returned by reference.
+   * (Functors do not have to derive from itk::LightObject, so they do
+   * not necessarily have a reference count. So we cannot return a
+   * SmartPointer). */
+  FunctorType& GetFunctor(void) { return m_Functor; };
+
+  /** Get the functor object.  The functor is returned by reference.
+   * (Functors do not have to derive from itk::LightObject, so they do
+   * not necessarily have a reference count. So we cannot return a
+   * SmartPointer.) */
+  const FunctorType& GetFunctor() const
+    {
+    return m_Functor;
+    };
+
+  /** Set the functor object.  This replaces the current Functor with a
+   * copy of the specified Functor. This allows the user to specify a
+   * functor that has ivars set differently than the default functor.
+   * This method requires an operator==() be defined on the functor
+   * (or the compiler's default implementation of operator==() being
+   * appropriate). */
+  void SetFunctor(const FunctorType& functor)
+    {
+      if (! (functor == m_Functor) )
+        {
+        m_Functor = functor;
+        this->Modified();
+        }
+    }
+
+  /** Image dimensions */
+  itkStaticConstMacro(Input1ImageDimension, unsigned int,
+                      TInputImage1::ImageDimension);
+  itkStaticConstMacro(Input2ImageDimension, unsigned int,
+                      TInputImage2::ImageDimension);
+  itkStaticConstMacro(Input3ImageDimension, unsigned int,
+                      TInputImage3::ImageDimension);
+  itkStaticConstMacro(Input4ImageDimension, unsigned int,
+                      TInputImage4::ImageDimension);
+  itkStaticConstMacro(OutputImageDimension, unsigned int,
+                      TOutputImage::ImageDimension);
+
+protected:
+  QuaternaryFunctorImageFilter();
+  virtual ~QuaternaryFunctorImageFilter() {};
+
+  /** Validate the presence of all three inputs. If one or more inputs
+   * are missing, throw an exception. */
+  void BeforeThreadedGenerateData();
+
+  /** QuaternaryFunctorImageFilter can be implemented as a multithreaded filter.
+   * Therefore, this implementation provides a ThreadedGenerateData() routine
+   * which is called for each processing thread. The output image data is
+   * allocated automatically by the superclass prior to calling
+   * ThreadedGenerateData().  ThreadedGenerateData can only write to the
+   * portion of the output image specified by the parameter
+   * "outputRegionForThread"
+   *
+   * \sa ImageToImageFilter::ThreadedGenerateData(),
+   *     ImageToImageFilter::GenerateData()  */
+  void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+                            int threadId );
+
+private:
+  QuaternaryFunctorImageFilter(const Self&); //purposely not implemented
+  void operator=(const Self&); //purposely not implemented
+
+  FunctorType m_Functor;
+};
+
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbQuaternaryFunctorImageFilter.txx"
+#endif
+
+#endif
diff --git a/Code/BasicFilters/otbQuaternaryFunctorImageFilter.txx b/Code/BasicFilters/otbQuaternaryFunctorImageFilter.txx
new file mode 100755
index 0000000000..bfb0ac8414
--- /dev/null
+++ b/Code/BasicFilters/otbQuaternaryFunctorImageFilter.txx
@@ -0,0 +1,178 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+  Some parts of this code are derived from ITK. See ITKCopyright.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 _otbQuaternaryFunctorImageFilter_txx
+#define _otbQuaternaryFunctorImageFilter_txx
+
+#include "otbQuaternaryFunctorImageFilter.h"
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionConstIterator.h"
+#include "itkProgressReporter.h"
+
+namespace otb
+{
+
+/**
+ * Constructor
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::QuaternaryFunctorImageFilter()
+{
+  this->InPlaceOff();
+}
+
+
+/**
+ * Connect one of the operands for pixel-wise addition
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::SetInput1( const TInputImage1 *image1 ) 
+{
+  // The ProcessObject is not const-correct so the const_cast is required here
+  SetNthInput( 0, const_cast<TInputImage1 *>( image1 ) );
+}
+
+
+/**
+ * Connect one of the operands for pixel-wise addition
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::SetInput2( const TInputImage2 *image2 ) 
+{
+  // The ProcessObject is not const-correct so the const_cast is required here
+  SetNthInput( 1, const_cast<TInputImage2 *>( image2 ) );
+}
+
+
+
+/**
+ * Connect one of the operands for pixel-wise addition
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::SetInput3( const TInputImage3 *image3 ) 
+{
+  // The ProcessObject is not const-correct so the const_cast is required here
+  SetNthInput( 2, const_cast<TInputImage3 *>( image3 ) );
+}
+
+
+/**
+ * Connect one of the operands for pixel-wise addition
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::SetInput4( const TInputImage4 *image4 ) 
+{
+  // The ProcessObject is not const-correct so the const_cast is required here
+  SetNthInput( 3, const_cast<TInputImage4 *>( image4 ) );
+}
+
+/**
+ * BeforeThreadedGenerateData function. Validate inputs
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::BeforeThreadedGenerateData()
+{
+  Input1ImagePointer inputPtr1
+    = dynamic_cast<const TInputImage1*>((itk::ProcessObject::GetInput(0)));
+  Input2ImagePointer inputPtr2
+    = dynamic_cast<const TInputImage2*>((itk::ProcessObject::GetInput(1)));
+  Input3ImagePointer inputPtr3
+    = dynamic_cast<const TInputImage3*>((itk::ProcessObject::GetInput(2)));
+  Input4ImagePointer inputPtr4
+    = dynamic_cast<const TInputImage4*>((itk::ProcessObject::GetInput(4)));
+  if (inputPtr1.IsNull() || inputPtr2.IsNull() || inputPtr3.IsNull() ||inputPtr4.IsNull())
+    {
+      itkExceptionMacro(<< "At least one input is missing."
+			<< " Input1 is " << inputPtr1.GetPointer() << ", "
+			<< " Input2 is " << inputPtr2.GetPointer() << ", "
+			<< " Input3 is " << inputPtr3.GetPointer() << ", "
+			<< " Input4 is " << inputPtr4.GetPointer());
+                     
+    }
+}
+
+/**
+ * ThreadedGenerateData function. Performs the pixel-wise addition
+ */
+template < class TInputImage1, class TInputImage2,class TInputImage3, 
+	   class TInputImage4, class TOutputImage, class TFunction  >
+void
+QuaternaryFunctorImageFilter<TInputImage1,TInputImage2,TInputImage3,TInputImage4,TOutputImage,TFunction>
+::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread,
+                        int threadId)
+{
+  // We use dynamic_cast since inputs are stored as DataObjects.  The
+  // ImageToImageFilter::GetInput(int) always returns a pointer to a
+  // TInputImage1 so it cannot be used for the second or third input.
+  Input1ImagePointer inputPtr1
+    = dynamic_cast<const TInputImage1*>((itk::ProcessObject::GetInput(0)));
+  Input2ImagePointer inputPtr2
+    = dynamic_cast<const TInputImage2*>((itk::ProcessObject::GetInput(1)));
+  Input3ImagePointer inputPtr3 
+    = dynamic_cast<const TInputImage3*>((itk::ProcessObject::GetInput(2)));
+  Input4ImagePointer inputPtr4
+    = dynamic_cast<const TInputImage4*>((itk::ProcessObject::GetInput(3)));
+  OutputImagePointer outputPtr = this->GetOutput(0);
+  
+  itk::ImageRegionConstIterator<TInputImage1> inputIt1(inputPtr1, outputRegionForThread);
+  itk::ImageRegionConstIterator<TInputImage2> inputIt2(inputPtr2, outputRegionForThread);
+  itk::ImageRegionConstIterator<TInputImage3> inputIt3(inputPtr3, outputRegionForThread);
+  itk::ImageRegionConstIterator<TInputImage4> inputIt4(inputPtr4, outputRegionForThread);
+  itk::ImageRegionIterator<TOutputImage> outputIt(outputPtr, outputRegionForThread);
+
+  itk::ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
+
+  inputIt1.GoToBegin();
+  inputIt2.GoToBegin();
+  inputIt3.GoToBegin();
+  outputIt.GoToBegin();
+
+
+  while( !inputIt1.IsAtEnd() ) 
+    {
+    outputIt.Set( m_Functor(inputIt1.Get(), inputIt2.Get(), inputIt3.Get(), inputIt4.Get()) );
+    ++inputIt1;
+    ++inputIt2;
+    ++inputIt3;
+    ++inputIt4;
+    ++outputIt;
+    progress.CompletedPixel(); // potential exception thrown here
+    }
+}
+} // end namespace otb
+
+#endif
diff --git a/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.h b/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.h
new file mode 100644
index 0000000000..455b74e94f
--- /dev/null
+++ b/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.h
@@ -0,0 +1,246 @@
+/*=========================================================================
+
+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 _otbConvexOrConcaveClassificationFilter_h
+#define _otbConvexOrConcaveClassificationFilter_h
+
+#include "otbQuaternaryFunctorImageFilter.h"
+namespace otb
+{
+namespace Functor
+  {
+    /** \class MultiScaleConvexOrConcaveDecisionRule
+     *  \brief This Functor apply a classification rule on two membership value along with two labels.
+     *
+     * \par
+     * This algorithm is based on the following publication:
+     * \par
+     * Martino Pesaresi and Jon Alti Benediktsson, Member, IEEE:
+     * A new approach for the morphological segmentation of high resolution
+     * satellite imagery. IEEE Transactions on geoscience and remote sensing, vol. 39,
+     * NO. 2, February 2001, p. 309-320.
+     * \par
+     *
+     * Given \f$x_1\f$ and \f$x_2\f$ two membership values, \f$L_1,L_2\f$ two labels associated,
+     * and \f$\sigma\f$ a tolerance value, the following decision rule is applied:
+     * 
+     *  \f[
+     *  L=\left{\begin{array}{lcl}
+     * L_{1} &:& x_{1}>x_{2} and x_{1}>\sigma\\
+     * L_{2} &:& x_{2}>x_{1} and x_{2}>\sigma\\
+     * 0   &:& otherwise.
+     *  \end{array}\right.
+     * \f]
+     *
+     * To help the distinction between \f$L_{1}\f$ labels and \f$L_{2}\f$,
+     * this functor adds the m_LabelSeparators value to the \f$L_{1}\f$ labels.
+     *
+     * This functor is the decision rule used for multi-scale classification using
+     * morphological profiles.
+     */
+    template<class TInput,class TLabeled>
+      class MultiScaleConvexOrConcaveDecisionRule
+      {
+	
+      public:
+	/**
+	 * Constructor
+	 */
+	MultiScaleConvexOrConcaveDecisionRule()
+	  {
+	    m_Sigma = 0.0;
+	    m_LabelSeparator = 10;
+	  }
+	/**
+	 * Destructor
+	 */
+	~MultiScaleConvexOrConcaveDecisionRule(){};
+	/**
+	 * Label the pixel to convex, concave or flat
+	 * \return The label of the pixel
+	 * \param x The image value
+	 * \param xlevel The leveling value
+	 */
+	inline TLabeled operator()(const TInput& opDeMax, const TInput& cloDeMax,const TLabeled& opDeChar, const TLabeled& cloDeChar)
+	  {
+	    TLabeled resp = 0;
+
+	    if( opDeMax>cloDeMax && static_cast<double>(opDeMax)>m_Sigma)
+	      {
+		resp = m_LabelSeparator + opDeChar;
+	      }
+	    else if (cloDeMax>opDeMax && static_cast<double>(cloDeMax)>m_Sigma)
+	      {
+		resp = cloDeChar;
+	      }
+	    return resp;
+	  }
+	/**
+	 * Set the tolerance value
+	 * \param sigma the tolerance value
+	 */
+	void SetSigma(const double & sigma)
+	  {
+	    m_Sigma = sigma;
+	  }
+	/**
+	 * Get the tolerance value
+	 * \return the tolerance value
+	 */
+	double GetSigma(void)
+	  {
+	    return m_Sigma;
+	  }
+	/**
+	 * Set the label separator
+	 * \param labelSpeparator the label separator
+	 */
+	void SetLabelSeparator(const TLabeled& labelSeparator)
+	  {
+	    m_LabelSeparator = labelSeparator;
+	  }
+	/**
+	 * Get the label separator
+	 * \return the label separator
+	 */
+	TLabeled GetLabelSeparator(void)
+	  {
+	    return m_LabelSeparator;
+	  }
+	
+      private:
+	/** Sigma (tolerance) parameter */
+	double m_Sigma;
+	/** Seperate between convex and concave labels */
+	TLabeled m_LabelSeparator;
+
+      };
+  } //end namespace Functor
+
+/** \class MultiScaleConvexOrConcaveClassificationFilter
+ *  \brief Apply the MultiScaleConvexOrConcaveDecisionRule to whole images.
+ *  
+ * See MultiScaleConvexOrConcaveDecisionRule functor documentation for more details.
+ * 
+ */
+template <class TInputImage, class TOutputImage>
+class ITK_EXPORT MultiScaleConvexOrConcaveClassificationFilter
+  : public QuaternaryFunctorImageFilter<TInputImage,TInputImage, TOutputImage,TOutputImage,TOutputImage,
+    Functor::MultiScaleConvexOrConcaveDecisionRule<typename TInputImage::PixelType, 
+                                         typename TOutputImage::PixelType> >
+{
+ public:
+  /** Standard typedefs */
+  typedef MultiScaleConvexOrConcaveClassificationFilter Self;
+  typedef QuaternaryFunctorImageFilter<TInputImage,TInputImage, TOutputImage,TOutputImage,TOutputImage,
+                                       Functor::MultiScaleConvexOrConcaveDecisionRule<typename TInputImage::PixelType, 
+                                       typename TOutputImage::PixelType> >Superclass;
+  typedef itk::SmartPointer<Self>           Pointer;
+  typedef itk::SmartPointer<const Self>     ConstPointer;
+  
+  /** Type macro */
+  itkNewMacro(Self);
+  
+  /** Creation through object factory macro */
+  itkTypeMacro(MultiScaleConvexOrConcaveClassificationFilter,QuaternaryFunctorImageFilter);
+  
+  /** Template class typedef */
+  typedef TInputImage InputImageType;
+  typedef TOutputImage OutputImageType;
+  typedef typename OutputImageType::PixelType LabelType;
+  typedef Functor::MultiScaleConvexOrConcaveDecisionRule<typename TInputImage::PixelType, 
+    typename TOutputImage::PixelType> DecisionFunctorType;
+  /**
+   * Set the opening profile derivative maxima image
+   * \param derivativeMaxima the opening profile derivative maxima image
+   * 
+   */
+  void SetOpeningProfileDerivativeMaxima(const TInputImage * derivativeMaxima)
+    {
+      this->SetInput1(derivativeMaxima);
+    }
+  /**
+   * Set the opening profile characteristics image
+   * \param characteristics the opening profile characteristics image
+   * 
+   */
+  void SetOpeningProfileCharacteristics(const TOutputImage * characteristics)
+    {
+      this->SetInput3(characteristics);
+    }
+  /**
+   * Set the closing profile derivative maxima image
+   * \param derivativeMaxima the closing profile derivative maxima image
+   * 
+   */
+  void SetClosingProfileDerivativeMaxima(const TInputImage * derivativeMaxima)
+    {
+      this->SetInput2(derivativeMaxima);
+    }
+  /**
+   * Set the closing profile characteristics image
+   * \param characteristics the closing profile characteristics image
+   * 
+   */
+  void SetClosingProfileCharacteristics(const TOutputImage * characteristics)
+    {
+      this->SetInput4(characteristics);
+    }
+
+  /** Set/Get the tolerance value */
+  itkSetMacro(Sigma,double);
+  itkGetMacro(Sigma,double);
+  /** Set/Get the label separator */
+  itkSetMacro(LabelSeparator,LabelType);
+  itkGetMacro(LabelSeparator,LabelType);
+
+
+  /** Set the functor parameters before calling the ThreadedGenerateData() */
+  virtual void BeforeThreadedGenerateData(void)
+    {
+      this->GetFunctor().SetLabelSeparator(m_LabelSeparator);
+      this->GetFunctor().SetSigma(m_Sigma);
+    }
+
+protected:
+  /** Constructor */
+  MultiScaleConvexOrConcaveClassificationFilter()
+    {
+      m_LabelSeparator = 10;
+      m_Sigma          = 0.0;
+    };
+  /** Destructor */
+  virtual ~MultiScaleConvexOrConcaveClassificationFilter() {};
+ /**PrintSelf method */
+  virtual void PrintSelf(std::ostream& os, itk::Indent indent) const
+    {
+      Superclass::PrintSelf(os,indent);
+      os<<indent<<"LabelSeparator: "<<m_LabelSeparator<<std::endl;
+      os<<indent<<"Sigma: "<<m_Sigma<<std::endl;
+    }
+
+private:
+  MultiScaleConvexOrConcaveClassificationFilter(const Self&); //purposely not implemented
+  void operator=(const Self&); //purposely not implemented
+
+  /** Label separator between convex and concave labels */
+  LabelType m_LabelSeparator;
+  /** Tolerance value */
+  double m_Sigma;
+};
+}// End namespace otb
+#endif
diff --git a/Testing/Code/MultiScale/CMakeLists.txt b/Testing/Code/MultiScale/CMakeLists.txt
index a7f02d0432..427fa4e15e 100644
--- a/Testing/Code/MultiScale/CMakeLists.txt
+++ b/Testing/Code/MultiScale/CMakeLists.txt
@@ -264,6 +264,10 @@ ADD_TEST(msTvMorphologicalClosingProfileFilter ${MULTISCALE_TESTS2}
 	 1	 
 )
 
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ otbMULTISCALE_TESTS3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 # -------            otb::ProfileToProfileDerivativeFilter   ----------
  
 ADD_TEST(msTuProfileToProfileDerivativeFilterNew ${MULTISCALE_TESTS3} 
@@ -307,6 +311,24 @@ ADD_TEST(msTvProfileDerivativeToMultiScaleCharacteristicsFilter ${MULTISCALE_TES
 	 1
 	 1	 
 )
+# -------            otb::MultiScaleConvexOrConcaveClassificationFilter   ----------
+ 
+ADD_TEST(msTuMultiScaleConvexOrConcaveClassificationFilterNew ${MULTISCALE_TESTS3} 
+         otbMultiScaleConvexOrConcaveClassificationFilterNew)
+
+ADD_TEST(msTvMultiScaleConvexOrConcaveClassificationFilter ${MULTISCALE_TESTS3} 
+   --compare-image ${TOL}
+		   ${BASELINE}/msMultiScaleConvexOrConcaveClassificationFilterOutput.tif
+ 		   ${TEMP}/msMultiScaleConvexOrConcaveClassificationFilterOutput.tif	  
+         otbMultiScaleConvexOrConcaveClassificationFilter
+	 ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
+	 ${TEMP}/msMultiScaleConvexOrConcaveClassificationFilterOutput.tif
+	 5
+	 1
+	 1
+	 1.	 
+)
+
 
 # -------       Fichiers sources CXX -----------------------------------
 SET(BasicMultiScale_SRCS1
@@ -340,6 +362,9 @@ otbProfileToProfileDerivativeFilterNew.cxx
 otbProfileToProfileDerivativeFilter.cxx
 otbProfileDerivativeToMultiScaleCharacteristicsFilterNew.cxx
 otbProfileDerivativeToMultiScaleCharacteristicsFilter.cxx
+otbMultiScaleConvexOrConcaveClassificationFilterNew.cxx
+otbMultiScaleConvexOrConcaveClassificationFilter.cxx
+
 )
 
 INCLUDE_DIRECTORIES("${OTBTesting_BINARY_DIR}")
diff --git a/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.cxx b/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.cxx
new file mode 100644
index 0000000000..9c363c045f
--- /dev/null
+++ b/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilter.cxx
@@ -0,0 +1,111 @@
+/*=========================================================================
+
+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 "otbMorphologicalOpeningProfileFilter.h"
+#include "otbMorphologicalClosingProfileFilter.h"
+#include "otbProfileToProfileDerivativeFilter.h"
+#include "otbProfileDerivativeToMultiScaleCharacteristicsFilter.h"
+#include "otbMultiScaleConvexOrConcaveClassificationFilter.h"
+#include "itkBinaryBallStructuringElement.h"
+#include "otbImageFileReader.h"
+#include "otbStreamingImageFileWriter.h"
+#include "otbImage.h"
+
+#include "itkMacro.h"
+
+int otbMultiScaleConvexOrConcaveClassificationFilter(int argc, char * argv[])
+{
+  const char * inputFilename = argv[1];
+  const char * outputFilename = argv[2];
+  const unsigned int profileSize = atoi(argv[3]);
+  const unsigned int initialValue = atoi(argv[4]);
+  const unsigned int step = atoi(argv[5]);
+  const double sigma = atof(argv[6]);
+       
+
+  const unsigned int Dimension = 2;
+  typedef double InputPixelType;
+  typedef double OutputPixelType;
+  typedef unsigned short LabeledPixelType;
+
+  typedef otb::Image<InputPixelType,Dimension> InputImageType;
+  typedef otb::Image<OutputPixelType,Dimension> OutputImageType;
+  typedef otb::Image<LabeledPixelType,2> LabeledImageType;
+
+  typedef otb::ImageFileReader<InputImageType> ReaderType;
+  typedef otb::StreamingImageFileWriter<LabeledImageType> LabeledWriterType;
+
+  typedef itk::BinaryBallStructuringElement<InputPixelType,Dimension> StructuringElementType;
+  typedef otb::MorphologicalOpeningProfileFilter<InputImageType,InputImageType,StructuringElementType>
+    OpeningProfileFilterType;
+  typedef otb::MorphologicalClosingProfileFilter<InputImageType,InputImageType,StructuringElementType>
+    ClosingProfileFilterType;
+  typedef otb::ProfileToProfileDerivativeFilter<InputImageType,InputImageType> DerivativeFilterType;
+  typedef otb::ProfileDerivativeToMultiScaleCharacteristicsFilter<InputImageType,OutputImageType,LabeledImageType> 
+    MultiScaleCharacteristicsFilterType;
+  typedef otb::MultiScaleConvexOrConcaveClassificationFilter<InputImageType,LabeledImageType> MultiScaleClassificationFilterType;
+  
+  
+  // Reading input image
+  ReaderType::Pointer reader = ReaderType::New();
+  reader->SetFileName(inputFilename);
+
+  // Instantiation
+  OpeningProfileFilterType::Pointer oprofileFilter = OpeningProfileFilterType::New();
+  oprofileFilter->SetInput(reader->GetOutput());
+  oprofileFilter->SetProfileSize(profileSize);
+  oprofileFilter->SetInitialValue(initialValue);
+  oprofileFilter->SetStep(step);
+
+  ClosingProfileFilterType::Pointer cprofileFilter = ClosingProfileFilterType::New();
+  cprofileFilter->SetInput(reader->GetOutput());
+  cprofileFilter->SetProfileSize(profileSize);
+  cprofileFilter->SetInitialValue(initialValue);
+  cprofileFilter->SetStep(step);
+  
+  DerivativeFilterType::Pointer oderivativeFilter = DerivativeFilterType::New();
+  oderivativeFilter->SetInput(oprofileFilter->GetOutput());
+  
+  DerivativeFilterType::Pointer cderivativeFilter = DerivativeFilterType::New();
+  cderivativeFilter->SetInput(cprofileFilter->GetOutput());
+
+  MultiScaleCharacteristicsFilterType::Pointer omsCharFilter = MultiScaleCharacteristicsFilterType::New();
+  omsCharFilter->SetInput(oderivativeFilter->GetOutput());
+  omsCharFilter->SetInitialValue(initialValue);
+  omsCharFilter->SetStep(step);
+  
+  MultiScaleCharacteristicsFilterType::Pointer cmsCharFilter = MultiScaleCharacteristicsFilterType::New();
+  cmsCharFilter->SetInput(cderivativeFilter->GetOutput());
+  cmsCharFilter->SetInitialValue(initialValue);
+  cmsCharFilter->SetStep(step);
+
+  MultiScaleClassificationFilterType::Pointer classificationFilter = MultiScaleClassificationFilterType::New();
+  classificationFilter->SetOpeningProfileDerivativeMaxima(omsCharFilter->GetOutput());
+  classificationFilter->SetOpeningProfileCharacteristics(omsCharFilter->GetOutputCharacteristics());
+  classificationFilter->SetClosingProfileDerivativeMaxima(cmsCharFilter->GetOutput());
+  classificationFilter->SetClosingProfileCharacteristics(cmsCharFilter->GetOutputCharacteristics());
+  classificationFilter->SetSigma(sigma);
+  classificationFilter->SetLabelSeparator(initialValue+profileSize*step);
+
+  LabeledWriterType::Pointer labeledWriter = LabeledWriterType::New();
+
+  labeledWriter->SetFileName(outputFilename);
+  labeledWriter->SetInput(classificationFilter->GetOutput());
+  labeledWriter->Update();
+  
+  return EXIT_SUCCESS;
+}
diff --git a/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilterNew.cxx b/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilterNew.cxx
new file mode 100644
index 0000000000..03447562ee
--- /dev/null
+++ b/Testing/Code/MultiScale/otbMultiScaleConvexOrConcaveClassificationFilterNew.cxx
@@ -0,0 +1,40 @@
+/*=========================================================================
+
+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 "itkBinaryBallStructuringElement.h"
+#include "otbMultiScaleConvexOrConcaveClassificationFilter.h"
+#include "otbImage.h"
+#include "itkMacro.h"
+
+int otbMultiScaleConvexOrConcaveClassificationFilterNew(int argc, char * argv[])
+{
+  const unsigned int Dimension = 2;
+  typedef double InputPixelType;
+  typedef double OutputPixelType;
+  typedef unsigned short LabeledPixelType;
+
+  typedef otb::Image<InputPixelType,Dimension> InputImageType;
+  typedef otb::Image<OutputPixelType,Dimension> OutputImageType;
+  typedef otb::Image<LabeledPixelType,2> LabeledImageType;
+
+  typedef otb::MultiScaleConvexOrConcaveClassificationFilter<InputImageType,LabeledImageType> MultiScaleClassificationFilterType;
+  
+  // Instantiation
+  MultiScaleClassificationFilterType::Pointer classificationFilter = MultiScaleClassificationFilterType::New();
+
+  return EXIT_SUCCESS;
+}
diff --git a/Testing/Code/MultiScale/otbMultiScaleTests3.cxx b/Testing/Code/MultiScale/otbMultiScaleTests3.cxx
index b43a9e264c..7370f4fb2d 100644
--- a/Testing/Code/MultiScale/otbMultiScaleTests3.cxx
+++ b/Testing/Code/MultiScale/otbMultiScaleTests3.cxx
@@ -30,4 +30,6 @@ REGISTER_TEST(otbProfileToProfileDerivativeFilterNew);
 REGISTER_TEST(otbProfileToProfileDerivativeFilter);
 REGISTER_TEST(otbProfileDerivativeToMultiScaleCharacteristicsFilterNew); 
 REGISTER_TEST(otbProfileDerivativeToMultiScaleCharacteristicsFilter);
+REGISTER_TEST(otbMultiScaleConvexOrConcaveClassificationFilterNew);
+REGISTER_TEST(otbMultiScaleConvexOrConcaveClassificationFilter);
 }
diff --git a/Testing/Code/MultiScale/otbProfileDerivativeToMultiScaleCharacteristicsFilter.cxx b/Testing/Code/MultiScale/otbProfileDerivativeToMultiScaleCharacteristicsFilter.cxx
index 66efbed305..6f0da0b149 100644
--- a/Testing/Code/MultiScale/otbProfileDerivativeToMultiScaleCharacteristicsFilter.cxx
+++ b/Testing/Code/MultiScale/otbProfileDerivativeToMultiScaleCharacteristicsFilter.cxx
@@ -32,7 +32,7 @@ int otbProfileDerivativeToMultiScaleCharacteristicsFilter(int argc, char * argv[
   const char * outputFilename2 = argv[3];
   const unsigned int profileSize = atoi(argv[4]);
   const unsigned int initialValue = atoi(argv[5]);
-  const unsigned int step = atoi(argv[5]);
+  const unsigned int step = atoi(argv[6]);
        
 
   const unsigned int Dimension = 2;
-- 
GitLab