From 0bd70bd4beefabca6b0223b0ee787e19d330cce2 Mon Sep 17 00:00:00 2001
From: Aurelien Bricier <aurelien.bricier@c-s.fr>
Date: Tue, 12 Oct 2010 17:54:10 +0200
Subject: [PATCH] ENH: added one-pass computation of multiple complex moments

---
 .../otbComplexMomentImageFunction.h           |  58 ++++---
 .../otbComplexMomentImageFunction.txx         | 148 ++++++++++--------
 .../ComplexMomentImageExample.cxx             |  13 +-
 Testing/Code/FeatureExtraction/CMakeLists.txt |   4 +-
 .../otbComplexMomentImage.cxx                 |  22 +--
 5 files changed, 141 insertions(+), 104 deletions(-)

diff --git a/Code/FeatureExtraction/otbComplexMomentImageFunction.h b/Code/FeatureExtraction/otbComplexMomentImageFunction.h
index 1b086db60a..a3223780e8 100644
--- a/Code/FeatureExtraction/otbComplexMomentImageFunction.h
+++ b/Code/FeatureExtraction/otbComplexMomentImageFunction.h
@@ -18,7 +18,7 @@
 #ifndef __otbComplexMomentImageFunction_h
 #define __otbComplexMomentImageFunction_h
 
-#include "otbGeometricMomentImageFunction.h"
+#include "itkImageFunction.h"
 
 #include <complex>
 
@@ -45,43 +45,48 @@ namespace otb
  *
  * \ingroup ImageFunctions
  */
-template <class TInput,
-    class TOutput = std::complex<double>,
-    class TPrecision = double,
-    class TCoordRep = float>
+
+template <class TInputImage, class TCoordRep = float>
 class ITK_EXPORT ComplexMomentImageFunction :
-  public GeometricMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep>
+    public itk::ImageFunction <TInputImage,
+      std::vector< std::vector< std::complex<double> > >,
+      TCoordRep>
 {
 public:
   /** Standard class typedefs. */
   typedef ComplexMomentImageFunction                                           Self;
-  typedef GeometricMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep> Superclass;
+  typedef itk::ImageFunction<TInputImage, 
+                             std::vector<
+                             std::vector<
+                             std::complex<double> > >, 
+                             TCoordRep>                                        Superclass;
   typedef itk::SmartPointer<Self>                                              Pointer;
   typedef itk::SmartPointer<const Self>                                        ConstPointer;
 
   /** Run-time type information (and related methods). */
-  itkTypeMacro(ComplexMomentImageFunction, GeometricMomentImageFunction);
+  itkTypeMacro(ComplexMomentImageFunction, ImageFunction);
 
   /** Method for creation through the object factory. */
   itkNewMacro(Self);
 
   /** InputImageType typedef support. */
-  typedef typename Superclass::InputType           InputType;
+  typedef TInputImage                              InputImageType;
   typedef typename Superclass::IndexType           IndexType;
   typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
   typedef typename Superclass::PointType           PointType;
 
-  typedef typename Superclass::OutputType ComplexType;
-
-  /** Type for calculation precision */
-  typedef typename Superclass::PrecisionType PrecisionType;
+  typedef float                                    ScalarRealType;
 
-  /** ComplexType for calculation precision */
-  typedef std::complex<PrecisionType> ComplexPrecisionType;
+  typedef typename Superclass::OutputType          ComplexType;
+  typedef typename std::complex<ScalarRealType>    ScalarComplexType;
 
+  /** Dimension of the underlying image. */
+  itkStaticConstMacro(ImageDimension, unsigned int,
+                      InputImageType::ImageDimension);
+  
   /** Evalulate the function at specified index */
   virtual ComplexType EvaluateAtIndex(const IndexType& index) const;
-
+  
   /** Evaluate the function at non-integer positions */
   virtual ComplexType Evaluate(const PointType& point) const
   {
@@ -96,11 +101,17 @@ public:
     this->ConvertContinuousIndexToNearestIndex(cindex, index);
     return this->EvaluateAtIndex(index);
   }
-
-  itkSetMacro(P, unsigned int);
-  itkGetConstReferenceMacro(P, unsigned int);
-  itkSetMacro(Q, unsigned int);
-  itkGetConstReferenceMacro(Q, unsigned int);
+  
+  /** Get/Set the radius of the neighborhood over which the
+   *  statistics are evaluated 
+   */
+  itkSetMacro( NeighborhoodRadius, unsigned int );
+  itkGetConstReferenceMacro( NeighborhoodRadius, unsigned int );
+  
+  itkSetMacro(Pmax, unsigned int);
+  itkGetConstReferenceMacro(Pmax, unsigned int);
+  itkSetMacro(Qmax, unsigned int);
+  itkGetConstReferenceMacro(Qmax, unsigned int);
 
 protected:
   ComplexMomentImageFunction();
@@ -111,8 +122,9 @@ private:
   ComplexMomentImageFunction(const Self &);  //purposely not implemented
   void operator =(const Self&);  //purposely not implemented
 
-  unsigned int m_P;
-  unsigned int m_Q;
+  unsigned int m_Pmax;
+  unsigned int m_Qmax;
+  unsigned int m_NeighborhoodRadius;
 
 };
 
