Skip to content

Commit 278792e

Browse files
Add save/load of edges' level
Relates #954
1 parent 4a0da54 commit 278792e

5 files changed

Lines changed: 79 additions & 4 deletions

File tree

g2o/core/abstract_graph.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,20 @@ class G2O_CORE_API AbstractGraph {
9393
std::vector<double> measurement; ///< the measurement as a vector
9494
std::vector<double>
9595
information; ///< upper triangular part of the information matrix
96+
int level =
97+
0; ///< the level of this edge, used for hierarchical optimization
9698
AbstractEdge() = default;
9799
AbstractEdge(std::string tag, std::vector<int> ids,
98100
std::vector<double> measurement,
99101
std::vector<double> information,
100-
std::vector<int> param_ids = {},
102+
std::vector<int> param_ids = {}, int level = 0,
101103
std::vector<AbstractData> data = {})
102104
: AbstractGraphElement(std::move(tag), std::move(data)),
103105
ids(std::move(ids)),
104106
param_ids(std::move(param_ids)),
105107
measurement(std::move(measurement)),
106-
information(std::move(information)) {}
108+
information(std::move(information)),
109+
level(level) {}
107110
};
108111

109112
AbstractGraph() = default;

g2o/core/io/io_g2o.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
5050
}
5151
return os;
5252
}
53+
54+
std::unordered_set<std::string> kInternalTokens = {"G2O_EDGE_LEVEL"};
5355
} // namespace
5456

5557
namespace g2o {
@@ -83,6 +85,27 @@ std::optional<AbstractGraph> IoG2O::load(std::istream& input) {
8385
continue;
8486
}
8587

88+
if (kInternalTokens.count(token) > 0) {
89+
// handle internal tokens that are not part of the graph, but control
90+
// loading/saving behavior
91+
if (token == "G2O_EDGE_LEVEL") {
92+
int level;
93+
if (!(current_line >> level)) {
94+
G2O_ERROR("Error reading edge level at line {}", line_number);
95+
continue;
96+
}
97+
G2O_TRACE("Setting edge level to {} for last edge", level);
98+
if (result.edges().empty()) {
99+
G2O_ERROR(
100+
"Got edge level {}, but no edge has been read yet at line {}",
101+
level, line_number);
102+
continue;
103+
}
104+
result.edges().back().level = level;
105+
}
106+
continue;
107+
}
108+
86109
token = mapType(token);
87110
G2O_TRACE("Reading token {}", token);
88111
Factory::TypeInfo type_info = factory->typeInfo(token);
@@ -232,6 +255,9 @@ bool IoG2O::save(std::ostream& output, const AbstractGraph& graph) {
232255
output << edge.information.size() << " ";
233256
output << edge.information << "\n";
234257
printData(output, edge.data);
258+
if (edge.level != 0) {
259+
output << "G2O_EDGE_LEVEL " << edge.level << "\n";
260+
}
235261
}
236262

237263
// After the vertices to be backward compatible

g2o/core/io/io_wrapper_json.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ inline void to_json(nlohmann::json& j,
115115
{"ids", edge.ids},
116116
{"measurement", edge.measurement},
117117
{"information", edge.information}};
118+
if (edge.level != 0) {
119+
j["level"] = edge.level;
120+
}
118121
internal::store_if_not_empty(j, "data", edge.data);
119122
internal::store_if_not_empty(j, "param_ids", edge.param_ids);
120123
}
@@ -125,6 +128,7 @@ inline void from_json(const nlohmann::json& j,
125128
j.at("ids").get_to(edge.ids);
126129
j.at("measurement").get_to(edge.measurement);
127130
j.at("information").get_to(edge.information);
131+
internal::get_to_if_exists(j, "level", edge.level);
128132
internal::get_to_if_exists(j, "data", edge.data);
129133
internal::get_to_if_exists(j, "param_ids", edge.param_ids);
130134
}

