From bc807bce5d20293faa6d593ce4686ee2a80e2096 Mon Sep 17 00:00:00 2001
From: Guillaume Pasero <guillaume.pasero@c-s.fr>
Date: Thu, 12 Jan 2017 11:25:13 +0100
Subject: [PATCH] ENH: implement sub-sampled computation in Advanced and
 HigherOrder textures

---
 .../otbScalarImageToAdvancedTexturesFilter.h  | 19 ++++++
 ...otbScalarImageToAdvancedTexturesFilter.txx | 53 +++++++++++++++-
 ...tbScalarImageToHigherOrderTexturesFilter.h | 20 ++++++
 ...ScalarImageToHigherOrderTexturesFilter.txx | 61 +++++++++++++++++--
 4 files changed, 148 insertions(+), 5 deletions(-)

diff --git a/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.h b/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.h
index 456cf4aca4..42c1a5f073 100644
--- a/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.h
+++ b/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.h
@@ -164,6 +164,18 @@ public:
   /** Get the input image maximum */
   itkGetMacro(InputImageMaximum, InputPixelType);
 
+  /** Set the sub-sampling factor */
+  itkSetMacro(SubsampleFactor, SizeType);
+
+  /** Get the sub-sampling factor */
+  itkGetMacro(SubsampleFactor, SizeType);
+
+  /** Set the sub-sampling offset */
+  itkSetMacro(SubsampleOffset, OffsetType);
+
+  /** Get the sub-sampling offset */
+  itkGetMacro(SubsampleOffset, OffsetType);
+
   /** Get the mean output image */
   OutputImageType * GetMeanOutput();
 
@@ -199,6 +211,8 @@ protected:
   ScalarImageToAdvancedTexturesFilter();
   /** Destructor */
   ~ScalarImageToAdvancedTexturesFilter() ITK_OVERRIDE;
+  /** Generate the output informations */
+  void GenerateOutputInformation() ITK_OVERRIDE;
   /** Generate the input requested region */
   void GenerateInputRequestedRegion() ITK_OVERRIDE;
   /** Before Parallel textures extraction */
@@ -231,6 +245,11 @@ private:
   /** Input image maximum */
   InputPixelType m_InputImageMaximum;
 
+  /** Sub-sampling factor */
+  SizeType m_SubsampleFactor;
+
+  /** Sub-sampling offset */
+  OffsetType m_SubsampleOffset;
 };
 } // End namespace otb
 
diff --git a/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.txx b/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.txx
index e0463ab5c8..80cf17d041 100644
--- a/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.txx
+++ b/Modules/Feature/Textures/include/otbScalarImageToAdvancedTexturesFilter.txx
@@ -37,6 +37,8 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
 , m_NumberOfBinsPerAxis(8)
 , m_InputImageMinimum(0)
 , m_InputImageMaximum(255)
+, m_SubsampleFactor()
+, m_SubsampleOffset()
 {
   // There are 10 outputs corresponding to the 9 textures indices
   this->SetNumberOfRequiredOutputs(10);
@@ -52,6 +54,9 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
   this->SetNthOutput(7, OutputImageType::New());
   this->SetNthOutput(8, OutputImageType::New());
   this->SetNthOutput(9, OutputImageType::New());
+
+  this->m_SubsampleFactor.Fill(1);
+  this->m_SubsampleOffset.Fill(0);
 }
 
 template <class TInputImage, class TOutputImage>
@@ -188,6 +193,38 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
   return static_cast<OutputImageType *>(this->GetOutput(9));
 }
 
+template <class TInputImage, class TOutputImage>
+void
+ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
+::GenerateOutputInformation()
+{
+  // First, call superclass implementation
+  Superclass::GenerateOutputInformation();
+
+  // Compute output size, origin & spacing
+  InputRegionType inputRegion = this->GetInput()->GetLargestPossibleRegion();
+  OutputRegionType outputRegion;
+  outputRegion.SetIndex(0,0);
+  outputRegion.SetIndex(1,0);
+  outputRegion.SetSize(0, 1 + (inputRegion.GetSize(0) - 1 - m_SubsampleOffset[0]) / m_SubsampleFactor[0]);
+  outputRegion.SetSize(1, 1 + (inputRegion.GetSize(1) - 1 - m_SubsampleOffset[1]) / m_SubsampleFactor[1]);
+
+  typename OutputImageType::SpacingType outSpacing = this->GetInput()->GetSpacing();
+  outSpacing[0] *= m_SubsampleFactor[0];
+  outSpacing[1] *= m_SubsampleFactor[1];
+
+  typename OutputImageType::PointType outOrigin;
+  this->GetInput()->TransformIndexToPhysicalPoint(inputRegion.GetIndex()+m_SubsampleOffset,outOrigin);
+
+  for (unsigned int i=0 ; i<this->GetNumberOfOutputs() ; i++)
+    {
+    OutputImagePointerType outputPtr = this->GetOutput(i);
+    outputPtr->SetLargestPossibleRegion(outputRegion);
+    outputPtr->SetOrigin(outOrigin);
+    outputPtr->SetSpacing(outSpacing);
+    }
+}
+
 template <class TInputImage, class TOutputImage>
 void
 ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