diff --git a/Code/FeatureExtraction/otbComplexMomentImageFunction.txx b/Code/FeatureExtraction/otbComplexMomentImageFunction.txx
index d1eeebc2ec..96106d52c0 100644
--- a/Code/FeatureExtraction/otbComplexMomentImageFunction.txx
+++ b/Code/FeatureExtraction/otbComplexMomentImageFunction.txx
@@ -19,11 +19,9 @@
 #define __otbComplexMomentImageFunction_txx
 
 #include "otbComplexMomentImageFunction.h"
-#include "itkImageRegionIterator.h"
-#include "itkImage.h"
 #include "itkConstNeighborhoodIterator.h"
 #include "itkNumericTraits.h"
-#include "otbMacro.h"
+#include "itkMacro.h"
 
 namespace otb
 {
@@ -31,96 +29,124 @@ namespace otb
 /**
    * Constructor
    */
-template <class TInput, class TOutput, class TPrecision, class TCoordRep>
-ComplexMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep>
+template <class TInputImage, class TCoordRep>
+ComplexMomentImageFunction<TInputImage, TCoordRep>
 ::ComplexMomentImageFunction()
 {
-  m_P = 0;
-  m_Q = 0;
+  m_NeighborhoodRadius = 1;
+  m_Pmax = 4;
+  m_Qmax = 4;
 }
 
 /**
    *
    */
-template <class TInput, class TOutput, class TPrecision, class TCoordRep>
+template <class TInputImage, class TCoordRep>
 void
-ComplexMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep>
+ComplexMomentImageFunction<TInputImage, TCoordRep>
 ::PrintSelf(std::ostream& os, itk::Indent indent) const
 {
   this->Superclass::PrintSelf(os, indent);
-  os << indent << " p indice value      : "  << m_P << std::endl;
-  os << indent << " q indice value      : "  << m_Q << std::endl;
+  os << indent << " p indice maximum value      : "  << m_Pmax << std::endl;
+  os << indent << " q indice maximum value      : "  << m_Qmax << std::endl;
+  os << indent << " Neighborhood radius value   : "  << m_NeighborhoodRadius << std::endl;
 }
 
-template <class TInput, class TOutput, class TPrecision, class TCoordRep>
-typename ComplexMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep>::ComplexType
-ComplexMomentImageFunction<TInput, TOutput, TPrecision, TCoordRep>
+template <class TInputImage, class TCoordRep>
+typename ComplexMomentImageFunction<TInputImage, TCoordRep>::ComplexType
+ComplexMomentImageFunction<TInputImage, TCoordRep>
 ::EvaluateAtIndex(const IndexType& index) const
 {
-  typename TInput::SizeType ImageSize;
-  ComplexPrecisionType      Sum;
-  ComplexPrecisionType      ValP;
-  ComplexPrecisionType      ValQ;
-  PrecisionType             Norm;
-  IndexType                 IndexValue;
-  IndexType                 indexPos = index;
-  typename TInput::SizeType kernelSize;
-
-  if (!this->GetInputImage())
+  // Build moments vector
+  ComplexType moments;//<--- pb VariableLengthVector : compute the length before
+  moments.resize(m_Pmax+1);
+  
+  std::vector<ScalarComplexType> valXpY, valXqY;
+  valXpY.resize(m_Pmax+1);
+  valXqY.resize(m_Qmax+1);
+    
+  // Initialize moments
+  for (unsigned int p = 0; p <= m_Pmax; p++)
     {
-    otbMsgDevMacro(<< "Pb with GetInputImage");
-    return (ComplexType(itk::NumericTraits<PrecisionType>::Zero, itk::NumericTraits<PrecisionType>::Zero));
+    moments.at(p).resize(m_Qmax+1);
+    valXpY.at(p) = ScalarComplexType(1.0,0.0);
+    for (unsigned int q = 0; q <= m_Qmax; q++)
+      {
+      moments.at(p).at(q) =  ScalarComplexType(0.0,0.0);
+      valXqY.at(q)        =  ScalarComplexType(1.0,0.0);
+      }
     }
-
-  if (this->GetNeighborhoodRadius() < 0)
+  std::cout << "moments initiated!" << std::endl;
+  // Check for input image
+  if( !this->GetInputImage() )
     {
-    ImageSize = this->GetInputImage()->GetBufferedRegion().GetSize();
-
-    indexPos[0] = ImageSize[0] / 2;
-    indexPos[1] = ImageSize[1] / 2;
-
-    kernelSize[0] = indexPos[0];
-    kernelSize[1] = indexPos[1];
+    return moments;
     }
-  else
+  
+  // Check for out of buffer
+  if ( !this->IsInsideBuffer( index ) )
     {
-    kernelSize.Fill(this->GetNeighborhoodRadius());
+    return moments;
     }
-
-  itk::ConstNeighborhoodIterator<TInput>
-  it(kernelSize, this->GetInputImage(), this->GetInputImage()->GetBufferedRegion());
-
+  
+  // Create an N-d neighborhood kernel, using a zeroflux boundary condition
+  typename InputImageType::SizeType kernelSize;
+  kernelSize.Fill( m_NeighborhoodRadius );
+  
+  itk::ConstNeighborhoodIterator<InputImageType>
+    it(kernelSize, this->GetInputImage(), this->GetInputImage()->GetBufferedRegion());
+  
   // Set the iterator at the desired location
-  it.SetLocation(indexPos);
-  Sum = ComplexPrecisionType(0.0, 0.0);
-  Norm = 0;
-
+  it.SetLocation(index);
+  
+  // Walk the neighborhood
   const unsigned int size = it.Size();
   for (unsigned int i = 0; i < size; ++i)
     {
-    IndexValue = it.GetIndex(i);
-    ValP = ComplexPrecisionType(1.0, 0.0);
-    ValQ = ComplexPrecisionType(1.0, 0.0);
-    unsigned int p  = m_P;
-    while (p > 0)
+    // Retrieve value, and centered-reduced position
+    ScalarRealType value = static_cast<ScalarRealType>(it.GetPixel(i));
+    ScalarRealType     x = static_cast<ScalarRealType>(it.GetOffset(i)[0]);
+    ScalarRealType     y = static_cast<ScalarRealType>(it.GetOffset(i)[1]);
+    
+    // Build complex value
+    ScalarComplexType xpy(x,y),xqy(x,-y);
+    
+    unsigned int pTmp = 1;
+    unsigned int qTmp = 1;
+    
+    while (pTmp <= m_Pmax)
       {
-      ValP *= ComplexPrecisionType(IndexValue[0] - indexPos[0], IndexValue[1] - indexPos[1]);
-      --p;
+      valXpY.at(pTmp) = valXpY.at(pTmp-1) * xpy;
+      pTmp ++;
       }
-    unsigned int q  = m_Q;
-    while (q > 0)
+    while (qTmp <= m_Qmax)
       {
-      ValQ *= ComplexPrecisionType(IndexValue[0] - indexPos[0], -(IndexValue[1] - indexPos[1]));
-      --q;
+      valXqY.at(qTmp) = valXqY.at(qTmp-1) * xqy;
+      qTmp ++;
       }
+    
 
-    Sum += (ValP * ValQ * ComplexPrecisionType(static_cast<PrecisionType>(it.GetPixel(i)), 0.0));
-    Norm += it.GetPixel(i);
+    // Update cumulants
+    for (unsigned int p = 0; p <= m_Pmax; p++)
+      {
+      for (unsigned int q= 0; q <= m_Qmax; q++)
+        {
+        moments.at(p).at(q) += valXpY.at(p) * valXqY.at(q) * value;   
+        }
+      }
+    }
+  
+  // Normalisation
+  for (unsigned int p = 0; p <= m_Pmax; p++)
+    {
+    for (unsigned int q= 0; q <= m_Qmax; q++)
+      {
+      moments.at(p).at(q) /= vcl_pow(moments.at(0).at(0), (p+q)/2);   
+      }
     }
 
-  Norm = vcl_pow(Norm, ((PrecisionType) m_P + (PrecisionType) m_Q) / 2.);
-
-  return (static_cast<ComplexType>(Sum / Norm));
+  // Return result
+  return moments;
 }
 
 } // namespace otb
