/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
* Copyright (C) 2007-2012 Institut Mines Telecom / Telecom Bretagne
*
* 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 otbSEMClassifier_h
#define otbSEMClassifier_h
#include "itkListSample.h"
#include "itkSampleClassifierFilter.h"
#include "otbGaussianModelComponent.h"
namespace otb
{
/** \class SEMClassifier
* \brief This class implements the Stochastic Expectation
* Maximization algorithm to perform an estimation of a mixture model.
*
* The first template argument is the type of the target sample
* data. This estimator expects one or more model component objects
* of the classes derived from the ModelComponentBase. The actual
* component (or module) parameters are updated by each component.
* Users can think this class as a strategy or a integration point
* for the SEM procedure.
*
* The number of classes (SetNumberOfClasses), the initial
* proportion (SetInitialProportions), the input sample (SetSample),
* the model components (AddComponent), and the maximum iteration
* (SetMaximumIteration) are required. The SEM procedure terminates
* when the current iteration reaches the maximum iteration or the model
* parameters converge.
*
* The difference from ExpectationMaximizationMixtureModelEstimator is
* that SEMClassifier include the maximum a posteriori decision on each
* sample. The class is to be seen as a classification and not an estimator.
*
* Recent API changes:
* N/A
*
* \sa ModelComponentBase, GaussianModelComponent
*
* \ingroup OTBLearningBase
*/
template
class ITK_EXPORT SEMClassifier : public itk::Statistics::SampleClassifierFilter>
{
public:
/** Standard class typedef*/
typedef SEMClassifier Self;
typedef itk::Statistics::SampleClassifierFilter> Superclass;
typedef itk::SmartPointer Pointer;
typedef itk::SmartPointer ConstPointer;
/** Standard macros */
itkTypeMacro(SEMClassifier, itk::Object);
itkNewMacro(Self);
/** TSample template argument related typedefs */
typedef typename itk::Statistics::ListSample SampleType;
typedef typename itk::Statistics::Subsample ClassSampleType;
typedef typename SampleType::MeasurementType MeasurementType;
typedef typename SampleType::MeasurementVectorType MeasurementVectorType;
/** Type of the segmentation vector */
typedef unsigned int ClassLabelType;
typedef std::vector ClassLabelVectorType;
/** Output type for GetClassSample method */
typedef itk::Statistics::MembershipSample OutputType;
/** Type of the mixture model component base class.
* Due to the stochastic purpose, the initial list of sample (from TInputImage)
* is duplicated as many times as the number of classes (into SampleLists). */
typedef otb::Statistics::ModelComponentBase ComponentType;
typedef typename ComponentType::Pointer ComponentPointerType;
/** Type of the component pointer storage, one component per class */
typedef std::vector ComponentVectorType;
/** type of the parameters needed for the component vectors */
typedef itk::Array ParameterType;
/** Type of the parameter pointer storage, one parameter set per class */
typedef std::vector ParameterVectorType;
/** Type of the vector of the probability values for each sample */
typedef std::vector ProportionVectorType;
typedef std::vector ProbaVectorType;
/** Type of the probability values of each sample per class */
typedef std::vector ProbaByClassVectorType;
/** Sets the target data that will be classified by this */
void SetSample(const TInputImage* sample);
/** Returns the target data */
const TInputImage* GetSample() const;
SampleType* GetSampleList() const;
/** Set/Gets the initial proportion values. The size of proportion
* vector should be same as the number of component (or classes).
* Choose between SetInitialProportions, SetClassLabels or
* SetNumberOfComponents */
void SetInitialProportions(ProportionVectorType& proportions);
itkGetConstReferenceMacro(InitialProportions, ProportionVectorType);
/** Gets the result proportion values */
itkGetConstReferenceMacro(Proportions, ProportionVectorType);
/** Set/Gets the initial segmentation. the size of the vector should be
* the same as the number of samples (length of MeasurementVector) */
void SetClassLabels(OutputType* labels);
void SetClassLabels(TOutputImage* imgLabels);
ClassLabelVectorType& GetClassLabels();
/** Set/Gets the maximum number of iterations. When the optimization
* process reaches the maximum number of interations, even if the
* class parameters aren't converged, the optimization process
* stops. */
itkSetMacro(MaximumIteration, int);
itkGetMacro(MaximumIteration, int);
/** Set/Gets the neighborhood to take into consideration
* in the contextual decision rule. Should be odd (def. 3). */
void SetNeighborhood(int neighborhood);
int GetNeighborhood();
/** Gets the current iteration. */
int GetCurrentIteration();
/** Adds a new component (or class). Has to be called after SetNumberOfClasses */
int AddComponent(int id, ComponentType* component);
/** Runs the optimization process. */
void Update() override;
/** Termination status after running optimization */
typedef enum { CONVERGED = 0, NOT_CONVERGED = 1 } TerminationCodeType;
/** Set/Get the termination threshold (ratio of the number of sample that
* change affected class during interation over the total number of
* samples (def is 1E-5) */
itkSetMacro(TerminationThreshold, double);
itkGetMacro(TerminationThreshold, double);
/** Gets the termination status */
itkGetMacro(TerminationCode, TerminationCodeType);
/* Return the classification result (as a standard classification result) */
OutputType* GetOutput();
/* Return the classification result (as an image) */
TOutputImage* GetOutputImage();
void Modified() const override;
protected:
SEMClassifier();
~SEMClassifier() override
{
}
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
/** Initialize the first segmentation, either randomly or by using
* a ClassLabelVectorType given in SetClassLabels. */
void InitParameters();
/** Stochastic part of the SEM */
void PerformStochasticProcess();
/** Estimation part of the SEM */
void PerformExpectationProcess();
/** Maximization part of the SEM.
* This method should be upgraded this a contextual point of view...
* It required to a Neighborhood knowledge into the TSample type... */
void PerformMaximizationProcess();
/** Make Decision through a Maximum a posteriori */
void GetMaximumAposterioriLabels();
private:
/** Target data sample pointer*/
const TInputImage* m_Sample;
int m_NbSamples;
typename SampleType::Pointer m_SampleList;
int m_MaximumIteration;
int m_CurrentIteration;
int m_NbChange;
double m_TerminationThreshold;
int m_Neighborhood;
TerminationCodeType m_TerminationCode;
mutable ComponentVectorType m_ComponentVector;
ProportionVectorType m_InitialProportions;
ProportionVectorType m_Proportions;
ProbaByClassVectorType m_Proba;
ClassLabelVectorType m_ClassLabels;
int m_ExternalLabels;
mutable int m_ComponentDeclared;
typename TOutputImage::Pointer m_OutputImage;
typename OutputType::Pointer m_Output;
}; // end of class
} // end of namespace
#ifndef OTB_MANUAL_INSTANTIATION
#include "otbSEMClassifier.hxx"
#endif
#endif