OTB  10.0.0
Orfeo Toolbox
otbStreamingStatisticsVectorImageFilter.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 otbStreamingStatisticsVectorImageFilter_hxx
22 #define otbStreamingStatisticsVectorImageFilter_hxx
24 
25 #include "itkImageRegionIterator.h"
26 #include "itkImageRegionConstIteratorWithIndex.h"
27 #include "itkProgressReporter.h"
28 #include "otbMacro.h"
29 #include "vcl_legacy_aliases.h"
30 
31 
32 namespace otb
33 {
34 
35 template <class TInputImage, class TPrecision>
37  : m_EnableMinMax(true),
38  m_EnableFirstOrderStats(true),
39  m_EnableSecondOrderStats(true),
40  m_UseUnbiasedEstimator(true),
41  m_IgnoreInfiniteValues(true),
42  m_IgnoreUserDefinedValue(false),
43  m_UserIgnoredValue(itk::NumericTraits<InternalPixelType>::Zero)
44 {
45  this->DynamicMultiThreadingOff();
46  // first output is a copy of the image, DataObject created by
47  // superclass
48 
49  // allocate the data objects for the outputs which are
50  // just decorators around vector/matrix types
51  for (unsigned int i = 1; i < 11; ++i)
52  {
53  this->itk::ProcessObject::SetNthOutput(i, this->MakeOutput(i).GetPointer());
54  }
55  // Initiate ignored pixel counters
56  m_IgnoredInfinitePixelCount = std::vector<unsigned int>(this->GetNumberOfWorkUnits(), 0);
57  m_IgnoredUserPixelCount = std::vector<unsigned int>(this->GetNumberOfWorkUnits(), 0);
58 }
59 
60 template <class TInputImage, class TPrecision>
62 {
63  switch (output)
64  {
65  case 0:
66  return static_cast<itk::DataObject*>(TInputImage::New().GetPointer());
67  break;
68  case 1:
69  case 2:
70  // min/max
71  return static_cast<itk::DataObject*>(PixelObjectType::New().GetPointer());
72  break;
73  case 3:
74  case 4:
75  // mean / sum
76  return static_cast<itk::DataObject*>(RealPixelObjectType::New().GetPointer());
77  break;
78  case 5:
79  case 6:
80  // covariance / correlation
81  return static_cast<itk::DataObject*>(MatrixObjectType::New().GetPointer());
82  break;
83  case 7:
84  case 8:
85  case 9:
86  // component mean, component covariance, component correlation
87  return static_cast<itk::DataObject*>(RealObjectType::New().GetPointer());
88  break;
89  case 10:
90  // relevant pixel
91  return static_cast<itk::DataObject*>(CountObjectType::New().GetPointer());
92  default:
93  // might as well make an image
94  return static_cast<itk::DataObject*>(TInputImage::New().GetPointer());
95  break;
96  }
97 }
98 
99 template <class TInputImage, class TPrecision>
102 {
103  return static_cast<CountObjectType*>(this->itk::ProcessObject::GetOutput(10));
104 }
105 
106 template <class TInputImage, class TPrecision>
109 {
110  return static_cast<const CountObjectType*>(this->itk::ProcessObject::GetOutput(10));
111 }
112 
113 
114 template <class TInputImage, class TPrecision>
117 {
118  return static_cast<PixelObjectType*>(this->itk::ProcessObject::GetOutput(1));
119 }
120 
121 template <class TInputImage, class TPrecision>
124 {
125  return static_cast<const PixelObjectType*>(this->itk::ProcessObject::GetOutput(1));
126 }
127 
128 template <class TInputImage, class TPrecision>
131 {
132  return static_cast<PixelObjectType*>(this->itk::ProcessObject::GetOutput(2));
133 }
134 
135 template <class TInputImage, class TPrecision>
138 {
139  return static_cast<const PixelObjectType*>(this->itk::ProcessObject::GetOutput(2));
140 }
141 
142 template <class TInputImage, class TPrecision>
145 {
146  return static_cast<RealObjectType*>(this->itk::ProcessObject::GetOutput(7));
147 }
148 
149 template <class TInputImage, class TPrecision>
152 {
153  return static_cast<const RealObjectType*>(this->itk::ProcessObject::GetOutput(7));
154 }
155 
156 template <class TInputImage, class TPrecision>
159 {
160  return static_cast<RealObjectType*>(this->itk::ProcessObject::GetOutput(8));
161 }
162 
163 template <class TInputImage, class TPrecision>
166 {
167  return static_cast<const RealObjectType*>(this->itk::ProcessObject::GetOutput(8));
168 }
169 
170 
171 template <class TInputImage, class TPrecision>
174 {
175  return static_cast<RealObjectType*>(this->itk::ProcessObject::GetOutput(9));
176 }
177 
178 template <class TInputImage, class TPrecision>
181 {
182  return static_cast<const RealObjectType*>(this->itk::ProcessObject::GetOutput(9));
183 }
184 
185 template <class TInputImage, class TPrecision>
188 {
189  return static_cast<RealPixelObjectType*>(this->itk::ProcessObject::GetOutput(3));
190 }
191 
192 template <class TInputImage, class TPrecision>
195 {
196  return static_cast<const RealPixelObjectType*>(this->itk::ProcessObject::GetOutput(3));
197 }
198 
199 template <class TInputImage, class TPrecision>
202 {
203  return static_cast<RealPixelObjectType*>(this->itk::ProcessObject::GetOutput(4));
204 }
205 
206 template <class TInputImage, class TPrecision>
209 {
210  return static_cast<const RealPixelObjectType*>(this->itk::ProcessObject::GetOutput(4));
211 }
212 
213 template <class TInputImage, class TPrecision>
216 {
217  return static_cast<MatrixObjectType*>(this->itk::ProcessObject::GetOutput(5));
218 }
219 
220 template <class TInputImage, class TPrecision>
223 {
224  return static_cast<const MatrixObjectType*>(this->itk::ProcessObject::GetOutput(5));
225 }
226 
227 template <class TInputImage, class TPrecision>
230 {
231  return static_cast<MatrixObjectType*>(this->itk::ProcessObject::GetOutput(6));
232 }
233 
234 template <class TInputImage, class TPrecision>
237 {
238  return static_cast<const MatrixObjectType*>(this->itk::ProcessObject::GetOutput(6));
239 }
240 
241 template <class TInputImage, class TPrecision>
243 {
244  Superclass::GenerateOutputInformation();
245  if (this->GetInput())
246  {
247  this->GetOutput()->CopyInformation(this->GetInput());
248  this->GetOutput()->SetLargestPossibleRegion(this->GetInput()->GetLargestPossibleRegion());
249 
250  if (this->GetOutput()->GetRequestedRegion().GetNumberOfPixels() == 0)
251  {
252  this->GetOutput()->SetRequestedRegion(this->GetOutput()->GetLargestPossibleRegion());
253  }
254  }
255 }
256 
257 template <class TInputImage, class TPrecision>
259 {
260  // This is commented to prevent the streaming of the whole image for the first stream strip
261  // It shall not cause any problem because the output image of this filter is not intended to be used.
262  // InputImagePointer image = const_cast< TInputImage * >( this->GetInput() );
263  // this->GraftOutput( image );
264  // Nothing that needs to be allocated for the remaining outputs
265 }
266 
267 template <class TInputImage, class TPrecision>
269 {
270  TInputImage* inputPtr = const_cast<TInputImage*>(this->GetInput());
271  inputPtr->UpdateOutputInformation();
272 
273  unsigned int numberOfThreads = this->GetNumberOfWorkUnits();
274  unsigned int numberOfComponent = inputPtr->GetNumberOfComponentsPerPixel();
275 
276  if (m_EnableMinMax)
277  {
278  PixelType tempPixel;
279  tempPixel.SetSize(numberOfComponent);
280 
281  tempPixel.Fill(itk::NumericTraits<InternalPixelType>::max());
282  this->GetMinimumOutput()->Set(tempPixel);
283 
284  tempPixel.Fill(itk::NumericTraits<InternalPixelType>::NonpositiveMin());
285  this->GetMaximumOutput()->Set(tempPixel);
286 
287  PixelType tempTemporariesPixel;
288  tempTemporariesPixel.SetSize(numberOfComponent);
289  tempTemporariesPixel.Fill(itk::NumericTraits<InternalPixelType>::max());
290  m_ThreadMin = std::vector<PixelType>(numberOfThreads, tempTemporariesPixel);
291 
292  tempTemporariesPixel.Fill(itk::NumericTraits<InternalPixelType>::NonpositiveMin());
293  m_ThreadMax = std::vector<PixelType>(numberOfThreads, tempTemporariesPixel);
294  }
295 
296  if (m_EnableSecondOrderStats)
297  {
298  m_EnableFirstOrderStats = true;
299  }
300 
301  if (m_EnableFirstOrderStats)
302  {
303  RealPixelType zeroRealPixel;
304  zeroRealPixel.SetSize(numberOfComponent);
305  zeroRealPixel.Fill(itk::NumericTraits<PrecisionType>::ZeroValue());
306  this->GetMeanOutput()->Set(zeroRealPixel);
307  this->GetSumOutput()->Set(zeroRealPixel);
308  m_ThreadFirstOrderAccumulators.resize(numberOfThreads);
309  std::fill(m_ThreadFirstOrderAccumulators.begin(), m_ThreadFirstOrderAccumulators.end(), zeroRealPixel);
310 
311  RealType zeroReal = itk::NumericTraits<RealType>::ZeroValue();
312  m_ThreadFirstOrderComponentAccumulators.resize(numberOfThreads);
313  std::fill(m_ThreadFirstOrderComponentAccumulators.begin(), m_ThreadFirstOrderComponentAccumulators.end(), zeroReal);
314  }
315 
316  if (m_EnableSecondOrderStats)
317  {
318  MatrixType zeroMatrix;
319  zeroMatrix.SetSize(numberOfComponent, numberOfComponent);
320  zeroMatrix.Fill(itk::NumericTraits<PrecisionType>::Zero);
321  this->GetCovarianceOutput()->Set(zeroMatrix);
322  this->GetCorrelationOutput()->Set(zeroMatrix);
323 
324  m_ThreadSecondOrderAccumulators.resize(numberOfThreads);
325  std::fill(m_ThreadSecondOrderAccumulators.begin(), m_ThreadSecondOrderAccumulators.end(), zeroMatrix);
326 
327  RealType zeroReal = itk::NumericTraits<RealType>::ZeroValue();
328  m_ThreadSecondOrderComponentAccumulators.resize(numberOfThreads);
329  std::fill(m_ThreadSecondOrderComponentAccumulators.begin(), m_ThreadSecondOrderComponentAccumulators.end(), zeroReal);
330  }
331 
332  if (m_IgnoreInfiniteValues)
333  {
334  m_IgnoredInfinitePixelCount.clear();
335  m_IgnoredInfinitePixelCount.resize(numberOfThreads,0);
336  }
337 
338  if (m_IgnoreUserDefinedValue)
339  {
340  m_IgnoredUserPixelCount.clear();
341  m_IgnoredUserPixelCount.resize(this->GetNumberOfWorkUnits(),0);
342  }
343 }
344 
345 template <class TInputImage, class TPrecision>
347 {
348  TInputImage* inputPtr = const_cast<TInputImage*>(this->GetInput());
349  const unsigned int nbPixels = inputPtr->GetLargestPossibleRegion().GetNumberOfPixels();
350  const unsigned int numberOfComponent = inputPtr->GetNumberOfComponentsPerPixel();
351 
352  PixelType minimum;
353  minimum.SetSize(numberOfComponent);
354  minimum.Fill(itk::NumericTraits<InternalPixelType>::max());
355  PixelType maximum;
356  maximum.SetSize(numberOfComponent);
357  maximum.Fill(itk::NumericTraits<InternalPixelType>::NonpositiveMin());
358 
359  RealPixelType streamFirstOrderAccumulator(numberOfComponent);
360  streamFirstOrderAccumulator.Fill(itk::NumericTraits<PrecisionType>::Zero);
361  MatrixType streamSecondOrderAccumulator(numberOfComponent, numberOfComponent);
362  streamSecondOrderAccumulator.Fill(itk::NumericTraits<PrecisionType>::Zero);
363 
364  RealType streamFirstOrderComponentAccumulator = itk::NumericTraits<RealType>::Zero;
365  RealType streamSecondOrderComponentAccumulator = itk::NumericTraits<RealType>::Zero;
366 
367  unsigned int ignoredInfinitePixelCount = 0;
368  unsigned int ignoredUserPixelCount = 0;
369 
370  // Accumulate results from all threads
371  const itk::ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
372  for (itk::ThreadIdType threadId = 0; threadId < numberOfThreads; ++threadId)
373  {
374  if (m_EnableMinMax)
375  {
376  const PixelType& threadMin = m_ThreadMin[threadId];
377  const PixelType& threadMax = m_ThreadMax[threadId];
378 
379  for (unsigned int j = 0; j < numberOfComponent; ++j)
380  {
381  if (threadMin[j] < minimum[j])
382  {
383  minimum[j] = threadMin[j];
384  }
385  if (threadMax[j] > maximum[j])
386  {
387  maximum[j] = threadMax[j];
388  }
389  }
390  }
391 
392  if (m_EnableFirstOrderStats)
393  {
394  streamFirstOrderAccumulator += m_ThreadFirstOrderAccumulators[threadId];
395  streamFirstOrderComponentAccumulator += m_ThreadFirstOrderComponentAccumulators[threadId];
396  }
397 
398  if (m_EnableSecondOrderStats)
399  {
400  streamSecondOrderAccumulator += m_ThreadSecondOrderAccumulators[threadId];
401  streamSecondOrderComponentAccumulator += m_ThreadSecondOrderComponentAccumulators[threadId];
402  }
403  // Ignored Infinite Pixels
404  ignoredInfinitePixelCount += m_IgnoredInfinitePixelCount[threadId];
405  // Ignored Pixels
406  ignoredUserPixelCount += m_IgnoredUserPixelCount[threadId];
407  }
408 
409  // There cannot be more ignored pixels than read pixels.
410  assert(nbPixels >= ignoredInfinitePixelCount + ignoredUserPixelCount);
411  if (nbPixels < ignoredInfinitePixelCount + ignoredUserPixelCount)
412  {
413  itkExceptionMacro("nbPixels < ignoredInfinitePixelCount + ignoredUserPixelCount");
414  }
415 
416  unsigned int nbRelevantPixel = nbPixels - (ignoredInfinitePixelCount + ignoredUserPixelCount);
417 
418  CountType nbRelevantPixels(numberOfComponent);
419  nbRelevantPixels.Fill(nbRelevantPixel);
420 
421  this->GetNbRelevantPixelsOutput()->Set(nbRelevantPixels);
422 
423  if (nbRelevantPixel == 0)
424  {
425  itkExceptionMacro("Statistics cannot be calculated with zero relevant pixels.");
426  }
427 
428  // Final calculations
429  if (m_EnableMinMax)
430  {
431  this->GetMinimumOutput()->Set(minimum);
432  this->GetMaximumOutput()->Set(maximum);
433  }
434 
435  if (m_EnableFirstOrderStats)
436  {
437  this->GetComponentMeanOutput()->Set(streamFirstOrderComponentAccumulator / (nbRelevantPixel * numberOfComponent));
438 
439  this->GetMeanOutput()->Set(streamFirstOrderAccumulator / nbRelevantPixel);
440  this->GetSumOutput()->Set(streamFirstOrderAccumulator);
441  }
442 
443  if (m_EnableSecondOrderStats)
444  {
445  MatrixType cor = streamSecondOrderAccumulator / nbRelevantPixel;
446  this->GetCorrelationOutput()->Set(cor);
447 
448  const RealPixelType& mean = this->GetMeanOutput()->Get();
449 
450  double regul = 1.0;
451  double regulComponent = 1.0;
452 
453  if (m_UseUnbiasedEstimator && nbRelevantPixel > 1)
454  {
455  regul = static_cast<double>(nbRelevantPixel) / (static_cast<double>(nbRelevantPixel) - 1.0);
456  }
457 
458  if (m_UseUnbiasedEstimator && (nbRelevantPixel * numberOfComponent) > 1)
459  {
460  regulComponent = static_cast<double>(nbRelevantPixel * numberOfComponent) / (static_cast<double>(nbRelevantPixel * numberOfComponent) - 1.0);
461  }
462 
463  MatrixType cov = cor;
464  for (unsigned int r = 0; r < numberOfComponent; ++r)
465  {
466  for (unsigned int c = 0; c < numberOfComponent; ++c)
467  {
468  cov(r, c) = regul * (cov(r, c) - mean[r] * mean[c]);
469  }
470  }
471  this->GetCovarianceOutput()->Set(cov);
472 
473  this->GetComponentMeanOutput()->Set(streamFirstOrderComponentAccumulator / (nbRelevantPixel * numberOfComponent));
474  this->GetComponentCorrelationOutput()->Set(streamSecondOrderComponentAccumulator / (nbRelevantPixel * numberOfComponent));
475  this->GetComponentCovarianceOutput()->Set(regulComponent * (this->GetComponentCorrelation() - (this->GetComponentMean() * this->GetComponentMean())));
476  }
477 }
478 
479 template <class TInputImage, class TPrecision>
481  itk::ThreadIdType threadId)
482 {
483  // Support progress methods/callbacks
484  itk::ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
485 
486  // Grab the input
487  InputImagePointer inputPtr = const_cast<TInputImage*>(this->GetInput());
488  PixelType& threadMin = m_ThreadMin[threadId];
489  PixelType& threadMax = m_ThreadMax[threadId];
490 
491 
492  itk::ImageRegionConstIteratorWithIndex<TInputImage> it(inputPtr, outputRegionForThread);
493 
494  for (it.GoToBegin(); !it.IsAtEnd(); ++it, progress.CompletedPixel())
495  {
496  const PixelType& vectorValue = it.Get();
497 
498  float finiteProbe = 0.;
499  bool userProbe = m_IgnoreUserDefinedValue;
500  for (unsigned int j = 0; j < vectorValue.GetSize(); ++j)
501  {
502  finiteProbe += (float)(vectorValue[j]);
503  userProbe = userProbe && (vectorValue[j] == m_UserIgnoredValue);
504  }
505 
506  if (m_IgnoreInfiniteValues && !(vnl_math_isfinite(finiteProbe)))
507  {
508  m_IgnoredInfinitePixelCount[threadId]++;
509  }
510  else
511  {
512  if (userProbe)
513  {
514  m_IgnoredUserPixelCount[threadId]++;
515  }
516  else
517  {
518  if (m_EnableMinMax)
519  {
520  for (unsigned int j = 0; j < vectorValue.GetSize(); ++j)
521  {
522  if (vectorValue[j] < threadMin[j])
523  {
524  threadMin[j] = vectorValue[j];
525  }
526  if (vectorValue[j] > threadMax[j])
527  {
528  threadMax[j] = vectorValue[j];
529  }
530  }
531  }
532 
533  if (m_EnableFirstOrderStats)
534  {
535  RealPixelType& threadFirstOrder = m_ThreadFirstOrderAccumulators[threadId];
536  RealType& threadFirstOrderComponent = m_ThreadFirstOrderComponentAccumulators[threadId];
537 
538  threadFirstOrder += vectorValue;
539 
540  for (unsigned int i = 0; i < vectorValue.GetSize(); ++i)
541  {
542  threadFirstOrderComponent += vectorValue[i];
543  }
544  }
545 
546  if (m_EnableSecondOrderStats)
547  {
548  MatrixType& threadSecondOrder = m_ThreadSecondOrderAccumulators[threadId];
549  RealType& threadSecondOrderComponent = m_ThreadSecondOrderComponentAccumulators[threadId];
550 
551  for (unsigned int r = 0; r < threadSecondOrder.Rows(); ++r)
552  {
553  for (unsigned int c = 0; c < threadSecondOrder.Cols(); ++c)
554  {
555  threadSecondOrder(r, c) += static_cast<PrecisionType>(vectorValue[r]) * static_cast<PrecisionType>(vectorValue[c]);
556  }
557  }
558  threadSecondOrderComponent += vectorValue.GetSquaredNorm();
559  }
560  }
561  }
562  }
563 }
564 
565 template <class TImage, class TPrecision>
567 {
568  Superclass::PrintSelf(os, indent);
569 
570  os << indent << "Min: " << this->GetMinimumOutput()->Get() << std::endl;
571  os << indent << "Max: " << this->GetMaximumOutput()->Get() << std::endl;
572  os << indent << "Mean: " << this->GetMeanOutput()->Get() << std::endl;
573  os << indent << "Covariance: " << this->GetCovarianceOutput()->Get() << std::endl;
574  os << indent << "Correlation: " << this->GetCorrelationOutput()->Get() << std::endl;
575  os << indent << "Relevant pixel: " << this->GetNbRelevantPixelsOutput()->Get() << std::endl;
576  os << indent << "Component Mean: " << this->GetComponentMeanOutput()->Get() << std::endl;
577  os << indent << "Component Covariance: " << this->GetComponentCovarianceOutput()->Get() << std::endl;
578  os << indent << "Component Correlation: " << this->GetComponentCorrelationOutput()->Get() << std::endl;
579  os << indent << "UseUnbiasedEstimator: " << (this->m_UseUnbiasedEstimator ? "true" : "false") << std::endl;
580 }
581 
582 } // end namespace otb
583 #endif
void PrintSelf(std::ostream &os, itk::Indent indent) const override
itk::SimpleDataObjectDecorator< RealPixelType > RealPixelObjectType
DataObjectPointer MakeOutput(DataObjectPointerArraySizeType idx) override
itk::ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType
void ThreadedGenerateData(const RegionType &outputRegionForThread, itk::ThreadIdType threadId) override
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.