From c61ea7fa470a23632024ee5f1713a53c50ced755 Mon Sep 17 00:00:00 2001
From: Gregoire Mercier <gregoire.mercier@telecom-bretagne.eu>
Date: Thu, 3 Mar 2011 18:57:11 +0100
Subject: [PATCH] ENH: complete FastICA with threaded optimizer

---
 ...astICAInternalOptimizerVectorImageFilter.h | 128 +++++++++++++++
 ...tICAInternalOptimizerVectorImageFilter.txx | 151 ++++++++++++++++++
 Code/Hyperspectral/otbFastICAImageFilter.h    |  28 ++--
 Code/Hyperspectral/otbFastICAImageFilter.txx  |  85 +++++++---
 4 files changed, 363 insertions(+), 29 deletions(-)
 create mode 100644 Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.h
 create mode 100644 Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.txx

diff --git a/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.h b/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.h
new file mode 100644
index 0000000000..d70f500fef
--- /dev/null
+++ b/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.h
@@ -0,0 +1,128 @@
+/*=========================================================================
+
+  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 __otbFastICAInternalOptimizerVectorImageFilter_h
+#define __otbFastICAInternalOptimizerVectorImageFilter_h
+
+#include "otbMacro.h"
+#include "itkImageToImageFilter.h"
+#include "otbMatrixMultiplyImageFilter.h"
+#include "otbStreamingStatisticsVectorImageFilter2.h"
+
+namespace otb 
+{
+/** \class FastICAInternalOptimizerVectorImageFilter
+ * \brief Internal optimisation of the FastICA unmixing filter
+ *
+ * This class implements the internal search for the unmixing matrix W
+ * in the FastICA technique.
+ *
+ * The class takes 2 inputs (initial image and its projection with the W matrix). 
+ *
+ * \ingroup Multithreaded
+ * \sa FastICAImageFilter
+ */
+template <class TInputImage, class TOutputImage>
+class ITK_EXPORT FastICAInternalOptimizerVectorImageFilter
+  : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+  /** Standard typedefs */
+  typedef FastICAInternalOptimizerVectorImageFilter          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(FastICAInternalOptimizerVectorImageFilter, ImageToImageFilter);
+
+  /** Dimension */
+	itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
+	itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension);
+
+  /** Template parameters typedefs */
+  typedef TInputImage  InputImageType;
+  typedef typename InputImageType::RegionType InputRegionType;
+
+  typedef TOutputImage OutputImageType;
+  typedef typename OutputImageType::RegionType OutputRegionType;
+
+  /** Filter types and related */
+  typedef StreamingStatisticsVectorImageFilter2< InputImageType > CovarianceEstimatorFilterType;
+  typedef typename CovarianceEstimatorFilterType::Pointer CovarianceEstimatorFilterPointerType;
+
+  typedef typename CovarianceEstimatorFilterType::RealType RealType;
+  typedef typename CovarianceEstimatorFilterType::RealPixelType VectorType;
+  typedef typename CovarianceEstimatorFilterType::MatrixObjectType MatrixObjectType;
+  typedef typename MatrixObjectType::ComponentType 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 );
+
+  itkSetMacro(CurrentBandForLoop,unsigned int);
+  itkGetMacro(CurrentBandForLoop,unsigned int);
+
+  itkGetMacro(W,InternalMatrixType);
+  itkSetMacro(W,InternalMatrixType);
+
+  itkSetMacro(ContrastFunction,ContrastFunctionType);
+  itkGetMacro(Beta,double);
+  itkGetMacro(Den,double);
+
+protected:
+  FastICAInternalOptimizerVectorImageFilter();
+  virtual ~FastICAInternalOptimizerVectorImageFilter() { }
+
+  virtual void GenerateOutputInformation();
+  virtual void BeforeThreadedGenerateData ();
+  virtual void ThreadedGenerateData ( const OutputRegionType &, int );
+  virtual void AfterThreadedGenerateData();
+
+  unsigned int m_CurrentBandForLoop;
+
+  std::vector<double> m_BetaVector;
+  std::vector<double> m_DenVector;
+  std::vector<double> m_NbSamples;
+
+  double m_Beta;
+  double m_Den;
+
+  InternalMatrixType m_W;
+  ContrastFunctionType m_ContrastFunction;
+  TransformFilterPointerType m_TransformFilter;
+private:
+  FastICAInternalOptimizerVectorImageFilter( const Self & ); // not implemented
+  void operator= ( const Self & ); // not implemented
+}; // end of class
+
+} // end of namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbFastICAInternalOptimizerVectorImageFilter.txx"
+#endif
+
+
+#endif
+
+
diff --git a/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.txx b/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.txx
new file mode 100644
index 0000000000..ef4171ba65
--- /dev/null
+++ b/Code/BasicFilters/otbFastICAInternalOptimizerVectorImageFilter.txx
@@ -0,0 +1,151 @@
+/*=========================================================================
+
+  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 __otbFastICAInternalOptimizerVectorImageFilter_txx
+#define __otbFastICAInternalOptimizerVectorImageFilter_txx
+#include "otbFastICAInternalOptimizerVectorImageFilter.h"
+
+#include <itkExceptionObject.h>
+#include <itkImageRegionConstIterator.h>
+#include <itkImageRegionIterator.h>
+
+#include <vnl/vnl_math.h>
+#include <vnl/vnl_matrix.h>
+
+namespace otb
+{
+
+template <class TInputImage, class TOutputImage>
+FastICAInternalOptimizerVectorImageFilter< TInputImage, TOutputImage >
+::FastICAInternalOptimizerVectorImageFilter()
+{
+  this->SetNumberOfRequiredInputs(2);
+
+  m_CurrentBandForLoop = 0;
+  m_Beta = 0.;
+  m_Den = 0.;
+  
+  m_ContrastFunction = &vcl_tanh;
+  
+  m_TransformFilter = TransformFilterType::New();
+}
+
+template <class TInputImage, class TOutputImage>
+void
+FastICAInternalOptimizerVectorImageFilter< TInputImage, TOutputImage >
+::GenerateOutputInformation()
+{
+  Superclass::GenerateOutputInformation();
+
+  this->GetOutput()->SetNumberOfComponentsPerPixel(
+    this->GetInput(0)->GetNumberOfComponentsPerPixel() );
+}
+
+template <class TInputImage, class TOutputImage>
+void
+FastICAInternalOptimizerVectorImageFilter< TInputImage, TOutputImage >
+::BeforeThreadedGenerateData ()
+{
+  if ( m_W.empty() )
+  {
+    throw itk::ExceptionObject( __FILE__, __LINE__,
+      "Give the initial W matrix", ITK_LOCATION );
+  }
+
+  m_BetaVector.resize( this->GetNumberOfThreads() );
+  m_DenVector.resize( this->GetNumberOfThreads() );
+  m_NbSamples.resize( this->GetNumberOfThreads() );
+}
+
+template <class TInputImage, class TOutputImage>
+void
+FastICAInternalOptimizerVectorImageFilter< TInputImage, TOutputImage >
+::ThreadedGenerateData ( const OutputRegionType & outputRegionForThread, int threadId )
+{
+  InputRegionType inputRegion;
+  this->CallCopyOutputRegionToInputRegion( inputRegion, outputRegionForThread );
+
+  itk::ImageRegionConstIterator< InputImageType > input0It 
+    ( this->GetInput(0), inputRegion );
+  itk::ImageRegionConstIterator< InputImageType > input1It
+    ( this->GetInput(1), inputRegion );
+  itk::ImageRegionIterator< OutputImageType > outputIt
+    ( this->GetOutput(), outputRegionForThread );
+
+  unsigned int nbBands = this->GetInput(0)->GetNumberOfComponentsPerPixel();
+  input0It.GoToBegin();
+  input1It.GoToBegin();
+  outputIt.GoToBegin();
+
+  double beta = 0.;
+  double den = 0.;
+  double nbSample = 0.;
+
+  while ( !input0It.IsAtEnd() && !input1It.IsAtEnd() && !outputIt.IsAtEnd() )
+  {
+    double x = static_cast<double>( input1It.Get()[GetCurrentBandForLoop()] );
+    double g_x = (*m_ContrastFunction)(x);
+
+    double x_g_x = x * g_x;
+    beta += x_g_x;
+
+    double gp = 1. - vcl_pow( g_x, 2. );
+    den += gp;
+
+    nbSample += 1.;
+
+    typename OutputImageType::PixelType z ( nbBands );
+    for ( unsigned int bd = 0; bd < nbBands; bd++ )
+      z[bd] = x * input0It.Get()[bd];
+    outputIt.Set(z);
+
+    ++input0It;
+    ++input1It;
+    ++outputIt;
+  } // end while loop
+
+  m_BetaVector[threadId] = beta;
+  m_DenVector[threadId] = den;
+  m_NbSamples[threadId] = nbSample;
+}
+
+template <class TInputImage, class TOutputImage>
+void
+FastICAInternalOptimizerVectorImageFilter< TInputImage, TOutputImage >
+::AfterThreadedGenerateData ()
+{
+  double beta = 0;
+  double den = 0.;
+  double nbSample = 0;
+
+  for ( int i = 0; i < this->GetNumberOfThreads(); i++ )
+  {
+    beta += m_BetaVector[i];
+    den += m_DenVector[i];
+    nbSample += m_NbSamples[i];
+  }
+
+  m_Beta = beta / nbSample;
+  m_Den = den / nbSample - m_Beta;
+}
+
+} // end of namespace otb
+
+#endif
+
+
+
diff --git a/Code/Hyperspectral/otbFastICAImageFilter.h b/Code/Hyperspectral/otbFastICAImageFilter.h
index 15553eb7be..5c83b010be 100644
--- a/Code/Hyperspectral/otbFastICAImageFilter.h
+++ b/Code/Hyperspectral/otbFastICAImageFilter.h
@@ -22,6 +22,8 @@
 #include "itkImageToImageFilter.h"
 #include "otbPCAImageFilter.h"
 #include "otbMatrixMultiplyImageFilter.h"
+#include "otbStreamingStatisticsVectorImageFilter2.h"
+#include "otbFastICAInternalOptimizerVectorImageFilter.h"
 
 namespace otb 
 {
@@ -74,8 +76,14 @@ public:
   typedef MatrixMultiplyImageFilter< TInputImage, TOutputImage, RealType > TransformFilterType;
   typedef typename TransformFilterType::Pointer TransformFilterPointerType;
 
-  typedef double (*ContrastFunctionType) ( double );
+  typedef FastICAInternalOptimizerVectorImageFilter< InputImageType, InputImageType >
+      InternalOptimizerType;
+  typedef typename InternalOptimizerType::Pointer InternalOptimizerPointerType;
+
+  typedef StreamingStatisticsVectorImageFilter2< InputImageType > MeanEstimatorFilterType;
+  typedef typename MeanEstimatorFilterType::Pointer MeanEstimatorFilterPointerType;
 
+  typedef double (*ContrastFunctionType) ( double );
 
   /** 
    * Set/Get the number of required largest principal components. 
@@ -85,11 +93,9 @@ public:
 
   itkGetConstMacro(PCAFilter,PCAFilterType *);
   itkGetMacro(PCAFilter,PCAFilterType *);
-  itkSetMacro(PCAFilter,PCAFilterType *);
 
   itkGetConstMacro(TransformFilter,TransformFilterType *);
   itkGetMacro(TransformFilter,TransformFilterType *);
-  itkSetMacro(TransformFilter,TransformFilterType *);
 
   VectorType GetMeanValues () const
   {
@@ -113,7 +119,7 @@ public:
   {
     return this->GetPCAFilter()->GetTransformationMatrix();
   }
-  void SetPCACovarianceMatrix ( const MatrixType & mat, bool isForward = true )
+  void SetPCATransformationMatrix ( const MatrixType & mat, bool isForward = true )
   {
     m_PCAFilter->SetTransformationMatrix(mat,isForward);
   }
@@ -128,14 +134,17 @@ public:
     this->Modified();
   }
 
-  itkGetMacro(MaximumOfIterations,unsigned int);
-  itkSetMacro(MaximumOfIterations,unsigned int);
+  itkGetMacro(NumberOfIterations,unsigned int);
+  itkSetMacro(NumberOfIterations,unsigned int);
 
   itkGetMacro(ConvergenceThreshold,double);
   itkSetMacro(ConvergenceThreshold,double);
 
   itkGetMacro(ContrastFunction,ContrastFunctionType);
 
+  itkGetMacro(Mu,double);
+  itkSetMacro(Mu,double);
+
 protected:
   FastICAImageFilter ();
   virtual ~FastICAImageFilter() { }
@@ -155,8 +164,6 @@ protected:
    */
   virtual void GenerateData ();
 
