Skip to main content

COLMAP bindings

Project description

Python bindings for COLMAP

This repository exposes to Python most COLMAP reconstruction objects, such as Cameras and Points3D, as well as estimators for absolute and relative poses.

Getting started

Wheels for Linux (Python 3.6/7/8/9) and macOS 10 & 11 (Python 3.7/8/9) can be install using pip:

pip install pycolmap

The wheels are automatically built and pushed to pypi at each release.

Building from source

Alternatively, we explain below how to compile PyCOLMAP from source.

COLMAP should first be installed as a library following the official guide. We recommend always building PyCOLMAP against the latest commit of the COLMAP dev branch - using a previous COLMAP build might not work. Currently, the earliest supported COLMAP commit is colmap/colmap@33e2692.

Then clone the repository and its submodules:

git clone --recursive git@github.com:colmap/pycolmap.git

And finally build PyCOLMAP:

cd pycolmap
pip install .

Windows

Building on Windows is currently not supported - please do not open any issues regarding this. Any contributions in this direction are welcome - please refer to issue #34. As a temporary workaround, we suggest using WSL.

Reconstruction pipeline

PyCOLMAP provides bindings for multiple steps of the standard reconstruction pipeline. They are defined in pipeline/ and include:

  • extracting and matching SIFT features
  • importing an image folder into a COLMAP database
  • inferring the camera parameters from the EXIF metadata of an image file
  • running two-view geometric verification of matches on a COLMAP database
  • triangulating points into an existing COLMAP model
  • running incremental reconstruction from a COLMAP database
  • dense reconstruction with multi-view stereo

Sparse & Dense reconstruction from a folder of images can be performed with:

output_path: pathlib.Path
image_dir: pathlib.Path

output_path.mkdir()
mvs_path = output_path / "mvs"
database_path = output_path / "database.db"

pycolmap.extract_features(database_path, image_dir)
pycolmap.match_exhaustive(database_path)
maps = pycolmap.incremental_mapping(database_path, image_dir, output_path)
maps[0].write(output_path)
# dense reconstruction
pycolmap.undistort_images(mvs_path, output_path, image_dir)
pycolmap.patch_match_stereo(mvs_path)  # requires compilation with CUDA
pycolmap.stereo_fusion(mvs_path / "dense.ply", mvs_path)

PyCOLMAP can leverage the GPU for feature extraction, matching, and multi-view stereo if COLMAP was compiled with CUDA support.

Similarly, PyCOLMAP can run Delauney Triangulation if COLMAP was compiled with CGAL support. This requires to build the package from source and is not available with the PyPI wheels.

All of the above steps are easily configurable with python dicts which are recursively merged into their respective defaults, e.g.

pycolmap.extract_features(database_path, image_dir, sift_options={"max_num_features": 512})
# equivalent to
ops = pycolmap.SiftExtractionOptions()
ops.max_num_features = 512
pycolmap.extract_features(database_path, image_dir, sift_options=ops)

To list available options, use

help(pycolmap.SiftExtractionOptions)

The default parameters can be looked up with

print(pycolmap.SiftExtractionOptions().summary())
# or
print(pycolmap.SiftExtractionOptions().todict())

For another example of usage, see hloc/reconstruction.py.

Reconstruction object

We can load and manipulate an existing COLMAP 3D reconstruction:

import pycolmap
reconstruction = pycolmap.Reconstruction("path/to/my/reconstruction/")
print(reconstruction.summary())

for image_id, image in reconstruction.images.items():
    print(image_id, image)

for point3D_id, point3D in reconstruction.points3D.items():
    print(point3D_id, point3D)

for camera_id, camera in reconstruction.cameras.items():
    print(camera_id, camera)

reconstruction.write("path/to/new/reconstruction/")

The object API mirrors the COLMAP C++ library. The bindings support many other operations, for example:

  • projecting a 3D point into an image with arbitrary camera model:
uv = camera.world_to_image(image.project(point3D.xyz))
  • aligning two 3D reconstructions by their camera poses:
tfm = reconstruction1.align_poses(reconstruction2)  # transforms reconstruction1 in-place
print(tfm.rotation, tfm.translation)

pycolmap also allows exporting models to TXT/NVM//CAM/Bundler/VRML/PLY (also supports pathlib.Path inputs). skip_distortion == True enables exporting more camera models, with the caveat of averaging the focal length parameters.

# Exports reconstruction to COLMAP text format.
reconstruction.write_text("path/to/new/reconstruction/")

# Exports in NVM format http://ccwu.me/vsfm/doc.html#nvm.
reconstruction.export_NVM("rec.nvm", skip_distortion=False)

# Creates a <img_name>.cam file for each image with pose/intrinsics information.
reconstruction.export_CAM("image_dir/", skip_distortion=False)

# Exports in Bundler format https://www.cs.cornell.edu/~snavely/bundler/.
reconstruction.export_bundler("rec.bundler.out", "rec.list.txt", skip_distortion=False)

# exports 3D points to PLY format.
reconstruction.export_PLY("rec.ply")

# Exports in VRML format https://en.wikipedia.org/wiki/VRML.
reconstruction.export_VRML("rec.images.wrl", "rec.points3D.wrl",
                           image_scale=1.0, image_rgb=[1.0, 0.0, 0.0])

Estimators

We provide robust RANSAC-based estimators for absolute camera pose (single-camera and multi-camera-rig), essential matrix, fundamental matrix, homography, and two-view relative pose for calibrated cameras.

All RANSAC and estimation parameters are exposed as objects that behave similarly as Python dataclasses. The RANSAC options are described in colmap/src/optim/ransac.h and their default values are:

ransac_options = pycolmap.RANSACOptions(
    max_error=4.0,  # reprojection error in pixels
    min_inlier_ratio=0.01,
    confidence=0.9999,
    min_num_trials=1000,
    max_num_trials=100000,
)

Absolute pose estimation

For instance, to estimate the absolute pose of a query camera given 2D-3D correspondences:

# Parameters:
# - points2D: Nx2 array; pixel coordinates
# - points3D: Nx3 array; world coordinates
# - camera: pycolmap.Camera
# Optional parameters:
# - max_error_px: float; RANSAC inlier threshold in pixels (default=12.0)
# - estimation_options: dict or pycolmap.AbsolutePoseEstimationOptions
# - refinement_options: dict or pycolmap.AbsolutePoseRefinementOptions
answer = pycolmap.absolute_pose_estimation(points2D, points3D, camera, max_error_px=12.0)
# Returns: dictionary of estimation outputs

2D and 3D points are passed as Numpy arrays or lists. The options are defined in estimators/absolute_pose.cc and can be passed as regular (nested) Python dictionaries:

pycolmap.absolute_pose_estimation(
    points2D, points3D, camera,
    estimation_options={'ransac': {'max_error': 12.0}},
    refinement_options={'refine_focal_length': True},
)

Absolute Pose Refinement

# Parameters:
# - tvec: List of 3 floats, translation component of the pose (world to camera)
# - qvec: List of 4 floats, quaternion component of the pose (world to camera)
# - points2D: Nx2 array; pixel coordinates
# - points3D: Nx3 array; world coordinates
# - inlier_mask: array of N bool; inlier_mask[i] is true if correpondence i is an inlier
# - camera: pycolmap.Camera
# Optional parameters:
# - refinement_options: dict or pycolmap.AbsolutePoseRefinementOptions
answer = pycolmap.pose_refinement(tvec, qvec, points2D, points3D, inlier_mask, camera)
# Returns: dictionary of refinement outputs

Essential matrix estimation

# Parameters:
# - points2D1: Nx2 array; pixel coordinates in image 1
# - points2D2: Nx2 array; pixel coordinates in image 2
# - camera1: pycolmap.Camera of image 1
# - camera2: pycolmap.Camera of image 2
# Optional parameters:
# - max_error_px: float; RANSAC inlier threshold in pixels (default=4.0)
# - options: dict or pycolmap.RANSACOptions
answer = pycolmap.essential_matrix_estimation(points2D1, points2D2, camera1, camera2)
# Returns: dictionary of estimation outputs

