ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ply.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "util/ply.h"
33 
34 #include <fstream>
35 
36 #include <Eigen/Core>
37 
38 #include "util/logging.h"
39 #include "util/misc.h"
40 
41 namespace colmap {
42 
43 std::vector<PlyPoint> ReadPly(const std::string& path) {
44  std::ifstream file(path, std::ios::binary);
45  CHECK(file.is_open()) << path;
46 
47  std::vector<PlyPoint> points;
48 
49  std::string line;
50 
51  // The index of the property for ASCII PLY files.
52  int X_index = -1;
53  int Y_index = -1;
54  int Z_index = -1;
55  int NX_index = -1;
56  int NY_index = -1;
57  int NZ_index = -1;
58  int R_index = -1;
59  int G_index = -1;
60  int B_index = -1;
61 
62  // The position in number of bytes of the property for binary PLY files.
63  int X_byte_pos = -1;
64  int Y_byte_pos = -1;
65  int Z_byte_pos = -1;
66  int NX_byte_pos = -1;
67  int NY_byte_pos = -1;
68  int NZ_byte_pos = -1;
69  int R_byte_pos = -1;
70  int G_byte_pos = -1;
71  int B_byte_pos = -1;
72 
73  // Flag to use double precision in binary PLY files
74  bool X_double = false;
75  bool Y_double = false;
76  bool Z_double = false;
77  bool NX_double = false;
78  bool NY_double = false;
79  bool NZ_double = false;
80 
81  bool in_vertex_section = false;
82  bool is_binary = false;
83  bool is_little_endian = false;
84  size_t num_bytes_per_line = 0;
85  size_t num_vertices = 0;
86 
87  int index = 0;
88  while (std::getline(file, line)) {
89  StringTrim(&line);
90 
91  if (line.empty()) {
92  continue;
93  }
94 
95  if (line == "end_header") {
96  break;
97  }
98 
99  if (line.size() >= 6 && line.substr(0, 6) == "format") {
100  if (line == "format ascii 1.0") {
101  is_binary = false;
102  } else if (line == "format binary_little_endian 1.0") {
103  is_binary = true;
104  is_little_endian = true;
105  } else if (line == "format binary_big_endian 1.0") {
106  is_binary = true;
107  is_little_endian = false;
108  }
109  }
110 
111  const std::vector<std::string> line_elems = StringSplit(line, " ");
112 
113  if (line_elems.size() >= 3 && line_elems[0] == "element") {
114  in_vertex_section = false;
115  if (line_elems[1] == "vertex") {
116  num_vertices = std::stoll(line_elems[2]);
117  in_vertex_section = true;
118  } else if (std::stoll(line_elems[2]) > 0) {
119  std::cout << "WARN: Only vertex elements supported; ignoring "
120  << line_elems[1] << std::endl;
121  }
122  }
123 
124  if (!in_vertex_section) {
125  continue;
126  }
127 
128  // Show diffuse, ambient, specular colors as regular colors.
129 
130  if (line_elems.size() >= 3 && line_elems[0] == "property") {
131  CHECK(line_elems[1] == "float" || line_elems[1] == "float32" ||
132  line_elems[1] == "double" || line_elems[1] == "float64" ||
133  line_elems[1] == "uchar")
134  << "PLY import only supports float, double, and uchar data types";
135 
136  if (line == "property float x" || line == "property float32 x" ||
137  line == "property double x" || line == "property float64 x") {
138  X_index = index;
139  X_byte_pos = num_bytes_per_line;
140  X_double = (line_elems[1] == "double" || line_elems[1] == "float64");
141  } else if (line == "property float y" || line == "property float32 y" ||
142  line == "property double y" || line == "property float64 y") {
143  Y_index = index;
144  Y_byte_pos = num_bytes_per_line;
145  Y_double = (line_elems[1] == "double" || line_elems[1] == "float64");
146  } else if (line == "property float z" || line == "property float32 z" ||
147  line == "property double z" || line == "property float64 z") {
148  Z_index = index;
149  Z_byte_pos = num_bytes_per_line;
150  Z_double = (line_elems[1] == "double" || line_elems[1] == "float64");
151  } else if (line == "property float nx" || line == "property float32 nx" ||
152  line == "property double nx" ||
153  line == "property float64 nx") {
154  NX_index = index;
155  NX_byte_pos = num_bytes_per_line;
156  NX_double = (line_elems[1] == "double" || line_elems[1] == "float64");
157  } else if (line == "property float ny" || line == "property float32 ny" ||
158  line == "property double ny" ||
159  line == "property float64 ny") {
160  NY_index = index;
161  NY_byte_pos = num_bytes_per_line;
162  NY_double = (line_elems[1] == "double" || line_elems[1] == "float64");
163  } else if (line == "property float nz" || line == "property float32 nz" ||
164  line == "property double nz" ||
165  line == "property float64 nz") {
166  NZ_index = index;
167  NZ_byte_pos = num_bytes_per_line;
168  NZ_double = (line_elems[1] == "double" || line_elems[1] == "float64");
169  } else if (line == "property uchar r" || line == "property uchar red" ||
170  line == "property uchar diffuse_red" ||
171  line == "property uchar ambient_red" ||
172  line == "property uchar specular_red") {
173  R_index = index;
174  R_byte_pos = num_bytes_per_line;
175  } else if (line == "property uchar g" || line == "property uchar green" ||
176  line == "property uchar diffuse_green" ||
177  line == "property uchar ambient_green" ||
178  line == "property uchar specular_green") {
179  G_index = index;
180  G_byte_pos = num_bytes_per_line;
181  } else if (line == "property uchar b" || line == "property uchar blue" ||
182  line == "property uchar diffuse_blue" ||
183  line == "property uchar ambient_blue" ||
184  line == "property uchar specular_blue") {
185  B_index = index;
186  B_byte_pos = num_bytes_per_line;
187  }
188 
189  index += 1;
190  if (line_elems[1] == "float" || line_elems[1] == "float32") {
191  num_bytes_per_line += 4;
192  } else if (line_elems[1] == "double" || line_elems[1] == "float64") {
193  num_bytes_per_line += 8;
194  } else if (line_elems[1] == "uchar") {
195  num_bytes_per_line += 1;
196  } else {
197  LOG(FATAL) << "Invalid data type: " << line_elems[1];
198  }
199  }
200  }
201 
202  const bool is_normal_missing =
203  (NX_index == -1) || (NY_index == -1) || (NZ_index == -1);
204  const bool is_rgb_missing =
205  (R_index == -1) || (G_index == -1) || (B_index == -1);
206 
207  CHECK(X_index != -1 && Y_index != -1 && Z_index)
208  << "Invalid PLY file format: x, y, z properties missing";
209 
210  points.reserve(num_vertices);
211 
212  if (is_binary) {
213  std::vector<char> buffer(num_bytes_per_line);
214  for (size_t i = 0; i < num_vertices; ++i) {
215  file.read(buffer.data(), num_bytes_per_line);
216 
217  PlyPoint point;
218 
219  if (is_little_endian) {
220  point.x = LittleEndianToNative(
221  X_double ? *reinterpret_cast<double*>(&buffer[X_byte_pos])
222  : *reinterpret_cast<float*>(&buffer[X_byte_pos]));
223  point.y = LittleEndianToNative(
224  Y_double ? *reinterpret_cast<double*>(&buffer[Y_byte_pos])
225  : *reinterpret_cast<float*>(&buffer[Y_byte_pos]));
226  point.z = LittleEndianToNative(
227  Z_double ? *reinterpret_cast<double*>(&buffer[Z_byte_pos])
228  : *reinterpret_cast<float*>(&buffer[Z_byte_pos]));
229 
230  if (!is_normal_missing) {
231  point.nx = LittleEndianToNative(
232  NX_double ? *reinterpret_cast<double*>(&buffer[NX_byte_pos])
233  : *reinterpret_cast<float*>(&buffer[NX_byte_pos]));
234  point.ny = LittleEndianToNative(
235  NY_double ? *reinterpret_cast<double*>(&buffer[NY_byte_pos])
236  : *reinterpret_cast<float*>(&buffer[NY_byte_pos]));
237  point.nz = LittleEndianToNative(
238  NZ_double ? *reinterpret_cast<double*>(&buffer[NZ_byte_pos])
239  : *reinterpret_cast<float*>(&buffer[NZ_byte_pos]));
240  }
241 
242  if (!is_rgb_missing) {
243  point.r = LittleEndianToNative(
244  *reinterpret_cast<uint8_t*>(&buffer[R_byte_pos]));
245  point.g = LittleEndianToNative(
246  *reinterpret_cast<uint8_t*>(&buffer[G_byte_pos]));
247  point.b = LittleEndianToNative(
248  *reinterpret_cast<uint8_t*>(&buffer[B_byte_pos]));
249  }
250  } else {
251  point.x = BigEndianToNative(
252  X_double ? *reinterpret_cast<double*>(&buffer[X_byte_pos])
253  : *reinterpret_cast<float*>(&buffer[X_byte_pos]));
254  point.y = BigEndianToNative(
255  Y_double ? *reinterpret_cast<double*>(&buffer[Y_byte_pos])
256  : *reinterpret_cast<float*>(&buffer[Y_byte_pos]));
257  point.z = BigEndianToNative(
258  Z_double ? *reinterpret_cast<double*>(&buffer[Z_byte_pos])
259  : *reinterpret_cast<float*>(&buffer[Z_byte_pos]));
260 
261  if (!is_normal_missing) {
262  point.nx = BigEndianToNative(
263  NX_double ? *reinterpret_cast<double*>(&buffer[NX_byte_pos])
264  : *reinterpret_cast<float*>(&buffer[NX_byte_pos]));
265  point.ny = BigEndianToNative(
266  NY_double ? *reinterpret_cast<double*>(&buffer[NY_byte_pos])
267  : *reinterpret_cast<float*>(&buffer[NY_byte_pos]));
268  point.nz = BigEndianToNative(
269  NZ_double ? *reinterpret_cast<double*>(&buffer[NZ_byte_pos])
270  : *reinterpret_cast<float*>(&buffer[NZ_byte_pos]));
271  }
272 
273  if (!is_rgb_missing) {
274  point.r = BigEndianToNative(
275  *reinterpret_cast<uint8_t*>(&buffer[R_byte_pos]));
276  point.g = BigEndianToNative(
277  *reinterpret_cast<uint8_t*>(&buffer[G_byte_pos]));
278  point.b = BigEndianToNative(
279  *reinterpret_cast<uint8_t*>(&buffer[B_byte_pos]));
280  }
281  }
282 
283  points.push_back(point);
284  }
285  } else {
286  while (std::getline(file, line)) {
287  StringTrim(&line);
288  std::stringstream line_stream(line);
289 
290  std::string item;
291  std::vector<std::string> items;
292  while (!line_stream.eof()) {
293  std::getline(line_stream, item, ' ');
294  StringTrim(&item);
295  items.push_back(item);
296  }
297 
298  PlyPoint point;
299 
300  point.x = std::stold(items.at(X_index));
301  point.y = std::stold(items.at(Y_index));
302  point.z = std::stold(items.at(Z_index));
303 
304  if (!is_normal_missing) {
305  point.nx = std::stold(items.at(NX_index));
306  point.ny = std::stold(items.at(NY_index));
307  point.nz = std::stold(items.at(NZ_index));
308  }
309 
310  if (!is_rgb_missing) {
311  point.r = std::stoi(items.at(R_index));
312  point.g = std::stoi(items.at(G_index));
313  point.b = std::stoi(items.at(B_index));
314  }
315 
316  points.push_back(point);
317  }
318  }
319 
320  return points;
321 }
322 
323 void WriteTextPlyPoints(const std::string& path,
324  const std::vector<PlyPoint>& points,
325  const bool write_normal, const bool write_rgb) {
326  std::ofstream file(path);
327  CHECK(file.is_open()) << path;
328 
329  file << "ply" << std::endl;
330  file << "format ascii 1.0" << std::endl;
331  file << "element vertex " << points.size() << std::endl;
332 
333  file << "property float x" << std::endl;
334  file << "property float y" << std::endl;
335  file << "property float z" << std::endl;
336 
337  if (write_normal) {
338  file << "property float nx" << std::endl;
339  file << "property float ny" << std::endl;
340  file << "property float nz" << std::endl;
341  }
342 
343  if (write_rgb) {
344  file << "property uchar red" << std::endl;
345  file << "property uchar green" << std::endl;
346  file << "property uchar blue" << std::endl;
347  }
348 
349  file << "end_header" << std::endl;
350 
351  for (const auto& point : points) {
352  file << point.x << " " << point.y << " " << point.z;
353 
354  if (write_normal) {
355  file << " " << point.nx << " " << point.ny << " " << point.nz;
356  }
357 
358  if (write_rgb) {
359  file << " " << static_cast<int>(point.r) << " "
360  << static_cast<int>(point.g) << " " << static_cast<int>(point.b);
361  }
362 
363  file << std::endl;
364  }
365 
366  file.close();
367 }
368 
369 void WriteBinaryPlyPoints(const std::string& path,
370  const std::vector<PlyPoint>& points,
371  const bool write_normal, const bool write_rgb) {
372  std::fstream text_file(path, std::ios::out);
373  CHECK(text_file.is_open()) << path;
374 
375  text_file << "ply" << std::endl;
376  text_file << "format binary_little_endian 1.0" << std::endl;
377  text_file << "element vertex " << points.size() << std::endl;
378 
379  text_file << "property float x" << std::endl;
380  text_file << "property float y" << std::endl;
381  text_file << "property float z" << std::endl;
382 
383  if (write_normal) {
384  text_file << "property float nx" << std::endl;
385  text_file << "property float ny" << std::endl;
386  text_file << "property float nz" << std::endl;
387  }
388 
389  if (write_rgb) {
390  text_file << "property uchar red" << std::endl;
391  text_file << "property uchar green" << std::endl;
392  text_file << "property uchar blue" << std::endl;
393  }
394 
395  text_file << "end_header" << std::endl;
396  text_file.close();
397 
398  std::fstream binary_file(path,
399  std::ios::out | std::ios::binary | std::ios::app);
400  CHECK(binary_file.is_open()) << path;
401 
402  for (const auto& point : points) {
403  WriteBinaryLittleEndian<float>(&binary_file, point.x);
404  WriteBinaryLittleEndian<float>(&binary_file, point.y);
405  WriteBinaryLittleEndian<float>(&binary_file, point.z);
406 
407  if (write_normal) {
408  WriteBinaryLittleEndian<float>(&binary_file, point.nx);
409  WriteBinaryLittleEndian<float>(&binary_file, point.ny);
410  WriteBinaryLittleEndian<float>(&binary_file, point.nz);
411  }
412 
413  if (write_rgb) {
414  WriteBinaryLittleEndian<uint8_t>(&binary_file, point.r);
415  WriteBinaryLittleEndian<uint8_t>(&binary_file, point.g);
416  WriteBinaryLittleEndian<uint8_t>(&binary_file, point.b);
417  }
418  }
419 
420  binary_file.close();
421 }
422 
423 void WriteTextPlyMesh(const std::string& path, const PlyMesh& mesh) {
424  std::fstream file(path, std::ios::out);
425  CHECK(file.is_open());
426 
427  file << "ply" << std::endl;
428  file << "format ascii 1.0" << std::endl;
429  file << "element vertex " << mesh.vertices.size() << std::endl;
430  file << "property float x" << std::endl;
431  file << "property float y" << std::endl;
432  file << "property float z" << std::endl;
433  file << "element face " << mesh.faces.size() << std::endl;
434  file << "property list uchar int vertex_index" << std::endl;
435  file << "end_header" << std::endl;
436 
437  for (const auto& vertex : mesh.vertices) {
438  file << vertex.x << " " << vertex.y << " " << vertex.z << std::endl;
439  }
440 
441  for (const auto& face : mesh.faces) {
442  file << StringPrintf("3 %d %d %d", face.vertex_idx1, face.vertex_idx2,
443  face.vertex_idx3)
444  << std::endl;
445  }
446 }
447 
448 void WriteBinaryPlyMesh(const std::string& path, const PlyMesh& mesh) {
449  std::fstream text_file(path, std::ios::out);
450  CHECK(text_file.is_open());
451 
452  text_file << "ply" << std::endl;
453  text_file << "format binary_little_endian 1.0" << std::endl;
454  text_file << "element vertex " << mesh.vertices.size() << std::endl;
455  text_file << "property float x" << std::endl;
456  text_file << "property float y" << std::endl;
457  text_file << "property float z" << std::endl;
458  text_file << "element face " << mesh.faces.size() << std::endl;
459  text_file << "property list uchar int vertex_index" << std::endl;
460  text_file << "end_header" << std::endl;
461  text_file.close();
462 
463  std::fstream binary_file(path,
464  std::ios::out | std::ios::binary | std::ios::app);
465  CHECK(binary_file.is_open()) << path;
466 
467  for (const auto& vertex : mesh.vertices) {
468  WriteBinaryLittleEndian<float>(&binary_file, vertex.x);
469  WriteBinaryLittleEndian<float>(&binary_file, vertex.y);
470  WriteBinaryLittleEndian<float>(&binary_file, vertex.z);
471  }
472 
473  for (const auto& face : mesh.faces) {
474  CHECK_LT(face.vertex_idx1, mesh.vertices.size());
475  CHECK_LT(face.vertex_idx2, mesh.vertices.size());
476  CHECK_LT(face.vertex_idx3, mesh.vertices.size());
477  const uint8_t kNumVertices = 3;
478  WriteBinaryLittleEndian<uint8_t>(&binary_file, kNumVertices);
479  WriteBinaryLittleEndian<int>(&binary_file, face.vertex_idx1);
480  WriteBinaryLittleEndian<int>(&binary_file, face.vertex_idx2);
481  WriteBinaryLittleEndian<int>(&binary_file, face.vertex_idx3);
482  }
483 
484  binary_file.close();
485 }
486 
487 } // namespace colmap
int points
std::vector< unsigned int > face
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
static const std::string path
Definition: PointCloud.cpp:59
void StringTrim(std::string *str)
Definition: string.cc:188
void WriteTextPlyMesh(const std::string &path, const PlyMesh &mesh)
Definition: ply.cc:423
void WriteBinaryPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
Definition: ply.cc:369
T LittleEndianToNative(const T x)
Definition: endian.h:75
std::vector< std::string > StringSplit(const std::string &str, const std::string &delim)
Definition: string.cc:166
T BigEndianToNative(const T x)
Definition: endian.h:84
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
void WriteTextPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
Definition: ply.cc:323
void WriteBinaryPlyMesh(const std::string &path, const PlyMesh &mesh)
Definition: ply.cc:448
std::vector< PlyPoint > ReadPly(const std::string &path)
Definition: ply.cc:43
std::vector< PlyMeshFace > faces
Definition: ply.h:55
std::vector< PlyMeshVertex > vertices
Definition: ply.h:54
float nz
Definition: ply.h:23
uint8_t b
Definition: ply.h:26
float nx
Definition: ply.h:21
float z
Definition: ply.h:20
uint8_t r
Definition: ply.h:24
uint8_t g
Definition: ply.h:25
float ny
Definition: ply.h:22
float x
Definition: ply.h:18
float y
Definition: ply.h:19