-  void PrintSelf(std::ostream& os, itk::Indent indent) const;
-
   /** Internal methods */
   virtual void ForwardGenerateData();
   virtual void ReverseGenerateData();
@@ -172,9 +179,10 @@ protected:
   MatrixType m_TransformationMatrix;
 
   /** FastICA parameters */
-  unsigned int m_MaximumOfIterations; // def is 50
+  unsigned int m_NumberOfIterations; // def is 50
   double m_ConvergenceThreshold; // def is 1e-4
-  ContrastFunctionType m_ConstrastFunction; // see g() function in the biblio. Def is tanh
+  ContrastFunctionType m_ContrastFunction; // see g() function in the biblio. Def is tanh
+  double m_Mu; // def is 1. in [0,1]
 
   PCAFilterPointerType m_PCAFilter;
   TransformFilterPointerType m_TransformFilter;
diff --git a/Code/Hyperspectral/otbFastICAImageFilter.txx b/Code/Hyperspectral/otbFastICAImageFilter.txx
index 05ff275418..e758496f5d 100644
--- a/Code/Hyperspectral/otbFastICAImageFilter.txx
+++ b/Code/Hyperspectral/otbFastICAImageFilter.txx
@@ -23,6 +23,7 @@
 
 #include "itkExceptionObject.h"
 #include "itkNumericTraits.h"