@@ -209,12 +246,19 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
   // We use only the first output since requested regions for all outputs are enforced to be equal
   // by the default GenerateOutputRequestedRegiont() implementation
   OutputRegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
+  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
 
   typename OutputRegionType::IndexType outputIndex = outputRequestedRegion.GetIndex();
   typename OutputRegionType::SizeType  outputSize   = outputRequestedRegion.GetSize();
   typename InputRegionType::IndexType  inputIndex;
   typename InputRegionType::SizeType   inputSize;
 
+  // Convert index and size to full grid
+  outputIndex[0] = outputIndex[0] * m_SubsampleFactor[0] + m_SubsampleOffset[0] + inputLargest.GetIndex(0);
+  outputIndex[1] = outputIndex[1] * m_SubsampleFactor[1] + m_SubsampleOffset[1] + inputLargest.GetIndex(1);
+  outputSize[0] = 1 + (outputSize[0] - 1) * m_SubsampleFactor[0];
+  outputSize[1] = 1 + (outputSize[1] - 1) * m_SubsampleFactor[1];
+
   // First, apply offset
   for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim)
     {
@@ -311,6 +355,8 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
   const unsigned int histSize = m_NumberOfBinsPerAxis;
   const long unsigned int twiceHistSize = 2 * m_NumberOfBinsPerAxis;
 
+  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
+
   // Set-up progress reporting
   itk::ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
 
@@ -330,11 +376,16 @@ ScalarImageToAdvancedTexturesFilter<TInputImage, TOutputImage>
     typename InputRegionType::IndexType inputIndex;
     typename InputRegionType::SizeType inputSize;
 
+    // Convert index to full grid
+    typename OutputImageType::IndexType outIndex;
+
     // First, create an window for neighborhood iterator based on m_Radius
     // For example, if xradius and yradius is 2. window size is 5x5 (2 * radius + 1).
     for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim)
       {
-      inputIndex[dim] = varianceIt.GetIndex()[dim] - m_Radius[dim];
+      outIndex[dim] = varianceIt.GetIndex()[dim] * m_SubsampleFactor[dim]
+        + m_SubsampleOffset[dim] + inputLargest.GetIndex(dim);
+      inputIndex[dim] = outIndex[dim] - m_Radius[dim];
       inputSize[dim] = 2 * m_Radius[dim] + 1;
       }
 
diff --git a/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.h b/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.h
index 4879c7565a..781d69f80b 100644
--- a/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.h
+++ b/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.h
@@ -148,6 +148,18 @@ public:
   itkSetMacro(FastCalculations, bool);
   itkBooleanMacro(FastCalculations);
 
+  /** Set the sub-sampling factor */
+  itkSetMacro(SubsampleFactor, SizeType);
+
+  /** Get the sub-sampling factor */
+  itkGetMacro(SubsampleFactor, SizeType);
+
+  /** Set the sub-sampling offset */
+  itkSetMacro(SubsampleOffset, OffsetType);
+
+  /** Get the sub-sampling offset */
+  itkGetMacro(SubsampleOffset, OffsetType);
+
   /** Get the Short Run Emphasis output image */
   OutputImageType * GetShortRunEmphasisOutput();
 
@@ -186,6 +198,8 @@ protected:
   ScalarImageToHigherOrderTexturesFilter();
   /** Destructor */
   ~ScalarImageToHigherOrderTexturesFilter() ITK_OVERRIDE;
+  /** Generate the output informations */
+  void GenerateOutputInformation() ITK_OVERRIDE;
   /** Generate the input requested region */
   void GenerateInputRequestedRegion() ITK_OVERRIDE;
   /** Parallel textures extraction */
@@ -215,6 +229,12 @@ private:
 
   /** Fast calculation */
   bool m_FastCalculations;
+
+  /** Sub-sampling factor */
+  SizeType m_SubsampleFactor;
+
+  /** Sub-sampling offset */
+  OffsetType m_SubsampleOffset;
 };
 } // End namespace otb
 
diff --git a/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.txx b/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.txx
index 66d4a4b65a..fecb7ae6bb 100644
--- a/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.txx
+++ b/Modules/Feature/Textures/include/otbScalarImageToHigherOrderTexturesFilter.txx
@@ -32,7 +32,9 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
   m_NumberOfBinsPerAxis(8),
   m_InputImageMinimum(0),
   m_InputImageMaximum(255),
