diff --git a/glomap/CMakeLists.txt b/glomap/CMakeLists.txt index e191049a..05681b38 100644 --- a/glomap/CMakeLists.txt +++ b/glomap/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES io/colmap_converter.cc io/colmap_io.cc io/pose_io.cc + io/utils.cc math/gravity.cc math/rigid3d.cc math/tree.cc @@ -44,6 +45,7 @@ set(HEADERS io/colmap_converter.h io/colmap_io.h io/pose_io.h + io/utils.h math/gravity.h math/l1_solver.h math/rigid3d.h diff --git a/glomap/controllers/track_retriangulation.cc b/glomap/controllers/track_retriangulation.cc index 92d73e0f..e2e26e8a 100644 --- a/glomap/controllers/track_retriangulation.cc +++ b/glomap/controllers/track_retriangulation.cc @@ -15,13 +15,20 @@ bool RetriangulateTracks(const TriangulatorOptions& options, std::unordered_map& cameras, std::unordered_map& images, std::unordered_map& tracks) { + // Collect the registered images + std::unordered_set registered_image_names; + for (const auto& [image_id, image] : images) { + if (image.is_registered) { + registered_image_names.insert(image.file_name); + } + } // Following code adapted from COLMAP - auto database_cache = - colmap::DatabaseCache::Create(database, - options.min_num_matches, - false, // ignore_watermarks - {} // reconstruct all possible images - ); + auto database_cache = colmap::DatabaseCache::Create( + database, + options.min_num_matches, + false, // ignore_watermarks + registered_image_names // reconstruct all possible images + ); // Check whether the image is in the database cache. If not, set the image // as not registered to avoid memory error. diff --git a/glomap/exe/global_mapper.cc b/glomap/exe/global_mapper.cc index fb1e512c..161b5c34 100644 --- a/glomap/exe/global_mapper.cc +++ b/glomap/exe/global_mapper.cc @@ -2,6 +2,8 @@ #include "glomap/controllers/option_manager.h" #include "glomap/io/colmap_io.h" +#include "glomap/io/pose_io.h" +#include "glomap/io/utils.h" #include "glomap/types.h" #include @@ -19,11 +21,16 @@ int RunMapper(int argc, char** argv) { std::string image_path = ""; std::string constraint_type = "ONLY_POINTS"; std::string output_format = "bin"; + std::string image_list_path = ""; OptionManager options; options.AddRequiredOption("database_path", &database_path); options.AddRequiredOption("output_path", &output_path); options.AddDefaultOption("image_path", &image_path); + options.AddDefaultOption("image_list_path", + &image_list_path, + "Path to the image list file, if not provided, " + "all images in the database will be used"); options.AddDefaultOption("constraint_type", &constraint_type, "{ONLY_POINTS, ONLY_CAMERAS, " @@ -61,6 +68,15 @@ int RunMapper(int argc, char** argv) { return EXIT_FAILURE; } + std::unordered_set image_filenames; + if (image_list_path != "") { + if (!colmap::ExistsFile(image_list_path)) { + LOG(ERROR) << "`image_list_path` is not a file"; + return EXIT_FAILURE; + } + ReadImageList(image_list_path, image_filenames); + } + // Load the database ViewGraph view_graph; std::unordered_map cameras; @@ -68,7 +84,8 @@ int RunMapper(int argc, char** argv) { std::unordered_map tracks; const colmap::Database database(database_path); - ConvertDatabaseToGlomap(database, view_graph, cameras, images); + ConvertDatabaseToGlomap( + database, view_graph, cameras, images, &image_filenames); if (view_graph.image_pairs.empty()) { LOG(ERROR) << "Can't continue without image pairs"; diff --git a/glomap/io/colmap_converter.cc b/glomap/io/colmap_converter.cc index abfe1ffc..e743f7b2 100644 --- a/glomap/io/colmap_converter.cc +++ b/glomap/io/colmap_converter.cc @@ -176,10 +176,13 @@ void ConvertColmapPoints3DToGlomapTracks( // For ease of debug, go through the database twice: first extract the available // pairs, then read matches from pairs. -void ConvertDatabaseToGlomap(const colmap::Database& database, - ViewGraph& view_graph, - std::unordered_map& cameras, - std::unordered_map& images) { +void ConvertDatabaseToGlomap( + const colmap::Database& database, + ViewGraph& view_graph, + std::unordered_map& cameras, + std::unordered_map& images, + const std::unordered_set* image_filenames) { + bool has_image_filenames = image_filenames && !image_filenames->empty(); // Add the images std::vector images_colmap = database.ReadAllImages(); image_t counter = 0; @@ -188,6 +191,11 @@ void ConvertDatabaseToGlomap(const colmap::Database& database, << images_colmap.size() << std::flush; counter++; + if (has_image_filenames && + image_filenames->find(image.Name()) == image_filenames->end()) { + continue; // Skip images not in the specified set + } + const image_t image_id = image.ImageId(); if (image_id == colmap::kInvalidImageId) continue; auto ite = images.insert(std::make_pair( @@ -239,6 +247,12 @@ void ConvertDatabaseToGlomap(const colmap::Database& database, colmap::image_t image_id1 = image_pair_colmap.first; colmap::image_t image_id2 = image_pair_colmap.second; + if (images.find(image_id1) == images.end() || + images.find(image_id2) == images.end()) { + // If the image is not added to the images map, then skip + continue; + } + colmap::FeatureMatches& feature_matches = all_matches[match_idx].second; // Initialize the image pair diff --git a/glomap/io/colmap_converter.h b/glomap/io/colmap_converter.h index 5bd4457e..5cf37ab4 100644 --- a/glomap/io/colmap_converter.h +++ b/glomap/io/colmap_converter.h @@ -27,9 +27,11 @@ void ConvertColmapPoints3DToGlomapTracks( const colmap::Reconstruction& reconstruction, std::unordered_map& tracks); -void ConvertDatabaseToGlomap(const colmap::Database& database, - ViewGraph& view_graph, - std::unordered_map& cameras, - std::unordered_map& images); +void ConvertDatabaseToGlomap( + const colmap::Database& database, + ViewGraph& view_graph, + std::unordered_map& cameras, + std::unordered_map& images, + const std::unordered_set* image_filenames = nullptr); } // namespace glomap diff --git a/glomap/io/pose_io.cc b/glomap/io/pose_io.cc index eeda2e6b..51f97c4d 100644 --- a/glomap/io/pose_io.cc +++ b/glomap/io/pose_io.cc @@ -7,7 +7,8 @@ namespace glomap { void ReadRelPose(const std::string& file_path, std::unordered_map& images, - ViewGraph& view_graph) { + ViewGraph& view_graph, + bool editable_images) { std::unordered_map name_idx; image_t max_image_id = 0; for (const auto& [image_id, image] : images) { @@ -35,21 +36,28 @@ void ReadRelPose(const std::string& file_path, std::getline(line_stream, item, ' '); file2 = item; + bool add_image = true; if (name_idx.find(file1) == name_idx.end()) { + if (!editable_images) { + add_image = false; + } max_image_id += 1; images.insert( std::make_pair(max_image_id, Image(max_image_id, -1, file1))); name_idx[file1] = max_image_id; } if (name_idx.find(file2) == name_idx.end()) { + if (!editable_images) { + add_image = false; + } max_image_id += 1; images.insert( std::make_pair(max_image_id, Image(max_image_id, -1, file2))); name_idx[file2] = max_image_id; } - image_t index1 = name_idx[file1]; - image_t index2 = name_idx[file2]; + image_t index1 = add_image ? name_idx[file1] : -1; + image_t index2 = add_image ? name_idx[file2] : -1; image_pair_t pair_id = ImagePair::ImagePairToPairId(index1, index2); @@ -64,10 +72,17 @@ void ReadRelPose(const std::string& file_path, std::getline(line_stream, item, ' '); pose_rel.translation[i] = std::stod(item); } + if (!add_image) continue; + counter++; + if (view_graph.image_pairs.find(pair_id) != view_graph.image_pairs.end()) { + // If the pair already exists, update the relative pose + view_graph.image_pairs[pair_id].cam2_from_cam1 = pose_rel; + view_graph.image_pairs[pair_id].is_valid = true; + continue; + } view_graph.image_pairs.insert( std::make_pair(pair_id, ImagePair(index1, index2, pose_rel))); - counter++; } LOG(INFO) << counter << " relpose are loaded" << std::endl; } diff --git a/glomap/io/pose_io.h b/glomap/io/pose_io.h index e98112f7..8c126fc5 100644 --- a/glomap/io/pose_io.h +++ b/glomap/io/pose_io.h @@ -7,9 +7,11 @@ namespace glomap { // Required data structures // IMAGE_NAME_1 IMAGE_NAME_2 QW QX QY QZ TX TY TZ +// editable_images: where the images can be added if they are not present void ReadRelPose(const std::string& file_path, std::unordered_map& images, - ViewGraph& view_graph); + ViewGraph& view_graph, + bool editable_images = true); // Required data structures // IMAGE_NAME_1 IMAGE_NAME_2 weight diff --git a/glomap/io/utils.cc b/glomap/io/utils.cc new file mode 100644 index 00000000..1cf03abb --- /dev/null +++ b/glomap/io/utils.cc @@ -0,0 +1,21 @@ +#include "glomap/io/utils.h" + +#include + +#include "colmap/util/string.h" + +namespace glomap { +void ReadImageList(const std::string& file_path, + std::unordered_set& image_filenames) { + std::ifstream image_list_file(file_path); + std::string line; + while (std::getline(image_list_file, line)) { + if (!line.empty()) { + colmap::StringTrim(&line); + image_filenames.insert(line); + } + } + image_list_file.close(); +} + +} // namespace glomap \ No newline at end of file diff --git a/glomap/io/utils.h b/glomap/io/utils.h new file mode 100644 index 00000000..655835d0 --- /dev/null +++ b/glomap/io/utils.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace glomap { +// Read a list of image filenames from a file. +// Required data structure: +// IMAGE_NAME_1 +// IMAGE_NAME_2 +// ... +// IMAGE_NAME_N +void ReadImageList(const std::string& file_path, + std::unordered_set& image_filenames); +} // namespace glomap diff --git a/glomap/processors/view_graph_manipulation.cc b/glomap/processors/view_graph_manipulation.cc index 38ec4dc6..84288e89 100644 --- a/glomap/processors/view_graph_manipulation.cc +++ b/glomap/processors/view_graph_manipulation.cc @@ -71,7 +71,10 @@ image_t ViewGraphManipulater::EstablishStrongClusters( StrongClusterCriteria criteria, double min_thres, int min_num_images) { - image_t num_img_before = view_graph.KeepLargestConnectedComponents(images); + // Mark all images as registered + for (auto& [image_id, image] : images) { + image.is_registered = true; + } // Construct the initial cluster by keeping the pairs with weight > min_thres UnionFind uf;