g2o/core/optimizable_graph.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ bool OptimizableGraph::load(std::istream& is, io::Format format) {
455455
information(r, c) = abstract_edge.information[idx++];
456456
if (r != c) information(c, r) = information(r, c);
457457
}
458+
edge->setLevel(abstract_edge.level);
458459
if (!addEdge(edge)) {
459460
G2O_ERROR(
460461
"Failure adding Edge {} IDs {}", abstract_edge.tag,
@@ -728,7 +729,7 @@ bool OptimizableGraph::saveEdge(AbstractGraph& abstract_graph,
728729
for (int c = r; c < e->dimension(); ++c)
729730
upper_triangle.push_back(information(r, c));
730731
abstract_graph.edges().emplace_back(tag, ids, data, upper_triangle,
731-
e->parameterIds());
732+
e->parameterIds(), e->level());
732733
saveUserData(abstract_graph.edges().back(), e->userData());
733734
return true;
734735
}

unit_test/general/graph_io.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ MATCHER(EdgeEqual, "") {
8484
Eq(expected.tag)),
8585
Field("ids", &g2o::AbstractGraph::AbstractEdge::ids,
8686
ElementsAreArray(expected.ids)),
87+
Field("level", &g2o::AbstractGraph::AbstractEdge::level,
88+
Eq(expected.level)),
8789
Field("param_ids", &g2o::AbstractGraph::AbstractEdge::param_ids,
8890
ElementsAreArray(expected.param_ids)),
8991
Field("measurement", &g2o::AbstractGraph::AbstractEdge::measurement,
@@ -124,7 +126,7 @@ class AbstractGraphIO : public TestWithParam<g2o::io::Format> {
124126

125127
abstract_graph_.edges().emplace_back(
126128
"EDGE_SE2", std::vector<int>{1, 2}, std::vector<double>{0.1, 0.2, 0.3},
127-
std::vector<double>{1., 2., 3., 4., 5., 6.}, std::vector<int>(),
129+
std::vector<double>{1., 2., 3., 4., 5., 6.}, std::vector<int>(), 1,
128130
std::vector<g2o::AbstractGraph::AbstractData>{
129131
{"VERTEX_TAG", "more fancy data"},
130132
{"VERTEX_TAG", "even more fancy data"}});
@@ -247,6 +249,7 @@ class OptimizableGraphIO : public TestWithParam<g2o::io::Format> {
247249
e2->vertices()[1] = optimizer_ptr_->vertex(2);
248250
e2->setMeasurement(g2o::SE2(0, 1, 0));
249251
e2->setInformation(g2o::MatrixN<3>::Identity());
252+
e2->setLevel(0);
250253
optimizer_ptr_->addEdge(e2);
251254
}
252255
std::unique_ptr<g2o::SparseOptimizer> optimizer_ptr_;
@@ -305,6 +308,44 @@ TEST_P(OptimizableGraphIO, SaveAndLoad) {
305308
EXPECT_THAT(buffer.str(), Eq(buffer_after_loading.str()));
306309
}
307310

311+
TEST_P(OptimizableGraphIO, SaveAndLoadWithDifferentEdgeLevels) {
312+
g2o::io::Format format = GetParam();
313+
314+
// Manually set different levels for the edges
315+
auto edge_iter = optimizer_ptr_->edges().begin();
316+
static_cast<g2o::OptimizableGraph::Edge*>(edge_iter->get())->setLevel(2);
317+
++edge_iter;
318+
static_cast<g2o::OptimizableGraph::Edge*>(edge_iter->get())->setLevel(5);
319+
320+
std::stringstream buffer(format == g2o::io::Format::kBinary
321+
? std::ios_base::binary | std::ios_base::in |
322+
std::ios_base::out
323+
: std::ios_base::in | std::ios_base::out);
324+
constexpr int kLevelToSave = 2;
325+
bool save_result = optimizer_ptr_->save(buffer, format, kLevelToSave);
326+
ASSERT_THAT(save_result, IsTrue());
327+
EXPECT_THAT(buffer.str(), Not(IsEmpty()));
328+
329+
auto loaded_optimizer = g2o::internal::createOptimizerForTests();
330+
loaded_optimizer->load(buffer, format);
331+
332+
EXPECT_THAT(loaded_optimizer->vertices(), Not(IsEmpty()));
333+
EXPECT_THAT(loaded_optimizer->edges(), Not(IsEmpty()));
334+
335+
for (const auto& edge_ptr : loaded_optimizer->edges()) {
336+
ASSERT_NE(edge_ptr, nullptr);
337+
EXPECT_THAT(
338+
static_cast<g2o::OptimizableGraph::Edge*>(edge_ptr.get())->level(),
339+
Eq(kLevelToSave));
340+
}
341+
342+
std::stringstream buffer_after_loading;
343+
save_result =
344+
loaded_optimizer->save(buffer_after_loading, format, kLevelToSave);
345+
ASSERT_THAT(save_result, IsTrue());
346+
EXPECT_THAT(buffer.str(), Eq(buffer_after_loading.str()));
347+
}
348+
308349
namespace {
309350
// We can always test G2O format, others depend on libraries
310351
const auto kFileformatsToTest =

0 commit comments

Comments
 (0)