+#include "itkProgressReporter.h"
 
 #include <vnl/vnl_matrix.h>
 #include <vnl/algo/vnl_matrix_inverse.h>
@@ -43,12 +44,13 @@ FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
   m_GivenTransformationMatrix = false;
   m_IsTransformationForward = true;
 
-  m_MaximumOfIterations = 50;
+  m_NumberOfIterations = 50;
   m_ConvergenceThreshold = 1E-4;
-  m_ConstrastFunction = &vcl_tanh;
+  m_ContrastFunction = &vcl_tanh;
+  m_Mu = 1.;
 
   m_PCAFilter = PCAFilterType::New();
-  m_PCAFilter->UseNormalization();
+  m_PCAFilter->SetUseNormalization(true);
 
   m_TransformFilter = TransformFilterType::New();
 }
@@ -86,11 +88,6 @@ FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
         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__,
@@ -216,31 +213,81 @@ void
 FastICAImageFilter< TInputImage, TOutputImage, TDirectionOfTransformation >
 ::GenerateTransformationMatrix ()
 {
-  double convergence = itk::NumericTraits<double>::Max();
+  itk::ProgressReporter reporter ( this, 0, GetNumberOfIterations(), GetNumberOfIterations() );
+
+  double convergence = itk::NumericTraits<double>::max();
   unsigned int iteration = 0;
 
-  unsigned int size = this->GetInput()->GetNumberOfComponentsPerPixel();
+  const unsigned int size = this->GetInput()->GetNumberOfComponentsPerPixel();
 
   // transformation matrix
   InternalMatrixType W ( size, size, vnl_matrix_identity );
 
-  while ( iteration++ < GetMaximumOfIterations() 
+  while ( iteration++ < GetNumberOfIterations() 
           && convergence > GetConvergenceThreshold() )
   {
-    otbMsgDebugMacro( "Iteration " << iteration << " / " << GetMaximumOfIterations() 
-      << ", MSE = " << convergence );
-
     InternalMatrixType W_old ( W );
 
-    // TODO le premier coup ne sert a rien
+    typename InputImageType::Pointer img = const_cast<InputImageType*>( this->GetInput() );
     TransformFilterPointerType transformer = TransformFilterType::New();
-    transformer->SetIntput( GetPCAFilter()->GetOutput() );
-    transformer->SetMatrix( W );
-    transformer->Update();
+    if ( !W.is_identity() )
+    {
+      transformer->SetInput( GetPCAFilter()->GetOutput() );
+      transformer->SetMatrix( W );
+      transformer->Update();
+      img = const_cast<InputImageType*>( transformer->GetOutput() );
+    }
 
-    // Faire un image to image filter...
+    for ( unsigned int band = 0; band < size; band++ )
+    {
+      InternalOptimizerPointerType optimizer = InternalOptimizerType::New();
+      optimizer->SetInput( 0, m_PCAFilter->GetOutput() );
+      optimizer->SetInput( 1, img );
+      optimizer->SetW( W );
+      optimizer->SetContrastFunction( this->GetContrastFunction() );
+      optimizer->SetCurrentBandForLoop( band );
+
+      MeanEstimatorFilterPointerType estimator = MeanEstimatorFilterType::New();
+      estimator->SetInput( optimizer->GetOutput() );
+      estimator->Update();
+
+      double norm = 0.;
+      for ( unsigned int bd = 0; bd < size; bd++ )
+      {
+        W(bd,band) -= m_Mu * ( estimator->GetMean()[bd] 
+                              - optimizer->GetBeta() * W(bd,band) / optimizer->GetDen() );
+        norm += vcl_pow( W(bd,band), 2. );
+      }
+      for ( unsigned int bd = 0; bd < size; bd++ )
+        W(bd,band) /= norm;
+    }
 
+    // Decorrelation of the W vectors
+    InternalMatrixType W_tmp = W * W.transpose();
+    InternalMatrixType Id ( W.rows(), W.cols(), vnl_matrix_identity );
+    vnl_generalized_eigensystem solver ( W_tmp, Id );
+    InternalMatrixType valP = solver.D;
+    for ( unsigned int i = 0; i < valP.size(); i++ )
+      valP(i,i) = 1. / vcl_sqrt( static_cast<double>( valP(i,i) ) ); // Watch for 0 or neg
+    W_tmp = solver.V * valP * solver.V.transpose();
+    W = W.transpose() * W;
+
+    // Convergence evaluation
+    convergence = 0.;
+    for ( unsigned int i = 0; i < W.rows(); i++ )
+      for ( unsigned int j = 0; j < W.cols(); j++ )
+        convergence += vcl_abs( W(i,j) - W_old(i,j) );
+
+    reporter.CompletedPixel();
   } // end of while loop
+
+  if ( size != this->GetNumberOfPrincipalComponentsRequired() )
+    this->m_TransformationMatrix = W.get_n_rows( 0, this->GetNumberOfPrincipalComponentsRequired() );
+  else
+    this->m_TransformationMatrix = W;
+
+  otbMsgDebugMacro( << "Final convergence " << convergence 
+    << " after " << iteration << " iterations" );
 }
 
 } // end of namespace otb
-- 
GitLab