Skip to main content

A performant Structure from Motion library for Python

Project description

pyTheia - A Python Structure-from-Motion and Geometric Vision Swiss Knife

pyTheia is based on TheiaSfM. It contains Python bindings for most of the functionalities of TheiaSfM and more.

The library is still in active development and the interfaces are not yet all fixed

With pyTheia you have access to a variety of different camera models, structure-from-motion pipelines and geometric vision algorithms.

Differences to the original library TheiaSfM

pyTheia does not aim at being an end-to-end SfM library. For example, building robust feature detection and matching pipelines is usually application and data specific (e.g. image resolution, runtime, pose priors, invariances, ...). This includes image pre- and postprocessing.

pyTheia is rather a "swiss knife" for quickly prototyping SfM related reconstruction applications without sacrificing perfomance. For example SOTA feature detection & matching, place recognition algorithms are based on deep learning, and easily usable from Python. However, using these algorithms from a C++ library is not always straighforward and especially quick testing and prototyping is cumbersome.

What was removed

Hence, we removed some libaries from the original TheiaSfM:

  • SuiteSparse: Optional for ceres, however all GPL related code was removed from src/math/matrix/sparse_cholesky_llt.cc (cholmod -> Eigen::SimplicialLDLT). This will probably be slower on large problems and potentially numerically a bit more unstable.
  • OpenImageIO: was used for image in and output and for recitification.
  • RapidJSON: Camera intrinsic in and output. Is part of cereal headers anyways.
  • RocksDB: Used for saving and loading extracted features efficiently.

Changes to the original TheiaSfM library

  • Global SfM algorithms:
    • LiGT position solver
    • Lagrange Dual rotation estimator
    • Hybrid rotation estimator
    • Possibility to fix multiple views in Robust_L1L2 solver
    • Nonlinear translation solver can fix multiple view or estimate all remaining views in reconstruction
  • Camera models
    • Double Sphere
    • Extended Unified
    • Orthographic
  • Bundle adjustment
    • Using a homogeneous representation for scene points
    • Extracting covariance information
    • Possibility to add a depth prior to 3D points
    • Position prior for camera poses (e.g. for GPS or known positions)
  • General
    • Added timestamp, position_prior_, position_prior_sqrt_information_ variables to View class Eigen::Matrix3d position_prior_sqrt_information_;
    • Added inverse_depth_, reference_descriptor, reference_bearing_ variables to Track class
    • Added covariance_, depth_prior_, depth_prior_variance_ to Feature class
  • Absolute Pose solvers
    • SQPnP
    • UncalibratedPlanarOrthographic Pose

Usage Examples

Full reconstruction example: Global, Hybrid or Incremental SfM using OpenCV feature detection and matching

Have a look at the short example: sfm_pipeline.py. Download the south_building dataset from here. Extract it somewhere and run:

python pytest/sfm_pipeline.py --image_path /path/to/south-building/images/

Creating a camera

The following example show you how to create a camera in pyTheia. You can construct it from a pt.sfm.CameraIntrinsicsPrior() or set all parameters using respective functions from pt.sfm.Camera() class.

import pytheia as pt
prior = pt.sfm.CameraIntrinsicsPrior()
prior.focal_length.value = [1000.]
prior.aspect_ratio.value = [1.]
prior.principal_point.value = [500., 500.]
prior.radial_distortion.value = [0., 0., 0., 0]
prior.tangential_distortion.value = [0., 0.]
prior.skew.value = [0]
prior.camera_intrinsics_model_type = 'PINHOLE' 
#'PINHOLE', 'DOUBLE_SPHERE', 'EXTENDED_UNIFIED', 'FISHEYE', 'FOV', 'DIVISION_UNDISTORTION'
camera = pt.sfm.Camera()
camera.SetFromCameraIntrinsicsPriors(prior)

# the camera object also carries extrinsics information
camera.SetPosition([0,0,-2])
camera.SetOrientationFromAngleAxis([0,0,0.1])

