Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to glog logging #176

Merged
merged 7 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "colmap/util/threading.h"

#include <glog/logging.h>
#include <pybind11/embed.h>
#include <pybind11/eval.h>
#include <pybind11/numpy.h>
Expand All @@ -26,7 +27,7 @@ inline T pyStringToEnum(const py::enum_<T>& enm, const std::string& value) {
return T(values[strVal].template cast<T>());
}
std::string msg =
"ERROR: Invalid string value " + value + " for enum " +
"Invalid string value " + value + " for enum " +
std::string(enm.attr("__name__").template cast<std::string>());
THROW_EXCEPTION(std::out_of_range, msg.c_str());
T t;
Expand Down Expand Up @@ -91,7 +92,7 @@ inline void make_dataclass(py::class_<T, options...> cls) {
<< "'.";
// We write the err message to give info even if exceptions
// is catched outside, e.g. in function overload resolve
std::cerr << "Internal TypeError: " << ss.str() << std::endl;
LOG(ERROR) << "Internal TypeError: " << ss.str();
throw(
py::type_error(std::string("Failed to merge dict into class: ") +
"Could not assign " +
Expand All @@ -105,12 +106,12 @@ inline void make_dataclass(py::class_<T, options...> cls) {
<< " defined readonly.";
throw py::attribute_error(ss.str());
} else if (ex.matches(PyExc_AttributeError)) {
std::cerr << "Internal AttributeError: "
<< py::str(ex.value()).cast<std::string>() << std::endl;
LOG(ERROR) << "Internal AttributeError: "
<< py::str(ex.value()).cast<std::string>();
throw;
} else {
std::cerr << "Internal Error: "
<< py::str(ex.value()).cast<std::string>() << std::endl;
LOG(ERROR) << "Internal Error: "
<< py::str(ex.value()).cast<std::string>();
throw;
}
}
Expand Down Expand Up @@ -245,7 +246,7 @@ void PyWait(Thread* thread, double gap = 2.0) {
PyInterrupt py_interrupt(gap);
while (thread->IsRunning()) {
if (py_interrupt.Raised()) {
std::cerr << "Stopping thread..." << std::endl;
LOG(ERROR) << "Stopping thread...";
thread->Stop();
thread->Wait();
throw py::error_already_set();
Expand Down
31 changes: 31 additions & 0 deletions main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,20 @@ using namespace pybind11::literals;
#include "reconstruction/reconstruction.cc"
#include "sift.cc"
#include "utils.h"
#include <glog/logging.h>

void init_reconstruction(py::module&);
void init_quaternion(py::module&);

struct Logging {
enum class Level {
INFO = google::GLOG_INFO,
WARNING = google::GLOG_WARNING,
ERROR = google::GLOG_ERROR,
FATAL = google::GLOG_FATAL,
};
}; // dummy class

PYBIND11_MODULE(pycolmap, m) {
m.doc() = "COLMAP plugin";
#ifdef VERSION_INFO
Expand All @@ -39,6 +49,27 @@ PYBIND11_MODULE(pycolmap, m) {
m.attr("__version__") = py::str("dev");
#endif

auto PyLogging =
py::class_<Logging>(m, "logging")
.def_readwrite_static("minloglevel", &FLAGS_minloglevel)
.def_readwrite_static("stderrthreshold", &FLAGS_stderrthreshold)
.def_readwrite_static("log_dir", &FLAGS_log_dir)
.def_readwrite_static("logtostderr", &FLAGS_logtostderr)
.def_readwrite_static("alsologtostderr", &FLAGS_alsologtostderr)
.def_static("info", [](std::string msg) { LOG(INFO) << msg; })
.def_static("warning", [](std::string msg) { LOG(WARNING) << msg; })
.def_static("error", [](std::string msg) { LOG(ERROR) << msg; })
.def_static("fatal", [](std::string msg) { LOG(FATAL) << msg; });
py::enum_<Logging::Level>(PyLogging, "Level")
.value("INFO", Logging::Level::INFO)
.value("WARNING", Logging::Level::WARNING)
.value("ERROR", Logging::Level::ERROR)
.value("FATAL", Logging::Level::FATAL)
.export_values();
google::InitGoogleLogging("");
google::InstallFailureSignalHandler();
FLAGS_logtostderr = true;

auto PyDevice = py::enum_<Device>(m, "Device")
.value("auto", Device::AUTO)
.value("cpu", Device::CPU)
Expand Down
15 changes: 1 addition & 14 deletions pipeline/extract_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ void extract_features(const py::object database_path_,
const std::string camera_model,
ImageReaderOptions reader_options,
SiftExtractionOptions sift_options,
const Device device,
bool verbose) {
const Device device) {
std::string database_path = py::str(database_path_).cast<std::string>();
THROW_CHECK_MSG(!ExistsFile(database_path),
database_path + " already exists.");
Expand Down Expand Up @@ -61,22 +60,11 @@ void extract_features(const py::object database_path_,
std::invalid_argument,
"Invalid camera parameters.");

std::stringstream oss;
std::streambuf* oldcerr = nullptr;
std::streambuf* oldcout = nullptr;
if (!verbose) {
oldcout = std::cout.rdbuf(oss.rdbuf());
}
py::gil_scoped_release release;
std::unique_ptr<Thread> extractor =
CreateFeatureExtractorController(reader_options, sift_options);

extractor->Start();
PyWait(extractor.get());

if (!verbose) {
std::cout.rdbuf(oldcout);
}
}

void init_extract_features(py::module& m) {
Expand Down Expand Up @@ -170,6 +158,5 @@ void init_extract_features(py::module& m) {
"reader_options"_a = ImageReaderOptions(),
"sift_options"_a = sift_extraction_options,
"device"_a = Device::AUTO,
"verbose"_a = true,
"Extract SIFT Features and write them to database");
}
32 changes: 7 additions & 25 deletions pipeline/images.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ using namespace pybind11::literals;

#include "helpers.h"
#include "log_exceptions.h"
#include <glog/logging.h>

void import_images(const py::object database_path_,
const py::object image_path_,
Expand Down Expand Up @@ -120,37 +121,31 @@ void undistort_images(py::object output_path_,
std::string output_type,
CopyType copy_type,
int num_patch_match_src_images,
UndistortCameraOptions undistort_camera_options,
bool verbose) {
UndistortCameraOptions undistort_camera_options) {
std::string output_path = py::str(output_path_).cast<std::string>();
std::string input_path = py::str(input_path_).cast<std::string>();
THROW_CHECK_DIR_EXISTS(input_path);
std::string image_path = py::str(image_path_).cast<std::string>();
THROW_CHECK_DIR_EXISTS(image_path);

CreateDirIfNotExists(output_path);
if (verbose) {
PrintHeading1("Reading reconstruction");
}
Reconstruction reconstruction;
reconstruction.Read(input_path);
if (verbose) {
std::cout << StringPrintf(" => Reconstruction with %d images and %d points",
reconstruction.NumImages(),
reconstruction.NumPoints3D())
<< std::endl;
}
LOG(INFO) << StringPrintf(" => Reconstruction with %d images and %d points",
reconstruction.NumImages(),
reconstruction.NumPoints3D());

std::vector<image_t> image_ids;
for (const auto& image_name : image_list) {
const Image* image = reconstruction.FindImageWithName(image_name);
if (image != nullptr) {
image_ids.push_back(image->ImageId());
} else {
std::cout << "WARN: Cannot find image " << image_name << std::endl;
LOG(WARNING) << "Cannot find image " << image_name;
}
}

py::gil_scoped_release release;
std::unique_ptr<Thread> undistorter;
if (output_type == "COLMAP") {
undistorter.reset(new COLMAPUndistorter(undistort_camera_options,
Expand All @@ -172,20 +167,8 @@ void undistort_images(py::object output_path_,
std::string("Invalid `output_type` - supported values are ") +
"{'COLMAP', 'PMVS', 'CMP-MVS'}.");
}

std::stringstream oss;
std::streambuf* oldcout = nullptr;
if (!verbose) {
oldcout = std::cout.rdbuf(oss.rdbuf());
}

py::gil_scoped_release release;
undistorter->Start();
PyWait(undistorter.get());

if (!verbose) {
std::cout.rdbuf(oldcout);
}
}

void init_images(py::module& m) {
Expand Down Expand Up @@ -317,6 +300,5 @@ void init_images(py::module& m) {
"copy_policy"_a = CopyType::COPY,
"num_patch_match_src_images"_a = 20,
"undistort_options"_a = undistort_options,
"verbose"_a = false,
"Undistort images");
}
19 changes: 1 addition & 18 deletions pipeline/match_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ void match_features(py::object database_path_,
SiftMatchingOptions sift_options,
const Opts& matching_options,
const TwoViewGeometryOptions& verification_options,
const Device device,
bool verbose) {
const Device device) {
const std::string database_path = py::str(database_path_).cast<std::string>();
THROW_CHECK_FILE_EXISTS(database_path);
try {
Expand All @@ -51,20 +50,8 @@ void match_features(py::object database_path_,
py::gil_scoped_release release;
std::unique_ptr<Thread> matcher = MatcherFactory(
matching_options, sift_options, verification_options, database_path);

std::stringstream oss;
std::streambuf* oldcerr = nullptr;
std::streambuf* oldcout = nullptr;
if (!verbose) {
oldcout = std::cout.rdbuf(oss.rdbuf());
}

matcher->Start();
PyWait(matcher.get());

if (!verbose) {
std::cout.rdbuf(oldcout);
}
}

void verify_matches(const py::object database_path_,
Expand Down Expand Up @@ -245,7 +232,6 @@ void init_match_features(py::module& m) {
"matching_options"_a = exhaustive_options,
"verification_options"_a = verification_options,
"device"_a = Device::AUTO,
"verbose"_a = true,
"Exhaustive feature matching");

m.def("match_sequential",
Expand All @@ -255,7 +241,6 @@ void init_match_features(py::module& m) {
"matching_options"_a = sequential_options,
"verification_options"_a = verification_options,
"device"_a = Device::AUTO,
"verbose"_a = true,
"Sequential feature matching");

m.def("match_spatial",
Expand All @@ -265,7 +250,6 @@ void init_match_features(py::module& m) {
"matching_options"_a = spatial_options,
"verification_options"_a = verification_options,
"device"_a = Device::AUTO,
"verbose"_a = true,
"Spatial feature matching");

m.def("match_vocabtree",
Expand All @@ -275,7 +259,6 @@ void init_match_features(py::module& m) {
"matching_options"_a = vocabtree_options,
"verification_options"_a = verification_options,
"device"_a = Device::AUTO,
"verbose"_a = true,
"Vocab tree feature matching");

m.def("verify_matches",
Expand Down
41 changes: 7 additions & 34 deletions pipeline/mvs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ void patch_match_stereo(py::object workspace_path_,
std::string workspace_format,
std::string pmvs_option_name,
mvs::PatchMatchOptions options,
std::string config_path,
bool verbose) {
std::string config_path) {
#ifndef COLMAP_CUDA_ENABLED
THROW_EXCEPTION(
std::runtime_error,
"ERROR: Dense stereo reconstruction requires CUDA, which is not "
"available on your system.");
THROW_EXCEPTION(std::runtime_error,
"Dense stereo reconstruction requires CUDA, which is not "
"available on the system.");
return;
#endif // COLMAP_CUDA_ENABLED
std::string workspace_path = py::str(workspace_path_).cast<std::string>();
Expand All @@ -48,31 +46,19 @@ void patch_match_stereo(py::object workspace_path_,
"Invalid `workspace_format` - supported values are "
"'COLMAP' or 'PMVS'.")

std::stringstream oss;
std::streambuf* oldcout = nullptr;
if (!verbose) {
oldcout = std::cout.rdbuf(oss.rdbuf());
}

py::gil_scoped_release release;
mvs::PatchMatchController controller(
options, workspace_path, workspace_format, pmvs_option_name, config_path);

controller.Start();
PyWait(&controller);
// controller.Wait();

if (!verbose) {
std::cout.rdbuf(oldcout);
}
}

Reconstruction stereo_fusion(py::object output_path_,
py::object workspace_path_,
std::string workspace_format,
std::string pmvs_option_name,
std::string input_type,
mvs::StereoFusionOptions options,
bool verbose) {
mvs::StereoFusionOptions options) {
std::string workspace_path = py::str(workspace_path_).cast<std::string>();
THROW_CHECK_DIR_EXISTS(workspace_path);

Expand All @@ -92,24 +78,13 @@ Reconstruction stereo_fusion(py::object output_path_,
"Invalid input type - supported values are 'photometric' and "
"'geometric'.");

py::gil_scoped_release release;
mvs::StereoFusion fuser(
options, workspace_path, workspace_format, pmvs_option_name, input_type);

std::stringstream oss;
std::streambuf* oldcout = nullptr;
if (!verbose) {
oldcout = std::cout.rdbuf(oss.rdbuf());
}
py::gil_scoped_release release;
fuser.Start();
PyWait(&fuser);

if (!verbose) {
std::cout.rdbuf(oldcout);
}

Reconstruction reconstruction;

// read data from sparse reconstruction
if (workspace_format == "colmap") {
reconstruction.Read(JoinPaths(workspace_path, "sparse"));
Expand Down Expand Up @@ -282,7 +257,6 @@ void init_mvs(py::module& m) {
"pmvs_option_name"_a = "option-all",
"options"_a = patch_match_options,
"config_path"_a = "",
"verbose"_a = true,
"Runs Patch-Match-Stereo (requires CUDA)");

m.def("stereo_fusion",
Expand All @@ -293,6 +267,5 @@ void init_mvs(py::module& m) {
"pmvs_option_name"_a = "option-all",
"input_type"_a = "geometric",
"options"_a = stereo_fusion_options,
"verbose"_a = true,
"Stereo Fusion");
}