Orfeo Toolbox  3.16
otbStereorectificationDeformationFieldSource.txx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: ORFEO Toolbox
4  Language: C++
5  Date: $Date$
6  Version: $Revision$
7 
8 
9  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
10  See OTBCopyright.txt for details.
11 
12 
13  This software is distributed WITHOUT ANY WARRANTY; without even
14  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  PURPOSE. See the above copyright notices for more information.
16 
17 =========================================================================*/
18 #ifndef __otbStereoSensorModelToElevationMapFilter_txx
19 #define __otbStereoSensorModelToElevationMapFilter_txx
20 
22 #include "itkProgressReporter.h"
23 #include "otbMath.h"
24 
25 // For partial specialization
26 #include "otbVectorImage.h"
27 
28 #include "otbDEMHandler.h"
29 
30 namespace otb
31 {
32 
33 template <class TInputImage, class TOutputImage>
36  m_ElevationOffset(50),
37  m_Scale(1),
38  m_GridStep(1),
39  m_LeftImage(),
40  m_RightImage(),
41  m_LeftToRightTransform(),
42  m_RightToLeftTransform(),
43  m_OutputOriginInLeftImage(),
44  m_MeanBaselineRatio(0),
45  m_UseDEM(false)
46 {
47  // Set the number of outputs to 2 (one deformation field for each
48  // image)
49  this->SetNumberOfOutputs(2);
50 
51  // Create the 2nd input (not created by default)
52  this->SetNthOutput(0, OutputImageType::New());
53  this->SetNthOutput(1, OutputImageType::New());
54 
55  // Build the RS Transforms
56  m_LeftToRightTransform = RSTransformType::New();
57  m_RightToLeftTransform = RSTransformType::New();
58 }
59 
60 template <class TInputImage, class TOutputImage>
62 ::OutputImageType *
65 {
66  if (this->GetNumberOfOutputs() < 1)
67  {
68  return 0;
69  }
70  return static_cast<const OutputImageType *>(this->itk::ProcessObject::GetOutput(0));
71 }
72 
73 template <class TInputImage, class TOutputImage>
75 ::OutputImageType *
78 {
79  if (this->GetNumberOfOutputs() < 1)
80  {
81  return 0;
82  }
83  return static_cast<OutputImageType *>(this->itk::ProcessObject::GetOutput(0));
84 }
85 
86 template <class TInputImage, class TOutputImage>
88 ::OutputImageType *
91 {
92  if (this->GetNumberOfOutputs() < 2)
93  {
94  return 0;
95  }
96  return static_cast<const OutputImageType *>(this->itk::ProcessObject::GetOutput(1));
97 }
98 
99 template <class TInputImage, class TOutputImage>
101 ::OutputImageType *
104 {
105  if (this->GetNumberOfOutputs() < 2)
106  {
107  return 0;
108  }
109  return static_cast<OutputImageType *>(this->itk::ProcessObject::GetOutput(1));
110 }
111 
112 template <class TInputImage, class TOutputImage>
113 void
116 {
117  //Superclass::GenerateOutputInformation();
118 
119  // Ensure that both left image and right image are available
120  if(m_LeftImage.IsNull() || m_RightImage.IsNull())
121  {
122  itkExceptionMacro(<<"Either left image or right image pointer is null, can not perform stereo-rectification.");
123  }
124 
125  // Ensure input images have up-to-date information
126  m_LeftImage->UpdateOutputInformation();
127  m_RightImage->UpdateOutputInformation();
128 
129  // Setup the DEM handler if needed
130  typename DEMHandler::Pointer demHandler = DEMHandler::Instance();
131 
132  // Set-up a transform to use the DEMHandler
133  typedef otb::GenericRSTransform<> RSTransform2DType;
134  RSTransform2DType::Pointer leftToGroundTransform = RSTransform2DType::New();
135  leftToGroundTransform->SetInputKeywordList(m_LeftImage->GetImageKeywordlist());
136 
137  leftToGroundTransform->InstanciateTransform();
138 
139  // Retrieve the deformation field pointers
140  OutputImageType * leftDFPtr = this->GetLeftDeformationFieldOutput();
141  OutputImageType * rightDFPtr = this->GetRightDeformationFieldOutput();
142 
143  // Set up the RS transforms
144  m_LeftToRightTransform->SetInputKeywordList(m_LeftImage->GetImageKeywordlist());
145  m_LeftToRightTransform->SetOutputKeywordList(m_RightImage->GetImageKeywordlist());
146  m_LeftToRightTransform->InstanciateTransform();
147 
148  m_RightToLeftTransform->SetInputKeywordList(m_RightImage->GetImageKeywordlist());
149  m_RightToLeftTransform->SetOutputKeywordList(m_LeftImage->GetImageKeywordlist());
150  m_RightToLeftTransform->InstanciateTransform();
151 
152  // Now, we must determine the optimized size, spacing and origin of the
153  // stereo-rectified images, as well as the position of the origin in
154  // the left image
155 
156  // First, spacing : choose a square spacing,
157  SpacingType outputSpacing;
158  outputSpacing.Fill(m_Scale * m_GridStep);
159  double mean_spacing=0.5*(vcl_abs(m_LeftImage->GetSpacing()[0])+vcl_abs(m_LeftImage->GetSpacing()[1]));
160  //double ratio_x = mean_spacing / vcl_abs(m_LeftImage->GetSpacing()[0]);
161  //double ratio_y = mean_spacing / vcl_abs(m_LeftImage->GetSpacing()[1]);
162 
163  outputSpacing[0]*=mean_spacing;
164  outputSpacing[1]*=mean_spacing;
165 
166  // Then, we retrieve the origin of the left input image
167  double localElevation = otb::DEMHandler::Instance()->GetDefaultHeightAboveEllipsoid();
168 
169  if(m_UseDEM)
170  {
171  RSTransform2DType::InputPointType tmpPoint;
172  localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(m_LeftImage->GetOrigin()));
173  }
174 
175  TDPointType leftInputOrigin;
176  leftInputOrigin[0] = m_LeftImage->GetOrigin()[0];
177  leftInputOrigin[1] = m_LeftImage->GetOrigin()[1];
178  leftInputOrigin[2] = localElevation;
179 
180  // Next, we will compute the parameters of the local epipolar line
181  // at the left image origin
182  TDPointType rightEpiPoint, leftEpiLineStart, leftEpiLineEnd;
183 
184  // This point is the image of the left input image origin at the
185  // average elevation
186  rightEpiPoint = m_LeftToRightTransform->TransformPoint(leftInputOrigin);
187 
188  // The begining of the epipolar line in the left image is the image
189  // of rightEpiPoint at a lower elevation (using the offset)
190  rightEpiPoint[2] = localElevation - m_ElevationOffset;
191  leftEpiLineStart = m_RightToLeftTransform->TransformPoint(rightEpiPoint);
192 
193  // The ending of the epipolar line in the left image is the image
194  // of rightEpiPoint at a higher elevation (using the offset)
195  rightEpiPoint[2] = localElevation + m_ElevationOffset;
196  leftEpiLineEnd = m_RightToLeftTransform->TransformPoint(rightEpiPoint);
197 
198  // Now, we can compute the equation of the epipolar line y = a*x+b
199  // epipolar angle is computed in left image physical space
200  double alpha = 0;
201  if (leftEpiLineEnd[0] == leftEpiLineStart[0])
202  {
203  if (leftEpiLineEnd[1] > leftEpiLineStart[1])
204  {
205  alpha = 0.5*otb::CONST_PI;
206  }
207  else
208  {
209  alpha = -0.5*otb::CONST_PI;
210  }
211  }
212  else
213  {
214  double a = (leftEpiLineEnd[1] - leftEpiLineStart[1])
215  / (leftEpiLineEnd[0] - leftEpiLineStart[0]);
216  double b = leftEpiLineStart[1] - a * leftEpiLineStart[0];
217  if (leftEpiLineEnd[0] > leftEpiLineStart[0])
218  {
219  alpha = vcl_atan(a);
220  }
221  else
222  {
223  alpha = otb::CONST_PI + vcl_atan(a);
224  }
225 
226  }
227 
228  // And compute the unitary vectors of the new axis (equivalent to
229  // the column of the rotation matrix)
230  // TODO: Check if we need to use the input image spacing here
231  double ux = vcl_cos(alpha);
232  double uy = vcl_sin(alpha);
233  double vx = - vcl_sin(alpha);
234  double vy = vcl_cos(alpha);
235 
236  // Now, we will compute the bounding box of the left input image in
237  // this rotated geometry
238 
239  // First we compute coordinates of the 4 corners (we omit ulx which
240  // coordinates are {0,0})
241  double urx = ux * m_LeftImage->GetLargestPossibleRegion().GetSize()[0] * m_LeftImage->GetSpacing()[0];
242  double ury = vx * m_LeftImage->GetLargestPossibleRegion().GetSize()[0] * m_LeftImage->GetSpacing()[0];
243  double llx = uy * m_LeftImage->GetLargestPossibleRegion().GetSize()[1] * m_LeftImage->GetSpacing()[1];
244  double lly = vy * m_LeftImage->GetLargestPossibleRegion().GetSize()[1] * m_LeftImage->GetSpacing()[1];
245  double lrx = ux * m_LeftImage->GetLargestPossibleRegion().GetSize()[0] * m_LeftImage->GetSpacing()[0]
246  + uy * m_LeftImage->GetLargestPossibleRegion().GetSize()[1] * m_LeftImage->GetSpacing()[1];
247  double lry = vx * m_LeftImage->GetLargestPossibleRegion().GetSize()[0] * m_LeftImage->GetSpacing()[0]
248  + vy * m_LeftImage->GetLargestPossibleRegion().GetSize()[1] * m_LeftImage->GetSpacing()[1];
249 
250  // Bounding box (this time we do not omit ulx)
251  double minx = std::min(std::min(std::min(urx,llx),lrx),0.);
252  double miny = std::min(std::min(std::min(ury,lly),lry),0.);
253  double maxx = std::max(std::max(std::max(urx,llx),lrx),0.);
254  double maxy = std::max(std::max(std::max(ury,lly),lry),0.);
255 
256  // We can now estimate the output image size, taking into account
257  // the scale parameter
258  m_RectifiedImageSize[0] = static_cast<unsigned int>((maxx-minx)/(mean_spacing*m_Scale));
259  m_RectifiedImageSize[1] = static_cast<unsigned int>((maxy-miny)/(mean_spacing*m_Scale));
260 
261  // Now, we can compute the origin of the epipolar images in the left
262  // input image geometry (we rotate back)
263  m_OutputOriginInLeftImage[0] = leftInputOrigin[0] + (ux * minx + vx * miny);
264  m_OutputOriginInLeftImage[1] = leftInputOrigin[1] + (uy * minx + vy * miny);
265  m_OutputOriginInLeftImage[2] = localElevation;
266 
267  // And also the size of the deformation field
268  SizeType outputSize;
269  outputSize[0] = (m_RectifiedImageSize[0] / m_GridStep + 2 );
270  outputSize[1] = (m_RectifiedImageSize[1] / m_GridStep + 2);
271 
272  // Build the output largest region
273  RegionType outputLargestRegion;
274  outputLargestRegion.SetSize(outputSize);
275 
276  // Update the information
277  leftDFPtr->SetLargestPossibleRegion(outputLargestRegion);
278  rightDFPtr->SetLargestPossibleRegion(outputLargestRegion);
279 
280  leftDFPtr->SetSpacing(outputSpacing);
281  rightDFPtr->SetSpacing(outputSpacing);
282 
283  leftDFPtr->SetNumberOfComponentsPerPixel(2);
284  rightDFPtr->SetNumberOfComponentsPerPixel(2);
285 }
286 
287 template <class TInputImage, class TOutputImage>
288 void
291 {
292  // Call superclass
293  Superclass::EnlargeOutputRequestedRegion(output);
294 
295  // Retrieve the deformation field pointers
296  OutputImageType * leftDFPtr = this->GetLeftDeformationFieldOutput();
297  OutputImageType * rightDFPtr = this->GetRightDeformationFieldOutput();
298 
299  // Prevent from streaming
300  leftDFPtr->SetRequestedRegionToLargestPossibleRegion();
301  rightDFPtr->SetRequestedRegionToLargestPossibleRegion();
302 }
303 
304 template <class TInputImage, class TOutputImage>
305 void
308 {
309  // Allocate the output
310  this->AllocateOutputs();
311 
312  // Setup the DEM handler if needed
313  typename DEMHandler::Pointer demHandler = DEMHandler::Instance();
314 
315  // Set-up a transform to use the DEMHandler
316  typedef otb::GenericRSTransform<> RSTransform2DType;
317  RSTransform2DType::Pointer leftToGroundTransform = RSTransform2DType::New();
318 
319  leftToGroundTransform->SetInputKeywordList(m_LeftImage->GetImageKeywordlist());
320 
321  leftToGroundTransform->InstanciateTransform();
322 
323  // Retrieve the output pointers
324  OutputImageType * leftDFPtr = this->GetLeftDeformationFieldOutput();
325  OutputImageType * rightDFPtr = this->GetRightDeformationFieldOutput();
326 
327  // Declare all the TDPoint variables we will need
328  TDPointType currentPoint1, currentPoint2,nextLineStart1,nextLineStart2, startLine1, endLine1, startLine2, endLine2, epiPoint1, epiPoint2;
329 
330  // Then, we retrieve the origin of the left input image
331  double localElevation = otb::DEMHandler::Instance()->GetDefaultHeightAboveEllipsoid();
332 
333  // Use the mean spacing as before
334  double mean_spacing=0.5*(vcl_abs(m_LeftImage->GetSpacing()[0])+vcl_abs(m_LeftImage->GetSpacing()[1]));
335 
336  // Initialize
337  currentPoint1 = m_OutputOriginInLeftImage;
338  if(m_UseDEM)
339  {
340  RSTransform2DType::InputPointType tmpPoint;
341  tmpPoint[0] = currentPoint1[0];
342  tmpPoint[1] = currentPoint1[1];
343  localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
344  }
345  currentPoint1[2] = localElevation;
346  currentPoint2 = m_LeftToRightTransform->TransformPoint(currentPoint1);
347  currentPoint2[2] = currentPoint1[2];
348 
349  // These are the points were the next stereo-rectified image line starts
350  nextLineStart1 = currentPoint1;
351  nextLineStart2 = currentPoint2;
352 
353  // We define the iterators we will use
355 
356  IteratorType it1(leftDFPtr,leftDFPtr->GetLargestPossibleRegion());
357  IteratorType it2(rightDFPtr,rightDFPtr->GetLargestPossibleRegion());
358 
359  it1.GoToBegin();
360  it2.GoToBegin();
361 
362  // Reset the mean baseline ratio
363  m_MeanBaselineRatio = 0;
364 
365  // Set-up progress reporting
366  itk::ProgressReporter progress(this, 0, leftDFPtr->GetLargestPossibleRegion().GetNumberOfPixels());
367 
368 
369  // We loop on the deformation fields
370  while(!it1.IsAtEnd() && !it2.IsAtEnd())
371  {
372  // 1 - We start by handling the special case where we are at beginning
373  // of a new line
374  if(it1.GetIndex()[0] == 0 || it2.GetIndex()[0] == 0)
375  {
376  // In which case we reset the current points
377  currentPoint1 = nextLineStart1;
378  currentPoint2 = nextLineStart2;
379  }
380 
381  // 2 - Next, we will fill the deformation fields
382  typename OutputImageType::PixelType dFValue1 = it1.Get();
383  typename OutputImageType::PixelType dFValue2 = it2.Get();
384 
385  // We must cast iterators position to physical space
386  PointType currentDFPoint1, currentDFPoint2;
387  leftDFPtr->TransformIndexToPhysicalPoint(it1.GetIndex(), currentDFPoint1);
388  rightDFPtr->TransformIndexToPhysicalPoint(it2.GetIndex(), currentDFPoint2);
389 
390  // Now we can compute the shifts
391  dFValue1[0] = currentPoint1[0] - currentDFPoint1[0];
392  dFValue1[1] = currentPoint1[1] - currentDFPoint1[1];
393  dFValue2[0] = currentPoint2[0] - currentDFPoint2[0];
394  dFValue2[1] = currentPoint2[1] - currentDFPoint2[1];
395 
396  // And set the values
397  it1.Set(dFValue1);
398  it2.Set(dFValue2);
399 
400  // 3 - Next, we will compute epipolar lines direction in both
401  // images
402  double a1;
403 
404  // First, for image 1
405 
406  // This point is the image of the left input image origin at the
407  // average elevation
408  epiPoint2 = m_LeftToRightTransform->TransformPoint(currentPoint1);
409 
410  // The begining of the epipolar line in the left image is the image
411  // of epiPoint2 at a lower elevation (using the offset)
412  epiPoint2[2] = localElevation - m_ElevationOffset;
413  startLine1 = m_RightToLeftTransform->TransformPoint(epiPoint2);
414 
415  // The endning of the epipolar line in the left image is the image
416  // of epiPoint2 at a higher elevation (using the offset)
417  epiPoint2[2] = localElevation + m_ElevationOffset;
418  endLine1 = m_RightToLeftTransform->TransformPoint(epiPoint2);
419 
420  // Estimate the local baseline ratio
421  double localBaselineRatio = vcl_sqrt((endLine1[0] - startLine1[0])
422  * (endLine1[0] - startLine1[0])
423  + (endLine1[1] - startLine1[1])
424  * (endLine1[1] - startLine1[1]))
425  / (2 * m_ElevationOffset);
426 
427  m_MeanBaselineRatio+=localBaselineRatio;
428 
429  // Now, we can compute the equation of the epipolar line y = a*x+b
430  // (compute angle in physical space)
431  double alpha1 = 0;
432  if (endLine1[0] == startLine1[0])
433  {
434  if (endLine1[1] > startLine1[1])
435  {
436  alpha1 = 0.5*otb::CONST_PI;
437  }
438  else
439  {
440  alpha1 = -0.5*otb::CONST_PI;
441  }
442  }
443  else
444  {
445  a1 = (endLine1[1] - startLine1[1]) / (endLine1[0] - startLine1[0]);
446  if (endLine1[0] > startLine1[0])
447  {
448  alpha1 = vcl_atan(a1);
449  }
450  else
451  {
452  alpha1 = otb::CONST_PI + vcl_atan(a1);
453  }
454  }
455 
456  // We do the same for image 2
457  currentPoint2[2] = localElevation;
458  epiPoint1 = m_RightToLeftTransform->TransformPoint(currentPoint2);
459 
460  epiPoint1[2] = localElevation - m_ElevationOffset;
461  startLine2 = m_LeftToRightTransform->TransformPoint(epiPoint1);
462 
463  epiPoint1[2] = localElevation + m_ElevationOffset;
464  endLine2 = m_LeftToRightTransform->TransformPoint(epiPoint1);
465 
466  // 4 - Determine position of next points
467 
468  // We want to move m_Scale pixels away in the epipolar line of the
469  // first image
470  // Take into account height direction
471  //double alpha1 = otb::CONST_PI - vcl_atan(a1);
472  double deltax1 = m_Scale * m_GridStep * mean_spacing * vcl_cos(alpha1);
473  double deltay1 = m_Scale * m_GridStep * mean_spacing * vcl_sin(alpha1);
474 
475  // Now we move currentPoint1
476  currentPoint1[0]+=deltax1;
477  currentPoint1[1]+=deltay1;
478  if(m_UseDEM)
479  {
480  RSTransform2DType::InputPointType tmpPoint;
481  tmpPoint[0] = currentPoint1[0];
482  tmpPoint[1] = currentPoint1[1];
483  localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
484  }
485  currentPoint1[2] = localElevation;
486 
487  // And we compute the equivalent displacement in right image
488  currentPoint2 = m_LeftToRightTransform->TransformPoint(currentPoint1);
489 
490  // 5 - Finally, we have to handle a special case for beginning of
491  // line, since at this position we are able to compute the
492  // position of the beginning of next line
493  if(it1.GetIndex()[0] == 0 || it2.GetIndex()[0] == 0)
494  {
495  // We want to move 1 pixel away in the direction orthogonal to
496  // epipolar line
497  double nextdeltax1 = -m_Scale * mean_spacing * m_GridStep * vcl_sin(alpha1);
498  double nextdeltay1 = m_Scale * mean_spacing * m_GridStep * vcl_cos(alpha1);
499 
500  // We can then update nextLineStart1
501  nextLineStart1[0] = currentPoint1[0] - deltax1 + nextdeltax1;
502  nextLineStart1[1] = currentPoint1[1] - deltay1 + nextdeltay1;
503  nextLineStart1[2] = localElevation;
504  if(m_UseDEM)
505  {
506  RSTransform2DType::InputPointType tmpPoint;
507  tmpPoint[0] = nextLineStart1[0];
508  tmpPoint[1] = nextLineStart1[1];
509  nextLineStart1[2] = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
510  }
511 
512 
513  // By construction, nextLineStart2 is always the image of
514  // nextLineStart1 by the left to right transform at the m_AverageElevation
515  nextLineStart2 = m_LeftToRightTransform->TransformPoint(nextLineStart1);
516  }
517 
518  // Last, we move forward
519  ++it1;
520  ++it2;
521 
522  // Update progress
523  progress.CompletedPixel();
524  }
525 
526  // Compute the mean baseline ratio
527  m_MeanBaselineRatio /= leftDFPtr->GetBufferedRegion().GetNumberOfPixels();
528 }
529 
530 template <class TInputImage, class TOutputImage>
531 void
533 ::PrintSelf( std::ostream& os, itk::Indent indent ) const
534 {
535  // Call superclass implementation
536  Superclass::PrintSelf(os,indent);
537 }
538 
539 } // end namespace otb
540 
541 #endif

Generated at Sun Feb 3 2013 00:48:44 for Orfeo Toolbox with doxygen 1.8.1.1