package edu.stanford.rsl.conrad.fitting; import java.util.Random; /** * This class implements a version of RANSAC that generates numberOfTries tries to generate the model that best fits the correspondences in x and y. * The algorithm chooses the model that has the best model fitness, i.e., the model that matches the best to the data. A correct match is counted if the * distance computed between model and real data is less than epsilon.<br> * If no model could be determined, epsilon is increased by a factor of ten and a warning is given. * @author akmaier * */ public class RANSACFittedFunction extends Function { /** * */ private static final long serialVersionUID = -8899279893077174756L; /** * This is the function that RANSAC is computed on- */ protected Function baseFunction; /** * This paremeter defines the number of tries to compute RANSAC */ protected int numberOfTries = 100000; /** * This parameter defines the fault tolerance */ protected double epsilon = 0.0001; public RANSACFittedFunction(Function func){ baseFunction = func; numberOfParameters = baseFunction.getNumberOfParameters(); } protected double evaluateModelFittnes(double [] x, double [] y, Function func){ double eval = 0; for (int i=0;i<x.length;i++){ if (Math.abs(func.evaluate(x[i])-y[i])< epsilon) eval++; } return eval; } @Override public void fitToPoints(double[] x, double[] y) { int corresp = baseFunction.getMinimumNumberOfCorrespondences(); double bestFit = 0; Function bestModel = null; double [] randX = new double [corresp]; double [] randY = new double [corresp]; Random random = new Random(); for (int i = 0; i < numberOfTries; i++){ try { Function currentTry = (Function) baseFunction.getClass().newInstance(); // compute next try: for (int j=0;j<corresp;j++){ int index = (int) (random.nextDouble() * (x.length-1)); randX[j] = x[index]; randY[j] = y[index]; } currentTry.fitToPoints(randX, randY); double fitness = evaluateModelFittnes(x, y, currentTry); if (fitness > bestFit) { bestModel = currentTry; bestFit = fitness; } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } if ((bestModel != null)) { baseFunction = bestModel; fittingDone = true; } else { System.err.println("RANSACFittedFunction: Warning increasing epsilon to " + epsilon +"\nChoose a bigger epsilon or more tries in the future!"); epsilon *= 1.1; fitToPoints(x, y); } } @Override public double evaluate(double x) { return baseFunction.evaluate(x); } @Override public String toString() { return "RANSAC " + baseFunction.toString(); } @Override public int getMinimumNumberOfCorrespondences() { // TODO Auto-generated method stub return baseFunction.getMinimumNumberOfCorrespondences(); } /** * @param baseFunction the baseFunction to set */ public void setBaseFunction(Function baseFunction) { this.baseFunction = baseFunction; } /** * @return the baseFunction */ public Function getBaseFunction() { return baseFunction; } /** * @param numberOfTries the numberOfTries to set */ public void setNumberOfTries(int numberOfTries) { this.numberOfTries = numberOfTries; } /** * @return the numberOfTries */ public int getNumberOfTries() { return numberOfTries; } /** * @return the epsilon */ public double getEpsilon() { return epsilon; } /** * @param epsilon the epsilon to set */ public void setEpsilon(double epsilon) { this.epsilon = epsilon; } @Override public double[] getParametersAsDoubleArray() { return baseFunction.getParametersAsDoubleArray(); } }