Fundamental matrix estimation

answer = pycolmap.fundamental_matrix_estimation(
    points2D1,
    points2D2,
    [max_error_px],  # optional RANSAC inlier threshold in pixels
    [options],       # optional dict or pycolmap.RANSACOptions
)

Homography estimation

answer = pycolmap.homography_matrix_estimation(
    points2D1,
    points2D2,
    [max_error_px],  # optional RANSAC inlier threshold in pixels
    [options],       # optional dict or pycolmap.RANSACOptions
)

Two-view geometry estimation

COLMAP can also estimate a relative pose between two calibrated cameras by estimating both E and H and accounting for the degeneracies of each model.

# Parameters:
# - points2D1: Nx2 array; pixel coordinates in image 1
# - points2D2: Nx2 array; pixel coordinates in image 2
# - camera1: pycolmap.Camera of image 1
# - camera2: pycolmap.Camera of image 2
# Optional parameters:
# - max_error_px: float; RANSAC inlier threshold in pixels (default=4.0)
# - options: dict or pycolmap.TwoViewGeometryOptions
answer = pycolmap.homography_matrix_estimation(points2D1, points2D2)
# Returns: dictionary of estimation outputs

The options are defined in estimators/two_view_geometry.cc and control how each model is selected. The return dictionary contains the relative pose, inlier mask, as well as the type of camera configuration, such as degenerate or planar. This type is an instance of the enum pycolmap.TwoViewGeometry whose values are explained in colmap/src/estimators/two_view_geometry.h.

Camera argument

All estimators expect a COLMAP camera object, which can be created as follow:

camera = pycolmap.Camera(
    COLMAP_CAMERA_MODEL_NAME,
    IMAGE_WIDTH, 
    IMAGE_HEIGHT,
    EXTRA_CAMERA_PARAMETERS,
)

The different camera models and their extra parameters are defined in colmap/src/base/camera_models.h. For example for a pinhole camera:

camera = pycolmap.Camera(
    model='SIMPLE_PINHOLE',
    width=width,
    height=height,
    params=[focal_length, cx, cy],
)

Alternatively, we can also pass a camera dictionary:

camera_dict = {
    'model': COLMAP_CAMERA_MODEL_NAME,
    'width': IMAGE_WIDTH,
    'height': IMAGE_HEIGHT,
    'params': EXTRA_CAMERA_PARAMETERS_LIST
}

SIFT feature extraction

import numpy as np
import pycolmap
from PIL import Image, ImageOps

# Input should be grayscale image with range [0, 1].
img = Image.open('image.jpg').convert('RGB')
img = ImageOps.grayscale(img)
img = np.array(img).astype(np.float) / 255.

# Optional parameters:
# - options: dict or pycolmap.SiftExtractionOptions
# - device: default pycolmap.Device.auto uses the GPU if available
sift = pycolmap.Sift()

# Parameters:
# - image: HxW float array
keypoints, scores, descriptors = sift.extract(img)
# Returns:
# - keypoints: Nx4 array; format: x (j), y (i), sigma, angle
# - scores: N array; DoG scores
# - descriptors: Nx128 array; L2-normalized descriptors

TODO

  • Add documentation
  • Add more detailed examples
  • Add unit tests for reconstruction bindings

Created and maintained by Mihai Dusmanu, Philipp Lindenberger, John Lambert, Paul-Edouard Sarlin, and other contributors.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

pycolmap-0.4.0-cp310-cp310-manylinux2014_x86_64.whl (14.4 MB view details)

Uploaded CPython 3.10

pycolmap-0.4.0-cp310-cp310-macosx_11_0_x86_64.whl (21.2 MB view details)

Uploaded CPython 3.10 macOS 11.0+ x86-64

