ceres-solver-v1 / colmap /src /controllers /automatic_reconstruction.cc
camenduru's picture
ceres-solver and colmap
7b7496d
// 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)
#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;
}
// Image undistortion.
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;
}
// Patch match stereo.
#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 // CUDA_ENABLED
std::cout
<< std::endl
<< "WARNING: Skipping patch match stereo because CUDA is not available."
<< std::endl;
return;
#endif // CUDA_ENABLED
if (IsStopped()) {
return;
}
// Stereo fusion.
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;
}
// Surface meshing.
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 // CGAL_ENABLED
std::cout << std::endl
<< "WARNING: Skipping Delaunay meshing because CGAL is "
"not available."
<< std::endl;
return;
#endif // CGAL_ENABLED
}
}
}
}
} // namespace colmap