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