pycolmap-0.4.0-cp39-cp39-manylinux2014_x86_64.whl (14.4 MB view details)

Uploaded CPython 3.9

pycolmap-0.4.0-cp39-cp39-macosx_11_0_x86_64.whl (21.2 MB view details)

Uploaded CPython 3.9 macOS 11.0+ x86-64

pycolmap-0.4.0-cp38-cp38-manylinux2014_x86_64.whl (14.4 MB view details)

Uploaded CPython 3.8

pycolmap-0.4.0-cp38-cp38-macosx_11_0_x86_64.whl (21.2 MB view details)

Uploaded CPython 3.8 macOS 11.0+ x86-64

pycolmap-0.4.0-cp37-cp37m-manylinux2014_x86_64.whl (14.4 MB view details)

Uploaded CPython 3.7m

pycolmap-0.4.0-cp37-cp37m-macosx_11_0_x86_64.whl (21.2 MB view details)

Uploaded CPython 3.7m macOS 11.0+ x86-64

File details

Details for the file pycolmap-0.4.0-cp310-cp310-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp310-cp310-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 734436da8ea7bb5242aa31b73c181aab683530eedbf120d2822ca0093f1bc6af
MD5 6f516c88780a3b1fb63d65671543f08e
BLAKE2b-256 4bbb90fb7e73617694c411c95f48dd51125f4eb6fa7baf16164ac730bd494809

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp310-cp310-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp310-cp310-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 5e56dd896dd2192dfecc3e69b22f48b72279ec836d77ffe3f5c41b36854ce2a8
MD5 43bdbc7a103d0c2108baee00063860cf
BLAKE2b-256 0055fb31c8b05d07f0b915fe2d371f55f9348118bae7ad216ae96beddd84b370

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp39-cp39-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp39-cp39-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 aa99cc2e9d5bb30f9b77d40f8d143f9492d0fdf2f4b6b3c4456fb5779012c97f
MD5 e1e9ea4bbffda38e02509287e7cfc2b9
BLAKE2b-256 8acbaee03c211fb8cbed91786b15e1cf3dcb35665df71603433bfc889a504c84

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp39-cp39-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp39-cp39-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 0fe640045f52765a0b56b21e50f5cda6a8c8cbd6ed28d22585deff348cd0324d
MD5 77368ee5a01c94f3cc2e81d4d1342a9e
BLAKE2b-256 da441c09b2282f063f7abebce1d4e343542b6b0d7b77c7cb4e77d8240c788e63

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp38-cp38-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp38-cp38-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cc764ccf1a5f74f31c35087b416b272ac0f700207bfcfdb7bc7c230ca8a84014
MD5 41daab542274eaa2d5b8a84454166e8f
BLAKE2b-256 00a3baa6c77d7d9f33b4dfc1cad742d5147adae185609803ba5fd55fed671766

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp38-cp38-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp38-cp38-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 28cd1dcbb7be363a1ed012324169bcebd007557d352abb07b0077c45b6ad836d
MD5 6dd29d34b18489cf3af72a2360121425
BLAKE2b-256 720b63573940d1a2ebea8238f765d093ad4c90af7ac2ee472dc2deee4712d306

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp37-cp37m-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp37-cp37m-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8fc9fc450cd64e06c28b8cd444d4877c5910ed5f18a3e4c247dd1cafdfa6c132
MD5 bb6be5c4caa690e294a5f96115ad972b
BLAKE2b-256 cc162f0c9af27e081ba797dc4d332354b3974df2f3662dae13e1145014747022

See more details on using hashes here.

File details

Details for the file pycolmap-0.4.0-cp37-cp37m-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pycolmap-0.4.0-cp37-cp37m-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 91142665c48883e63fcf3f3e31ff6130401f9e6d39c9409c3b12bf6dff7d3967
MD5 bce8406b015e162fee83a16cdb43f601
BLAKE2b-256 0bd88cdd307fb7b40578b3cd9ed0a11c307c6a346919a068c26be8f282ee0bcc

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page