| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "controllers/automatic_reconstruction.h" |
| |
|
| | #include "base/undistortion.h" |
| | #include "controllers/incremental_mapper.h" |
| | #include "feature/extraction.h" |
| | #include "feature/matching.h" |
| | #include "mvs/fusion.h" |
| | #include "mvs/meshing.h" |
| | #include "mvs/patch_match.h" |
| | #include "util/misc.h" |
| | #include "util/option_manager.h" |
| |
|
| | namespace colmap { |
| |
|
| | AutomaticReconstructionController::AutomaticReconstructionController( |
| | const Options& options, ReconstructionManager* reconstruction_manager) |
| | : options_(options), |
| | reconstruction_manager_(reconstruction_manager), |
| | active_thread_(nullptr) { |
| | CHECK(ExistsDir(options_.workspace_path)); |
| | CHECK(ExistsDir(options_.image_path)); |
| | CHECK_NOTNULL(reconstruction_manager_); |
| |
|
| | option_manager_.AddAllOptions(); |
| |
|
| | *option_manager_.image_path = options_.image_path; |
| | *option_manager_.database_path = |
| | JoinPaths(options_.workspace_path, "database.db"); |
| |
|
| | if (options_.data_type == DataType::VIDEO) { |
| | option_manager_.ModifyForVideoData(); |
| | } else if (options_.data_type == DataType::INDIVIDUAL) { |
| | option_manager_.ModifyForIndividualData(); |
| | } else if (options_.data_type == DataType::INTERNET) { |
| | option_manager_.ModifyForInternetData(); |
| | } else { |
| | LOG(FATAL) << "Data type not supported"; |
| | } |
| |
|
| | CHECK(ExistsCameraModelWithName(options_.camera_model)); |
| |
|
| | if (options_.quality == Quality::LOW) { |
| | option_manager_.ModifyForLowQuality(); |
| | } else if (options_.quality == Quality::MEDIUM) { |
| | option_manager_.ModifyForMediumQuality(); |
| | } else if (options_.quality == Quality::HIGH) { |
| | option_manager_.ModifyForHighQuality(); |
| | } else if (options_.quality == Quality::EXTREME) { |
| | option_manager_.ModifyForExtremeQuality(); |
| | } |
| |
|
| | option_manager_.sift_extraction->num_threads = options_.num_threads; |
| | option_manager_.sift_matching->num_threads = options_.num_threads; |
| | option_manager_.mapper->num_threads = options_.num_threads; |
| | option_manager_.poisson_meshing->num_threads = options_.num_threads; |
| |
|
| | ImageReaderOptions& reader_options = *option_manager_.image_reader; |
| | reader_options.database_path = *option_manager_.database_path; |
| | reader_options.image_path = *option_manager_.image_path; |
| | if (!options_.mask_path.empty()) { |
| | reader_options.mask_path = options_.mask_path; |
| | option_manager_.image_reader->mask_path = options_.mask_path; |
| | option_manager_.stereo_fusion->mask_path = options_.mask_path; |
| | } |
| | reader_options.single_camera = options_.single_camera; |
| | reader_options.camera_model = options_.camera_model; |
| |
|
| | option_manager_.sift_extraction->use_gpu = options_.use_gpu; |
| | option_manager_.sift_matching->use_gpu = options_.use_gpu; |
| |
|
| | option_manager_.sift_extraction->gpu_index = options_.gpu_index; |
| | option_manager_.sift_matching->gpu_index = options_.gpu_index; |
| | option_manager_.patch_match_stereo->gpu_index = options_.gpu_index; |
| |
|
| | feature_extractor_ = std::make_unique<SiftFeatureExtractor>( |
| | reader_options, *option_manager_.sift_extraction); |
| |
|
| | exhaustive_matcher_ = std::make_unique<ExhaustiveFeatureMatcher>( |
| | *option_manager_.exhaustive_matching, *option_manager_.sift_matching, |
| | *option_manager_.database_path); |
| |
|
| | if (!options_.vocab_tree_path.empty()) { |
| | option_manager_.sequential_matching->loop_detection = true; |
| | option_manager_.sequential_matching->vocab_tree_path = |
| | options_.vocab_tree_path; |
| | } |
| |
|
| | sequential_matcher_ = std::make_unique<SequentialFeatureMatcher>( |
| | *option_manager_.sequential_matching, *option_manager_.sift_matching, |
| | *option_manager_.database_path); |
| |
|
| | if (!options_.vocab_tree_path.empty()) { |
| | option_manager_.vocab_tree_matching->vocab_tree_path = |
| | options_.vocab_tree_path; |
| | vocab_tree_matcher_ = std::make_unique<VocabTreeFeatureMatcher>( |
| | *option_manager_.vocab_tree_matching, *option_manager_.sift_matching, |
| | *option_manager_.database_path); |
| | } |
| | } |
| |
|
| | void AutomaticReconstructionController::Stop() { |
| | if (active_thread_ != nullptr) { |
| | active_thread_->Stop(); |
| | } |
| | Thread::Stop(); |
| | } |
| |
|
| | void AutomaticReconstructionController::Run() { |
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | RunFeatureExtraction(); |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | RunFeatureMatching(); |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | if (options_.sparse) { |
| | RunSparseMapper(); |
| | } |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | if (options_.dense) { |
| | RunDenseMapper(); |
| | } |
| | } |
| |
|
| | void AutomaticReconstructionController::RunFeatureExtraction() { |
| | CHECK(feature_extractor_); |
| | active_thread_ = feature_extractor_.get(); |
| | feature_extractor_->Start(); |
| | feature_extractor_->Wait(); |
| | feature_extractor_.reset(); |
| | active_thread_ = nullptr; |
| | } |
| |
|
| | void AutomaticReconstructionController::RunFeatureMatching() { |
| | Thread* matcher = nullptr; |
| | if (options_.data_type == DataType::VIDEO) { |
| | matcher = sequential_matcher_.get(); |
| | } else if (options_.data_type == DataType::INDIVIDUAL || |
| | options_.data_type == DataType::INTERNET) { |
| | Database database(*option_manager_.database_path); |
| | const size_t num_images = database.NumImages(); |
| | if (options_.vocab_tree_path.empty() || num_images < 200) { |
| | matcher = exhaustive_matcher_.get(); |
| | } else { |
| | matcher = vocab_tree_matcher_.get(); |
| | } |
| | } |
| |
|
| | CHECK(matcher); |
| | active_thread_ = matcher; |
| | matcher->Start(); |
| | matcher->Wait(); |
| | exhaustive_matcher_.reset(); |
| | sequential_matcher_.reset(); |
| | vocab_tree_matcher_.reset(); |
| | active_thread_ = nullptr; |
| | } |
| |
|
| | void AutomaticReconstructionController::RunSparseMapper() { |
| | const auto sparse_path = JoinPaths(options_.workspace_path, "sparse"); |
| | if (ExistsDir(sparse_path)) { |
| | auto dir_list = GetDirList(sparse_path); |
| | std::sort(dir_list.begin(), dir_list.end()); |
| | if (dir_list.size() > 0) { |
| | std::cout << std::endl |
| | << "WARNING: Skipping sparse reconstruction because it is " |
| | "already computed" |
| | << std::endl; |
| | for (const auto& dir : dir_list) { |
| | reconstruction_manager_->Read(dir); |
| | } |
| | return; |
| | } |
| | } |
| |
|
| | IncrementalMapperController mapper( |
| | option_manager_.mapper.get(), *option_manager_.image_path, |
| | *option_manager_.database_path, reconstruction_manager_); |
| | active_thread_ = &mapper; |
| | mapper.Start(); |
| | mapper.Wait(); |
| | active_thread_ = nullptr; |
| |
|
| | CreateDirIfNotExists(sparse_path); |
| | reconstruction_manager_->Write(sparse_path, &option_manager_); |
| | } |
| |
|
| | void AutomaticReconstructionController::RunDenseMapper() { |
| | CreateDirIfNotExists(JoinPaths(options_.workspace_path, "dense")); |
| |
|
| | for (size_t i = 0; i < reconstruction_manager_->Size(); ++i) { |
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | const std::string dense_path = |
| | JoinPaths(options_.workspace_path, "dense", std::to_string(i)); |
| | const std::string fused_path = JoinPaths(dense_path, "fused.ply"); |
| |
|
| | std::string meshing_path; |
| | if (options_.mesher == Mesher::POISSON) { |
| | meshing_path = JoinPaths(dense_path, "meshed-poisson.ply"); |
| | } else if (options_.mesher == Mesher::DELAUNAY) { |
| | meshing_path = JoinPaths(dense_path, "meshed-delaunay.ply"); |
| | } |
| |
|
| | if (ExistsFile(fused_path) && ExistsFile(meshing_path)) { |
| | continue; |
| | } |
| |
|
| | |
| |
|
| | if (!ExistsDir(dense_path)) { |
| | CreateDirIfNotExists(dense_path); |
| |
|
| | UndistortCameraOptions undistortion_options; |
| | undistortion_options.max_image_size = |
| | option_manager_.patch_match_stereo->max_image_size; |
| | COLMAPUndistorter undistorter(undistortion_options, |
| | reconstruction_manager_->Get(i), |
| | *option_manager_.image_path, dense_path); |
| | active_thread_ = &undistorter; |
| | undistorter.Start(); |
| | undistorter.Wait(); |
| | active_thread_ = nullptr; |
| | } |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | |
| |
|
| | #ifdef CUDA_ENABLED |
| | { |
| | mvs::PatchMatchController patch_match_controller( |
| | *option_manager_.patch_match_stereo, dense_path, "COLMAP", ""); |
| | active_thread_ = &patch_match_controller; |
| | patch_match_controller.Start(); |
| | patch_match_controller.Wait(); |
| | active_thread_ = nullptr; |
| | } |
| | #else |
| | std::cout |
| | << std::endl |
| | << "WARNING: Skipping patch match stereo because CUDA is not available." |
| | << std::endl; |
| | return; |
| | #endif |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | |
| |
|
| | if (!ExistsFile(fused_path)) { |
| | auto fusion_options = *option_manager_.stereo_fusion; |
| | const int num_reg_images = reconstruction_manager_->Get(i).NumRegImages(); |
| | fusion_options.min_num_pixels = |
| | std::min(num_reg_images + 1, fusion_options.min_num_pixels); |
| | mvs::StereoFusion fuser( |
| | fusion_options, dense_path, "COLMAP", "", |
| | options_.quality == Quality::HIGH ? "geometric" : "photometric"); |
| | active_thread_ = &fuser; |
| | fuser.Start(); |
| | fuser.Wait(); |
| | active_thread_ = nullptr; |
| |
|
| | std::cout << "Writing output: " << fused_path << std::endl; |
| | WriteBinaryPlyPoints(fused_path, fuser.GetFusedPoints()); |
| | mvs::WritePointsVisibility(fused_path + ".vis", |
| | fuser.GetFusedPointsVisibility()); |
| | } |
| |
|
| | if (IsStopped()) { |
| | return; |
| | } |
| |
|
| | |
| |
|
| | if (!ExistsFile(meshing_path)) { |
| | if (options_.mesher == Mesher::POISSON) { |
| | mvs::PoissonMeshing(*option_manager_.poisson_meshing, fused_path, |
| | meshing_path); |
| | } else if (options_.mesher == Mesher::DELAUNAY) { |
| | #ifdef CGAL_ENABLED |
| | mvs::DenseDelaunayMeshing(*option_manager_.delaunay_meshing, dense_path, |
| | meshing_path); |
| | #else |
| | std::cout << std::endl |
| | << "WARNING: Skipping Delaunay meshing because CGAL is " |
| | "not available." |
| | << std::endl; |
| | return; |
| |
|
| | #endif |
| | } |
| | } |
| | } |
| | } |
| |
|
| | } |
| |
|