All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.alg.geo.f.EpipolarMinimizeGeometricError Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 1.1.7
Show newest version
/*
 * Copyright (c) 2011-2018, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.geo.f;

import boofcv.alg.geo.PerspectiveOps;
import georegression.geometry.GeometryMath_F64;
import georegression.struct.point.Point2D_F64;
import georegression.struct.point.Point3D_F64;
import georegression.struct.point.Vector3D_F64;
import org.ddogleg.solver.Polynomial;
import org.ddogleg.solver.PolynomialOps;
import org.ddogleg.solver.PolynomialRoots;
import org.ddogleg.solver.RootFinderType;
import org.ejml.data.Complex_F64;
import org.ejml.data.DMatrixRMaj;

import java.util.List;

import static boofcv.misc.BoofMiscOps.pow2;

/**
 * Given point correspondences x[1] and x[2] and a fundamental matrix F, compute the
 * correspondences x'[1] and x'[2] which minimize the geometric error
 * subject to x'[2] F' x'[1] = 0
 *
 * 

* Page 318 in: R. Hartley, and A. Zisserman, "Multiple View Geometry in Computer Vision", 2nd Ed, Cambridge 2003 *

* * @author Peter Abeles */ public class EpipolarMinimizeGeometricError { // Ft = inv(T2')*F*inv(T1) DMatrixRMaj Ft = new DMatrixRMaj(3,3); // [1 0 -x1;0 1 -y1; 0 0 1]; DMatrixRMaj T1 = new DMatrixRMaj(3,3); // [1 0 -x2;0 1 -y2; 0 0 1]; DMatrixRMaj T2 = new DMatrixRMaj(3,3); FundamentalExtractEpipoles extract = new FundamentalExtractEpipoles(); Point3D_F64 e1 = new Point3D_F64(); Point3D_F64 e2 = new Point3D_F64(); // rotation matrices DMatrixRMaj R1 = new DMatrixRMaj(3,3); DMatrixRMaj R2 = new DMatrixRMaj(3,3); // lines Vector3D_F64 l1 = new Vector3D_F64(); Vector3D_F64 l2 = new Vector3D_F64(); PolynomialRoots rootFinder = PolynomialOps.createRootFinder(6, RootFinderType.EVD); Polynomial poly = new Polynomial(6); double solutionT; /** * Minimizes the geometric error * * @param F21 (Input) Fundamental matrix x2 * F21 * x1 == 0 * @param x1 (Input) Point 1 x-coordinate. Pixels * @param y1 (Input) Point 1 y-coordinate. Pixels * @param x2 (Input) Point 2 x-coordinate. Pixels * @param y2 (Input) Point 2 y-coordinate. Pixels * @param p1 (Output) Point 1. Pixels * @param p2 (Output) Point 2. Pixels * @return true if a solution was found or false if it failed */ public boolean process(DMatrixRMaj F21 , double x1 , double y1, double x2, double y2, Point2D_F64 p1 , Point2D_F64 p2 ) { // translations used to move points to the origin assignTinv(T1,x1,y1); assignTinv(T2,x2,y2); // take F to the new coordinate system // F1 = T2'*F*T1 PerspectiveOps.multTranA(T2,F21,T1,Ft); extract.process(Ft,e1,e2); // normalize so that e[x]*e[x] + e[y]*e[y] == 1 normalizeEpipole(e1); normalizeEpipole(e2); assignR(R1,e1); assignR(R2,e2); // Ft = R2*Ft*R1' PerspectiveOps.multTranC(R2,Ft,R1,Ft); double f1 = e1.z; double f2 = e2.z; double a = Ft.get(1,1); double b = Ft.get(1,2); double c = Ft.get(2,1); double d = Ft.get(2,2); if( !solvePolynomial(f1,f2,a,b,c,d)) return false; if( !selectBestSolution(rootFinder.getRoots(),f1,f2,a,b,c,d)) return false; // find the closeset point on the two lines below to the origin double t = solutionT; l1.set(t*f1,1,-t); l2.set(-f2*(c*t+d),a*t+b,c*t+d); closestPointToOrigin(l1,e1); // recycle epipole storage closestPointToOrigin(l2,e2); // original coordinates originalCoordinates(T1,R1,e1); originalCoordinates(T2,R2,e2); // back to 2D coordinates p1.set(e1.x/e1.z, e1.y/e1.z); p2.set(e2.x/e2.z, e2.y/e2.z); return true; } void closestPointToOrigin( Vector3D_F64 l , Point3D_F64 p ) { p.x = -l.x*l.z; p.y = -l.y*l.z; p.z = l.x*l.x + l.y*l.y; } void originalCoordinates( DMatrixRMaj T , DMatrixRMaj R , Point3D_F64 p ) { GeometryMath_F64.multTran(R,p,p); GeometryMath_F64.mult(T,p,p); } /** * Solves for the roots of an ugly polynomial defined in 12.7 in book [1] * * Coefficients found using Sage Math * * {@code * a,b,c,d,f1,f2,t = var('a,b,c,d,f1,f2,t') * g = t*((a*t+b)^2 + f2^2*(c*t+d)^2)^2 - (a*d-b*c)*(1+f1^2*t^2)^2*(a*t+b)*(c*t+d) * g.expand().collect(t) * } * */ public boolean solvePolynomial( double f1, double f2 , double a , Double b , double c , double d ) { double f1_2 = f1*f1; double f1_4 = f1_2*f1_2; double f2_2 = f2*f2; double f2_4 = f2_2*f2_2; double a_2 = a*a; double b_2 = b*b; double c_2 = c*c; double d_2 = d*d; double b_3 = b_2*b; double d_3 = d_2*d; double a_4 = a_2*a_2; double b_4 = b_2*b_2; double c_4 = c_2*c_2; double d_4 = d_2*d_2; poly.size = 6; poly.c[5] = a*b*c_2*f1_4 - a_2*c*d*f1_4; poly.c[4] = b_2*c_2*f1_4 - a_2*d_2*f1_4 + c_4*f2_4 + 2*a_2*c_2*f2_2 + a_4; poly.c[3] = 2*(3*c_2*d_2*f2_4 + b_2*c_2*f1_2 - a_2*d_2*f1_2 + b_2*c_2*f2_2 + 4*a*b*c*d*f2_2 + a_2*d_2*f2_2 + 3*a_2*b_2); poly.c[2] = (4*c*d_3*f2_4 + 2*b_2*c*d*f1_2 - 2*a*b*d_2*f1_2 + 4*b_2*c*d*f2_2 + 4*a*b*d_2*f2_2 + 4*a*b_3 + a*b*c_2 - a_2*c*d); poly.c[1] = d_4*f2_4 + 2*b_2*d_2*f2_2 + b_4 + b_2*c_2 - a_2*d_2; poly.c[0] = b_2*c*d - a*b*d_2; return rootFinder.process(poly); } boolean selectBestSolution( List roots , double f1, double f2 , double a , Double b , double c , double d ) { // cost at t=infinty, must do better than this double best = 1.0/(f1*f1) + c*c/(a*a + f2*f2*c*c); int bestIndex = -1; for (int i = 0; i < roots.size(); i++) { Complex_F64 cr = roots.get(i); if( !cr.isReal() ) continue; double t = cr.real; double left = t*t/(1+f1*f1*t*t); double right = pow2(c*t + d)/(pow2(a*t+b) + f2*f2*pow2(c*t+d)); double squaredDistance = left + right; if(squaredDistance < best ) { best = squaredDistance; bestIndex = i; solutionT = t; } } return bestIndex != -1; } static void assignTinv(DMatrixRMaj T , double x , double y ) { T.data[0] = T.data[4] = T.data[8] = 1; T.data[2] = x; T.data[5]= y; } static void assignR( DMatrixRMaj R , Point3D_F64 e ) { R.data[0] = e.x; R.data[1] = e.y; R.data[3] = -e.y; R.data[4] = e.x; R.data[8] = 1; } static void normalizeEpipole( Point3D_F64 e ) { double n =e.x*e.x + e.y*e.y; e.divideIP(Math.sqrt(n)); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy