// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de) #ifndef COLMAP_SRC_SFM_INCREMENTAL_MAPPER_H_ #define COLMAP_SRC_SFM_INCREMENTAL_MAPPER_H_ #include "base/database.h" #include "base/database_cache.h" #include "base/reconstruction.h" #include "optim/bundle_adjustment.h" #include "sfm/incremental_triangulator.h" #include "util/alignment.h" namespace colmap { // Class that provides all functionality for the incremental reconstruction // procedure. Example usage: // // IncrementalMapper mapper(&database_cache); // mapper.BeginReconstruction(&reconstruction); // CHECK(mapper.FindInitialImagePair(options, image_id1, image_id2)); // CHECK(mapper.RegisterInitialImagePair(options, image_id1, image_id2)); // while (...) { // const auto next_image_ids = mapper.FindNextImages(options); // for (const auto image_id : next_image_ids) { // CHECK(mapper.RegisterNextImage(options, image_id)); // if (...) { // mapper.AdjustLocalBundle(...); // } else { // mapper.AdjustGlobalBundle(...); // } // } // } // mapper.EndReconstruction(false); // class IncrementalMapper { public: struct Options { // Minimum number of inliers for initial image pair. int init_min_num_inliers = 100; // Maximum error in pixels for two-view geometry estimation for initial // image pair. double init_max_error = 4.0; // Maximum forward motion for initial image pair. double init_max_forward_motion = 0.95; // Minimum triangulation angle for initial image pair. double init_min_tri_angle = 16.0; // Maximum number of trials to use an image for initialization. int init_max_reg_trials = 2; // Maximum reprojection error in absolute pose estimation. double abs_pose_max_error = 12.0; // Minimum number of inliers in absolute pose estimation. int abs_pose_min_num_inliers = 30; // Minimum inlier ratio in absolute pose estimation. double abs_pose_min_inlier_ratio = 0.25; // Whether to estimate the focal length in absolute pose estimation. bool abs_pose_refine_focal_length = true; // Whether to estimate the extra parameters in absolute pose estimation. bool abs_pose_refine_extra_params = true; // Number of images to optimize in local bundle adjustment. int local_ba_num_images = 6; // Minimum triangulation for images to be chosen in local bundle adjustment. double local_ba_min_tri_angle = 6; // Thresholds for bogus camera parameters. Images with bogus camera // parameters are filtered and ignored in triangulation. double min_focal_length_ratio = 0.1; // Opening angle of ~130deg double max_focal_length_ratio = 10; // Opening angle of ~5deg double max_extra_param = 1; // Maximum reprojection error in pixels for observations. double filter_max_reproj_error = 4.0; // Minimum triangulation angle in degrees for stable 3D points. double filter_min_tri_angle = 1.5; // Maximum number of trials to register an image. int max_reg_trials = 3; // If reconstruction is provided as input, fix the existing image poses. bool fix_existing_images = false; // Number of threads. int num_threads = -1; // Method to find and select next best image to register. enum class ImageSelectionMethod { MAX_VISIBLE_POINTS_NUM, MAX_VISIBLE_POINTS_RATIO, MIN_UNCERTAINTY, }; ImageSelectionMethod image_selection_method = ImageSelectionMethod::MIN_UNCERTAINTY; bool Check() const; }; struct LocalBundleAdjustmentReport { size_t num_merged_observations = 0; size_t num_completed_observations = 0; size_t num_filtered_observations = 0; size_t num_adjusted_observations = 0; }; // Create incremental mapper. The database cache must live for the entire // life-time of the incremental mapper. explicit IncrementalMapper(const DatabaseCache* database_cache); // Prepare the mapper for a new reconstruction, which might have existing // registered images (in which case `RegisterNextImage` must be called) or // which is empty (in which case `RegisterInitialImagePair` must be called). void BeginReconstruction(Reconstruction* reconstruction); // Cleanup the mapper after the current reconstruction is done. If the // model is discarded, the number of total and shared registered images will // be updated accordingly. void EndReconstruction(const bool discard); // Find initial image pair to seed the incremental reconstruction. The image // pairs should be passed to `RegisterInitialImagePair`. This function // automatically ignores image pairs that failed to register previously. bool FindInitialImagePair(const Options& options, image_t* image_id1, image_t* image_id2); // Find best next image to register in the incremental reconstruction. The // images should be passed to `RegisterNextImage`. This function automatically // ignores images that failed to registered for `max_reg_trials`. std::vector FindNextImages(const Options& options); // Attempt to seed the reconstruction from an image pair. bool RegisterInitialImagePair(const Options& options, const image_t image_id1, const image_t image_id2); // Attempt to register image to the existing model. This requires that // a previous call to `RegisterInitialImagePair` was successful. bool RegisterNextImage(const Options& options, const image_t image_id); // Triangulate observations of image. size_t TriangulateImage(const IncrementalTriangulator::Options& tri_options, const image_t image_id); // Retriangulate image pairs that should have common observations according to // the scene graph but don't due to drift, etc. To handle drift, the employed // reprojection error thresholds should be relatively large. If the thresholds // are too large, non-robust bundle adjustment will break down; if the // thresholds are too small, we cannot fix drift effectively. size_t Retriangulate(const IncrementalTriangulator::Options& tri_options); // Complete tracks by transitively following the scene graph correspondences. // This is especially effective after bundle adjustment, since many cameras // and point locations might have improved. Completion of tracks enables // better subsequent registration of new images. size_t CompleteTracks(const IncrementalTriangulator::Options& tri_options); // Merge tracks by using scene graph correspondences. Similar to // `CompleteTracks`, this is effective after bundle adjustment and improves // the redundancy in subsequent bundle adjustments. size_t MergeTracks(const IncrementalTriangulator::Options& tri_options); // Adjust locally connected images and points of a reference image. In // addition, refine the provided 3D points. Only images connected to the // reference image are optimized. If the provided 3D points are not locally // connected to the reference image, their observing images are set as // constant in the adjustment. LocalBundleAdjustmentReport AdjustLocalBundle( const Options& options, const BundleAdjustmentOptions& ba_options, const IncrementalTriangulator::Options& tri_options, const image_t image_id, const std::unordered_set& point3D_ids); // Global bundle adjustment using Ceres Solver or PBA. bool AdjustGlobalBundle(const Options& options, const BundleAdjustmentOptions& ba_options); bool AdjustParallelGlobalBundle( const BundleAdjustmentOptions& ba_options, const ParallelBundleAdjuster::Options& parallel_ba_options); // Filter images and point observations. size_t FilterImages(const Options& options); size_t FilterPoints(const Options& options); const Reconstruction& GetReconstruction() const; // Number of images that are registered in at least on reconstruction. size_t NumTotalRegImages() const; // Number of shared images between current reconstruction and all other // previous reconstructions. size_t NumSharedRegImages() const; // Get changed 3D points, since the last call to `ClearModifiedPoints3D`. const std::unordered_set& GetModifiedPoints3D(); // Clear the collection of changed 3D points. void ClearModifiedPoints3D(); private: // Find seed images for incremental reconstruction. Suitable seed images have // a large number of correspondences and have camera calibration priors. The // returned list is ordered such that most suitable images are in the front. std::vector FindFirstInitialImage(const Options& options) const; // For a given first seed image, find other images that are connected to the // first image. Suitable second images have a large number of correspondences // to the first image and have camera calibration priors. The returned list is // ordered such that most suitable images are in the front. std::vector FindSecondInitialImage(const Options& options, const image_t image_id1) const; // Find local bundle for given image in the reconstruction. The local bundle // is defined as the images that are most connected, i.e. maximum number of // shared 3D points, to the given image. std::vector FindLocalBundle(const Options& options, const image_t image_id) const; // Register / De-register image in current reconstruction and update // the number of shared images between all reconstructions. void RegisterImageEvent(const image_t image_id); void DeRegisterImageEvent(const image_t image_id); bool EstimateInitialTwoViewGeometry(const Options& options, const image_t image_id1, const image_t image_id2); // Class that holds all necessary data from database in memory. const DatabaseCache* database_cache_; // Class that holds data of the reconstruction. Reconstruction* reconstruction_; // Class that is responsible for incremental triangulation. std::unique_ptr triangulator_; // Number of images that are registered in at least on reconstruction. size_t num_total_reg_images_; // Number of shared images between current reconstruction and all other // previous reconstructions. size_t num_shared_reg_images_; // Estimated two-view geometry of last call to `FindFirstInitialImage`, // used as a cache for a subsequent call to `RegisterInitialImagePair`. image_pair_t prev_init_image_pair_id_; TwoViewGeometry prev_init_two_view_geometry_; // Images and image pairs that have been used for initialization. Each image // and image pair is only tried once for initialization. std::unordered_map init_num_reg_trials_; std::unordered_set init_image_pairs_; // The number of registered images per camera. This information is used // to avoid duplicate refinement of camera parameters and degradation of // already refined camera parameters in local bundle adjustment when multiple // images share intrinsics. std::unordered_map num_reg_images_per_camera_; // The number of reconstructions in which images are registered. std::unordered_map num_registrations_; // Images that have been filtered in current reconstruction. std::unordered_set filtered_images_; // Number of trials to register image in current reconstruction. Used to set // an upper bound to the number of trials to register an image. std::unordered_map num_reg_trials_; // Images that were registered before beginning the reconstruction. // This image list will be non-empty, if the reconstruction is continued from // an existing reconstruction. std::unordered_set existing_image_ids_; }; } // namespace colmap #endif // COLMAP_SRC_SFM_INCREMENTAL_MAPPER_H_