• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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