# project with intrinsics image to camera coordinates
camera_intrinsics = camera.CameraIntrinsics()
pt2 = [100.,100.]
pt3 = camera_intrinsics.ImageToCameraCoordinates(pt2)
pt2 = camera_intrinsics.CameraToImageCoordinates(pt3)

# project with camera extrinsics
pt3_h = [1,1,2,1] # homogeneous 3d point
depth, pt2 = camera.ProjectPoint(pt3_h)
# get a ray from camera to 3d point in the world frame
ray = camera.PixelToUnitDepthRay(pt2)
pt3_h_ = ray*depth + camera.GetPosition() # == pt3_h[:3]

Solve for absolute or relative camera pose

pyTheia integrates a lot of performant geometric vision algorithms. Have a look at the tests

import pytheia as pt

# absolute pose
pose = pt.sfm.PoseFromThreePoints(pts2D, pts3D) # Kneip
pose = pt.sfm.FourPointsPoseFocalLengthRadialDistortion(pts2D, pts3D)
pose = pt.sfm.FourPointPoseAndFocalLength(pts2D, pts3D)
pose = pt.sfm.DlsPnp(pts2D, pts3D)
... and more

# relative pose
pose = pt.sfm.NormalizedEightPointFundamentalMatrix(pts2D, pts2D)
pose = pt.sfm.FourPointHomography(pts2D, pts2D)
pose = pt.sfm.FivePointRelativePose(pts2D, pts2D)
pose = pt.sfm.SevenPointFundamentalMatrix(pts2D, pts2D)
... and more

# ransac estimation
params = pt.solvers.RansacParameters()
params.error_thresh = 0.1
params.max_iterations = 100
params.failure_probability = 0.01

# absolute pose ransac
correspondences2D3D = pt.matching.FeatureCorrespondence2D3D(
  pt.sfm.Feature(point1), pt.sfm.Feature(point2))

pnp_type =  pt.sfm.PnPType.DLS #  pt.sfm.PnPType.SQPnP,  pt.sfm.PnPType.KNEIP
success, abs_ori, summary = pt.sfm.EstimateCalibratedAbsolutePose(
  params, pt.sfm.RansacType(0), pnp_type, correspondences2D3D)

success, abs_ori, summary = pt.sfm.EstimateAbsolutePoseWithKnownOrientation(
  params, pt.sfm.RansacType(0), correspondences2D3D)
... and more
# relative pose ransac
correspondences2D2D = pt.matching.FeatureCorrespondence(
            pt.sfm.Feature(point1), pt.sfm.Feature(point2))

success, rel_ori, summary = pt.sfm.EstimateRelativePose(
        params, pt.sfm.RansacType(0), correspondences2D2D)

success, rad_homog, summary = pt.sfm.EstimateRadialHomographyMatrix(
        params, pt.sfm.RansacType(0), correspondences2D2D)  

success, rad_homog, summary = pt.sfm.EstimateFundamentalMatrix(
        params, pt.sfm.RansacType(0), correspondences2D2D)  
... and more

Bundle Adjustment of views or points

import pytheia as pt
recon = pt.sfm.Reconstruction()
# add some views and points
veiw_id = recon.AddView() 
...
track_id = recon.AddTrack()
...
covariance = np.eye(2) * 0.5**2
point = [200,200]
recon.AddObservation(track_id, view_id, pt.sfm.Feature(point, covariance))

# robust BA
opts = pt.sfm.BundleAdjustmentOptions()
opts.robust_loss_width = 1.345
opts.loss_function_type = pt.sfm.LossFunctionType.HUBER

res = BundleAdjustReconstruction(opts, recon)
res = BundleAdjustPartialReconstruction(opts, {view_ids}, {track_ids}, recon)
res = BundleAdjustPartialViewsConstant(opts, {var_view_ids}, {const_view_ids}, recon)

# optimize absolute pose on normalized 2D 3D correspondences
res = pt.sfm.OptimizeAbsolutePoseOnNormFeatures(
  [pt.sfm.FeatureCorrespondence2D3D], R_init, p_init, opts)

