1
// Copyright (c) 2011 libmv authors.
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
4
// of this software and associated documentation files (the "Software"), to
5
// deal in the Software without restriction, including without limitation the
6
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
// sell copies of the Software, and to permit persons to whom the Software is
8
// furnished to do so, subject to the following conditions:
10
// The above copyright notice and this permission notice shall be included in
11
// all copies or substantial portions of the Software.
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
#include "libmv/base/vector.h"
22
#include "libmv/logging/logging.h"
23
#include "libmv/multiview/fundamental.h"
24
#include "libmv/multiview/projection.h"
25
#include "libmv/numeric/levenberg_marquardt.h"
26
#include "libmv/numeric/numeric.h"
27
#include "libmv/simple_pipeline/reconstruction.h"
28
#include "libmv/simple_pipeline/tracks.h"
33
void CoordinatesForMarkersInImage(const vector<Marker> &markers,
37
for (int i = 0; i < markers.size(); ++i) {
38
const Marker &marker = markers[i];
39
if (markers[i].image == image) {
40
coords.push_back(Vec2(marker.x, marker.y));
43
coordinates->resize(2, coords.size());
44
for (int i = 0; i < coords.size(); i++) {
45
coordinates->col(i) = coords[i];
49
void GetImagesInMarkers(const vector<Marker> &markers,
50
int *image1, int *image2) {
51
if (markers.size() < 2) {
54
*image1 = markers[0].image;
55
for (int i = 1; i < markers.size(); ++i) {
56
if (markers[i].image != *image1) {
57
*image2 = markers[i].image;
62
LOG(FATAL) << "Only one image in the markers.";
67
bool EuclideanReconstructTwoFrames(const vector<Marker> &markers,
68
EuclideanReconstruction *reconstruction) {
69
if (markers.size() < 16) {
70
LG << "Not enough markers to initialize from two frames: " << markers.size();
75
GetImagesInMarkers(markers, &image1, &image2);
78
CoordinatesForMarkersInImage(markers, image1, &x1);
79
CoordinatesForMarkersInImage(markers, image2, &x2);
82
NormalizedEightPointSolver(x1, x2, &F);
84
// The F matrix should be an E matrix, but squash it just to be sure.
85
Eigen::JacobiSVD<Mat3> svd(F, Eigen::ComputeFullU | Eigen::ComputeFullV);
87
// See Hartley & Zisserman page 294, result 11.1, which shows how to get the
88
// closest essential matrix to a matrix that is "almost" an essential matrix.
89
double a = svd.singularValues()(0);
90
double b = svd.singularValues()(1);
91
double s = (a + b) / 2.0;
92
LG << "Initial reconstruction's rotation is non-euclidean by "
93
<< (((a - b) / std::max(a, b)) * 100) << "%; singular values:"
94
<< svd.singularValues().transpose();
98
Mat3 E = svd.matrixU() * diag.asDiagonal() * svd.matrixV().transpose();
100
// Recover motion between the two images. Since this function assumes a
101
// calibrated camera, use the identity for K.
104
Mat3 K = Mat3::Identity();
105
if (!MotionFromEssentialAndCorrespondence(E,
109
LG << "Failed to compute R and t from E and K.";
113
// Image 1 gets the reference frame, image 2 gets the relative motion.
114
reconstruction->InsertCamera(image1, Mat3::Identity(), Vec3::Zero());
115
reconstruction->InsertCamera(image2, R, t);
117
LG << "From two frame reconstruction got:\nR:\n" << R
118
<< "\nt:" << t.transpose();
124
Mat3 DecodeF(const Vec9 &encoded_F) {
125
// Decode F and force it to be rank 2.
126
Map<const Mat3> full_rank_F(encoded_F.data(), 3, 3);
127
Eigen::JacobiSVD<Mat3> svd(full_rank_F, Eigen::ComputeFullU | Eigen::ComputeFullV);
128
Vec3 diagonal = svd.singularValues();
130
Mat3 F = svd.matrixU() * diagonal.asDiagonal() * svd.matrixV().transpose();
134
// This is the stupidest way to refine F known to mankind, since it requires
135
// doing a full SVD of F at each iteration. This uses sampson error.
136
struct FundamentalSampsonCostFunction {
138
typedef Vec FMatrixType;
139
typedef Vec9 XMatrixType;
141
// Assumes markers are ordered by track.
142
FundamentalSampsonCostFunction(const vector<Marker> &markers)
143
: markers(markers) {}
145
Vec operator()(const Vec9 &encoded_F) const {
146
// Decode F and force it to be rank 2.
147
Mat3 F = DecodeF(encoded_F);
149
Vec residuals(markers.size() / 2);
151
for (int i = 0; i < markers.size() / 2; ++i) {
152
const Marker &marker1 = markers[2*i + 0];
153
const Marker &marker2 = markers[2*i + 1];
154
CHECK_EQ(marker1.track, marker2.track);
155
Vec2 x1(marker1.x, marker1.y);
156
Vec2 x2(marker2.x, marker2.y);
158
residuals[i] = SampsonDistance(F, x1, x2);
162
const vector<Marker> &markers;
167
bool ProjectiveReconstructTwoFrames(const vector<Marker> &markers,
168
ProjectiveReconstruction *reconstruction) {
169
if (markers.size() < 16) {
174
GetImagesInMarkers(markers, &image1, &image2);
177
CoordinatesForMarkersInImage(markers, image1, &x1);
178
CoordinatesForMarkersInImage(markers, image2, &x2);
181
NormalizedEightPointSolver(x1, x2, &F);
183
// XXX Verify sampson distance.
185
// Refine the resulting projection fundamental matrix using Sampson's
186
// approximation of geometric error. This avoids having to do a full bundle
187
// at the cost of some accuracy.
189
// TODO(keir): After switching to a better bundling library, use a proper
190
// full bundle adjust here instead of this lame bundle adjustment.
191
typedef LevenbergMarquardt<FundamentalSampsonCostFunction> Solver;
193
FundamentalSampsonCostFunction fundamental_cost(markers);
195
// Pack the initial P matrix into a size-12 vector..
196
Vec9 encoded_F = Map<Vec9>(F.data(), 3, 3);
198
Solver solver(fundamental_cost);
200
Solver::SolverParameters params;
201
Solver::Results results = solver.minimize(params, &encoded_F);
202
// TODO(keir): Check results to ensure clean termination.
204
// Recover F from the minimization.
205
F = DecodeF(encoded_F);
208
// Image 1 gets P = [I|0], image 2 gets arbitrary P.
209
Mat34 P1 = Mat34::Zero();
210
P1.block<3, 3>(0, 0) = Mat3::Identity();
212
ProjectionsFromFundamental(F, &P1, &P2);
214
reconstruction->InsertCamera(image1, P1);
215
reconstruction->InsertCamera(image2, P2);
217
LG << "From two frame reconstruction got P2:\n" << P2;