Skip to content

Commit f207532

Browse files
Add graph summary print
1 parent e5f7a90 commit f207532

6 files changed

Lines changed: 131 additions & 1 deletion

File tree

g2o/apps/g2o_cli/g2o.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ int main(int argc, char** argv) {
130130
std::string robustKernel;
131131
bool computeMarginals = false;
132132
bool printSolverProperties = false;
133+
bool printGraphSummary = false;
133134
double huberWidth = -1.;
134135
double gain = 1e-6;
135136
int maxIterationsWithGain = std::numeric_limits<int>::max();
@@ -207,6 +208,8 @@ int main(int argc, char** argv) {
207208
"specify a types library which will be loaded");
208209
#endif
209210
app.add_option("--stats", statsFile, "specify a file for the statistics");
211+
app.add_flag("--graph_summary", printGraphSummary,
212+
"print a short summary of the loaded graph and exit");
210213
app.add_flag("--list_types", listTypes, "list the registered types");
211214
app.add_flag("--list_robust_kernels", listRobustKernels,
212215
"list the registered robust kernels");
@@ -316,6 +319,11 @@ int main(int argc, char** argv) {
316319
cerr << "Loaded " << optimizer.vertices().size() << " vertices\n";
317320
cerr << "Loaded " << optimizer.edges().size() << " edges\n";
318321

322+
if (printGraphSummary) {
323+
optimizer.printGraphSummary(cout);
324+
return 0;
325+
}
326+
319327
if (optimizer.vertices().empty()) {
320328
cerr << "Graph contains no vertices\n";
321329
return 1;

g2o/core/sparse_optimizer.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,44 @@ double SparseOptimizer::activeRobustChi2() const {
128128
return chi;
129129
}
130130

131+
void SparseOptimizer::printGraphSummary(std::ostream& os) const {
132+
const size_t nVertices = vertices().size();
133+
const size_t nEdges = edges().size();
134+
os << "Graph summary:\n";
135+
os << " vertices: " << nVertices << "\n";
136+
os << " edges: " << nEdges << "\n";
137+
138+
if (nVertices > 0) {
139+
const int maxDim = maxDimension();
140+
int nPoses = 0;
141+
int nLandmarks = 0;
142+
std::unordered_set<int> levels;
143+
for (const auto& it : vertices()) {
144+
auto v = std::static_pointer_cast<OptimizableGraph::Vertex>(it.second);
145+
if (v->dimension() == maxDim)
146+
nPoses++;
147+
else
148+
nLandmarks++;
149+
}
150+
for (const auto& e : edges()) {
151+
auto oe = static_cast<OptimizableGraph::Edge*>(e.get());
152+
if (oe) {
153+
levels.insert(oe->level());
154+
}
155+
}
156+
os << " poses: " << nPoses << "\n";
157+
os << " landmarks: " << nLandmarks << "\n";
158+
os << " levels: " << levels.size() << "\n";
159+
}
160+
161+
os << " active vertices: " << activeVertices_.size() << "\n";
162+
os << " active edges: " << activeEdges_.size() << "\n";
163+
if (!activeEdges_.empty()) {
164+
os << " active chi2: " << activeChi2() << "\n";
165+
os << " active robust chi2: " << activeRobustChi2() << "\n";
166+
}
167+
}
168+
131169
std::shared_ptr<OptimizableGraph::Vertex> SparseOptimizer::findGauge() {
132170
if (vertices().empty()) return nullptr;
133171

g2o/core/sparse_optimizer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define G2O_GRAPH_OPTIMIZER_CHOL_H_
2929

3030
#include <memory>
31+
#include <ostream>
3132
#include <utility>
3233
#include <vector>
3334

@@ -171,6 +172,11 @@ class G2O_CORE_API SparseOptimizer : public OptimizableGraph {
171172
*/
172173
double activeRobustChi2() const;
173174

175+
/**
176+
* Print a short summary of the currently loaded graph.
177+
*/
178+
void printGraphSummary(std::ostream& os = std::cout) const;
179+
174180
//! verbose information during optimization
175181
bool verbose() const { return verbose_; }
176182
void setVerbose(bool verbose);

python/core/py_sparse_optimizer.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <g2o/core/optimization_algorithm_with_hessian.h>
1111
#include <g2o/core/sparse_optimizer.h>
1212

13+
#include <sstream>
1314
#include <utility>
1415

1516
namespace g2o {
@@ -83,7 +84,13 @@ void declareSparseOptimizer(py::module& m) {
8384
.def("gauge_freedom", &CLS::gaugeFreedom) // -> bool
8485
.def("active_chi2", &CLS::activeChi2) // -> double
8586
.def("active_robust_chi2", &CLS::activeRobustChi2) // -> double
86-
.def("verbose", &CLS::verbose) // -> bool
87+
.def("print_graph_summary",
88+
[](CLS& optimizer) {
89+
std::ostringstream os;
90+
optimizer.printGraphSummary(os);
91+
return os.str();
92+
})
93+
.def("verbose", &CLS::verbose) // -> bool
8794
.def("set_verbose", &CLS::setVerbose,
8895
"verbose"_a) // -> void
8996

python/tests/test_core.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ def test_set_verbose(self, basic_se2_optimizer):
5151
basic_se2_optimizer.set_verbose(False)
5252
# Should not raise an exception
5353

54+
def test_print_graph_summary(self, simple_se2_graph):
55+
"""Test Python binding for graph summary."""
56+
summary = simple_se2_graph.print_graph_summary()
57+
assert "vertices:" in summary
58+
assert "edges:" in summary
59+
assert "poses:" in summary
60+
5461

5562
class TestBlockSolvers:
5663
"""Test BlockSolver configurations."""

unit_test/general/graph_operations.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <cstddef>
3232
#include <memory>
3333
#include <numeric>
34+
#include <sstream>
3435
#include <unordered_set>
3536
#include <utility>
3637
#include <vector>
@@ -152,6 +153,69 @@ TEST(General, GraphAddEdge) {
152153
ASSERT_TRUE(optimizer->edges().empty());
153154
}
154155

156+
TEST(General, GraphPrintSummary) {
157+
auto optimizer = g2o::internal::createOptimizerForTests();
158+
159+
auto v0 = std::make_shared<g2o::VertexSE2>();
160+
v0->setId(0);
161+
auto v1 = std::make_shared<g2o::VertexSE2>();
162+
v1->setId(1);
163+
164+
ASSERT_TRUE(optimizer->addVertex(v0));
165+
ASSERT_TRUE(optimizer->addVertex(v1));
166+
167+
auto e0 = std::make_shared<g2o::EdgeSE2>();
168+
e0->setVertex(0, v0);
169+
e0->setVertex(1, v1);
170+
e0->setLevel(0);
171+
ASSERT_TRUE(optimizer->addEdge(e0));
172+
173+
std::ostringstream os;
174+
optimizer->printGraphSummary(os);
175+
const std::string summary = os.str();
176+
177+
EXPECT_NE(summary.find("vertices: 2"), std::string::npos);
178+
EXPECT_NE(summary.find("edges: 1"), std::string::npos);
179+
EXPECT_NE(summary.find("levels: 1"), std::string::npos);
180+
EXPECT_NE(summary.find("connected_components: 1"), std::string::npos);
181+
}
182+
183+
TEST(General, GraphConnectedComponents) {
184+
auto optimizer = g2o::internal::createOptimizerForTests();
185+
186+
auto v0 = std::make_shared<g2o::VertexSE2>();
187+
v0->setId(0);
188+
auto v1 = std::make_shared<g2o::VertexSE2>();
189+
v1->setId(1);
190+
auto v2 = std::make_shared<g2o::VertexSE2>();
191+
v2->setId(2);
192+
193+
ASSERT_TRUE(optimizer->addVertex(v0));
194+
ASSERT_TRUE(optimizer->addVertex(v1));
195+
ASSERT_TRUE(optimizer->addVertex(v2));
196+
197+
auto e01 = std::make_shared<g2o::EdgeSE2>();
198+
e01->setVertex(0, v0);
199+
e01->setVertex(1, v1);
200+
e01->setLevel(0);
201+
ASSERT_TRUE(optimizer->addEdge(e01));
202+
203+
auto e12 = std::make_shared<g2o::EdgeSE2>();
204+
e12->setVertex(0, v1);
205+
e12->setVertex(1, v2);
206+
e12->setLevel(1);
207+
ASSERT_TRUE(optimizer->addEdge(e12));
208+
209+
EXPECT_FALSE(optimizer->isConnected(0));
210+
EXPECT_EQ(optimizer->numConnectedComponents(0), 2);
211+
212+
EXPECT_FALSE(optimizer->isConnected(1));
213+
EXPECT_EQ(optimizer->numConnectedComponents(1), 2);
214+
215+
EXPECT_FALSE(optimizer->isConnected(2));
216+
EXPECT_EQ(optimizer->numConnectedComponents(2), 3);
217+
}
218+
155219
TEST(General, GraphIndexMapping) {
156220
auto optimizer = g2o::internal::createOptimizerForTests();
157221

0 commit comments

Comments
 (0)