Skip to content

Commit

Permalink
Pull updates from COLMAP (#204)
Browse files Browse the repository at this point in the history
* Remove std flags

* Turn Point3D into simple data struct

* Camera becomes simple data struct

* Update doc on Camera
  • Loading branch information
sarlinpe authored Dec 7, 2023
1 parent 17d0494 commit 91752cb
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 221 deletions.
6 changes: 0 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ project(PyCOLMAP)
set(CMAKE_CUDA_ARCHITECTURES "native")
find_package(colmap 3.9 REQUIRED)

if(${CERES_VERSION} VERSION_LESS "2.2.0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
endif()

add_subdirectory(pybind11)

pybind11_add_module(pycolmap main.cc)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,14 @@ The `TwoViewGeometryOptions` control how each model is selected. The output stru

### Camera argument

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

```python
camera = pycolmap.Camera(
COLMAP_CAMERA_MODEL_NAME,
IMAGE_WIDTH,
IMAGE_HEIGHT,
EXTRA_CAMERA_PARAMETERS,
model=camera_model_name_or_id,
width=width,
height=height,
params=params,
)
```

Expand Down
4 changes: 2 additions & 2 deletions helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ inline void AddStringToEnumConstructor(py::enum_<T>& enm) {
py::implicitly_convertible<std::string, T>();
}

template <typename T>
inline void make_dataclass(py::class_<T> cls) {
template <typename T, typename... options>
inline void make_dataclass(py::class_<T, options...> cls) {
cls.def(py::init([cls](py::dict dict) {
auto self = py::object(cls());
self.attr("mergedict").attr("__call__")(dict);
Expand Down
2 changes: 1 addition & 1 deletion package/build-wheels-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ make install
cd $CURRDIR
git clone https://github.com/colmap/colmap.git
cd colmap
git checkout 0d9ab40f6037b5ede71f3af3b8d1f5091f68855d
git checkout c0355417328f3706a30a9265fd52bc7a5aa4cb8c
mkdir build/
cd build/
CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake .. -DCMAKE_BUILD_TYPE=Release \
Expand Down
2 changes: 1 addition & 1 deletion package/build-wheels-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ brew install \
cd $CURRDIR
git clone https://github.com/colmap/colmap.git
cd colmap
git checkout 0d9ab40f6037b5ede71f3af3b8d1f5091f68855d
git checkout c0355417328f3706a30a9265fd52bc7a5aa4cb8c
mkdir build
cd build
cmake .. -DGUI_ENABLED=OFF -DCUDA_ENABLED=OFF -DCGAL_ENABLED=OFF #-DBoost_USE_STATIC_LIBS=ON -DBOOSTROOT=${BOOST_DIR} -DBoost_NO_SYSTEM_PATHS=ON
Expand Down
17 changes: 8 additions & 9 deletions pipeline/images.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,18 @@ Camera infer_camera_from_image(const py::object image_path_,
std::invalid_argument,
(std::string("Cannot read image file: ") + image_path).c_str());

Camera camera;
camera.SetCameraId(kInvalidCameraId);
camera.SetModelIdFromName(options.camera_model);
double focal_length = 0.0;
if (bitmap.ExifFocalLength(&focal_length)) {
camera.SetPriorFocalLength(true);
} else {
bool has_prior_focal_length = bitmap.ExifFocalLength(&focal_length);
if (!has_prior_focal_length) {
focal_length = options.default_focal_length_factor *
std::max(bitmap.Width(), bitmap.Height());
camera.SetPriorFocalLength(false);
}
camera.InitializeWithId(
camera.ModelId(), focal_length, bitmap.Width(), bitmap.Height());
Camera camera = Camera::CreateFromModelName(kInvalidCameraId,
options.camera_model,
focal_length,
bitmap.Width(),
bitmap.Height());
camera.has_prior_focal_length = has_prior_focal_length;
THROW_CUSTOM_CHECK_MSG(
camera.VerifyParams(),
std::invalid_argument,
Expand Down
193 changes: 72 additions & 121 deletions reconstruction/camera.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Author: Philipp Lindenberger (Phil26AT)

#include "colmap/scene/image.h"
#include "colmap/scene/camera.h"

#include "colmap/sensor/models.h"
#include "colmap/util/misc.h"
#include "colmap/util/types.h"

Expand All @@ -16,109 +18,65 @@ using namespace pybind11::literals;

#include "log_exceptions.h"

PYBIND11_MAKE_OPAQUE(std::unordered_map<camera_t, Camera>);
using CameraMap = std::unordered_map<camera_t, Camera>;
PYBIND11_MAKE_OPAQUE(CameraMap);

// TODO: cleanup
std::string PrintCamera(const Camera& camera) {
std::stringstream ss;
ss << "<Camera 'camera_id="
<< (camera.CameraId() != kInvalidCameraId
? std::to_string(camera.CameraId())
: "Invalid")
<< ", model=" << camera.ModelName() << ", width=" << camera.Width()
<< ", height=" << camera.Height() << ", num_params=" << camera.NumParams()
<< (camera.camera_id != kInvalidCameraId ? std::to_string(camera.camera_id)
: "Invalid")
<< ", model=" << camera.ModelName() << ", width=" << camera.width
<< ", height=" << camera.height << ", num_params=" << camera.params.size()
<< "'>";
return ss.str();
}

void init_camera(py::module& m) {
using CameraMap = std::unordered_map<camera_t, Camera>;
auto PyCameraModelId = py::enum_<CameraModelId>(m, "CameraModelId")
.value("INVALID", CameraModelId::kInvalid);
#define CAMERA_MODEL_CASE(CameraModel) \
PyCameraModelId.value(CameraModel::model_name.c_str(), CameraModel::model_id);

py::bind_map<CameraMap>(m, "MapCameraIdCamera")
.def("__repr__", [](const CameraMap& self) {
std::string repr = "{";
bool is_first = true;
for (auto& pair : self) {
if (!is_first) {
repr += ", ";
}
is_first = false;
repr += std::to_string(pair.first) + ": " + PrintCamera(pair.second);
}
repr += "}";
return repr;
});
CAMERA_MODEL_CASES

py::class_<Camera, std::shared_ptr<Camera>>(m, "Camera")
.def(py::init<>())
.def(py::init([](const std::string& name,
size_t width,
size_t height,
const std::vector<double>& params,
camera_t camera_id) {
std::unique_ptr<Camera> camera =
std::unique_ptr<Camera>(new Camera());
THROW_CHECK(ExistsCameraModelWithName(name));
camera->SetModelIdFromName(name);
camera->SetWidth(width);
camera->SetHeight(height);
camera->SetParams(params);
camera->SetCameraId(camera_id);
return camera;
}),
"model"_a,
"width"_a,
"height"_a,
"params"_a,
"id"_a = kInvalidCameraId)
.def(py::init([](py::dict camera_dict) {
std::unique_ptr<Camera> camera =
std::unique_ptr<Camera>(new Camera());
std::string name = camera_dict["model"].cast<std::string>();
THROW_CHECK(ExistsCameraModelWithName(name));
camera->SetModelIdFromName(name);
camera->SetWidth(camera_dict["width"].cast<size_t>());
camera->SetHeight(camera_dict["height"].cast<size_t>());
camera->SetParams(
camera_dict["params"].cast<std::vector<double>>());
return camera;
}),
"camera_dict"_a)
.def_property("camera_id",
&Camera::CameraId,
&Camera::SetCameraId,
"Unique identifier of the camera.")
.def_property(
"model_id",
&Camera::ModelId,
[](Camera& self, int model_id) {
THROW_CHECK(ExistsCameraModelWithId(model_id));
self.SetModelId(model_id);
},
"Camera model ID.")
#undef CAMERA_MODEL_CASE
PyCameraModelId.export_values();
AddStringToEnumConstructor(PyCameraModelId);
py::implicitly_convertible<int, CameraModelId>();

py::bind_map<CameraMap>(m, "MapCameraIdCamera");

py::class_<Camera, std::shared_ptr<Camera>> PyCamera(m, "Camera");
PyCamera.def(py::init<>())
.def_static("create",
&Camera::CreateFromModelId,
"camera_id"_a,
"model"_a,
"focal_length"_a,
"width"_a,
"height"_a)
.def_readwrite(
"camera_id", &Camera::camera_id, "Unique identifier of the camera.")
.def_readwrite("model_id", &Camera::model_id, "Camera model ID.")
.def_property(
"model_name",
&Camera::ModelName,
[](Camera& self, std::string model_name) {
THROW_CHECK(ExistsCameraModelWithName(model_name));
self.SetModelIdFromName(model_name);
self.model_id = CameraModelNameToId(model_name);
},
"Camera model name (connected to model_id).")
.def_property(
"width", &Camera::Width, &Camera::SetWidth, "Width of camera sensor.")
.def_property("height",
&Camera::Height,
&Camera::SetHeight,
"Height of camera sensor.")
.def_readwrite("width", &Camera::width, "Width of camera sensor.")
.def_readwrite("height", &Camera::height, "Height of camera sensor.")
.def("mean_focal_length", &Camera::MeanFocalLength)
.def_property(
"focal_length", &Camera::FocalLength, &Camera::SetFocalLength)
.def_property(
"focal_length_x", &Camera::FocalLengthX, &Camera::SetFocalLengthX)
.def_property(
"focal_length_y", &Camera::FocalLengthY, &Camera::SetFocalLengthY)
.def_property("has_prior_focal_length",
&Camera::HasPriorFocalLength,
&Camera::SetPriorFocalLength)
.def_readwrite("has_prior_focal_length", &Camera::has_prior_focal_length)
.def_property("principal_point_x",
&Camera::PrincipalPointX,
&Camera::SetPrincipalPointX)
Expand All @@ -137,18 +95,20 @@ void init_camera(py::module& m) {
.def("calibration_matrix",
&Camera::CalibrationMatrix,
"Compute calibration matrix from params.")
.def(
"params_info",
&Camera::ParamsInfo,
"Get human-readable information about the parameter vector ordering.")
.def("num_params", &Camera::NumParams, "Number of raw camera parameters.")
.def("params_info",
&Camera::ParamsInfo,
"Get human-readable information about the parameter vector "
"ordering.")
.def_property(
"params",
[](Camera& self) {
return Eigen::Map<Eigen::VectorXd>(self.ParamsData(),
self.NumParams());
// Return a view (via a numpy array) instead of a copy.
return Eigen::Map<Eigen::VectorXd>(self.params.data(),
self.params.size());
},
[](Camera& self, const std::vector<double>& params) {
self.params = params;
},
&Camera::SetParams,
"Camera parameters.")
.def("params_to_string",
&Camera::ParamsToString,
Expand All @@ -158,37 +118,11 @@ void init_camera(py::module& m) {
"Set camera parameters from comma-separated list.")
.def("verify_params",
&Camera::VerifyParams,
"Check whether parameters are valid, i.e. the parameter vector has\n"
"the correct dimensions that match the specified camera model.")
"Check whether parameters are valid, i.e. the parameter vector has"
"\nthe correct dimensions that match the specified camera model.")
.def("has_bogus_params",
&Camera::HasBogusParams,
"Check whether camera has bogus parameters.")
.def(
"initialize_with_id",
[](Camera& self,
const int model_id,
const double focal_length,
const size_t width,
const size_t height) {
THROW_CHECK(ExistsCameraModelWithId(model_id));
self.InitializeWithId(model_id, focal_length, width, height);
},
"Initialize parameters for given camera model ID and focal length, "
"and set\n"
"the principal point to be the image center.")
.def(
"initialize_with_name",
[](Camera& self,
std::string model_name,
const double focal_length,
const size_t width,
const size_t height) {
THROW_CHECK(ExistsCameraModelWithName(model_name));
self.InitializeWithName(model_name, focal_length, width, height);
},
"Initialize parameters for given camera model name and focal length, "
"and set\n"
"the principal point to be the image center.")
.def("cam_from_img",
&Camera::CamFromImg,
"Project point in image plane to world / infinity.")
Expand Down Expand Up @@ -256,14 +190,31 @@ void init_camera(py::module& m) {
.def("summary", [](const Camera& self) {
std::stringstream ss;
ss << "Camera:\n\tcamera_id="
<< (self.CameraId() != kInvalidCameraId
? std::to_string(self.CameraId())
<< (self.camera_id != kInvalidCameraId
? std::to_string(self.camera_id)
: "Invalid")
<< "\n\tmodel = " << self.ModelName()
<< "\n\twidth = " << self.Width() << "\n\theight = " << self.Height()
<< "\n\tnum_params = " << self.NumParams()
<< "\n\tmodel = " << self.ModelName() << "\n\twidth = " << self.width
<< "\n\theight = " << self.height
<< "\n\tnum_params = " << self.params.size()
<< "\n\tparams_info = " << self.ParamsInfo()
<< "\n\tparams = " << self.ParamsToString();
return ss.str();
});
PyCamera.def(py::init([PyCamera](py::dict dict) {
auto self = py::object(PyCamera());
for (auto& it : dict) {
auto key_str = it.first.cast<std::string>();
if ((key_str == "model") || (key_str == "model_name")) {
self.attr("model_id") = it.second; // Implicit conversion.
} else {
self.attr(it.first) = it.second;
}
}
return self.cast<Camera>();
}),
"dict"_a);
PyCamera.def(py::init([PyCamera](py::kwargs kwargs) {
py::dict dict = kwargs.cast<py::dict>();
return PyCamera(dict).cast<Camera>();
}));
}
4 changes: 2 additions & 2 deletions reconstruction/image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void init_image(py::module& m) {
.def(
"set_up",
[](Image& self, const class Camera& camera) {
THROW_CHECK_EQ(self.CameraId(), camera.CameraId());
THROW_CHECK_EQ(self.CameraId(), camera.camera_id);
self.SetUp(camera);
},
"Setup the image and necessary internal data structures before being "
Expand Down Expand Up @@ -256,7 +256,7 @@ void init_image(py::module& m) {
std::vector<Eigen::Vector2d> world_points(point3Ds.size());
for (int idx = 0; idx < point3Ds.size(); ++idx) {
world_points[idx] =
(self.CamFromWorld() * point3Ds[idx].XYZ()).hnormalized();
(self.CamFromWorld() * point3Ds[idx].xyz).hnormalized();
}
return world_points;
},
Expand Down
Loading

0 comments on commit 91752cb

Please sign in to comment.