-  m_FastCalculations(false)
+  m_FastCalculations(false),
+  m_SubsampleFactor(),
+  m_SubsampleOffset()
 {
   // There are 11 outputs corresponding to the 8 textures indices
   this->SetNumberOfRequiredOutputs(10);
@@ -71,6 +73,8 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
     }
   this->SetOffsets( offsets );
 
+  this->m_SubsampleFactor.Fill(1);
+  this->m_SubsampleOffset.Fill(0);
 }
 
 template <class TInputImage, class TOutputImage>
@@ -231,6 +235,38 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
   this->SetOffsets( offsetVector );
 }
 
+template <class TInputImage, class TOutputImage>
+void
+ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
+::GenerateOutputInformation()
+{
+  // First, call superclass implementation
+  Superclass::GenerateOutputInformation();
+
+  // Compute output size, origin & spacing
+  InputRegionType inputRegion = this->GetInput()->GetLargestPossibleRegion();
+  OutputRegionType outputRegion;
+  outputRegion.SetIndex(0,0);
+  outputRegion.SetIndex(1,0);
+  outputRegion.SetSize(0, 1 + (inputRegion.GetSize(0) - 1 - m_SubsampleOffset[0]) / m_SubsampleFactor[0]);
+  outputRegion.SetSize(1, 1 + (inputRegion.GetSize(1) - 1 - m_SubsampleOffset[1]) / m_SubsampleFactor[1]);
+
+  typename OutputImageType::SpacingType outSpacing = this->GetInput()->GetSpacing();
+  outSpacing[0] *= m_SubsampleFactor[0];
+  outSpacing[1] *= m_SubsampleFactor[1];
+
+  typename OutputImageType::PointType outOrigin;
+  this->GetInput()->TransformIndexToPhysicalPoint(inputRegion.GetIndex()+m_SubsampleOffset,outOrigin);
+
+  for (unsigned int i=0 ; i<this->GetNumberOfOutputs() ; i++)
+    {
+    OutputImagePointerType outputPtr = this->GetOutput(i);
+    outputPtr->SetLargestPossibleRegion(outputRegion);
+    outputPtr->SetOrigin(outOrigin);
+    outputPtr->SetSpacing(outSpacing);
+    }
+}
+
 template <class TInputImage, class TOutputImage>
 void
 ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
@@ -252,7 +288,17 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
   // We use only the first output since requested regions for all outputs are enforced to be equal
   // by the default GenerateOutputRequestedRegiont() implementation
   OutputRegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
-  InputRegionType inputRequestedRegion = outputRequestedRegion;
+  typename OutputRegionType::IndexType outputIndex = outputRequestedRegion.GetIndex();
+  typename OutputRegionType::SizeType  outputSize   = outputRequestedRegion.GetSize();
+  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
+
+  // Convert index and size to full grid
+  outputIndex[0] = outputIndex[0] * m_SubsampleFactor[0] + m_SubsampleOffset[0] + inputLargest.GetIndex(0);
+  outputIndex[1] = outputIndex[1] * m_SubsampleFactor[1] + m_SubsampleOffset[1] + inputLargest.GetIndex(1);
+  outputSize[0] = 1 + (outputSize[0] - 1) * m_SubsampleFactor[0];
+  outputSize[1] = 1 + (outputSize[1] - 1) * m_SubsampleFactor[1];
+  
+  InputRegionType inputRequestedRegion(outputIndex,outputSize);
 
   // Apply the radius
   inputRequestedRegion.PadByRadius(m_Radius);
@@ -297,6 +343,8 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
   inputPtr->TransformIndexToPhysicalPoint( outputImagesIterators[0].GetIndex() + m_Radius, bottomRightPoint );
   double maxDistance = topLeftPoint.EuclideanDistanceTo(bottomRightPoint);
 
+  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
+
   // Set-up progress reporting
   itk::ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
 
@@ -304,12 +352,17 @@ ScalarImageToHigherOrderTexturesFilter<TInputImage, TOutputImage>
   while ( !outputImagesIterators[0].IsAtEnd() )
     {
     // Compute the region on which run-length matrix will be estimated
-    typename InputRegionType::IndexType inputIndex = outputImagesIterators[0].GetIndex() - m_Radius;
+    typename InputRegionType::IndexType inputIndex;
     typename InputRegionType::SizeType  inputSize;
 
-    // First, apply offset
+    // Convert index to full grid
+    typename OutputImageType::IndexType outIndex;
+
     for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim)
       {
+      outIndex[dim] = outputImagesIterators[0].GetIndex()[dim] * m_SubsampleFactor[dim]
+        + m_SubsampleOffset[dim] + inputLargest.GetIndex(dim);
+      inputIndex[dim] = outIndex[dim] - m_Radius[dim];
       inputSize[dim] = 2 * m_Radius[dim] + 1;
       }
 
-- 
GitLab