PoseLib: minimal solvers for calibrated camera pose, with LO-RANSAC wrapped around them
Minimal solvers for calibrated camera pose estimation
At a glance
- What is it?
- PoseLib is a C++ library of minimal solvers for camera pose estimation, covering point-point, point-line, line-point, and line-line correspondences, with robust estimators built on LO-RANSAC for the common problems. Its stated goals are speed, a consistent calling interface across solvers, and minimal dependencies, with Eigen as the only external one.
- Who is it for?
- PoseLib fits a vision team that already knows the geometry and wants the solvers rather than a framework, a project integrating one problem into another pipeline, and anyone who wants to extract a single standalone solver without dragging in a whole structure-from-motion system. It is the wrong choice for a team that wants a complete camera calibration and reconstruction pipeline, and the wrong choice if you need a camera model PoseLib does not ship.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The scope is minimal solvers, and that is a deliberate boundary
PoseLib provides a collection of minimal solvers for camera pose estimation, with the focus on calibrated absolute pose estimation problems from different types of correspondences, for example point-point, point-line, line-point, and line-line.
The word minimal here is a precise technical term and not a marketing one. A minimal solver is one that solves the problem exactly given the smallest possible number of correspondences. For point-to-point absolute pose, the minimal case is three points. A RANSAC loop then draws random minimal subsets, solves each exactly, and scores the resulting hypothesis against all the data, keeping the best. The efficiency of the whole procedure comes from the minimal solver being small and fast, so the interesting engineering is in the solvers and in the surrounding estimation machinery, not in the pipeline.
The stated goals are four. A quick implementation of the current state of the art solvers. A consistent calling interface between different solvers, so that the difference between a point-point solver and a point-line solver is the data, not the API shape. Minimized dependencies, both external, currently only Eigen, and internal, with each solver being mostly stand-alone so that it is easy to extract just a specific solver to integrate into other frameworks. And outlier-tolerant estimators based on LO-RANSAC that just work out of the box for most cases.
That third goal is the one that distinguishes this from a structure from motion library. PoseLib is not a pipeline. It is a set of pieces you can take individually, and the design assumption is that the person using it knows the geometry well enough to choose the right solver and the right error metric for their data.
The project is BSD-3-Clause licensed, written in C++ with Python bindings, and the last push to the repository was on 2026-10-07.
Four outlier-tolerant estimators, and the RANSAC options that shape them
Estimators that reject outliers are provided for the most common problems: absolute pose from points and lines, relative pose from points using the essential or fundamental matrix with unknown shared focal length, relative pose from points and monocular depth estimates, homography, and generalized relative pose.
Implementing an outlier-tolerant estimator for other problems is described as fairly straightforward, with `absolute_pose.h` given as the example, and the project asks that if you implement estimators for other problems you consider submitting a pull request.
The general RANSAC options are a small struct with a few values that are worth understanding:
struct RansacOptions {
size_t max_iterations = 100000;
size_t min_iterations = 1000;
double dyn_num_trials_mult = 3.0;
double success_prob = 0.9999;
unsigned long seed = 0;
// If we should use PROSAC sampling. Assumes data is sorted
bool progressive_sampling = false;
size_t max_prosac_iterations = 100000;
// Whether to treat the input 'best_model' as an initial model and score it before running the main RANSAC loop
bool score_initial_model = false;
};The defaults are conservative, with up to 100000 iterations, at least 1000, a dynamic trial multiplier of 3.0, and a success probability of 0.9999. The dynamic multiplier is the standard adaptive RANSAC idea, where the number of trials is computed from the inlier ratio and then inflated by a factor. The success probability is the confidence that at least one all-inlier sample was drawn, which at 0.9999 means the loop will keep going until the statistical chance of having missed the good sample is negligible.
`progressive_sampling` switches to PROSAC, which assumes the data is sorted by quality and samples progressively along that order. That is a big win when the ordering is meaningful, for example a set of feature matches sorted by descriptor distance, and a liability when it is not, which is why the option defaults to off and the comment says the data is assumed sorted.
`score_initial_model` lets you hand in a model you already have and have it scored before the loop starts, which is useful when you have a prior pose from another stage of the pipeline.
The refinement options, and the recommendation to use a different loss
The non-linear refinement is configured separately:
struct BundleOptions {
size_t max_iterations = 100;
enum LossType {
TRIVIAL, TRUNCATED, HUBER, CAUCHY, TRUNCATED_CAUCHY, TRUNCATED_LE_ZACH
} loss_type = LossType::CAUCHY;
double loss_scale = 1.0;
double gradient_tol = 1e-8;
double step_tol = 1e-8;
double initial_lambda = 1e-3;
double min_lambda = 1e-10;
double max_lambda = 1e10;
bool verbose = false;
};This is a Levenberg-Marquardt style refinement, with a damping parameter that starts at 1e-3, is bounded between 1e-10 and 1e10, and two convergence tolerances on the gradient and the step. Six loss functions are available, from TRIVIAL, which applies no down-weighting at all, through HUBER and CAUCHY, to the truncated variants and TRUNCATED_LE_ZACH.
The defaults are Cauchy with a scale of 1.0, but the documentation says something more specific about the estimators, and it is a recommendation you should follow rather than the default. Most estimators directly minimize the MSAC score, using `loss_type = TRUNCATED` and `loss_scale = threshold`, over all input correspondences. In practice the project found that this works quite well and avoids recursive LO where inliers are added in steps.
And the specific advice: for the relative pose estimators using monocular depth, use `TRUNCATED_CAUCHY`.
That last note is the kind of detail that separates a library with real usage behind it from a generic RANSAC implementation. A depth-based relative pose estimator has a different noise profile from a two-view geometry estimator, and the loss function that works well for one is not the one that works well for the other.
There is also a separate set of estimator-specific options, described as containing the error thresholds and other settings, with `AbsolutePoseOptions` and `MonoDepthRelativePoseOptions` given as examples. A relative pose one looks like this:
struct RelativePoseOptions {
RansacOptions ransac;
BundleOptions bundle;
double max_error = 1.0; // Inlier threshold (in pixels)
bool tangent_sampson = false; // Whether to use tangent Sampson error in LO
bool real_focal_check = false; // Whether to use real focal length checking
};Note the inlier threshold is in pixels, which makes the options camera-dependent, and that the two booleans switch between the Sampson error and a tangent variant for the refinement.
COLMAP-compatible camera models, with one strict requirement
PoseLib uses COLMAP-compatible camera models, defined in `colmap_models.h`. Six are currently supported: SIMPLE_PINHOLE, PINHOLE, SIMPLE_RADIAL, RADIAL, OPENCV, and OPENCV_FISHEYE.
The compatibility matters more than it might sound. COLMAP is the reference implementation many structure from motion pipelines are built around, and a camera model that matches means a reconstruction can be read by COLMAP and a camera can be handed over from one to the other without a conversion step.
Adding a model is described as relatively straightforward, with an invitation to open a pull request, but there is a caveat that matters for anyone porting one: in contrast to COLMAP, PoseLib requires analytical jacobians for the distortion mappings, which makes it a bit more work to port them.
That is the sort of constraint that only appears when you try to add something. The analytical jacobian is the derivative of the distortion function, and the refinement step needs it to compute the Gauss-Newton update. A numerical approximation would work, more slowly, but the library asks for the analytic form, which is a decision that keeps the refinement fast at the cost of making new models harder to add.
The `Camera` struct contains `width` and `height` fields, and the documentation is careful that these are not used anywhere in the code base and are provided simply to be consistent with COLMAP. That is worth knowing, because a user who sets them expecting an effect will get none.
The class also provides the helper function `initialize_from_txt(str)`, which initializes the camera from a line given by the `cameras.txt` file of a COLMAP reconstruction, so a camera can be read straight out of an existing reconstruction.
The Python bindings expose the `poselib.Camera` class with read-only `focal()`, `focal_x()`, `focal_y()`, `model_name()`, and `principal_point()` methods and a read-write `params` property, and the documentation notes that this is currently only used as a return type for some methods. For supplying camera information to the estimators, the documentation says to use Python dicts instead.
The Python bindings are the practical entry point
The Python bindings can be installed by running `pip install .`, and they expose all the minimal solvers, for example `poselib.p3p(x, X)`, as well as all the outlier-tolerant estimators.
A call looks like this:
camera = {'model': 'SIMPLE_PINHOLE', 'width': 1200, 'height': 800, 'params': [960, 600, 400]}
pose, info = poselib.estimate_absolute_pose(p2d, p3d, camera, {'max_error': 16.0}, {})and a relative pose one like this:
F, info = poselib.estimate_fundamental(p2d_1, p2d_2, {'max_error': 0.75, 'progressive_sampling': True}, {})
mask_inl = info['inliers']The camera is a plain dict with the model name, the image dimensions, and the parameters, which is the COLMAP parameterisation. The two trailing dicts are the RANSAC options and the Bundle options, and the documentation explains that omitted values are set to their default, with one exception: the `loss_scale` used for the Cauchy loss is set to half of the threshold used in RANSAC, which the project says seems to be a good heuristic. Dicts with the default options can be obtained as `opt = poselib.RansacOptions()` or `poselib.BundleOptions()`.
That last convenience is more than a convenience. The defaults in C++ are struct member initialisers, so a Python caller who does not know the default can ask for it rather than guessing, and the exception for the loss scale is handled for you so the two option sets stay consistent.
The return value `info` is a dict containing information about the estimation, inliers, iterations, and so on, and the second example shows the inlier mask being pulled out of it as `info['inliers']`. The full table of estimators, with a note that it shows which error threshold each one uses, `max_error` or `max_errors`, and that all thresholds are given in pixels, is in the documentation, with the source in `pyposelib.cc` and `robust.h`.
The build is a CMake one driven from setup.py through a custom `CMakeBuild` command, with the compile options passed as CMake arguments, including turning on the Python package, building static libraries, and setting the install prefix to the extension directory so install commands work. The Windows path checks the CMake version and requires 3.1.0 or newer there.
Where PoseLib is the wrong tool
The boundaries are sharp, and they follow from the deliberate minimalism.
First, this is not a structure from motion pipeline. There is no feature detection, no matching, no bundle adjustment over a whole scene, no incremental map building, no reconstruction output. You get solvers for pose problems and outlier-tolerant estimators around them. If you want a system that takes images and produces a point cloud, PoseLib is a component of that system, not the system.
Second, the correspondence types are the boundary. The absolute pose solvers work from point-point, point-line, line-point, and line-line correspondences, which covers a great deal of structure from motion geometry, but it does not cover everything. Problems that need a different input, a homography with additional constraints, or a pose problem involving a known rotation, are not in the box, and the project says the same about the estimators: it is fairly straightforward to implement them for other problems, but that is work you do.
Third, the camera model list is six, and adding one requires analytical jacobians for the distortion. A project working with a camera whose distortion is not in that list has a porting task on its hands, and the porting is not trivial, which the documentation says outright.
Fourth, and this is a real constraint for a library of this kind, the tuning is problem-specific. The loss function that works for a two-view fundamental matrix estimator is not the one recommended for a monocular depth estimator, the inlier thresholds are in pixels and therefore depend on your image resolution and scene, and the PROSAC option only helps if your data is sorted by quality. The library gives you well-chosen defaults and then expects you to know when to move them.
Fifth, the camera struct's `width` and `height` are documented as unused, which is a small trap. The Python bindings expose a `Camera` class with read-only accessors and a read-write `params` property, but the documentation points you at plain dicts for supplying camera information to the estimators, which means there are two ways to express a camera and only one of them is the supported path.
Sixth, the release cadence is slow. The newest tags are v2.0.3 in August 2024, v2.0.4 a few days later, and v2.0.5 in August 2025, and the last push was on 2026-10-07. So the tree is being worked on while the published version is over a year old, and the current tree requires a newer toolchain than the last release did.
Editorial conclusion
PoseLib fits a vision team that already knows the geometry and wants the solvers rather than a framework, a project integrating one problem into another pipeline, and anyone who wants to extract a single standalone solver without dragging in a whole structure-from-motion system. It is the wrong choice for a team that wants a complete camera calibration and reconstruction pipeline, and the wrong choice if you need a camera model PoseLib does not ship. Check three things before you adopt it: that your correspondences are of the kinds it supports, from point-point up to line-line, that you can supply the inlier threshold in pixels for the problem you care about, and that you have read the difference between the LO-RANSAC and MSAC paths, because the recommended loss types differ between problem types and the documentation says so explicitly.
Frequently asked questions
What does PoseLib provide and what are its dependencies?
It provides a collection of minimal solvers for camera pose estimation, focused on calibrated absolute pose problems from point-point, point-line, line-point, and line-line correspondences, plus robust estimators based on LO-RANSAC. The only external dependency is Eigen, and each solver is mostly stand-alone so a single one can be extracted into another framework.
How do I install and call PoseLib from Python?
Run `pip install .` for the Python bindings, then call the estimators with a camera dict, the correspondences, the RANSAC options, and the refinement options, for example `pose, info = poselib.estimate_absolute_pose(p2d, p3d, camera, {'max_error': 16.0}, {})`. The minimal solvers are exposed directly too, for example `poselib.p3p(x, X)`.
Which loss function should I use with the PoseLib estimators?
Most estimators minimize the MSAC score directly, with `loss_type = TRUNCATED` and `loss_scale = threshold`, over all input correspondences, and the project reports that this works well and avoids recursive LO where inliers are added in steps. For the relative pose estimators using monocular depth, the recommendation is `TRUNCATED_CAUCHY`.
Which camera models does PoseLib support?
It uses COLMAP-compatible camera models and currently supports SIMPLE_PINHOLE, PINHOLE, SIMPLE_RADIAL, RADIAL, OPENCV, and OPENCV_FISHEYE. Adding a model is described as relatively straightforward, but in contrast to COLMAP, PoseLib requires analytical jacobians for the distortion mappings, which makes it more work to port.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/poselib-poselib)Was this useful?