github.com/kocurvik/fastpose · Benchmark · ETH3D · ScanNet++ · PhotoTourism

PoseLib accuracy, a fraction of the runtime.

fastpose is a numba-compiled LO-RANSAC engine for camera pose estimation. It includes estimators for calibred/uncalibrated relative pose, relative pose with known depth (RePoseD), homography and fundamental matrix estimation, and calibrated/uncalibrated absolute pose. Against PoseLib’s C++ implementation on the same correspondences, budget and error thresholds, it lands the same relative poses — a median difference of a few hundredths of an mAA point — and gets there two to ten times sooner on one CPU core, or one to two orders of magnitude sooner on a GPU.

The frontier, in one panel

ScanNet++ · calibrated RePoseD 3‑point · RoMa v2 4096

PoseLib needs 1101 ms and 5000 iterations to reach 89.01 mAA. fastpose passes it on the GPU in 12.7 ms — and beats it on a single core in 84 ms.

Install it

Two dependencies, numpy and numba. Every estimate_* function takes device='cuda' to run the same estimate batched on the GPU.

$pip install fastpose
# warm the numba kernels once, before you fork workers
from fastpose.estimators.warmup import warmup
warmup()

# calibrated relative pose, 5-point, on the CPU
from fastpose.estimators import estimate_relative_pose
model, info = estimate_relative_pose(x1, x2, camera1=K1, camera2=K2,
                                     iterations=5000, max_error=2.0)

# the pose comes out of the model dict
R = model['R']                 # 3x3 rotation
t = model['t']                 # 3-vector translation, up to scale
inliers = info['inliers']      # boolean mask over the correspondences

# the same call, batched on the GPU
model, info = estimate_relative_pose(x1, x2, camera1=K1, camera2=K2,
                                     iterations=5000, max_error=2.0,
                                     device='cuda')
R, t = model['R'], model['t']

Numba compiles lazily, so the first call to each estimator is slow. Run fastpose-warmup (or --device cuda) up front, and always warm the cache before forking worker processes.

The accuracy–time frontier

Each point is one RANSAC budget, 100 to 5000 iterations, and 20 000 on the GPU. Accuracy saturates early, so the curves separate along the horizontal rather than the vertical. Note the compressed accuracy axis: a panel usually spans about one mAA point, so a curve further left is faster at equal accuracy, not a trade against it.

Matcher
Correspondences

Applies to every chart on this page.

On the GPU, the budget stops being a trade-off

On a CPU every iteration is paid for in full, so the budget trades against latency. The CUDA backend runs the batch on the device, where 5000 iterations — past the flattening of the curves above — costs roughly what a single core needs for its cheapest 100-iteration run. Going on to 20 000 costs about 60% more, and moves accuracy by under a point either way.

Runtime as the budget grows

Speed-up by solver

Wall-clock time per pair at a matched 5000 iterations, divided by PoseLib’s time on the same pairs; 1× is parity. The scale is logarithmic because the GPU sits two orders of magnitude from the CPU. Median over the datasets each solver runs on.

Speed-up over PoseLib — matched at 5000 iterations

How this was measured

Data

Produced by fastposebench.

Every 5th pair of the standard benchmark: ETH3D and ScanNet++ (shared intrinsics) and PhotoTourism (varying intrinsics). Correspondences from RoMa v2 base and from LoMa; the budget selector is total sampled matches for RoMa v2 and maximum keypoints for LoMa.

Matched conditions

Both backends see the identical correspondences, the same Sampson threshold (2.0 px), the same truncated reprojection threshold (16.0 px) and the same iteration count. Timings are mean wall-clock per pair and exclude the depth network, which costs about 88 ms per pair on its own.

Hardware

CPU runs are one process pinned to 1 or 4 cores. The GPU column is the CUDA backend running the whole estimate batched on one device, at 5000–20 000 iterations; lower budgets do not fill the device and are not reported.

Accuracy

Pose mAA@10° is the mean average accuracy of the max of rotation and translation error, integrated to a 10° threshold, in percent. Higher is better. mAA@5° and the median pose error are in the table views.

Citing fastpose

If you found fastpose useful please consider citing:

@misc{fastpose,
  title  = {{fastpose -- A Fast Python Backend for Robust Camera Pose Estimation}},
  author = {Kocur, Viktor},
  url    = {https://github.com/kocurvik/fastpose},
  year   = {2026}
}

References

fastpose is a numba port of algorithms implemented in PoseLib. If you use it, please cite PoseLib and the papers behind the solvers you actually run.

Code & data

  • fastpose — the library benchmarked here.
  • fastposebench — the harness that produced every number on this page.
  • PoseLib — Larsson and contributors, the C++ baseline and the source of the ported solvers.

Datasets

  • ETH3D — Schöps, Schönberger, Galliani, Sattler, Schindler, Pollefeys and Geiger, A Multi-View Stereo Benchmark with High-Resolution Images and Multi-Camera Videos, CVPR 2017.
  • ScanNet++ — Yeshwanth, Liu, Nießner and Dai, ScanNet++: A High-Fidelity Dataset of 3D Indoor Scenes, ICCV 2023.
  • PhotoTourism — the IMC 2021 release; Jin, Mishkin, Mishchuk, Matas, Fua, Yi and Trulls, Image Matching across Wide Baselines: From Paper to Practice, IJCV 2021.

Correspondences & depth

  • RoMa v2 — Edstedt, Harder Better Faster Denser Feature Matching; dense matcher, base model.
  • LoMa — sparse keypoint matcher, LoMaB configuration.
  • MoGe — monocular geometry; the RePoseD solvers are fed MoGe‑v1 ViT‑L depth.

Solvers on this page

  • Calibrated 5-point — Nistér, An efficient solution to the five-point relative pose problem, TPAMI 2004; nullspace and action-matrix formulation from Stewénius, Engels and Nistér, ISPRS 2006.
  • Shared-focal 6-point — Stewénius, Nistér, Kahl and Schaffalitzky, A minimal solution for relative pose with unknown focal length, IVC 2008; PoseLib’s solver generated with Larsson, Åström and Oskarsson, CVPR 2017.
  • Varying-focal 7-point — Hartley-style isotropic normalization, In defense of the eight-point algorithm, TPAMI 1997; focal lengths recovered with Rybkin’s closed form (2017), an SVD-free equivalent of Bougnoux, ICCV 1998. The pipeline appears as a baseline in Kocur et al., Are Minimal Radial Distortion Solvers Really Necessary for Relative Pose Estimation?, IJCV 2026.
  • RePoseD 3-point — Ding, Kocur, Vávra, Berger Haladová, Yang, Sattler and Kukelova, RePoseD: Efficient Relative Pose Estimation With Known Depth Information, ICCV 2025.