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

org.ddogleg.fitting.modelset.ransac.Ransac Maven / Gradle / Ivy

Go to download

DDogleg Numerics is a high performance Java library for non-linear optimization, robust model fitting, polynomial root finding, sorting, and more.

There is a newer version: 0.23.4
Show newest version
/*
 * Copyright (c) 2012-2017, Peter Abeles. All Rights Reserved.
 *
 * This file is part of DDogleg (http://ddogleg.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 org.ddogleg.fitting.modelset.ransac;

import org.ddogleg.fitting.modelset.DistanceFromModel;
import org.ddogleg.fitting.modelset.ModelGenerator;
import org.ddogleg.fitting.modelset.ModelManager;
import org.ddogleg.fitting.modelset.ModelMatcher;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


/**
 * 

* RANSAC is an abbreviation for "RANdom SAmple Consensus" and is an iterative algorithm. The model with the * largest set of inliers is found by randomly sampling the set of points and fitting a model. The algorithm * terminates when the maximum number of iterations has been reached. An inlier is defined as a point which * has an error less than a user specified threshold to the estimated model being considered. The algorithm was * first published by Fischler and Bolles in 1981." *

* *

* Sample Points: By default the minimum number of points are sampled. The user to override this default and set * it to any number. *

* * @author Peter Abeles */ public class Ransac implements ModelMatcher { // how many points are drawn to generate the model protected int sampleSize; // how close a point needs to be considered part of the model protected double thresholdFit; // generates an initial model given a set of points protected ModelGenerator modelGenerator; // computes the distance a point is from the model protected DistanceFromModel modelDistance; // used to randomly select points/samples protected Random rand; // list of points which are a candidate for the best fit set protected List candidatePoints = new ArrayList(); // list of samples from the best fit model protected List bestFitPoints = new ArrayList(); // the best model found so far protected Model bestFitParam; // the current model being considered protected Model candidateParam; // the maximum number of iterations it will perform protected int maxIterations; // copy of the input data set so that it can be modified protected List dataSet = new ArrayList<>(); // the set of points which were initially sampled protected List initialSample = new ArrayList(); // list of indexes converting it from match set to input list protected int []matchToInput = new int[1]; protected int []bestMatchToInput = new int[1]; /** * Creates a new instance of the ransac algorithm. The number of points sampled will default to the * minimum number. To override this default invoke {@link #setSampleSize(int)}. * * @param randSeed The random seed used by the random number generator. * @param modelGenerator Creates new model(s) given a small number of points. * @param modelDistance Computes the difference between a point an a model. * @param maxIterations The maximum number of iterations the RANSAC algorithm will perform. * @param thresholdFit How close of a fit a points needs to be to the model to be considered a fit. */ public Ransac(long randSeed, ModelManager modelManager, ModelGenerator modelGenerator, DistanceFromModel modelDistance, int maxIterations, double thresholdFit) { this.modelGenerator = modelGenerator; this.modelDistance = modelDistance; this.rand = new Random(randSeed); this.maxIterations = maxIterations; this.bestFitParam = modelManager.createModelInstance(); this.candidateParam = modelManager.createModelInstance(); this.sampleSize = modelGenerator.getMinimumPoints(); this.thresholdFit = thresholdFit; } @Override public boolean process(List _dataSet ) { // see if it has the minimum number of points if (_dataSet.size() < modelGenerator.getMinimumPoints() ) return false; // the data set will be modified so a copy is needed. Otherwise indexes of match set will not // be correct dataSet.clear(); dataSet.addAll(_dataSet); // configure internal data structures initialize(dataSet); // iterate until it has exhausted all iterations or stop if the entire data set // is in the inlier set for (int i = 0; i < maxIterations && bestFitPoints.size() != dataSet.size(); i++) { // sample the a small set of points randomDraw(dataSet, sampleSize, initialSample, rand); // get the candidate(s) for this sample set if( modelGenerator.generate(initialSample, candidateParam ) ) { // see if it can find a model better than the current best one selectMatchSet(_dataSet, thresholdFit, candidateParam); // save this results if (bestFitPoints.size() < candidatePoints.size()) { swapCandidateWithBest(); } } } return bestFitPoints.size() > 0; } /** * Initialize internal data structures */ public void initialize( List dataSet ) { bestFitPoints.clear(); if( dataSet.size() > matchToInput.length ) { matchToInput = new int[ dataSet.size() ]; bestMatchToInput = new int[ dataSet.size() ]; } } /** * Performs a random draw in the dataSet. When an element is selected it is moved to the end of the list * so that it can't be selected again. * * @param dataSet List that points are to be selected from. Modified. */ public static void randomDraw(List dataSet, int numSample, List initialSample, Random rand) { initialSample.clear(); for (int i = 0; i < numSample; i++) { // index of last element that has not been selected int indexLast = dataSet.size()-i-1; // randomly select an item from the list which has not been selected int indexSelected = rand.nextInt(indexLast+1); T a = dataSet.get(indexSelected); initialSample.add(a); // Swap the selected item with the last unselected item in the list. This way the selected // item can't be selected again and the last item can now be selected dataSet.set(indexSelected,dataSet.set(indexLast,a)); } } /** * Looks for points in the data set which closely match the current best * fit model in the optimizer. * * @param dataSet The points being considered */ @SuppressWarnings({"ForLoopReplaceableByForEach"}) protected void selectMatchSet(List dataSet, double threshold, Model param) { candidatePoints.clear(); modelDistance.setModel(param); for (int i = 0; i < dataSet.size(); i++) { Point point = dataSet.get(i); double distance = modelDistance.computeDistance(point); if (distance < threshold) { matchToInput[candidatePoints.size()] = i; candidatePoints.add(point); } } } /** * Turns the current candidates into the best ones. */ protected void swapCandidateWithBest() { List tempPts = candidatePoints; candidatePoints = bestFitPoints; bestFitPoints = tempPts; int tempIndex[] = matchToInput; matchToInput = bestMatchToInput; bestMatchToInput = tempIndex; Model m = candidateParam; candidateParam = bestFitParam; bestFitParam = m; } @Override public List getMatchSet() { return bestFitPoints; } @Override public int getInputIndex(int matchIndex) { return bestMatchToInput[matchIndex]; } @Override public Model getModelParameters() { return bestFitParam; } @Override public double getFitQuality() { return bestFitPoints.size(); } public int getMaxIterations() { return maxIterations; } public void setMaxIterations(int maxIterations) { this.maxIterations = maxIterations; } @Override public int getMinimumSize() { return sampleSize; } /** * Override the number of points that are sampled and used to generate models. If this value * is not set it defaults to the minimum number. * * @param sampleSize Number of sample points. */ public void setSampleSize(int sampleSize) { this.sampleSize = sampleSize; } public double getThresholdFit() { return thresholdFit; } public void setThresholdFit(double thresholdFit) { this.thresholdFit = thresholdFit; } @Override public Class getPointType() { return modelDistance.getPointType(); } @Override public Class getModelType() { return modelDistance.getModelType(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy