Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions cmake/FindDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,39 @@ if(TESTS_ENABLED)
find_package(GTest REQUIRED)
endif()

include(FetchContent)

# PoseLib with EQUIRECTANGULAR support
FetchContent_Declare(PoseLib
GIT_REPOSITORY https://github.com/zalo/PoseLib.git
GIT_TAG feature/equirectangular
EXCLUDE_FROM_ALL
SYSTEM
)
message(STATUS "Configuring PoseLib (with EQUIRECTANGULAR support)...")
if (FETCH_POSELIB)
FetchContent_MakeAvailable(PoseLib)
else()
find_package(PoseLib REQUIRED)
endif()
message(STATUS "Configuring PoseLib... done")

# COLMAP with EQUIRECTANGULAR support
FetchContent_Declare(COLMAP
GIT_REPOSITORY https://github.com/zalo/colmap.git
GIT_TAG feature/equirectangular
EXCLUDE_FROM_ALL
)
message(STATUS "Configuring COLMAP (with EQUIRECTANGULAR support)...")
set(UNINSTALL_ENABLED OFF CACHE INTERNAL "")
set(GUI_ENABLED OFF CACHE INTERNAL "")
if (FETCH_COLMAP)
FetchContent_MakeAvailable(COLMAP)
else()
find_package(COLMAP REQUIRED)
endif()
message(STATUS "Configuring COLMAP... done")

