1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // This is a homography decomposition implementation contributed to OpenCV
4 // by Samson Yilma. It implements the homography decomposition algorithm
5 // descriped in the research report:
6 // Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
7 // for vision-based control", Research Report 6303, INRIA (2007)
8 //
9 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
10 //
11 // By downloading, copying, installing or using the software you agree to this license.
12 // If you do not agree to this license, do not download, install,
13 // copy or use the software.
14 //
15 //
16 // License Agreement
17 // For Open Source Computer Vision Library
18 //
19 // Copyright (C) 2014, Samson Yilma¸ (samson_yilma@yahoo.com), all rights reserved.
20 //
21 // Third party copyrights are property of their respective owners.
22 //
23 // Redistribution and use in source and binary forms, with or without modification,
24 // are permitted provided that the following conditions are met:
25 //
26 // * Redistribution's of source code must retain the above copyright notice,
27 // this list of conditions and the following disclaimer.
28 //
29 // * Redistribution's in binary form must reproduce the above copyright notice,
30 // this list of conditions and the following disclaimer in the documentation
31 // and/or other materials provided with the distribution.
32 //
33 // * The name of the copyright holders may not be used to endorse or promote products
34 // derived from this software without specific prior written permission.
35 //
36 // This software is provided by the copyright holders and contributors "as is" and
37 // any express or implied warranties, including, but not limited to, the implied
38 // warranties of merchantability and fitness for a particular purpose are disclaimed.
39 // In no event shall the Intel Corporation or contributors be liable for any direct,
40 // indirect, incidental, special, exemplary, or consequential damages
41 // (including, but not limited to, procurement of substitute goods or services;
42 // loss of use, data, or profits; or business interruption) however caused
43 // and on any theory of liability, whether in contract, strict liability,
44 // or tort (including negligence or otherwise) arising in any way out of
45 // the use of this software, even if advised of the possibility of such damage.
46 //
47 //M*/
48
49 #include "precomp.hpp"
50 #include <memory>
51
52 namespace cv
53 {
54
55 namespace HomographyDecomposition
56 {
57
58 //struct to hold solutions of homography decomposition
59 typedef struct _CameraMotion {
60 cv::Matx33d R; //!< rotation matrix
61 cv::Vec3d n; //!< normal of the plane the camera is looking at
62 cv::Vec3d t; //!< translation vector
63 } CameraMotion;
64
signd(const double x)65 inline int signd(const double x)
66 {
67 return ( x >= 0 ? 1 : -1 );
68 }
69
70 class HomographyDecomp {
71
72 public:
HomographyDecomp()73 HomographyDecomp() {}
~HomographyDecomp()74 virtual ~HomographyDecomp() {}
75 virtual void decomposeHomography(const cv::Matx33d& H, const cv::Matx33d& K,
76 std::vector<CameraMotion>& camMotions);
77 bool isRotationValid(const cv::Matx33d& R, const double epsilon=0.01);
78
79 protected:
80 bool passesSameSideOfPlaneConstraint(CameraMotion& motion);
81 virtual void decompose(std::vector<CameraMotion>& camMotions) = 0;
getHnorm() const82 const cv::Matx33d& getHnorm() const {
83 return _Hnorm;
84 }
85
86 private:
87 cv::Matx33d normalize(const cv::Matx33d& H, const cv::Matx33d& K);
88 void removeScale();
89 cv::Matx33d _Hnorm;
90 };
91
92 class HomographyDecompZhang : public HomographyDecomp {
93
94 public:
HomographyDecompZhang()95 HomographyDecompZhang():HomographyDecomp() {}
~HomographyDecompZhang()96 virtual ~HomographyDecompZhang() {}
97
98 private:
99 virtual void decompose(std::vector<CameraMotion>& camMotions);
100 bool findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion);
101 };
102
103 class HomographyDecompInria : public HomographyDecomp {
104
105 public:
HomographyDecompInria()106 HomographyDecompInria():HomographyDecomp() {}
~HomographyDecompInria()107 virtual ~HomographyDecompInria() {}
108
109 private:
110 virtual void decompose(std::vector<CameraMotion>& camMotions);
111 double oppositeOfMinor(const cv::Matx33d& M, const int row, const int col);
112 void findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R);
113 };
114
115 // normalizes homography with intrinsic camera parameters
normalize(const Matx33d & H,const Matx33d & K)116 Matx33d HomographyDecomp::normalize(const Matx33d& H, const Matx33d& K)
117 {
118 return K.inv() * H * K;
119 }
120
removeScale()121 void HomographyDecomp::removeScale()
122 {
123 Mat W;
124 SVD::compute(_Hnorm, W);
125 _Hnorm = _Hnorm * (1.0/W.at<double>(1));
126 }
127
128 /*! This checks that the input is a pure rotation matrix 'm'.
129 * The conditions for this are: R' * R = I and det(R) = 1 (proper rotation matrix)
130 */
isRotationValid(const Matx33d & R,const double epsilon)131 bool HomographyDecomp::isRotationValid(const Matx33d& R, const double epsilon)
132 {
133 Matx33d RtR = R.t() * R;
134 Matx33d I(1,0,0, 0,1,0, 0,0,1);
135 if (norm(RtR, I, NORM_INF) > epsilon)
136 return false;
137 return (fabs(determinant(R) - 1.0) < epsilon);
138 }
139
passesSameSideOfPlaneConstraint(CameraMotion & motion)140 bool HomographyDecomp::passesSameSideOfPlaneConstraint(CameraMotion& motion)
141 {
142 typedef Matx<double, 1, 1> Matx11d;
143 Matx31d t = Matx31d(motion.t);
144 Matx31d n = Matx31d(motion.n);
145 Matx11d proj = n.t() * motion.R.t() * t;
146 if ( (1 + proj(0, 0) ) <= 0 )
147 return false;
148 return true;
149 }
150
151 //!main routine to decompose homography
decomposeHomography(const Matx33d & H,const cv::Matx33d & K,std::vector<CameraMotion> & camMotions)152 void HomographyDecomp::decomposeHomography(const Matx33d& H, const cv::Matx33d& K,
153 std::vector<CameraMotion>& camMotions)
154 {
155 //normalize homography matrix with intrinsic camera matrix
156 _Hnorm = normalize(H, K);
157 //remove scale of the normalized homography
158 removeScale();
159 //apply decomposition
160 decompose(camMotions);
161 }
162
163 /* function computes R&t from tstar, and plane normal(n) using
164 R = H * inv(I + tstar*transpose(n) );
165 t = R * tstar;
166 returns true if computed R&t is a valid solution
167 */
findMotionFrom_tstar_n(const cv::Vec3d & tstar,const cv::Vec3d & n,CameraMotion & motion)168 bool HomographyDecompZhang::findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion)
169 {
170 Matx31d tstar_m = Mat(tstar);
171 Matx31d n_m = Mat(n);
172 Matx33d temp = tstar_m * n_m.t();
173 temp(0, 0) += 1.0;
174 temp(1, 1) += 1.0;
175 temp(2, 2) += 1.0;
176 motion.R = getHnorm() * temp.inv();
177 motion.t = motion.R * tstar;
178 motion.n = n;
179 return passesSameSideOfPlaneConstraint(motion);
180 }
181
decompose(std::vector<CameraMotion> & camMotions)182 void HomographyDecompZhang::decompose(std::vector<CameraMotion>& camMotions)
183 {
184 Mat W, U, Vt;
185 SVD::compute(getHnorm(), W, U, Vt);
186 double lambda1=W.at<double>(0);
187 double lambda3=W.at<double>(2);
188 double lambda1m3 = (lambda1-lambda3);
189 double lambda1m3_2 = lambda1m3*lambda1m3;
190 double lambda1t3 = lambda1*lambda3;
191
192 double t1 = 1.0/(2.0*lambda1t3);
193 double t2 = sqrt(1.0+4.0*lambda1t3/lambda1m3_2);
194 double t12 = t1*t2;
195
196 double e1 = -t1 + t12; //t1*(-1.0f + t2 );
197 double e3 = -t1 - t12; //t1*(-1.0f - t2);
198 double e1_2 = e1*e1;
199 double e3_2 = e3*e3;
200
201 double nv1p = sqrt(e1_2*lambda1m3_2 + 2*e1*(lambda1t3-1) + 1.0);
202 double nv3p = sqrt(e3_2*lambda1m3_2 + 2*e3*(lambda1t3-1) + 1.0);
203 double v1p[3], v3p[3];
204
205 v1p[0]=Vt.at<double>(0)*nv1p, v1p[1]=Vt.at<double>(1)*nv1p, v1p[2]=Vt.at<double>(2)*nv1p;
206 v3p[0]=Vt.at<double>(6)*nv3p, v3p[1]=Vt.at<double>(7)*nv3p, v3p[2]=Vt.at<double>(8)*nv3p;
207
208 /*The eight solutions are
209 (A): tstar = +- (v1p - v3p)/(e1 -e3), n = +- (e1*v3p - e3*v1p)/(e1-e3)
210 (B): tstar = +- (v1p + v3p)/(e1 -e3), n = +- (e1*v3p + e3*v1p)/(e1-e3)
211 */
212 double v1pmv3p[3], v1ppv3p[3];
213 double e1v3me3v1[3], e1v3pe3v1[3];
214 double inv_e1me3 = 1.0/(e1-e3);
215
216 for(int kk=0;kk<3;++kk){
217 v1pmv3p[kk] = v1p[kk]-v3p[kk];
218 v1ppv3p[kk] = v1p[kk]+v3p[kk];
219 }
220
221 for(int kk=0; kk<3; ++kk){
222 double e1v3 = e1*v3p[kk];
223 double e3v1=e3*v1p[kk];
224 e1v3me3v1[kk] = e1v3-e3v1;
225 e1v3pe3v1[kk] = e1v3+e3v1;
226 }
227
228 Vec3d tstar_p, tstar_n;
229 Vec3d n_p, n_n;
230
231 ///Solution group A
232 for(int kk=0; kk<3; ++kk) {
233 tstar_p[kk] = v1pmv3p[kk]*inv_e1me3;
234 tstar_n[kk] = -tstar_p[kk];
235 n_p[kk] = e1v3me3v1[kk]*inv_e1me3;
236 n_n[kk] = -n_p[kk];
237 }
238
239 CameraMotion cmotion;
240 //(A) Four different combinations for solution A
241 // (i) (+, +)
242 if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion))
243 camMotions.push_back(cmotion);
244
245 // (ii) (+, -)
246 if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion))
247 camMotions.push_back(cmotion);
248
249 // (iii) (-, +)
250 if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion))
251 camMotions.push_back(cmotion);
252
253 // (iv) (-, -)
254 if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion))
255 camMotions.push_back(cmotion);
256 //////////////////////////////////////////////////////////////////
257 ///Solution group B
258 for(int kk=0;kk<3;++kk){
259 tstar_p[kk] = v1ppv3p[kk]*inv_e1me3;
260 tstar_n[kk] = -tstar_p[kk];
261 n_p[kk] = e1v3pe3v1[kk]*inv_e1me3;
262 n_n[kk] = -n_p[kk];
263 }
264
265 //(B) Four different combinations for solution B
266 // (i) (+, +)
267 if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion))
268 camMotions.push_back(cmotion);
269
270 // (ii) (+, -)
271 if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion))
272 camMotions.push_back(cmotion);
273
274 // (iii) (-, +)
275 if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion))
276 camMotions.push_back(cmotion);
277
278 // (iv) (-, -)
279 if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion))
280 camMotions.push_back(cmotion);
281 }
282
oppositeOfMinor(const Matx33d & M,const int row,const int col)283 double HomographyDecompInria::oppositeOfMinor(const Matx33d& M, const int row, const int col)
284 {
285 int x1 = col == 0 ? 1 : 0;
286 int x2 = col == 2 ? 1 : 2;
287 int y1 = row == 0 ? 1 : 0;
288 int y2 = row == 2 ? 1 : 2;
289
290 return (M(y1, x2) * M(y2, x1) - M(y1, x1) * M(y2, x2));
291 }
292
293 //computes R = H( I - (2/v)*te_star*ne_t )
findRmatFrom_tstar_n(const cv::Vec3d & tstar,const cv::Vec3d & n,const double v,cv::Matx33d & R)294 void HomographyDecompInria::findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R)
295 {
296 Matx31d tstar_m = Matx31d(tstar);
297 Matx31d n_m = Matx31d(n);
298 Matx33d I(1.0, 0.0, 0.0,
299 0.0, 1.0, 0.0,
300 0.0, 0.0, 1.0);
301
302 R = getHnorm() * (I - (2/v) * tstar_m * n_m.t() );
303 }
304
decompose(std::vector<CameraMotion> & camMotions)305 void HomographyDecompInria::decompose(std::vector<CameraMotion>& camMotions)
306 {
307 const double epsilon = 0.001;
308 Matx33d S;
309
310 //S = H'H - I
311 S = getHnorm().t() * getHnorm();
312 S(0, 0) -= 1.0;
313 S(1, 1) -= 1.0;
314 S(2, 2) -= 1.0;
315
316 //check if H is rotation matrix
317 if( norm(S, NORM_INF) < epsilon) {
318 CameraMotion motion;
319 motion.R = Matx33d(getHnorm());
320 motion.t = Vec3d(0, 0, 0);
321 motion.n = Vec3d(0, 0, 0);
322 camMotions.push_back(motion);
323 return;
324 }
325
326 //! Compute nvectors
327 Vec3d npa, npb;
328
329 double M00 = oppositeOfMinor(S, 0, 0);
330 double M11 = oppositeOfMinor(S, 1, 1);
331 double M22 = oppositeOfMinor(S, 2, 2);
332
333 double rtM00 = sqrt(M00);
334 double rtM11 = sqrt(M11);
335 double rtM22 = sqrt(M22);
336
337 double M01 = oppositeOfMinor(S, 0, 1);
338 double M12 = oppositeOfMinor(S, 1, 2);
339 double M02 = oppositeOfMinor(S, 0, 2);
340
341 int e12 = signd(M12);
342 int e02 = signd(M02);
343 int e01 = signd(M01);
344
345 double nS00 = abs(S(0, 0));
346 double nS11 = abs(S(1, 1));
347 double nS22 = abs(S(2, 2));
348
349 //find max( |Sii| ), i=0, 1, 2
350 int indx = 0;
351 if(nS00 < nS11){
352 indx = 1;
353 if( nS11 < nS22 )
354 indx = 2;
355 }
356 else {
357 if(nS00 < nS22 )
358 indx = 2;
359 }
360
361 switch (indx) {
362 case 0:
363 npa[0] = S(0, 0), npb[0] = S(0, 0);
364 npa[1] = S(0, 1) + rtM22, npb[1] = S(0, 1) - rtM22;
365 npa[2] = S(0, 2) + e12 * rtM11, npb[2] = S(0, 2) - e12 * rtM11;
366 break;
367 case 1:
368 npa[0] = S(0, 1) + rtM22, npb[0] = S(0, 1) - rtM22;
369 npa[1] = S(1, 1), npb[1] = S(1, 1);
370 npa[2] = S(1, 2) - e02 * rtM00, npb[2] = S(1, 2) + e02 * rtM00;
371 break;
372 case 2:
373 npa[0] = S(0, 2) + e01 * rtM11, npb[0] = S(0, 2) - e01 * rtM11;
374 npa[1] = S(1, 2) + rtM00, npb[1] = S(1, 2) - rtM00;
375 npa[2] = S(2, 2), npb[2] = S(2, 2);
376 break;
377 default:
378 break;
379 }
380
381 double traceS = S(0, 0) + S(1, 1) + S(2, 2);
382 double v = 2.0 * sqrt(1 + traceS - M00 - M11 - M22);
383
384 double ESii = signd(S(indx, indx)) ;
385 double r_2 = 2 + traceS + v;
386 double nt_2 = 2 + traceS - v;
387
388 double r = sqrt(r_2);
389 double n_t = sqrt(nt_2);
390
391 Vec3d na = npa / norm(npa);
392 Vec3d nb = npb / norm(npb);
393
394 double half_nt = 0.5 * n_t;
395 double esii_t_r = ESii * r;
396
397 Vec3d ta_star = half_nt * (esii_t_r * nb - n_t * na);
398 Vec3d tb_star = half_nt * (esii_t_r * na - n_t * nb);
399
400 camMotions.resize(4);
401
402 Matx33d Ra, Rb;
403 Vec3d ta, tb;
404
405 //Ra, ta, na
406 findRmatFrom_tstar_n(ta_star, na, v, Ra);
407 ta = Ra * ta_star;
408
409 camMotions[0].R = Ra;
410 camMotions[0].t = ta;
411 camMotions[0].n = na;
412
413 //Ra, -ta, -na
414 camMotions[1].R = Ra;
415 camMotions[1].t = -ta;
416 camMotions[1].n = -na;
417
418 //Rb, tb, nb
419 findRmatFrom_tstar_n(tb_star, nb, v, Rb);
420 tb = Rb * tb_star;
421
422 camMotions[2].R = Rb;
423 camMotions[2].t = tb;
424 camMotions[2].n = nb;
425
426 //Rb, -tb, -nb
427 camMotions[3].R = Rb;
428 camMotions[3].t = -tb;
429 camMotions[3].n = -nb;
430 }
431
432 } //namespace HomographyDecomposition
433
434 // function decomposes image-to-image homography to rotation and translation matrices
decomposeHomographyMat(InputArray _H,InputArray _K,OutputArrayOfArrays _rotations,OutputArrayOfArrays _translations,OutputArrayOfArrays _normals)435 int decomposeHomographyMat(InputArray _H,
436 InputArray _K,
437 OutputArrayOfArrays _rotations,
438 OutputArrayOfArrays _translations,
439 OutputArrayOfArrays _normals)
440 {
441 using namespace std;
442 using namespace HomographyDecomposition;
443
444 Mat H = _H.getMat().reshape(1, 3);
445 CV_Assert(H.cols == 3 && H.rows == 3);
446
447 Mat K = _K.getMat().reshape(1, 3);
448 CV_Assert(K.cols == 3 && K.rows == 3);
449
450 auto_ptr<HomographyDecomp> hdecomp(new HomographyDecompInria);
451
452 vector<CameraMotion> motions;
453 hdecomp->decomposeHomography(H, K, motions);
454
455 int nsols = static_cast<int>(motions.size());
456 int depth = CV_64F; //double precision matrices used in CameraMotion struct
457
458 if (_rotations.needed()) {
459 _rotations.create(nsols, 1, depth);
460 for (int k = 0; k < nsols; ++k ) {
461 _rotations.getMatRef(k) = Mat(motions[k].R);
462 }
463 }
464
465 if (_translations.needed()) {
466 _translations.create(nsols, 1, depth);
467 for (int k = 0; k < nsols; ++k ) {
468 _translations.getMatRef(k) = Mat(motions[k].t);
469 }
470 }
471
472 if (_normals.needed()) {
473 _normals.create(nsols, 1, depth);
474 for (int k = 0; k < nsols; ++k ) {
475 _normals.getMatRef(k) = Mat(motions[k].n);
476 }
477 }
478
479 return nsols;
480 }
481
482 } //namespace cv
483