45 bool CheckParallelRays(
const Eigen::Vector3d& ray1,
const Eigen::Vector3d& ray2,
46 const Eigen::Vector3d& ray3) {
47 const double kParallelThreshold = 1
e-5;
48 return ray1.cross(ray2).isApproxToConstant(0, kParallelThreshold) &&
49 ray1.cross(ray3).isApproxToConstant(0, kParallelThreshold);
53 bool CheckCollinearPoints(
const Eigen::Vector3d& X1,
const Eigen::Vector3d& X2,
54 const Eigen::Vector3d& X3) {
55 const double kMinNonCollinearity = 1
e-5;
56 const Eigen::Vector3d X12 = X2 - X1;
57 const double non_collinearity_measure =
58 X12.cross(X1 - X3).squaredNorm() / X12.squaredNorm();
59 return non_collinearity_measure < kMinNonCollinearity;
63 const Eigen::Vector2d& point2D) {
65 const Eigen::Vector3d bearing =
66 inv_proj_matrix.leftCols<3>() * point2D.homogeneous();
67 const Eigen::Vector3d proj_center = inv_proj_matrix.rightCols<1>();
68 const Eigen::Vector3d bearing_normalized = bearing.normalized();
70 pluecker << bearing_normalized, proj_center.cross(bearing_normalized);
74 Eigen::Vector3d PointFromPlueckerLineAndDepth(
const Eigen::Vector6d& pluecker,
76 return pluecker.head<3>().cross(pluecker.tail<3>()) +
77 depth * pluecker.head<3>();
95 Eigen::Matrix<double, 3, 6> ComputePolynomialCoefficients(
96 const std::vector<Eigen::Vector6d>& plueckers,
97 const std::vector<Eigen::Vector3d>& points3D) {
98 CHECK_EQ(plueckers.size(), 3);
99 CHECK_EQ(points3D.size(), 3);
101 Eigen::Matrix<double, 3, 6> K;
102 const std::array<int, 3> is = {{0, 0, 1}};
103 const std::array<int, 3> js = {{1, 2, 2}};
105 for (
int k = 0; k < 3; ++k) {
108 const Eigen::Vector3d moment_difference =
109 plueckers[i].head<3>().cross(plueckers[i].tail<3>()) -
110 plueckers[j].head<3>().cross(plueckers[j].tail<3>());
112 K(k, 1) = -2 * plueckers[i].head<3>().dot(plueckers[j].head<3>());
113 K(k, 2) = 2 * moment_difference.dot(plueckers[i].head<3>());
115 K(k, 4) = -2 * moment_difference.dot(plueckers[j].head<3>());
116 K(k, 5) = moment_difference.squaredNorm() -
117 (points3D[i] - points3D[j]).squaredNorm();
124 int SolveQuadratic(
const double b,
const double c,
double* roots) {
125 const double delta = b * b - 4 * c;
128 const double sqrt_delta = std::sqrt(delta);
129 roots[0] = -0.5 * (b + sqrt_delta);
130 roots[1] = -0.5 * (b - sqrt_delta);
140 void ComputeLambdaValues(
const Eigen::Matrix<double, 3, 6>::ConstRowXpr& k,
141 const double lambda_j,
142 std::vector<double>* lambdas_i) {
145 const int num_solutions =
146 SolveQuadratic(k(1) * lambda_j + k(2),
147 lambda_j * (k(3) * lambda_j + k(4)) + k(5), roots);
148 for (
int i = 0; i < num_solutions; ++i) {
150 lambdas_i->push_back(roots[i]);
158 std::vector<Eigen::Vector3d> ComputeDepthsSylvester(
159 const Eigen::Matrix<double, 3, 6>& K) {
162 Eigen::VectorXd roots_real;
163 Eigen::VectorXd roots_imag;
165 return std::vector<Eigen::Vector3d>();
169 std::vector<Eigen::Vector3d> depths;
170 depths.reserve(roots_real.size());
172 const double kMaxRootImagRatio = 1
e-3;
173 if (std::abs(roots_imag(i)) > kMaxRootImagRatio * std::abs(roots_real(i))) {
177 const double lambda_3 = roots_real(i);
182 std::vector<double> lambdas_2;
183 ComputeLambdaValues(K.row(2), lambda_3, &lambdas_2);
188 for (
const double lambda_2 : lambdas_2) {
189 std::vector<double> lambdas_1_1;
190 ComputeLambdaValues(K.row(0), lambda_2, &lambdas_1_1);
191 std::vector<double> lambdas_1_2;
192 ComputeLambdaValues(K.row(1), lambda_3, &lambdas_1_2);
193 for (
const double lambda_1_1 : lambdas_1_1) {
194 for (
const double lambda_1_2 : lambdas_1_2) {
195 const double kMaxLambdaRatio = 1
e-2;
196 if (std::abs(lambda_1_1 - lambda_1_2) <
197 kMaxLambdaRatio * std::max(lambda_1_1, lambda_1_2)) {
198 const double lambda_1 = (lambda_1_1 + lambda_1_2) / 2;
199 depths.emplace_back(lambda_1, lambda_2, lambda_3);
212 const std::vector<X_t>& points2D,
const std::vector<Y_t>& points3D) {
213 CHECK_EQ(points2D.size(), 3);
214 CHECK_EQ(points3D.size(), 3);
216 if (CheckCollinearPoints(points3D[0], points3D[1], points3D[2])) {
217 return std::vector<GP3PEstimator::M_t>({});
221 std::vector<Eigen::Vector6d> plueckers(3);
222 for (
size_t i = 0; i < 3; ++i) {
223 plueckers[i] = ComposePlueckerLine(points2D[i].rel_tform, points2D[i].xy);
226 if (CheckParallelRays(plueckers[0].head<3>(), plueckers[1].head<3>(),
227 plueckers[2].head<3>())) {
228 return std::vector<GP3PEstimator::M_t>({});
232 const Eigen::Matrix<double, 3, 6> K =
233 ComputePolynomialCoefficients(plueckers, points3D);
236 const std::vector<Eigen::Vector3d> depths = ComputeDepthsSylvester(K);
237 if (depths.empty()) {
238 return std::vector<GP3PEstimator::M_t>({});
246 Eigen::Matrix3d points3D_world;
247 for (
size_t i = 0; i < 3; ++i) {
248 points3D_world.col(i) = points3D[i];
251 std::vector<M_t> models(depths.size());
252 for (
size_t i = 0; i < depths.size(); ++i) {
253 Eigen::Matrix3d points3D_camera;
254 for (
size_t j = 0; j < 3; ++j) {
255 points3D_camera.col(j) =
256 PointFromPlueckerLineAndDepth(plueckers[j], depths[i][j]);
259 const Eigen::Matrix4d transform =
260 Eigen::umeyama(points3D_world, points3D_camera,
false);
261 models[i] = transform.topLeftCorner<3, 4>();
268 const std::vector<Y_t>& points3D,
269 const M_t& proj_matrix,
270 std::vector<double>* residuals) {
271 CHECK_EQ(points2D.size(), points3D.size());
273 residuals->resize(points2D.size(), 0);
278 const double P_00 = proj_matrix(0, 0);
279 const double P_01 = proj_matrix(0, 1);
280 const double P_02 = proj_matrix(0, 2);
281 const double P_03 = proj_matrix(0, 3);
282 const double P_10 = proj_matrix(1, 0);
283 const double P_11 = proj_matrix(1, 1);
284 const double P_12 = proj_matrix(1, 2);
285 const double P_13 = proj_matrix(1, 3);
286 const double P_20 = proj_matrix(2, 0);
287 const double P_21 = proj_matrix(2, 1);
288 const double P_22 = proj_matrix(2, 2);
289 const double P_23 = proj_matrix(2, 3);
291 for (
size_t i = 0; i < points2D.size(); ++i) {
293 const double X_0 = points3D[i](0);
294 const double X_1 = points3D[i](1);
295 const double X_2 = points3D[i](2);
298 const double pgx_0 = P_00 * X_0 + P_01 * X_1 + P_02 * X_2 + P_03;
299 const double pgx_1 = P_10 * X_0 + P_11 * X_1 + P_12 * X_2 + P_13;
300 const double pgx_2 = P_20 * X_0 + P_21 * X_1 + P_22 * X_2 + P_23;
303 const double pcx_2 = rel_tform(2, 0) * pgx_0 + rel_tform(2, 1) * pgx_1 +
304 rel_tform(2, 2) * pgx_2 + rel_tform(2, 3);
307 if (pcx_2 > std::numeric_limits<double>::epsilon()) {
308 const double pcx_0 = rel_tform(0, 0) * pgx_0 + rel_tform(0, 1) * pgx_1 +
309 rel_tform(0, 2) * pgx_2 + rel_tform(0, 3);
310 const double pcx_1 = rel_tform(1, 0) * pgx_0 + rel_tform(1, 1) * pgx_1 +
311 rel_tform(1, 2) * pgx_2 + rel_tform(1, 3);
312 const double inv_pcx_norm =
313 1 / std::sqrt(pcx_0 * pcx_0 + pcx_1 * pcx_1 + pcx_2 * pcx_2);
315 const double x_0 = points2D[i].xy(0);
316 const double x_1 = points2D[i].xy(1);
319 const double inv_x_norm = 1 / std::sqrt(x_0 * x_0 + x_1 * x_1 + 1);
320 const double cosine_dist =
321 1 - inv_pcx_norm * inv_x_norm * (pcx_0 * x_0 + pcx_1 * x_1 + pcx_2);
322 (*residuals)[i] = cosine_dist * cosine_dist;
324 const double inv_pcx_2 = 1.0 / pcx_2;
325 const double dx_0 = x_0 - pcx_0 * inv_pcx_2;
326 const double dx_1 = x_1 - pcx_1 * inv_pcx_2;
327 const double reproj_error = dx_0 * dx_0 + dx_1 * dx_1;
328 (*residuals)[i] = reproj_error;
330 LOG(FATAL) <<
"Invalid residual type";
333 (*residuals)[i] = std::numeric_limits<double>::max();
static std::vector< M_t > Estimate(const std::vector< X_t > &points2D, const std::vector< Y_t > &points3D)
void Residuals(const std::vector< X_t > &points2D, const std::vector< Y_t > &points3D, const M_t &proj_matrix, std::vector< double > *residuals)
ResidualType residual_type
Matrix< double, 3, 4 > Matrix3x4d
Eigen::Matrix< double, 6, 1 > Vector6d
bool FindPolynomialRootsCompanionMatrix(const Eigen::VectorXd &coeffs_all, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Eigen::Matrix< double, 9, 1 > ComputeDepthsSylvesterCoeffs(const Eigen::Matrix< double, 3, 6 > &K)
Eigen::Matrix3x4d InvertProjectionMatrix(const Eigen::Matrix3x4d &proj_matrix)
Eigen::MatrixXd::Index Index