Berkeley SfM
pose_estimator_2d3d.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, The Regents of the University of California (Regents).
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  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following
14  * disclaimer in the documentation and/or other materials provided
15  * with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Please contact the author(s) of this library if you have any questions.
34  * Authors: Erik Nelson ( eanelson@eecs.berkeley.edu )
35  * David Fridovich-Keil ( dfk@eecs.berkeley.edu )
36  */
37 
38 #include "pose_estimator_2d3d.h"
39 
40 #include <ceres/ceres.h>
41 #include <Eigen/SVD>
42 #include <glog/logging.h>
43 
44 #include "normalization.h"
45 #include "../optimization/cost_functors.h"
46 
47 namespace bsfm {
48 
50  : T_(Matrix3d::Identity()), U_(Matrix4d::Identity()) {}
51 
53 
55  const Point3DList& points_3d,
56  const CameraIntrinsics& intrinsics) {
57  if (points_2d.size() != points_3d.size()) {
58  VLOG(1) << "Inputs 'points_2d' and 'points_3d' do not contain the "
59  "same number of elements.";
60  return false;
61  }
62 
63  // Copy camera intrinsics.
64  intrinsics_ = intrinsics;
65 
66  // Normalize the 3D points.
67  T_ = ComputeNormalization(points_2d);
68  U_ = ComputeNormalization(points_3d);
69 
70  points_2d_.clear();
71  for (size_t ii = 0; ii < points_2d.size(); ++ii) {
72  // Get (u, v) image-space point and normalize.
73  double u = points_2d[ii].u_;
74  double v = points_2d[ii].v_;
75  u = T_(0, 0) * u + T_(0, 2);
76  v = T_(1, 1) * v + T_(1, 2);
77 
78  points_2d_.push_back(Feature(u, v));
79  }
80 
81  points_3d_.clear();
82  for (size_t ii = 0; ii < points_3d.size(); ++ii) {
83  // Get (x, y, z) world-space point and normalize.
84  double x = points_3d[ii].X();
85  double y = points_3d[ii].Y();
86  double z = points_3d[ii].Z();
87  x = U_(0, 0) * x + U_(0, 3);
88  y = U_(1, 1) * y + U_(1, 3);
89  z = U_(2, 2) * z + U_(2, 3);
90 
91  points_3d_.push_back(Point3D(x, y, z));
92  }
93 
94  return true;
95 }
96 
97 bool PoseEstimator2D3D::Solve(Pose& camera_pose) {
98  // Get an initial P.
99  Matrix34d P;
100  if (!ComputeInitialSolution(P)) {
101  VLOG(1) << "Failed to compute an initial solution for P.";
102  return false;
103  }
104 
105  // Refine P with non-linear optimization.
106  Matrix34d P_opt = P;
107 
108  if (!OptimizeSolution(P_opt)) {
109  VLOG(1) << "Failed to optimize P. Continuing using the initial solution.";
110  P_opt = P;
111  }
112 
113  // Get the camera pose from the computed projection matrix.
114  if (!ExtractPose(P_opt, camera_pose)) {
115  VLOG(1) << "Computed rotation is non-invertible.";
116  return false;
117  }
118 
119  return true;
120 }
121 
123  // Use Eq. 7.2 from H&Z: Multiple-View Geometry to determine an
124  // over-determined solution for the camera projection matrix P.
125 
126  // First build the A matrix, which is 2n x 12.
127  MatrixXd A;
128  A.resize(points_2d_.size() * 2, 12);
129  for (size_t ii = 0; ii < points_2d_.size(); ii++) {
130  // Get (u, v) image-space point.
131  const double u = points_2d_[ii].u_;
132  const double v = points_2d_[ii].v_;
133 
134  // Get (x, y, z) world-space point.
135  const double x = points_3d_[ii].X();
136  const double y = points_3d_[ii].Y();
137  const double z = points_3d_[ii].Z();
138 
139  // First 4 columns, top row of block.
140  A(2*ii+0, 0) = 0;
141  A(2*ii+0, 1) = 0;
142  A(2*ii+0, 2) = 0;
143  A(2*ii+0, 3) = 0;
144 
145  // Second 4 columns, top row of block.
146  A(2*ii+0, 4) = -x;
147  A(2*ii+0, 5) = -y;
148  A(2*ii+0, 6) = -z;
149  A(2*ii+0, 7) = -1;
150 
151  // Third 4 columns, top row of block.
152  A(2*ii+0, 8) = v*x;
153  A(2*ii+0, 9) = v*y;
154  A(2*ii+0, 10) = v*z;
155  A(2*ii+0, 11) = v;
156 
157  // First 4 columns, bottom row of block.
158  A(2*ii+1, 0) = x;
159  A(2*ii+1, 1) = y;
160  A(2*ii+1, 2) = z;
161  A(2*ii+1, 3) = 1;
162 
163  // Second 4 columns, bottom row of block.
164  A(2*ii+1, 4) = 0;
165  A(2*ii+1, 5) = 0;
166  A(2*ii+1, 6) = 0;
167  A(2*ii+1, 7) = 0;
168 
169  // Third 4 columns, bottom row of block.
170  A(2*ii+1, 8) = -u*x;
171  A(2*ii+1, 9) = -u*y;
172  A(2*ii+1, 10) = -u*z;
173  A(2*ii+1, 11) = -u;
174  }
175 
176  // Get svd(A). Save some time and compute a thin U. We still need a full V.
177  Eigen::JacobiSVD<Eigen::MatrixXd> svd;
178  svd.compute(A, Eigen::ComputeThinU | Eigen::ComputeFullV);
179  if (!svd.computeV()) {
180  VLOG(1) << "Failed to compute a singular value decomposition of A matrix.";
181  return false;
182  }
183 
184  // Get the projection matrix elements from the SVD decomposition.
185  const VectorXd P_vec = svd.matrixV().col(11).normalized();
186 
187  // Reshape the vector into the initial solution.
188  initial_solution.row(0) = P_vec.topRows(4).transpose();
189  initial_solution.row(1) = P_vec.middleRows(4, 4).transpose();
190  initial_solution.row(2) = P_vec.bottomRows(4).transpose();
191 
192  return true;
193 }
194 
196  // Create the non-linear least squares problem and cost function.
197  ceres::Problem problem;
198 
199  // Add a term to the cost function for each 2D<-->3D correspondence.
200  Matrix34d optimized_solution = solution;
201  for (size_t ii = 0; ii < points_2d_.size(); ++ii) {
202  problem.AddResidualBlock(
204  NULL, /* squared loss */
205  optimized_solution.data());
206  }
207 
208  // Solve the non-linear least squares problem to get the projection matrix.
209  ceres::Solver::Summary summary;
210  ceres::Solver::Options options;
211  options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
212  ceres::Solve(options, &problem, &summary);
213 
214  // Store the solved variable back in 'solution'.
215  if (summary.IsSolutionUsable()) {
216  solution = optimized_solution.normalized();
217  }
218 
219  return summary.IsSolutionUsable();
220 }
221 
223  // Un-normalize the projection matrix.
224  const Matrix34d P_unnormalized = T_.inverse() * P * U_;
225 
226  // Extract camera extrinsics matrix.
227  Matrix34d Rt = intrinsics_.Kinv() * P_unnormalized;
228 
229  // [R|t] is only determined up to scale. To get this scale, note that det(R)
230  // must equal 1. Also note that for an nxn matrix, c^n*det(R) = det(cR).
231  // Use this property to scale our matrix.
232  double det = Rt.block(0, 0, 3, 3).determinant();
233  if (std::abs(det) < 1e-16) {
234  LOG(WARNING) << "Computed rotation has a determinant of 0.";
235  return false;
236  }
237 
238  // Make sure the determinant is positive.
239  if (det < 0.0) {
240  det *= -1.0;
241  Rt *= -1.0;
242  }
243 
244  // Normalize the rotation and translation.
245  Rt *= std::pow(1.0 / det, 1.0 / 3.0);
246 
247  // Initialize the output pose from the rotation and translation blocks.
248  pose = Pose(Rt);
249  return true;
250 }
251 
252 } //\namespace bsfm
std::vector< Point3D > Point3DList
Definition: point_3d.h:100
bool ExtractPose(const Matrix34d &P, Pose &pose)
bool OptimizeSolution(Matrix34d &solution)
bool Initialize(const FeatureList &points_2d, const Point3DList &points_3d, const CameraIntrinsics &intrinsics)
bool Solve(Pose &camera_pose)
Matrix3d ComputeNormalization(const FeatureMatchList &matched_features, bool use_feature_set1)
std::vector< Feature > FeatureList
Definition: feature.h:65
static ceres::CostFunction * Create(const Feature &x, const Point3D &X)
Definition: cost_functors.h:96
Definition: camera.cpp:50
::Eigen::Matrix< double, 3, 4 > Matrix34d
Definition: types.h:87
bool ComputeInitialSolution(Matrix34d &initial_solution)