From 2a2b639787dbb17bb7d0f6ad8c5c36572b8ca0d1 Mon Sep 17 00:00:00 2001
From: Gregoire Mercier <gregoire.mercier@telecom-bretagne.eu>
Date: Wed, 2 Mar 2011 19:06:35 +0100
Subject: [PATCH] ADD: FastICA

---
 Code/Hyperspectral/otbFastICAImageFilter.h   | 196 +++++++++++++++
 Code/Hyperspectral/otbFastICAImageFilter.txx | 250 +++++++++++++++++++
 2 files changed, 446 insertions(+)
 create mode 100644 Code/Hyperspectral/otbFastICAImageFilter.h
 create mode 100644 Code/Hyperspectral/otbFastICAImageFilter.txx

diff --git a/Code/Hyperspectral/otbFastICAImageFilter.h b/Code/Hyperspectral/otbFastICAImageFilter.h
new file mode 100644
index 0000000000..15553eb7be
--- /dev/null
+++ b/Code/Hyperspectral/otbFastICAImageFilter.h
@@ -0,0 +1,196 @@
+/*=========================================================================
+
+  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 __otbFastICAImageFilter_h
+#define __otbFastICAImageFilter_h
+
+#include "otbMacro.h"
+#include "itkImageToImageFilter.h"
+#include "otbPCAImageFilter.h"
+#include "otbMatrixMultiplyImageFilter.h"
+
+namespace otb 
+{
+/** \class FastICAImageFilter
+ * \brief Performs a Independent Component Analysis
+ *
+ * The internal structure of this filter is a filter-to-filter like structure. 
+ * The estimation of the covariance matrix has persistent capabilities...
+ *
+ * \sa PCAImageFilter
+ */
+template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation >
+class ITK_EXPORT FastICAImageFilter 
+  : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+  /** Standard typedefs */
+  typedef FastICAImageFilter                                 Self;
+  typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+  typedef itk::SmartPointer<Self>                            Pointer;
+  typedef itk::SmartPointer<const Self>                      ConstPointer;
+
+  /** Type macro */
+  itkNewMacro(Self);
+
+  /** Creation through object factory macro */
+  itkTypeMacro(FastICAImageFilter, ImageToImageFilter);
+
+  /** Dimension */
+	itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
+	itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension);
+
+  typedef Transform::TransformDirection TransformDirectionEnumType;
+  itkStaticConstMacro(DirectionOfTransformation,TransformDirectionEnumType,TDirectionOfTransformation);
+
+  /** typedefs */
+  typedef TInputImage InputImageType;
+  typedef TOutputImage OutputImageType;
+
+  typedef PCAImageFilter< InputImageType, OutputImageType, TDirectionOfTransformation >
+      PCAFilterType;
+  typedef typename PCAFilterType::Pointer PCAFilterPointerType;
+
+  typedef typename PCAFilterType::RealType RealType;
+  typedef typename PCAFilterType::VectorType VectorType;
+  typedef typename PCAFilterType::MatrixType MatrixType;
+  typedef typename MatrixType::InternalMatrixType InternalMatrixType;
+  typedef typename InternalMatrixType::element_type MatrixElementType;
+
+  typedef MatrixMultiplyImageFilter< TInputImage, TOutputImage, RealType > TransformFilterType;
+  typedef typename TransformFilterType::Pointer TransformFilterPointerType;
+
+  typedef double (*ContrastFunctionType) ( double );
+
+
+  /** 
+   * Set/Get the number of required largest principal components. 
+   */
+  itkGetMacro(NumberOfPrincipalComponentsRequired,unsigned int);
+  itkSetMacro(NumberOfPrincipalComponentsRequired,unsigned int);
+
+  itkGetConstMacro(PCAFilter,PCAFilterType *);
+  itkGetMacro(PCAFilter,PCAFilterType *);
+  itkSetMacro(PCAFilter,PCAFilterType *);
+
+  itkGetConstMacro(TransformFilter,TransformFilterType *);
+  itkGetMacro(TransformFilter,TransformFilterType *);
+  itkSetMacro(TransformFilter,TransformFilterType *);
+
+  VectorType GetMeanValues () const
+  {
+    return this->GetPCAFilter()->GetMeanValues();
+  }
+  void SetMeanValues ( const VectorType & vec )
+  {
+    m_PCAFilter->SetMeanValues(vec);
+  }
+
+  VectorType GetStdDevValues ( ) const
+  {
+    return this->GetPCAFilter()->GetStdDevValues();
+  }
+  void SetStdDevValues ( const VectorType & vec )
+  {
+    m_PCAFilter->SetStdDevValues(vec);
+  }
+
+  MatrixType GetPCATransformationMatrix () const
+  {
+    return this->GetPCAFilter()->GetTransformationMatrix();
+  }
+  void SetPCACovarianceMatrix ( const MatrixType & mat, bool isForward = true )
+  {
+    m_PCAFilter->SetTransformationMatrix(mat,isForward);
+  }
+
+  itkGetConstMacro(TransformationMatrix,MatrixType);
+  itkGetMacro(TransformationMatrix,MatrixType);
+  void SetTransformationMatrix ( const MatrixType & mat, bool isForward = true ) 
+  {
+    m_IsTransformationForward = isForward;
+    m_GivenTransformationMatrix = true;
+    m_TransformationMatrix = mat;
+    this->Modified();
+  }
+
+  itkGetMacro(MaximumOfIterations,unsigned int);
+  itkSetMacro(MaximumOfIterations,unsigned int);
+
+  itkGetMacro(ConvergenceThreshold,double);
+  itkSetMacro(ConvergenceThreshold,double);
+
+  itkGetMacro(ContrastFunction,ContrastFunctionType);
+
+protected:
+  FastICAImageFilter ();
+  virtual ~FastICAImageFilter() { }
+
+  /** GenerateOutputInformation
+   * Propagate vector length info and modify if needed 
+   * NumberOfPrincipalComponentsRequired
+   *
+   * In REVERSE mode, the covariance matrix or the transformation matrix 
+   * (which may not be square) has to be given,
+   * otherwize, GenerateOutputInformation throws an itk::ExceptionObject
+   */
+  virtual void GenerateOutputInformation();
+
+  /** GenerateData
+   * Through a filter of filter structure
+   */
+  virtual void GenerateData ();
+
+  void PrintSelf(std::ostream& os, itk::Indent indent) const;
+
+  /** Internal methods */
+  virtual void ForwardGenerateData();
+  virtual void ReverseGenerateData();
+
+  /** this is the specifical part of FastICA */
+  virtual void GenerateTransformationMatrix();
+
+  unsigned int m_NumberOfPrincipalComponentsRequired;
+
+  /** Transformation matrix refers to the ICA step (not PCA) */
+  bool m_GivenTransformationMatrix;
+  bool m_IsTransformationForward;
+  MatrixType m_TransformationMatrix;
+
+  /** FastICA parameters */
+  unsigned int m_MaximumOfIterations; // def is 50
+  double m_ConvergenceThreshold; // def is 1e-4
+  ContrastFunctionType m_ConstrastFunction; // see g() function in the biblio. Def is tanh
+
+  PCAFilterPointerType m_PCAFilter;
+  TransformFilterPointerType m_TransformFilter;
+
+private:
+  FastICAImageFilter ( const Self & );
+  void operator= ( const Self & );
+}; // end of class
+
+} // end of namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbFastICAImageFilter.txx"
+#endif
+
+
+#endif
+
+
diff --git a/Code/Hyperspectral/otbFastICAImageFilter.txx b/Code/Hyperspectral/otbFastICAImageFilter.txx
new file mode 100644
index 0000000000..05ff275418
--- /dev/null
+++ b/Code/Hyperspectral/otbFastICAImageFilter.txx
@@ -0,0 +1,250 @@
+/*=========================================================================
+
+  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 __otbFastICAImageFilter_txx
+#define __otbFastICAImageFilter_txx
+#include "otbFastICAImageFilter.h"
+
+#include "otbMacro.h"
+
+#include "itkExceptionObject.h"
+#include "itkNumericTraits.h"
+
+#include <vnl/vnl_matrix.h>
+#include <vnl/algo/vnl_matrix_inverse.h>
+#include <vnl/algo/vnl_generalized_eigensystem.h>
+
+namespace otb
+{
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::FastICAImageFilter ()
+{
+  this->SetNumberOfRequiredInputs(1);
+
+  m_NumberOfPrincipalComponentsRequired = 0;
+
+  m_GivenTransformationMatrix = false;
+  m_IsTransformationForward = true;
+
+  m_MaximumOfIterations = 50;
+  m_ConvergenceThreshold = 1E-4;
+  m_ConstrastFunction = &vcl_tanh;
+
+  m_PCAFilter = PCAFilterType::New();
+  m_PCAFilter->UseNormalization();
+
+  m_TransformFilter = TransformFilterType::New();
+}
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+void 
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::GenerateOutputInformation()
+// throw itk::ExceptionObject
+{
+  Superclass::GenerateOutputInformation();
+
+  switch ( DirectionOfTransformation )
+  {
+    case Transform::FORWARD:
+    {
+      if ( m_NumberOfPrincipalComponentsRequired == 0 
+          || m_NumberOfPrincipalComponentsRequired 
+            > this->GetInput()->GetNumberOfComponentsPerPixel() )
+      {
+        m_NumberOfPrincipalComponentsRequired = 
+          this->GetInput()->GetNumberOfComponentsPerPixel();
+      }
+
+      this->GetOutput()->SetNumberOfComponentsPerPixel(
+        m_NumberOfPrincipalComponentsRequired );
+      break;
+    }
+    case Transform::INVERSE:
+    {
+      unsigned int theOutputDimension = 0;
+      if ( m_GivenTransformationMatrix )
+      {
+        theOutputDimension = m_TransformationMatrix.Rows() >= m_TransformationMatrix.Cols() ?
+          m_TransformationMatrix.Rows() : m_TransformationMatrix.Cols();
+      }
+      else if ( m_GivenCovarianceMatrix )
+      {
+        theOutputDimension = m_CovarianceMatrix.Rows() >= m_CovarianceMatrix.Cols() ?
+          m_CovarianceMatrix.Rows() : m_CovarianceMatrix.Cols();
+      }
+      else
+      {
+        throw itk::ExceptionObject(__FILE__, __LINE__,
+          "Mixture matrix is required to know the output size",
+          ITK_LOCATION);
+      }
+
+      this->GetOutput()->SetNumberOfComponentsPerPixel( theOutputDimension );
+
+      break;
+    }
+    default:
+      throw itk::ExceptionObject(__FILE__, __LINE__,
+          "Class should be templeted with FORWARD or INVERSE only...",
+          ITK_LOCATION );
+  }
+}
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+void
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::GenerateData ()
+{
+  switch ( DirectionOfTransformation )
+  {
+    case Transform::FORWARD:
+      return ForwardGenerateData();
+    case Transform::INVERSE:
+      return ReverseGenerateData();
+    default:
+      throw itk::ExceptionObject(__FILE__, __LINE__,
+          "Class should be templated with FORWARD or INVERSE only...",
+          ITK_LOCATION );
+  }
+}
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+void
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::ForwardGenerateData ()
+{
+  typename InputImageType::Pointer inputImgPtr 
+    = const_cast<InputImageType*>( this->GetInput() );
+
+  m_PCAFilter->SetInput( inputImgPtr );
+  m_PCAFilter->Update();
+
+  if ( !m_GivenTransformationMatrix )
+  {
+    GenerateTransformationMatrix();
+  }
+  else if ( !m_IsTransformationForward )
+  {
+    // prevent from multiple inversion in the pipelines
+    m_IsTransformationForward = true;
+    vnl_svd< MatrixElementType > invertor ( m_TransformationMatrix.GetVnlMatrix() );
+    m_TransformationMatrix = invertor.pinverse();
+  }
+
+  if ( m_TransformationMatrix.GetVnlMatrix().empty() )
+  {
+    throw itk::ExceptionObject( __FILE__, __LINE__,
+          "Empty transformation matrix",
+          ITK_LOCATION);
+  }
+
+  m_TransformFilter->SetInput( m_PCAFilter->GetOutput() );
+  m_TransformFilter->SetMatrix( m_TransformationMatrix.GetVnlMatrix() );
+  m_TransformFilter->GraftOutput( this->GetOutput() );
+  m_TransformFilter->Update();
+
+  this->GraftOutput( m_TransformFilter->GetOutput() );
+}
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+void
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::ReverseGenerateData ()
+{
+  if ( !m_GivenTransformationMatrix )
+  {
+    throw itk::ExceptionObject( __FILE__, __LINE__,
+                                "No Transformation matrix given",
+                                ITK_LOCATION );
+  }
+
+  if ( m_TransformationMatrix.GetVnlMatrix().empty() )
+  {
+    throw itk::ExceptionObject( __FILE__, __LINE__,
+          "Empty transformation matrix",
+          ITK_LOCATION);
+  }
+
+  if ( m_IsTransformationForward )
+  {
+    // prevent from multiple inversion in the pipelines
+    m_IsTransformationForward = false;
+    vnl_svd< MatrixElementType > invertor ( m_TransformationMatrix.GetVnlMatrix() );
+    m_TransformationMatrix = invertor.pinverse();
+  }
+
+  m_TransformFilter->SetInput( this->GetInput() );
+  m_TransformFilter->SetMatrix( m_TransformationMatrix.GetVnlMatrix() );
+
+  /*
+   * PCA filter may throw exception if 
+   * the mean, stdDev and transformation matrix
+   * have not been given at this point
+   */
+  m_PCAFilter->SetInput( m_TransformFilter->GetOutput() );
+  m_PCAFilter->GraftOutput( this->GetOutput() );
+  m_PCAFilter->Update();
+
+  this->GraftOutput( m_PCAFilter->GetOutput() );
+}
+
+template < class TInputImage, class TOutputImage, 
+            Transform::TransformDirection TDirectionOfTransformation >
+void
+FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
+::GenerateTransformationMatrix ()
+{
+  double convergence = itk::NumericTraits<double>::Max();
+  unsigned int iteration = 0;
+
+  unsigned int size = this->GetInput()->GetNumberOfComponentsPerPixel();
+
+  // transformation matrix
+  InternalMatrixType W ( size, size, vnl_matrix_identity );
+
+  while ( iteration++ < GetMaximumOfIterations() 
+          && convergence > GetConvergenceThreshold() )
+  {
+    otbMsgDebugMacro( "Iteration " << iteration << " / " << GetMaximumOfIterations() 
+      << ", MSE = " << convergence );
+
+    InternalMatrixType W_old ( W );
+
+    // TODO le premier coup ne sert a rien
+    TransformFilterPointerType transformer = TransformFilterType::New();
+    transformer->SetIntput( GetPCAFilter()->GetOutput() );
+    transformer->SetMatrix( W );
+    transformer->Update();
+
+    // Faire un image to image filter...
+
+  } // end of while loop
+}
+
+} // end of namespace otb
+
+#endif
+
+
-- 
GitLab