Goal
Fit a parametric model to a set of data points where an unknown fraction of the observations are gross errors (outliers). Inputs: the data set ; a minimal sample size determined by the model's degrees of freedom; an inlier-distance threshold ; a desired success probability ; a prior inlier-fraction estimate ; and a consensus-set acceptance threshold . Outputs: model parameters estimated from the largest consistent subset of , where consistency is defined by . The defining property is the random-sample-consensus strategy: draw a minimal random subset, instantiate a model, count global inliers, and retain the best hypothesis — inverting the classical approach of fitting all data and pruning residuals iteratively.
Algorithm
Let denote the data set and the number of points. Let denote the minimal sample size — the number of points required to instantiate the model uniquely. Let denote the inlier-distance threshold: a datum is an inlier to model iff . Let denote the desired probability that at least one drawn minimal sample consists entirely of inliers. Let denote the estimated inlier fraction — the probability that a uniformly chosen datum is an inlier. Let denote the minimum consensus-set size required to accept a hypothesis. Let denote model parameters and a consensus set — the subset of consistent with . Let denote the number of trials to execute.
The number of trials needed so that the probability of drawing at least one all-inlier minimal sample is at least :
Derived in §II.B from the geometric-series identity. Expected value and standard deviation of the trial count are
For , , : and .
Procedure
- Compute and round up to the nearest integer.
- Initialise and undefined.
- For each trial :
- Draw a uniform random subset with .
- Instantiate model from .
- Form the consensus set .
- If set and ; if terminate early.
- Re-fit on by least squares (optional; improves accuracy when ).
- Return and .
Implementation
Generic RANSAC loop in Rust:
use rand::seq::SliceRandom;
pub trait RansacModel: Sized {
type Point;
const SAMPLE_SIZE: usize;
fn fit_minimal(sample: &[&Self::Point]) -> Option<Self>;
fn residual(&self, point: &Self::Point) -> f64;
}
pub fn ransac<M: RansacModel>(
points: &[M::Point],
eps: f64,
p_success: f64,
w: f64,
) -> Option<(M, Vec<usize>)> {
let s = M::SAMPLE_SIZE;
// k = log(1 − p) / log(1 − w^s)
let k = ((1.0 - p_success).ln() / (1.0 - w.powi(s as i32)).ln()).ceil() as usize;
let mut rng = rand::thread_rng();
let indices: Vec<usize> = (0..points.len()).collect();
let mut best: Option<(M, Vec<usize>)> = None;
for _ in 0..k {
let picks: Vec<usize> = indices.choose_multiple(&mut rng, s).copied().collect();
let sample: Vec<&M::Point> = picks.iter().map(|&i| &points[i]).collect();
let Some(theta) = M::fit_minimal(&sample) else { continue };
let inliers: Vec<usize> = points
.iter()
.enumerate()
.filter(|(_, p)| theta.residual(p) <= eps)
.map(|(i, _)| i)
.collect();
if best.as_ref().map_or(true, |(_, b)| inliers.len() > b.len()) {
best = Some((theta, inliers));
}
}
best
}
Remarks
- Complexity: , where is the iteration count, is the number of data points, and is the cost of one model instantiation plus residual evaluations.
- Iteration count is exponentially sensitive to : , so halving with raises from 16 to 256 (§II.B). For and , — impractical without guided sampling.
- Fat-tailed convergence: for small (§II.B), so yields only ~63% success probability. Production budgets – trials.
- Founding application — the Location Determination Problem (§IV.B–E): camera pose from aerial-image landmark correspondences, , achieved accuracy X: 0.1 ft, Y: 6.4 ft, Z: 2.1 ft, Heading: 0.01°, Pitch: 0.10°, Roll: 0.12° on a 4000 ft real-image benchmark.
- See
ransacfor the four design axes — sampling, verification, local optimisation, threshold treatment — that organise the modern RANSAC family.
References
- M. A. Fischler, R. C. Bolles. Random Sample Consensus: A Paradigm for Model Fitting with Applications to Image Analysis and Automated Cartography. Communications of the ACM, 24(6)–395, 1981. dl.acm.org