diff --git a/Examples/FeatureExtraction/ComplexMomentImageExample.cxx b/Examples/FeatureExtraction/ComplexMomentImageExample.cxx
index aedbf9bf9e..1ba7425254 100644
--- a/Examples/FeatureExtraction/ComplexMomentImageExample.cxx
+++ b/Examples/FeatureExtraction/ComplexMomentImageExample.cxx
@@ -75,10 +75,9 @@ int main(int argc, char * argv[])
   //  Software Guide : EndLatex
 
   // Software Guide : BeginCodeSnippet
-  typedef std::complex<float>
-  ComplexType;
-  typedef otb::ComplexMomentImageFunction<InputImageType, ComplexType> CMType;
-
+  typedef otb::ComplexMomentImageFunction<InputImageType> CMType;
+  typedef CMType::ComplexType ComplexType;
+ 
   CMType::Pointer cmFunction = CMType::New();
   // Software Guide : EndCodeSnippet
 
@@ -92,8 +91,8 @@ int main(int argc, char * argv[])
   // Software Guide : BeginCodeSnippet
   reader->Update();
   cmFunction->SetInputImage(reader->GetOutput());
-  cmFunction->SetQ(Q);
-  cmFunction->SetP(P);
+  cmFunction->SetQmax(Q);
+  cmFunction->SetPmax(P);
   // Software Guide : EndCodeSnippet
 
   //  Software Guide : BeginLatex
