OTB  10.0.0
Orfeo Toolbox
otbScalarImageToAdvancedTexturesFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2024 Centre National d'Etudes Spatiales (CNES)
3  *
4  * This file is part of Orfeo Toolbox
5  *
6  * https://www.orfeo-toolbox.org/
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef otbScalarImageToAdvancedTexturesFilter_hxx
22 #define otbScalarImageToAdvancedTexturesFilter_hxx
23 
25 #include "itkImageRegionIteratorWithIndex.h"
26 #include "itkConstNeighborhoodIterator.h"
27 #include "itkImageRegionIterator.h"
28 #include "itkProgressReporter.h"
29 #include "itkNumericTraits.h"
30 #include <algorithm>
31 #include "otbMacro.h" //for
32 namespace otb
33 {
34 template <class TInputImage, class TOutputImage>
36  : m_Radius(),
37  m_Offset(),
38  m_NeighborhoodRadius(),
39  m_NumberOfBinsPerAxis(8),
40  m_InputImageMinimum(0),
41  m_InputImageMaximum(255),
42  m_SubsampleFactor(),
43  m_SubsampleOffset()
44 {
45  this->DynamicMultiThreadingOn();
46  // There are 10 outputs corresponding to the 9 textures indices
47  this->SetNumberOfRequiredOutputs(10);
48 
49  // Create the 10 outputs
50  this->SetNthOutput(0, OutputImageType::New());
51  this->SetNthOutput(1, OutputImageType::New());
52  this->SetNthOutput(2, OutputImageType::New());
53  this->SetNthOutput(3, OutputImageType::New());
54  this->SetNthOutput(4, OutputImageType::New());
55  this->SetNthOutput(5, OutputImageType::New());
56  this->SetNthOutput(6, OutputImageType::New());
57  this->SetNthOutput(7, OutputImageType::New());
58  this->SetNthOutput(8, OutputImageType::New());
59  this->SetNthOutput(9, OutputImageType::New());
60 
61  this->m_SubsampleFactor.Fill(1);
62  this->m_SubsampleOffset.Fill(0);
63 }
64 
65 template <class TInputImage, class TOutputImage>
67 {
68 }
69 template <class TInputImage, class TOutputImage>
72 {
73  if (this->GetNumberOfOutputs() < 1)
74  {
75  return nullptr;
76  }
77  return static_cast<OutputImageType*>(this->GetOutput(0));
78 }
79 
80 template <class TInputImage, class TOutputImage>
83 {
84  if (this->GetNumberOfOutputs() < 2)
85  {
86  return nullptr;
87  }
88  return static_cast<OutputImageType*>(this->GetOutput(1));
89 }
90 
91 template <class TInputImage, class TOutputImage>
94 {
95  if (this->GetNumberOfOutputs() < 3)
96  {
97  return nullptr;
98  }
99  return static_cast<OutputImageType*>(this->GetOutput(2));
100 }
101 
102 template <class TInputImage, class TOutputImage>
105 {
106  if (this->GetNumberOfOutputs() < 4)
107  {
108  return nullptr;
109  }
110  return static_cast<OutputImageType*>(this->GetOutput(3));
111 }
112 
113 template <class TInputImage, class TOutputImage>
116 {
117  if (this->GetNumberOfOutputs() < 5)
118  {
119  return nullptr;
120  }
121  return static_cast<OutputImageType*>(this->GetOutput(4));
122 }
123 
124 template <class TInputImage, class TOutputImage>
127 {
128  if (this->GetNumberOfOutputs() < 6)
129  {
130  return nullptr;
131  }
132  return static_cast<OutputImageType*>(this->GetOutput(5));
133 }
134 
135 template <class TInputImage, class TOutputImage>
138 {
139  if (this->GetNumberOfOutputs() < 7)
140  {
141  return nullptr;
142  }
143  return static_cast<OutputImageType*>(this->GetOutput(6));
144 }
145 
146 template <class TInputImage, class TOutputImage>
149 {
150  if (this->GetNumberOfOutputs() < 8)
151  {
152  return nullptr;
153  }
154  return static_cast<OutputImageType*>(this->GetOutput(7));
155 }
156 
157 template <class TInputImage, class TOutputImage>
160 {
161  if (this->GetNumberOfOutputs() < 9)
162  {
163  return nullptr;
164  }
165  return static_cast<OutputImageType*>(this->GetOutput(8));
166 }
167 
168 template <class TInputImage, class TOutputImage>
171 {
172  if (this->GetNumberOfOutputs() < 10)
173  {
174  return nullptr;
175  }
176  return static_cast<OutputImageType*>(this->GetOutput(9));
177 }
178 
179 template <class TInputImage, class TOutputImage>
181 {
182  // First, call superclass implementation
183  Superclass::GenerateOutputInformation();
184 
185  // Compute output size, origin & spacing
186  InputRegionType inputRegion = this->GetInput()->GetLargestPossibleRegion();
187  OutputRegionType outputRegion;
188  outputRegion.SetIndex(0, 0);
189  outputRegion.SetIndex(1, 0);
190  outputRegion.SetSize(0, 1 + (inputRegion.GetSize(0) - 1 - m_SubsampleOffset[0]) / m_SubsampleFactor[0]);
191  outputRegion.SetSize(1, 1 + (inputRegion.GetSize(1) - 1 - m_SubsampleOffset[1]) / m_SubsampleFactor[1]);
192 
193  typename OutputImageType::SpacingType outSpacing = this->GetInput()->GetSignedSpacing();
194  outSpacing[0] *= m_SubsampleFactor[0];
195  outSpacing[1] *= m_SubsampleFactor[1];
196 
197  typename OutputImageType::PointType outOrigin;
198  this->GetInput()->TransformIndexToPhysicalPoint(inputRegion.GetIndex() + m_SubsampleOffset, outOrigin);
199 
200  for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++)
201  {
202  OutputImagePointerType outputPtr = this->GetOutput(i);
203  outputPtr->SetLargestPossibleRegion(outputRegion);
204  outputPtr->SetOrigin(outOrigin);
205  outputPtr->SetSignedSpacing(outSpacing);
206  }
207 }
208 
209 template <class TInputImage, class TOutputImage>
211 {
212  // First, call superclass implementation
213  Superclass::GenerateInputRequestedRegion();
214 
215  // Retrieve the input and output pointers
216  InputImagePointerType inputPtr = const_cast<InputImageType*>(this->GetInput());
217  OutputImagePointerType outputPtr = this->GetOutput();
218 
219  if (!inputPtr || !outputPtr)
220  {
221  return;
222  }
223 
224  // Retrieve the output requested region
225  // We use only the first output since requested regions for all outputs are enforced to be equal
226  // by the default GenerateOutputRequestedRegiont() implementation
227  OutputRegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
228  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
229 
230  typename OutputRegionType::IndexType outputIndex = outputRequestedRegion.GetIndex();
231  typename OutputRegionType::SizeType outputSize = outputRequestedRegion.GetSize();
232  typename InputRegionType::IndexType inputIndex;
233  typename InputRegionType::SizeType inputSize;
234 
235  // Convert index and size to full grid
236  outputIndex[0] = outputIndex[0] * m_SubsampleFactor[0] + m_SubsampleOffset[0] + inputLargest.GetIndex(0);
237  outputIndex[1] = outputIndex[1] * m_SubsampleFactor[1] + m_SubsampleOffset[1] + inputLargest.GetIndex(1);
238  outputSize[0] = 1 + (outputSize[0] - 1) * m_SubsampleFactor[0];
239  outputSize[1] = 1 + (outputSize[1] - 1) * m_SubsampleFactor[1];
240 
241  // First, apply offset
242  for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim)
243  {
244  inputIndex[dim] = std::min(outputIndex[dim], outputIndex[dim] + m_Offset[dim]);
245  inputSize[dim] = std::max(outputIndex[dim] + outputSize[dim], outputIndex[dim] + outputSize[dim] + m_Offset[dim]) - inputIndex[dim];
246  }
247 
248  // Build the input requested region
249  InputRegionType inputRequestedRegion;
250  inputRequestedRegion.SetIndex(inputIndex);
251  inputRequestedRegion.SetSize(inputSize);
252 
253  // Apply the radius
254  inputRequestedRegion.PadByRadius(m_Radius);
255 
256  // Try to apply the requested region to the input image
257  if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
258  {
259  inputPtr->SetRequestedRegion(inputRequestedRegion);
260  }
261  else
262  {
263  // Build an exception
264  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
265  e.SetLocation(ITK_LOCATION);
266  e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
267  e.SetDataObject(inputPtr);
268  throw e;
269  }
270 }
271 
272 template <class TInputImage, class TOutputImage>
274 {
275  unsigned int minRadius = 0;
276  for (unsigned int i = 0; i < m_Offset.GetOffsetDimension(); i++)
277  {
278  unsigned int distance = std::abs(m_Offset[i]);
279  if (distance > minRadius)
280  {
281  minRadius = distance;
282  }
283  }
284  m_NeighborhoodRadius.Fill(minRadius);
285 }
286 
287 template <class TInputImage, class TOutputImage>
289 {
290  // Retrieve the input and output pointers
291  InputImagePointerType inputPtr = const_cast<InputImageType*>(this->GetInput());
292  OutputImagePointerType meanPtr = this->GetMeanOutput();
293  OutputImagePointerType variancePtr = this->GetVarianceOutput();
294  OutputImagePointerType dissimilarityPtr = this->GetDissimilarityOutput();
295  OutputImagePointerType sumAveragePtr = this->GetSumAverageOutput();
296  OutputImagePointerType sumVariancePtr = this->GetSumVarianceOutput();
297  OutputImagePointerType sumEntropytPtr = this->GetSumEntropyOutput();
298  OutputImagePointerType differenceEntropyPtr = this->GetDifferenceEntropyOutput();
299  OutputImagePointerType differenceVariancePtr = this->GetDifferenceVarianceOutput();
300  OutputImagePointerType ic1Ptr = this->GetIC1Output();
301  OutputImagePointerType ic2Ptr = this->GetIC2Output();
302 
303  // Build output iterators
304  itk::ImageRegionIteratorWithIndex<OutputImageType> varianceIt(variancePtr, outputRegionForThread);
305  itk::ImageRegionIterator<OutputImageType> meanIt(meanPtr, outputRegionForThread);
306  itk::ImageRegionIterator<OutputImageType> dissimilarityIt(dissimilarityPtr, outputRegionForThread);
307  itk::ImageRegionIterator<OutputImageType> sumAverageIt(sumAveragePtr, outputRegionForThread);
308  itk::ImageRegionIterator<OutputImageType> sumVarianceIt(sumVariancePtr, outputRegionForThread);
309  itk::ImageRegionIterator<OutputImageType> sumEntropytIt(sumEntropytPtr, outputRegionForThread);
310  itk::ImageRegionIterator<OutputImageType> differenceEntropyIt(differenceEntropyPtr, outputRegionForThread);
311  itk::ImageRegionIterator<OutputImageType> differenceVarianceIt(differenceVariancePtr, outputRegionForThread);
312  itk::ImageRegionIterator<OutputImageType> ic1It(ic1Ptr, outputRegionForThread);
313  itk::ImageRegionIterator<OutputImageType> ic2It(ic2Ptr, outputRegionForThread);
314 
315  // Go to begin
316  varianceIt.GoToBegin();
317  meanIt.GoToBegin();
318  dissimilarityIt.GoToBegin();
319  sumAverageIt.GoToBegin();
320  sumVarianceIt.GoToBegin();
321  sumEntropytIt.GoToBegin();
322  differenceEntropyIt.GoToBegin();
323  differenceVarianceIt.GoToBegin();
324  ic1It.GoToBegin();
325  ic2It.GoToBegin();
326 
327  const double log2 = std::log(2.0);
328  const unsigned int histSize = m_NumberOfBinsPerAxis;
329  const long unsigned int twiceHistSize = 2 * m_NumberOfBinsPerAxis;
330 
331  InputRegionType inputLargest = inputPtr->GetLargestPossibleRegion();
332 
333  // Iterate on outputs to compute textures
334  while (!varianceIt.IsAtEnd() && !meanIt.IsAtEnd() && !dissimilarityIt.IsAtEnd() && !sumAverageIt.IsAtEnd() && !sumVarianceIt.IsAtEnd() &&
335  !sumEntropytIt.IsAtEnd() && !differenceEntropyIt.IsAtEnd() && !differenceVarianceIt.IsAtEnd() && !ic1It.IsAtEnd() && !ic2It.IsAtEnd())
336  {
337  // Compute the region on which co-occurence will be estimated
338  typename InputRegionType::IndexType inputIndex;
339  typename InputRegionType::SizeType inputSize;
340 
341  // Convert index to full grid
342  typename OutputImageType::IndexType outIndex;
343 
344  // First, create an window for neighborhood iterator based on m_Radius
345  // For example, if xradius and yradius is 2. window size is 5x5 (2 * radius + 1).
346  for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim)
347  {
348  outIndex[dim] = varianceIt.GetIndex()[dim] * m_SubsampleFactor[dim] + m_SubsampleOffset[dim] + inputLargest.GetIndex(dim);
349  inputIndex[dim] = outIndex[dim] - m_Radius[dim];
350  inputSize[dim] = 2 * m_Radius[dim] + 1;
351  }
352 
353  // Build the input region
354  InputRegionType inputRegion;
355  inputRegion.SetIndex(inputIndex);
356  inputRegion.SetSize(inputSize);
357  inputRegion.Crop(inputPtr->GetRequestedRegion());
358 
359  CooccurrenceIndexedListPointerType GLCIList = CooccurrenceIndexedListType::New();
360  GLCIList->Initialize(m_NumberOfBinsPerAxis, m_InputImageMinimum, m_InputImageMaximum);
361 
362  typedef itk::ConstNeighborhoodIterator<InputImageType> NeighborhoodIteratorType;
363  NeighborhoodIteratorType neighborIt;
364  neighborIt = NeighborhoodIteratorType(m_NeighborhoodRadius, inputPtr, inputRegion);
365  for (neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt)
366  {
367  const InputPixelType centerPixelIntensity = neighborIt.GetCenterPixel();
368  bool pixelInBounds;
369  const InputPixelType pixelIntensity = neighborIt.GetPixel(m_Offset, pixelInBounds);
370  if (!pixelInBounds)
371  {
372  continue; // don't put a pixel in the co-occurrence list if the value is
373  // out of bounds
374  }
375  GLCIList->AddPixelPair(centerPixelIntensity, pixelIntensity);
376  }
377 
378  PixelValueType m_Mean = itk::NumericTraits<PixelValueType>::Zero;
379  PixelValueType m_Variance = itk::NumericTraits<PixelValueType>::Zero;
380  PixelValueType m_Dissimilarity = itk::NumericTraits<PixelValueType>::Zero;
381  PixelValueType m_SumAverage = itk::NumericTraits<PixelValueType>::Zero;
382  PixelValueType m_SumEntropy = itk::NumericTraits<PixelValueType>::Zero;
383  PixelValueType m_SumVariance = itk::NumericTraits<PixelValueType>::Zero;
384  PixelValueType m_DifferenceEntropy = itk::NumericTraits<PixelValueType>::Zero;
385  PixelValueType m_DifferenceVariance = itk::NumericTraits<PixelValueType>::Zero;
386  PixelValueType m_IC1 = itk::NumericTraits<PixelValueType>::Zero;
387  PixelValueType m_IC2 = itk::NumericTraits<PixelValueType>::Zero;
388 
389  double Entropy = 0;
390 
391  typedef itk::Array<double> DoubleArrayType;
392  DoubleArrayType hx(histSize);
393  DoubleArrayType hy(histSize);
394  DoubleArrayType pdxy(twiceHistSize);
395 
396  for (long unsigned int i = 0; i < histSize; i++)
397  {
398  hx[i] = 0.0;
399  hy[i] = 0.0;
400  pdxy[i] = 0.0;
401  }
402  for (long unsigned int i = histSize; i < twiceHistSize; i++)
403  {
404  pdxy[i] = 0.0;
405  }
406 
407  /* hx.Fill(0.0); hy.Fill(0.0); pdxy.Fill(0.0); */
408  double hxy1 = 0;
409 
410  // get co-occurrence vector and totalfrequency
411  VectorType glcVector = GLCIList->GetVector();
412  double totalFrequency = static_cast<double>(GLCIList->GetTotalFrequency());
413 
414  VectorConstIteratorType constVectorIt;
415  // Normalize the GreyLevelCooccurrenceListType
416  // Compute Mean, Entropy (f12), hx, hy, pdxy
417  constVectorIt = glcVector.begin();
418  while (constVectorIt != glcVector.end())
419  {
420  CooccurrenceIndexType index = (*constVectorIt).first;
421  double frequency = (*constVectorIt).second / totalFrequency;
422  m_Mean += static_cast<double>(index[0]) * frequency;
423  Entropy -= (frequency > 0.0001) ? frequency * std::log(frequency) / log2 : 0.;
424  unsigned int i = index[1];
425  unsigned int j = index[0];
426  hx[j] += frequency;
427  hy[i] += frequency;
428 
429  if (i + j > histSize - 1)
430  {
431  pdxy[i + j] += frequency;
432  }
433  if (i <= j)
434  {
435  pdxy[j - i] += frequency;
436  }
437  ++constVectorIt;
438  }
439 
440  // second pass over normalized co-occurrence list to find variance and pipj.
441  // pipj is needed to calculate f11
442  constVectorIt = glcVector.begin();
443  while (constVectorIt != glcVector.end())
444  {
445  double frequency = (*constVectorIt).second / totalFrequency;
446  CooccurrenceIndexType index = (*constVectorIt).first;
447  unsigned int i = index[1];
448  unsigned int j = index[0];
449  double index0 = static_cast<double>(index[0]);
450  m_Variance += ((index0 - m_Mean) * (index0 - m_Mean)) * frequency;
451  double pipj = hx[j] * hy[i];
452  hxy1 -= (pipj > 0.0001) ? frequency * std::log(pipj) : 0.;
453  ++constVectorIt;
454  }
455 
456  // iterate histSize to compute sumEntropy
457  double PSSquareCumul = 0;
458  for (long unsigned int k = histSize; k < twiceHistSize; k++)
459  {
460  m_SumAverage += k * pdxy[k];
461  m_SumEntropy -= (pdxy[k] > 0.0001) ? pdxy[k] * std::log(pdxy[k]) / log2 : 0;
462  PSSquareCumul += k * k * pdxy[k];
463  }
464  m_SumVariance = PSSquareCumul - m_SumAverage * m_SumAverage;
465 
466  double PDSquareCumul = 0;
467  double PDCumul = 0;
468  double hxCumul = 0;
469  double hyCumul = 0;
470 
471  for (long unsigned int i = 0; i < histSize; ++i)
472  {
473  double pdTmp = pdxy[i];
474  PDCumul += i * pdTmp;
475  m_DifferenceEntropy -= (pdTmp > 0.0001) ? pdTmp * std::log(pdTmp) / log2 : 0;
476  PDSquareCumul += i * i * pdTmp;
477 
478  // comput hxCumul and hyCumul
479  double marginalfreq = hx[i];
480  hxCumul += (marginalfreq > 0.0001) ? std::log(marginalfreq) * marginalfreq : 0;
481 
482  marginalfreq = hy[i];
483  hyCumul += (marginalfreq > 0.0001) ? std::log(marginalfreq) * marginalfreq : 0;
484  }
485  m_DifferenceVariance = PDSquareCumul - PDCumul * PDCumul;
486 
487  /* pipj computed below is totally different from earlier one which was used
488  * to compute hxy1. */
489  double hxy2 = 0;
490  for (unsigned int i = 0; i < histSize; ++i)
491  {
492  for (unsigned int j = 0; j < histSize; ++j)
493  {
494  double pipj = hx[j] * hy[i];
495  hxy2 -= (pipj > 0.0001) ? pipj * std::log(pipj) : 0.;
496  double frequency = GLCIList->GetFrequency(i, j, glcVector) / totalFrequency;
497  m_Dissimilarity += (static_cast<double>(j) - static_cast<double>(i)) * (frequency * frequency);
498  }
499  }
500 
501  // Information measures of correlation 1 & 2
502  m_IC1 = (std::abs(std::max(hxCumul, hyCumul)) > 0.0001) ? (Entropy - hxy1) / (std::max(hxCumul, hyCumul)) : 0;
503  m_IC2 = 1 - std::exp(-2. * std::abs(hxy2 - Entropy));
504  m_IC2 = (m_IC2 >= 0) ? std::sqrt(m_IC2) : 0;
505 
506  // Fill outputs
507  meanIt.Set(m_Mean);
508  varianceIt.Set(m_Variance);
509  dissimilarityIt.Set(m_Dissimilarity);
510  sumAverageIt.Set(m_SumAverage);
511  sumVarianceIt.Set(m_SumVariance);
512  sumEntropytIt.Set(m_SumEntropy);
513  differenceEntropyIt.Set(m_DifferenceEntropy);
514  differenceVarianceIt.Set(m_DifferenceVariance);
515  ic1It.Set(m_IC1);
516  ic2It.Set(m_IC2);
517 
518  // Increment iterators
519  ++varianceIt;
520  ++meanIt;
521  ++dissimilarityIt;
522  ++sumAverageIt;
523  ++sumVarianceIt;
524  ++sumEntropytIt;
525  ++differenceEntropyIt;
526  ++differenceVarianceIt;
527  ++ic1It;
528  ++ic2It;
529  }
530 }
531 
532 } // End namespace otb
533 
534 #endif
CooccurrenceIndexedListType::Pointer CooccurrenceIndexedListPointerType
CooccurrenceIndexedListType::IndexType CooccurrenceIndexType
CooccurrenceIndexedListType::PixelValueType PixelValueType
void DynamicThreadedGenerateData(const OutputRegionType &outputRegion) override
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.