OTB  10.0.0
Orfeo Toolbox
otbDifferenceImageFilter.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 otbDifferenceImageFilter_hxx
22 #define otbDifferenceImageFilter_hxx
23 
25 
26 #include "itkConstNeighborhoodIterator.h"
27 #include "itkImageRegionIterator.h"
28 #include "itkNeighborhoodAlgorithm.h"
29 #include "itkProgressReporter.h"
30 #include "itkDefaultConvertPixelTraits.h"
31 
32 namespace otb
33 {
34 
35 //----------------------------------------------------------------------------
36 template <class TInputImage, class TOutputImage>
38 {
39  this->DynamicMultiThreadingOff();
40  // We require two inputs to execute.
41  this->SetNumberOfRequiredInputs(2);
42 
43  // Set the default DifferenceThreshold.
44  m_DifferenceThreshold = itk::NumericTraits<ScalarRealType>::Zero;
45 
46  // Set the default ToleranceRadius.
47  m_ToleranceRadius = 0;
48 
49  // Initialize statistics about difference image.
50  itk::NumericTraits<RealType>::SetLength(m_MeanDifference, itk::DefaultConvertPixelTraits<RealType>::GetNumberOfComponents());
51  itk::NumericTraits<AccumulateType>::SetLength(m_TotalDifference, itk::DefaultConvertPixelTraits<RealType>::GetNumberOfComponents());
52  m_MeanDifference = itk::NumericTraits<RealType>::ZeroValue(m_MeanDifference);
53  m_TotalDifference = itk::NumericTraits<AccumulateType>::ZeroValue(m_TotalDifference);
54  m_NumberOfPixelsWithDifferences = 0;
55 
56 }
57 
58 //----------------------------------------------------------------------------
59 template <class TInputImage, class TOutputImage>
60 void DifferenceImageFilter<TInputImage, TOutputImage>::PrintSelf(std::ostream& os, itk::Indent indent) const
61 {
62  this->Superclass::PrintSelf(os, indent);
63  os << indent << "ToleranceRadius: " << m_ToleranceRadius << "\n";
64  os << indent << "DifferenceThreshold: " << m_DifferenceThreshold << "\n";
65  os << indent << "MeanDifference: " << m_MeanDifference << "\n";
66  os << indent << "TotalDifference: " << m_TotalDifference << "\n";
67  os << indent << "NumberOfPixelsWithDifferences: " << m_NumberOfPixelsWithDifferences << "\n";
68 }
69 
70 //----------------------------------------------------------------------------
71 template <class TInputImage, class TOutputImage>
73 {
74  // The valid image should be input 0.
75  this->SetInput(0, validImage);
76 }
77 
78 //----------------------------------------------------------------------------
79 template <class TInputImage, class TOutputImage>
81 {
82  // The test image should be input 1.
83  this->SetInput(1, testImage);
84 }
85 
86 template <class TInputImage, class TOutputImage>
88 {
89  Superclass::GenerateOutputInformation();
90  if (this->GetInput(0)->GetNumberOfComponentsPerPixel() != this->GetInput(1)->GetNumberOfComponentsPerPixel())
91  {
92  itkExceptionMacro(<< "Image 1 has " << this->GetInput(0)->GetNumberOfComponentsPerPixel() << " bands, whereas image 2 has "
93  << this->GetInput(1)->GetNumberOfComponentsPerPixel());
94  }
95  this->GetOutput()->SetNumberOfComponentsPerPixel(this->GetInput(0)->GetNumberOfComponentsPerPixel());
96 }
97 
98 template <class TInputImage, class TOutputImage>
100 {
101  this->UpdateOutputInformation();
102 
103  int numberOfThreads = this->GetNumberOfWorkUnits();
104 
105  itk::NumericTraits<RealType>::SetLength(m_MeanDifference, this->GetInput(0)->GetNumberOfComponentsPerPixel());
106  itk::NumericTraits<AccumulateType>::SetLength(m_TotalDifference, this->GetInput(0)->GetNumberOfComponentsPerPixel());
107 
108  // Initialize statistics about difference image.
109  m_MeanDifference = itk::NumericTraits<RealType>::ZeroValue(m_MeanDifference);
110  m_TotalDifference = itk::NumericTraits<AccumulateType>::ZeroValue(m_TotalDifference);
111  m_NumberOfPixelsWithDifferences = 0;
112 
113  // Resize the thread temporaries
114  m_ThreadDifferenceSum.reserve(numberOfThreads);
115  m_ThreadNumberOfPixels.SetSize(numberOfThreads);
116 
117  // Initialize the temporaries
118  for (int i = 0; i < numberOfThreads; ++i)
119  {
120  m_ThreadDifferenceSum.push_back(m_TotalDifference);
121  }
122  m_ThreadNumberOfPixels.Fill(0);
123 }
124 
125 //----------------------------------------------------------------------------
126 template <class TInputImage, class TOutputImage>
128 {
129  typedef itk::ConstNeighborhoodIterator<InputImageType> SmartIterator;
130  typedef itk::ImageRegionConstIterator<InputImageType> InputIterator;
131  typedef itk::ImageRegionIterator<OutputImageType> OutputIterator;
132  typedef itk::NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType> FacesCalculator;
133  typedef typename FacesCalculator::RadiusType RadiusType;
134  typedef typename FacesCalculator::FaceListType FaceListType;
135  typedef typename FaceListType::iterator FaceListIterator;
136  typedef typename InputImageType::PixelType InputPixelType;
137 
138  // Prepare standard boundary condition.
139  itk::ZeroFluxNeumannBoundaryCondition<InputImageType> nbc;
140 
141  // Get a pointer to each image.
142  const InputImageType* validImage = this->GetInput(0);
143  const InputImageType* testImage = this->GetInput(1);
144  OutputImageType* outputPtr = this->GetOutput();
145 
146  // Create a radius of pixels.
147  RadiusType radius;
148  radius.Fill(std::max(0, m_ToleranceRadius));
149 
150  // Find the data-set boundary faces.
151  FacesCalculator boundaryCalculator;
152  FaceListType faceList = boundaryCalculator(testImage, threadRegion, radius);
153 
154  // Support progress methods/callbacks.
155  itk::ProgressReporter progress(this, threadId, threadRegion.GetNumberOfPixels());
156 
157  AccumulateType threadDifferenceSum;
158  itk::NumericTraits<AccumulateType>::SetLength(threadDifferenceSum, this->GetInput(0)->GetNumberOfComponentsPerPixel()); // @post: threadDifferenceSum=={0...}
159  unsigned long threadNumberOfPixels = 0;
160 
161  // Process the internal face and each of the boundary faces.
162  for (FaceListIterator face = faceList.begin(); face != faceList.end(); ++face)
163  {
164  SmartIterator test(radius, testImage, *face); // Iterate over test image.
165  InputIterator valid(validImage, *face); // Iterate over valid image.
166  OutputIterator out(outputPtr, *face); // Iterate over output image.
167  test.OverrideBoundaryCondition(&nbc);
168 
169  // Extract a typical pixel in order to know the exact size of the
170  // pixel, the typical 0, and the typical max
171  valid.GoToBegin();
172  InputPixelType t = valid.Get();
173 
174  const auto pixel_size = itk::NumericTraits<InputPixelType>::GetLength(t);
175  const auto out_max = itk::NumericTraits<OutputPixelType>::max(t);
176  const auto zero = itk::NumericTraits<OutputPixelType>::ZeroValue(t);
177 
178  OutputPixelType minimumDifference = out_max;
179 
180  for (valid.GoToBegin(), test.GoToBegin(), out.GoToBegin(); !valid.IsAtEnd(); ++valid, ++test, ++out)
181  {
182  // Get the current valid pixel.
183  t = valid.Get();
184 
185  // Find the closest-valued pixel in the neighborhood of the test
186  // image.
187  minimumDifference = out_max; // reset by assignment, avoid allocation in VLV case
188  unsigned int neighborhoodSize = test.Size();
189  for (unsigned int i = 0; i < neighborhoodSize; ++i)
190  {
191 
192  for (unsigned int j = 0; j < pixel_size; ++j)
193  {
194  // Use the RealType for the difference to make sure we get
195  // the sign.
196  // Work on component independently in order to avoid
197  // allocation of VLV pixels
198  ScalarRealType d = static_cast<ScalarRealType>(itk::DefaultConvertPixelTraits<InputPixelType>::GetNthComponent(j, t) -
199  itk::DefaultConvertPixelTraits<InputPixelType>::GetNthComponent(j, test.GetPixel(i)));
200  d = std::abs(d);
201  ScalarRealType m = static_cast<ScalarRealType>(itk::DefaultConvertPixelTraits<OutputPixelType>::GetNthComponent(j, minimumDifference));
202  if (d < m)
203  {
204  itk::DefaultConvertPixelTraits<OutputPixelType>::SetNthComponent(j, minimumDifference, d);
205  // std::cout << std::setprecision(16) << minimumDifference[j] << std::endl;
206  // std::cout << std::setprecision(16) << t << std::endl;
207  // std::cout << std::setprecision(16) << test.GetPixel(i) << std::endl;
208  // std::cout << "----------------------" << std::endl;
209  }
210  }
211  }
212 
213  // for complex and vector type. FIXME: module might be better
214  // ScalarRealType tMax=std::abs(t[0]);
215  ScalarRealType tMax = 0.01; // Avoiding the 0 case for neighborhood computing
216  // NB: still more restrictive than before for small values.
217  for (unsigned int j = 0; j < pixel_size; ++j)
218  {
219  ScalarRealType tc = static_cast<ScalarRealType>(itk::DefaultConvertPixelTraits<InputPixelType>::GetNthComponent(j, t));
220  tMax = std::max(std::abs(tc), tMax);
221  }
222 
223  // Check if difference is above threshold
224  // the threshold is interpreted as relative to the value
225  bool isDifferent = false;
226 
227  for (unsigned int j = 0; j < pixel_size; ++j)
228  {
229  ScalarRealType m = static_cast<ScalarRealType>(itk::DefaultConvertPixelTraits<OutputPixelType>::GetNthComponent(j, minimumDifference));
230  if (m > m_DifferenceThreshold * tMax)
231  {
232  // std::cout << std::setprecision(16) << minimumDifference[j] << std::endl;
233  isDifferent = true;
234  break;
235  }
236  }
237 
238  if (isDifferent)
239  {
240  // Store the minimum difference value in the output image.
241  out.Set(minimumDifference);
242 
243  // Update local difference image statistics.
244  threadDifferenceSum += minimumDifference;
245  threadNumberOfPixels++;
246  }
247  else
248  {
249  // Difference is below threshold.
250  out.Set(zero);
251  }
252 
253  // Update progress.
254  progress.CompletedPixel();
255  }
256  }
257 
258  // Update global difference image statistics.
259  m_ThreadDifferenceSum[threadId] += threadDifferenceSum;
260  m_ThreadNumberOfPixels[threadId] += threadNumberOfPixels;
261 }
262 
263 template <class TInputImage, class TOutputImage>
265 {
266  // Set statistics about difference image.
267  int numberOfThreads = this->GetNumberOfWorkUnits();
268  for (int i = 0; i < numberOfThreads; ++i)
269  {
270  m_TotalDifference += m_ThreadDifferenceSum[i];
271  m_NumberOfPixelsWithDifferences += m_ThreadNumberOfPixels[i];
272  }
273 
274  // Get the total number of pixels processed in the region.
275  // This is different from the m_TotalNumberOfPixels which
276  // is the number of pixels that actually have differences
277  // above the intensity threshold.
278  OutputImageRegionType region = this->GetOutput()->GetRequestedRegion();
279  unsigned int numberOfPixels = region.GetNumberOfPixels();
280 
281  // Calculate the mean difference.
282  m_MeanDifference = m_TotalDifference / numberOfPixels;
283 }
284 
285 
286 
287 } // end namespace otb
288 
289 #endif
void PrintSelf(std::ostream &os, itk::Indent indent) const override
void ThreadedGenerateData(const OutputImageRegionType &threadRegion, itk::ThreadIdType threadId) override
OutputImageType::PixelType OutputPixelType
itk::NumericTraits< OutputPixelType >::ScalarRealType ScalarRealType
OutputImageType::RegionType OutputImageRegionType
itk::NumericTraits< RealType >::AccumulateType AccumulateType
virtual void SetValidInput(const InputImageType *validImage)
virtual void SetTestInput(const InputImageType *testImage)
unsigned int GetNumberOfComponents(PixelType const &pix)
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.