Skip to content

Commit 5d4cd57

Browse files
committed
add command-line option to save the reconstructed skeletons into PLY files
1 parent a6f5e13 commit 5d4cd57

File tree

3 files changed

+103
-22
lines changed

3 files changed

+103
-22
lines changed

AdTree/main.cpp

+86-13
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,56 @@
4444

4545
using namespace easy3d;
4646

47+
// save the smoothed skeleton into a PLY file (where each vertex has a radius)
48+
void save_skeleton(Skeleton* skeleton, PointCloud* cloud, const std::string& file_name) {
49+
const ::Graph& sgraph = skeleton->get_smoothed_skeleton();
50+
if (boost::num_edges(sgraph) == 0) {
51+
std::cerr << "failed to save skeleton (no edge exists)" << std::endl;
52+
return;
53+
}
54+
55+
// convert the boost graph to Graph (avoid modifying easy3d's GraphIO, or writing IO for boost graph)
56+
57+
std::unordered_map<SGraphVertexDescriptor, easy3d::Graph::Vertex> vvmap;
58+
easy3d::Graph g;
59+
60+
auto vertexRadius = g.add_vertex_property<float>("v:radius");
61+
auto vts = boost::vertices(sgraph);
62+
for (SGraphVertexIterator iter = vts.first; iter != vts.second; ++iter) {
63+
SGraphVertexDescriptor vd = *iter;
64+
if (boost::degree(vd, sgraph) != 0) { // ignore isolated vertices
65+
const vec3& vp = sgraph[vd].cVert;
66+
auto v = g.add_vertex(vp);
67+
vertexRadius[v] = sgraph[vd].radius;
68+
vvmap[vd] = v;
69+
}
70+
}
71+
72+
auto egs = boost::edges(sgraph);
73+
for (SGraphEdgeIterator iter = egs.first; iter != egs.second; ++iter) {
74+
SGraphEdgeDescriptor ed = *iter; // the edge descriptor
75+
SGraphEdgeProp ep = sgraph[ed]; // the edge property
76+
77+
SGraphVertexDescriptor s = boost::source(*iter, sgraph);
78+
SGraphVertexDescriptor t = boost::target(*iter, sgraph);
79+
g.add_edge(vvmap[s], vvmap[t]);
80+
}
81+
82+
auto offset = cloud->get_model_property<dvec3>("translation");
83+
if (offset) {
84+
auto prop = g.model_property<dvec3>("translation");
85+
prop[0] = offset[0];
86+
}
87+
88+
if (GraphIO::save(file_name, &g))
89+
std::cout << "model of skeletons saved to: " << file_name << std::endl;
90+
else
91+
std::cerr << "failed to save the model of skeletons into file" << std::endl;
92+
}
93+
94+
4795
// returns the number of processed input files.
48-
int batch_reconstruct(std::vector<std::string>& point_cloud_files, const std::string& output_folder) {
96+
int batch_reconstruct(std::vector<std::string>& point_cloud_files, const std::string& output_folder, bool export_skeleton) {
4997
int count(0);
5098
for (std::size_t i=0; i<point_cloud_files.size(); ++i) {
5199
const std::string& xyz_file = point_cloud_files[i];
@@ -111,7 +159,12 @@ int batch_reconstruct(std::vector<std::string>& point_cloud_files, const std::st
111159
++count;
112160
}
113161
else
114-
std::cerr << "failed in saving the model of branches" << std::endl;
162+
std::cerr << "failed to save the model of branches" << std::endl;
163+
164+
if (export_skeleton) {
165+
const std::string& skeleton_file = output_folder + "/" + file_system::base_name(cloud->name()) + "_skeleton.ply";
166+
save_skeleton(skeleton, cloud, skeleton_file);
167+
}
115168

116169
delete cloud;
117170
delete mesh;
@@ -123,15 +176,30 @@ int batch_reconstruct(std::vector<std::string>& point_cloud_files, const std::st
123176

124177

125178
int main(int argc, char *argv[]) {
126-
// argc = 3;
179+
// argc = 2;
127180
// argv[1] = "/Users/lnan/Projects/adtree/data";
128181
// argv[2] = "/Users/lnan/Projects/adtree/data-results";
129182

130183
if (argc == 1) {
131184
TreeViewer viewer;
132185
viewer.run();
133186
return EXIT_SUCCESS;
134-
} else if (argc == 3) {
187+
} else if (argc >= 3) {
188+
bool export_skeleton = false;
189+
for (int i = 0; i < argc; ++i) {
190+
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-skeleton") == 0) {
191+
export_skeleton = true;
192+
break;
193+
}
194+
}
195+
196+
if (export_skeleton) {
197+
std::cout << "You have requested to save the reconstructed tree skeleton(s) in PLY format into the output directory." << std::endl;
198+
std::cout << "The skeleton file(s) can be visualized using Easy3D: https://github.com/LiangliangNan/Easy3D" << std::endl;
199+
}
200+
else
201+
std::cout << "Tree skeleton(s) will not be saved (append '-s' or '-skeleton' in commandline to enable it)" << std::endl;
202+
135203
std::string first_arg(argv[1]);
136204
std::string second_arg(argv[2]);
137205
if (file_system::is_file(second_arg))
@@ -140,7 +208,7 @@ int main(int argc, char *argv[]) {
140208
std::string output_dir = second_arg;
141209
if (file_system::is_file(first_arg)) {
142210
std::vector<std::string> cloud_files = {first_arg};
143-
return batch_reconstruct(cloud_files, output_dir) > 0;
211+
return batch_reconstruct(cloud_files, output_dir, export_skeleton) > 0;
144212
} else if (file_system::is_directory(first_arg)) {
145213
std::vector<std::string> entries;
146214
file_system::get_directory_entries(first_arg, entries, false);
@@ -149,7 +217,7 @@ int main(int argc, char *argv[]) {
149217
if (file_name.size() > 3 && file_name.substr(file_name.size() - 3) == "xyz")
150218
cloud_files.push_back(first_arg + "/" + file_name);
151219
}
152-
return batch_reconstruct(cloud_files, output_dir) > 0;
220+
return batch_reconstruct(cloud_files, output_dir, export_skeleton) > 0;
153221
} else
154222
std::cerr
155223
<< "WARNING: unknown first argument (expecting either a point cloud file in *.xyz format or a\n"
@@ -158,13 +226,18 @@ int main(int argc, char *argv[]) {
158226
}
159227

160228
std::cerr << "Usage: AdTree can be run in three modes, which can be selected based on arguments:" << std::endl;
161-
std::cerr << " - GUI mode." << std::endl;
162-
std::cerr << " Command: ./AdTree" << std::endl;
163-
std::cerr << " - Single processing mode (i.e., processing a single point cloud file)." << std::endl;
164-
std::cerr << " Command: ./AdTree <xyz_file_path> <output_directory>" << std::endl;
165-
std::cerr << " - Batch processing mode (i.e., all *.xyz files in the input directory will be treated as input \n";
166-
std::cerr << " for reconstruction and the reconstructed models will be save in the output directory).\n";
167-
std::cerr << " Command: ./AdTree <xyz_files_directory> <output_directory>" << std::endl;
229+
std::cerr << " 1) GUI mode." << std::endl;
230+
std::cerr << " Command: ./AdTree" << std::endl << std::endl;
231+
std::cerr << " 2) Commandline single processing mode (i.e., processing a single point cloud file)." << std::endl;
232+
std::cerr << " Command: ./AdTree <xyz_file_path> <output_directory> [-s|-skeleton]" << std::endl;
233+
std::cerr << " - <xyz_file_path>: a mandatory argument specifying the path to the input point cloud file" << std::endl;
234+
std::cerr << " - <output_directory>: a mandatory argument specifying where to save the results" << std::endl;
235+
std::cerr << " - [-s] or [-skeleton]: also export the skeletons (omit this argument it if you don't need skeletons)" << std::endl << std::endl;
236+
std::cerr << " 3) Commandline batch processing mode (i.e., all *.xyz files in an input directory will be processed)." << std::endl;
237+
std::cerr << " Command: ./AdTree <xyz_files_directory> <output_directory> [-s|-skeleton]" << std::endl;
238+
std::cerr << " - <xyz_files_directory>: a mandatory argument specifying the directory containing the input point cloud files" << std::endl;
239+
std::cerr << " - <output_directory>: a mandatory argument specifying where to save the results" << std::endl;
240+
std::cerr << " - [-s] or [-skeleton]: also export the skeletons (omit this argument it if you don't need skeletons)" << std::endl << std::endl;
168241

169242
return EXIT_FAILURE;
170243
}

AdTree/tree_viewer.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ void TreeViewer::export_skeleton() const {
251251
auto e = g.add_edge(vvmap[s], vvmap[t]);
252252
edgeRadius[e] = skeleton[*iter].nRadius;
253253
}
254-
#else // save the smoothed skeleton, for which each very has a radius.
254+
#else // save the smoothed skeleton into a PLY file (where each vertex has a radius)
255255
const ::Graph& skeleton = skeleton_->get_smoothed_skeleton();
256256
if (boost::num_edges(skeleton) == 0) {
257257
std::cerr << "skeleton has 0 edges" << std::endl;

README.md

+16-8
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,26 @@ Don't have any experience with C/C++ programming? Have a look at [How to build A
6363
6464
After obtaining the executable, AdTree can be run in three modes, which can be selected based
6565
on arguments.
66-
- GUI mode that provides a user interface with menus. You can double-click to run the app or from the command
67-
using `./AdTree`.
68-
69-
- Single processing mode (i.e., processing a single point cloud file) from the command line using
66+
- GUI mode. It provides a user interface with menus. You can double-click the app or run it from the commandline
67+
```
68+
./AdTree
7069
```
71-
./AdTree <xyz_file_path> <output_directory>
70+
71+
- Commandline single processing mode (i.e., processing a single point cloud file).
7272
```
73-
- Batch processing mode (i.e., all *.xyz files in the input directory will be treated as input and the reconstructed
74-
models will be saved in the output directory) from the command line using
73+
./AdTree <xyz_file_path> <output_directory> [-s|-skeleton]
74+
```
75+
- `<xyz_file_path>`: a mandatory argument specifying the path to the input point cloud file
76+
- `<output_directory>`: a mandatory argument specifying where to save the results
77+
- `[-s]` or `[-skeleton]`: also export the skeletons (omit this argument it if you don't need skeletons)
78+
79+
- Commandline batch processing mode (i.e., all *.xyz files in an input directory will be processed).
7580
```
76-
./AdTree <xyz_files_directory> <output_directory>
81+
./AdTree <xyz_files_directory> <output_directory> [-s|-skeleton]
7782
```
83+
- `<xyz_files_directory>`: a mandatory argument specifying the directory containing the input point cloud files
84+
- `<output_directory>`: a mandatory argument specifying where to save the results
85+
- `[-s]` or `[-skeleton]`: also export the skeletons (omit this argument it if you don't need skeletons)
7886
7987
<p align="center">
8088
<img src="./resources/images/ui.jpg" width="600">

0 commit comments

Comments
 (0)