/*
* Copyright (c) 2011-2015, 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.factory.geo;
import boofcv.abst.geo.*;
import boofcv.abst.geo.bundle.BundleAdjustmentCalibratedDense;
import boofcv.abst.geo.f.*;
import boofcv.abst.geo.h.LeastSquaresHomography;
import boofcv.abst.geo.h.WrapHomographyLinear;
import boofcv.abst.geo.pose.*;
import boofcv.abst.geo.triangulate.*;
import boofcv.abst.geo.trifocal.WrapTrifocalAlgebraicPoint7;
import boofcv.abst.geo.trifocal.WrapTrifocalLinearPoint7;
import boofcv.alg.geo.ModelObservationResidualN;
import boofcv.alg.geo.f.DistanceEpipolarConstraint;
import boofcv.alg.geo.h.HomographyLinear4;
import boofcv.alg.geo.h.HomographyResidualSampson;
import boofcv.alg.geo.h.HomographyResidualTransfer;
import boofcv.alg.geo.pose.P3PFinsterwalder;
import boofcv.alg.geo.pose.P3PGrunert;
import boofcv.alg.geo.pose.PnPLepetitEPnP;
import boofcv.alg.geo.pose.PoseFromPairLinear6;
import boofcv.alg.geo.trifocal.TrifocalAlgebraicPoint7;
import boofcv.struct.geo.AssociatedPair;
import georegression.fitting.MotionTransformPoint;
import georegression.fitting.se.FitSpecialEuclideanOps_F64;
import georegression.struct.point.Point3D_F64;
import georegression.struct.se.Se3_F64;
import org.ddogleg.optimization.FactoryOptimization;
import org.ddogleg.optimization.UnconstrainedLeastSquares;
import org.ddogleg.solver.PolynomialOps;
import org.ddogleg.solver.RootFinderType;
import org.ddogleg.struct.FastQueue;
/**
* Factory for creating abstracted algorithms related to multi-view geometry
*
* @author Peter Abeles
*/
public class FactoryMultiView {
/**
* Creates bundle adjustment for a camera with a know and fixed intrinsic calibration
*
* @param tol Convergence tolerance. Try 1e-8
* @param maxIterations Maximum number of iterations. Try 200 or more
* @return Bundle Adjustment
*/
public static BundleAdjustmentCalibrated bundleCalibrated(double tol , int maxIterations) {
return new BundleAdjustmentCalibratedDense(tol,maxIterations);
}
/**
* Returns an algorithm for estimating a homography matrix given a set of
* {@link AssociatedPair}.
*
* @see HomographyLinear4
*
* @param normalize If input is in pixel coordinates set to true. False if in normalized image coordinates.
* @return Homography estimator.
*/
public static Estimate1ofEpipolar computeHomography( boolean normalize ) {
HomographyLinear4 alg = new HomographyLinear4(normalize);
return new WrapHomographyLinear(alg);
}
/**
* Creates a non-linear optimizer for refining estimates of homography matrices.
*
* @see HomographyResidualSampson
* @see HomographyResidualTransfer
*
* @param tol Tolerance for convergence. Try 1e-8
* @param maxIterations Maximum number of iterations it will perform. Try 100 or more.
* @return Homography refinement
*/
public static RefineEpipolar refineHomography( double tol , int maxIterations , EpipolarError type ) {
ModelObservationResidualN residuals;
switch( type ) {
case SIMPLE:
residuals = new HomographyResidualTransfer();
break;
case SAMPSON:
residuals = new HomographyResidualSampson();;
break;
default:
throw new IllegalArgumentException("Type not supported: "+type);
}
return new LeastSquaresHomography(tol,maxIterations,residuals);
}
/**
*
* Returns an algorithm for estimating a fundamental or essential matrix given a set of
* {@link AssociatedPair} in pixel coordinates. The number of hypotheses returned and minimum number of samples
* is dependent on the implementation. The ambiguity from multiple hypotheses can be resolved using other
* sample points and testing additional constraints.
*
*
*
* All estimated epipolar matrices will have the following constraint:
* x'*F*x = 0, where F is the epipolar matrix, x' = currLoc, and x = keyLoc.
*
*
*
* There are more differences between these algorithms than the minimum number of sample points. Consult
* the literature for information on critical surfaces which will work or not work with each algorithm. In
* general, algorithm which require fewer samples have less issues with critical surfaces than the 8-point
* algorithm.
*
*
*
* IMPORTANT: When estimating a fundamental matrix use pixel coordinates. When estimating an essential matrix
* use normalized image coordinates from a calibrated camera.
*
*
*
* IMPORTANT. The number of allowed sample points varies depending on the algorithm. The 8 point algorithm can
* process 8 or more points. Both the 5 an 7 point algorithms require exactly 5 and 7 points exactly. In addition
* the 5-point algorithm is only for the calibrated (essential) case.
*
*
* @see boofcv.alg.geo.f.EssentialNister5
* @see boofcv.alg.geo.f.FundamentalLinear7
* @see boofcv.alg.geo.f.FundamentalLinear8
*
* @param which Specifies which algorithm is to be created
* @return Fundamental or essential estimation algorithm that returns multiple hypotheses.
*/
public static EstimateNofEpipolar computeFundamental_N( EnumEpipolar which )
{
switch( which ) {
case FUNDAMENTAL_8_LINEAR:
return new Estimate1toNofEpipolar(new WrapFundamentalLinear8(true));
case ESSENTIAL_8_LINEAR:
return new Estimate1toNofEpipolar(new WrapFundamentalLinear8(false));
case FUNDAMENTAL_7_LINEAR:
return new WrapFundamentalLinear7(true);
case ESSENTIAL_7_LINEAR:
return new WrapFundamentalLinear7(false);
case ESSENTIAL_5_NISTER:
return new WrapEssentialNister5();
}
throw new IllegalArgumentException("Unknown algorithm "+which);
}
/**
*
* Similar to {@link #computeFundamental_N}, but it returns only a single hypothesis. If
* the underlying algorithm generates multiple hypotheses they are resolved by considering additional
* sample points. For example, if you are using the 7 point algorithm at least one additional sample point
* is required to resolve that ambiguity. So 8 or more sample points are now required.
*
*
*
* All estimated epipolar matrices will have the following constraint:
* x'*F*x = 0, where F is the epipolar matrix, x' = currLoc, and x = keyLoc.
*
*
*
* See {@link #computeFundamental_N} for a description of the algorithms and what 'minimumSamples'
* and 'isFundamental' do.
*
*
*
* The 8-point algorithm already returns a single hypothesis and ignores the 'numRemoveAmbiguity' parameter.
* All other algorithms require one or more points to remove ambiguity. Understanding a bit of theory is required
* to understand what a good number of points is. If a single point is used then to select the correct answer that
* point must be in the inlier set. If more than one point, say 10, then not all of those points must be in the
* inlier set,
*
*
* @see GeoModelEstimatorNto1
*
* @param which Specifies which algorithm is to be created
* @param numRemoveAmbiguity Number of sample points used to prune hypotheses. Ignored if only a single solution.
* @return Fundamental or essential estimation algorithm that returns a single hypothesis.
*/
public static Estimate1ofEpipolar computeFundamental_1( EnumEpipolar which, int numRemoveAmbiguity)
{
switch( which ) {
case FUNDAMENTAL_8_LINEAR:
return new WrapFundamentalLinear8(true);
case ESSENTIAL_8_LINEAR:
return new WrapFundamentalLinear8(false);
}
if( numRemoveAmbiguity <= 0 )
throw new IllegalArgumentException("numRemoveAmbiguity must be greater than zero");
EstimateNofEpipolar alg = computeFundamental_N(which);
DistanceEpipolarConstraint distance = new DistanceEpipolarConstraint();
return new EstimateNto1ofEpipolar(alg,distance,numRemoveAmbiguity);
}
/**
* Creates a non-linear optimizer for refining estimates of fundamental or essential matrices.
*
* @see boofcv.alg.geo.f.FundamentalResidualSampson
* @see boofcv.alg.geo.f.FundamentalResidualSimple
*
* @param tol Tolerance for convergence. Try 1e-8
* @param maxIterations Maximum number of iterations it will perform. Try 100 or more.
* @return RefineEpipolar
*/
public static RefineEpipolar refineFundamental( double tol , int maxIterations , EpipolarError type )
{
switch( type ) {
case SAMPSON:
return new LeastSquaresFundamental(tol,maxIterations,true);
case SIMPLE:
return new LeastSquaresFundamental(tol,maxIterations,false);
}
throw new IllegalArgumentException("Type not supported: "+type);
}
/**
* Creates a trifocal tensor estimation algorithm.
*
* @param type Which algorithm.
* @param iterations If the algorithm is iterative, then this is the number of iterations. Try 200
* @return Trifocal tensor estimator
*/
public static Estimate1ofTrifocalTensor estimateTrifocal_1(EnumTrifocal type, int iterations) {
switch( type ) {
case LINEAR_7:
return new WrapTrifocalLinearPoint7();
case ALGEBRAIC_7:
UnconstrainedLeastSquares optimizer = FactoryOptimization.leastSquaresLM(1e-3, false);
TrifocalAlgebraicPoint7 alg = new TrifocalAlgebraicPoint7(optimizer,iterations,1e-12,1e-12);
return new WrapTrifocalAlgebraicPoint7(alg);
}
throw new IllegalArgumentException("Unknown type "+type);
}
/**
* Creates an estimator for the PnP problem that uses only three observations, which is the minimal case
* and known as P3P.
*
* @param which The algorithm which is to be returned.
* @param numIterations Number of iterations. Only used by some algorithms and recommended number varies
* significantly by algorithm.
* @return An estimator which can return multiple estimates.
*/
public static EstimateNofPnP computePnP_N(EnumPNP which , int numIterations ) {
MotionTransformPoint motionFit = FitSpecialEuclideanOps_F64.fitPoints3D();
switch( which ) {
case P3P_GRUNERT:
P3PGrunert grunert = new P3PGrunert(PolynomialOps.createRootFinder(5, RootFinderType.STURM));
return new WrapP3PLineDistance(grunert,motionFit);
case P3P_FINSTERWALDER:
P3PFinsterwalder finster = new P3PFinsterwalder(PolynomialOps.createRootFinder(4,RootFinderType.STURM));
return new WrapP3PLineDistance(finster,motionFit);
case EPNP:
Estimate1ofPnP epnp = computePnP_1(which,numIterations,0);
return new Estimate1toNofPnP(epnp);
}
throw new IllegalArgumentException("Type "+which+" not known");
}
/**
* Created an estimator for the P3P problem that selects a single solution by considering additional
* observations.
*
*
* NOTE: EPnP has several tuning parameters and the defaults here might not be the best for your situation.
*
*
* @param which The algorithm which is to be returned.
* @param numIterations Number of iterations. Only used by some algorithms and recommended number varies
* significantly by algorithm.
* @param numTest How many additional sample points are used to remove ambiguity in the solutions. Not used
* if only a single solution is found.
* @return An estimator which returns a single estimate.
*/
public static Estimate1ofPnP computePnP_1(EnumPNP which, int numIterations , int numTest) {
if( which == EnumPNP.EPNP ) {
PnPLepetitEPnP alg = new PnPLepetitEPnP(0.1);
alg.setNumIterations(numIterations);
return new WrapPnPLepetitEPnP(alg);
}
FastQueue solutions = new FastQueue(4,Se3_F64.class,true);
return new EstimateNto1ofPnP(computePnP_N(which,-1),solutions,numTest);
}
/**
* Returns a solution to the PnP problem for 4 or more points using EPnP. Fast and fairly
* accurate algorithm. Can handle general and planar scenario automatically.
*
* @see PnPLepetitEPnP
*
* @param numIterations If more then zero then non-linear optimization is done. More is not always better. Try 10
* @param magicNumber Affects how the problem is linearized. See comments in {@link PnPLepetitEPnP}. Try 0.1
* @return Estimate1ofPnP
*/
public static Estimate1ofPnP computePnPwithEPnP(int numIterations, double magicNumber) {
PnPLepetitEPnP alg = new PnPLepetitEPnP(magicNumber);
alg.setNumIterations(numIterations);
return new WrapPnPLepetitEPnP(alg);
}
/**
* Refines a pose solution to the PnP problem using non-linear least squares..
*
* @param tol Convergence tolerance. Try 1e-8
* @param maxIterations Maximum number of iterations. Try 200
*/
public static RefinePnP refinePnP( double tol , int maxIterations ) {
return new PnPRefineRodrigues(tol,maxIterations);
}
/**
* Estimate the camera motion give two observations and the 3D world coordinate of each points.
*
* @return PoseFromPairLinear6
*/
public static PoseFromPairLinear6 triangulatePoseFromPair() {
return new PoseFromPairLinear6();
}
/**
* Triangulate two view by finding the intersection of two rays.
*
* @see boofcv.alg.geo.triangulate.TriangulateGeometric
*
* @return Two view triangulation algorithm
*/
public static TriangulateTwoViewsCalibrated triangulateTwoGeometric() {
return new WrapGeometricTriangulation();
}
/**
* Triangulate two view using the Discrete Linear Transform (DLT)
*
* @see boofcv.alg.geo.triangulate.TriangulateLinearDLT
*
* @return Two view triangulation algorithm
*/
public static TriangulateTwoViewsCalibrated triangulateTwoDLT() {
return new WrapTwoViewsTriangulateDLT();
}
/**
* Triangulate N views using the Discrete Linear Transform (DLT)
*
* @see boofcv.alg.geo.triangulate.TriangulateLinearDLT
*
* @return Two view triangulation algorithm
*/
public static TriangulateNViewsCalibrated triangulateNDLT() {
return new WrapNViewsTriangulateDLT();
}
/**
* Triangulate two view by finding the depth of the pixel using a linear algorithm.
*
* @see boofcv.alg.geo.triangulate.PixelDepthLinear
*
* @return Two view triangulation algorithm
*/
public static TriangulateTwoViewsCalibrated triangulateTwoLinearDepth() {
return new WrapPixelDepthLinear();
}
/**
* Refine the triangulation using Sampson error. Approximately takes in account epipolar constraints.
*
* @see boofcv.alg.geo.triangulate.ResidualsTriangulateSampson
*
* @param convergenceTol Tolerance for finishing optimization
* @param maxIterations Maximum number of allowed iterations
* @return Triangulation refinement algorithm.
*/
public static RefineTriangulationEpipolar triangulateRefineEpipolar( double convergenceTol, int maxIterations ) {
return new LeastSquaresTriangulateEpipolar(convergenceTol,maxIterations);
}
/**
* Refine the triangulation by computing the difference between predicted and actual pixel location.
* Does not take in account epipolar constraints.
*
* @see boofcv.alg.geo.triangulate.ResidualsTriangulateSimple
*
* @param convergenceTol Tolerance for finishing optimization
* @param maxIterations Maximum number of allowed iterations
* @return Triangulation refinement algorithm.
*/
public static RefineTriangulationCalibrated triangulateRefine( double convergenceTol, int maxIterations ) {
return new LeastSquaresTriangulateCalibrated(convergenceTol,maxIterations);
}
}
Ads help maintain this website.