Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ProjectionParametersSpherical.cpp
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 #include "Geometry/ProjectionParametersSpherical.hh"
27 #include <Base/Common/CompareFloatingPoint.hh>
28 #include <MathAlgo/PolynomialSolve.hh>
29 #include <MathAlgo/Minpack.hh>
30 
31 using namespace BIAS;
32 using namespace std;
33 
35 Distort(BIAS::HomgPoint2D& point2d) const
36 {
37  BIASERR("unfinished");
38  BIASABORT;
39  return false;
40 }
41 
42 
45 {
46  BIASERR("unfinished");
47  BIASABORT;
48  return false;
49 }
50 
51 
52 // void ProjectionParametersSpherical::GetFirstBorderPixel(PixelIterator& it) {
53 // it.x = 0;
54 // it.y = 0;
55 // BIASERR("Please implment me for running on the outer field of view circle");
56 // }
57 
58 
59 // bool ProjectionParametersSpherical::GetNextBorderPixel(PixelIterator& it) {
60 // BIASERR("Please implment me for running on the outer field of view circle");
61 // return ProjectionParametersBase::GetNextBorderPixel(it);
62 // }
63 
64 
67 {
69  return true;
70 
72  dynamic_cast<const ProjectionParametersSpherical*>(p);
73  if(pps == NULL)
74  return true;
75 
76  if(radius_ != pps->radius_)
77  return true;
78  if(interpolatorInit_ != pps->interpolatorInit_)
79  return true;
80  if(interpolatorInit_ &&
81  interpolatorAngle_.DoLuTSplinesDiffer(pps->interpolatorAngle_))
82  return true;
83  if(MaxCamAngle_ != pps->MaxCamAngle_)
84  return true;
85 
86  return false;
87 }
88 
89 
92  HomgPoint2D& x,
93  bool IgnoreDistortion) const
94 {
95  ProjectLocal(localX, x, IgnoreDistortion);
96  double radius =
97  sqrt( (x[0]-principalX_) * (x[0]-principalX_) +
98  (x[1]-principalY_) * (x[1]-principalY_) );
99 
100  return (x[0]>=0.0 && x[1]>=0.0 &&
101  x[0]<=(double)(width_-1) && x[1]<=(double)(height_-1) &&
102  radius<= radius_);
103 }
104 
105 
108  bool IgnoreDistortion) const
109 {
110  Vector3<double> rPhiTheta = point.CoordEuclideanToSphere();
111  if (Equal(rPhiTheta[0], 0.0)) {
112  p2d.SetZero();
113  return 0;
114  }
115  if (!IgnoreDistortion) {
116  TransfCoordSphere2Image_(rPhiTheta[2], rPhiTheta[1], p2d);
117  return 0;
118  }
119  else {
120  TransfCoordSphere2CalibImage_(rPhiTheta[2], rPhiTheta[1], p2d);
121  return 0;
122  }
123  return 1;
124 }
125 
126 
128 UnProjectToImagePlane(const HomgPoint2D& pos, const double& depth,
129  bool IgnoreDistortion) const
130 {
131  Vector3<double> d, p;
132  UnProjectLocal(pos, p, d, IgnoreDistortion);
133  double radius = sqrt(d[2]*d[2] + d[1]*d[1] + d[0]*d[0]);
134  if(Equal(radius, 0.0))
135  return HomgPoint3D(0,0,0,0);
136  d*= depth/radius;
137  if(Equal(d.NormL2(), 0.0))
138  return HomgPoint3D(0,0,0,0);
139  HomgPoint3D local(d);
140  local[3] = 1.0;
141  return Pose_.LocalToGlobal(local);
142 }
143 
144 
145 
148  Vector3<double>& direction, bool IgnoreDistortion) const
149 {
150  //radius = 1;
151  //cout<<"[ProjectionParametersSpherical] UnProjectLocal "<<pos<<endl;
152  Vector3<double> rPhiTheta(1,0,0);
153  if (!IgnoreDistortion) {
154  TransfCoordImage2Sphere_(pos, rPhiTheta[2], rPhiTheta[1]);
155  }
156  else {
157  TransfCoordCalibImage2Sphere_(pos, rPhiTheta[2], rPhiTheta[1]);
158  }
159  direction = rPhiTheta.CoordSphereToEuclidean();
160  origin = Vector3<double>(0.0);
161 }
162 
165  const double& depth,
166  bool IgnoreDistortion) const
167 {
168  Vector3<double> x, p;
169  UnProjectLocal(pos, p, x, IgnoreDistortion);
170  x *= depth;
171  return x;
172 }
173 
174 
176 SetUndistortion(const std::vector<double>& undistAngles,
177  const std::vector<double>& distAngles,
178  const double radius)
179 {
180  radius_ = radius;
181 
182  if (undistAngles.size() < 2 || distAngles.size() < 2 ||
183  undistAngles.size() != distAngles.size()) {
184  BIASERR("Initialization failed, expected min. 2 and same number of "
185  "undistorted and distorted angles (given " << undistAngles.size()
186  << " and " << distAngles.size() << " angles)!");
187  return -1;
188  }
189 
190  // set interpolator from undistorted to distorted angles
191  double dummy = 0.0;
192  interpolatorAngle_.SetKnotPoints(undistAngles);
193  interpolatorAngle_.SetControlPoints(distAngles);
194  interpolatorAngle_.InitSpline();
195  interpolatorAngle_.Spline(dummy, undistAngles[0], 4);
196  MaxCamAngle_ = undistAngles[undistAngles.size()-1];
197  int lutsamples = int(radius_*100);
198  interpolatorAngle_.PrepareLuTSpline(undistAngles[0], MaxCamAngle_,
199  lutsamples, 4);
200 
201  // set interpolator from distorted to undistorted angles
202  interpolatorAngleBack_.SetKnotPoints(distAngles);
203  interpolatorAngleBack_.SetControlPoints(undistAngles);
204  interpolatorAngleBack_.InitSpline();
205  interpolatorAngleBack_.Spline(dummy, distAngles[0], 4);
206  interpolatorAngleBack_.PrepareLuTSpline(distAngles[0],
207  distAngles[distAngles.size()-1],
208  lutsamples, 4);
209  interpolatorInit_ = true;
210 
211  return 0;
212 }
213 
214 
216 SetMaxCamAngle(const double maxCamAngle)
217 {
218  if (interpolatorInit_) {
219  BIASWARN("Recalculating undistortion polynomial since max. camera "
220  "angles has been reset manually!");
221  RecalculateUndistortion(maxCamAngle);
222  } else {
223  MaxCamAngle_ = maxCamAngle;
224  }
225 }
226 
227 
229 RecalculateUndistortion(const double maxCamAngle)
230 {
231  if (!interpolatorInit_) {
232  BEXCEPTION("Undistortion polynomial has not been set previously! "
233  "Call SetUndistortion instead of RecalculateUndistortion!");
234  }
235  if (maxCamAngle > MaxCamAngle_) {
236  BIASWARN("Extrapolating undistortion polynomial linearly since new "
237  "max. cam angle > old max. cam angle. Might result in strange "
238  "behaviour!");
239  }
240  std::vector<double> dummy;
241  interpolatorAngle_.GetKnotPoints(dummy);
242  const unsigned int numSamples = dummy.size();
243  std::vector<double> undistAngles(numSamples);
244  std::vector<double> distAngles(numSamples);
245  double distMaxCamAngle = MaxCamAngle_; // current distorted max. camera angle
246  interpolatorAngle_.Spline(distMaxCamAngle, MaxCamAngle_, 4);
247  const double linearExtrapolationFactor = distMaxCamAngle / MaxCamAngle_;
248  double theta = 0; // undistorted angle samples
249  const double thetaStep = maxCamAngle / (numSamples - 1);
250  for (unsigned int i = 0; i < numSamples; i++, theta += thetaStep) {
251  undistAngles[i] = theta;
252  // compute distorted angle sample
253  if (theta < MaxCamAngle_) {
254  interpolatorAngle_.Spline(distAngles[i], theta, 4);
255  } else {
256  // extrapolate angle linearly
257  distAngles[i] = theta * linearExtrapolationFactor;
258  }
259  }
260  // recalculate polynomial interpolation and set new max. camera angle
261  SetUndistortion(undistAngles, distAngles, radius_);
262 }
263 
264 
266 InitAngleCorrFromPoly(const double maxCamAngle,
267  const std::vector<double>& coefficients,
268  const bool mapsRadiusToZ)
269 {
270  double pix_def, ang_def, ang_opt;
271 
272  // calculate distortion function
273  std::vector<double> radiusInPixel, thetaInRad;
274  double maxradius = 0;
275  MaxCamAngle_ = maxCamAngle;
276 
277  acCoeff_ = coefficients;
278  std::vector<double> distAngles;
279  std::vector<double> undistAngles;
280  unsigned int width, height;
281  GetImageSize(width, height);
282  PolynomialSolve PS; // helper object to evaluate polynomial
283 
284  unsigned int rad = radius_ != 0.0 ? (unsigned int)rint(radius_) :
285  (unsigned int)rint(double(width)/2.0);
286 
287  // Compute samples for distorted and corresponding undistorted
288  // azimuth angles to interpolate undistortion polynomial from
289  if (mapsRadiusToZ)
290  {
291  // run radially from distortion center to sphere border
292  for (unsigned int currentRadius = 0; currentRadius <= rad; currentRadius++)
293  {
294  // pick radius
295  radiusInPixel.push_back(currentRadius);
296  // compute z coordinate such that (currentRadius, 0, -z) is ray in
297  // the camera coordinate system (this is the Scaramuzza polynomial)
298  double z = PS.EvaluatePolynomial(currentRadius, acCoeff_);
299  // compute theta for the radius
300  double currentTheta = atan2(currentRadius, -z);
301  thetaInRad.push_back(currentTheta);
302  // check if we have to increase the radius
303  if (currentRadius > maxradius && currentTheta < MaxCamAngle_)
304  maxradius = currentRadius;
305  }
306  } else {
307  // run radially from distortion center to sphere border
308  for (unsigned int sampleIndex = 0; sampleIndex <= rad; sampleIndex++)
309  {
310  // Sample angle equidistantly in width/2 steps.
311  // This is only to have a good number of sampling points, it does NOT
312  // assume the circle radius to be in the range width/2.
313  // Nevertheless sample the range of the given radius.
314  double currentTheta = MaxCamAngle_ * double(sampleIndex) / double(rad);
315 
316  // pick radius
317  radiusInPixel.push_back(PS.EvaluatePolynomial(currentTheta, acCoeff_));
318  thetaInRad.push_back(currentTheta);
319  // check if we have to increase the radius
320  if (radiusInPixel.back() > maxradius)
321  maxradius = radiusInPixel.back();
322  }
323  }
324 
325  // set radius of outer image rim
326  radius_ = maxradius;
327 
328  // number of steps for azimuth angle sampling
329  const int steps = 14;
330 
331  // numerically deriving distortion function in 0
332  double thetaPerPixelCenter = thetaInRad[1] - thetaInRad[0];
333  Interpolator interpolatorThetaToPixels;
334  interpolatorThetaToPixels.SetKnotPoints(thetaInRad);
335  interpolatorThetaToPixels.SetControlPoints(radiusInPixel);
336  interpolatorThetaToPixels.InitSpline();
337 
338  //MaxCamAngle_ = thetaInRad.back();
339  for (double sampleTheta = 0; sampleTheta - 1e-10 <= MaxCamAngle_;
340  sampleTheta += MaxCamAngle_/steps)
341  {
342  ang_opt = sampleTheta;
343 
344  interpolatorThetaToPixels.Spline(pix_def, ang_opt);
345 
346  ang_def = pix_def * thetaPerPixelCenter;
347  distAngles.push_back(ang_opt);
348  undistAngles.push_back(ang_def);
349  }
350 
351  return SetUndistortion(distAngles, undistAngles, radius_);
352 }
353 
354 
357  double &theta, double &phi) const
358 {
359  HomgPoint2D SourceH = Source;
360  SourceH.Homogenize();
361 
362  double rSrc = sqrt((SourceH[0]-principalX_)*(SourceH[0]-principalX_)+
363  (SourceH[1]-principalY_)*(SourceH[1]-principalY_));
364  phi = atan2(SourceH[1]-principalY_,SourceH[0]-principalX_);
365 
366  if (interpolatorInit_) {
367  double CalMaxCamAngle = 0;
368  // calculate calibrated maximum angle
369  const_cast<BIAS::Interpolator*>
370  (&interpolatorAngle_)->Spline(CalMaxCamAngle, MaxCamAngle_, 4);
371  //interpolatorAngle_.SplineFromLuT(CalMaxCamAngle, MaxCamAngle_);
372 
373  // calculate calibrated maximum radius
374  double RadiusCalMax = MaxCamAngle_ / CalMaxCamAngle * radius_;
375  // calculate calibrated theta and inverse
376  double CalTheta = rSrc /RadiusCalMax * MaxCamAngle_;
377  const_cast<BIAS::Interpolator*>
378  (&interpolatorAngleBack_)->Spline(theta, CalTheta, 4);
379  //interpolatorAngleBack_.SplineFromLuT(theta, CalTheta);
380  } else {
381  theta = rSrc / radius_ * MaxCamAngle_;
382  }
383 }
384 
385 
387 TransfCoordRotateSphere2Image_(double theta, double phi,
388  double viewCenterX,
389  double viewCenterY,
390  HomgPoint2D &Source) const
391 {
392  double viewTheta, viewPhi;
393  HomgPoint2D viewCenter(viewCenterX,viewCenterY,1);
394  TransfCoordImage2Sphere_(viewCenter, viewTheta, viewPhi);
395  // avoid z-rotation by changing the rotation axes
396  Vector3<double> vecPoint(1.0,phi,theta);
397  Vector3<double> vecView(1.0,viewPhi,viewTheta);
398  //Vector3<double> vecCenter(0,0,1);
399  Vector3<double> vecAxis(cos(viewPhi+M_PI/2),sin(viewPhi+M_PI/2),0);
400  Vector3<double> vecRot(0,0,0);
401  vecPoint = vecPoint.CoordSphereToEuclidean();
402  vecView = vecView.CoordSphereToEuclidean();
403  /*
404  // With 1 rotation and moving axis
405  RMatrix rMat;
406  rMat.Set(vecAxis, viewTheta);
407  double x,y,z;
408  rMat.GetRotationAnglesZYX(x,y,z);
409  vecRot = rMat*vecPoint;
410  //cout << x << "," << y << "," << z << endl;
411  */
412 
413  //With Quaternions
414  Quaternion<double> rot;
415  rot.SetValueAsAxisRad (vecAxis, viewTheta);
416  rot.MultVec(vecPoint, vecRot);
417 
418  /*
419  //With 2 rotations alpha, beta (wrong rotation!!)
420  // viewAlpha: angle between x-z-Plane and vecView
421  double viewAlpha = -1 * atan2(vecView[1],vecView[2]);
422  // viewBeta: angle between y-z-Plane and vecView
423  double viewBeta = atan2(vecView[0],vecView[2]);
424  RMatrix rMat;
425  rMat.SetZYX(viewAlpha,viewBeta,0);
426  vecRot = rMat*vecPoint;
427  */
428 
429  vecRot = vecRot.CoordEuclideanToSphere();
430  phi = vecRot[1];
431  theta = vecRot[2];
432 
433  if (theta > MaxCamAngle_) return -1;
434  if (phi>M_PI) phi-=2*M_PI;
435 
436  double rSrc = 0;
437  if (interpolatorInit_) {
438  double CalMaxCamAngle=0;
439  double CalTheta=0;
440  // calculate calibrated angles
441  interpolatorAngle_.SplineFromLuT(CalMaxCamAngle, MaxCamAngle_);
442  interpolatorAngle_.SplineFromLuT(CalTheta, theta);
443  // apply calculate calibrated radius from calibrated angles
444  rSrc = radius_ * CalTheta / CalMaxCamAngle;
445  } else {
446  rSrc = radius_ * theta / MaxCamAngle_;
447  }
448 
449  Source[0] = rSrc * cos(phi) + principalX_;
450  Source[1] = rSrc * sin(phi) + principalY_;
451  Source[2] = 1;
452 
453  return 0;
454 }
455 
456 
458 TransfCoordSphere2Image_(double theta, double phi,
459  HomgPoint2D &Source) const
460 {
461  EnforceNormalRange_(theta, phi);
462 
463  double rSrc = 0;
464  if (interpolatorInit_) {
465  double CalMaxCamAngle=0;
466  double CalTheta=0;
467  // calculate calibrated angles
468  interpolatorAngle_.SplineFromLuT(CalMaxCamAngle, MaxCamAngle_);
469  interpolatorAngle_.SplineFromLuT(CalTheta, theta);
470  // apply calculate calibrated radius from calibrated angles
471  rSrc = radius_ * CalTheta / CalMaxCamAngle;
472  } else {
473  rSrc = radius_ * theta / MaxCamAngle_;
474  }
475 
476  Source[0] = rSrc * cos(phi) + principalX_;
477  Source[1] = rSrc * sin(phi) + principalY_;
478  Source[2] = 1;
479 }
480 
481 
483 TransfCoordSphere2CalibImage_(double theta, double phi,
484  HomgPoint2D &Source) const
485 {
486  EnforceNormalRange_(theta, phi);
487 
488  // apply radius from calibrated angles
489  double rSrc = radius_ * theta / MaxCamAngle_;
490 
491  Source[0] = rSrc * cos(phi) + principalX_;
492  Source[1] = rSrc * sin(phi) + principalY_;
493  Source[2] = 1;
494 }
495 
496 
499  double &theta, double &phi) const
500 {
501  HomgPoint2D SourceH = Source;
502  SourceH.Homogenize();
503 
504  double rSrc = sqrt((SourceH[0]-principalX_)*(SourceH[0]-principalX_)+
505  (SourceH[1]-principalY_)*(SourceH[1]-principalY_));
506  phi = atan2(SourceH[1]-principalY_,SourceH[0]-principalX_);
507  theta = rSrc / radius_ * MaxCamAngle_;
508 }
509 
510 
512 EnforceNormalRange_(double& theta, double& phi) const
513 {
514  // theta and phi are periodic in 2*M_PI, if they are out of a suitable
515  // range, try to find best equivalent angle by adding 2*l*M_PI
516 
517  // make sure phi and theta dont contain illegal numbers
518  INFNANCHECK(theta);
519  INFNANCHECK(phi);
520 
521  // the following range adaption performs only if theta and phi are close to [0;2pi[
522  // what they ALWAYS should be, check it just for safety:
523  BIASASSERT(fabs(theta)<20.0);
524  BIASASSERT(fabs(phi)<20.0);
525 
526  // bring theta to range [0;2pi[
527  while (theta >= M_PI*2.0) theta -= M_PI*2.0;
528  while (theta < 0.0) theta += M_PI*2.0;
529 
530  // bring phi to range [-pi;pi[
531  while (phi >= M_PI) phi -= M_PI*2.0;
532  while (phi < -M_PI) phi += M_PI*2.0;
533 }
534 
535 
538 {
539  const ProjectionParametersSpherical *pPPS =
540  dynamic_cast<const ProjectionParametersSpherical* >(pPPB);
541  // different projection model has a very different view
542  if (!pPPS) {
543  BIASERR("View difference for different projection models requested !");
544  return DBL_MAX;
545  }
546 
547  Vector3<double> C0 = pPPS->GetC();
548  // Optical axes of the 2 cameras
549  Vector3<double> A0, A;
550 
551  RMatrix R = GetR(), R0 = pPPS->GetR();
552 
553  A[0] = R[0][2]; A[1] = R[1][2]; A[2] = R[2][2];
554  A.Normalize();
555 
556  A0[0] = R0[0][2]; A0[1] = R0[1][2]; A0[2] = R0[2][2];
557  A0.Normalize();
558 
559  // compute field of view, assuming const K !
560  double coshalffieldofview = cos(MaxCamAngle_);
561 
562  double newness = (GetC()-C0).NormL2();
563  newness += 1.0/(1.0-coshalffieldofview)*(1.0-fabs(A.ScalarProduct(A0)));
564  return newness;
565 }
566 
567 
570  const double perspHalfViewingAngleDEG,
572 {
573  double halffov = perspHalfViewingAngleDEG * M_PI/180.0;
574 
575  // calculate rotation quaternion
576  double viewTheta, viewPhi;
577  TransfCoordImage2Sphere_(viewCenter, viewTheta, viewPhi);
578  Vector3<double> vecAxis(cos(viewPhi+M_PI/2), sin(viewPhi+M_PI/2), 0);
579  Quaternion<double> rot;
580  rot.SetValueAsAxisRad (vecAxis, viewTheta);
581 
582  // measure 1 deg angle in pixel at viewCenter
583  vector<Vector3<double> > vecRPT;//unit sphere points spanning 1Deg theta area
584  vector<HomgPoint2D> vecSpProj; //projected into image
585  vecRPT.push_back(Vector3<double>(1.0, -M_PI, 0.5/180.0*M_PI));
586  vecRPT.push_back(Vector3<double>(1.0, 0, 0.5/180.0*M_PI));
587  vecRPT.push_back(Vector3<double>(1.0, -M_PI/2, 0.5/180.0*M_PI));
588  vecRPT.push_back(Vector3<double>(1.0, M_PI/2, 0.5/180.0*M_PI));
589  for (unsigned int i=0; i<vecRPT.size(); i++) {
590  Vector3<double>vecRot;
591  rot.MultVec(vecRPT[i].CoordSphereToEuclidean(), vecRot);
592  vecSpProj.push_back(ProjectLocal(vecRot));
593  }
594  double MaxPixDiff=0;
595  for (unsigned int i=0; i<vecSpProj.size(); i+=2) {
596  if ((vecSpProj[i]-vecSpProj[i+1]).NormL2()>MaxPixDiff) {
597  MaxPixDiff = (vecSpProj[i]-vecSpProj[i+1]).NormL2();
598  }
599  }
600  int imgsize = int(MaxPixDiff*tan(halffov)/tan(M_PI/180.0));
601  double focallength = imgsize/tan(halffov);
602 
603  // set ProjectionParametersPerspective
604  pp.SetQC(GetPose().GetQ()*rot, GetC());
605  pp.SetCov(GetCov());
606  pp.SetImageSize(imgsize*2,imgsize*2);
607  pp.SetFocalLengthAndAspect(focallength, 1);
608  pp.SetPrincipal(imgsize,imgsize);
609  pp.SetUndistortion(0,0,0,0);
610 
611  return true;
612 }
613 
614 
616 GetFakeKMatrix(double& imgsize, int resolution, const double& maxangle) const
617 {
618  imgsize = 0;
619  // try user specified maxangle
620  double ang = maxangle;
621  // check if camera supports such large angles, otherwise clip
622  if (ang > GetMaxCamAngle()) ang = GetMaxCamAngle() - 0.01;
623  return ProjectionParametersBase::GetFakeKMatrix(imgsize, resolution, ang);
624 }
625 
626 
627 #ifdef BIAS_HAVE_XML2
628 
630  double& Version) const
631 {
632  TopLevelTag = "ProjectionParametersSpherical";
633  Version = 0.1;
634  return 0;
635 }
636 
637 
638 int ProjectionParametersSpherical::XMLOut(const xmlNodePtr Node,
639  XMLIO& XMLObject) const
640 {
641  xmlNodePtr theNode;
642  string TopLevelName;
643  double Version;
644  ProjectionParametersBase::XMLGetClassName(TopLevelName, Version);
645  theNode = XMLObject.addChildNode(Node, TopLevelName);
646  XMLObject.addAttribute(theNode, "Version", Version);
647  ProjectionParametersBase::XMLOut(theNode, XMLObject);
648 
649  xmlNodePtr childNode, childNode2;
650  XMLObject.addAttribute(Node,"Radius", radius_);
651  XMLObject.addAttribute(Node,"MaxAngle", MaxCamAngle_);
652  if (interpolatorInit_) {
653  childNode = XMLObject.addChildNode(Node,"Distortion");
654  vector<double> controlpoints, knotpoints;
655  interpolatorAngle_.GetControlPoints(controlpoints);
656  interpolatorAngle_.GetKnotPoints(knotpoints);
657  for (unsigned int i=0; i<controlpoints.size(); i++) {
658  childNode2= XMLObject.addChildNode(childNode,"MeasPoint");
659  XMLObject.addAttribute(childNode2, "AngRay", knotpoints[i]);
660  XMLObject.addAttribute(childNode2, "AngDeformed", controlpoints[i]);
661  }
662  }
663  return 0;
664 }
665 
666 
667 int ProjectionParametersSpherical::XMLIn(const xmlNodePtr Node,
668  XMLIO& XMLObject)
669 {
670  string TopLevelName;
671  double Version;
672  xmlNodePtr childNode;
673 
674  ProjectionParametersBase::XMLGetClassName(TopLevelName, Version);
675  if ((childNode = XMLObject.getChild(Node, TopLevelName))==NULL) {
676  BIASERR("Error in xml, no tag" << TopLevelName);
677  return -1;
678  }
679  if (ProjectionParametersBase::XMLIn(childNode, XMLObject)!=0) return -1;
680 
681  radius_ = XMLObject.getAttributeValueDouble(Node, "Radius");
682  xmlAttrPtr maxAngleAttr = XMLObject.getAttributeByName(Node, "MaxAngle");
683  if (maxAngleAttr)
684  MaxCamAngle_ = XMLObject.getAttributeValueDouble(maxAngleAttr);
685 
686  // Read distortion, if given, either as control points from spline interpolation
687  // or as the values of the polynom
688  childNode = XMLObject.getChild(Node, "Distortion");
689  if (childNode)
690  {
691  xmlNodePtr childNode2 = XMLObject.getFirstChild(childNode);
692  string nodeName2 = (childNode2 ? XMLObject.getNodeName(childNode2) : "");
693  if (nodeName2.compare("MeasPoint") == 0)
694  {
695  // here we have the control points representation
696  vector<double> AngleCorrX, AngleCorrY;
697  while (childNode2) {
698  nodeName2 = XMLObject.getNodeName(childNode2);
699  if (nodeName2.compare("MeasPoint") == 0) {
700  double x = XMLObject.getAttributeValueDouble(childNode2, "AngRay");
701  double y = XMLObject.getAttributeValueDouble(childNode2, "AngDeformed");
702  AngleCorrX.push_back(x);
703  AngleCorrY.push_back(y);
704  }
705  childNode2 = XMLObject.getNextChild(childNode2);
706  }
707  SetUndistortion(AngleCorrX, AngleCorrY, radius_);
708  }
709  else
710  {
711  // this is the spline interpolation representation
712  childNode2 = XMLObject.getChild(childNode, "AngleCalibPoly");
713  if (childNode2) {
714  MaxCamAngle_ = XMLObject.getAttributeValueDouble(childNode2, "MaxCamAngle");
715  vector<double> coefficients;
716  coefficients.push_back(XMLObject.getAttributeValueDouble(childNode2, "C0"));
717  coefficients.push_back(XMLObject.getAttributeValueDouble(childNode2, "C1"));
718  coefficients.push_back(XMLObject.getAttributeValueDouble(childNode2, "C2"));
719  coefficients.push_back(XMLObject.getAttributeValueDouble(childNode2, "C3"));
720  coefficients.push_back(XMLObject.getAttributeValueDouble(childNode2, "C4"));
721  InitAngleCorrFromPoly(MaxCamAngle_, coefficients);
722  } else {
723  BIASERR("No polynomial distortion parameters given (either XML nodes MeasPoint or "
724  "XML node AngleCalibPoly as child of XML node Distortion)!");
725  }
726  }
727  } else {
728  // No distortion is given, hence max. angle must be set as attribute!
729  interpolatorInit_ = false;
730  if (!maxAngleAttr) {
731  BIASERR("Max. camera angle is not set (either use XML attribut MaxAngle or set "
732  "distortion parameters)!");
733  }
734  }
735  return 0;
736 }
737 #endif
738 
739 
740 // LM error function
741 int PolynomialWithoutLinearMonomialError(void *p,int m, int n,const double *x,
742  double *fvec, int iflag)
743 {
744  vector<double> params;
746 
747  params.push_back(x[0]);
748  params.push_back(0);
749  for (int i=1; i<n; i++) {
750  params.push_back(x[i]);
751  }
752  PolynomialSolve PS;
753  BIASASSERT(int(pps->myvaluesPolynomialWithoutLinearMonomialError.size())==m);
754  BIASASSERT(int(pps->mylocationsPolynomialWithoutLinearMonomialError.size())==m);
755  for (int j=0; j<m; j++) {
758  params);
759  }
760  return 0;
761 }
762 
763 // helper function for EstimateUndistortionPolynomial
765 FitPolynomialWithoutLinearMonomial_(const unsigned int degree,
766  const std::vector<double>& x,
767  const std::vector<double>& y,
768  std::vector<double> &coefficients)
769 {
770  // See PolynomialSolve::FitPolynomial, but without linear component
771  // (see scaramuzza paper)
772  BIASASSERT(x.size()==y.size());
773  coefficients.resize(degree+1, 0.0);
774  Matrix<double> A(x.size(), degree+1);
775  Vector<double> B(y.size()), X(degree);
776  for (unsigned int row=0; row<x.size(); row++) {
777  const double & xc = x[row];
778  for (unsigned int col=0; col<degree+1; col++) {
779  if (col==1) col=2;
780  A[row][col] = 1;
781  for (unsigned int colpower=0; colpower<col; colpower++)
782  A[row][col] *= xc;
783  }
784  B[row] = y[row];
785  }
786  Matrix<double> A2(x.size(), degree);
787  for (unsigned int row=0; row<x.size(); row++) {
788  A2[row][0] = A[row][0];
789  for (unsigned int col=2; col<degree+1; col++) {
790  A2[row][col-1] = A[row][col];
791  }
792  }
793 
794  SVD mySVD;
795  int result = mySVD.Solve(A2, B, X);
796 
797 
798  Vector<double> coeff(degree), resultcoeff(degree);
799  coeff[0] = coefficients[0] = X[0];
800  coefficients[1] = 0;
801  for (unsigned int col=2; col<degree+1; col++) {
802  coeff[col-1] = coefficients[col] = X[col-1];
803  }
804 
805  //these are now class members
806  myvaluesPolynomialWithoutLinearMonomialError = y;
807  mylocationsPolynomialWithoutLinearMonomialError = x;
808 
809  int res;
810  if ((res = LevenbergMarquardt(&PolynomialWithoutLinearMonomialError, NULL,
811  x.size(),degree,
812  coeff,resultcoeff,
813  MINPACK_DEF_TOLERANCE))!=0) {
814  BIASERR("Error in Levenberg-Marquardt-Optimization of F: "<<res);
815  //return -1;
816  }
817 
818  coefficients[0] = resultcoeff[0];
819  coefficients[1] = 0;
820  for (unsigned int col=2; col<degree+1; col++) {
821  coefficients[col] = resultcoeff[col-1];
822  }
823 
824  return result;
825 }
826 
828 EstimateUndistortionPolynomial(std::vector<double>& coefficients,
829  const unsigned int degree,
830  bool LinearMonomialIsZero)
831 {
832  vector<double> a,b;
833  a.reserve((unsigned int)(radius_+1)); b.reserve((unsigned int)(radius_+1));
834  // run from principal point to fov circle
835  for (unsigned int i=1; i<radius_; i++) {
836  // compute 2D point in image
837  HomgPoint2D pos(principalX_+double(i),principalY_,1);
838  Vector3<double> rPhiTheta(1,0,0);
839  TransfCoordImage2Sphere_(pos, rPhiTheta[2], rPhiTheta[1]);
840  // compute ray
841  Vector3<double> ret = rPhiTheta.CoordSphereToEuclidean();
842  //scale the first two components to have the length of the radius,
843  //than I will have the appropriate length of the z-component and can
844  //make the assumption that the optical ray at this radius and
845  //ret are parallel but have different length. Why the negative sign?
846  //probably the combination of spherical to euclidean transformations.
847  ret *= -1.0 * double(i) / sqrt((ret[0]*ret[0] + ret[1]*ret[1]));
848  // save radius
849  a.push_back(double(i));
850  // versus z-coordinate
851  b.push_back(ret[2]);
852  }
853  if (LinearMonomialIsZero) {
854  return FitPolynomialWithoutLinearMonomial_(degree, a, b, coefficients);
855  } else {
856  PolynomialSolve PS;
857  return PS.FitPolynomial(degree, a, b, coefficients);
858  }
859 }
860 
862 EstimateDistortionPolynomial(std::vector<double>& coefficients,
863  const unsigned int degree,
864  bool LinearMonomialIsZero)
865 {
866  vector<double> a,b;
867  a.reserve((unsigned int)(radius_+1));
868  b.reserve((unsigned int)(radius_+1));
869 
870  double CalMaxCamAngle=0;
871  interpolatorAngle_.SplineFromLuT(CalMaxCamAngle, MaxCamAngle_);
872  // run from principal point to fov circle
873  a.push_back(0);
874  b.push_back(0);
875  for (unsigned int i=1; i<radius_; i++) {
876  // sample angle equidistantly in radius_ steps
877  double theta = MaxCamAngle_ * double(i)/double(radius_);
878  double CalTheta=0;
879  // calculate calibrated angles
880  interpolatorAngle_.SplineFromLuT(CalTheta, theta);
881  // apply calculate calibrated radius from calibrated angles
882  double rSrc = radius_ * CalTheta/CalMaxCamAngle;
883  // save theta
884  a.push_back(theta);
885  // versus radius
886  b.push_back(rSrc);
887  }
888  if (LinearMonomialIsZero) {
889  return FitPolynomialWithoutLinearMonomial_(degree, a, b, coefficients);
890  } else {
891  PolynomialSolve PS;
892  return PS.FitPolynomial(degree, a, b, coefficients);
893  }
894 }
895 
897 ProjectOutsidePositions_(int& posX, int& posY)
898 {
899  if(posX<0) posX=0;
900  if(posY<0) posY=0;
901  if(posX>static_cast<int>(width_-1)) posX=width_-1;
902  if(posY>static_cast<int>(height_-1)) posY=height_-1;
903 }
904 
906  //as no double bresenham circle implementation exists yet, we reduce the radius
907  int rad = (int)rint(floor(radius_));
908  int center[2] = {(int)rint(principalX_), (int)rint(principalY_)};
909  fovCircle_.Init(center,rad);
910  int next[2];
911  fovCircle_.GetNext(next);
912  ProjectOutsidePositions_(next[0], next[1]);
913  it.x = next[0];
914  it.y = next[1];
915 
916 }
917 
919  int next[2];
920  bool res = fovCircle_.GetNext(next);
921  ProjectOutsidePositions_(next[0], next[1]);
922  it.x = next[0];
923  it.y = next[1];
924  return res;
925 }
926 
927 // void ProjectionParametersSpherical::GetFirstEdgePosition(PixelIterator& it) {
928 // it.x = -0.5;
929 // it.y = -0.5;
930 // }
931 
932 // bool ProjectionParametersShperical::GetNextEdgePosition(PixelIterator& it) {
933 // if (it.x==-0.5) {
934 // if (it.y==-0.5) {
935 // // upper left image pixel, move right
936 // it.x++;
937 // return true;
938 // }
939 // // on way back up at left image border
940 // it.y--;
941 // // return false if finished
942 // return (it.y>-0.5);
943 // } else if (it.x<width_-0.5) {
944 // if (it.y==-0.5) {
945 // // moving left on upper image boundary
946 // it.x++;
947 // return true;
948 // }
949 // // moving right on lower image boundary
950 // it.x--;
951 // return true;
952 // } else {
953 // if (it.y<height_-0.5) {
954 // // moving down on right image side
955 // it.y++;return true;
956 // }
957 // // lower right corner
958 // it.x--;
959 // return true;
960 // }
961 // }
virtual BIAS::KMatrix GetFakeKMatrix(double &imgsize, int resolution=0, const double &maxangle=1.4) const
Returns a fake KMatrix for the camera.
virtual BIAS::Vector3< double > GetC() const
Get projection center.
void addAttribute(const xmlNodePtr Node, const std::string &AttributeName, bool AttributeValue)
Add an attribute to a node.
Definition: XMLIO.cpp:156
virtual void SetCov(const Matrix< POSE_TYPE > &Cov)
Set pose covariance matrix with respect to C, Q.
void InitSpline()
call this for restart at t= first knot point initiates recalculation of all polynom coefficients at f...
xmlNodePtr getNextChild()
Get the next child of the parent specified in the last getFirstChild() call, the class remembers the ...
Definition: XMLIO.cpp:466
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
virtual bool GetNextBorderPixel(PixelIterator &it)
call this iteratively to run at the outer boundary of an image.
HomgPoint3D UnProjectToImagePlane(const HomgPoint2D &pos, const double &depth=1.0, bool IgnoreDistortion=false) const
map points from image onto unit diameter image plane in 3D.
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
BIAS::Vector3< T > CoordEuclideanToSphere() const
coordinate transform.
Definition: Vector3.cpp:113
Vector< double > Solve(const Vector< double > &y) const
Definition: SVD.cpp:135
virtual int XMLOut(const xmlNodePtr Node, XMLIO &XMLObject) const
Specialization of XML write function.
camera parameters which define the mapping between rays in the camera coordinate system and pixels in...
virtual void SetPrincipal(const double x, const double y)
Set principal point in pixels relative to top left corner, virtual overload to recalculate K_...
xmlNodePtr getChild(const xmlNodePtr ParentNode, const std::string &ChildName)
Get a child of a Parent node by specifying the childs name, NULL is returned if the ParentNode has no...
Definition: XMLIO.cpp:489
int FitPolynomial(const unsigned int degree, const std::vector< double > &x, const std::vector< double > &y, std::vector< double > &coefficients)
given locations x and measurements y=f(x) approximate f by a polynomial of arbitrary degree and retur...
void ScalarProduct(const Vector3< T > &argvec, T &result) const
scalar product (=inner product) of two vectors, storing the result in result
Definition: Vector3.hh:603
virtual void SetImageSize(const unsigned int w, const unsigned int h)
Set image dimensions (in pixels).
std::vector< double > myvaluesPolynomialWithoutLinearMonomialError
bool DoesPointProjectIntoImageLocal(const BIAS::Vector3< double > &localX, HomgPoint2D &x, bool IgnoreDistortion=false) const
Checks if 3D point projects into specified image and returns belonging 2D image point.
HomgPoint2D & Homogenize()
homogenize class data member elements to W==1 by divison by W
Definition: HomgPoint2D.hh:215
virtual const bool DoIntrinsicsDiffer(const ProjectionParametersBase *p) const
void SetZero()
set all values to 0
Definition: Vector3.hh:559
void SetKnotPoints(const std::vector< double > &kPnt)
set the additional knot points, if you want nonuniform interpolation.
virtual BIAS::KMatrix GetFakeKMatrix(double &imgsize, int resolution=0, const double &maxangle=1.4) const
Returns a fake KMatrix for the fisheye camera.
virtual void GetFirstBorderPixel(PixelIterator &it)
call this to start a run at the outer boundary of an image.
int EstimateUndistortionPolynomial(std::vector< double > &coefficients, const unsigned int degree=4, bool LinearMonomialIsZero=true)
Estimates undistortion polynomial mapping z coordinates to radius according to Scaramuzza&#39;s Matlab to...
std::vector< double > mylocationsPolynomialWithoutLinearMonomialError
int MultVec(const Vector3< QUAT_TYPE > &vec, Vector3< QUAT_TYPE > &res) const
rotates the given Vector qith the quaternion ( q v q* ) the resulting vector is given in res ...
Definition: Quaternion.hh:136
base class for solving polynomial equations
POLYNOMIALSOLVE_TYPE EvaluatePolynomial(const POLYNOMIALSOLVE_TYPE x, const std::vector< POLYNOMIALSOLVE_TYPE > &coeff) const
numerically robust way to evaluate a polynomial at position x, uses Horner scheme (same principle as ...
virtual int XMLIn(const xmlNodePtr Node, XMLIO &XMLObject)
Specialization of XML read function.
virtual HomgPoint2D ProjectLocal(const Vector3< double > &point, bool IgnoreDistortion=false) const
calculates the projection of a point in the local camera coordinate system to a pixel in the image pl...
3D rotation matrix
Definition: RMatrix.hh:49
void TransfCoordSphere2CalibImage_(double theta, double phi, HomgPoint2D &Source) const
Transforms a calibrated pair of polar coordinates phi, theta to a coordinate pair of image coordinate...
this class interpolates a function y=f(t) between given control points (the y-values) ...
Definition: Interpolator.hh:71
void RecalculateUndistortion(const double maxCamAngle)
Recalculate undistortion polynomial for new maximal camera angle.
Wrapper class for reading and writing XML files based on the XML library libxml2. ...
Definition: XMLIO.hh:72
xmlNodePtr addChildNode(const xmlNodePtr ParentNode, const std::string &NewNodeName)
Add a child node to an incoming node with the given name.
Definition: XMLIO.cpp:131
int InitAngleCorrFromPoly(const double maxCamAngle, const double acCoeff0, const double acCoeff1, const double acCoeff2, const double acCoeff3, const double acCoeff4)
int TransfCoordRotateSphere2Image_(double theta, double phi, double viewCenterX, double viewCenterY, HomgPoint2D &Source) const
Transforms a calibrated pair of polar coordinates phi, theta to a coordinate pair of image coordinate...
int Spline(double &res, double t, unsigned int k=3)
these functions do the Spline interpolation which reaches each control point.
virtual BIAS::RMatrix GetR() const
Get orientation as rotation matrix R.
long int LevenbergMarquardt(minpack_funcder_mn fun, void *p, long int m, long int n, Vector< double > &InitialGuess, Vector< double > &Result, double Tolerance)
Solves an overdetermined system f(x) = 0 of m non-linear equations with n parameters using the Levenb...
Definition: Minpack.cpp:97
xmlNodePtr getFirstChild(const xmlNodePtr ParentNode)
Get the first child of a given parent, or NULL for no childs.
Definition: XMLIO.cpp:452
void SetControlPoints(const std::vector< double > &cPnt1)
set the control points, which control the interpolating curve
Definition: Interpolator.hh:87
camera parameters which define the mapping between rays in the camera coordinate system and pixels in...
class HomgPoint3D describes a point with 3 degrees of freedom in projective coordinates.
Definition: HomgPoint3D.hh:61
void TransfCoordSphere2Image_(double theta, double phi, HomgPoint2D &Source) const
Transforms a calibrated pair of polar coordinates phi, theta to a coordinate pair of image coordinate...
virtual Vector3< double > UnProjectToPointLocal(const HomgPoint2D &pos, const double &depth, bool IgnoreDistortion=false) const
calculates a 3D point in a local camera coordinate system specified by camSystem, which belongs to th...
int FitPolynomialWithoutLinearMonomial_(const unsigned int degree, const std::vector< double > &x, const std::vector< double > &y, std::vector< double > &coefficients)
helper function for EstimateUndistortionPolynomial
virtual int XMLOut(const xmlNodePtr Node, XMLIO &XMLObject) const
specialization of XML write function
Can be used to run along the image border.
virtual int XMLIn(const xmlNodePtr Node, XMLIO &XMLObject)
specialization of XML read function
bool Equal(const T left, const T right, const T eps)
comparison function for floating point values See http://www.boost.org/libs/test/doc/components/test_...
xmlAttrPtr getAttributeByName(const xmlNodePtr Node, const std::string &attribute_name)
search for a specific attribute
Definition: XMLIO.cpp:650
double x
If using BorderPixel methods these are the coordinates of the pixel.
BIAS::Vector3< T > CoordSphereToEuclidean() const
coordinate transfrom.
Definition: Vector3.cpp:78
double getAttributeValueDouble(const xmlAttrPtr Attribute) const
Definition: XMLIO.cpp:736
virtual const bool DoIntrinsicsDiffer(const ProjectionParametersBase *p) const
int SetUndistortion(const std::vector< double > &undistAngles, const std::vector< double > &distAngles, const double radius)
Set undistortion polynomial from corresponding undistorted and distorted azimuth angles (in rad) and ...
void TransfCoordImage2Sphere_(const HomgPoint2D &Source, double &theta, double &phi) const
Transforms a coordinate pair of image coordinates x, y in the uncalibrated spherical image...
std::string getNodeName(const xmlNodePtr Node) const
Get the name of a given Node.
Definition: XMLIO.cpp:543
K describes the mapping from world coordinates (wcs) to pixel coordinates (pcs).
Definition: KMatrix.hh:48
bool GetPerspectiveCutOutParameters(const BIAS::HomgPoint2D &viewCenter, const double perspHalfViewingAngleDEG, ProjectionParametersPerspective &pp) const
Calculates perspective camera with specified viewing angle and optical axis aligned with the optical ...
void TransfCoordCalibImage2Sphere_(const HomgPoint2D &Source, double &theta, double &phi) const
Transforms a coordinate pair of image coordinates x, y in the calibrated spherical image...
virtual int XMLGetClassName(std::string &TopLevelTag, double &Version) const
specialization of XML block name function
virtual void SetQC(const BIAS::Quaternion< double > &Q, const BIAS::Vector3< double > &C)
Set pose from unit quaternion Q and projection center C.
void SetFocalLengthAndAspect(double f, double AspectRatio)
Set the current camera focal length in pixel and the a spect ratio.
virtual int XMLGetClassName(std::string &TopLevelTag, double &Version) const
Specialization of XML block name function.
void SetMaxCamAngle(const double maxCamAngle)
Set maximal camera angle (in rad), i.e.
virtual bool Undistort(BIAS::HomgPoint2D &point2d) const
Interface defintion for lens undistortion function, implemented by derived classes.
int EstimateDistortionPolynomial(std::vector< double > &coefficients, const unsigned int degree=4, bool LinearMonomialIsZero=false)
Estimates distortion polynomial mapping angle theta to radius according to Scaramuzza&#39;s Matlab toolbo...
Camera parameters which define the mapping between rays in the camera coordinate system and pixels in...
void EnforceNormalRange_(double &theta, double &phi) const
theta and phi are periodic in 2*M_PI, if they are out of a suitable range, try to find best equivalen...
Vector3< T > & Normalize()
normalize this vector to length 1
Definition: Vector3.hh:663
virtual bool Distort(BIAS::HomgPoint2D &point2d) const
Interface defintion for lens distortion function, implemented by derived classes. ...
virtual double ViewDifference(const ProjectionParametersBase *pPPB) const
Returns difference between two projections like |C1-C2|.
class BIASGeometryBase_EXPORT HomgPoint3D
void SetValueAsAxisRad(const Vector3< QUAT_TYPE > &axis, QUAT_TYPE angle)
sets the quaternion with given rotation axis and angle (in rad)
Definition: Quaternion.hh:183
virtual void UnProjectLocal(const HomgPoint2D &pos, Vector3< double > &origin, Vector3< double > &direction, bool IgnoreDistortion=false) const
calculates the viewing ray from the camera center (in the camera coordinate system) which belongs to ...