@@ -126,7 +125,7 @@ int main(int argc, char * argv[])
   ComplexType Result = cmFunction->EvaluateAtIndex(center);
 
   std::cout << "The moment of order (" << P << "," << Q <<
-  ") is equal to " << Result << std::endl;
+  ") is equal to " << Result.at(P).at(Q) << std::endl;
   // Software Guide : EndCodeSnippet
 
   return EXIT_SUCCESS;
diff --git a/Testing/Code/FeatureExtraction/CMakeLists.txt b/Testing/Code/FeatureExtraction/CMakeLists.txt
index 30506970fc..e95f12a706 100644
--- a/Testing/Code/FeatureExtraction/CMakeLists.txt
+++ b/Testing/Code/FeatureExtraction/CMakeLists.txt
@@ -69,8 +69,8 @@ ADD_TEST(feTvComplexMomentImage ${FEATUREEXTRACTION_TESTS1}
                         ${TEMP}/feComplexMomentImage.txt
         otbComplexMomentImage
 	${INPUTDATA}/TeteAToto.png
-  1
-  1
+  4
+  4
   ${TEMP}/feComplexMomentImage.txt)
 
 ADD_TEST(feTvHuImage ${FEATUREEXTRACTION_TESTS1}
diff --git a/Testing/Code/FeatureExtraction/otbComplexMomentImage.cxx b/Testing/Code/FeatureExtraction/otbComplexMomentImage.cxx
index 2e37798187..3124331437 100644
--- a/Testing/Code/FeatureExtraction/otbComplexMomentImage.cxx
+++ b/Testing/Code/FeatureExtraction/otbComplexMomentImage.cxx
@@ -42,8 +42,8 @@ int otbComplexMomentImage(int argc, char * argv[])
   typedef itk::Image<InputPixelType,  Dimension> InputImageType;
   typedef otb::ImageFileReader<InputImageType>   ReaderType;
 
-  typedef std::complex<float>                                          ComplexType;
-  typedef otb::ComplexMomentImageFunction<InputImageType, ComplexType> CMType;
+  typedef otb::ComplexMomentImageFunction<InputImageType> CMType;
+  typedef CMType::ComplexType                             ComplexType;
 
   ReaderType::Pointer reader         = ReaderType::New();
   CMType::Pointer     function = CMType::New();
@@ -53,8 +53,8 @@ int otbComplexMomentImage(int argc, char * argv[])
   reader->Update();
   function->SetInputImage(reader->GetOutput());
 
-  function->SetQ(q);
-  function->SetP(p);
+  function->SetQmax(q);
+  function->SetPmax(p);
 
   InputImageType::IndexType index;
   index[0] = 10;
@@ -64,15 +64,15 @@ int otbComplexMomentImage(int argc, char * argv[])
 
   std::ofstream outputStream(outputFilename);
 
-  Result = function->EvaluateAtIndex(index);
-  outputStream << std::setprecision(10) << "function->EvaluateAtIndex( index ): " << Result << std::endl;
-
-  outputStream << " With NeighborhoodRadius(3):" << std::endl;
-
   function->SetNeighborhoodRadius(3);
   Result = function->EvaluateAtIndex(index);
-  outputStream << "function->EvaluateAtIndex( index ): " << Result << std::endl;
-
+  for (unsigned int k=0; k<=p; k++)
+    {
+    for (unsigned int l=0; l<=q; l++)
+      {
+      outputStream << "ComplexMoment c(" << k << l << ") : " << Result.at(k).at(l) << std::endl;
+      }
+    }
   outputStream.close();
 
   return EXIT_SUCCESS;
-- 
GitLab