g2o
bal_example.cpp
Go to the documentation of this file.
1 // g2o - General Graph Optimization
2 // Copyright (C) 2012 R. Kümmerle
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #include <Eigen/Core>
28 #include <Eigen/StdVector>
29 #include <Eigen/Geometry>
30 #include <iostream>
31 
32 #include "g2o/stuff/command_args.h"
33 #include "g2o/core/batch_stats.h"
35 #include "g2o/core/block_solver.h"
36 #include "g2o/core/solver.h"
38 #include "g2o/core/base_vertex.h"
43 
44 #include "EXTERNAL/ceres/autodiff.h"
45 
46 #if defined G2O_HAVE_CHOLMOD
48 #elif defined G2O_HAVE_CSPARSE
50 #endif
51 
52 using namespace g2o;
53 using namespace std;
54 using namespace Eigen;
55 
65 class VertexCameraBAL : public BaseVertex<9, Eigen::VectorXd>
66 {
67  public:
70  {
71  }
72 
73  virtual bool read(std::istream& /*is*/)
74  {
75  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
76  return false;
77  }
78 
79  virtual bool write(std::ostream& /*os*/) const
80  {
81  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
82  return false;
83  }
84 
85  virtual void setToOriginImpl()
86  {
87  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
88  }
89 
90  virtual void oplusImpl(const double* update)
91  {
92  Eigen::VectorXd::ConstMapType v(update, VertexCameraBAL::Dimension);
93  _estimate += v;
94  }
95 };
96 
102 class VertexPointBAL : public BaseVertex<3, Eigen::Vector3d>
103 {
104  public:
107  {
108  }
109 
110  virtual bool read(std::istream& /*is*/)
111  {
112  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
113  return false;
114  }
115 
116  virtual bool write(std::ostream& /*os*/) const
117  {
118  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
119  return false;
120  }
121 
122  virtual void setToOriginImpl()
123  {
124  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
125  }
126 
127  virtual void oplusImpl(const double* update)
128  {
129  Eigen::Vector3d::ConstMapType v(update);
130  _estimate += v;
131  }
132 };
133 
156 class EdgeObservationBAL : public BaseBinaryEdge<2, Vector2d, VertexCameraBAL, VertexPointBAL>
157 {
158  public:
161  {
162  }
163  virtual bool read(std::istream& /*is*/)
164  {
165  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
166  return false;
167  }
168  virtual bool write(std::ostream& /*os*/) const
169  {
170  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
171  return false;
172  }
173 
174  template<typename T>
175  inline void cross(const T x[3], const T y[3], T result[3]) const
176  {
177  result[0] = x[1] * y[2] - x[2] * y[1];
178  result[1] = x[2] * y[0] - x[0] * y[2];
179  result[2] = x[0] * y[1] - x[1] * y[0];
180  }
181 
182  template<typename T>
183  inline T dot(const T x[3], const T y[3]) const { return (x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);}
184 
185  template<typename T>
186  inline T squaredNorm(const T x[3]) const { return dot<T>(x, x);}
187 
191  template<typename T>
192  bool operator()(const T* camera, const T* point, T* error) const
193  {
194  // Rodrigues' formula for the rotation
195  T p[3];
196  T theta = sqrt(squaredNorm(camera));
197  if (theta > T(0)) {
198  T v[3];
199  v[0] = camera[0] / theta;
200  v[1] = camera[1] / theta;
201  v[2] = camera[2] / theta;
202  T cth = cos(theta);
203  T sth = sin(theta);
204 
205  T vXp[3];
206  cross(v, point, vXp);
207  T vDotp = dot(v, point);
208  T oneMinusCth = T(1) - cth;
209 
210  for (int i = 0; i < 3; ++i)
211  p[i] = point[i] * cth + vXp[i] * sth + v[i] * vDotp * oneMinusCth;
212  } else {
213  // taylor expansion for theta close to zero
214  T aux[3];
215  cross(camera, point, aux);
216  for (int i = 0; i < 3; ++i)
217  p[i] = point[i] + aux[i];
218  }
219 
220  // translation of the camera
221  p[0] += camera[3];
222  p[1] += camera[4];
223  p[2] += camera[5];
224 
225  // perspective division
226  T projectedPoint[2];
227  projectedPoint[0] = - p[0] / p[2];
228  projectedPoint[1] = - p[1] / p[2];
229 
230  // conversion to pixel coordinates
231  T radiusSqr = projectedPoint[0]*projectedPoint[0] + projectedPoint[1]*projectedPoint[1];
232  T f = T(camera[6]);
233  T k1 = T(camera[7]);
234  T k2 = T(camera[8]);
235  T r_p = T(1) + k1 * radiusSqr + k2 * radiusSqr * radiusSqr;
236  T prediction[2];
237  prediction[0] = f * r_p * projectedPoint[0];
238  prediction[1] = f * r_p * projectedPoint[1];
239 
240  error[0] = prediction[0] - T(measurement()(0));
241  error[1] = prediction[1] - T(measurement()(1));
242 
243  return true;
244  }
245 
247  {
248  const VertexCameraBAL* cam = static_cast<const VertexCameraBAL*>(vertex(0));
249  const VertexPointBAL* point = static_cast<const VertexPointBAL*>(vertex(1));
250 
251  (*this)(cam->estimate().data(), point->estimate().data(), _error.data());
252  }
253 
255  {
256  // use numeric Jacobians
257  //BaseBinaryEdge<2, Vector2d, VertexCameraBAL, VertexPointBAL>::linearizeOplus();
258  //return;
259 
260  const VertexCameraBAL* cam = static_cast<const VertexCameraBAL*>(vertex(0));
261  const VertexPointBAL* point = static_cast<const VertexPointBAL*>(vertex(1));
262  typedef ceres::internal::AutoDiff<EdgeObservationBAL, double, VertexCameraBAL::Dimension, VertexPointBAL::Dimension> BalAutoDiff;
263 
264  Matrix<double, Dimension, VertexCameraBAL::Dimension, Eigen::RowMajor> dError_dCamera;
265  Matrix<double, Dimension, VertexPointBAL::Dimension, Eigen::RowMajor> dError_dPoint;
266  double *parameters[] = { const_cast<double*>(cam->estimate().data()), const_cast<double*>(point->estimate().data()) };
267  double *jacobians[] = { dError_dCamera.data(), dError_dPoint.data() };
268  double value[Dimension];
269  bool diffState = BalAutoDiff::Differentiate(*this, parameters, Dimension, value, jacobians);
270 
271  // copy over the Jacobians (convert row-major -> column-major)
272  if (diffState) {
273  _jacobianOplusXi = dError_dCamera;
274  _jacobianOplusXj = dError_dPoint;
275  } else {
276  assert(0 && "Error while differentiating");
277  _jacobianOplusXi.setZero();
278  _jacobianOplusXi.setZero();
279  }
280  }
281 };
282 
283 int main(int argc, char** argv)
284 {
285  int maxIterations;
286  bool verbose;
287  bool usePCG;
288  string outputFilename;
289  string inputFilename;
290  string statsFilename;
291  CommandArgs arg;
292  arg.param("i", maxIterations, 5, "perform n iterations");
293  arg.param("o", outputFilename, "", "write points into a vrml file");
294  arg.param("pcg", usePCG, false, "use PCG instead of the Cholesky");
295  arg.param("v", verbose, false, "verbose output of the optimization process");
296  arg.param("stats", statsFilename, "", "specify a file for the statistics");
297  arg.paramLeftOver("graph-input", inputFilename, "", "file which will be processed");
298 
299  arg.parseArgs(argc, argv);
300 
301  typedef g2o::BlockSolver< g2o::BlockSolverTraits<9, 3> > BalBlockSolver;
302 #ifdef G2O_HAVE_CHOLMOD
303  string choleskySolverName = "CHOLMOD";
305 #elif defined G2O_HAVE_CSPARSE
306  string choleskySolverName = "CSparse";
308 #else
309 #error neither CSparse nor CHOLMOD are available
310 #endif
311  typedef g2o::LinearSolverPCG<BalBlockSolver::PoseMatrixType> BalLinearSolverPCG;
312 
313  g2o::SparseOptimizer optimizer;
315  if (usePCG) {
316  cout << "Using PCG" << endl;
317  linearSolver = new BalLinearSolverPCG();
318  } else {
319  cout << "Using Cholesky: " << choleskySolverName << endl;
320  BalLinearSolver* cholesky = new BalLinearSolver();
321  cholesky->setBlockOrdering(true);
322  linearSolver = cholesky;
323  }
324  BalBlockSolver* solver_ptr = new BalBlockSolver(linearSolver);
326  //solver->setUserLambdaInit(1);
327  optimizer.setAlgorithm(solver);
328  if (statsFilename.size() > 0){
329  optimizer.setComputeBatchStatistics(true);
330  }
331 
332  vector<VertexPointBAL*> points;
333  vector<VertexCameraBAL*> cameras;
334 
335  // parse BAL dataset
336  cout << "Loading BAL dataset " << inputFilename << endl;
337  {
338  ifstream ifs(inputFilename.c_str());
339  int numCameras, numPoints, numObservations;
340  ifs >> numCameras >> numPoints >> numObservations;
341 
342  cerr << PVAR(numCameras) << " " << PVAR(numPoints) << " " << PVAR(numObservations) << endl;
343 
344  int id = 0;
345  cameras.reserve(numCameras);
346  for (int i = 0; i < numCameras; ++i, ++id) {
347  VertexCameraBAL* cam = new VertexCameraBAL;
348  cam->setId(id);
349  optimizer.addVertex(cam);
350  cameras.push_back(cam);
351  }
352 
353  points.reserve(numPoints);
354  for (int i = 0; i < numPoints; ++i, ++id) {
356  p->setId(id);
357  p->setMarginalized(true);
358  bool addedVertex = optimizer.addVertex(p);
359  if (! addedVertex) {
360  cerr << "failing adding vertex" << endl;
361  }
362  points.push_back(p);
363  }
364 
365  // read in the observation
366  for (int i = 0; i < numObservations; ++i) {
367  int camIndex, pointIndex;
368  double obsX, obsY;
369  ifs >> camIndex >> pointIndex >> obsX >> obsY;
370 
371  assert(camIndex >= 0 && (size_t)camIndex < cameras.size() && "Index out of bounds");
372  VertexCameraBAL* cam = cameras[camIndex];
373  assert(pointIndex >= 0 && (size_t)pointIndex < points.size() && "Index out of bounds");
374  VertexPointBAL* point = points[pointIndex];
375 
377  e->setVertex(0, cam);
378  e->setVertex(1, point);
379  e->setInformation(Eigen::Matrix2d::Identity());
380  e->setMeasurement(Eigen::Vector2d(obsX, obsY));
381  bool addedEdge = optimizer.addEdge(e);
382  if (! addedEdge) {
383  cerr << "error adding edge" << endl;
384  }
385  }
386 
387  // read in the camera params
388  Eigen::VectorXd cameraParameter(9);
389  for (int i = 0; i < numCameras; ++i) {
390  for (int j = 0; j < 9; ++j)
391  ifs >> cameraParameter(j);
392  VertexCameraBAL* cam = cameras[i];
393  cam->setEstimate(cameraParameter);
394  }
395 
396  // read in the points
397  Eigen::Vector3d p;
398  for (int i = 0; i < numPoints; ++i) {
399  ifs >> p(0) >> p(1) >> p(2);
400 
401  VertexPointBAL* point = points[i];
402  point->setEstimate(p);
403  }
404 
405  }
406  cout << "done." << endl;
407 
408  cout << "Initializing ... " << flush;
409  optimizer.initializeOptimization();
410  cout << "done." << endl;
411  optimizer.setVerbose(verbose);
412  cout << "Start to optimize" << endl;
413  optimizer.optimize(maxIterations);
414 
415  if (statsFilename!=""){
416  cerr << "writing stats to file \"" << statsFilename << "\" ... ";
417  ofstream fout(statsFilename.c_str());
418  const BatchStatisticsContainer& bsc = optimizer.batchStatistics();
419  for (size_t i=0; i<bsc.size(); i++)
420  fout << bsc[i] << endl;
421  cerr << "done." << endl;
422  }
423 
424  // dump the points
425  if (outputFilename.size() > 0) {
426  ofstream fout(outputFilename.c_str()); // loadable with meshlab
427  fout
428  << "#VRML V2.0 utf8\n"
429  << "Shape {\n"
430  << " appearance Appearance {\n"
431  << " material Material {\n"
432  << " diffuseColor " << 1 << " " << 0 << " " << 0 << "\n"
433  << " ambientIntensity 0.2\n"
434  << " emissiveColor 0.0 0.0 0.0\n"
435  << " specularColor 0.0 0.0 0.0\n"
436  << " shininess 0.2\n"
437  << " transparency 0.0\n"
438  << " }\n"
439  << " }\n"
440  << " geometry PointSet {\n"
441  << " coord Coordinate {\n"
442  << " point [\n";
443  for (vector<VertexPointBAL*>::const_iterator it = points.begin(); it != points.end(); ++it) {
444  fout << (*it)->estimate().transpose() << endl;
445  }
446  fout << " ]\n" << " }\n" << "}\n" << " }\n";
447  }
448 
449  return 0;
450 }
virtual void oplusImpl(const double *update)
const BatchStatisticsContainer & batchStatistics() const
virtual bool read(std::istream &)
read the vertex from a stream, i.e., the internal state of the vertex
#define __PRETTY_FUNCTION__
Definition: macros.h:89
Command line parsing of argc and argv.
Definition: command_args.h:46
void setComputeBatchStatistics(bool computeBatchStatistics)
Implementation of the Levenberg Algorithm.
int optimize(int iterations, bool online=false)
Templatized BaseVertex.
Definition: base_vertex.h:50
virtual void setMeasurement(const Measurement &m)
Definition: base_edge.h:76
virtual bool read(std::istream &)
read the vertex from a stream, i.e., the internal state of the vertex
linear solver using PCG, pre-conditioner is block Jacobi
void setVertex(size_t i, Vertex *v)
Definition: hyper_graph.h:194
void cross(const T x[3], const T y[3], T result[3]) const
virtual void oplusImpl(const double *update)
Definition: bal_example.cpp:90
virtual void setId(int id)
sets the id of the node in the graph be sure that the graph keeps consistent after changing the id ...
bool parseArgs(int argc, char **argv, bool exitOnError=true)
basic solver for Ax = b
Definition: linear_solver.h:41
void setVerbose(bool verbose)
linear solver which uses CSparse
void paramLeftOver(const std::string &name, std::string &p, const std::string &defValue, const std::string &desc, bool optional=false)
void setAlgorithm(OptimizationAlgorithm *algorithm)
EIGEN_STRONG_INLINE void setInformation(const InformationType &information)
Definition: base_edge.h:69
virtual bool read(std::istream &)
read the vertex from a stream, i.e., the internal state of the vertex
Definition: bal_example.cpp:73
void param(const std::string &name, bool &p, bool defValue, const std::string &desc)
basic solver for Ax = b which has to reimplemented for different linear algebra libraries ...
void setEstimate(const EstimateType &et)
set the estimate for the vertex also calls updateCache()
Definition: base_vertex.h:101
virtual bool write(std::ostream &) const
write the vertex to a stream
3D world feature
void setMarginalized(bool marginalized)
true => this node should be marginalized out during the optimization
edge representing the observation of a world feature by a camera
bool operator()(const T *camera, const T *point, T *error) const
virtual bool write(std::ostream &) const
write the vertex to a stream
Definition: bal_example.cpp:79
const EstimateType & estimate() const
return the current estimate of the vertex
Definition: base_vertex.h:99
Implementation of a solver operating on the blocks of the Hessian.
Definition: block_solver.h:96
T dot(const T x[3], const T y[3]) const
virtual bool initializeOptimization(HyperGraph::EdgeSet &eset)
virtual bool addEdge(HyperGraph::Edge *e)
virtual bool addVertex(HyperGraph::Vertex *v, Data *userData)
int main(int argc, char **argv)
virtual void setToOriginImpl()
sets the node to the origin (used in the multilevel stuff)
std::vector< G2OBatchStatistics > BatchStatisticsContainer
Definition: batch_stats.h:81
static const int Dimension
dimension of the estimate (minimal) in the manifold space
Definition: base_vertex.h:57
virtual bool write(std::ostream &) const
write the vertex to a stream
virtual void setToOriginImpl()
sets the node to the origin (used in the multilevel stuff)
Definition: bal_example.cpp:85
T squaredNorm(const T x[3]) const
camera vertex which stores the parameters for a pinhole camera
Definition: bal_example.cpp:65