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:
-
Repeat for up to iterations:
- Sample random data points
- Fit a model to the -point sample (skip if degenerate)
- Score: count inliers — points with residual
- If this model has more inliers than the current best, update the best
- Optionally refit the model on all inliers for a tighter fit
- Update the iteration bound dynamically based on the current inlier ratio
-
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) } } }
| Method | Purpose |
|---|---|
MIN_SAMPLES | Minimum points for a model (e.g., 4 for homography) |
fit | Fit model from selected points |
residual | Distance from a point to the model |
is_degenerate | Reject degenerate samples (e.g., collinear points for homography) |
refit | Optional: 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:
| Model | MIN_SAMPLES | Residual | Usage |
|---|---|---|---|
| Homography | 4 | Reprojection error (pixels) | dlt_homography_ransac |
| Fundamental matrix | 8 | Epipolar distance | fundamental_8point_ransac |
| PnP (DLT) | 6 | Reprojection 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.