44
44
45
45
using namespace easy3d ;
46
46
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
+
47
95
// 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 ) {
49
97
int count (0 );
50
98
for (std::size_t i=0 ; i<point_cloud_files.size (); ++i) {
51
99
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
111
159
++count;
112
160
}
113
161
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
+ }
115
168
116
169
delete cloud;
117
170
delete mesh;
@@ -123,15 +176,30 @@ int batch_reconstruct(std::vector<std::string>& point_cloud_files, const std::st
123
176
124
177
125
178
int main (int argc, char *argv[]) {
126
- // argc = 3 ;
179
+ // argc = 2 ;
127
180
// argv[1] = "/Users/lnan/Projects/adtree/data";
128
181
// argv[2] = "/Users/lnan/Projects/adtree/data-results";
129
182
130
183
if (argc == 1 ) {
131
184
TreeViewer viewer;
132
185
viewer.run ();
133
186
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
+
135
203
std::string first_arg (argv[1 ]);
136
204
std::string second_arg (argv[2 ]);
137
205
if (file_system::is_file (second_arg))
@@ -140,7 +208,7 @@ int main(int argc, char *argv[]) {
140
208
std::string output_dir = second_arg;
141
209
if (file_system::is_file (first_arg)) {
142
210
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 ;
144
212
} else if (file_system::is_directory (first_arg)) {
145
213
std::vector<std::string> entries;
146
214
file_system::get_directory_entries (first_arg, entries, false );
@@ -149,7 +217,7 @@ int main(int argc, char *argv[]) {
149
217
if (file_name.size () > 3 && file_name.substr (file_name.size () - 3 ) == " xyz" )
150
218
cloud_files.push_back (first_arg + " /" + file_name);
151
219
}
152
- return batch_reconstruct (cloud_files, output_dir) > 0 ;
220
+ return batch_reconstruct (cloud_files, output_dir, export_skeleton ) > 0 ;
153
221
} else
154
222
std::cerr
155
223
<< " 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[]) {
158
226
}
159
227
160
228
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;
168
241
169
242
return EXIT_FAILURE;
170
243
}
0 commit comments