namespace VisualMath.Accord.MachineLearning { using System; /// /// Multipurpose RANSAC algorithm. /// /// The model type to be trained by RANSAC. /// /// RANSAC is an abbreviation for "RANdom SAmple Consensus". It is an iterative /// method to estimate parameters of a mathematical model from a set of observed /// data which contains outliers. It is a non-deterministic algorithm in the sense /// that it produces a reasonable result only with a certain probability, with this /// probability increasing as more iterations are allowed. /// /// public class RANSAC where TModel : class { // Ransac parameters private int s; // number of samples private double t; // inlier threshold private int maxSamplings = 100; private int maxEvaluations = 1000; private double probability = 0.99; // Ransac functions private Func fitting; private Func distances; private Func degenerate; #region Properties /// /// Model fitting function. /// public Func Fitting { get { return fitting; } set { fitting = value; } } /// /// Degenerative set detection function. /// public Func Degenerate { get { return degenerate; } set { degenerate = value; } } /// /// Distance function. /// public Func Distances { get { return distances; } set { distances = value; } } /// /// Gets or sets the minimum distance between a data point and /// the model used to decide whether the point is an inlier or not. /// public double Threshold { get { return t; } set { t = value; } } /// /// Gets or sets the minimum number of samples from the data /// required by the fitting function to fit a model. /// public int Samples { get { return s; } set { s = value; } } /// /// Maximum number of attempts to select a non-degenerate data set. /// /// /// The default value is 100. /// public int MaxSamplings { get { return maxSamplings; } set { maxSamplings = value; } } /// /// Maximum number of iterations. /// /// /// The default value is 1000. /// public int MaxEvaluations { get { return maxEvaluations; } set { maxEvaluations = value; } } /// /// Gets or sets the probability of obtaining a random /// sample of the input points that contains no outliers. /// public double Probability { get { return probability; } set { probability = value; } } #endregion /// /// Constructs a new RANSAC algorithm. /// /// /// The minimum number of samples from the data /// required by the fitting function to fit a model. /// public RANSAC(int minSamples) { this.s = minSamples; } /// /// Constructs a new RANSAC algorithm. /// /// /// The minimum number of samples from the data /// required by the fitting function to fit a model. /// /// /// The minimum distance between a data point and /// the model used to decide whether the point is /// an inlier or not. /// public RANSAC(int minSamples, double threshold) { this.s = minSamples; this.t = threshold; } /// /// Constructs a new RANSAC algorithm. /// /// /// The minimum number of samples from the data /// required by the fitting function to fit a model. /// /// /// The minimum distance between a data point and /// the model used to decide whether the point is /// an inlier or not. /// /// /// The probability of obtaining a random sample of /// the input points that contains no outliers. /// public RANSAC(int minSamples, double threshold, double probability) { if (minSamples < 0) throw new ArgumentOutOfRangeException("minSamples"); if (threshold < 0) throw new ArgumentOutOfRangeException("threshold"); if (probability > 1.0 || probability < 0.0) throw new ArgumentException("Probability should be a value between 0 and 1", "probability"); this.s = minSamples; this.t = threshold; this.probability = probability; } /// /// Computes the model using the RANSAC algorithm. /// /// The total number of points in the data set. public TModel Compute(int size) { int[] inliers; return Compute(size, out inliers); } /// /// Computes the model using the RANSAC algorithm. /// /// The total number of points in the data set. /// The indexes of the outlier points in the data set. public TModel Compute(int size, out int[] inliers) { // We are going to find the best model (which fits // the maximum number of inlier points as possible). TModel bestModel = null; int[] bestInliers = null; int maxInliers = 0; // For this we are going to search for random samples // of the original points which contains no outliers. int count = 0; // Total number of trials performed double N = maxEvaluations; // Estimative of number of trials needed. // While the number of trials is less than our estimative, // and we have not surpassed the maximum number of trials while (count < N && count < maxEvaluations) { TModel model = null; int[] sample = null; int samplings = 0; // While the number of samples attempted is less // than the maximum limit of attempts while (samplings < maxSamplings) { // Select at random s datapoints to form a trial model. sample = Statistics.Tools.Random(size, s); // If the sampled points are not in a degenerate configuration, if (!degenerate(sample)) { // Fit model using the random selection of points model = fitting(sample); break; // Exit the while loop. } samplings++; // Increase the samplings counter } // Now, evaluate the distances between total points and the model returning the // indices of the points that are inliers (according to a distance threshold t). inliers = distances(model, t); // Check if the model was the model which highest number of inliers: if (inliers.Length > maxInliers) { // Yes, this model has the highest number of inliers. maxInliers = inliers.Length; // Set the new maximum, bestModel = model; // This is the best model found so far, bestInliers = inliers; // Store the indices of the current inliers. // Update estimate of N, the number of trials to ensure we pick, // with probability p, a data set with no outliers. double pInlier = (double)inliers.Length / (double)size; double pNoOutliers = 1.0 - System.Math.Pow(pInlier, s); N = System.Math.Log(1.0 - probability) / System.Math.Log(pNoOutliers); } count++; // Increase the trial counter. } inliers = bestInliers; return bestModel; } } }