1 // Copyright (c) 2010 libmv authors.
2 //
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:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
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
19 // IN THE SOFTWARE.
20 
21 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
22 
23 // Copyright (c) 2012, 2013 Pierre MOULON.
24 
25 // This Source Code Form is subject to the terms of the Mozilla Public
26 // License, v. 2.0. If a copy of the MPL was not distributed with this
27 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
28 
29 #include "openMVG/multiview/projection.hpp"
30 #include "openMVG/numeric/numeric.h"
31 
32 namespace openMVG {
33 
34 /// Compute P = K[R|t]
P_From_KRt(const Mat3 & K,const Mat3 & R,const Vec3 & t,Mat34 * P)35 void P_From_KRt(
36   const Mat3 &K,  const Mat3 &R,  const Vec3 &t, Mat34 *P) {
37   *P = K * HStack(R,t);
38 }
39 
KRt_From_P(const Mat34 & P,Mat3 * Kp,Mat3 * Rp,Vec3 * tp)40 void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) {
41   // Decompose using the RQ decomposition HZ A4.1.1 pag.579.
42   Mat3 K = P.block(0, 0, 3, 3);
43 
44   Mat3 Q;
45   Q.setIdentity();
46 
47   // Set K(2,1) to zero.
48   if (K(2,1) != 0) {
49     double c = -K(2,2);
50     double s = K(2,1);
51     double l = std::hypot(c, s);
52     c /= l; s /= l;
53     Mat3 Qx;
54     Qx << 1, 0, 0,
55           0, c, -s,
56           0, s, c;
57     K = K * Qx;
58     Q = Qx.transpose() * Q;
59   }
60   // Set K(2,0) to zero.
61   if (K(2,0) != 0) {
62     double c = K(2,2);
63     double s = K(2,0);
64     double l = std::hypot(c, s);
65     c /= l; s /= l;
66     Mat3 Qy;
67     Qy << c, 0, s,
68           0, 1, 0,
69          -s, 0, c;
70     K = K * Qy;
71     Q = Qy.transpose() * Q;
72   }
73   // Set K(1,0) to zero.
74   if (K(1,0) != 0) {
75     double c = -K(1,1);
76     double s = K(1,0);
77     double l = std::hypot(c, s);
78     c /= l; s /= l;
79     Mat3 Qz;
80     Qz << c,-s, 0,
81           s, c, 0,
82           0, 0, 1;
83     K = K * Qz;
84     Q = Qz.transpose() * Q;
85   }
86 
87   Mat3 R = Q;
88 
89   //Mat3 H = P.block(0, 0, 3, 3);
90   // RQ decomposition
91   //Eigen::HouseholderQR<Mat3> qr(H);
92   //Mat3 K = qr.matrixQR().triangularView<Eigen::Upper>();
93   //Mat3 R = qr.householderQ();
94 
95   // Ensure that the diagonal is positive and R determinant == 1.
96   if (K(2,2) < 0) {
97     K = -K;
98     R = -R;
99   }
100   if (K(1,1) < 0) {
101     Mat3 S;
102     S << 1, 0, 0,
103          0,-1, 0,
104          0, 0, 1;
105     K = K * S;
106     R = S * R;
107   }
108   if (K(0,0) < 0) {
109     Mat3 S;
110     S << -1, 0, 0,
111           0, 1, 0,
112           0, 0, 1;
113     K = K * S;
114     R = S * R;
115   }
116 
117   // Compute translation.
118   Eigen::PartialPivLU<Mat3> lu(K);
119   Vec3 t = lu.solve(P.col(3));
120 
121   if (R.determinant()<0) {
122     R = -R;
123     t = -t;
124   }
125 
126   // scale K so that K(2,2) = 1
127   K = K / K(2,2);
128 
129   *Kp = K;
130   *Rp = R;
131   *tp = t;
132 }
133 
F_from_P(const Mat34 & P1,const Mat34 & P2)134 Mat3 F_from_P(const Mat34 & P1, const Mat34 & P2)
135 {
136   Mat3 F12;
137 
138   using Mat24 = Eigen::Matrix<double, 2, 4>;
139   Mat24 X1 = P1.block<2, 4>(1, 0);
140   Mat24 X2;  X2 << P1.row(2), P1.row(0);
141   Mat24 X3 = P1.block<2, 4>(0, 0);
142   Mat24 Y1 = P2.block<2, 4>(1, 0);
143   Mat24 Y2;  Y2 << P2.row(2), P2.row(0);
144   Mat24 Y3 = P2.block<2, 4>(0, 0);
145 
146 
147   Mat4 X1Y1, X2Y1, X3Y1, X1Y2, X2Y2, X3Y2, X1Y3, X2Y3, X3Y3;
148   X1Y1 << X1, Y1;  X2Y1 << X2, Y1;  X3Y1 << X3, Y1;
149   X1Y2 << X1, Y2;  X2Y2 << X2, Y2;  X3Y2 << X3, Y2;
150   X1Y3 << X1, Y3;  X2Y3 << X2, Y3;  X3Y3 << X3, Y3;
151 
152 
153   F12 <<
154     X1Y1.determinant(), X2Y1.determinant(), X3Y1.determinant(),
155     X1Y2.determinant(), X2Y2.determinant(), X3Y2.determinant(),
156     X1Y3.determinant(), X2Y3.determinant(), X3Y3.determinant();
157 
158   return F12;
159 }
160 
Project(const Mat34 & P,const Vec3 & X)161 Vec2 Project(const Mat34 &P, const Vec3 &X) {
162   return Vec3(P * X.homogeneous()).hnormalized();
163 }
164 
Project(const Mat34 & P,const Mat3X & X,Mat2X * x)165 void Project(const Mat34 &P, const Mat3X &X, Mat2X *x) {
166   x->resize(2, X.cols());
167   for (size_t c = 0; c < static_cast<size_t>(X.cols()); ++c) {
168     x->col(c) = Project(P, Vec3(X.col(c)));
169   }
170 }
171 
Project(const Mat34 & P,const Mat4X & X,Mat2X * x)172 void Project(const Mat34 &P, const Mat4X &X, Mat2X *x) {
173   x->resize(2, X.cols());
174   for (Mat4X::Index c = 0; c < X.cols(); ++c) {
175     const Vec3 hx = P * X.col(c);
176     x->col(c) = hx.hnormalized();
177   }
178 }
179 
Project(const Mat34 & P,const Mat3X & X)180 Mat2X Project(const Mat34 &P, const Mat3X &X) {
181   Mat2X x(2, X.cols());
182   Project(P, X, &x);
183   return x;
184 }
185 
Project(const Mat34 & P,const Mat4X & X)186 Mat2X Project(const Mat34 &P, const Mat4X &X) {
187   Mat2X x(2, X.cols());
188   Project(P, X, &x);
189   return x;
190 }
191 
Depth(const Mat3 & R,const Vec3 & t,const Vec3 & X)192 double Depth(const Mat3 &R, const Vec3 &t, const Vec3 &X) {
193   return (R*X)[2] + t[2];
194 }
195 
196 /// Estimates the root mean square error (2D)
RootMeanSquareError(const Mat2X & x_image,const Mat4X & X_world,const Mat34 & P)197 double RootMeanSquareError(const Mat2X &x_image,
198   const Mat4X &X_world,
199   const Mat34 &P) {
200     const Mat2X::Index num_points = x_image.cols();
201     const Mat2X dx = Project(P, X_world) - x_image;
202     return std::sqrt(dx.squaredNorm() / num_points);
203 }
204 
205 /// Estimates the root mean square error (2D)
RootMeanSquareError(const Mat2X & x_image,const Mat3X & X_world,const Mat3 & K,const Mat3 & R,const Vec3 & t)206 double RootMeanSquareError(const Mat2X &x_image,
207   const Mat3X &X_world,
208   const Mat3 &K,
209   const Mat3 &R,
210   const Vec3 &t) {
211     Mat34 P;
212     P_From_KRt(K, R, t, &P);
213     return RootMeanSquareError(x_image, X_world.colwise().homogeneous(), P);
214 }
215 
216 } // namespace openMVG
217