src/tools/lvr2_plymerger/Main.cpp
Go to the documentation of this file.
1 
28 #include "Options.hpp"
29 
30 #include <rply.h>
31 
32 #include "lvr2/io/ModelFactory.hpp"
33 #include "lvr2/io/Progress.hpp"
34 
35 #include <boost/filesystem.hpp>
36 
37 #include <iostream>
38 #include <fstream>
39 #include <tuple>
40 
41 using std::ofstream;
42 using std::cout;
43 using std::endl;
44 using std::pair;
45 
46 using namespace lvr2;
47 
49  string filename,
50  size_t& numPoints,
51  bool& hasNormals,
52  bool& hasColors)
53 {
54  numPoints = 0;
55  hasNormals = false;
56  hasColors = false;
57 
58  p_ply ply = ply_open(filename.c_str(), NULL, 0, NULL);
59 
60  if(!ply)
61  {
62  std::cout << timestamp << "Could not open '" << filename << "." << endl;
63  return;
64  }
65 
66  if ( !ply_read_header( ply ) )
67  {
68  std::cout << timestamp << "Could not read header." << endl;
69  return;
70  }
71 
72  p_ply_element element = 0;
73  char buf[256] = "";
74  const char * name = buf;
75  long int n;
76 
77  bool found_vertices = false;
78  bool found_points = false;
79  bool found_faces = false;
80 
81  while( (element = ply_get_next_element(ply, element)))
82  {
83  ply_get_element_info( element, &name, &n );
84  if(!strcmp( name, "vertex" ))
85  {
86  found_vertices = true;
87  }
88  else if(!strcmp( name, "point" ))
89  {
90  found_points = true;
91  }
92  else if(!strcmp( name, "face" ))
93  {
94  found_faces = true;
95  }
96  }
97 
98  // Parsed header and saved meta information. Close file.
99  ply_close(ply);
100 
101  // Found faces and vertices without additional point field. Assuming
102  // that the .ply does not contain point cloud data
103  if(found_faces && found_vertices && !found_points)
104  {
105  cout << timestamp << "Warning: While parsing '" << filename << "': Found mesh data without points." << endl;
106  //return;
107  }
108 
109 
110  // Re-open file and try to extract properties of found point cloud data.
111  ply = ply_open(filename.c_str(), NULL, 0, NULL);
112 
113  if(!ply)
114  {
115  std::cout << timestamp << "Could not open '" << filename << "." << endl;
116  return;
117  }
118 
119  if ( !ply_read_header( ply ) )
120  {
121  std::cout << timestamp << "Could not read header." << endl;
122  return;
123  }
124 
125  element = NULL;
126  while ((element = ply_get_next_element( ply, element )))
127  {
128  ply_get_element_info(element, &name, &n);
129 
130  // Found vertices without points -> scan for vertex field
131  if(found_vertices && !found_points)
132  {
133  if (!strcmp( name, "vertex" ) )
134  {
135 // cout << timestamp << "Reading points from vertex field." << endl;
136 // cout << timestamp << "File contains " << n << " points." << endl;
137  p_ply_property prop = NULL;
138  numPoints = n;
139  while ((prop = ply_get_next_property(element, prop)))
140  {
141  ply_get_property_info( prop, &name, NULL, NULL, NULL );
142  if ( !strcmp( name, "red" ) || !strcmp( name, "r" ))
143  {
144  //cout << timestamp << "Found colors." << endl;
145  hasColors = true;
146  }
147  else if(!strcmp( name, "nx"))
148  {
149  //cout << timestamp << "Found normals." << endl;
150  hasNormals = true;
151  }
152  }
153  }
154  }
155  else if(found_points)
156  {
157  if (!strcmp( name, "point" ) )
158  {
159 // cout << timestamp << "Reading points from point field." << endl;
160 // cout << timestamp << "File contains " << n << " points." << endl;
161  p_ply_property prop = NULL;
162  numPoints = n;
163  while ((prop = ply_get_next_property(element, prop)))
164  {
165  ply_get_property_info( prop, &name, NULL, NULL, NULL );
166  if ( !strcmp( name, "red" ) || !strcmp( name, "r" ))
167  {
168  //cout << timestamp << "Found colors." << endl;
169  hasColors = true;
170  }
171  else if(!strcmp( name, "nx"))
172  {
173  //cout << timestamp << "Found normals." << endl;
174  hasNormals = true;
175  }
176  }
177  }
178  }
179  }
180  ply_close(ply);
181 }
182 
183 
184 void writePLYHeader(ofstream& outfile, size_t numPoints, bool writeColors, bool writeNormals)
185 {
186  outfile << "ply" << endl;
187  outfile << "format binary_little_endian 1.0" << endl;
188  outfile << "element vertex " << numPoints << endl;
189  outfile << "property float x" << endl;
190  outfile << "property float y" << endl;
191  outfile << "property float z" << endl;
192  if(writeColors)
193  {
194  outfile << "property uchar red" << endl;
195  outfile << "property uchar green" << endl;
196  outfile << "property uchar blue" << endl;
197  }
198 
199  if(writeNormals)
200  {
201  outfile << "property float nx" << endl;
202  outfile << "property float ny" << endl;
203  outfile << "property float nz" << endl;
204  }
205  outfile << "end_header" << endl;
206 }
207 
208 void addToFile(ofstream& out, string filename)
209 {
210  ModelPtr model = ModelFactory::readModel(filename);
211  PointBufferPtr pointBuffer = model->m_pointCloud;
212 
213  size_t np, nn, nc;
214  size_t w_color;
215  np = pointBuffer->numPoints();
216  nc = nn = np;
217 
218  floatArr points = pointBuffer->getPointArray();
219  ucharArr colors = pointBuffer->getColorArray(w_color);
220  floatArr normals = pointBuffer->getNormalArray();
221 
222  // Determine size of single point
223  size_t buffer_size = 3 * sizeof(float);
224 
225  if(colors)
226  {
227  buffer_size += w_color * sizeof(unsigned char);
228  }
229 
230  if(normals)
231  {
232  buffer_size += 3 * sizeof(float);
233  }
234 
235  char* buffer = new char[buffer_size];
236 
237  for(size_t i = 0; i < np; i++)
238  {
239  char* ptr = &buffer[0];
240 
241  // Write coordinates to buffer
242  *((float*)ptr) = points[3*i];
243  ptr += sizeof(float);
244  *((float*)ptr) = points[3*i+1];
245  ptr += sizeof(float);
246  *((float*)ptr) = points[3*i+2];
247 
248  // Write colors to buffer
249  if(colors)
250  {
251  ptr += sizeof(float);
252  *((unsigned char*)ptr) = colors[3 * i];
253  ptr += sizeof(unsigned char);
254  *((unsigned char*)ptr) = colors[3 * i + 1];
255  ptr += sizeof(unsigned char);
256  *((unsigned char*)ptr) = colors[3 * i + 2];
257  }
258 
259  if(normals)
260  {
261  ptr += sizeof(unsigned char);
262  *((float*)ptr) = normals[3*i];
263  ptr += sizeof(float);
264  *((float*)ptr) = normals[3*i+1];
265  ptr += sizeof(float);
266  *((float*)ptr) = normals[3*i+2];
267  }
268  out.write((const char*)buffer, buffer_size);
269  }
270 
271  delete[] buffer;
272 }
273 
277 int main(int argc, char** argv)
278 {
279  // Parse command line arguments
280  ply_merger::Options options(argc, argv);
281 
282  // Stores the names of the found ply files in the input directory.
283  vector<pair<string, size_t> > ply_file_names;
284 
285  bool mergeColors = false;
286  bool mergeNormals = false;
287  size_t totalNumPoints = 0;
288 
289  // Check given arguments
290  boost::filesystem::path inputDir(options.inputDir());
291  if(boost::filesystem::exists(inputDir) && boost::filesystem::is_directory(inputDir))
292  {
293  // Loop over directory and store names of all .ply files
294  boost::filesystem::directory_iterator end;
295  for(boost::filesystem::directory_iterator it(inputDir); it != end; ++it)
296  {
297  std::string extension = it->path().extension().string();
298  if(extension == ".ply")
299  {
300  ply_file_names.push_back(pair<string, size_t>(it->path().string(), 0));
301  }
302  }
303 
304  size_t numFilesWithNormals = 0;
305  size_t numFilesWithColors = 0;
306 
307  for(auto it = ply_file_names.begin(); it != ply_file_names.end(); it++)
308  {
309  size_t numPoints = 0;
310  bool hasColors = false;
311  bool hasNormals = false;
312  parsePLYHeader(it->first, numPoints, hasNormals, hasColors);
313 
314  if(hasColors)
315  {
316  numFilesWithColors++;
317  }
318 
319  if(hasNormals)
320  {
321  numFilesWithNormals++;
322  }
323 
324  it->second = numPoints;
325  totalNumPoints += numPoints;
326  }
327 
328  cout << numFilesWithNormals << " " << ply_file_names.size() << endl;
329 
330  // Check if all files have same structure
331  if(numFilesWithColors > 0 && numFilesWithColors == ply_file_names.size())
332  {
333  mergeColors = true;
334  }
335 
336  if(numFilesWithNormals > 0 && numFilesWithNormals == ply_file_names.size())
337  {
338  mergeNormals = true;
339  }
340 
341  cout << timestamp << "Parsed directory. Reading " << totalNumPoints << " points from " << ply_file_names.size() << " files." << endl;
342  if(mergeNormals)
343  {
344  cout << timestamp << "Merging normals." << endl;
345  }
346 
347  if(mergeColors)
348  {
349  cout << timestamp << "Merging colors." << endl;
350  }
351  }
352  else
353  {
354  std::cout << timestamp << options.inputDir() << " does not exist or is not a directory." << std::endl;
355  }
356 
357  string outfile_name = options.outputFile();
358  ofstream out;
359  out.open(outfile_name.c_str(), std::ios::binary);
360  writePLYHeader(out, totalNumPoints, mergeColors, mergeNormals);
361 
362  size_t maxChunkPoints = 1e7;
363  auto it = ply_file_names.begin();
364 
365  PacmanProgressBar progress(ply_file_names.size(), "Merging...");
366 
367  while(it != ply_file_names.end())
368  {
369  size_t pointsInChunk = 0;
370  vector<string> filesInChunk;
371  do
372  {
373  filesInChunk.push_back(it->first);
374  pointsInChunk += it->second;
375  ++it;
376  }
377  while(it != ply_file_names.end() && pointsInChunk < maxChunkPoints);
378 
379 
380  for(auto chunkIt: filesInChunk)
381  {
382  addToFile(out, chunkIt);
383  }
384 
385  for(size_t c = 0; c < filesInChunk.size(); c++)
386  {
387  ++progress;
388  }
389  }
390 
391  return 0;
392 }
393 
Definition: rply.c:193
const kaboom::Options * options
int main(int argc, char **argv)
Main entry point for the LSSR surface executable.
int ply_close(p_ply ply)
Definition: rply.c:658
static Timestamp timestamp
A global time stamp object for program runtime measurement.
Definition: Timestamp.hpp:116
static ModelPtr readModel(std::string filename)
int ply_get_property_info(p_ply_property property, const char **name, e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type)
Definition: rply.c:713
boost::shared_array< unsigned char > ucharArr
Definition: DataStruct.hpp:137
std::shared_ptr< PointBuffer > PointBufferPtr
p_ply_property ply_get_next_property(p_ply_element element, p_ply_property last)
Definition: rply.c:704
A class to parse the program options for the reconstruction executable.
void addToFile(ofstream &out, string filename)
boost::shared_array< float > floatArr
Definition: DataStruct.hpp:133
p_ply_element ply_get_next_element(p_ply ply, p_ply_element last)
Definition: rply.c:687
std::shared_ptr< Model > ModelPtr
Definition: Model.hpp:80
int ply_get_element_info(p_ply_element element, const char **name, long *ninstances)
Definition: rply.c:696
void writePLYHeader(ofstream &outfile, size_t numPoints, bool writeColors, bool writeNormals)
p_ply ply_open(const char *name, p_ply_error_cb error_cb, long idata, void *pdata)
Definition: rply.c:355
#define NULL
Definition: mydefs.hpp:141
int ply_read_header(p_ply ply)
Definition: rply.c:384
void parsePLYHeader(string filename, size_t &numPoints, bool &hasNormals, bool &hasColors)
char ** argv


lvr2
Author(s): Thomas Wiemann , Sebastian Pütz , Alexander Mock , Lars Kiesow , Lukas Kalbertodt , Tristan Igelbrink , Johan M. von Behren , Dominik Feldschnieders , Alexander Löhr
autogenerated on Mon Feb 28 2022 22:46:08