Skip to content

Commit

Permalink
Make glog usable in Python (#237)
Browse files Browse the repository at this point in the history
* Separate glog bindings
* API closer to glog
* More compact
* Log to file by default and control path
* Log Python call frame
* Add example
  • Loading branch information
sarlinpe authored Jan 15, 2024
1 parent b149280 commit d51b455
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
*.egg-info/
build/
.cache/
example/
43 changes: 43 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import shutil
import urllib.request
import zipfile
from pathlib import Path

import pycolmap
from pycolmap import logging


def run():
output_path = Path("example/")
image_path = output_path / "Fountain/images"
database_path = output_path / "database.db"
sfm_path = output_path / "sfm"

output_path.mkdir(exist_ok=True)
logging.set_log_destination(logging.INFO, output_path / "INFO.log.") # + time

data_url = "https://cvg-data.inf.ethz.ch/local-feature-evaluation-schoenberger2017/Strecha-Fountain.zip"
if not image_path.exists():
logging.info("Downloading the data.")
zip_path = output_path / "data.zip"
urllib.request.urlretrieve(data_url, zip_path)
with zipfile.ZipFile(zip_path, "r") as fid:
fid.extractall(output_path)
logging.info(f"Data extracted to {output_path}.")

if database_path.exists():
database_path.unlink()
pycolmap.extract_features(database_path, image_path)

pycolmap.match_exhaustive(database_path)

if sfm_path.exists():
shutil.rmtree(sfm_path)
sfm_path.mkdir(exist_ok=True)
recs = pycolmap.incremental_mapping(database_path, image_path, sfm_path)
for idx, rec in recs.items():
logging.info(f"#{idx} {rec.summary()}")


if __name__ == "__main__":
run()
96 changes: 67 additions & 29 deletions pycolmap/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,75 @@ namespace py = pybind11;
using namespace colmap;

struct Logging {
enum class Level {
INFO_ = google::GLOG_INFO,
WARNING_ = google::GLOG_WARNING,
ERROR_ = google::GLOG_ERROR,
FATAL_ = google::GLOG_FATAL,
// TODO: Replace with google::LogSeverity in glog >= v0.7.0
enum class LogSeverity {
GLOG_INFO = google::GLOG_INFO,
GLOG_WARNING = google::GLOG_WARNING,
GLOG_ERROR = google::GLOG_ERROR,
GLOG_FATAL = google::GLOG_FATAL,
};
}; // dummy class

std::pair<std::string, int> GetPythonCallFrame() {
const auto frame = py::module_::import("sys").attr("_getframe")(0);
const std::string file = py::str(frame.attr("f_code").attr("co_filename"));
const std::string function = py::str(frame.attr("f_code").attr("co_name"));
const int line = py::int_(frame.attr("f_lineno"));
return std::make_pair(file + ":" + function, line);
}

void BindLogging(py::module& m) {
py::class_<Logging> PyLogging(m, "logging");
PyLogging.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(
"set_log_destination",
[](const Logging::LogSeverity severity, const std::string& path) {
google::SetLogDestination(
static_cast<google::LogSeverity>(severity), path.c_str());
})
.def_static(
"info",
[](const std::string& msg) {
auto frame = GetPythonCallFrame();
google::LogMessage(frame.first.c_str(), frame.second).stream()
<< msg;
})
.def_static("warning",
[](const std::string& msg) {
auto frame = GetPythonCallFrame();
google::LogMessage(
frame.first.c_str(), frame.second, google::GLOG_WARNING)
.stream()
<< msg;
})
.def_static("error",
[](const std::string& msg) {
auto frame = GetPythonCallFrame();
google::LogMessage(
frame.first.c_str(), frame.second, google::GLOG_ERROR)
.stream()
<< msg;
})
.def_static("fatal", [](const std::string& msg) {
auto frame = GetPythonCallFrame();
google::LogMessageFatal(frame.first.c_str(), frame.second).stream()
<< msg;
});
py::enum_<Logging::LogSeverity>(PyLogging, "Level")
.value("INFO", Logging::LogSeverity::GLOG_INFO)
.value("WARNING", Logging::LogSeverity::GLOG_WARNING)
.value("ERROR", Logging::LogSeverity::GLOG_ERROR)
.value("FATAL", Logging::LogSeverity::GLOG_FATAL)
.export_values();
google::InitGoogleLogging("");
google::InstallFailureSignalHandler();
FLAGS_alsologtostderr = true;
}

PYBIND11_MODULE(pycolmap, m) {
m.doc() = "COLMAP plugin";
#ifdef VERSION_INFO
Expand All @@ -39,36 +100,13 @@ PYBIND11_MODULE(pycolmap, m) {
m.attr("COLMAP_version") = py::str(GetVersionInfo());
m.attr("COLMAP_build") = py::str(GetBuildInfo());

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", [](const std::string& msg) { LOG(INFO) << msg; })
.def_static("warning",
[](const std::string& msg) { LOG(WARNING) << msg; })
.def_static("error",
[](const std::string& msg) { LOG(ERROR) << msg; })
.def_static("fatal",
[](const 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)
.value("cuda", Device::CUDA);
AddStringToEnumConstructor(PyDevice);

BindLogging(m);
BindGeometry(m);
BindOptim(m);
BindScene(m);
Expand Down

0 comments on commit d51b455

Please sign in to comment.