OTB  9.0.0
Orfeo Toolbox
otbFineRegistrationImageFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2022 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 otbFineRegistrationImageFilter_hxx
22 #define otbFineRegistrationImageFilter_hxx
23 
25 
26 #include "itkProgressReporter.h"
27 #include "itkLinearInterpolateImageFunction.h"
28 #include "itkImageRegionIteratorWithIndex.h"
29 #include "itkNormalizedCorrelationImageToImageMetric.h"
30 #include "itkMacro.h"
31 
32 namespace otb
33 {
37 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
39 {
40  this->SetNumberOfRequiredInputs(2);
41  this->SetNumberOfRequiredOutputs(2);
42  this->SetNthOutput(1, TOutputDisplacementField::New());
44 
45  // Default radius
46  m_Radius.Fill(2);
47  m_SearchRadius.Fill(4);
48 
49  // Default sub-pixel precision
50  m_SubPixelAccuracy = 0.1;
51  m_ConvergenceAccuracy = 0.01;
52 
53  // Max number of iterations
54  m_MaxIter = 100;
55 
56  // Flags
57  m_UseSpacing = true;
58  m_Minimize = true;
59 
60  // Default currentMetric
61  m_Metric = itk::NormalizedCorrelationImageToImageMetric<TInputImage, TInputImage>::New();
62 
63  // Default interpolator
64  m_Interpolator = itk::LinearInterpolateImageFunction<TInputImage, double>::New();
65 
66  // Translation
67  m_Translation = TranslationType::New();
68 
69  // Grid Step
70  m_GridStep.Fill(1);
71 
72  // Default offset
73  m_InitialOffset.Fill(0);
74 
75  m_Transform = nullptr;
76 }
77 
78 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
80 {
81  // Process object is not const-correct so the const casting is required.
82  this->SetNthInput(0, const_cast<TInputImage*>(image));
83 }
84 
85 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
87 {
88  // Process object is not const-correct so the const casting is required.
89  this->SetNthInput(1, const_cast<TInputImage*>(image));
90 }
91 
92 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
94 {
95  if (this->GetNumberOfInputs() < 1)
96  {
97  return nullptr;
98  }
99  return static_cast<const TInputImage*>(this->itk::ProcessObject::GetInput(0));
100 }
101 
102 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
104 {
105  if (this->GetNumberOfInputs() < 2)
106  {
107  return nullptr;
108  }
109  return static_cast<const TInputImage*>(this->itk::ProcessObject::GetInput(1));
110 }
111 
112 template <class TInputImage, class T0utputCorrelation, class TOutputDisplacementField>
114 {
115  if (this->GetNumberOfOutputs() < 2)
116  {
117  return nullptr;
118  }
119  return static_cast<TOutputDisplacementField*>(this->itk::ProcessObject::GetOutput(1));
120 }
121 
122 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
124 {
125  // Call superclass implementation
126  Superclass::GenerateOutputInformation();
127 
128  // Retrieve output pointers
129  TOutputCorrelation* outputPtr = this->GetOutput();
130  TOutputDisplacementField* outputFieldPtr = this->GetOutputDisplacementField();
131 
132  // Update size and spacing according to grid step
133  InputImageRegionType largestRegion = outputPtr->GetLargestPossibleRegion();
134  SizeType outputSize = largestRegion.GetSize();
135  SpacingType outputSpacing = outputPtr->GetSignedSpacing();
136 
137  for (unsigned int dim = 0; dim < TOutputCorrelation::ImageDimension; ++dim)
138  {
139  outputSize[dim] /= m_GridStep[dim];
140  outputSpacing[dim] *= m_GridStep[dim];
141  }
142 
143  // Set spacing
144  outputPtr->SetSignedSpacing(outputSpacing);
145  outputFieldPtr->SetSignedSpacing(outputSpacing);
146 
147  // Set largest region size
148  largestRegion.SetSize(outputSize);
149  outputPtr->SetLargestPossibleRegion(largestRegion);
150  outputFieldPtr->SetLargestPossibleRegion(largestRegion);
151 }
152 
153 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
155 {
156  // call the superclass' implementation of this method
157  Superclass::GenerateInputRequestedRegion();
158 
159  // get pointers to the input and output
160  TInputImage* fixedPtr = const_cast<TInputImage*>(this->GetFixedInput());
161  TInputImage* movingPtr = const_cast<TInputImage*>(this->GetMovingInput());
162 
163  TOutputCorrelation* outputPtr = this->GetOutput();
164 
165  if (!fixedPtr || !movingPtr || !outputPtr)
166  {
167  return;
168  }
169 
170  // get a copy of the fixed requested region (should equal the output
171  // requested region)
172  InputImageRegionType fixedRequestedRegion, movingRequestedRegion;
173  fixedRequestedRegion = outputPtr->GetRequestedRegion();
174 
175  // Apply grid step
176  SizeType fixedRequestedSize = fixedRequestedRegion.GetSize();
177  IndexType fixedRequestedIndex = fixedRequestedRegion.GetIndex();
178 
179  for (unsigned int dim = 0; dim < TOutputCorrelation::ImageDimension; ++dim)
180  {
181  fixedRequestedSize[dim] *= m_GridStep[dim];
182  fixedRequestedIndex[dim] *= m_GridStep[dim];
183  }
184 
185  fixedRequestedRegion.SetSize(fixedRequestedSize);
186  fixedRequestedRegion.SetIndex(fixedRequestedIndex);
187 
188  // pad the input requested region by the operator radius
189  fixedRequestedRegion.PadByRadius(m_Radius);
190 
191 
192  // get a copy of the moving requested region (should equal the output
193  // requested region)
194  InputImageRegionType searchFixedRequestedRegion = fixedRequestedRegion;
195  searchFixedRequestedRegion.PadByRadius(m_SearchRadius);
196 
197 
198  // Find corners of the search window
199  IndexType ulIndex = searchFixedRequestedRegion.GetIndex();
200 
201  IndexType lrIndex;
202  for (unsigned int dim = 0; dim < TInputImage::ImageDimension; ++dim)
203  {
204  lrIndex[dim] = searchFixedRequestedRegion.GetIndex()[dim] + searchFixedRequestedRegion.GetSize()[dim] - 1;
205  }
206 
207  // Transform to physical space
208  PointType ulPoint, lrPoint;
209  fixedPtr->TransformIndexToPhysicalPoint(lrIndex, lrPoint);
210  fixedPtr->TransformIndexToPhysicalPoint(ulIndex, ulPoint);
211 
212  // Apply default offset
213  lrPoint += m_InitialOffset;
214  ulPoint += m_InitialOffset;
215 
216  // Transform back into moving region index space
217  IndexType movingIndex1, movingIndex2, movingIndex;
218  movingPtr->TransformPhysicalPointToIndex(ulPoint, movingIndex1);
219  movingPtr->TransformPhysicalPointToIndex(lrPoint, movingIndex2);
220 
221  // Find requested region
222  SizeType movingSize;
223 
224  for (unsigned int dim = 0; dim < TInputImage::ImageDimension; ++dim)
225  {
226  movingIndex[dim] = std::min(movingIndex1[dim], movingIndex2[dim]);
227  movingSize[dim] = std::max(movingIndex1[dim], movingIndex2[dim]) - movingIndex[dim] + 1;
228  }
229 
230  movingRequestedRegion.SetIndex(movingIndex);
231  movingRequestedRegion.SetSize(movingSize);
232 
233  // crop the fixed region at the fixed's largest possible region
234  if (fixedRequestedRegion.Crop(fixedPtr->GetLargestPossibleRegion()))
235  {
236  fixedPtr->SetRequestedRegion(fixedRequestedRegion);
237  }
238  else
239  {
240  // Couldn't crop the region (requested region is outside the largest
241  // possible region). Throw an exception.
242  // store what we tried to request (prior to trying to crop)
243  fixedPtr->SetRequestedRegion(fixedRequestedRegion);
244 
245  // build an exception
246  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
247  std::ostringstream msg;
248  msg << this->GetNameOfClass() << "::GenerateInputRequestedRegion()";
249  e.SetLocation(msg.str());
250  e.SetDescription("Requested region is (at least partially) outside the largest possible region of image 1.");
251  e.SetDataObject(fixedPtr);
252  throw e;
253  }
254 
255  // crop the moving region at the moving's largest possible region
256  if (movingRequestedRegion.Crop(movingPtr->GetLargestPossibleRegion()))
257  {
258  movingPtr->SetRequestedRegion(movingRequestedRegion);
259  }
260  else
261  {
262  // Couldn't crop the region (requested region is outside the largest
263  // possible region). This case might happen so we do not throw any exception but
264  // request a null region instead
265  movingSize.Fill(0);
266  movingRequestedRegion.SetSize(movingSize);
267  movingIndex.Fill(0);
268  movingRequestedRegion.SetIndex(movingIndex);
269 
270  // store what we tried to request (prior to trying to crop)
271  movingPtr->SetRequestedRegion(movingRequestedRegion);
272  }
273  return;
274 }
275 
276 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
278 {
279  typename TranslationType::ParametersType paramsgn(2);
280  paramsgn[0] = val1;
281  paramsgn[1] = val2;
282 
283  double res = oldRes;
284  flag = false;
285 
286  try
287  {
288  res = m_Metric->GetValue(paramsgn);
289  }
290  catch (itk::ExceptionObject& err)
291  {
292  flag = true;
293  itkWarningMacro(<< err.GetDescription());
294  }
295 
296  return res;
297 }
298 
299 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
301  double potBestVal, double parx, double pary, double& bestVal, typename TranslationType::ParametersType& optParams)
302 {
303  if (bestVal > potBestVal)
304  {
305  bestVal = potBestVal;
306  optParams[0] = parx;
307  optParams[1] = pary;
308  }
309 }
310 
311 
312 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
314  double& out1, double& out2, double& out3,
315  double& out4)
316 {
317  out1 = out2;
318  out2 = in3;
319  out3 = in1 + gn * (in2 - in1);
320  out4 = in2 - gn * (in2 - in1);
321 }
322 
323 
324 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
326 {
327  if (!m_Minimize)
328  {
329  a = -a;
330  b = -b;
331  }
332 }
333 
334 
335 template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField>
337 {
338  // Allocate outputs
339  this->AllocateOutputs();
340 
341  // Get the image pointers
342  const TInputImage* fixedPtr = this->GetFixedInput();
343  const TInputImage* movingPtr = this->GetMovingInput();
344  TOutputCorrelation* outputPtr = this->GetOutput();
345  TOutputDisplacementField* outputDfPtr = this->GetOutputDisplacementField();
346 
347  // Wire currentMetric
348  m_Interpolator->SetInputImage(this->GetMovingInput());
349  m_Metric->SetTransform(m_Translation);
350  m_Metric->SetInterpolator(m_Interpolator);
351  m_Metric->SetFixedImage(fixedPtr);
352  m_Metric->SetMovingImage(movingPtr);
353  m_Metric->SetComputeGradient(false);
354 
356  itk::ImageRegionIteratorWithIndex<TOutputCorrelation> outputIt(outputPtr, outputPtr->GetRequestedRegion());
357  itk::ImageRegionIterator<TOutputDisplacementField> outputDfIt(outputDfPtr, outputPtr->GetRequestedRegion());
358  outputIt.GoToBegin();
359  outputDfIt.GoToBegin();
361 
362  // support progress methods/callbacks
363  itk::ProgressReporter progress(this, 0, outputPtr->GetRequestedRegion().GetNumberOfPixels());
364 
365  // GoToBegin
366  outputIt.GoToBegin();
367  outputDfIt.GoToBegin();
368 
369  // Correl, max correl, maxPosition
370  double currentMetric, optMetric;
371 
372  // Optimal translation parameters
373  typename TranslationType::ParametersType params(2), optParams(2);
374 
375  // Final displacement value
376  DisplacementValueType displacementValue;
377  displacementValue[0] = m_InitialOffset[0];
378  displacementValue[1] = m_InitialOffset[1];
379 
380  // Local initial offset: enable the possibility of a different initial offset for each pixel
381  SpacingType localOffset = m_InitialOffset;
382 
383  // Get fixed image spacing
384  SpacingType fixedSpacing = fixedPtr->GetSignedSpacing();
385 
386 
387  // Walk the images
388  while (!outputIt.IsAtEnd() && !outputDfIt.IsAtEnd())
389  {
390  // Reset
391  if (m_Minimize)
392  {
393  optMetric = itk::NumericTraits<double>::max();
394  }
395  else
396  {
397  optMetric = itk::NumericTraits<double>::NonpositiveMin();
398  }
399  optParams.Fill(0);
400 
401  // Build the region on which to compute the currentMetric
402  InputImageRegionType currentMetricRegion;
403 
404  // Apply grid step
405  IndexType currentIndex = outputIt.GetIndex();
406  for (unsigned int dim = 0; dim < TInputImage::ImageDimension; ++dim)
407  {
408  currentIndex[dim] *= m_GridStep[dim];
409  }
410  currentMetricRegion.SetIndex(currentIndex);
411  SizeType size;
412  size.Fill(1);
413  currentMetricRegion.SetSize(size);
414  currentMetricRegion.PadByRadius(m_Radius);
415  currentMetricRegion.Crop(fixedPtr->GetLargestPossibleRegion());
416  m_Metric->SetFixedImageRegion(currentMetricRegion);
417  m_Metric->Initialize();
418 
419 
420  // Compute the local offset if required (and the transform was specified)
421  if (m_Transform.IsNotNull())
422  {
423  PointType inputPoint, outputPoint;
424  for (unsigned int dim = 0; dim < TInputImage::ImageDimension; ++dim)
425  {
426  inputPoint[dim] = currentIndex[dim];
427  }
428  outputPoint = m_Transform->TransformPoint(inputPoint);
429  for (unsigned int dim = 0; dim < TInputImage::ImageDimension; ++dim)
430  {
431  localOffset[dim] = outputPoint[dim] - inputPoint[dim]; // FIXME check the direction
432  }
433  }
434 
435  // Compute the correlation at each location
436  for (int i = -static_cast<int>(m_SearchRadius[0]); i <= static_cast<int>(m_SearchRadius[0]); ++i)
437  {
438  for (int j = -static_cast<int>(m_SearchRadius[1]); j <= static_cast<int>(m_SearchRadius[1]); ++j)
439  {
440  params[0] = localOffset[0] + static_cast<double>(i * fixedSpacing[0]);
441  params[1] = localOffset[1] + static_cast<double>(j * fixedSpacing[1]);
442 
443  try
444  {
445  // compute currentMetric
446  currentMetric = m_Metric->GetValue(params);
447 
448  // Check for maximum
449  if ((m_Minimize && (currentMetric < optMetric)) || (!m_Minimize && (currentMetric > optMetric)))
450  {
451  optMetric = currentMetric;
452  optParams = params;
453  }
454  }
455  catch (itk::ExceptionObject& err)
456  {
457  itkWarningMacro(<< err.GetDescription());
458  }
459  }
460  }
461 
462 
463  // golden section search
464  typename TranslationType::ParametersType paramsgn(2);
465  double gn = (sqrt(5.0) - 1.0) / 2.0;
466  SpacingType subPixelSpacing = fixedSpacing;
467  double ax = optParams[0] - static_cast<double>(subPixelSpacing[0]);
468  double ay = optParams[1] - static_cast<double>(subPixelSpacing[1]);
469  double bx = optParams[0] + static_cast<double>(subPixelSpacing[0]);
470  double by = optParams[1] + static_cast<double>(subPixelSpacing[1]);
471 
472 
473  double cx = bx - gn * (bx - ax);
474  double cy = optParams[1]; // by-gn*(by-ay);
475  double dx = ax + gn * (bx - ax);
476  double dy = optParams[1]; // ay+gn*(by-ay);
477  double fc = 0, fd = 0;
478  bool exitWhile = false;
479  int nbIter = 0;
480  double bestvalold = itk::NumericTraits<double>::max(), bestval = optMetric;
481  double diff = itk::NumericTraits<double>::max();
482  double diffx = itk::NumericTraits<double>::max();
483  double diffy = itk::NumericTraits<double>::max();
484 
485  // init
486  fc = callMetric(cx, cy, fc, exitWhile);
487  fd = callMetric(dx, dy, fd, exitWhile);
488  updateMinimize(fc, fd);
489 
490  // loop
491  while ((diffx > m_SubPixelAccuracy || diffy > m_SubPixelAccuracy) && (diff > m_ConvergenceAccuracy) && (!exitWhile) && (nbIter <= m_MaxIter))
492  {
493  nbIter++;
494 
495  // x direction
496  if (fc < fd)
497  {
498  updateOptParams(fc, cx, cy, bestval, optParams);
499  updatePoints(gn, ay, by, cx, bx, dx, dy, cy);
500  fc = callMetric(cx, cy, fc, exitWhile);
501  fd = callMetric(dx, dy, fd, exitWhile);
502  }
503  else
504  {
505  updateOptParams(fd, dx, dy, bestval, optParams);
506  updatePoints(gn, by, ay, dx, ax, cx, cy, dy);
507  fc = callMetric(cx, cy, fc, exitWhile);
508  fd = callMetric(dx, dy, fd, exitWhile);
509  }
510  updateMinimize(fc, fd);
511 
512  // y direction
513  if (fc < fd)
514  {
515  updateOptParams(fc, cx, cy, bestval, optParams);
516  updatePoints(gn, ax, bx, cy, by, dy, dx, cx);
517  fc = callMetric(cx, cy, fc, exitWhile);
518  fd = callMetric(dx, dy, fd, exitWhile);
519  }
520  else
521  {
522  updateOptParams(fd, dx, dy, bestval, optParams);
523  updatePoints(gn, bx, ax, dy, ay, cy, cx, dx);
524  fc = callMetric(cx, cy, fc, exitWhile);
525  fd = callMetric(dx, dy, fd, exitWhile);
526  }
527  updateMinimize(fc, fd);
528 
529  // Before eventual exit, take benefit from the last evaluations of fd and fc
530  updateOptParams(fd, dx, dy, bestval, optParams);
531  updateOptParams(fc, cx, cy, bestval, optParams);
532 
533  if (!m_Minimize)
534  bestval = -bestval;
535 
536  diff = fabs(bestval - bestvalold);
537  bestvalold = bestval;
538  diffx = fabs(bx - ax);
539  diffy = fabs(by - ay);
540  }
541 
542  // Store the offset and the correlation value
543  outputIt.Set(optMetric);
544  if (m_UseSpacing)
545  {
546  displacementValue[0] = optParams[0];
547  displacementValue[1] = optParams[1];
548  }
549  else
550  {
551  displacementValue[0] = optParams[0] / fixedSpacing[0];
552  displacementValue[1] = optParams[1] / fixedSpacing[1];
553  }
554  outputDfIt.Set(displacementValue);
555 
556 
557  // Update iterators
558  ++outputIt;
559  ++outputDfIt;
560 
561  // Update progress
562  progress.CompletedPixel();
563  }
564 }
565 } // end namespace otb
566 
567 #endif
otb::FineRegistrationImageFilter::GetFixedInput
const TInputImage * GetFixedInput()
Definition: otbFineRegistrationImageFilter.hxx:93
otb::FineRegistrationImageFilter::SizeType
TInputImage::SizeType SizeType
Definition: otbFineRegistrationImageFilter.h:94
otb::FineRegistrationImageFilter::GenerateInputRequestedRegion
void GenerateInputRequestedRegion(void) override
Definition: otbFineRegistrationImageFilter.hxx:154
otb::FineRegistrationImageFilter::updatePoints
void updatePoints(double &gn, double &in1, double &in2, double &in3, double &out1, double &out2, double &out3, double &out4)
Definition: otbFineRegistrationImageFilter.hxx:313
otb::FineRegistrationImageFilter::FineRegistrationImageFilter
FineRegistrationImageFilter()
Definition: otbFineRegistrationImageFilter.hxx:38
otb::FineRegistrationImageFilter::GenerateData
void GenerateData() override
Definition: otbFineRegistrationImageFilter.hxx:336
otb::FineRegistrationImageFilter::PointType
TInputImage::PointType PointType
Definition: otbFineRegistrationImageFilter.h:97
otb::FineRegistrationImageFilter::updateOptParams
void updateOptParams(double potBestVal, double parx, double pary, double &bestVal, typename TranslationType::ParametersType &optParams)
Definition: otbFineRegistrationImageFilter.hxx:300
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otb::FineRegistrationImageFilter::callMetric
double callMetric(double val1, double val2, double &oldRes, bool &flag)
Definition: otbFineRegistrationImageFilter.hxx:277
otb::FineRegistrationImageFilter::updateMinimize
void updateMinimize(double &a, double &b)
Definition: otbFineRegistrationImageFilter.hxx:325
otb::FineRegistrationImageFilter::DisplacementValueType
TOutputDisplacementField::PixelType DisplacementValueType
Definition: otbFineRegistrationImageFilter.h:91
otb::FineRegistrationImageFilter::GenerateOutputInformation
void GenerateOutputInformation(void) override
Definition: otbFineRegistrationImageFilter.hxx:123
otb::FineRegistrationImageFilter::SetFixedInput
void SetFixedInput(const TInputImage *image)
Definition: otbFineRegistrationImageFilter.hxx:79
otb::FineRegistrationImageFilter::SetMovingInput
void SetMovingInput(const TInputImage *image)
Definition: otbFineRegistrationImageFilter.hxx:86
otb::FineRegistrationImageFilter::GetOutputDisplacementField
TOutputDisplacementField * GetOutputDisplacementField()
Definition: otbFineRegistrationImageFilter.hxx:113
otbFineRegistrationImageFilter.h
otb::FineRegistrationImageFilter::InputImageRegionType
TInputImage::RegionType InputImageRegionType
Definition: otbFineRegistrationImageFilter.h:93
otb::FineRegistrationImageFilter::GetMovingInput
const TInputImage * GetMovingInput()
Definition: otbFineRegistrationImageFilter.hxx:103
otb::FineRegistrationImageFilter::SpacingType
TInputImage::SpacingType SpacingType
Definition: otbFineRegistrationImageFilter.h:96
otb::FineRegistrationImageFilter::IndexType
TInputImage::IndexType IndexType
Definition: otbFineRegistrationImageFilter.h:95