OTB  10.0.0
Orfeo Toolbox
otbStereoSensorModelToElevationMapFilter.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 otbStereoSensorModelToElevationMapFilter_hxx
22 #define otbStereoSensorModelToElevationMapFilter_hxx
23 
25 
26 #include "itkImageRegionIteratorWithIndex.h"
27 #include "itkProgressReporter.h"
28 #include "itkLinearInterpolateImageFunction.h"
30 #include "itkConstNeighborhoodIterator.h"
31 #include "otbDEMHandler.h"
32 
33 namespace otb
34 {
35 template <class TInputImage, class TOutputHeight>
37 {
38  // Filter has two inputs
39  this->SetNumberOfRequiredInputs(2);
40  this->SetNumberOfRequiredOutputs(2);
41  this->SetNthOutput(1, OutputImageType::New());
42 
43  // Default interpolator
44  m_Interpolator = itk::LinearInterpolateImageFunction<TInputImage>::New();
45 
46  // Default correlation radius
47  m_Radius.Fill(3);
48 
49  // Height exploration setup
50  m_LowerElevation = -20;
51  m_HigherElevation = 20;
52  m_ElevationStep = 1.;
53  m_CorrelationThreshold = 0.7;
54  m_VarianceThreshold = 4;
55  m_SubtractInitialElevation = false;
56 }
57 
58 template <class TInputImage, class TOutputHeight>
60 {
61 }
62 
63 template <class TInputImage, class TOutputHeight>
65 {
66  this->SetNthInput(0, const_cast<TInputImage*>(image));
67 }
68 
69 template <class TInputImage, class TOutputHeight>
71 {
72  this->SetNthInput(1, const_cast<TInputImage*>(image));
73 }
74 
75 template <class TInputImage, class TOutputHeight>
77 {
78  if (this->GetNumberOfInputs() < 1)
79  {
80  return nullptr;
81  }
82  return static_cast<const TInputImage*>(this->itk::ProcessObject::GetInput(0));
83 }
84 
85 template <class TInputImage, class TOutputHeight>
87 {
88  if (this->GetNumberOfInputs() < 2)
89  {
90  return nullptr;
91  }
92  return static_cast<const TInputImage*>(this->itk::ProcessObject::GetInput(1));
93 }
94 
95 template <class TInputImage, class TOutputHeight>
97 {
98  if (this->GetNumberOfOutputs() < 2)
99  {
100  return nullptr;
101  }
102  return static_cast<TOutputHeight*>(this->itk::ProcessObject::GetOutput(1));
103 }
104 
105 template <class TInputImage, class TOutputHeight>
107 {
108  // Call the superclass implementation
109  Superclass::GenerateInputRequestedRegion();
110 
111  // Retrieve pointers
112  InputImageType* masterPtr = const_cast<InputImageType*>(this->GetMasterInput());
113  InputImageType* slavePtr = const_cast<InputImageType*>(this->GetSlaveInput());
114  OutputImageType* outputPtr = this->GetOutput();
115 
116  if (!masterPtr || !slavePtr || !outputPtr)
117  {
118  return;
119  }
120 
121  // get a copy of the fixed requested region (should equal the output
122  // requested region)
123  typename InputImageType::RegionType masterRequestedRegion, slaveRequestedRegion;
124  masterRequestedRegion = outputPtr->GetRequestedRegion();
125 
126  // pad the master requested region by the operator radius
127  masterRequestedRegion.PadByRadius(m_Radius);
128 
129  // Find corners of the master requested region
130  typename InputImageType::IndexType mul, mur, mll, mlr;
131  mul = masterRequestedRegion.GetIndex();
132  mur = masterRequestedRegion.GetIndex();
133  mur[0] += masterRequestedRegion.GetSize()[0] - 1;
134  mll = masterRequestedRegion.GetIndex();
135  mll[1] += masterRequestedRegion.GetSize()[1] - 1;
136  mlr = masterRequestedRegion.GetIndex();
137  mlr[0] += masterRequestedRegion.GetSize()[0] - 1;
138  mlr[1] += masterRequestedRegion.GetSize()[1] - 1;
139 
140  // Transform to physical space
141  typename InputImageType::PointType mpul, mpur, mpll, mplr;
142  masterPtr->TransformIndexToPhysicalPoint(mul, mpul);
143  masterPtr->TransformIndexToPhysicalPoint(mur, mpur);
144  masterPtr->TransformIndexToPhysicalPoint(mll, mpll);
145  masterPtr->TransformIndexToPhysicalPoint(mlr, mplr);
146 
147  // Build the transform to switch from the master to the slave image
148  typename GenericRSTransformType::Pointer transform = GenericRSTransformType::New();
149  transform->SetInputImageMetadata(&(masterPtr->GetImageMetadata()));
150  transform->SetOutputImageMetadata(&(slavePtr->GetImageMetadata()));
151 
152  transform->InstantiateTransform();
153 
154  typename GenericRSTransformType::ParametersType params(1);
155 
156  // Build minimum height and maximum height points corresponding to
157  // corners of the master requested region
158  typename InputImageType::PointType sMinPul, sMinPur, sMinPll, sMinPlr, sMaxPul, sMaxPur, sMaxPll, sMaxPlr;
159 
160  // Lower case
161  params[0] = m_LowerElevation;
162  transform->SetParameters(params);
163 
164  sMinPul = transform->TransformPoint(mpul);
165  sMinPur = transform->TransformPoint(mpur);
166  sMinPll = transform->TransformPoint(mpll);
167  sMinPlr = transform->TransformPoint(mplr);
168 
169  // Higher case
170  params[0] = m_HigherElevation;
171  transform->SetParameters(params);
172 
173  sMaxPul = transform->TransformPoint(mpul);
174  sMaxPur = transform->TransformPoint(mpur);
175  sMaxPll = transform->TransformPoint(mpll);
176  sMaxPlr = transform->TransformPoint(mplr);
177 
178  // Transform to index
179  typename InputImageType::IndexType sMinul, sMinur, sMinll, sMinlr, sMaxul, sMaxur, sMaxll, sMaxlr;
180 
181  slavePtr->TransformPhysicalPointToIndex(sMinPul, sMinul);
182  slavePtr->TransformPhysicalPointToIndex(sMaxPul, sMaxul);
183  slavePtr->TransformPhysicalPointToIndex(sMinPur, sMinur);
184  slavePtr->TransformPhysicalPointToIndex(sMaxPur, sMaxur);
185  slavePtr->TransformPhysicalPointToIndex(sMinPll, sMinll);
186  slavePtr->TransformPhysicalPointToIndex(sMaxPll, sMaxll);
187  slavePtr->TransformPhysicalPointToIndex(sMinPlr, sMinlr);
188  slavePtr->TransformPhysicalPointToIndex(sMaxPlr, sMaxlr);
189 
190  // Find the corresponding bounding box
191  typename InputImageType::IndexType sul, slr;
192 
193  sul[0] = std::min(std::min(std::min(sMinul[0], sMaxul[0]), std::min(sMinur[0], sMaxur[0])),
194  std::min(std::min(sMinll[0], sMaxll[0]), std::min(sMinlr[0], sMaxlr[0])));
195  sul[1] = std::min(std::min(std::min(sMinul[1], sMaxul[1]), std::min(sMinur[1], sMaxur[1])),
196  std::min(std::min(sMinll[1], sMaxll[1]), std::min(sMinlr[1], sMaxlr[1])));
197  slr[0] = std::max(std::max(std::max(sMinul[0], sMaxul[0]), std::max(sMinur[0], sMaxur[0])),
198  std::max(std::max(sMinll[0], sMaxll[0]), std::max(sMinlr[0], sMaxlr[0])));
199  slr[1] = std::max(std::max(std::max(sMinul[1], sMaxul[1]), std::max(sMinur[1], sMaxur[1])),
200  std::max(std::max(sMinll[1], sMaxll[1]), std::max(sMinlr[1], sMaxlr[1])));
201 
202  // Build the slave requested region
203  slaveRequestedRegion.SetIndex(sul);
204  typename InputImageType::SizeType ssize;
205  ssize[0] = slr[0] - sul[0] + 1;
206  ssize[1] = slr[1] - sul[1] + 1;
207  slaveRequestedRegion.SetSize(ssize);
208 
209  // crop the master region at the master's largest possible region
210  if (masterRequestedRegion.Crop(masterPtr->GetLargestPossibleRegion()))
211  {
212  masterPtr->SetRequestedRegion(masterRequestedRegion);
213  }
214  else
215  {
216  // Couldn't crop the region (requested region is outside the largest
217  // possible region). Throw an exception.
218  // store what we tried to request (prior to trying to crop)
219  masterPtr->SetRequestedRegion(masterRequestedRegion);
220 
221  // build an exception
222  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
223  std::ostringstream msg;
224  msg << this->GetNameOfClass() << "::GenerateInputRequestedRegion()";
225  e.SetLocation(msg.str());
226  e.SetDescription("Requested region is (at least partially) outside the largest possible region of image 1.");
227  e.SetDataObject(masterPtr);
228  throw e;
229  }
230 
231  // crop the slave region at the slave's largest possible region
232  if (slaveRequestedRegion.Crop(slavePtr->GetLargestPossibleRegion()))
233  {
234  slavePtr->SetRequestedRegion(slaveRequestedRegion);
235  }
236  else
237  {
238  // Couldn't crop the region (requested region is outside the largest
239  // possible region). This case might happen so we do not throw any exception but
240  // request a null region instead
241  typename InputImageType::SizeType slaveSize;
242  slaveSize.Fill(0);
243  slaveRequestedRegion.SetSize(slaveSize);
244  typename InputImageType::IndexType slaveIndex;
245  slaveIndex.Fill(0);
246  slaveRequestedRegion.SetIndex(slaveIndex);
247 
248  // store what we tried to request (prior to trying to crop)
249  slavePtr->SetRequestedRegion(slaveRequestedRegion);
250  }
251  return;
252 }
253 
254 template <class TInputImage, class TOutputHeight>
256 {
257  // Wire the interpolator
258  m_Interpolator->SetInputImage(this->GetSlaveInput());
259  this->GetCorrelationOutput()->FillBuffer(0.);
260 
261  // Initialize with average elevation
262  this->GetOutput()->FillBuffer(otb::DEMHandler::GetInstance().GetDefaultHeightAboveEllipsoid());
263 
264  // Initialize with DEM elevation (not threadable because of some
265  // mutex in ossim)
266  OutputImageType* outputPtr = this->GetOutput();
267 
268  typename GenericRSTransformType::Pointer rsTransform = GenericRSTransformType::New();
269  rsTransform->SetInputImageMetadata(&(outputPtr->GetImageMetadata()));
270  rsTransform->InstantiateTransform();
271 
272  // Fill output
273  itk::ImageRegionIteratorWithIndex<OutputImageType> outputIt(outputPtr, outputPtr->GetBufferedRegion());
274 
275  for (outputIt.GoToBegin(); !outputIt.IsAtEnd(); ++outputIt)
276  {
277  // Retrieve physical point
278  OutputPointType outputPoint, geoPoint;
279  outputPtr->TransformIndexToPhysicalPoint(outputIt.GetIndex(), outputPoint);
280 
281  // Transform to geo point
282  geoPoint = rsTransform->TransformPoint(outputPoint);
283  outputIt.Set(otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(geoPoint));
284  }
285 
286  const InputImageType* masterPtr = this->GetMasterInput();
287  const InputImageType* slavePtr = this->GetSlaveInput();
288 
289  // Set-up the forward-inverse sensor model transform
290  m_MasterToSlave = GenericRSTransform3DType::New();
291  m_MasterToSlave->SetInputImageMetadata(&(masterPtr->GetImageMetadata()));
292  m_MasterToSlave->SetOutputImageMetadata(&(slavePtr->GetImageMetadata()));
293  m_MasterToSlave->InstantiateTransform();
294 }
295 
296 template <class TInputImage, class TOutputHeight>
298 {
299  // Retrieve pointers
300  const InputImageType* masterPtr = this->GetMasterInput();
301  OutputImageType* outputPtr = this->GetOutput();
302  OutputImageType* correlPtr = this->GetCorrelationOutput();
303 
304  // TODO: Uncomment when model optimization pushed
305  // // GCP refinement
306  // unsigned int gcpCount1 = this->GetMasterInput()->GetGCPCount();
307  // unsigned int gcpCount2 = this->GetSlaveInput()->GetGCPCount();
308 
309 
310  // transform->ClearInputTiePoints();
311  // transform->ClearOutputTiePoints();
312 
313  // for(unsigned int i = 0; i < gcpCount1; ++i)
314  // {
315  // typename GenericRSTransformType::InputPointType imagePoint, groundPoint, groundPointWGS84;
316  // // Transform GCP ground part to WGS84
317  // groundPoint[0] = this->GetMasterInput()->GetGCPs(i).m_GCPX;
318  // groundPoint[1] = this->GetMasterInput()->GetGCPs(i).m_GCPY;
319 
320  // typename GenericRSTransformType::Pointer gcpTransform = GenericRSTransformType::New();
321  // gcpTransform->SetInputProjectionRef(this->GetInput()->GetGCPProjection());
322  // gcpTransform->InstantiateTransform();
323 
324  // groundPointWGS84 = gcpTransform->TransformPoint(groundPoint);
325 
326  // imagePoint[0] = this->GetMasterInput()->GetGCPs(i).m_GCPCol;
327  // imagePoint[1] = this->GetMasterInput()->GetGCPs(i).m_GCPRow;
328 
329  // transform->AddInputTiePoint(imagePoint, groundPointWGS84);
330  // transform->OptimizeInputTransformOn();
331  // }
332 
333  // for(unsigned int i = 0; i < gcpCount2; ++i)
334  // {
335  // typename GenericRSTransformType::InputPointType imagePoint, groundPoint, groundPointWGS84;
336  // // Transform GCP ground part to WGS84
337  // groundPoint[0] = this->GetSlaveInput()->GetGCPs(i).m_GCPX;
338  // groundPoint[1] = this->GetSlaveInput()->GetGCPs(i).m_GCPY;
339 
340  // typename GenericRSTransformType::Pointer gcpTransform = GenericRSTransformType::New();
341  // gcpTransform->SetInputProjectionRef(this->GetInput()->GetGCPProjection());
342  // gcpTransform->InstantiateTransform();
343 
344  // groundPointWGS84 = gcpTransform->TransformPoint(groundPoint);
345 
346  // imagePoint[0] = this->GetSlaveInput()->GetGCPs(i).m_GCPCol;
347  // imagePoint[1] = this->GetSlaveInput()->GetGCPs(i).m_GCPRow;
348 
349  // transform->AddOutputTiePoint(imagePoint, groundPointWGS84);
350  // transform->OptimizeOutputTransformOn();
351  // }
352 
353 
354  // Define an iterator on the output elevation map
355  itk::ConstNeighborhoodIterator<InputImageType> inputIt(m_Radius, masterPtr, outputRegionForThread);
356  itk::ImageRegionIterator<OutputImageType> outputIt(outputPtr, outputRegionForThread);
357  itk::ImageRegionIteratorWithIndex<OutputImageType> correlIt(correlPtr, outputRegionForThread);
358 
359  // Start visiting buffer again
360  outputIt.GoToBegin();
361  correlIt.GoToBegin();
362  inputIt.GoToBegin();
363 
364 
365  // This will hold the master patch
366  std::vector<double> master;
367  master.reserve(inputIt.Size());
368  master = std::vector<double>(inputIt.Size(), 0);
369 
370  // And this will hold the slave patch
371  std::vector<double> slave;
372  slave.reserve(inputIt.Size());
373  slave = std::vector<double>(inputIt.Size(), 0);
374 
375 
376  // Walk the output map
377  while (!outputIt.IsAtEnd() && !inputIt.IsAtEnd())
378  {
379  // Define some loop variables
380  typename InputImageType::PointType inPoint, outPoint, currentPoint, optimalPoint;
381  typename GenericRSTransform3DType::InputPointType in3DPoint, out3DPoint;
382  typename InputImageType::IndexType index;
383 
384  // Retrieve initial height
385  double initHeight = outputIt.Get();
386  double optimalHeight = initHeight;
387  double optimalCorrelation = 0;
388 
389  // Check if there is an height info
390  if (initHeight != -32768)
391  {
392  // These are used to estimate master patch variance
393  double masterSum = 0;
394  double masterVariance = 0;
395 
396  // Fill master patch
397  for (unsigned int i = 0; i < inputIt.Size(); ++i)
398  {
399  // Add to the master vector
400  double value = inputIt.GetPixel(i);
401  master[i] = value;
402 
403  // Cumulate for mean
404  masterSum += value;
405  }
406 
407  // Finalize mean
408  masterSum /= inputIt.Size();
409 
410  // Complete pooled variance computation
411  for (unsigned int i = 0; i < inputIt.Size(); ++i)
412  {
413  masterVariance += (master[i] - masterSum) * (master[i] - masterSum);
414  }
415  masterVariance /= (inputIt.Size() - 1);
416 
417  // Check for high enough variance so that correlation will be reliable
418  if (masterVariance > m_VarianceThreshold)
419  {
420  // Explore candidate heights
421  for (double height = initHeight + m_LowerElevation; height < initHeight + m_HigherElevation; height += m_ElevationStep)
422  {
423 
424  // Interpolate slave patch
425  for (unsigned int i = 0; i < inputIt.Size(); ++i)
426  {
427  // Retrieve the current index
428  index = inputIt.GetIndex(i);
429 
430  // Retrieve master physical point
431  masterPtr->TransformIndexToPhysicalPoint(index, inPoint);
432  in3DPoint[0] = inPoint[0];
433  in3DPoint[1] = inPoint[1];
434  in3DPoint[2] = height;
435 
436  // Transform to slave
437  out3DPoint = m_MasterToSlave->TransformPoint(in3DPoint);
438  outPoint[0] = out3DPoint[0];
439  outPoint[1] = out3DPoint[1];
440 
441  // Interpolate
442  if (m_Interpolator->IsInsideBuffer(outPoint))
443  {
444  // And fill slave vector
445  slave[i] = m_Interpolator->Evaluate(outPoint);
446  }
447  else
448  {
449  // If out of buffer, fill with 0.
450  slave[i] = 0.;
451  }
452  }
453 
454  // Now that we have the master and slave patches, call the
455  // correlation function
456  double correlationValue = this->Correlation(master, slave);
457 
458  // Check if a better correlation was found
459  if (correlationValue > optimalCorrelation)
460  {
461  // If found, update values
462  optimalCorrelation = correlationValue;
463  optimalHeight = height;
464  }
465  }
466  }
467  // TODO: refinenement step ?
468  }
469 
470  // Final check on best correlation value
471  double finalOffset = 0.;
472 
473  // Switch to subtract initial elevation mode
474  if (m_SubtractInitialElevation)
475  {
476  finalOffset = initHeight;
477  }
478 
479  if (optimalCorrelation > m_CorrelationThreshold)
480  {
481  outputIt.Set(optimalHeight - finalOffset);
482  correlIt.Set(optimalCorrelation);
483  }
484  else
485  {
486  outputIt.Set(initHeight - finalOffset);
487  correlIt.Set(0);
488  }
489 
490  // And iterators
491  ++inputIt;
492  ++outputIt;
493  ++correlIt;
494  }
495 }
496 
497 template <class TInputImage, class TOutputHeight>
498 double StereoSensorModelToElevationFilter<TInputImage, TOutputHeight>::Correlation(const std::vector<double>& master, const std::vector<double>& slave) const
499 {
500  double meanSlave = 0;
501  double meanMaster = 0;
502  double correlationValue = 0;
503 
504  // Compute means
505  for (unsigned int i = 0; i < master.size(); ++i)
506  {
507  meanSlave += slave[i];
508  meanMaster += master[i];
509  }
510  meanSlave /= slave.size();
511  meanMaster /= master.size();
512 
513  double crossProd = 0.;
514  double squareSumMaster = 0.;
515  double squareSumSlave = 0.;
516 
517 
518  // Compute correlation
519  for (unsigned int i = 0; i < master.size(); ++i)
520  {
521  crossProd += (slave[i] - meanSlave) * (master[i] - meanMaster);
522  squareSumSlave += (slave[i] - meanSlave) * (slave[i] - meanSlave);
523  squareSumMaster += (master[i] - meanMaster) * (master[i] - meanMaster);
524  }
525 
526  correlationValue = std::abs(crossProd / (std::sqrt(squareSumSlave) * std::sqrt(squareSumMaster)));
527 
528  return correlationValue;
529 }
530 
531 } // end namespace otb
532 
533 #endif
static DEMHandler & GetInstance()
itk::SmartPointer< Self > Pointer
itk::Point< ScalarType, NInputDimensions > InputPointType
double Correlation(const std::vector< double > &master, const std::vector< double > &slave) const
void DynamicThreadedGenerateData(const OutputRegionType &outputRegionForThread) override
Superclass::ParametersType ParametersType
Definition: otbTransform.h:74
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
double GetHeightAboveEllipsoid(DEMHandlerTLS const &, double lon, double lat)