Path: blob/master/modules/calib3d/src/homography_decomp.cpp
16339 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// This is a homography decomposition implementation contributed to OpenCV3// by Samson Yilma. It implements the homography decomposition algorithm4// described in the research report:5// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition6// for vision-based control", Research Report 6303, INRIA (2007)7//8// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.9//10// By downloading, copying, installing or using the software you agree to this license.11// If you do not agree to this license, do not download, install,12// copy or use the software.13//14//15// License Agreement16// For Open Source Computer Vision Library17//18// Copyright (C) 2014, Samson Yilma ([email protected]), all rights reserved.19// Copyright (C) 2018, Intel Corporation, 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 documentation31// and/or other materials provided with the distribution.32//33// * The name of the copyright holders may not be used to endorse or promote products34// derived from this software without specific prior written permission.35//36// This software is provided by the copyright holders and contributors "as is" and37// any express or implied warranties, including, but not limited to, the implied38// 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 damages41// (including, but not limited to, procurement of substitute goods or services;42// loss of use, data, or profits; or business interruption) however caused43// and on any theory of liability, whether in contract, strict liability,44// or tort (including negligence or otherwise) arising in any way out of45// the use of this software, even if advised of the possibility of such damage.46//47//M*/4849#include "precomp.hpp"50#include <memory>5152namespace cv53{5455namespace HomographyDecomposition56{5758//struct to hold solutions of homography decomposition59typedef struct _CameraMotion {60cv::Matx33d R; //!< rotation matrix61cv::Vec3d n; //!< normal of the plane the camera is looking at62cv::Vec3d t; //!< translation vector63} CameraMotion;6465inline int signd(const double x)66{67return ( x >= 0 ? 1 : -1 );68}6970class HomographyDecomp {7172public:73HomographyDecomp() {}74virtual ~HomographyDecomp() {}75virtual void decomposeHomography(const cv::Matx33d& H, const cv::Matx33d& K,76std::vector<CameraMotion>& camMotions);77bool isRotationValid(const cv::Matx33d& R, const double epsilon=0.01);7879protected:80bool passesSameSideOfPlaneConstraint(CameraMotion& motion);81virtual void decompose(std::vector<CameraMotion>& camMotions) = 0;82const cv::Matx33d& getHnorm() const {83return _Hnorm;84}8586private:87/**88* Normalize the homograhpy \f$H\f$.89*90* @param H Homography matrix.91* @param K Intrinsic parameter matrix.92* @return It returns93* \f[94* K^{-1} * H * K95* \f]96*/97cv::Matx33d normalize(const cv::Matx33d& H, const cv::Matx33d& K);98void removeScale();99cv::Matx33d _Hnorm;100};101102class HomographyDecompZhang CV_FINAL : public HomographyDecomp {103104public:105HomographyDecompZhang():HomographyDecomp() {}106virtual ~HomographyDecompZhang() {}107108private:109virtual void decompose(std::vector<CameraMotion>& camMotions) CV_OVERRIDE;110bool findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion);111};112113class HomographyDecompInria CV_FINAL : public HomographyDecomp {114115public:116HomographyDecompInria():HomographyDecomp() {}117virtual ~HomographyDecompInria() {}118119private:120virtual void decompose(std::vector<CameraMotion>& camMotions) CV_OVERRIDE;121double oppositeOfMinor(const cv::Matx33d& M, const int row, const int col);122void findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R);123};124125// normalizes homography with intrinsic camera parameters126Matx33d HomographyDecomp::normalize(const Matx33d& H, const Matx33d& K)127{128return K.inv() * H * K;129}130131void HomographyDecomp::removeScale()132{133Mat W;134SVD::compute(_Hnorm, W);135_Hnorm = _Hnorm * (1.0/W.at<double>(1));136}137138/*! This checks that the input is a pure rotation matrix 'm'.139* The conditions for this are: R' * R = I and det(R) = 1 (proper rotation matrix)140*/141bool HomographyDecomp::isRotationValid(const Matx33d& R, const double epsilon)142{143Matx33d RtR = R.t() * R;144Matx33d I(1,0,0, 0,1,0, 0,0,1);145if (norm(RtR, I, NORM_INF) > epsilon)146return false;147return (fabs(determinant(R) - 1.0) < epsilon);148}149150bool HomographyDecomp::passesSameSideOfPlaneConstraint(CameraMotion& motion)151{152typedef Matx<double, 1, 1> Matx11d;153Matx31d t = Matx31d(motion.t);154Matx31d n = Matx31d(motion.n);155Matx11d proj = n.t() * motion.R.t() * t;156if ( (1 + proj(0, 0) ) <= 0 )157return false;158return true;159}160161//!main routine to decompose homography162void HomographyDecomp::decomposeHomography(const Matx33d& H, const cv::Matx33d& K,163std::vector<CameraMotion>& camMotions)164{165//normalize homography matrix with intrinsic camera matrix166_Hnorm = normalize(H, K);167//remove scale of the normalized homography168removeScale();169//apply decomposition170decompose(camMotions);171}172173/* function computes R&t from tstar, and plane normal(n) using174R = H * inv(I + tstar*transpose(n) );175t = R * tstar;176returns true if computed R&t is a valid solution177*/178bool HomographyDecompZhang::findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion)179{180Matx31d tstar_m = Mat(tstar);181Matx31d n_m = Mat(n);182Matx33d temp = tstar_m * n_m.t();183temp(0, 0) += 1.0;184temp(1, 1) += 1.0;185temp(2, 2) += 1.0;186motion.R = getHnorm() * temp.inv();187motion.t = motion.R * tstar;188motion.n = n;189return passesSameSideOfPlaneConstraint(motion);190}191192void HomographyDecompZhang::decompose(std::vector<CameraMotion>& camMotions)193{194Mat W, U, Vt;195SVD::compute(getHnorm(), W, U, Vt);196CV_Assert(W.total() > 2 && Vt.total() > 7);197double lambda1=W.at<double>(0);198double lambda3=W.at<double>(2);199double lambda1m3 = (lambda1-lambda3);200double lambda1m3_2 = lambda1m3*lambda1m3;201double lambda1t3 = lambda1*lambda3;202203double t1 = 1.0/(2.0*lambda1t3);204double t2 = sqrt(1.0+4.0*lambda1t3/lambda1m3_2);205double t12 = t1*t2;206207double e1 = -t1 + t12; //t1*(-1.0f + t2 );208double e3 = -t1 - t12; //t1*(-1.0f - t2);209double e1_2 = e1*e1;210double e3_2 = e3*e3;211212double nv1p = sqrt(e1_2*lambda1m3_2 + 2*e1*(lambda1t3-1) + 1.0);213double nv3p = sqrt(e3_2*lambda1m3_2 + 2*e3*(lambda1t3-1) + 1.0);214double v1p[3], v3p[3];215216v1p[0]=Vt.at<double>(0)*nv1p, v1p[1]=Vt.at<double>(1)*nv1p, v1p[2]=Vt.at<double>(2)*nv1p;217v3p[0]=Vt.at<double>(6)*nv3p, v3p[1]=Vt.at<double>(7)*nv3p, v3p[2]=Vt.at<double>(8)*nv3p;218219/*The eight solutions are220(A): tstar = +- (v1p - v3p)/(e1 -e3), n = +- (e1*v3p - e3*v1p)/(e1-e3)221(B): tstar = +- (v1p + v3p)/(e1 -e3), n = +- (e1*v3p + e3*v1p)/(e1-e3)222*/223double v1pmv3p[3], v1ppv3p[3];224double e1v3me3v1[3], e1v3pe3v1[3];225double inv_e1me3 = 1.0/(e1-e3);226227for(int kk=0;kk<3;++kk){228v1pmv3p[kk] = v1p[kk]-v3p[kk];229v1ppv3p[kk] = v1p[kk]+v3p[kk];230}231232for(int kk=0; kk<3; ++kk){233double e1v3 = e1*v3p[kk];234double e3v1=e3*v1p[kk];235e1v3me3v1[kk] = e1v3-e3v1;236e1v3pe3v1[kk] = e1v3+e3v1;237}238239Vec3d tstar_p, tstar_n;240Vec3d n_p, n_n;241242///Solution group A243for(int kk=0; kk<3; ++kk) {244tstar_p[kk] = v1pmv3p[kk]*inv_e1me3;245tstar_n[kk] = -tstar_p[kk];246n_p[kk] = e1v3me3v1[kk]*inv_e1me3;247n_n[kk] = -n_p[kk];248}249250CameraMotion cmotion;251//(A) Four different combinations for solution A252// (i) (+, +)253if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion))254camMotions.push_back(cmotion);255256// (ii) (+, -)257if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion))258camMotions.push_back(cmotion);259260// (iii) (-, +)261if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion))262camMotions.push_back(cmotion);263264// (iv) (-, -)265if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion))266camMotions.push_back(cmotion);267//////////////////////////////////////////////////////////////////268///Solution group B269for(int kk=0;kk<3;++kk){270tstar_p[kk] = v1ppv3p[kk]*inv_e1me3;271tstar_n[kk] = -tstar_p[kk];272n_p[kk] = e1v3pe3v1[kk]*inv_e1me3;273n_n[kk] = -n_p[kk];274}275276//(B) Four different combinations for solution B277// (i) (+, +)278if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion))279camMotions.push_back(cmotion);280281// (ii) (+, -)282if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion))283camMotions.push_back(cmotion);284285// (iii) (-, +)286if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion))287camMotions.push_back(cmotion);288289// (iv) (-, -)290if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion))291camMotions.push_back(cmotion);292}293294double HomographyDecompInria::oppositeOfMinor(const Matx33d& M, const int row, const int col)295{296int x1 = col == 0 ? 1 : 0;297int x2 = col == 2 ? 1 : 2;298int y1 = row == 0 ? 1 : 0;299int y2 = row == 2 ? 1 : 2;300301return (M(y1, x2) * M(y2, x1) - M(y1, x1) * M(y2, x2));302}303304//computes R = H( I - (2/v)*te_star*ne_t )305void HomographyDecompInria::findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R)306{307Matx31d tstar_m = Matx31d(tstar);308Matx31d n_m = Matx31d(n);309Matx33d I(1.0, 0.0, 0.0,3100.0, 1.0, 0.0,3110.0, 0.0, 1.0);312313R = getHnorm() * (I - (2/v) * tstar_m * n_m.t() );314}315316void HomographyDecompInria::decompose(std::vector<CameraMotion>& camMotions)317{318const double epsilon = 0.001;319Matx33d S;320321//S = H'H - I322S = getHnorm().t() * getHnorm();323S(0, 0) -= 1.0;324S(1, 1) -= 1.0;325S(2, 2) -= 1.0;326327//check if H is rotation matrix328if( norm(S, NORM_INF) < epsilon) {329CameraMotion motion;330motion.R = Matx33d(getHnorm());331motion.t = Vec3d(0, 0, 0);332motion.n = Vec3d(0, 0, 0);333camMotions.push_back(motion);334return;335}336337//! Compute nvectors338Vec3d npa, npb;339340double M00 = oppositeOfMinor(S, 0, 0);341double M11 = oppositeOfMinor(S, 1, 1);342double M22 = oppositeOfMinor(S, 2, 2);343344double rtM00 = sqrt(M00);345double rtM11 = sqrt(M11);346double rtM22 = sqrt(M22);347348double M01 = oppositeOfMinor(S, 0, 1);349double M12 = oppositeOfMinor(S, 1, 2);350double M02 = oppositeOfMinor(S, 0, 2);351352int e12 = signd(M12);353int e02 = signd(M02);354int e01 = signd(M01);355356double nS00 = abs(S(0, 0));357double nS11 = abs(S(1, 1));358double nS22 = abs(S(2, 2));359360//find max( |Sii| ), i=0, 1, 2361int indx = 0;362if(nS00 < nS11){363indx = 1;364if( nS11 < nS22 )365indx = 2;366}367else {368if(nS00 < nS22 )369indx = 2;370}371372switch (indx) {373case 0:374npa[0] = S(0, 0), npb[0] = S(0, 0);375npa[1] = S(0, 1) + rtM22, npb[1] = S(0, 1) - rtM22;376npa[2] = S(0, 2) + e12 * rtM11, npb[2] = S(0, 2) - e12 * rtM11;377break;378case 1:379npa[0] = S(0, 1) + rtM22, npb[0] = S(0, 1) - rtM22;380npa[1] = S(1, 1), npb[1] = S(1, 1);381npa[2] = S(1, 2) - e02 * rtM00, npb[2] = S(1, 2) + e02 * rtM00;382break;383case 2:384npa[0] = S(0, 2) + e01 * rtM11, npb[0] = S(0, 2) - e01 * rtM11;385npa[1] = S(1, 2) + rtM00, npb[1] = S(1, 2) - rtM00;386npa[2] = S(2, 2), npb[2] = S(2, 2);387break;388default:389break;390}391392double traceS = S(0, 0) + S(1, 1) + S(2, 2);393double v = 2.0 * sqrt(1 + traceS - M00 - M11 - M22);394395double ESii = signd(S(indx, indx)) ;396double r_2 = 2 + traceS + v;397double nt_2 = 2 + traceS - v;398399double r = sqrt(r_2);400double n_t = sqrt(nt_2);401402Vec3d na = npa / norm(npa);403Vec3d nb = npb / norm(npb);404405double half_nt = 0.5 * n_t;406double esii_t_r = ESii * r;407408Vec3d ta_star = half_nt * (esii_t_r * nb - n_t * na);409Vec3d tb_star = half_nt * (esii_t_r * na - n_t * nb);410411camMotions.resize(4);412413Matx33d Ra, Rb;414Vec3d ta, tb;415416//Ra, ta, na417findRmatFrom_tstar_n(ta_star, na, v, Ra);418ta = Ra * ta_star;419420camMotions[0].R = Ra;421camMotions[0].t = ta;422camMotions[0].n = na;423424//Ra, -ta, -na425camMotions[1].R = Ra;426camMotions[1].t = -ta;427camMotions[1].n = -na;428429//Rb, tb, nb430findRmatFrom_tstar_n(tb_star, nb, v, Rb);431tb = Rb * tb_star;432433camMotions[2].R = Rb;434camMotions[2].t = tb;435camMotions[2].n = nb;436437//Rb, -tb, -nb438camMotions[3].R = Rb;439camMotions[3].t = -tb;440camMotions[3].n = -nb;441}442443} //namespace HomographyDecomposition444445// function decomposes image-to-image homography to rotation and translation matrices446int decomposeHomographyMat(InputArray _H,447InputArray _K,448OutputArrayOfArrays _rotations,449OutputArrayOfArrays _translations,450OutputArrayOfArrays _normals)451{452using namespace std;453using namespace HomographyDecomposition;454455Mat H = _H.getMat().reshape(1, 3);456CV_Assert(H.cols == 3 && H.rows == 3);457458Mat K = _K.getMat().reshape(1, 3);459CV_Assert(K.cols == 3 && K.rows == 3);460461cv::Ptr<HomographyDecomp> hdecomp(new HomographyDecompInria);462463vector<CameraMotion> motions;464hdecomp->decomposeHomography(H, K, motions);465466int nsols = static_cast<int>(motions.size());467int depth = CV_64F; //double precision matrices used in CameraMotion struct468469if (_rotations.needed()) {470_rotations.create(nsols, 1, depth);471for (int k = 0; k < nsols; ++k ) {472_rotations.getMatRef(k) = Mat(motions[k].R);473}474}475476if (_translations.needed()) {477_translations.create(nsols, 1, depth);478for (int k = 0; k < nsols; ++k ) {479_translations.getMatRef(k) = Mat(motions[k].t);480}481}482483if (_normals.needed()) {484_normals.create(nsols, 1, depth);485for (int k = 0; k < nsols; ++k ) {486_normals.getMatRef(k) = Mat(motions[k].n);487}488}489490return nsols;491}492493void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,494InputArrayOfArrays _normals,495InputArray _beforeRectifiedPoints,496InputArray _afterRectifiedPoints,497OutputArray _possibleSolutions,498InputArray _pointsMask)499{500CV_Assert(_beforeRectifiedPoints.type() == CV_32FC2 && _afterRectifiedPoints.type() == CV_32FC2);501CV_Assert(_pointsMask.empty() || _pointsMask.type() == CV_8U);502503Mat beforeRectifiedPoints = _beforeRectifiedPoints.getMat();504Mat afterRectifiedPoints = _afterRectifiedPoints.getMat();505Mat pointsMask = _pointsMask.getMat();506int nsolutions = (int)_rotations.total();507int npoints = (int)beforeRectifiedPoints.total();508CV_Assert(pointsMask.empty() || pointsMask.checkVector(1, CV_8U) == npoints);509const uchar* pointsMaskPtr = pointsMask.data;510511std::vector<uchar> solutionMask(nsolutions, (uchar)1);512std::vector<Mat> normals(nsolutions);513std::vector<Mat> rotnorm(nsolutions);514Mat R;515516for( int i = 0; i < nsolutions; i++ )517{518_normals.getMat(i).convertTo(normals[i], CV_64F);519CV_Assert(normals[i].total() == 3);520_rotations.getMat(i).convertTo(R, CV_64F);521rotnorm[i] = R*normals[i];522CV_Assert(rotnorm[i].total() == 3);523}524525for( int j = 0; j < npoints; j++ )526{527if( !pointsMaskPtr || pointsMaskPtr[j] )528{529Point2f prevPoint = beforeRectifiedPoints.at<Point2f>(j);530Point2f currPoint = afterRectifiedPoints.at<Point2f>(j);531532for( int i = 0; i < nsolutions; i++ )533{534if( !solutionMask[i] )535continue;536537const double* normal_i = normals[i].ptr<double>();538const double* rotnorm_i = rotnorm[i].ptr<double>();539double prevNormDot = normal_i[0]*prevPoint.x + normal_i[1]*prevPoint.y + normal_i[2];540double currNormDot = rotnorm_i[0]*currPoint.x + rotnorm_i[1]*currPoint.y + rotnorm_i[2];541542if (prevNormDot <= 0 || currNormDot <= 0)543solutionMask[i] = (uchar)0;544}545}546}547548std::vector<int> possibleSolutions;549for( int i = 0; i < nsolutions; i++ )550if( solutionMask[i] )551possibleSolutions.push_back(i);552553Mat(possibleSolutions).copyTo(_possibleSolutions);554}555556} //namespace cv557558559