pyTheia - A Python Structure-from-Motion and Geometric Vision Swiss Knife
---------------------
pyTheia is based on [TheiaSfM](http://www.theia-sfm.org).
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](pytest/sfm_pipeline.py).
Download the south_building dataset from [here](https://demuc.de/colmap/datasets/).
Extract it somewhere and run:
```bash
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.
``` Python
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](pytest/sfm)
``` Python
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
``` Python
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:
* [http://ceres-solver.org/](ceres-solver)
Installing the ceres-solver will also install the neccessary dependencies for pyTheia:
* gflags
* glog
* Eigen
```bash
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 ;)
```bash
# 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:
```bash
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:
```bash
conda install -c conda-forge libstdcxx-ng
```
Another solution is to check the GLIBCXX versions. If the version that the library requires is installed, then we can create a symbolic link into the conda environment.
```
strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX
# if the GLIBCXX version is available then do:
ln -sf /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ${CONDA_PREFIX}/lib/libstdc++.so.6
```
### 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:
```bash
# 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:
```bash
docker build -t pytheia:1.0 .
docker run -it pytheia:1.0
```
Then all the wheels will be inside the container in the folder /home/wheelhouse.
Open a second terminal and run
```bash
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
```
Raw data
{
"_id": null,
"home_page": "https://github.com/urbste/pyTheiaSfM.git",
"name": "pytheia",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Steffen Urban, Shengyu Yin",
"author_email": "urbste@googlemail.com, shengyu952014@outlook.com",
"download_url": null,
"platform": null,
"description": "\npyTheia - A Python Structure-from-Motion and Geometric Vision Swiss Knife\n---------------------\n\npyTheia is based on [TheiaSfM](http://www.theia-sfm.org).\nIt contains Python bindings for most of the functionalities of TheiaSfM and more.\n\n**The library is still in active development and the interfaces are not yet all fixed**\n\nWith pyTheia you have access to a variety of different camera models, structure-from-motion pipelines and geometric vision algorithms.\n\n# Differences to the original library TheiaSfM\npyTheia 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. \n\npyTheia is rather a \"swiss knife\" for quickly prototyping SfM related reconstruction applications without sacrificing perfomance.\nFor 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.\n\n## What was removed\nHence, we removed some libaries from the original TheiaSfM:\n* 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.\n* OpenImageIO: was used for image in and output and for recitification.\n* RapidJSON: Camera intrinsic in and output. Is part of cereal headers anyways.\n* RocksDB: Used for saving and loading extracted features efficiently.\n\n## Changes to the original TheiaSfM library\n\n\n* Global SfM algorithms:\n * LiGT position solver\n * Lagrange Dual rotation estimator\n * Hybrid rotation estimator\n * Possibility to fix multiple views in Robust_L1L2 solver\n * Nonlinear translation solver can fix multiple view or estimate all remaining views in reconstruction\n* Camera models\n * Double Sphere\n * Extended Unified\n * Orthographic\n* Bundle adjustment\n * Using a homogeneous representation for scene points\n * Extracting covariance information\n * Possibility to add a depth prior to 3D points\n * Position prior for camera poses (e.g. for GPS or known positions)\n* General\n * Added timestamp, position_prior_, position_prior_sqrt_information_ variables to **View** class\n Eigen::Matrix3d position_prior_sqrt_information_;\n * Added inverse_depth_, reference_descriptor, reference_bearing_ variables to **Track** class\n * Added covariance_, depth_prior_, depth_prior_variance_ to **Feature** class\n* Absolute Pose solvers\n * SQPnP\n * UncalibratedPlanarOrthographic Pose\n\n## Usage Examples\n\n### Full reconstruction example: Global, Hybrid or Incremental SfM using OpenCV feature detection and matching\nHave a look at the short example: [sfm_pipeline.py](pytest/sfm_pipeline.py).\nDownload the south_building dataset from [here](https://demuc.de/colmap/datasets/).\nExtract it somewhere and run: \n```bash\npython pytest/sfm_pipeline.py --image_path /path/to/south-building/images/\n```\n\n### Creating a camera\nThe following example show you how to create a camera in pyTheia.\nYou can construct it from a pt.sfm.CameraIntrinsicsPrior() or set all parameters using respective functions from pt.sfm.Camera() class.\n``` Python\nimport pytheia as pt\nprior = pt.sfm.CameraIntrinsicsPrior()\nprior.focal_length.value = [1000.]\nprior.aspect_ratio.value = [1.]\nprior.principal_point.value = [500., 500.]\nprior.radial_distortion.value = [0., 0., 0., 0]\nprior.tangential_distortion.value = [0., 0.]\nprior.skew.value = [0]\nprior.camera_intrinsics_model_type = 'PINHOLE' \n#'PINHOLE', 'DOUBLE_SPHERE', 'EXTENDED_UNIFIED', 'FISHEYE', 'FOV', 'DIVISION_UNDISTORTION'\ncamera = pt.sfm.Camera()\ncamera.SetFromCameraIntrinsicsPriors(prior)\n\n# the camera object also carries extrinsics information\ncamera.SetPosition([0,0,-2])\ncamera.SetOrientationFromAngleAxis([0,0,0.1])\n\n# project with intrinsics image to camera coordinates\ncamera_intrinsics = camera.CameraIntrinsics()\npt2 = [100.,100.]\npt3 = camera_intrinsics.ImageToCameraCoordinates(pt2)\npt2 = camera_intrinsics.CameraToImageCoordinates(pt3)\n\n# project with camera extrinsics\npt3_h = [1,1,2,1] # homogeneous 3d point\ndepth, pt2 = camera.ProjectPoint(pt3_h)\n# get a ray from camera to 3d point in the world frame\nray = camera.PixelToUnitDepthRay(pt2)\npt3_h_ = ray*depth + camera.GetPosition() # == pt3_h[:3]\n```\n\n### Solve for absolute or relative camera pose\npyTheia integrates a lot of performant geometric vision algorithms. \nHave a look at the [tests](pytest/sfm)\n``` Python\nimport pytheia as pt\n\n# absolute pose\npose = pt.sfm.PoseFromThreePoints(pts2D, pts3D) # Kneip\npose = pt.sfm.FourPointsPoseFocalLengthRadialDistortion(pts2D, pts3D)\npose = pt.sfm.FourPointPoseAndFocalLength(pts2D, pts3D)\npose = pt.sfm.DlsPnp(pts2D, pts3D)\n... and more\n\n# relative pose\npose = pt.sfm.NormalizedEightPointFundamentalMatrix(pts2D, pts2D)\npose = pt.sfm.FourPointHomography(pts2D, pts2D)\npose = pt.sfm.FivePointRelativePose(pts2D, pts2D)\npose = pt.sfm.SevenPointFundamentalMatrix(pts2D, pts2D)\n... and more\n\n# ransac estimation\nparams = pt.solvers.RansacParameters()\nparams.error_thresh = 0.1\nparams.max_iterations = 100\nparams.failure_probability = 0.01\n\n# absolute pose ransac\ncorrespondences2D3D = pt.matching.FeatureCorrespondence2D3D(\n pt.sfm.Feature(point1), pt.sfm.Feature(point2))\n\npnp_type = pt.sfm.PnPType.DLS # pt.sfm.PnPType.SQPnP, pt.sfm.PnPType.KNEIP\nsuccess, abs_ori, summary = pt.sfm.EstimateCalibratedAbsolutePose(\n params, pt.sfm.RansacType(0), pnp_type, correspondences2D3D)\n\nsuccess, abs_ori, summary = pt.sfm.EstimateAbsolutePoseWithKnownOrientation(\n params, pt.sfm.RansacType(0), correspondences2D3D)\n... and more\n# relative pose ransac\ncorrespondences2D2D = pt.matching.FeatureCorrespondence(\n pt.sfm.Feature(point1), pt.sfm.Feature(point2))\n\nsuccess, rel_ori, summary = pt.sfm.EstimateRelativePose(\n params, pt.sfm.RansacType(0), correspondences2D2D)\n\nsuccess, rad_homog, summary = pt.sfm.EstimateRadialHomographyMatrix(\n params, pt.sfm.RansacType(0), correspondences2D2D) \n\nsuccess, rad_homog, summary = pt.sfm.EstimateFundamentalMatrix(\n params, pt.sfm.RansacType(0), correspondences2D2D) \n... and more\n```\n\n### Bundle Adjustment of views or points\n``` Python\nimport pytheia as pt\nrecon = pt.sfm.Reconstruction()\n# add some views and points\nveiw_id = recon.AddView() \n...\ntrack_id = recon.AddTrack()\n...\ncovariance = np.eye(2) * 0.5**2\npoint = [200,200]\nrecon.AddObservation(track_id, view_id, pt.sfm.Feature(point, covariance))\n\n# robust BA\nopts = pt.sfm.BundleAdjustmentOptions()\nopts.robust_loss_width = 1.345\nopts.loss_function_type = pt.sfm.LossFunctionType.HUBER\n\nres = BundleAdjustReconstruction(opts, recon)\nres = BundleAdjustPartialReconstruction(opts, {view_ids}, {track_ids}, recon)\nres = BundleAdjustPartialViewsConstant(opts, {var_view_ids}, {const_view_ids}, recon)\n\n# optimize absolute pose on normalized 2D 3D correspondences\nres = pt.sfm.OptimizeAbsolutePoseOnNormFeatures(\n [pt.sfm.FeatureCorrespondence2D3D], R_init, p_init, opts)\n\n# bundle camera adjust pose only\nres = BundleAdjustView(recon, opts, view_id)\nres = BundleAdjustViewWithCov(recon, view_id)\nres = BundleAdjustViewsWithCov(recon, opts, [view_id1,view_id2])\n\n# optimize structure only\nres = BundleAdjustTrack(recon, opts, trackid)\nres = BundleAdjustTrackWithCov(recon, opts, [view_id1,view_id2])\nres = BundleAdjustTracksWithCov(recon, opts, [view_id1,trackid])\n\n# two view optimization\nres = BundleAdjustTwoViewsAngular(recon, [pt.sfm.FeatureCorrespondence], pt.sfm.TwoViewInfo())\n```\n\n## Building\nThis section describes how to build on Ubuntu locally or on WSL2 both with sudo rights.\nThe basic dependency is:\n* [http://ceres-solver.org/](ceres-solver)\n\nInstalling the ceres-solver will also install the neccessary dependencies for pyTheia:\n* gflags\n* glog\n* Eigen\n\n```bash\nsudo apt install cmake build-essential \n\n# cd to your favourite library folder\nmkdir LIBS\ncd LIBS\n\n# eigen\ngit clone https://gitlab.com/libeigen/eigen\ncd eigen && git checkout 3.4.0\nmkdir -p build && cd build && cmake .. && sudo make install\n\n# libgflags libglog libatlas-base-dev\nsudo apt install libgflags-dev libgoogle-glog-dev libatlas-base-dev\n\n# ceres solver\ncd LIBS\ngit clone https://ceres-solver.googlesource.com/ceres-solver\ncd ceres-solver && git checkout 2.1.0 && mkdir build && cd build\ncmake .. -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARKS=OFF\nmake -j && make install\n```\n\n### Local build without sudo\nTo build it locally it is best to set the EXPORT_BUILD_DIR flag for the ceres-solver.\nYou will still need ```sudo apt install libgflags-dev libgoogle-glog-dev libatlas-base-dev```. \nSo go ask your admin ;)\n\n```bash\n# cd to your favourite library folder. The local installation will be all relative to this path!\nmkdir /home/LIBS\ncd /home/LIBS\n\n# eigen\ngit clone https://gitlab.com/libeigen/eigen\ncd eigen && git checkout 3.4.0\nmkdir -p build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=/home/LIBS/eigen/build && make -j install\n\ncd /home/LIBS\ngit clone https://ceres-solver.googlesource.com/ceres-solver\ncd ceres-solver && git checkout 2.1.0 && mkdir build && cd build\ncmake .. -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARKS=OFF -DEXPORT_BUILD_DIR=ON\nmake -j\n\n# cd to the pyTheiaSfM folder\ncd pyTheiaSfM && mkdir build && cd build \ncmake -DEigen3_DIR=/home/LIBS/eigen/build/share/eigen3/cmake/ .. \nmake -j\n```\n\n## How to build Python wheels\n### Local build with sudo installed ceres-solver and Eigen\nTested on Ubuntu. In your Python >= 3.6 environment of choice run:\n```bash\nsh build_and_install.sh\n```\n\nIf you have problems like **/lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found** on Ubuntu 22.04 in an Anaconda environment try:\n```bash\nconda install -c conda-forge libstdcxx-ng\n```\n\nAnother solution is to check the GLIBCXX versions. If the version that the library requires is installed, then we can create a symbolic link into the conda environment.\n```\nstrings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX\n# if the GLIBCXX version is available then do:\nln -sf /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ${CONDA_PREFIX}/lib/libstdc++.so.6\n```\n\n### With Docker\nThe docker build will actually build manylinux wheels for Linux (Python 3.6-3.12).\nThere are two ways to do that. One will clutter the source directory, but you will have the wheel file directly available (./wheelhouse/).\nAnother drawback of this approach is that the files will have been created with docker sudo rights and are diffcult to delete:\n```bash\n# e.g. for python 3.9\ndocker run --rm -e PYTHON_VERSION=\"cp39-cp39\" -v `pwd`:/home urbste/pytheia_base:1.2.0 /home/pypackage/build-wheel-linux.sh\n```\n\nThe other one is cleaner but you will have to copy the wheels out of the docker container afterwards:\n```bash\ndocker build -t pytheia:1.0 .\ndocker run -it pytheia:1.0\n```\nThen all the wheels will be inside the container in the folder /home/wheelhouse.\nOpen a second terminal and run\n```bash\ndocker ps # this will give you a list of running containers to find the correct CONTAINER_ID\ndocker cp CONTAINER_ID:/home/wheelhouse /path/to/result/folder/pytheia_wheels\n```\n",
"bugtrack_url": null,
"license": "BSD",
"summary": "A performant Structure from Motion library for Python",
"version": "0.2.9",
"project_urls": {
"Homepage": "https://github.com/urbste/pyTheiaSfM.git"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "b6129486d22be7d078e37b5edc69b6ab146fb206abc697b01908d04f04ae6729",
"md5": "fa557854e8c366365ffeb822e2b04f63",
"sha256": "c7408d523f23dada2c112b927b533fa3b6e6ee690416425308338d68671298a0"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "fa557854e8c366365ffeb822e2b04f63",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": null,
"size": 7712488,
"upload_time": "2025-08-13T10:37:30",
"upload_time_iso_8601": "2025-08-13T10:37:30.961211Z",
"url": "https://files.pythonhosted.org/packages/b6/12/9486d22be7d078e37b5edc69b6ab146fb206abc697b01908d04f04ae6729/pytheia-0.2.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a5a591b55f100e6d4a46f197feac8867728cf518774fcc389bb2c7f2da0fed8d",
"md5": "21617f374f5e6b39c492876968f135a3",
"sha256": "481ec1d1c29ad5ca07c7d5def9840c2045071f1ebd6162c1d9cdebd3cd9be682"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp310-cp310-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "21617f374f5e6b39c492876968f135a3",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": null,
"size": 4454773,
"upload_time": "2025-08-13T10:37:34",
"upload_time_iso_8601": "2025-08-13T10:37:34.550944Z",
"url": "https://files.pythonhosted.org/packages/a5/a5/91b55f100e6d4a46f197feac8867728cf518774fcc389bb2c7f2da0fed8d/pytheia-0.2.9-cp310-cp310-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "4874b345db6a4e2082b855b0d762283ea9e42a5c38d4164d3d7c040d89257084",
"md5": "61fe287e0523324cd0cdc55b1b70717c",
"sha256": "0cb35218752e8e86bf1dfd89d2308f3854880a93a28b11dab37cd6e515d6de61"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "61fe287e0523324cd0cdc55b1b70717c",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": null,
"size": 7712087,
"upload_time": "2025-08-13T10:37:39",
"upload_time_iso_8601": "2025-08-13T10:37:39.763286Z",
"url": "https://files.pythonhosted.org/packages/48/74/b345db6a4e2082b855b0d762283ea9e42a5c38d4164d3d7c040d89257084/pytheia-0.2.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "6f2a30e028231b96fe2774b031a897a487de72dda1e6828445b8ad6525a93f87",
"md5": "5961071b96fd03712a2f9865f95ae34b",
"sha256": "19f4c0326c78b67aeb31ca7974dc96a3a30ccd3a4e524e43ed3a8549a58372dd"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp311-cp311-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "5961071b96fd03712a2f9865f95ae34b",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": null,
"size": 4454480,
"upload_time": "2025-08-13T10:37:41",
"upload_time_iso_8601": "2025-08-13T10:37:41.669358Z",
"url": "https://files.pythonhosted.org/packages/6f/2a/30e028231b96fe2774b031a897a487de72dda1e6828445b8ad6525a93f87/pytheia-0.2.9-cp311-cp311-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "dc9554e1282908a7473d614402fa5e247f41e6599d886edaa171cad254be4ac4",
"md5": "dd6ea0e8f9e974b42584dd8381f1d4ff",
"sha256": "2b8f23eaccedd477f2e7ed20ce0185eba9ff9c7f3bf10e56f743c96981117038"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp36-cp36m-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "dd6ea0e8f9e974b42584dd8381f1d4ff",
"packagetype": "bdist_wheel",
"python_version": "cp36",
"requires_python": null,
"size": 7734635,
"upload_time": "2025-08-13T10:37:29",
"upload_time_iso_8601": "2025-08-13T10:37:29.160722Z",
"url": "https://files.pythonhosted.org/packages/dc/95/54e1282908a7473d614402fa5e247f41e6599d886edaa171cad254be4ac4/pytheia-0.2.9-cp36-cp36m-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "231ed328367212b4f33dafa15113948ac1635eb8017357c84a352d2f26c5b338",
"md5": "cc384c5356ec7f8f1bd23a392475f377",
"sha256": "df10cac35fc94808d925c4db80e7e9145ccada43cc97d977cd6b24d371600280"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp36-cp36m-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "cc384c5356ec7f8f1bd23a392475f377",
"packagetype": "bdist_wheel",
"python_version": "cp36",
"requires_python": null,
"size": 4476091,
"upload_time": "2025-08-13T10:37:30",
"upload_time_iso_8601": "2025-08-13T10:37:30.833049Z",
"url": "https://files.pythonhosted.org/packages/23/1e/d328367212b4f33dafa15113948ac1635eb8017357c84a352d2f26c5b338/pytheia-0.2.9-cp36-cp36m-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2f643158fa79556ffa74ca637bdccb16d02bcfa373dbfeefd4b367c05a654590",
"md5": "193d99ef5e4151311988b42af19329e1",
"sha256": "76c893e032a78715d81ce8a23e44688cab2173f32ae4e42fcc02d9dd7f044e51"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp37-cp37m-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "193d99ef5e4151311988b42af19329e1",
"packagetype": "bdist_wheel",
"python_version": "cp37",
"requires_python": null,
"size": 7731472,
"upload_time": "2025-08-13T10:37:30",
"upload_time_iso_8601": "2025-08-13T10:37:30.643668Z",
"url": "https://files.pythonhosted.org/packages/2f/64/3158fa79556ffa74ca637bdccb16d02bcfa373dbfeefd4b367c05a654590/pytheia-0.2.9-cp37-cp37m-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b12b20410c1ec053ed38c7a3b5a3e170948277c0bc92d6f288fc9462fe404ebd",
"md5": "39d0961163c034bfcabaa04b6c3c8116",
"sha256": "c6b231c0efe375d063ff1701d13b1e0c75dace7e13a8e99f7390863ca863f4e0"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp37-cp37m-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "39d0961163c034bfcabaa04b6c3c8116",
"packagetype": "bdist_wheel",
"python_version": "cp37",
"requires_python": null,
"size": 4472892,
"upload_time": "2025-08-13T10:37:33",
"upload_time_iso_8601": "2025-08-13T10:37:33.640777Z",
"url": "https://files.pythonhosted.org/packages/b1/2b/20410c1ec053ed38c7a3b5a3e170948277c0bc92d6f288fc9462fe404ebd/pytheia-0.2.9-cp37-cp37m-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "38bb6d4c3514429bcbf7f9c5e2a6f0a65b27c9a20271b35b2aeffa5e068c332a",
"md5": "50d049458c065698a2d0a45e323b7350",
"sha256": "b8e38f61aff03b4b2f374c85c13e78cf73d313500f6ed2ef4b0857a700559d92"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp38-cp38-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "50d049458c065698a2d0a45e323b7350",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": null,
"size": 7711431,
"upload_time": "2025-08-13T10:37:29",
"upload_time_iso_8601": "2025-08-13T10:37:29.987340Z",
"url": "https://files.pythonhosted.org/packages/38/bb/6d4c3514429bcbf7f9c5e2a6f0a65b27c9a20271b35b2aeffa5e068c332a/pytheia-0.2.9-cp38-cp38-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2224032fbf79076079a654887d950f91395793299752534910bdc37b2341058d",
"md5": "eaea51fde278607c1d12c72dbbdfcffa",
"sha256": "7d482930b94917d49b4e516f4532f781e180ecf37a59e65357b68511d460094d"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp38-cp38-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "eaea51fde278607c1d12c72dbbdfcffa",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": null,
"size": 4453429,
"upload_time": "2025-08-13T10:37:32",
"upload_time_iso_8601": "2025-08-13T10:37:32.673798Z",
"url": "https://files.pythonhosted.org/packages/22/24/032fbf79076079a654887d950f91395793299752534910bdc37b2341058d/pytheia-0.2.9-cp38-cp38-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "33b1adcb0358deee81ccf840b3e9561a1d61f97325de403544e6a46e6cde152f",
"md5": "02c5fd4e432f97fd0d134f50da83c922",
"sha256": "61bd3ef462bd37c71b3777d1b33f01712c6cf896547281ae91ae84d138c41966"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp39-cp39-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "02c5fd4e432f97fd0d134f50da83c922",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": null,
"size": 7711322,
"upload_time": "2025-08-13T10:37:20",
"upload_time_iso_8601": "2025-08-13T10:37:20.595735Z",
"url": "https://files.pythonhosted.org/packages/33/b1/adcb0358deee81ccf840b3e9561a1d61f97325de403544e6a46e6cde152f/pytheia-0.2.9-cp39-cp39-manylinux_2_35_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "690c07480e0d3ad7c2c8d9d3a516109d418a91368259d8ce1402f55fc91d0d29",
"md5": "4f70426d2b2577a68b8310896c0bb025",
"sha256": "bdc4efa06f5b8169ab3a990475f9d2f0a43ef84ebb6f86ed6958dabfb816f427"
},
"downloads": -1,
"filename": "pytheia-0.2.9-cp39-cp39-manylinux_2_35_x86_64.whl",
"has_sig": false,
"md5_digest": "4f70426d2b2577a68b8310896c0bb025",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": null,
"size": 4453326,
"upload_time": "2025-08-13T10:37:21",
"upload_time_iso_8601": "2025-08-13T10:37:21.968830Z",
"url": "https://files.pythonhosted.org/packages/69/0c/07480e0d3ad7c2c8d9d3a516109d418a91368259d8ce1402f55fc91d0d29/pytheia-0.2.9-cp39-cp39-manylinux_2_35_x86_64.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-13 10:37:30",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "urbste",
"github_project": "pyTheiaSfM",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pytheia"
}