# bundle camera adjust pose only
res = BundleAdjustView(recon, opts, view_id)
res = BundleAdjustViewWithCov(recon, view_id)
res = BundleAdjustViewsWithCov(recon, opts, [view_id1,view_id2])

# optimize structure only
res = BundleAdjustTrack(recon, opts, trackid)
res = BundleAdjustTrackWithCov(recon, opts, [view_id1,view_id2])
res = BundleAdjustTracksWithCov(recon, opts, [view_id1,trackid])

# two view optimization
res = BundleAdjustTwoViewsAngular(recon, [pt.sfm.FeatureCorrespondence], pt.sfm.TwoViewInfo())

Building

This section describes how to build on Ubuntu locally or on WSL2 both with sudo rights. The basic dependency is:

Installing the ceres-solver will also install the neccessary dependencies for pyTheia:

  • gflags
  • glog
  • Eigen
sudo apt install cmake build-essential 

# cd to your favourite library folder
mkdir LIBS
cd LIBS

# eigen
git clone https://gitlab.com/libeigen/eigen
cd eigen && git checkout 3.4.0
mkdir -p build && cd build && cmake .. && sudo make install

# libgflags libglog libatlas-base-dev
sudo apt install libgflags-dev libgoogle-glog-dev libatlas-base-dev

# ceres solver
cd LIBS
git clone https://ceres-solver.googlesource.com/ceres-solver
cd ceres-solver && git checkout 2.1.0 && mkdir build && cd build
cmake .. -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARKS=OFF
make -j && make install

Local build without sudo

To build it locally it is best to set the EXPORT_BUILD_DIR flag for the ceres-solver. You will still need sudo apt install libgflags-dev libgoogle-glog-dev libatlas-base-dev. So go ask your admin ;)

# cd to your favourite library folder. The local installation will be all relative to this path!
mkdir /home/LIBS
cd /home/LIBS

# eigen
git clone https://gitlab.com/libeigen/eigen
cd eigen && git checkout 3.4.0
mkdir -p build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=/home/LIBS/eigen/build && make -j install

cd /home/LIBS
git clone https://ceres-solver.googlesource.com/ceres-solver
cd ceres-solver && git checkout 2.1.0 && mkdir build && cd build
cmake .. -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARKS=OFF -DEXPORT_BUILD_DIR=ON
make -j

# cd to the pyTheiaSfM folder
cd pyTheiaSfM && mkdir build && cd build 
cmake -DEigen3_DIR=/home/LIBS/eigen/build/share/eigen3/cmake/ .. 
make -j

How to build Python wheels

Local build with sudo installed ceres-solver and Eigen

Tested on Ubuntu. In your Python >= 3.6 environment of choice run:

sh build_and_install.sh

If you have problems like /lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found on Ubuntu 22.04 in an Anaconda environment try:

conda install -c conda-forge libstdcxx-ng

With Docker

The docker build will actually build manylinux wheels for Linux (Python 3.6-3.12). There are two ways to do that. One will clutter the source directory, but you will have the wheel file directly available (./wheelhouse/). Another drawback of this approach is that the files will have been created with docker sudo rights and are diffcult to delete:

# e.g. for python 3.9
docker run --rm -e PYTHON_VERSION="cp39-cp39" -v `pwd`:/home urbste/pytheia_base:1.2.0 /home/pypackage/build-wheel-linux.sh

The other one is cleaner but you will have to copy the wheels out of the docker container afterwards:

docker build -t pytheia:0.1 .
docker run -it pytheia:0.1

Then all the wheels will be inside the container in the folder /home/wheelhouse. Open a second terminal and run

docker ps # this will give you a list of running containers to find the correct CONTAINER_ID
docker cp CONTAINER_ID:/home/wheelhouse /path/to/result/folder/pytheia_wheels

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

pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.7m manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.7m manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.6m manylinux: glibc 2.17+ x86-64

pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB view details)