set(CUDA_MIN_VERSION "7.0")
if(CUDA_ENABLED)
if(CMAKE_VERSION VERSION_LESS 3.17)
Expand Down
11 changes: 5 additions & 6 deletions glomap/controllers/track_retriangulation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ bool RetriangulateTracks(const TriangulatorOptions& options,
std::unordered_map<image_t, Image>& images,
std::unordered_map<track_t, Track>& tracks) {
// Following code adapted from COLMAP
auto database_cache =
colmap::DatabaseCache::Create(database,
options.min_num_matches,
false, // ignore_watermarks
{} // reconstruct all possible images
);
colmap::DatabaseCache::Options cache_options;
cache_options.min_num_matches = options.min_num_matches;
cache_options.ignore_watermarks = false;
// image_names defaults to empty set (reconstruct all possible images)
auto database_cache = colmap::DatabaseCache::Create(database, cache_options);

// Check whether the image is in the database cache. If not, set the image
// as not registered to avoid memory error.
Expand Down
15 changes: 15 additions & 0 deletions glomap/estimators/view_graph_calibration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ void ViewGraphCalibrator::Reset(
focals_.clear();
focals_.reserve(cameras.size());
for (const auto& [camera_id, camera] : cameras) {
// Skip spherical cameras - they don't have a focal length to estimate
if (camera.IsSpherical()) {
continue;
}
focals_[camera_id] = camera.Focal();
}

Expand Down Expand Up @@ -85,6 +89,12 @@ void ViewGraphCalibrator::AddImagePair(
const camera_t camera_id1 = images.at(image_pair.image_id1).camera_id;
const camera_t camera_id2 = images.at(image_pair.image_id2).camera_id;

// Skip image pairs involving spherical cameras
if (cameras.at(camera_id1).IsSpherical() ||
cameras.at(camera_id2).IsSpherical()) {
return;
}

if (camera_id1 == camera_id2) {
problem_->AddResidualBlock(
FetzerFocalLengthSameCameraCost::Create(
Expand Down Expand Up @@ -123,6 +133,11 @@ void ViewGraphCalibrator::CopyBackResults(
std::unordered_map<camera_t, Camera>& cameras) {
size_t counter = 0;
for (auto& [camera_id, camera] : cameras) {
// Skip spherical cameras - they were not optimized
if (camera.IsSpherical()) {
continue;
}

if (!problem_->HasParameterBlock(&(focals_[camera_id]))) continue;

// if the estimated parameter is too crazy, reject it
Expand Down
24 changes: 16 additions & 8 deletions glomap/io/colmap_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -384,19 +384,27 @@ void ConvertDatabaseToGlomap(const colmap::Database& database,

// Collect the fundemental matrices
if (two_view.config == colmap::TwoViewGeometry::UNCALIBRATED) {
image_pair.F = two_view.F;
if (two_view.F.has_value()) {
image_pair.F = two_view.F.value();
}
} else if (two_view.config == colmap::TwoViewGeometry::CALIBRATED) {
FundamentalFromMotionAndCameras(
cameras.at(images.at(image_pair.image_id1).camera_id),
cameras.at(images.at(image_pair.image_id2).camera_id),
two_view.cam2_from_cam1,
&image_pair.F);
if (two_view.cam2_from_cam1.has_value()) {
FundamentalFromMotionAndCameras(
cameras.at(images.at(image_pair.image_id1).camera_id),
cameras.at(images.at(image_pair.image_id2).camera_id),
two_view.cam2_from_cam1.value(),
&image_pair.F);
}
} else if (two_view.config == colmap::TwoViewGeometry::PLANAR ||
two_view.config == colmap::TwoViewGeometry::PANORAMIC ||
two_view.config ==
colmap::TwoViewGeometry::PLANAR_OR_PANORAMIC) {
image_pair.H = two_view.H;
image_pair.F = two_view.F;
if (two_view.H.has_value()) {
image_pair.H = two_view.H.value();
}
if (two_view.F.has_value()) {
image_pair.F = two_view.F.value();
}
}
image_pair.config = two_view.config;

Expand Down
10 changes: 6 additions & 4 deletions glomap/processors/view_graph_manipulation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,13 @@ void ViewGraphManipulater::DecomposeRelPose(
return;

image_pair.config = two_view_geometry.config;
image_pair.cam2_from_cam1 = two_view_geometry.cam2_from_cam1;
if (two_view_geometry.cam2_from_cam1.has_value()) {
image_pair.cam2_from_cam1 = two_view_geometry.cam2_from_cam1.value();

if (image_pair.cam2_from_cam1.translation.norm() > EPS) {
image_pair.cam2_from_cam1.translation =
image_pair.cam2_from_cam1.translation.normalized();
if (image_pair.cam2_from_cam1.translation.norm() > EPS) {
image_pair.cam2_from_cam1.translation =
image_pair.cam2_from_cam1.translation.normalized();
}
}
});
}
Expand Down
37 changes: 36 additions & 1 deletion glomap/scene/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <colmap/scene/camera.h>
#include <colmap/sensor/models.h>
#include <colmap/util/logging.h>

#include <PoseLib/misc/colmap_models.h>

Expand All @@ -20,18 +21,52 @@ struct Camera : public colmap::Camera {

bool has_refined_focal_length = false;

// Returns true if this is a spherical camera model (e.g., EQUIRECTANGULAR)
// Spherical cameras do not have meaningful focal length or principal point
inline bool IsSpherical() const;

inline double Focal() const;
inline Eigen::Vector2d PrincipalPoint() const;
inline Eigen::Matrix3d GetK() const;
};

double Camera::Focal() const { return (FocalLengthX() + FocalLengthY()) / 2.0; }
bool Camera::IsSpherical() const {
// Check model_id for EQUIRECTANGULAR (most robust)
// Also check model name as fallback for compatibility
return model_id == colmap::CameraModelId::kEquirectangular ||
ModelName() == "EQUIRECTANGULAR" || ModelName() == "SPHERICAL";
}

double Camera::Focal() const {
// Spherical cameras don't have a meaningful focal length
// Return a dummy value based on image dimensions for compatibility
if (IsSpherical()) {
constexpr double kPi = 3.141592653589793238462643383279502884L;
return static_cast<double>(std::max(width, height)) / kPi;
}
return (FocalLengthX() + FocalLengthY()) / 2.0;
}

Eigen::Vector2d Camera::PrincipalPoint() const {
// Spherical cameras don't have a principal point in the traditional sense
// Return the image center for compatibility
if (IsSpherical()) {
return Eigen::Vector2d(width / 2.0, height / 2.0);
}
return Eigen::Vector2d(PrincipalPointX(), PrincipalPointY());
}

Eigen::Matrix3d Camera::GetK() const {
// Spherical cameras don't have a 3x3 intrinsic matrix
// Return identity-like matrix for compatibility (should not be used)
if (IsSpherical()) {
LOG(WARNING) << "GetK() called on spherical camera - intrinsic matrix is "
"not meaningful for equirectangular cameras";
Eigen::Matrix3d K = Eigen::Matrix3d::Identity();
K(0, 2) = width / 2.0;
K(1, 2) = height / 2.0;
return K;
}
Eigen::Matrix3d K;
K << FocalLengthX(), 0, PrincipalPointX(), 0, FocalLengthY(),
PrincipalPointY(), 0, 0, 1;
Expand Down