text: // Copyright (c) 2018, 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)
  
  #include "util/option_manager.h"
  
  #include <boost/filesystem/operations.hpp>
  #include <boost/property_tree/ini_parser.hpp>
  
  #include "base/image_reader.h"
  #include "controllers/incremental_mapper.h"
  #include "feature/extraction.h"
  #include "feature/matching.h"
  #include "feature/sift.h"
  #include "mvs/fusion.h"
  #include "mvs/meshing.h"
  #include "mvs/patch_match.h"
  #include "optim/bundle_adjustment.h"
  #include "ui/render_options.h"
  #include "util/misc.h"
  #include "util/random.h"
  #include "util/version.h"
  
  namespace config = boost::program_options;
  
  namespace colmap {
  
  OptionManager::OptionManager(bool add_project_options) {
    project_path.reset(new std::string());
    database_path.reset(new std::string());
    image_path.reset(new std::string());
  
    image_reader.reset(new ImageReaderOptions());
    sift_extraction.reset(new SiftExtractionOptions());
    sift_matching.reset(new SiftMatchingOptions());
    exhaustive_matching.reset(new ExhaustiveMatchingOptions());
    sequential_matching.reset(new SequentialMatchingOptions());
    vocab_tree_matching.reset(new VocabTreeMatchingOptions());
    spatial_matching.reset(new SpatialMatchingOptions());
    transitive_matching.reset(new TransitiveMatchingOptions());
    image_pairs_matching.reset(new ImagePairsMatchingOptions());
    bundle_adjustment.reset(new BundleAdjustmentOptions());
    mapper.reset(new IncrementalMapperOptions());
    patch_match_stereo.reset(new mvs::PatchMatchOptions());
    stereo_fusion.reset(new mvs::StereoFusionOptions());
    poisson_meshing.reset(new mvs::PoissonMeshingOptions());
    delaunay_meshing.reset(new mvs::DelaunayMeshingOptions());
    render.reset(new RenderOptions());
  
    Reset();
  
    desc_->add_options()("help,h", "");
  
    AddRandomOptions();
    AddLogOptions();
  
    if (add_project_options) {
      desc_->add_options()("project_path", config::value<std::string>());
    }
  }
  
  void OptionManager::ModifyForIndividualData() {
    mapper->min_focal_length_ratio = 0.1;
    mapper->max_focal_length_ratio = 10;
    mapper->max_extra_param = std::numeric_limits<double>::max();
  }
  
  void OptionManager::ModifyForVideoData() {
    const bool kResetPaths = false;
    ResetOptions(kResetPaths);
    mapper->mapper.init_min_tri_angle /= 2;
    mapper->ba_global_images_ratio = 1.4;
    mapper->ba_global_points_ratio = 1.4;
    mapper->min_focal_length_ratio = 0.1;
    mapper->max_focal_length_ratio = 10;
    mapper->max_extra_param = std::numeric_limits<double>::max();
    stereo_fusion->min_num_pixels = 15;
  }
  
  void OptionManager::ModifyForInternetData() {
    stereo_fusion->min_num_pixels = 10;
  }
  
  void OptionManager::ModifyForLowQuality() {
    sift_extraction->max_image_size = 1000;
    sequential_matching->loop_detection_num_images /= 2;
    vocab_tree_matching->num_images /= 2;
    mapper->ba_local_max_num_iterations /= 2;
    mapper->ba_global_max_num_iterations /= 2;
    mapper->ba_global_images_ratio *= 1.2;
    mapper->ba_global_points_ratio *= 1.2;
    mapper->ba_global_max_refinements = 2;
    patch_match_stereo->max_image_size = 1000;
    patch_match_stereo->window_radius = 4;
    patch_match_stereo->window_step = 2;
    patch_match_stereo->num_samples /= 2;
    patch_match_stereo->num_iterations = 3;
    patch_match_stereo->geom_consistency = false;
    stereo_fusion->check_num_images /= 2;
    stereo_fusion->max_image_size = 1000;
  }
  
  void OptionManager::ModifyForMediumQuality() {
    sift_extraction->max_image_size = 1600;
    sequential_matching->loop_detection_num_images /= 1.5;
    vocab_tree_matching->num_images /= 1.5;
    mapper->ba_local_max_num_iterations /= 1.5;
    mapper->ba_global_max_num_iterations /= 1.5;
    mapper->ba_global_images_ratio *= 1.1;
    mapper->ba_global_points_ratio *= 1.1;
    mapper->ba_global_max_refinements = 2;
    patch_match_stereo->max_image_size = 1600;
    patch_match_stereo->window_radius = 4;
    patch_match_stereo->window_step = 2;
    patch_match_stereo->num_samples /= 1.5;
    patch_match_stereo->num_iterations = 5;
    patch_match_stereo->geom_consistency = false;
    stereo_fusion->check_num_images /= 1.5;
    stereo_fusion->max_image_size = 1600;
  }
  
  void OptionManager::ModifyForHighQuality() {
    sift_extraction->estimate_affine_shape = true;
    sift_extraction->max_image_size = 2400;
    sift_matching->guided_matching = true;
    mapper->ba_local_max_num_iterations = 30;
    mapper->ba_local_max_refinements = 3;
    mapper->ba_global_max_num_iterations = 75;
    patch_match_stereo->max_image_size = 2400;
    stereo_fusion->max_image_size = 2400;
  }
  
  void OptionManager::ModifyForExtremeQuality() {
    // Most of the options are set to extreme quality by default.
    sift_extraction->estimate_affine_shape = true;
    sift_extraction->domain_size_pooling = true;
    sift_matching->guided_matching = true;
    mapper->ba_local_max_num_iterations = 40;
    mapper->ba_local_max_refinements = 3;
    mapper->ba_global_max_num_iterations = 100;
  }
  
  void OptionManager::AddAllOptions() {
    AddLogOptions();
    AddRandomOptions();
    AddDatabaseOptions();
    AddImageOptions();
    AddExtractionOptions();
    AddMatchingOptions();
    AddExhaustiveMatchingOptions();
    AddSequentialMatchingOptions();
    AddVocabTreeMatchingOptions();
    AddSpatialMatchingOptions();
    AddTransitiveMatchingOptions();
    AddImagePairsMatchingOptions();
    AddBundleAdjustmentOptions();
    AddMapperOptions();
    AddPatchMatchStereoOptions();
    AddStereoFusionOptions();
    AddPoissonMeshingOptions();
    AddDelaunayMeshingOptions();
    AddRenderOptions();
  }
  
  void OptionManager::AddLogOptions() {
    if (added_log_options_) {
      return;
    }
    added_log_options_ = true;
  
    AddAndRegisterDefaultOption("log_to_stderr", &FLAGS_logtostderr);
    AddAndRegisterDefaultOption("log_level", &FLAGS_v);
  }
  
  void OptionManager::AddRandomOptions() {
    if (added_random_options_) {
      return;
    }
    added_random_options_ = true;
  
    AddAndRegisterDefaultOption("random_seed", &kDefaultPRNGSeed);
  }
  
  void OptionManager::AddDatabaseOptions() {
    if (added_database_options_) {
      return;
    }
    added_database_options_ = true;
  
    AddAndRegisterRequiredOption("database_path", database_path.get());
  }
  
  void OptionManager::AddImageOptions() {
    if (added_image_options_) {
      return;
    }
    added_image_options_ = true;
  
    AddAndRegisterRequiredOption("image_path", image_path.get());
  }
  
  void OptionManager::AddExtractionOptions() {
    if (added_extraction_options_) {
      return;
    }
    added_extraction_options_ = true;
  
    AddAndRegisterDefaultOption("ImageReader.mask_path",
                                &image_reader->mask_path);
    AddAndRegisterDefaultOption("ImageReader.camera_model",
                                &image_reader->camera_model);
    AddAndRegisterDefaultOption("ImageReader.single_camera",
                                &image_reader->single_camera);
    AddAndRegisterDefaultOption("ImageReader.single_camera_per_folder",
                                &image_reader->single_camera_per_folder);
    AddAndRegisterDefaultOption("ImageReader.single_camera_per_image",
                                &image_reader->single_camera_per_image);
    AddAndRegisterDefaultOption("ImageReader.existing_camera_id",
                                &image_reader->existing_camera_id);
    AddAndRegisterDefaultOption("ImageReader.camera_params",
                                &image_reader->camera_params);
    AddAndRegisterDefaultOption("ImageReader.default_focal_length_factor",
                                &image_reader->default_focal_length_factor);
    AddAndRegisterDefaultOption("ImageReader.camera_mask_path",
                                &image_reader->camera_mask_path);
  
    AddAndRegisterDefaultOption("SiftExtraction.num_threads",
                                &sift_extraction->num_threads);
    AddAndRegisterDefaultOption("SiftExtraction.use_gpu",
                                &sift_extraction->use_gpu);
    AddAndRegisterDefaultOption("SiftExtraction.gpu_index",
                                &sift_extraction->gpu_index);
    AddAndRegisterDefaultOption("SiftExtraction.max_image_size",
                                &sift_extraction->max_image_size);
    AddAndRegisterDefaultOption("SiftExtraction.max_num_features",
                                &sift_extraction->max_num_features);
    AddAndRegisterDefaultOption("SiftExtraction.first_octave",
                                &sift_extraction->first_octave);
    AddAndRegisterDefaultOption("SiftExtraction.num_octaves",
                                &sift_extraction->num_octaves);
    AddAndRegisterDefaultOption("SiftExtraction.octave_resolution",
                                &sift_extraction->octave_resolution);
    AddAndRegisterDefaultOption("SiftExtraction.peak_threshold",
                                &sift_extraction->peak_threshold);
    AddAndRegisterDefaultOption("SiftExtraction.edge_threshold",
                                &sift_extraction->edge_threshold);
    AddAndRegisterDefaultOption("SiftExtraction.estimate_affine_shape",
                                &sift_extraction->estimate_affine_shape);
    AddAndRegisterDefaultOption("SiftExtraction.max_num_orientations",
                                &sift_extraction->max_num_orientations);
    AddAndRegisterDefaultOption("SiftExtraction.upright",
                                &sift_extraction->upright);
    AddAndRegisterDefaultOption("SiftExtraction.domain_size_pooling",
                                &sift_extraction->domain_size_pooling);
    AddAndRegisterDefaultOption("SiftExtraction.dsp_min_scale",
                                &sift_extraction->dsp_min_scale);
    AddAndRegisterDefaultOption("SiftExtraction.dsp_max_scale",
                                &sift_extraction->dsp_max_scale);
    AddAndRegisterDefaultOption("SiftExtraction.dsp_num_scales",
                                &sift_extraction->dsp_num_scales);
  }
  
  void OptionManager::AddMatchingOptions() {
    if (added_match_options_) {
      return;
    }
    added_match_options_ = true;
  
    AddAndRegisterDefaultOption("SiftMatching.num_threads",
                                &sift_matching->num_threads);
    AddAndRegisterDefaultOption("SiftMatching.use_gpu", &sift_matching->use_gpu);
    AddAndRegisterDefaultOption("SiftMatching.gpu_index",
                                &sift_matching->gpu_index);
    AddAndRegisterDefaultOption("SiftMatching.max_ratio",
                                &sift_matching->max_ratio);
    AddAndRegisterDefaultOption("SiftMatching.max_distance",
                                &sift_matching->max_distance);
    AddAndRegisterDefaultOption("SiftMatching.cross_check",
                                &sift_matching->cross_check);
    AddAndRegisterDefaultOption("SiftMatching.max_error",
                                &sift_matching->max_error);
    AddAndRegisterDefaultOption("SiftMatching.max_num_matches",
                                &sift_matching->max_num_matches);
    AddAndRegisterDefaultOption("SiftMatching.confidence",
                                &sift_matching->confidence);
    AddAndRegisterDefaultOption("SiftMatching.max_num_trials",
                                &sift_matching->max_num_trials);
    AddAndRegisterDefaultOption("SiftMatching.min_inlier_ratio",
                                &sift_matching->min_inlier_ratio);
    AddAndRegisterDefaultOption("SiftMatching.min_num_inliers",
                                &sift_matching->min_num_inliers);
    AddAndRegisterDefaultOption("SiftMatching.multiple_models",
                                &sift_matching->multiple_models);
    AddAndRegisterDefaultOption("SiftMatching.guided_matching",
                                &sift_matching->guided_matching);
    AddAndRegisterDefaultOption("SiftMatching.planar_scene",
                                &sift_matching->planar_scene);
  }
  
  void OptionManager::AddExhaustiveMatchingOptions() {
    if (added_exhaustive_match_options_) {
      return;
    }
    added_exhaustive_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("ExhaustiveMatching.block_size",
                                &exhaustive_matching->block_size);
  }
  
  void OptionManager::AddSequentialMatchingOptions() {
    if (added_sequential_match_options_) {
      return;
    }
    added_sequential_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("SequentialMatching.overlap",
                                &sequential_matching->overlap);
    AddAndRegisterDefaultOption("SequentialMatching.quadratic_overlap",
                                &sequential_matching->quadratic_overlap);
    AddAndRegisterDefaultOption("SequentialMatching.loop_detection",
                                &sequential_matching->loop_detection);
    AddAndRegisterDefaultOption("SequentialMatching.loop_detection_period",
                                &sequential_matching->loop_detection_period);
    AddAndRegisterDefaultOption("SequentialMatching.loop_detection_num_images",
                                &sequential_matching->loop_detection_num_images);
    AddAndRegisterDefaultOption(
        "SequentialMatching.loop_detection_num_nearest_neighbors",
        &sequential_matching->loop_detection_num_nearest_neighbors);
    AddAndRegisterDefaultOption("SequentialMatching.loop_detection_num_checks",
                                &sequential_matching->loop_detection_num_checks);
    AddAndRegisterDefaultOption(
        "SequentialMatching.loop_detection_num_images_after_verification",
        &sequential_matching->loop_detection_num_images_after_verification);
    AddAndRegisterDefaultOption(
        "SequentialMatching.loop_detection_max_num_features",
        &sequential_matching->loop_detection_max_num_features);
    AddAndRegisterDefaultOption("SequentialMatching.vocab_tree_path",
                                &sequential_matching->vocab_tree_path);
  }
  
  void OptionManager::AddVocabTreeMatchingOptions() {
    if (added_vocab_tree_match_options_) {
      return;
    }
    added_vocab_tree_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("VocabTreeMatching.num_images",
                                &vocab_tree_matching->num_images);
    AddAndRegisterDefaultOption("VocabTreeMatching.num_nearest_neighbors",
                                &vocab_tree_matching->num_nearest_neighbors);
    AddAndRegisterDefaultOption("VocabTreeMatching.num_checks",
                                &vocab_tree_matching->num_checks);
    AddAndRegisterDefaultOption(
        "VocabTreeMatching.num_images_after_verification",
        &vocab_tree_matching->num_images_after_verification);
    AddAndRegisterDefaultOption("VocabTreeMatching.max_num_features",
                                &vocab_tree_matching->max_num_features);
    AddAndRegisterDefaultOption("VocabTreeMatching.vocab_tree_path",
                                &vocab_tree_matching->vocab_tree_path);
    AddAndRegisterDefaultOption("VocabTreeMatching.match_list_path",
                                &vocab_tree_matching->match_list_path);
  }
  
  void OptionManager::AddSpatialMatchingOptions() {
    if (added_spatial_match_options_) {
      return;
    }
    added_spatial_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("SpatialMatching.is_gps",
                                &spatial_matching->is_gps);
    AddAndRegisterDefaultOption("SpatialMatching.ignore_z",
                                &spatial_matching->ignore_z);
    AddAndRegisterDefaultOption("SpatialMatching.max_num_neighbors",
                                &spatial_matching->max_num_neighbors);
    AddAndRegisterDefaultOption("SpatialMatching.max_distance",
                                &spatial_matching->max_distance);
  }
  
  void OptionManager::AddTransitiveMatchingOptions() {
    if (added_transitive_match_options_) {
      return;
    }
    added_transitive_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("TransitiveMatching.batch_size",
                                &transitive_matching->batch_size);
    AddAndRegisterDefaultOption("TransitiveMatching.num_iterations",
                                &transitive_matching->num_iterations);
  }
  
  void OptionManager::AddImagePairsMatchingOptions() {
    if (added_image_pairs_match_options_) {
      return;
    }
    added_image_pairs_match_options_ = true;
  
    AddMatchingOptions();
  
    AddAndRegisterDefaultOption("ImagePairsMatching.block_size",
                                &image_pairs_matching->block_size);
  }
  
  void OptionManager::AddBundleAdjustmentOptions() {
    if (added_ba_options_) {
      return;
    }
    added_ba_options_ = true;
  
    AddAndRegisterDefaultOption(
        "BundleAdjustment.max_num_iterations",
        &bundle_adjustment->solver_options.max_num_iterations);
    AddAndRegisterDefaultOption(
        "BundleAdjustment.max_linear_solver_iterations",
        &bundle_adjustment->solver_options.max_linear_solver_iterations);
    AddAndRegisterDefaultOption(
        "BundleAdjustment.function_tolerance",
        &bundle_adjustment->solver_options.function_tolerance);
    AddAndRegisterDefaultOption(
        "BundleAdjustment.gradient_tolerance",
        &bundle_adjustment->solver_options.gradient_tolerance);
    AddAndRegisterDefaultOption(
        "BundleAdjustment.parameter_tolerance",
        &bundle_adjustment->solver_options.parameter_tolerance);
    AddAndRegisterDefaultOption("BundleAdjustment.refine_focal_length",
                                &bundle_adjustment->refine_focal_length);
    AddAndRegisterDefaultOption("BundleAdjustment.refine_principal_point",
                                &bundle_adjustment->refine_principal_point);
    AddAndRegisterDefaultOption("BundleAdjustment.refine_extra_params",
                                &bundle_adjustment->refine_extra_params);
    AddAndRegisterDefaultOption("BundleAdjustment.refine_extrinsics",
                                &bundle_adjustment->refine_extrinsics);
  }
  
  void OptionManager::AddMapperOptions() {
    if (added_mapper_options_) {
      return;
    }
    added_mapper_options_ = true;
  
    AddAndRegisterDefaultOption("Mapper.min_num_matches",
                                &mapper->min_num_matches);
    AddAndRegisterDefaultOption("Mapper.ignore_watermarks",
                                &mapper->ignore_watermarks);
    AddAndRegisterDefaultOption("Mapper.multiple_models",
                                &mapper->multiple_models);
    AddAndRegisterDefaultOption("Mapper.max_num_models", &mapper->max_num_models);
    AddAndRegisterDefaultOption("Mapper.max_model_overlap",
                                &mapper->max_model_overlap);
    AddAndRegisterDefaultOption("Mapper.min_model_size", &mapper->min_model_size);
    AddAndRegisterDefaultOption("Mapper.init_image_id1", &mapper->init_image_id1);
    AddAndRegisterDefaultOption("Mapper.init_image_id2", &mapper->init_image_id2);
    AddAndRegisterDefaultOption("Mapper.init_num_trials",
                                &mapper->init_num_trials);
    AddAndRegisterDefaultOption("Mapper.extract_colors", &mapper->extract_colors);
    AddAndRegisterDefaultOption("Mapper.num_threads", &mapper->num_threads);
    AddAndRegisterDefaultOption("Mapper.min_focal_length_ratio",
                                &mapper->min_focal_length_ratio);
    AddAndRegisterDefaultOption("Mapper.max_focal_length_ratio",
                                &mapper->max_focal_length_ratio);
    AddAndRegisterDefaultOption("Mapper.max_extra_param",
                                &mapper->max_extra_param);
    AddAndRegisterDefaultOption("Mapper.ba_refine_focal_length",
                                &mapper->ba_refine_focal_length);
    AddAndRegisterDefaultOption("Mapper.ba_refine_principal_point",
                                &mapper->ba_refine_principal_point);
    AddAndRegisterDefaultOption("Mapper.ba_refine_extra_params",
                                &mapper->ba_refine_extra_params);
    AddAndRegisterDefaultOption(
        "Mapper.ba_min_num_residuals_for_multi_threading",
        &mapper->ba_min_num_residuals_for_multi_threading);
    AddAndRegisterDefaultOption("Mapper.ba_local_num_images",
                                &mapper->ba_local_num_images);
    AddAndRegisterDefaultOption("Mapper.ba_local_function_tolerance",
                                &mapper->ba_local_function_tolerance);
    AddAndRegisterDefaultOption("Mapper.ba_local_max_num_iterations",
                                &mapper->ba_local_max_num_iterations);
    AddAndRegisterDefaultOption("Mapper.ba_global_use_pba",
                                &mapper->ba_global_use_pba);
    AddAndRegisterDefaultOption("Mapper.ba_global_pba_gpu_index",
                                &mapper->ba_global_pba_gpu_index);
    AddAndRegisterDefaultOption("Mapper.ba_global_images_ratio",
                                &mapper->ba_global_images_ratio);
    AddAndRegisterDefaultOption("Mapper.ba_global_points_ratio",
                                &mapper->ba_global_points_ratio);
    AddAndRegisterDefaultOption("Mapper.ba_global_images_freq",
                                &mapper->ba_global_images_freq);
    AddAndRegisterDefaultOption("Mapper.ba_global_points_freq",
                                &mapper->ba_global_points_freq);
    AddAndRegisterDefaultOption("Mapper.ba_global_function_tolerance",
                                &mapper->ba_global_function_tolerance);
    AddAndRegisterDefaultOption("Mapper.ba_global_max_num_iterations",
                                &mapper->ba_global_max_num_iterations);
    AddAndRegisterDefaultOption("Mapper.ba_global_max_refinements",
                                &mapper->ba_global_max_refinements);
    AddAndRegisterDefaultOption("Mapper.ba_global_max_refinement_change",
                                &mapper->ba_global_max_refinement_change);
    AddAndRegisterDefaultOption("Mapper.ba_local_max_refinements",
                                &mapper->ba_local_max_refinements);
    AddAndRegisterDefaultOption("Mapper.ba_local_max_refinement_change",
                                &mapper->ba_local_max_refinement_change);
    AddAndRegisterDefaultOption("Mapper.snapshot_path", &mapper->snapshot_path);
    AddAndRegisterDefaultOption("Mapper.snapshot_images_freq",
                                &mapper->snapshot_images_freq);
    AddAndRegisterDefaultOption("Mapper.fix_existing_images",
                                &mapper->fix_existing_images);
  
    // IncrementalMapper.
    AddAndRegisterDefaultOption("Mapper.init_min_num_inliers",
                                &mapper->mapper.init_min_num_inliers);
    AddAndRegisterDefaultOption("Mapper.init_max_error",
                                &mapper->mapper.init_max_error);
    AddAndRegisterDefaultOption("Mapper.init_max_forward_motion",
                                &mapper->mapper.init_max_forward_motion);
    AddAndRegisterDefaultOption("Mapper.init_min_tri_angle",
                                &mapper->mapper.init_min_tri_angle);
    AddAndRegisterDefaultOption("Mapper.init_max_reg_trials",
                                &mapper->mapper.init_max_reg_trials);
    AddAndRegisterDefaultOption("Mapper.abs_pose_max_error",
                                &mapper->mapper.abs_pose_max_error);
    AddAndRegisterDefaultOption("Mapper.abs_pose_min_num_inliers",
                                &mapper->mapper.abs_pose_min_num_inliers);
    AddAndRegisterDefaultOption("Mapper.abs_pose_min_inlier_ratio",
                                &mapper->mapper.abs_pose_min_inlier_ratio);
    AddAndRegisterDefaultOption("Mapper.filter_max_reproj_error",
                                &mapper->mapper.filter_max_reproj_error);
    AddAndRegisterDefaultOption("Mapper.filter_min_tri_angle",
                                &mapper->mapper.filter_min_tri_angle);
    AddAndRegisterDefaultOption("Mapper.max_reg_trials",
                                &mapper->mapper.max_reg_trials);
    AddAndRegisterDefaultOption("Mapper.local_ba_min_tri_angle",
                                &mapper->mapper.local_ba_min_tri_angle);
  
    // IncrementalTriangulator.
    AddAndRegisterDefaultOption("Mapper.tri_max_transitivity",
                                &mapper->triangulation.max_transitivity);
    AddAndRegisterDefaultOption("Mapper.tri_create_max_angle_error",
                                &mapper->triangulation.create_max_angle_error);
    AddAndRegisterDefaultOption("Mapper.tri_continue_max_angle_error",
                                &mapper->triangulation.continue_max_angle_error);
    AddAndRegisterDefaultOption("Mapper.tri_merge_max_reproj_error",
                                &mapper->triangulation.merge_max_reproj_error);
    AddAndRegisterDefaultOption("Mapper.tri_complete_max_reproj_error",
                                &mapper->triangulation.complete_max_reproj_error);
    AddAndRegisterDefaultOption("Mapper.tri_complete_max_transitivity",
                                &mapper->triangulation.complete_max_transitivity);
    AddAndRegisterDefaultOption("Mapper.tri_re_max_angle_error",
                                &mapper->triangulation.re_max_angle_error);
    AddAndRegisterDefaultOption("Mapper.tri_re_min_ratio",
                                &mapper->triangulation.re_min_ratio);
    AddAndRegisterDefaultOption("Mapper.tri_re_max_trials",
                                &mapper->triangulation.re_max_trials);
    AddAndRegisterDefaultOption("Mapper.tri_min_angle",
                                &mapper->triangulation.min_angle);
    AddAndRegisterDefaultOption("Mapper.tri_ignore_two_view_tracks",
                                &mapper->triangulation.ignore_two_view_tracks);
  }
  
  void OptionManager::AddPatchMatchStereoOptions() {
    if (added_patch_match_stereo_options_) {
      return;
    }
    added_patch_match_stereo_options_ = true;
  
    AddAndRegisterDefaultOption("PatchMatchStereo.max_image_size",
                                &patch_match_stereo->max_image_size);
    AddAndRegisterDefaultOption("PatchMatchStereo.gpu_index",
                                &patch_match_stereo->gpu_index);
    AddAndRegisterDefaultOption("PatchMatchStereo.depth_min",
                                &patch_match_stereo->depth_min);
    AddAndRegisterDefaultOption("PatchMatchStereo.depth_max",
                                &patch_match_stereo->depth_max);
    AddAndRegisterDefaultOption("PatchMatchStereo.window_radius",
                                &patch_match_stereo->window_radius);
    AddAndRegisterDefaultOption("PatchMatchStereo.window_step",
                                &patch_match_stereo->window_step);
    AddAndRegisterDefaultOption("PatchMatchStereo.sigma_spatial",
                                &patch_match_stereo->sigma_spatial);
    AddAndRegisterDefaultOption("PatchMatchStereo.sigma_color",
                                &patch_match_stereo->sigma_color);
    AddAndRegisterDefaultOption("PatchMatchStereo.num_samples",
                                &patch_match_stereo->num_samples);
    AddAndRegisterDefaultOption("PatchMatchStereo.ncc_sigma",
                                &patch_match_stereo->ncc_sigma);
    AddAndRegisterDefaultOption("PatchMatchStereo.min_triangulation_angle",
                                &patch_match_stereo->min_triangulation_angle);
    AddAndRegisterDefaultOption("PatchMatchStereo.incident_angle_sigma",
                                &patch_match_stereo->incident_angle_sigma);
    AddAndRegisterDefaultOption("PatchMatchStereo.num_iterations",
                                &patch_match_stereo->num_iterations);
    AddAndRegisterDefaultOption("PatchMatchStereo.geom_consistency",
                                &patch_match_stereo->geom_consistency);
    AddAndRegisterDefaultOption(
        "PatchMatchStereo.geom_consistency_regularizer",
        &patch_match_stereo->geom_consistency_regularizer);
    AddAndRegisterDefaultOption("PatchMatchStereo.geom_consistency_max_cost",
                                &patch_match_stereo->geom_consistency_max_cost);
    AddAndRegisterDefaultOption("PatchMatchStereo.filter",
                                &patch_match_stereo->filter);
    AddAndRegisterDefaultOption("PatchMatchStereo.filter_min_ncc",
                                &patch_match_stereo->filter_min_ncc);
    AddAndRegisterDefaultOption(
        "PatchMatchStereo.filter_min_triangulation_angle",
        &patch_match_stereo->filter_min_triangulation_angle);
    AddAndRegisterDefaultOption("PatchMatchStereo.filter_min_num_consistent",
                                &patch_match_stereo->filter_min_num_consistent);
    AddAndRegisterDefaultOption(
        "PatchMatchStereo.filter_geom_consistency_max_cost",
        &patch_match_stereo->filter_geom_consistency_max_cost);
    AddAndRegisterDefaultOption("PatchMatchStereo.cache_size",
                                &patch_match_stereo->cache_size);
    AddAndRegisterDefaultOption("PatchMatchStereo.allow_missing_files",
                                &patch_match_stereo->allow_missing_files);
    AddAndRegisterDefaultOption("PatchMatchStereo.write_consistency_graph",
                                &patch_match_stereo->write_consistency_graph);
  }
  
  void OptionManager::AddStereoFusionOptions() {
    if (added_stereo_fusion_options_) {
      return;
    }
    added_stereo_fusion_options_ = true;
  
    AddAndRegisterDefaultOption("StereoFusion.mask_path",
                                &stereo_fusion->mask_path);
    AddAndRegisterDefaultOption("StereoFusion.num_threads",
                                &stereo_fusion->num_threads);
    AddAndRegisterDefaultOption("StereoFusion.max_image_size",
                                &stereo_fusion->max_image_size);
    AddAndRegisterDefaultOption("StereoFusion.min_num_pixels",
                                &stereo_fusion->min_num_pixels);
    AddAndRegisterDefaultOption("StereoFusion.max_num_pixels",
                                &stereo_fusion->max_num_pixels);
    AddAndRegisterDefaultOption("StereoFusion.max_traversal_depth",
                                &stereo_fusion->max_traversal_depth);
    AddAndRegisterDefaultOption("StereoFusion.max_reproj_error",
                                &stereo_fusion->max_reproj_error);
    AddAndRegisterDefaultOption("StereoFusion.max_depth_error",
                                &stereo_fusion->max_depth_error);
    AddAndRegisterDefaultOption("StereoFusion.max_normal_error",
                                &stereo_fusion->max_normal_error);
    AddAndRegisterDefaultOption("StereoFusion.check_num_images",
                                &stereo_fusion->check_num_images);
    AddAndRegisterDefaultOption("StereoFusion.cache_size",
                                &stereo_fusion->cache_size);
    AddAndRegisterDefaultOption("StereoFusion.use_cache",
                                &stereo_fusion->use_cache);
  }
  
  void OptionManager::AddPoissonMeshingOptions() {
    if (added_poisson_meshing_options_) {
      return;
    }
    added_poisson_meshing_options_ = true;
  
    AddAndRegisterDefaultOption("PoissonMeshing.point_weight",
                                &poisson_meshing->point_weight);
    AddAndRegisterDefaultOption("PoissonMeshing.depth", &poisson_meshing->depth);
    AddAndRegisterDefaultOption("PoissonMeshing.color", &poisson_meshing->color);
    AddAndRegisterDefaultOption("PoissonMeshing.trim", &poisson_meshing->trim);
    AddAndRegisterDefaultOption("PoissonMeshing.num_threads",
                                &poisson_meshing->num_threads);
  }
  
  void OptionManager::AddDelaunayMeshingOptions() {
    if (added_delaunay_meshing_options_) {
      return;
    }
    added_delaunay_meshing_options_ = true;
  
    AddAndRegisterDefaultOption("DelaunayMeshing.max_proj_dist",
                                &delaunay_meshing->max_proj_dist);
    AddAndRegisterDefaultOption("DelaunayMeshing.max_depth_dist",
                                &delaunay_meshing->max_depth_dist);
    AddAndRegisterDefaultOption("DelaunayMeshing.visibility_sigma",
                                &delaunay_meshing->visibility_sigma);
    AddAndRegisterDefaultOption("DelaunayMeshing.distance_sigma_factor",
                                &delaunay_meshing->distance_sigma_factor);
    AddAndRegisterDefaultOption("DelaunayMeshing.quality_regularization",
                                &delaunay_meshing->quality_regularization);
    AddAndRegisterDefaultOption("DelaunayMeshing.max_side_length_factor",
                                &delaunay_meshing->max_side_length_factor);
    AddAndRegisterDefaultOption("DelaunayMeshing.max_side_length_percentile",
                                &delaunay_meshing->max_side_length_percentile);
    AddAndRegisterDefaultOption("DelaunayMeshing.num_threads",
                                &delaunay_meshing->num_threads);
  }
  
  void OptionManager::AddRenderOptions() {
    if (added_render_options_) {
      return;
    }
    added_render_options_ = true;
  
    AddAndRegisterDefaultOption("Render.min_track_len", &render->min_track_len);
    AddAndRegisterDefaultOption("Render.max_error", &render->max_error);
    AddAndRegisterDefaultOption("Render.refresh_rate", &render->refresh_rate);
    AddAndRegisterDefaultOption("Render.adapt_refresh_rate",
                                &render->adapt_refresh_rate);
    AddAndRegisterDefaultOption("Render.image_connections",
                                &render->image_connections);
    AddAndRegisterDefaultOption("Render.projection_type",
                                &render->projection_type);
  }
  
  void OptionManager::Reset() {
    FLAGS_logtostderr = false;
    FLAGS_v = 2;
  
    const bool kResetPaths = true;
    ResetOptions(kResetPaths);
  
    desc_.reset(new boost::program_options::options_description());
  
    options_bool_.clear();
    options_int_.clear();
    options_double_.clear();
    options_string_.clear();
  
    added_log_options_ = false;
    added_random_options_ = false;
    added_database_options_ = false;
    added_image_options_ = false;
    added_extraction_options_ = false;
    added_match_options_ = false;
    added_exhaustive_match_options_ = false;
    added_sequential_match_options_ = false;
    added_vocab_tree_match_options_ = false;
    added_spatial_match_options_ = false;
    added_transitive_match_options_ = false;
    added_image_pairs_match_options_ = false;
    added_ba_options_ = false;
    added_mapper_options_ = false;
    added_patch_match_stereo_options_ = false;
    added_stereo_fusion_options_ = false;
    added_poisson_meshing_options_ = false;
    added_delaunay_meshing_options_ = false;
    added_render_options_ = false;
  }
  
  void OptionManager::ResetOptions(const bool reset_paths) {
    if (reset_paths) {
      *project_path = "";
      *database_path = "";
      *image_path = "";
    }
    *image_reader = ImageReaderOptions();
    *sift_extraction = SiftExtractionOptions();
    *sift_matching = SiftMatchingOptions();
    *exhaustive_matching = ExhaustiveMatchingOptions();
    *sequential_matching = SequentialMatchingOptions();
    *vocab_tree_matching = VocabTreeMatchingOptions();
    *spatial_matching = SpatialMatchingOptions();
    *transitive_matching = TransitiveMatchingOptions();
    *image_pairs_matching = ImagePairsMatchingOptions();
    *bundle_adjustment = BundleAdjustmentOptions();
    *mapper = IncrementalMapperOptions();
    *patch_match_stereo = mvs::PatchMatchOptions();
    *stereo_fusion = mvs::StereoFusionOptions();
    *poisson_meshing = mvs::PoissonMeshingOptions();
    *delaunay_meshing = mvs::DelaunayMeshingOptions();
    *render = RenderOptions();
  }
  
  bool OptionManager::Check() {
    bool success = true;
  
    if (added_database_options_) {
      const auto database_parent_path = GetParentDir(*database_path);
      success = success && CHECK_OPTION_IMPL(!ExistsDir(*database_path)) &&
                CHECK_OPTION_IMPL(database_parent_path == "" ||
                                  ExistsDir(database_parent_path));
    }
  
    if (added_image_options_)
      success = success && CHECK_OPTION_IMPL(ExistsDir(*image_path));
  
    if (image_reader) success = success && image_reader->Check();
    if (sift_extraction) success = success && sift_extraction->Check();
  
    if (sift_matching) success = success && sift_matching->Check();
    if (exhaustive_matching) success = success && exhaustive_matching->Check();
    if (sequential_matching) success = success && sequential_matching->Check();
    if (vocab_tree_matching) success = success && vocab_tree_matching->Check();
    if (spatial_matching) success = success && spatial_matching->Check();
    if (transitive_matching) success = success && transitive_matching->Check();
    if (image_pairs_matching) success = success && image_pairs_matching->Check();
  
    if (bundle_adjustment) success = success && bundle_adjustment->Check();
    if (mapper) success = success && mapper->Check();
  
    if (patch_match_stereo) success = success && patch_match_stereo->Check();
    if (stereo_fusion) success = success && stereo_fusion->Check();
    if (poisson_meshing) success = success && poisson_meshing->Check();
    if (delaunay_meshing) success = success && delaunay_meshing->Check();
  
  #ifdef GUI_ENABLED
    if (render) success = success && render->Check();
  #endif
  
    return success;
  }
  
  void OptionManager::Parse(const int argc, char** argv) {
    config::variables_map vmap;
  
    try {
      config::store(config::parse_command_line(argc, argv, *desc_), vmap);
  
      if (vmap.count("help")) {
        std::cout << StringPrintf("%s (%s)", GetVersionInfo().c_str(),
                                  GetBuildInfo().c_str())
                  << std::endl
                  << std::endl;
        std::cout
            << "Options can either be specified via command-line or by defining"
            << std::endl
            << "them in a .ini project file passed to `--project_path`."
            << std::endl
            << std::endl;
        std::cout << *desc_ << std::endl;
        exit(EXIT_SUCCESS);
      }
  
      if (vmap.count("project_path")) {
        *project_path = vmap["project_path"].as<std::string>();
        if (!Read(*project_path)) {
          exit(EXIT_FAILURE);
        }
      } else {
        vmap.notify();
      }
    } catch (std::exception& exc) {
      std::cerr << "ERROR: Failed to parse options - " << exc.what() << "."
                << std::endl;
      exit(EXIT_FAILURE);
    } catch (...) {
      std::cerr << "ERROR: Failed to parse options for unknown reason."
                << std::endl;
      exit(EXIT_FAILURE);
    }
  
    if (!Check()) {
      std::cerr << "ERROR: Invalid options provided." << std::endl;
      exit(EXIT_FAILURE);
    }
  }
  
  bool OptionManager::Read(const std::string& path) {
    config::variables_map vmap;
  
    if (!ExistsFile(path)) {
      std::cout << "ERROR: Configuration file does not exist." << std::endl;
      return false;
    }
  
    try {
      std::ifstream file(path);
      CHECK(file.is_open()) << path;
      config::store(config::parse_config_file(file, *desc_), vmap);
      vmap.notify();
    } catch (std::exception& e) {
      std::cout << "ERROR: Failed to parse options " << e.what() << "."
                << std::endl;
      return false;
    } catch (...) {
      std::cout << "ERROR: Failed to parse options for unknown reason."
                << std::endl;
      return false;
    }
  
    return Check();
  }
  
  bool OptionManager::ReRead(const std::string& path) {
    Reset();
    AddAllOptions();
    return Read(path);
  }
  
  void OptionManager::Write(const std::string& path) const {
    boost::property_tree::ptree pt;
  
    // First, put all options without a section and then those with a section.
    // This is necessary as otherwise older Boost versions will write the
    // options without a section in between other sections and therefore
    // the errors will be assigned to the wrong section if read later.
  
    for (const auto& option : options_bool_) {
      if (!StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_int_) {
      if (!StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_double_) {
      if (!StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_string_) {
      if (!StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_bool_) {
      if (StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_int_) {
      if (StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_double_) {
      if (StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    for (const auto& option : options_string_) {
      if (StringContains(option.first, ".")) {
        pt.put(option.first, *option.second);
      }
    }
  
    boost::property_tree::write_ini(path, pt);
  }
  
  }  // namespace colmap
  