RANSAC

RANSAC (Random Sample Consensus) is a robust estimation method that fits a model to data containing outliers. Unlike least-squares methods that use all data points and can be corrupted by even a single outlier, RANSAC repeatedly samples minimal subsets, fits a model to each, and selects the model with the most support (inliers).

Algorithm

Given data points and a model requiring points to fit:

  1. Repeat for up to iterations:

    1. Sample random data points
    2. Fit a model to the -point sample (skip if degenerate)
    3. Score: count inliers — points with residual
    4. If this model has more inliers than the current best, update the best
    5. Optionally refit the model on all inliers for a tighter fit
    6. Update the iteration bound dynamically based on the current inlier ratio
  2. Return the best model and its inlier set

Dynamic Iteration Bound

After finding a model with inlier ratio , the number of iterations needed to find an all-inlier sample with probability is:

where is the minimum sample size. As more inliers are found, increases and decreases, allowing early termination. The iteration count is clamped to — it can only decrease.

Example: For (homography), 50% inliers, and 99% confidence: iterations.

The Estimator Trait

calibration-rs provides a generic RANSAC engine that works with any model implementing the Estimator trait:

#![allow(unused)]
fn main() {
pub trait Estimator {
    type Datum;
    type Model;

    const MIN_SAMPLES: usize;

    fn fit(data: &[Self::Datum], sample_indices: &[usize])
        -> Option<Self::Model>;

    fn residual(model: &Self::Model, datum: &Self::Datum) -> f64;

    fn is_degenerate(data: &[Self::Datum], sample_indices: &[usize])
        -> bool { false }

    fn refit(data: &[Self::Datum], inliers: &[usize])
        -> Option<Self::Model> {
        Self::fit(data, inliers)
    }
}
}
MethodPurpose
MIN_SAMPLESMinimum points for a model (e.g., 4 for homography)
fitFit model from selected points
residualDistance from a point to the model
is_degenerateReject degenerate samples (e.g., collinear points for homography)
refitOptional: refit model on full inlier set (may differ from fit)

Configuration

#![allow(unused)]
fn main() {
pub struct RansacOptions {
    pub max_iters: usize,      // Maximum iterations
    pub thresh: f64,           // Inlier distance threshold
    pub min_inliers: usize,    // Minimum consensus set size
    pub confidence: f64,       // Desired probability (0-1) for dynamic bound
    pub seed: u64,             // RNG seed for reproducibility
    pub refit_on_inliers: bool, // Refit model on full inlier set
}
}

Choosing the threshold : The threshold should reflect the expected noise level. For pixel-space residuals, pixels is typical. For normalized-coordinate residuals, scale accordingly by the focal length.

Result

#![allow(unused)]
fn main() {
pub struct RansacResult<M> {
    pub success: bool,
    pub model: Option<M>,
    pub inliers: Vec<usize>,
    pub inlier_rms: f64,
    pub iters: usize,
}
}

Instantiated Models

RANSAC is used with several models in calibration-rs:

ModelMIN_SAMPLESResidualUsage
Homography4Reprojection error (pixels)dlt_homography_ransac
Fundamental matrix8Epipolar distancefundamental_8point_ransac
PnP (DLT)6Reprojection error (pixels)dlt_ransac

Deterministic Seeding

All RANSAC calls in calibration-rs use a deterministic seed for the random number generator. This ensures:

  • Reproducible results across runs
  • Deterministic tests that do not flake
  • Debuggable behavior — the same input always produces the same output

The seed can be configured via RansacOptions::seed.

Best-Model Selection

When multiple models achieve the same inlier count, RANSAC selects the model with the lowest inlier RMS (root mean square of inlier residuals). This breaks ties in favor of more accurate models.