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();
}
}