Uploaded CPython 3.6m manylinux: glibc 2.17+ x86-64

File details

Details for the file pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 d46d87ed1052ba3b6376d47c7c53165054650297db1285bf5676b32a951b0b78
MD5 96b19adb5aa36e2d9516bce6bb8a72f6
BLAKE2b-256 7bf6786b17aa9c0727dc1a7d5a3f423578fa3bf02b0b10b781d3c9bae54d2c27

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0fa5c99011f4c5a7b56ce67b357ecef557c53469c19946e12bfbe7b6a9fe074e
MD5 055b38e121b9371ef7e08be79582bd4b
BLAKE2b-256 a4854fbbf84b5bf068341423a1397d5ed5fa46e6053095a48f5f548a8e8308c7

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 cef3c7569a499d703cebaa4536b51ae86213ab93701db23bc91d25d12b12be0d
MD5 aedde2ed1cfc9c8cdc385b3df8f16b5c
BLAKE2b-256 5b541800c6072c597ca16709dc0fbb39cc36f5ef1d490c8260fa56f605cd07d9

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3a18ea9116106b146350f385e8acbd84613d18fa26dcde2849ff8b55fcef2c1a
MD5 ee2b62585dbecaa9279d1ce3ba5b75bc
BLAKE2b-256 b5666ef50c4dfb06a3cf5888c8b4111624397b93a6033674921c7ba64f68da0d

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 2b1d3375237fcaff3b2689fe4b3c3b50bc95762f62bad97a8b47f383b44db2e5
MD5 c693d14e8986c6fa3f7430ea1bd6447a
BLAKE2b-256 dd5a4afc2bb6af60a1355f426682521c3fbc612f135debecc9c57bc852f146dd

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bb7307295034dea54e30427aa82a5ec3d552c2f0c5787ad7943323e317de2f4e
MD5 ee96fe9b308e4106dc1f8bb932f09d8a
BLAKE2b-256 2350b283d2fe08b7ed467da9436014d72fb3efe19be93213bcb785c196f928c7

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 d2989dab909d8495d80a15b5419c19d2866ea9ccc2e59358993e365a992e0286
MD5 be5f268cda88234cb3dec0b562b19b71
BLAKE2b-256 42a55c6c7c233e766593beb0ede275b1a25e7fab12580a49540dd615f218fe05

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 82cbe442c08088d4081cbdde7ae6f7c3c965c9b2d071e86e72e93b77ee174c5a
MD5 53695014ecec6bee5e9f1938ae200a7c
BLAKE2b-256 ee2393a0c2693464b05be248db4dabba88477b01e610848226720bfb2ebefd82

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 51afcd9fadf6d2e02d844196eb30863a3319450bc706dabb5e2eaf28c2b4326d
MD5 a7f4a4a67cfc89349735d4d906135f4b
BLAKE2b-256 44b22ba41e56c6485c1d00a804589bf20d71ff0e4f172e8f0c769bc2c6e1aa9c

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 149c91a5805ace84a54a7920f70faf734a1fdb2c7ac340d24dc749bed9e30022
MD5 7e0ed4789ceb73af0d1c4a091f51937b
BLAKE2b-256 fc97b78d4101586712b6ca2253a1c33f9c89c93191c5586f7be9d10e5cfc3c0b

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 98797802c6082bcdc31ca55358baf661075d2a4c5eb1cd06754ba85a68c64dad
MD5 4c9371220eb693f497a232482460aeae
BLAKE2b-256 c1bb8650ec457f43fc7eef873e01ca8df75d1d2a3aca72d2b5da8611fed122bb

See more details on using hashes here.

File details

Details for the file pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytheia-0.2.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5d8b4985cacd7102af0aae4a4a9275ef32d6aea57e771d5b35a854fd0abc4a91
MD5 b09ad3f9de19a248dfe4923a4093646c
BLAKE2b-256 93df6da296e7690e4f1d71ebeb4a979d653637804e6fef9932dbcd9f49d88608

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