kornia.geometry.ransac#
Robust model fitting with RANSAC (Random Sample Consensus), used to estimate affine transforms, homographies, fundamental and essential matrices from noisy correspondences.
Affine transforms#
model_type="affine" estimates a full 2D affine transform, including translation,
from three non-collinear source points. It returns a source-to-target (3, 3)
matrix with bottom row [0, 0, 1] and a boolean (N,) inlier mask.
inl_th is a Euclidean distance in the target points’ units.
Affine fitting and residual scoring compute in at least float32, even inside an
autocast context, and the returned matrix retains the input dtype. At least four
inliers are required to accept a model; otherwise the result is the all-zero
failure matrix and an all-false mask.
from kornia.geometry import RANSAC
estimator = RANSAC("affine", inl_th=2.0, seed=0)
matrix, inliers = estimator(points1, points2)
warp_matrix = matrix[:2] # 2x3 form accepted by warp_affine after batching
Affine estimation supports uniform and PROSAC sampling, MSAC and inlier-count
scoring, and the existing local_optimization="dlt" loop. Here the refit is
linear least squares with free translation using
find_affine(); "lm", compile=True and
DEGENSAC are not supported. max_lo_iters=0 disables local optimization.
Like the other model types, each call estimates one model for one correspondence
set; batch_size batches hypotheses, not independent problems.
AdaLAM shares the linear fitting primitive but retains its specialized loop: its local 2x2 models are constrained by a matched seed, and its variable-size neighborhoods, sampling and confidence-based inlier selection are unchanged.
Batches, scores, and refinement#
Hypotheses are generated and verified in batches. The sampling budget is
max_samples minimal samples when given, otherwise batch_size * max_iter
(2048 * max_iter, the historical default, with batch_size="auto"); the
last batch is truncated to it. Seven-point fundamental and five-point essential
solvers may produce several candidate matrices from each set. Early stopping is
checked between batches, and confidence=1 runs the whole budget.
model_type="fundamental" draws seven-point samples and completes each with
up to three rank-2 matrices, the roots of the cubic det F = 0, like
"fundamental_7pt"; "fundamental_8pt" draws eight-point samples. Seven
correspondences make an all-inlier sample more likely than eight, so fewer
samples reach the same confidence.
The default batch_size="auto" picks the batches per call. With the default
local_optimization="lm" (see below), a CPU call starts with 256 samples, 512
for homographies, and doubles the batch after each one up to 2048, 4096 for
homographies: inputs with many inliers stop after a small first batch, and the
others pay the per-batch overhead only a few times. On CUDA and MPS the batch
is the whole budget up to 8192 samples. Essential matrices start smaller on
every device, with 64 samples up to 1024 on CPU and 256 up to 8192 on CUDA and
MPS: a five-point sample needs few draws at high inlier ratios, and its
eigenvalue solve runs on the host (on MPS, the whole solve). All shrink for many
correspondences, down to 64 samples, so that above that floor a batch scores at
most 2**22 (CPU) or 2**25 residuals. The rest of
this paragraph describes local_optimization="dlt": on CUDA and MPS a
homography batch costs about the same from a few hundred up to 8192 hypotheses,
so the budget is drawn in batches of 8192; the epipolar solvers are
compute-bound past 2048 hypotheses, so their batches stop there. Verification
holds a batch x N residual matrix, so past 2**27 entries (about 1 GiB
at peak in float32) the batch shrinks with N, down to 2048. Other devices
retain the historical 2048-sample batch. A PROSAC batch
of either size spans most of the growth schedule, whereas certifying a model
after a small first batch drawn from a short prefix can stop on a poorly
conditioned fit. On CPU
the cost is linear in batch * N residuals and the eight-point solver costs
about ten DLTs, so the batch aims at a millisecond or so of work: 256 to 2048
hypotheses for homographies and 128 to 512 for the epipolar models, fewer for
more correspondences, which lets early stopping pay off. An integer
batch_size fixes the batch on every device.
The default score_type="msac" minimizes the sum of squared residuals
truncated at inl_th ** 2; "ransac" counts inliers instead. The returned
internal score is normalized to increase with quality. Confidence stopping
always uses the number of inliers, not this score.
Fundamental and essential models use Sampson residuals. Essential estimation
expects camera-normalized coordinates, so its threshold is not in pixels.
Line-segment homographies use the squared mean distance from the transferred
endpoints to the target segment’s line; zero-length target segments are outliers.
Their local optimization re-weights segments by the same distance.
A model is accepted only with more inliers than its minimal sample (three correspondences for affine transforms, four for homographies, five for essential and seven or eight for fundamental matrices): a model fitted to a sample always fits that sample, so only further inliers show a consensus. Without one, the estimator returns the all-zero matrix and an empty mask.
prosac_sampling=True enables the per-sample growth schedule of
Chum and Matas (CVPR 2005).
Order the correspondences from best to worst before calling the estimator.
For example, when smaller descriptor ratios indicate better matches:
order = ratios.argsort()
estimator = RANSAC("fundamental", score_type="msac", prosac_sampling=True, seed=0)
F, sorted_inliers = estimator(points1[order], points2[order])
inliers = sorted_inliers.new_empty(sorted_inliers.shape)
inliers[order] = sorted_inliers
The prefix grows within a batch, and each growth sample includes the newest
correspondence. Stopping follows the paper’s termination-length test: a ranked
prefix certifies the incumbent when its support there is non-random (a Chernoff
bound on accidental inliers with probability 0.05, at significance 0.05) and the
draws it needs for confidence fit inside the prefix’s growth interval, so
that only draws from that prefix count. As in OpenCV’s USAC, a prefix shorter
than min(N / 2, 100) or supporting fewer than 20% of all correspondences
cannot terminate; the whole set is always a candidate, so PROSAC never runs
longer than the uniform bound. Stopping is checked between batches.
Uninformative or reversed rankings can hurt accuracy. The weights argument
is not used to rank correspondences.
PROSAC pays off when the ranking tracks inlier-ness and the inlier ratio is low,
where uniform sampling cannot stop early: on SIFT correspondences with a ratio
test (HEB), it adds about 0.1 mAA at equal time for homographies. On learned
matchers with 85% or more inliers, uniform sampling already stops after one
batch, and the confidence-ranked prefixes can be spatially clustered, so the
model PROSAC certifies first may fit them well and the pose poorly. Prefer
uniform sampling there, or run PROSAC with confidence=1 when the extra
draws are affordable.
Local optimization#
Homographies, fundamental and essential matrices default to
local_optimization="lm". The sampling loop keeps the eight best-scoring
minimal models of the whole run, in coordinates normalized once per call (the
caller’s calibrated ones for essential matrices, see below). After sampling,
all eight are refined together with
max_lo_iters Levenberg-Marquardt iterations that minimize the squared
Sampson distance or one-way transfer error of every correspondence, truncated
at inl_th. The best-scoring model among the refits and the minimal models
is then refined on its own inliers with refine_iters iterations of a Cauchy
loss of scale inl_th / 3, the noise level of a threshold at three standard
deviations, and the returned mask holds the inliers of the returned model after
conversion to the input dtype. If rounding removes sufficient support, the call
returns the all-zero model and empty inlier mask used for estimation failure.
Fundamental matrices keep rank two through the parametrization of
Bartoli and Sturm (TPAMI 2004);
the refinement follows PoseLib’s refine_fundamental and
refine_homography. Refining several models once sampling ends, rather than
each new incumbent, is the batched form of PoseLib’s rule of refining every
minimal model that improves on the best one so far. The homography and
fundamental-matrix solvers take their null spaces from a partial-pivoted LU
factorization, the five-point solver from Householder reflections, and scores come from
one matrix product per batch, which keeps the per-hypothesis cost low on every
device. The refinements are a few dozen small operations per iteration and run
on the CPU in float64 for every device, where launch latency would dominate.
Early stopping follows the support of the best minimal model.
On PhotoTourism pairs this raises the pose mAA of fundamental matrices by 0.04 to 0.1 over the subset refits below at the same sample budget. SIFT matches take a third of the time on CPU and half on CUDA; ALIKED matches with LightGlue, on which both stop early, take about 0.5 ms longer on CPU. On the HEB homographies it adds about 0.04 mAA at a quarter to a half of the time.
Essential matrices are estimated in the caller’s calibrated coordinates, without
the normalization, which would take them off the essential manifold, and are
returned with unit Frobenius norm and their largest entry positive. They are
refined as E = U diag(1, 1, 0) V^T with five parameters, as many as the
rotation and translation direction of PoseLib’s refine_relpose: rotations
of U about three axes and of V about its first two, which leaves out
the rotation of both about their third axes that does not change E. The
five-point samples are solved with Nister’s method in float64: a Householder
null space, the degree-ten polynomial from polynomial products, and its real
roots from the eigenvalues of its companion matrix on the host. MPS, which has
no float64, solves its samples on the host. On PhotoTourism pairs this raises
the pose mAA of essential matrices by 0.03 to 0.07 over the subset refits
below, in about half the time on CPU, and an eighth of it with 8000 SIFT
features per image.
local_optimization="dlt", the only choice for line-segment homographies,
refits the incumbent on its inliers. For the
homography models the refit is the iteratively re-weighted least squares of
find_homography_dlt_iterated(), whose Gaussian
weights use inl_th as their standard deviation, so a correspondence at the
threshold keeps weight exp(-1/2). A refit
replaces the model when it raises the score, or when it ties it: a least-squares
fit on the same support is more precise than the minimal-sample model. A refit
that does not raise the score ends the refinement. This is iterative refitting,
not the full LO-RANSAC algorithm with inner resampling and a threshold schedule.
Set max_lo_iters=0 to disable it. By default, lo_sample_size=32 caps each
randomized non-minimal refit: max_lo_iters independent 32-inlier subsets are
fit in one solver batch, then the best accepted consensus gets a full-inlier
refit. Consensus sets no larger than the cap use ordinary iterative full-inlier
refitting, which lo_sample_size=None selects for every consensus set. This
bounded variant is inspired by
Lebeda et al. (BMVC 2012);
it does not implement the complete LO+ algorithm. The cap must be at least the
non-minimal solver’s sample size (eight for fundamental/essential matrices).
On PhotoTourism fundamental matrices the subset refits match or beat full-inlier
refitting at a lower cost on both CPU and CUDA.
Compiled estimation#
compile=True (torch 2.14 or later) runs the whole local_optimization="lm"
estimation as one torch.compile graph on CPU and CUDA. The sampling loop is a
torch.while_loop that carries the pool of the best minimal models, the
early-stopping bound is computed inside the graph, and the batch sizes and the
number of models that survive the degeneracy tests are dynamic. The graph does
not depend on the number of correspondences, the threshold, the confidence or
the sample budget: it is traced once per model type, score type, iteration
counts, dtype and device. The first call compiles it, in 30 s for homographies
to 60 s for essential matrices on an Apple M1, and saves the compiled function
next to inductor’s cache (TORCHINDUCTOR_CACHE_DIR); later processes load it
in about a second. KORNIA_RANSAC_AOT=0 disables that file, and each process
then compiles again, in 17 to 35 s once inductor’s kernels are cached.
Ordinary calls and calls under torch.inference_mode() share the same artifact.
Compiled arithmetic also runs with autocast disabled, using the input’s working
precision and float64 host refinement. The artifact cache distinguishes CPU
thread counts, default dtypes and default devices; changing these settings may require another
first-call compilation. On CPU, large explicit sampling batches are split so
scoring holds at most 2**22 residuals per batch (or one hypothesis when
N alone exceeds that limit). This keeps the total sample budget and can
check confidence stopping earlier than the requested batch size would.
The sampling keys are a counter-based hash of the call’s seed and each
sample’s index, computed inside the graph, and Floyd’s algorithm turns them
into subsets on every device, where compilation fuses its steps into one
kernel. A sample’s keys therefore depend neither on batch boundaries nor on the
device, calls with different seeds do not share samples, and seeded calls
never touch a global generator, including during concurrent calls. An unseeded
call draws one seed from the input device’s global generator and then hashes it
as a seeded call does.
On CUDA the loop counters and stopping bound stay on the host, with a transfer
of the leading score and inlier count after each batch; compilation therefore
does not remove every host-device synchronization.
For the initial implementation at revision ad91a621, on that CPU
(4 threads, synthetic scenes of 500 to 5000 correspondences with
20% or 50% inliers, averaged over seeds), compiled calls were 1.2 to 1.7 times
faster for homographies, 1.6 to 2.4 times for fundamental matrices and 1.1 to
1.6 times for essential matrices, whose five-point solver gained nothing: its
time goes to LAPACK factorizations and the eigenvalues of the companion matrix.
The compiled call runs the same algorithm with its own random stream, so a
seeded call is reproducible but differs from the eager one. On synthetic
scenes of 500 and 2000 correspondences with 20% to 50% inliers its mean inlier
recall was within 0.015 of eager’s for homographies and essential matrices and
within 0.03 for fundamental matrices, where eager runs with other seeds differ
from each other as much (60 seeds per case at 20% and 30% inliers). PROSAC
sampling, degensac=True and local_optimization="dlt" are not supported.
The current sampler draws a different seeded stream;
use benchmarks/geometry/ransac_compile_synthetic.py to measure current
latency and recovery, including --confidence 1 for equal sample budgets.
Dominant planes#
A seven-point sample with five correspondences on one plane yields a fundamental
matrix consistent with the whole plane, whatever its two other correspondences
are (Chum, Werner and Matas, CVPR 2005). When most inliers lie on one plane, such
a wrong model can have the most support and stop the sampling, and no refinement
recovers the off-plane inliers it never counted. "fundamental" and
"fundamental_7pt" support opt-in DEGENSAC (degensac=True): record-setting samples
are tested for this degeneracy, and a degenerate one hands its plane homography
to a plane-and-parallax search over the correspondences off the plane, whose
best model, refined, competes with the minimal ones. Each plane is searched once
per call.
sampson_homography_distance() measures the
plane. The test’s tolerance, taken from Chum’s implementation, also flags samples
in scenes without a dominant plane, so the estimate for a given seed can change
there too. degensac=False (the default) and degensac=None leave it off,
retaining the plain seven-point behavior.
- class kornia.geometry.ransac.RANSAC(model_type='homography', inl_th=2.0, batch_size='auto', max_iter=10, confidence=0.99, max_lo_iters=5, score_type='msac', prosac_sampling=False, seed=None, lo_sample_size=32, max_samples=None, local_optimization=None, refine_iters=3, degensac=False, compile=False)[source]#
Module for robust geometry estimation with RANSAC. https://en.wikipedia.org/wiki/Random_sample_consensus.
- Convention:
kp1andkp2are passed as the first- and second-image arguments of the estimator selected bymodel_type, so a homography mapskp1tokp2.forwardreturns a(3, 3)model and an(N,)bool inlier mask, or an all-zero model and no inliers when no candidate has more inliers than its minimal sample (three correspondences for affine transforms, four for homographies, five for"essential", seven for"fundamental"and"fundamental_7pt", eight for"fundamental_8pt")."fundamental"draws seven-point samples, like"fundamental_7pt";"fundamental_8pt"draws eight-point ones.inl_this in the keypoints’ own units (pixels, or calibrated units for"essential"): a correspondence is an inlier when its one-way transfer error for"homography"or"affine", its Sampson distance for the fundamental models and"essential", or the mean distance of its transferred endpoints from the image-2 segment’s line for"homography_from_linesegments"is at mostinl_th. Two-view geometry compares this with OpenCV.score_type="msac"(the default) ranks candidates bysum(1 - min(e / inl_th**2, 1))over the squared errorse; acceptance and early stopping count inliers for either score.local_optimization="lm", the default for homographies, fundamental and essential matrices, keeps the eight best-scoring minimal models of the whole run. After sampling, it refines them together withmax_lo_itersLevenberg-Marquardt iterations on all correspondences with the squared error truncated atinl_th, keeps the best-scoring refit or minimal model, and refines that one on its inliers withrefine_itersiterations of a Cauchy loss of scaleinl_th / 3. The returned mask holds the inliers of the returned model after conversion to the input dtype; if that conversion loses sufficient support, the call returns the all-zero failure result. Early stopping follows the support of the best minimal model. The refinements run on the CPU in float64 whatever the device of the correspondences: they are a few dozen small operations per iteration, which launch latency makes slower on an accelerator. Homographies and fundamental matrices are estimated on Hartley-normalized correspondences; essential matrices in the caller’s calibrated coordinates, which a normalization would take off the essential manifold, refined on that manifold and returned with unit Frobenius norm.local_optimization="dlt", the only choice for"affine"and line segments, refits each new best model from its inliers: with the defaultlo_sample_size=32,max_lo_itersrandomized refits on 32-inlier subsets followed by one full-inlier refit;lo_sample_size=Nonerefits all inliers iteratively. Affine models use a linear least-squares refit with free translation and return a homogeneous matrix with bottom row[0, 0, 1]; their minimal samples need three non-collinear source points.degensac=True, an opt-in for"fundamental"and"fundamental_7pt"withlocal_optimization="lm", runs DEGENSAC (Chum, Werner and Matas, CVPR 2005). Every seven-point model that sets a new record among the raw scores is tested for an H-degenerate sample: five or more of its seven correspondences related by one homography. Such a model fits the dominant plane and whatever happens to agree with it off the plane, so the homography is refined, and fundamental matrices are drawn from it and pairs of correspondences off the plane (plane and parallax). The best of them, refined, joins the eight-model pool and, when it outscores the incumbent, sets the stopping bound. Thresholds and iteration counts follow Chum’s implementation in pydegensac; the draws come from a private host generator. A plane already searched in the call is not searched again: a sample whose homography’s inliers lie 95% or more inside it is skipped before refinement, and a refined plane whose inliers match it (Jaccard index 0.95 or more) before the search. The default,degensac=False, keeps the plain seven-point loop. Explicit recovery reproduces that result exactly when no record-setting sample is degenerate. Chum’s tolerance, three times the squared threshold for five of the seven correspondences, also flags samples in many scenes without a dominant plane; there the recovered models only join the competition.prosac_sampling=Trueexpects correspondences sorted best-first and stops with PROSAC’s termination-length test;confidence=1runs the wholebatch_size * max_iterbudget.A seeded call uses a private generator and leaves torch’s global RNG state unchanged;
seed=Nonedraws the minimal samples from the global generator. DEGENSAC’s recovery draws always come from a private host generator, which an unseeded call seeds from the sample that triggers the recovery.compile=Trueruns the wholelocal_optimization="lm"estimation as onetorch.compilegraph (torch 2.14 or later, CPU and CUDA). The graph is traced once per configuration and does not depend on the number of correspondences, the threshold, the confidence or the budget; it is also saved next to inductor’s cache (TORCHINDUCTOR_CACHE_DIR), so a later process loads it instead of compiling. It runs the same algorithm with its own random stream: a seeded call is reproducible and leaves every global generator unchanged, while an unseeded call consumes one global random seed. Its result is not the eager one. PROSAC sampling and DEGENSAC are not supported.
- Parameters:
model_type (
str, optional) – “affine”, “homography”, “fundamental”, “fundamental_7pt”, “fundamental_8pt”, “essential”, or “homography_from_linesegments”. Default:"homography"inl_th (
float, optional) – positive inlier threshold, in the units given above. Default:2.0batch_size (
Union[int,str], optional) – hypotheses generated and verified at once, or"auto"to pick the batches per call from the device and the number of correspondences (seeresolve_batch_size()). Default:"auto"max_iter (
int, optional) – with an integerbatch_size, the maximum number of batches, for a budget ofbatch_size * max_iterminimal samples; with"auto", the budget is2048 * max_iter. The seven- and five-point solvers can return multiple models per sample. Default:10confidence (
float, optional) – stopping confidence in(0, 1]; 1 disables early stopping. Default:0.99max_lo_iters (
int, optional) – local optimization iterations: Levenberg-Marquardt iterations withlocal_optimization="lm", refits with"dlt"; zero disables local optimization. Default:5score_type (
str, optional) – “msac” (default) for truncated squared residuals, or “ransac” for support count. Default:"msac"prosac_sampling (
bool, optional) – use PROSAC sampling on best-first ordered correspondences. The growth schedule advances per sampled set within each batch; stopping tests the incumbent’s support within ranked prefixes (Chum and Matas, 2005, section 2.2) as well as within the whole set. It pays off when the ranking tracks inlier-ness and the inlier ratio is low (ratio-tested SIFT). On learned matches with 85% or more inliers it can certify a model fitted to a spatially clustered top-ranked prefix after one batch and score below uniform sampling; use uniform sampling orconfidence=1there. Default:Falseseed (
Optional[int], optional) – optional seed, reset on each call for reproducible estimation on the same device. Default:Nonelo_sample_size (
Optional[int], optional) – withlocal_optimization="dlt", the inlier-subset size for a batch ofmax_lo_itersrandomized local refits followed by a full-inlier refit (default 32). None uses iterative full-inlier refitting. Unused with"lm". Default:32max_samples (
Optional[int], optional) – optional budget of minimal samples that overrides the one implied bybatch_sizeandmax_iter; the last batch is truncated to it. Default:Nonelocal_optimization (
Optional[str], optional) –"lm"or"dlt", as described above; None picks"lm"for homographies, fundamental and essential matrices and"dlt"for affine transforms and line segments. Default:Nonerefine_iters (
int, optional) – Levenberg-Marquardt iterations of the final refinement withlocal_optimization="lm"; zero disables it. Default:3compile (
bool, optional) – runlocal_optimization="lm"as one compiled graph, as described above. Default:Falsedegensac (
Optional[bool], optional) – run DEGENSAC’s dominant-plane recovery, as described above. Defaults to False; None also leaves it off. True enables it for"fundamental"and"fundamental_7pt"withlocal_optimization="lm", the only supported combinations; True with any other raisesValueError. Default:False
- forward(kp1, kp2, weights=None)[source]#
Call main forward method to execute the RANSAC algorithm.
- Parameters:
kp1 (
Tensor) – source image keypoints \((N, 2)\) (or line segments \((N, 2, 2)\)).kp2 (
Tensor) – target image keypoints with the same shape. For PROSAC, both inputs must be sorted best-first using the same correspondence-quality ordering. Essential estimation uses camera-normalized coordinates and a threshold in those units.weights (
Optional[Tensor], optional) – optional correspondences weights. Not used now. Default:None
- Return type:
- Returns:
Estimated model, shape of \((3, 3)\), or zeros if no valid model is found. A model needs more inliers than its minimal sample, since a model fitted to a sample fits that sample.
Boolean inlier mask, shape of \((N,)\), in the supplied correspondence order.
- resolve_batch_size(num_tc, device)[source]#
Return the batch size of a
local_optimization="dlt"call: the configured one, or the"auto"choice.On CUDA and MPS a homography or affine batch costs about the same from a few hundred up to 8192 hypotheses (the four- and three-point solves are launch-bound), so the whole
sample_budgetis drawn in batches of up to 8192; the epipolar solvers are compute-bound past 2048 hypotheses, so their batches stop there and early stopping is checked in between. The verification holds abatch x Nresidual matrix and a few temporaries of that size, so past2**27entries (about 1 GiB at peak in float32) the batch shrinks withN, down to the 2048 of the historical fixed batch. On CPU the cost is linear inbatch * Nresiduals, and the eight-point solver is about ten times a DLT, so the batch aims at a millisecond or so of work, 256 to 2048 hypotheses for homographies and 128 to 512 for affine and the epipolar models: a three-point affine sample needs far fewer draws than a four-point one, and the smaller batches let early stopping act sooner (1.1-4.3x faster at equal recall on 500-8000 synthetic matches with 10-50% inliers). Other devices retain the historical 2048-sample batch.With
local_optimization="lm"an"auto"batch instead starts at 256 samples on CPU (512 for homographies) and doubles after every batch, up to 2048 (4096 for homographies), so that inputs with many inliers stop after a small first batch while the rest pay the per-batch overhead a few times only. On CUDA and MPS it is the whole budget up to 8192 samples. Essential matrices start at 64 samples on CPU, up to 1024, and at 256 on CUDA and MPS, up to 8192: a five-point sample needs few draws at high inlier ratios, and its host eigenvalue solve costs the same on every device. All shrink, down to 64 samples, when a batch would score more than2**22(CPU) or2**25(accelerators) residuals, counting the three models of a seven-point sample and the ten candidate slots of a five-point one. On accelerators scoring holds two or three times that many entries at its peak, about 0.5 GiB in float32; on CPU it scores tiles of about a million residuals, independently of the batch. An integerbatch_sizeis kept for every batch, except that compiled CPU RANSAC splits a batch whose candidate-by-correspondence score matrix would exceed the CPU memory bound. This preserves the sample budget, though the extra batch boundaries can change when confidence-based stopping is evaluated.- Return type: