Skip to content

Commit

Permalink
Remove COLMAP factors and dependency (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarlinpe authored Feb 5, 2024
1 parent 5d6303f commit c0028b4
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 397 deletions.
17 changes: 9 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
cmake_minimum_required(VERSION 3.10)
project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION})

if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
set(CMAKE_CUDA_ARCHITECTURES "native")
endif()
find_package(COLMAP REQUIRED)
if(${COLMAP_VERSION} VERSION_LESS "3.9.0")
message( SEND_ERROR "COLMAP version >= 3.9.0 required, found ${COLMAP_VERSION}." )
find_package(Ceres REQUIRED)
if(NOT TARGET Ceres::ceres)
# Older Ceres versions don't come with an imported interface target.
add_library(Ceres::ceres INTERFACE IMPORTED)
target_include_directories(
Ceres::ceres INTERFACE ${CERES_INCLUDE_DIRS})
target_link_libraries(
Ceres::ceres INTERFACE ${CERES_LIBRARIES})
endif()
if(${CERES_VERSION} VERSION_LESS "2.1.0")
message( SEND_ERROR "Ceres version >= 2.1 required, found ${CERES_VERSION}." )
endif()


find_package(Python REQUIRED COMPONENTS Interpreter Development.Module)

find_package(pybind11 2.11.1 REQUIRED)

pybind11_add_module(pyceres _pyceres/bindings.cc)
target_include_directories(pyceres PRIVATE ${PROJECT_SOURCE_DIR})
target_link_libraries(pyceres PRIVATE colmap::colmap glog::glog Ceres::ceres)
target_link_libraries(pyceres PRIVATE glog::glog Ceres::ceres)
target_compile_definitions(pyceres PRIVATE VERSION_INFO="${PROJECT_VERSION}")
install(TARGETS pyceres LIBRARY DESTINATION .)
29 changes: 4 additions & 25 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ ARG UBUNTU_VERSION=22.04
ARG NVIDIA_CUDA_VERSION=12.3.1
FROM nvidia/cuda:${NVIDIA_CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION} as builder

ARG COLMAP_VERSION=3.9.1
ARG CUDA_ARCHITECTURES=70
ENV CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES}
ENV QT_XCB_GL_INTEGRATION=xcb_egl

# Prevent stop building ubuntu at time zone selection.
Expand All @@ -17,21 +14,12 @@ RUN apt-get update && \
cmake \
ninja-build \
build-essential \
libboost-program-options-dev \
libboost-filesystem-dev \
libboost-graph-dev \
libboost-system-dev \
libeigen3-dev \
libflann-dev \
libfreeimage-dev \
libmetis-dev \
libgoogle-glog-dev \
libgflags-dev \
libgtest-dev \
libsqlite3-dev \
libglew-dev \
qtbase5-dev \
libqt5opengl5-dev \
libcgal-dev \
libatlas-base-dev \
libsuitesparse-dev \
python-is-python3 \
python3-minimal \
python3-pip \
Expand All @@ -47,16 +35,7 @@ RUN apt-get install -y --no-install-recommends --no-install-suggests wget && \
cmake ../ceres-solver-2.1.0 -GNinja && \
ninja install

# Install Colmap.
RUN wget "https://github.com/colmap/colmap/archive/refs/tags/${COLMAP_VERSION}.tar.gz" -O colmap-${COLMAP_VERSION}.tar.gz && \
tar zxvf colmap-${COLMAP_VERSION}.tar.gz && \
mkdir colmap-build && \
cd colmap-build && \
cmake ../colmap-${COLMAP_VERSION} -GNinja -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES} && \
ninja install


# Build pyceres.
COPY . /pyceres
WORKDIR /pyceres
RUN pip install . -vv --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES}
RUN pip install . -vv
34 changes: 9 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,30 @@ This repository provides minimal Python bindings for the [Ceres Solver](http://c

## Installation

1. Install [COLMAP 3.9.1](https://colmap.github.io/)

2. Clone the repository and build the package:
Clone the repository and build the package:

```sh
git clone https://github.com/cvg/pyceres.git
cd pyceres
python -m pip install .
```

### Docker image

Alternatively, you can build the Docker image:

```sh
export COLMAP_VERSION=3.9.1
export CUDA_ARCHITECTURES=70
docker build -t pyceres \
--build-arg COLMAP_VERSION=${COLMAP_VERSION} \
--build-arg CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES} \
-f Dockerfile .
docker build -t pyceres -f Dockerfile .
```

## Factor graph optimization

For now we support the following cost functions, defined in `_pyceres/factors/`:
- camera reprojection error (with fixed or variable pose)
- rig reprojection error (with fixed or variable rig extrinsics)
- relative pose prior
- absolute pose prior

All factors support basic observation covariances. Reprojection error costs rely on camera models defined in COLMAP. Absolute poses are represented as quaternions and are expressed in the sensor frame, so are pose residuals, which use the right-hand convention as in the [GTSAM library](https://github.com/borglab/gtsam).

## Examples
See the Jupyter notebooks in `examples/`.
Factors may be defined in Python (see [`examples/test_python_cost.py`](./examples/test_python_cost.py)) or in C++ with associated Python bindings.
[PyCOLMAP](https://github.com/colmap/colmap/tree/main/pycolmap) provides the following cost functions in `pycolmap.cost_functions`:
- reprojection error for different camera models, with fixed or variable pose and 3D points
- reprojection error for multi-camera rigs, with fixed or variable rig extrinsics
- error of absolute and relative poses
- Sampson error for epipolar geometry

## TODO
- [ ] Define a clean interface for covariances, like in GTSAM
- [ ] Add bindings for Ceres covariance estimation
- [ ] Proper benchmark against GTSAM
See [`examples/`](./examples/) to use these factors.

## Credits
Pyceres was inspired by the work of Nikolaus Mitchell for [ceres_python_bindings](https://github.com/Edwinem/ceres_python_bindings) and is maintained by [Philipp Lindenberger](https://github.com/Phil26AT) and [Paul-Edouard Sarlin](https://psarlin.com/).
87 changes: 13 additions & 74 deletions _pyceres/factors/bindings.h
Original file line number Diff line number Diff line change
@@ -1,85 +1,24 @@
#pragma once

#include "_pyceres/factors/bundle.h"
#include "_pyceres/factors/common.h"
#include "_pyceres/factors/pose_graph.h"
#include "_pyceres/helpers.h"

#include <ceres/ceres.h>
#include <ceres/normal_prior.h>
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;
using namespace colmap;

void bind_factors(py::module& m) {
m.def("ReprojErrorCost",
&CreateCostFunction<ReprojErrorCostFunction, const Eigen::Vector2d&>,
py::arg("camera_model_id"),
py::arg("point2D"));
m.def("ReprojErrorCost",
&CreateCostFunction<ReprojErrorCostFunctionWithNoise,
const Eigen::Vector2d&,
const double>,
py::arg("camera_model_id"),
py::arg("point2D"),
py::arg("stddev"));
m.def("ReprojErrorCost",
&CreateCostFunction<ReprojErrorConstantPoseCostFunction,
const Rigid3d&,
const Eigen::Vector2d&>,
py::arg("camera_model_id"),
py::arg("cam_from_world"),
py::arg("point2D"));
m.def("ReprojErrorCost",
&CreateCostFunction<ReprojErrorConstantPoseCostFunctionWithNoise,
const Rigid3d&,
const Eigen::Vector2d&,
const double>,
py::arg("camera_model_id"),
py::arg("cam_from_world"),
py::arg("point2D"),
py::arg("stddev"));

m.def("RigReprojErrorCost",
&CreateCostFunction<RigReprojErrorCostFunction, const Eigen::Vector2d&>,
py::arg("camera_model_id"),
py::arg("point2D"));
m.def("RigReprojErrorCost",
&CreateCostFunction<RigReprojErrorCostFunctionWithNoise,
const Eigen::Vector2d&,
const double>,
py::arg("camera_model_id"),
py::arg("point2D"),
py::arg("stddev"));
m.def("RigReprojErrorCost",
&CreateCostFunction<RigReprojErrorConstantRigCostFunction,
const Rigid3d&,
const Eigen::Vector2d&>,
py::arg("camera_model_id"),
py::arg("cam_from_rig"),
py::arg("point2D"));
m.def("RigReprojErrorCost",
&CreateCostFunction<RigReprojErrorConstantRigCostFunctionWithNoise,
const Rigid3d&,
const Eigen::Vector2d&,
const double>,
py::arg("camera_model_id"),
py::arg("cam_from_rig"),
py::arg("point2D"),
py::arg("stddev"));

m.def("PoseGraphRelativeCost",
&PoseGraphRelativeCost::Create,
py::arg("j_from_i"),
py::arg("covariance"));
m.def("PoseGraphAbsoluteCost",
&PoseGraphAbsoluteCost::Create,
py::arg("cam_from_world"),
py::arg("covariance"));

m.def("NormalPrior",
&CreateNormalPrior,
py::arg("mean"),
py::arg("covariance"));
m.def(
"NormalPrior",
[](const Eigen::VectorXd& mean,
const Eigen::Matrix<double, -1, -1>& covariance) {
THROW_CHECK_EQ(covariance.cols(), mean.size());
THROW_CHECK_EQ(covariance.cols(), covariance.rows());
return new ceres::NormalPrior(covariance.inverse().llt().matrixL(),
mean);
},
py::arg("mean"),
py::arg("covariance"));
}
128 changes: 0 additions & 128 deletions _pyceres/factors/bundle.h

This file was deleted.

14 changes: 0 additions & 14 deletions _pyceres/factors/common.h

This file was deleted.

Loading

0 comments on commit c0028b4

Please sign in to comment.