/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2009, Intel Corporation and others, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" #include #include namespace cv { // correctMatches function is Copyright (C) 2009, Jostein Austvik Jacobsen. // triangulatePoints function is derived from reconstructPointsFor3View, originally by Valery Mosyagin. // HZ, R. Hartley and A. Zisserman, Multiple View Geometry in Computer Vision, Cambridge Univ. Press, 2003. // This method is the same as reconstructPointsFor3View, with only a few numbers adjusted for two-view geometry void triangulatePoints( InputArray _P1, InputArray _P2, InputArray _points1, InputArray _points2, OutputArray _points4D ) { CV_INSTRUMENT_REGION(); Mat points1 = _points1.getMat(), points2 = _points2.getMat(); int depth1 = points1.depth(), depth2 = points2.depth(); const float *p1f = depth1 == CV_32F ? points1.ptr() : 0; const float *p2f = depth2 == CV_32F ? points2.ptr() : 0; const double *p1d = depth1 == CV_64F ? points1.ptr() : 0; const double *p2d = depth2 == CV_64F ? points2.ptr() : 0; int pstep1, ystep1, pstep2, ystep2, npoints1, npoints2; CV_Assert(depth1 == depth2 && (depth1 == CV_32F || depth1 == CV_64F)); if ((points1.rows == 1 || points1.cols == 1) && points1.channels() == 2) { npoints1 = points1.rows + points1.cols - 1; ystep1 = 1; pstep1 = 2; } else { npoints1 = points1.cols; ystep1 = (int)(points1.step/points1.elemSize()); pstep1 = 1; } if ((points2.rows == 1 || points2.cols == 1) && points2.channels() == 2) { npoints2 = points2.rows + points2.cols - 1; ystep2 = 1; pstep2 = 2; } else { npoints2 = points2.cols; ystep2 = (int)(points2.step/points2.elemSize()); pstep2 = 1; } CV_Assert(npoints1 == npoints2); _points4D.create(4, npoints1, depth1); Mat points4D = _points4D.getMat(); Matx matrA; Matx matrU; Matx matrW; Matx matrV; size_t step4 = 4*sizeof(double); Matx P1; Matx P2; _P1.getMat().convertTo(P1, CV_64F); _P2.getMat().convertTo(P2, CV_64F); // Solve system for each point for( int i = 0; i < npoints1; i++ ) { // Fill matrix for current point double x1 = p1f ? (double)p1f[pstep1*i] : p1d[pstep1*i]; double y1 = p1f ? (double)p1f[pstep1*i + ystep1] : p1d[pstep1*i + ystep1]; double x2 = p2f ? (double)p2f[pstep2*i] : p2d[pstep2*i]; double y2 = p2f ? (double)p2f[pstep2*i + ystep2] : p2d[pstep2*i + ystep2]; for(int k = 0; k < 4; k++) { matrA(k, 0) = x1*P1(2, k) - P1(0, k); matrA(k, 1) = y1*P1(2, k) - P1(1, k); matrA(k, 2) = x2*P2(2, k) - P2(0, k); matrA(k, 3) = y2*P2(2, k) - P2(1, k); } // Solve system for current point hal::SVD64f(matrA.val, step4, matrW.val, matrU.val, step4, matrV.val, step4, 4, 4, 4); // Copy computed point if(depth1 == CV_32F) { points4D.at(0, i) = (float)matrV(3, 0); points4D.at(1, i) = (float)matrV(3, 1); points4D.at(2, i) = (float)matrV(3, 2); points4D.at(3, i) = (float)matrV(3, 3); } else { points4D.at(0, i) = matrV(3, 0); points4D.at(1, i) = matrV(3, 1); points4D.at(2, i) = matrV(3, 2); points4D.at(3, i) = matrV(3, 3); } } } /* * The Optimal Triangulation Method (see HZ for details) * For each given point correspondence points1[i] <-> points2[i], and a fundamental matrix F, * computes the corrected correspondences new_points1[i] <-> new_points2[i] that minimize the * geometric error d(points1[i],new_points1[i])^2 + d(points2[i],new_points2[i])^2 (where d(a,b) * is the geometric distance between points a and b) subject to the epipolar constraint * new_points2' * F * new_points1 = 0. * * _F : 3x3 fundamental matrix * _points1 : 1xN matrix containing the first set of points * _points2 : 1xN matrix containing the second set of points * _newPoints1 : the optimized _points1. * _newPoints2 : the optimized -points2. */ void correctMatches( InputArray _F, InputArray _points1, InputArray _points2, OutputArray _newPoints1, OutputArray _newPoints2 ) { CV_INSTRUMENT_REGION(); Mat points1 = _points1.getMat(), points2 = _points2.getMat(); int depth1 = points1.depth(), depth2 = points2.depth(); CV_Assert((depth1 == CV_32F || depth1 == CV_64F) && depth1 == depth2); CV_Assert(points1.size() == points2.size()); CV_Assert(points1.rows == 1 || points1.cols == 1); if (points1.channels() != 2) CV_Error( cv::Error::StsUnmatchedSizes, "The first set of points must contain two channels; one for x and one for y" ); if (points2.channels() != 2) CV_Error( cv::Error::StsUnmatchedSizes, "The second set of points must contain two channels; one for x and one for y" ); _newPoints1.create(points1.size(), points1.type()); _newPoints2.create(points2.size(), points2.type()); Mat newPoints1 = _newPoints1.getMat(), newPoints2 = _newPoints2.getMat(); Matx33d F, U, Vt; Matx31d S; int npoints = points1.rows + points1.cols - 1; // Make sure F uses double precision _F.getMat().convertTo(F, CV_64F); for (int p = 0; p < npoints; ++p) { // Replace F by T2-t * F * T1-t double x1, y1, x2, y2; if (depth1 == CV_32F) { Point2f p1 = points1.at(p); Point2f p2 = points2.at(p); x1 = p1.x; y1 = p1.y; x2 = p2.x; y2 = p2.y; } else { Point2d p1 = points1.at(p); Point2d p2 = points2.at(p); x1 = p1.x; y1 = p1.y; x2 = p2.x; y2 = p2.y; } Matx33d T1i(1, 0, x1, 0, 1, y1, 0, 0, 1); Matx33d T2i(1, 0, x2, 0, 1, y2, 0, 0, 1); Matx33d TFT = T2i.t()*F*T1i; // Compute the right epipole e1 from F * e1 = 0 SVDecomp(TFT, S, U, Vt); double scale = sqrt(Vt(2, 0)*Vt(2, 0) + Vt(2, 1)*Vt(2, 1)); Vec3d e1(Vt(2, 0)/scale, Vt(2, 1)/scale, Vt(2, 2)/scale); if (e1(2) < 0) e1 = -e1; // Compute the left epipole e2 from e2' * F = 0 => F' * e2 = 0 scale = sqrt(U(0, 2)*U(0, 2) + U(1, 2)*U(1, 2)); Vec3d e2(U(0, 2)/scale, U(1, 2)/scale, U(2, 2)/scale); if (e2(2) < 0) e2 = -e2; // Replace F by R2 * F * R1' Matx33d R1_t(e1(0), -e1(1), 0, e1(1), e1(0), 0, 0, 0, 1); Matx33d R2(e2(0), e2(1), 0, -e2(1), e2(0), 0, 0, 0, 1); Matx33d RTFTR = R2*TFT*R1_t; // Set f1 = e1(3), f2 = e2(3), a = F22, b = F23, c = F32, d = F33 double f1 = e1(2); double f2 = e2(2); double a = RTFTR(1,1); double b = RTFTR(1,2); double c = RTFTR(2,1); double d = RTFTR(2,2); // Form the polynomial g(t) = k6*t^6 + k5*t^5 + k4*t^4 + k3*t^3 + k2*t^2 + k1*t + k0 // from f1, f2, a, b, c and d Vec polynomial( -a*d*d*b+b*b*c*d, +f2*f2*f2*f2*d*d*d*d+b*b*b*b+2*b*b*f2*f2*d*d-a*a*d*d+b*b*c*c, +4*a*b*b*b+4*b*b*f2*f2*c*d+4*f2*f2*f2*f2*c*d*d*d-a*a*d*c+b*c*c*a+4*a*b*f2*f2*d*d-2*a*d*d*f1*f1*b+2*b*b*c*f1*f1*d, +6*a*a*b*b+6*f2*f2*f2*f2*c*c*d*d+2*b*b*f2*f2*c*c+2*a*a*f2*f2*d*d-2*a*a*d*d*f1*f1+2*b*b*c*c*f1*f1+8*a*b*f2*f2*c*d, +4*a*a*a*b+2*b*c*c*f1*f1*a+4*f2*f2*f2*f2*c*c*c*d+4*a*b*f2*f2*c*c+4*a*a*f2*f2*c*d-2*a*a*d*f1*f1*c-a*d*d*f1*f1*f1*f1*b+b*b*c*f1*f1*f1*f1*d, +f2*f2*f2*f2*c*c*c*c+2*a*a*f2*f2*c*c-a*a*d*d*f1*f1*f1*f1+b*b*c*c*f1*f1*f1*f1+a*a*a*a, +b*c*c*f1*f1*f1*f1*a-a*a*d*f1*f1*f1*f1*c); // Solve g(t) for t to get 6 roots double rdata[6*2]; Mat result(6, 1, CV_64FC2, rdata); solvePoly(polynomial, result); // Evaluate the cost function s(t) at the real part of the 6 roots double t_min = DBL_MAX; double s_val = 1./(f1*f1) + (c*c)/(a*a+f2*f2*c*c); for (int ti = 0; ti < 6; ++ti) { Vec2d root_i = result.at(ti); double t = root_i(0); double s = (t*t)/(1 + f1*f1*t*t) + ((c*t + d)*(c*t + d))/((a*t + b)*(a*t + b) + f2*f2*(c*t + d)*(c*t + d)); if (s < s_val) { s_val = s; t_min = t; } } // find the optimal x1 and y1 as the points on l1 and l2 closest to the origin scale = t_min*t_min*f1*f1+1; Vec3d tmp31(t_min*t_min*f1/scale, t_min/scale, 1); Vec3d tmp31_2 = T1i*(R1_t*tmp31); x1 = tmp31_2(0); y1 = tmp31_2(1); scale = f2*f2*(c*t_min+d)*(c*t_min+d) + (a*t_min+b)*(a*t_min+b); tmp31 = Vec3d(f2*(c*t_min+d)*(c*t_min+d)/scale, -(a*t_min+b)*(c*t_min+d)/scale, 1); tmp31_2 = T2i*(R2.t()*tmp31); x2 = tmp31_2(0); y2 = tmp31_2(1); // Return the points in the matrix format that the user wants if (depth1 == CV_32F) { newPoints1.at(p) = Point2f((float)x1, (float)y1); newPoints2.at(p) = Point2f((float)x2, (float)y2); } else { newPoints1.at(p) = Point2d(x1, y1); newPoints2.at(p) = Point2d(x2, y2); } } } }