ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FileIOFactory.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <Eigen.h>
9 #include <FileSystem.h>
10 #include <Helper.h>
11 #include <Logging.h>
12 #include <ProgressReporters.h>
13 #include <liblzf/lzf.h>
14 #include <rply.h>
15 
16 #include <cstdint>
17 #include <cstdio>
18 #include <sstream>
19 
20 #include "io/FileFormatIO.h"
21 #include "io/PointCloudIO.h"
22 
23 // CV_IO_LIB
24 #include <ecvMesh.h>
25 #include <ecvPointCloud.h>
26 #include <ecvScalarField.h>
27 
28 // QT
29 #include <QFileInfo>
30 
31 // References for PCD file IO
32 // http://pointclouds.org/documentation/tutorials/pcd_file_format.php
33 // https://github.com/PointCloudLibrary/pcl/blob/master/io/src/pcd_io.cpp
34 // https://www.mathworks.com/matlabcentral/fileexchange/40382-matlab-to-point-cloud-library
35 
36 namespace cloudViewer {
37 
38 namespace {
39 using namespace io;
40 using namespace cloudViewer;
41 
42 namespace ply_pointcloud_reader {
43 
44 struct PLYReaderState {
48  long vertex_num;
50  long normal_num;
52  long color_num;
53  double normal[3];
54 };
55 
56 int ReadVertexCallback(p_ply_argument argument) {
57  PLYReaderState *state_ptr;
58  long index;
59  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
60  &index);
61  if (state_ptr->vertex_index >= state_ptr->vertex_num) {
62  return 0; // some sanity check
63  }
64 
65  double value = ply_get_argument_value(argument);
66  CCVector3 &P = *state_ptr->pointcloud_ptr->getPointPtr(
67  static_cast<size_t>(state_ptr->vertex_index));
68  P(index) = static_cast<PointCoordinateType>(value);
69  if (index == 2) { // reading 'z'
70  state_ptr->vertex_index++;
71  ++(*state_ptr->progress_bar);
72  }
73  return 1;
74 }
75 
76 int ReadNormalCallback(p_ply_argument argument) {
77  PLYReaderState *state_ptr;
78  long index;
79  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
80  &index);
81  if (state_ptr->normal_index >= state_ptr->normal_num) {
82  return 0;
83  }
84 
85  double value = ply_get_argument_value(argument);
86  state_ptr->normal[index] = value;
87 
88  if (index == 2) { // reading 'nz'
89  state_ptr->pointcloud_ptr->addEigenNorm(
90  Eigen::Vector3d(state_ptr->normal[0], state_ptr->normal[1],
91  state_ptr->normal[2]));
92  state_ptr->normal_index++;
93  }
94  return 1;
95 }
96 
97 int ReadColorCallback(p_ply_argument argument) {
98  PLYReaderState *state_ptr;
99  long index;
100  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
101  &index);
102  if (state_ptr->color_index >= state_ptr->color_num) {
103  return 0;
104  }
105 
106  double value = ply_get_argument_value(argument);
107  ecvColor::Rgb &C = state_ptr->pointcloud_ptr->getPointColorPtr(
108  static_cast<size_t>(state_ptr->color_index));
109  C(index) = static_cast<ColorCompType>(value);
110 
111  if (index == 2) { // reading 'blue'
112  state_ptr->color_index++;
113  }
114  return 1;
115 }
116 
117 } // namespace ply_pointcloud_reader
118 
119 /********************************* PCD UTILITY
120  * *************************************/
121 enum PCDDataType {
122  PCD_DATA_ASCII = 0,
123  PCD_DATA_BINARY = 1,
124  PCD_DATA_BINARY_COMPRESSED = 2
125 };
126 
127 struct PCLPointField {
128 public:
129  std::string name;
130  int size;
131  char type;
132  int count;
133  // helper variable
135  int offset;
136 };
137 
138 struct PCDHeader {
139 public:
140  std::string version;
141  std::vector<PCLPointField> fields;
142  int width;
143  int height;
144  int points;
146  std::string viewpoint;
147  // helper variables
153  bool has_ring;
155 };
156 
157 bool CheckHeader(PCDHeader &header) {
158  if (header.points <= 0 || header.pointsize <= 0) {
159  utility::LogWarning("[CheckHeader] PCD has no data.");
160  return false;
161  }
162  if (header.fields.size() == 0 || header.pointsize <= 0) {
163  utility::LogWarning("[CheckHeader] PCD has no fields.");
164  return false;
165  }
166  header.has_points = false;
167  header.has_normals = false;
168  header.has_colors = false;
169  header.has_ring = false;
170  header.has_intensity = false;
171  bool has_x = false;
172  bool has_y = false;
173  bool has_z = false;
174  bool has_normal_x = false;
175  bool has_normal_y = false;
176  bool has_normal_z = false;
177  bool has_rgb = false;
178  bool has_rgba = false;
179  bool has_ring = false;
180  bool has_intensity = false;
181  for (const auto &field : header.fields) {
182  if (field.name == "x") {
183  has_x = true;
184  } else if (field.name == "y") {
185  has_y = true;
186  } else if (field.name == "z") {
187  has_z = true;
188  } else if (field.name == "normal_x") {
189  has_normal_x = true;
190  } else if (field.name == "normal_y") {
191  has_normal_y = true;
192  } else if (field.name == "normal_z") {
193  has_normal_z = true;
194  } else if (field.name == "rgb") {
195  has_rgb = true;
196  } else if (field.name == "rgba") {
197  has_rgba = true;
198  } else if (field.name == "intensity") {
199  has_intensity = true;
200  } else if (field.name == "ring") {
201  has_ring = true;
202  }
203  }
204  header.has_points = (has_x && has_y && has_z);
205  header.has_normals = (has_normal_x && has_normal_y && has_normal_z);
206  header.has_colors = (has_rgb || has_rgba);
207  header.has_intensity = has_intensity;
208  header.has_ring = has_ring;
209  if (header.has_points == false) {
211  "[CheckHeader] Fields for point data are not complete.");
212  return false;
213  }
214  return true;
215 }
216 
217 bool ReadPCDHeader(FILE *file, PCDHeader &header) {
218  char line_buffer[DEFAULT_IO_BUFFER_SIZE];
219  size_t specified_channel_count = 0;
220 
221  while (fgets(line_buffer, DEFAULT_IO_BUFFER_SIZE, file)) {
222  std::string line(line_buffer);
223  if (line == "") {
224  continue;
225  }
226  std::vector<std::string> st;
227  utility::SplitString(st, line, "\t\r\n ");
228  std::stringstream sstream(line);
229  sstream.imbue(std::locale::classic());
230  std::string line_type;
231  sstream >> line_type;
232  if (line_type.substr(0, 1) == "#") {
233  } else if (line_type.substr(0, 7) == "VERSION") {
234  if (st.size() >= 2) {
235  header.version = st[1];
236  }
237  } else if (line_type.substr(0, 6) == "FIELDS" ||
238  line_type.substr(0, 7) == "COLUMNS") {
239  specified_channel_count = st.size() - 1;
240  if (specified_channel_count == 0) {
241  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
242  return false;
243  }
244  header.fields.resize(specified_channel_count);
245  int count_offset = 0, offset = 0;
246  for (size_t i = 0; i < specified_channel_count;
247  i++, count_offset += 1, offset += 4) {
248  header.fields[i].name = st[i + 1];
249  header.fields[i].size = 4;
250  header.fields[i].type = 'F';
251  header.fields[i].count = 1;
252  header.fields[i].count_offset = count_offset;
253  header.fields[i].offset = offset;
254  }
255  header.elementnum = count_offset;
256  header.pointsize = offset;
257  } else if (line_type.substr(0, 4) == "SIZE") {
258  if (specified_channel_count != st.size() - 1) {
259  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
260  return false;
261  }
262  int offset = 0, col_type = 0;
263  for (size_t i = 0; i < specified_channel_count;
264  i++, offset += col_type) {
265  sstream >> col_type;
266  header.fields[i].size = col_type;
267  header.fields[i].offset = offset;
268  }
269  header.pointsize = offset;
270  } else if (line_type.substr(0, 4) == "TYPE") {
271  if (specified_channel_count != st.size() - 1) {
272  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
273  return false;
274  }
275  for (size_t i = 0; i < specified_channel_count; i++) {
276  header.fields[i].type = st[i + 1].c_str()[0];
277  }
278  } else if (line_type.substr(0, 5) == "COUNT") {
279  if (specified_channel_count != st.size() - 1) {
280  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
281  return false;
282  }
283  int count_offset = 0, offset = 0, col_count = 0;
284  for (size_t i = 0; i < specified_channel_count; i++) {
285  sstream >> col_count;
286  header.fields[i].count = col_count;
287  header.fields[i].count_offset = count_offset;
288  header.fields[i].offset = offset;
289  count_offset += col_count;
290  offset += col_count * header.fields[i].size;
291  }
292  header.elementnum = count_offset;
293  header.pointsize = offset;
294  } else if (line_type.substr(0, 5) == "WIDTH") {
295  sstream >> header.width;
296  } else if (line_type.substr(0, 6) == "HEIGHT") {
297  sstream >> header.height;
298  header.points = header.width * header.height;
299  } else if (line_type.substr(0, 9) == "VIEWPOINT") {
300  if (st.size() >= 2) {
301  header.viewpoint = st[1];
302  }
303  } else if (line_type.substr(0, 6) == "POINTS") {
304  sstream >> header.points;
305  } else if (line_type.substr(0, 4) == "DATA") {
306  header.datatype = PCD_DATA_ASCII;
307  if (st.size() >= 2) {
308  if (st[1].substr(0, 17) == "binary_compressed") {
309  header.datatype = PCD_DATA_BINARY_COMPRESSED;
310  } else if (st[1].substr(0, 6) == "binary") {
311  header.datatype = PCD_DATA_BINARY;
312  }
313  }
314  break;
315  }
316  }
317  if (CheckHeader(header) == false) {
318  return false;
319  }
320  return true;
321 }
322 
323 PointCoordinateType UnpackBinaryPCDElement(const char *data_ptr,
324  const char type,
325  const int size) {
326  const char type_uppercase = std::toupper(type, std::locale());
327  if (type_uppercase == 'I') {
328  if (size == 1) {
329  std::int8_t data;
330  memcpy(&data, data_ptr, sizeof(data));
331  return static_cast<PointCoordinateType>(data);
332  } else if (size == 2) {
333  std::int16_t data;
334  memcpy(&data, data_ptr, sizeof(data));
335  return static_cast<PointCoordinateType>(data);
336  } else if (size == 4) {
337  std::int32_t data;
338  memcpy(&data, data_ptr, sizeof(data));
339  return static_cast<PointCoordinateType>(data);
340  } else {
341  return static_cast<PointCoordinateType>(0.0);
342  }
343  } else if (type_uppercase == 'U') {
344  if (size == 1) {
345  std::uint8_t data;
346  memcpy(&data, data_ptr, sizeof(data));
347  return static_cast<PointCoordinateType>(data);
348  } else if (size == 2) {
349  std::uint16_t data;
350  memcpy(&data, data_ptr, sizeof(data));
351  return static_cast<PointCoordinateType>(data);
352  } else if (size == 4) {
353  std::uint32_t data;
354  memcpy(&data, data_ptr, sizeof(data));
355  return static_cast<PointCoordinateType>(data);
356  } else {
357  return static_cast<PointCoordinateType>(0.0);
358  }
359  } else if (type_uppercase == 'F') {
360  if (size == 4) {
361  float data;
362  memcpy(&data, data_ptr, sizeof(data));
363  return static_cast<PointCoordinateType>(data);
364  } else {
365  return static_cast<PointCoordinateType>(0.0);
366  }
367  }
368  return static_cast<PointCoordinateType>(0.0);
369 }
370 
371 ecvColor::Rgb UnpackBinaryPCDColor(const char *data_ptr,
372  const char type,
373  const int size) {
374  if (size == 4) {
375  std::uint8_t data[4];
376  memcpy(data, data_ptr, 4);
377  // color data is packed in BGR order.
378  return ecvColor::Rgb(data[2], data[1], data[0]);
379  } else {
380  return ecvColor::Rgb();
381  }
382 }
383 
384 ScalarType UnpackBinaryPCDScalar(const char *data_ptr,
385  const char type,
386  const int size) {
387  if (size == 1) {
388  std::uint8_t data;
389  memcpy(&data, data_ptr, 1);
390  ScalarType scalar = static_cast<ScalarType>(data);
391  return scalar;
392  } else {
393  return static_cast<ScalarType>(0);
394  }
395 }
396 
397 PointCoordinateType UnpackASCIIPCDElement(const char *data_ptr,
398  const char type,
399  const int size) {
400  char *end;
401  const char type_uppercase = std::toupper(type, std::locale());
402  if (type_uppercase == 'I') {
403  return static_cast<PointCoordinateType>(std::strtol(data_ptr, &end, 0));
404  } else if (type_uppercase == 'U') {
405  return static_cast<PointCoordinateType>(
406  std::strtoul(data_ptr, &end, 0));
407  } else if (type_uppercase == 'F') {
408  return static_cast<PointCoordinateType>(std::strtod(data_ptr, &end));
409  }
410  return static_cast<PointCoordinateType>(0.0);
411 }
412 
413 ecvColor::Rgb UnpackASCIIPCDColor(const char *data_ptr,
414  const char type,
415  const int size) {
416  if (size == 4) {
417  std::uint8_t data[4] = {0, 0, 0, 0};
418  char *end;
419  const char type_uppercase = std::toupper(type, std::locale());
420  if (type_uppercase == 'I') {
421  std::int32_t value = std::strtol(data_ptr, &end, 0);
422  memcpy(data, &value, 4);
423  } else if (type_uppercase == 'U') {
424  std::uint32_t value = std::strtoul(data_ptr, &end, 0);
425  memcpy(data, &value, 4);
426  } else if (type_uppercase == 'F') {
427  float value = std::strtof(data_ptr, &end);
428  memcpy(data, &value, 4);
429  }
430  return ecvColor::Rgb(data[2], data[1], data[0]);
431  } else {
432  return ecvColor::Rgb();
433  }
434 }
435 
436 ScalarType UnpackASCIIPCDScalar(const char *data_ptr,
437  const char type,
438  const int size) {
439  if (size == 1) {
440  std::uint8_t data = 0;
441  char *end;
442  if (type == 'I') {
443  std::int32_t value = std::strtol(data_ptr, &end, 0);
444  memcpy(&data, &value, 1);
445  } else if (type == 'U') {
446  std::uint32_t value = std::strtoul(data_ptr, &end, 0);
447  memcpy(&data, &value, 1);
448  } else if (type == 'F') {
449  float value = std::strtof(data_ptr, &end);
450  memcpy(&data, &value, 1);
451  }
452  ScalarType scalar = static_cast<ScalarType>(data);
453  return scalar;
454  } else {
455  return static_cast<ScalarType>(0);
456  }
457 }
458 
459 bool ReadPCDData(FILE *file,
460  const PCDHeader &header,
461  ccPointCloud &pointcloud,
462  const ReadPointCloudOption &params) {
463  pointcloud.clear();
464  // The header should have been checked
465  if (header.has_points) {
466  pointcloud.resize(static_cast<unsigned int>(header.points));
467  } else {
469  "[ReadPCDData] Fields for point data are not complete.");
470  return false;
471  }
472  if (header.has_normals) {
473  pointcloud.reserveTheNormsTable();
474  }
475  if (header.has_colors) {
476  pointcloud.resizeTheRGBTable();
477  }
478 
479  // if the input field already exists...
480  size_t pointCount = static_cast<size_t>(header.points);
481 
482  // create new scalar field
483  ccScalarField *cc_intensity_field = nullptr;
484  if (header.has_intensity) {
485  int id = pointcloud.getScalarFieldIndexByName("intensity");
486  if (id >= 0) {
487  pointcloud.deleteScalarField(id);
488  }
489 
490  cc_intensity_field = new ccScalarField("intensity");
491  if (!cc_intensity_field->reserveSafe(
492  static_cast<unsigned>(pointCount))) {
493  cc_intensity_field->release();
494  return false;
495  }
496  }
497  ccScalarField *cc_ring_field = nullptr;
498  if (header.has_ring) {
499  int id = pointcloud.getScalarFieldIndexByName("ring");
500  if (id >= 0) {
501  pointcloud.deleteScalarField(id);
502  }
503  cc_ring_field = new ccScalarField("ring");
504  if (!cc_ring_field->reserveSafe(static_cast<unsigned>(pointCount))) {
505  cc_ring_field->release();
506  return false;
507  }
508  }
509 
510  utility::CountingProgressReporter reporter(params.update_progress);
511  reporter.SetTotal(header.points);
512 
513  CCVector3 P(0, 0, 0);
514  CCVector3 N(0, 0, 0);
515  ecvColor::Rgb col;
516  ScalarType ring;
517  ScalarType intensity;
518 
519  if (header.datatype == PCD_DATA_ASCII) {
520  char line_buffer[DEFAULT_IO_BUFFER_SIZE];
521  unsigned int idx = 0;
522  while (fgets(line_buffer, DEFAULT_IO_BUFFER_SIZE, file) &&
523  idx < (unsigned int)header.points) {
524  std::string line(line_buffer);
525  std::vector<std::string> strs;
526  utility::SplitString(strs, line, "\t\r\n ");
527  if ((int)strs.size() < header.elementnum) {
528  continue;
529  }
530 
531  bool find_point = false;
532  bool find_normal = false;
533  bool find_color = false;
534  bool find_ring = false;
535  bool find_intensity = false;
536  for (size_t i = 0; i < header.fields.size(); i++) {
537  const auto &field = header.fields[i];
538  if (field.name == "x") {
539  find_point = true;
541  strs[field.count_offset].c_str(), field.type,
542  field.size);
543  } else if (field.name == "y") {
544  find_point = true;
546  strs[field.count_offset].c_str(), field.type,
547  field.size);
548  } else if (field.name == "z") {
549  find_point = true;
551  strs[field.count_offset].c_str(), field.type,
552  field.size);
553  } else if (field.name == "normal_x") {
554  find_normal = true;
555  N.x = UnpackASCIIPCDElement(
556  strs[field.count_offset].c_str(), field.type,
557  field.size);
558  } else if (field.name == "normal_y") {
559  find_normal = true;
560  N.y = UnpackASCIIPCDElement(
561  strs[field.count_offset].c_str(), field.type,
562  field.size);
563  } else if (field.name == "normal_z") {
564  find_normal = true;
565  N.z = UnpackASCIIPCDElement(
566  strs[field.count_offset].c_str(), field.type,
567  field.size);
568  } else if (field.name == "rgb" || field.name == "rgba") {
569  find_color = true;
570  col = UnpackASCIIPCDColor(strs[field.count_offset].c_str(),
571  field.type, field.size);
572  } else if (field.name == "intensity") {
573  find_intensity = true;
574  intensity = UnpackASCIIPCDScalar(
575  strs[field.count_offset].c_str(), field.type,
576  field.size);
577  } else if (field.name == "ring") {
578  find_ring = true;
579  ring = UnpackASCIIPCDScalar(
580  strs[field.count_offset].c_str(), field.type,
581  field.size);
582  }
583  }
584 
585  if (find_point) {
586  pointcloud.setPoint(idx, P);
587  }
588  if (header.has_normals && find_normal) {
589  pointcloud.addNorm(N);
590  }
591  if (header.has_colors && find_color) {
592  pointcloud.setPointColor(idx, col);
593  }
594  if (header.has_intensity && find_intensity) {
595  cc_intensity_field->addElement(intensity);
596  }
597  if (header.has_ring && find_ring) {
598  cc_ring_field->addElement(ring);
599  }
600 
601  idx++;
602  if (idx % 1000 == 0) {
603  reporter.Update(idx);
604  }
605  }
606  } else if (header.datatype == PCD_DATA_BINARY) {
607  std::unique_ptr<char[]> buffer(new char[header.pointsize]);
608  for (unsigned int i = 0; i < (unsigned int)header.points; i++) {
609  if (fread(buffer.get(), header.pointsize, 1, file) != 1) {
611  "[ReadPCDData] Failed to read data record.");
612  pointcloud.clear();
613  return false;
614  }
615 
616  bool find_point = false;
617  bool find_normal = false;
618  bool find_color = false;
619  bool find_ring = false;
620  bool find_intensity = false;
621  for (const auto &field : header.fields) {
622  if (field.name == "x") {
623  find_point = true;
624  P.x = UnpackBinaryPCDElement(buffer.get() + field.offset,
625  field.type, field.size);
626  } else if (field.name == "y") {
627  find_point = true;
628  P.y = UnpackBinaryPCDElement(buffer.get() + field.offset,
629  field.type, field.size);
630  } else if (field.name == "z") {
631  find_point = true;
632  P.z = UnpackBinaryPCDElement(buffer.get() + field.offset,
633  field.type, field.size);
634  } else if (field.name == "normal_x") {
635  find_normal = true;
636  N.x = UnpackBinaryPCDElement(buffer.get() + field.offset,
637  field.type, field.size);
638  } else if (field.name == "normal_y") {
639  find_normal = true;
640  N.y = UnpackBinaryPCDElement(buffer.get() + field.offset,
641  field.type, field.size);
642  } else if (field.name == "normal_z") {
643  find_normal = true;
644  N.z = UnpackBinaryPCDElement(buffer.get() + field.offset,
645  field.type, field.size);
646  } else if (field.name == "rgb" || field.name == "rgba") {
647  find_color = true;
648  col = UnpackBinaryPCDColor(buffer.get() + field.offset,
649  field.type, field.size);
650  } else if (field.name == "intensity") {
651  find_intensity = true;
652  intensity =
653  UnpackBinaryPCDScalar(buffer.get() + field.offset,
654  field.type, field.size);
655  } else if (field.name == "ring") {
656  find_ring = true;
657  ring = UnpackBinaryPCDScalar(buffer.get() + field.offset,
658  field.type, field.size);
659  }
660  }
661 
662  if (find_point) {
663  pointcloud.setPoint(i, P);
664  }
665  if (header.has_normals && find_normal) {
666  pointcloud.addNorm(N);
667  }
668  if (header.has_colors && find_color) {
669  pointcloud.setPointColor(i, col);
670  }
671  if (header.has_intensity && find_intensity) {
672  cc_intensity_field->addElement(intensity);
673  }
674  if (header.has_ring && find_ring) {
675  cc_ring_field->addElement(ring);
676  }
677 
678  if (i % 1000 == 0) {
679  reporter.Update(i);
680  }
681  }
682  } else if (header.datatype == PCD_DATA_BINARY_COMPRESSED) {
683  double reporter_total = 100.0;
684  reporter.SetTotal(int(reporter_total));
685  reporter.Update(int(reporter_total * 0.01));
686 
687  std::uint32_t compressed_size;
688  std::uint32_t uncompressed_size;
689  if (fread(&compressed_size, sizeof(compressed_size), 1, file) != 1) {
690  utility::LogWarning("[ReadPCDData] Failed to read data record.");
691  pointcloud.clear();
692  return false;
693  }
694  if (fread(&uncompressed_size, sizeof(uncompressed_size), 1, file) !=
695  1) {
696  utility::LogWarning("[ReadPCDData] Failed to read data record.");
697  pointcloud.clear();
698  return false;
699  }
701  "PCD data with {:d} compressed size, and {:d} uncompressed "
702  "size.",
703  compressed_size, uncompressed_size);
704  std::unique_ptr<char[]> buffer_compressed(new char[compressed_size]);
705  reporter.Update(int(reporter_total * .1));
706  if (fread(buffer_compressed.get(), 1, compressed_size, file) !=
707  compressed_size) {
708  utility::LogWarning("[ReadPCDData] Failed to read data record.");
709  pointcloud.clear();
710  return false;
711  }
712  std::unique_ptr<char[]> buffer(new char[uncompressed_size]);
713  reporter.Update(int(reporter_total * .2));
714  if (lzf_decompress(buffer_compressed.get(),
715  (unsigned int)compressed_size, buffer.get(),
716  (unsigned int)uncompressed_size) !=
717  uncompressed_size) {
718  utility::LogWarning("[ReadPCDData] Uncompression failed.");
719  pointcloud.clear();
720  return false;
721  }
722 
723  std::vector<CCVector3> normals;
724  if (header.has_normals) {
725  normals.resize(static_cast<unsigned int>(header.points));
726  }
727 
728  for (const auto &field : header.fields) {
729  const char *base_ptr = buffer.get() + field.offset * header.points;
730  double progress =
731  double(base_ptr - buffer.get()) / uncompressed_size;
732  reporter.Update(int(reporter_total * (progress + .2)));
733 
734  if (field.name == "x") {
735  for (size_t i = 0; i < (size_t)header.points; i++) {
736  pointcloud.getPointPtr(i)->x = UnpackBinaryPCDElement(
737  base_ptr + i * field.size * field.count, field.type,
738  field.size);
739  }
740  } else if (field.name == "y") {
741  for (size_t i = 0; i < (size_t)header.points; i++) {
742  pointcloud.getPointPtr(i)->y = UnpackBinaryPCDElement(
743  base_ptr + i * field.size * field.count, field.type,
744  field.size);
745  }
746  } else if (field.name == "z") {
747  for (size_t i = 0; i < (size_t)header.points; i++) {
748  pointcloud.getPointPtr(i)->z = UnpackBinaryPCDElement(
749  base_ptr + i * field.size * field.count, field.type,
750  field.size);
751  }
752  } else if (field.name == "normal_x") {
753  for (size_t i = 0; i < (size_t)header.points; i++) {
754  normals[i].x = UnpackBinaryPCDElement(
755  base_ptr + i * field.size * field.count, field.type,
756  field.size);
757  }
758  } else if (field.name == "normal_y") {
759  for (size_t i = 0; i < (size_t)header.points; i++) {
760  normals[i].y = UnpackBinaryPCDElement(
761  base_ptr + i * field.size * field.count, field.type,
762  field.size);
763  }
764  } else if (field.name == "normal_z") {
765  for (size_t i = 0; i < (size_t)header.points; i++) {
766  normals[i].z = UnpackBinaryPCDElement(
767  base_ptr + i * field.size * field.count, field.type,
768  field.size);
769  }
770  } else if (field.name == "rgb" || field.name == "rgba") {
771  for (unsigned int i = 0; i < (unsigned int)header.points; i++) {
772  ecvColor::Rgb color = UnpackBinaryPCDColor(
773  base_ptr + i * field.size * field.count, field.type,
774  field.size);
775  pointcloud.setPointColor(i, color);
776  }
777  } else if (field.name == "intensity") {
778  for (unsigned int i = 0; i < (unsigned int)header.points; i++) {
779  ScalarType intensity = UnpackBinaryPCDScalar(
780  base_ptr + i * field.size * field.count, field.type,
781  field.size);
782  cc_intensity_field->addElement(intensity);
783  }
784  } else if (field.name == "ring") {
785  for (unsigned int i = 0; i < (unsigned int)header.points; i++) {
786  ScalarType ring = UnpackBinaryPCDScalar(
787  base_ptr + i * field.size * field.count, field.type,
788  field.size);
789  cc_ring_field->addElement(ring);
790  }
791  }
792  }
793 
794  if (header.has_normals) {
795  pointcloud.addNorms(normals);
796  }
797  }
798 
799  if (header.has_intensity && cc_intensity_field) {
800  cc_intensity_field->computeMinAndMax();
801  pointcloud.addScalarField(cc_intensity_field);
802  pointcloud.setCurrentDisplayedScalarField(0);
803  pointcloud.showSF(true);
804  }
805 
806  if (header.has_ring && cc_ring_field) {
807  cc_ring_field->computeMinAndMax();
808  pointcloud.addScalarField(cc_ring_field);
809  pointcloud.setCurrentDisplayedScalarField(0);
810  pointcloud.showSF(true);
811  }
812 
813  reporter.Finish();
814  return true;
815 }
816 
817 bool GenerateHeader(const ccPointCloud &pointcloud,
818  const bool write_ascii,
819  const bool compressed,
820  PCDHeader &header) {
821  if (!pointcloud.hasPoints()) {
822  return false;
823  }
824 
825  header.version = "0.7";
826  header.width = (int)pointcloud.size();
827  header.height = 1;
828  header.points = header.width;
829  header.fields.clear();
830  PCLPointField field;
831  field.type = 'F';
832  field.size = 4;
833  field.count = 1;
834  field.name = "x";
835  header.fields.push_back(field);
836  field.name = "y";
837  header.fields.push_back(field);
838  field.name = "z";
839  header.fields.push_back(field);
840  header.elementnum = 3;
841  header.pointsize = 12;
842  if (pointcloud.hasNormals()) {
843  field.name = "normal_x";
844  header.fields.push_back(field);
845  field.name = "normal_y";
846  header.fields.push_back(field);
847  field.name = "normal_z";
848  header.fields.push_back(field);
849  header.elementnum += 3;
850  header.pointsize += 12;
851  }
852  if (pointcloud.hasColors()) {
853  field.name = "rgb";
854  header.fields.push_back(field);
855  header.elementnum++;
856  header.pointsize += 4;
857  }
858  if (write_ascii) {
859  header.datatype = PCD_DATA_ASCII;
860  } else {
861  if (compressed) {
862  header.datatype = PCD_DATA_BINARY_COMPRESSED;
863  } else {
864  header.datatype = PCD_DATA_BINARY;
865  }
866  }
867  return true;
868 }
869 
870 bool WritePCDHeader(FILE *file, const PCDHeader &header) {
871  fprintf(file, "# .PCD v%s - Point Cloud Data file format\n",
872  header.version.c_str());
873  fprintf(file, "VERSION %s\n", header.version.c_str());
874  fprintf(file, "FIELDS");
875  for (const auto &field : header.fields) {
876  fprintf(file, " %s", field.name.c_str());
877  }
878  fprintf(file, "\n");
879  fprintf(file, "SIZE");
880  for (const auto &field : header.fields) {
881  fprintf(file, " %d", field.size);
882  }
883  fprintf(file, "\n");
884  fprintf(file, "TYPE");
885  for (const auto &field : header.fields) {
886  fprintf(file, " %c", field.type);
887  }
888  fprintf(file, "\n");
889  fprintf(file, "COUNT");
890  for (const auto &field : header.fields) {
891  fprintf(file, " %d", field.count);
892  }
893  fprintf(file, "\n");
894  fprintf(file, "WIDTH %d\n", header.width);
895  fprintf(file, "HEIGHT %d\n", header.height);
896  fprintf(file, "VIEWPOINT 0 0 0 1 0 0 0\n");
897  fprintf(file, "POINTS %d\n", header.points);
898 
899  switch (header.datatype) {
900  case PCD_DATA_BINARY:
901  fprintf(file, "DATA binary\n");
902  break;
903  case PCD_DATA_BINARY_COMPRESSED:
904  fprintf(file, "DATA binary_compressed\n");
905  break;
906  case PCD_DATA_ASCII:
907  default:
908  fprintf(file, "DATA ascii\n");
909  break;
910  }
911  return true;
912 }
913 
914 float ConvertRGBToFloat(const ecvColor::Rgb &color) {
915  std::uint8_t rgba[4] = {0, 0, 0, 0};
916  rgba[2] = (std::uint8_t)std::max(std::min((int)color.r, 255), 0);
917  rgba[1] = (std::uint8_t)std::max(std::min((int)color.g, 255), 0);
918  rgba[0] = (std::uint8_t)std::max(std::min((int)color.b, 255), 0);
919  float value;
920  memcpy(&value, rgba, 4);
921  return value;
922 }
923 
924 bool WritePCDData(FILE *file,
925  const PCDHeader &header,
926  const ccPointCloud &pointcloud,
927  const WritePointCloudOption &params) {
928  bool has_normal = pointcloud.hasNormals();
929  bool has_color = pointcloud.hasColors();
930 
931  utility::CountingProgressReporter reporter(params.update_progress);
932  reporter.SetTotal(pointcloud.size());
933 
934  if (header.datatype == PCD_DATA_ASCII) {
935  for (unsigned int i = 0; i < pointcloud.size(); i++) {
936  const auto &point = *pointcloud.getPoint(i);
937  fprintf(file, "%.10g %.10g %.10g", point.x, point.y, point.z);
938  if (has_normal) {
939  const auto &normal = pointcloud.getPointNormal(i);
940  fprintf(file, " %.10g %.10g %.10g", normal.x, normal.y,
941  normal.z);
942  }
943  if (has_color) {
944  const auto &color = pointcloud.getPointColor(i);
945  fprintf(file, " %.10g", ConvertRGBToFloat(color));
946  }
947  fprintf(file, "\n");
948  if (i % 1000 == 0) {
949  reporter.Update(i);
950  }
951  }
952  } else if (header.datatype == PCD_DATA_BINARY) {
953  std::unique_ptr<float[]> data(new float[header.elementnum]);
954  for (unsigned int i = 0; i < pointcloud.size(); i++) {
955  const auto &point = *pointcloud.getPoint(i);
956  data[0] = (float)point[0];
957  data[1] = (float)point[1];
958  data[2] = (float)point[2];
959  int idx = 3;
960  if (has_normal) {
961  const auto &normal = pointcloud.getPointNormal(i);
962  data[idx + 0] = (float)normal[0];
963  data[idx + 1] = (float)normal[1];
964  data[idx + 2] = (float)normal[2];
965  idx += 3;
966  }
967  if (has_color) {
968  const auto &color = pointcloud.getPointColor(i);
969  data[idx] = ConvertRGBToFloat(color);
970  }
971  fwrite(data.get(), sizeof(float), header.elementnum, file);
972  if (i % 1000 == 0) {
973  reporter.Update(i);
974  }
975  }
976  } else if (header.datatype == PCD_DATA_BINARY_COMPRESSED) {
977  double report_total = double(pointcloud.size() * 2);
978  // 0%-50% packing into buffer
979  // 50%-75% compressing buffer
980  // 75%-100% writing compressed buffer
981  reporter.SetTotal(int(report_total));
982 
983  int strip_size = header.points;
984  std::uint32_t buffer_size =
985  (std::uint32_t)(header.elementnum * header.points);
986  std::unique_ptr<float[]> buffer(new float[buffer_size]);
987  std::unique_ptr<float[]> buffer_compressed(new float[buffer_size * 2]);
988  for (unsigned int i = 0; i < pointcloud.size(); i++) {
989  const auto &point = *pointcloud.getPoint(i);
990  buffer[0 * strip_size + i] = (float)point(0);
991  buffer[1 * strip_size + i] = (float)point(1);
992  buffer[2 * strip_size + i] = (float)point(2);
993  int idx = 3;
994  if (has_normal) {
995  const auto &normal = pointcloud.getPointNormal(i);
996  buffer[(idx + 0) * strip_size + i] = (float)normal(0);
997  buffer[(idx + 1) * strip_size + i] = (float)normal(1);
998  buffer[(idx + 2) * strip_size + i] = (float)normal(2);
999  idx += 3;
1000  }
1001  if (has_color) {
1002  const auto &color = pointcloud.getPointColor(i);
1003  buffer[idx * strip_size + i] = ConvertRGBToFloat(color);
1004  }
1005  if (i % 1000 == 0) {
1006  reporter.Update(i);
1007  }
1008  }
1009  std::uint32_t buffer_size_in_bytes = buffer_size * sizeof(float);
1010  std::uint32_t size_compressed =
1011  lzf_compress(buffer.get(), buffer_size_in_bytes,
1012  buffer_compressed.get(), buffer_size_in_bytes * 2);
1013  if (size_compressed == 0) {
1014  utility::LogWarning("[WritePCDData] Failed to compress data.");
1015  return false;
1016  }
1018  "[WritePCDData] {:d} bytes data compressed into {:d} bytes.",
1019  buffer_size_in_bytes, size_compressed);
1020  reporter.Update(int(report_total * 0.75));
1021  fwrite(&size_compressed, sizeof(size_compressed), 1, file);
1022  fwrite(&buffer_size_in_bytes, sizeof(buffer_size_in_bytes), 1, file);
1023  fwrite(buffer_compressed.get(), 1, size_compressed, file);
1024  }
1025  reporter.Finish();
1026  return true;
1027 }
1028 
1029 /********************************* PCD UTILITY
1030  * *************************************/
1031 
1032 } // unnamed namespace
1033 
1034 namespace io {
1035 
1036 /********************************* PCD IO *************************************/
1038  return CONTAINS_POINTS;
1039 }
1040 
1041 bool ReadPointCloudFromPCD(const std::string &filename,
1042  ccPointCloud &pointcloud,
1043  const ReadPointCloudOption &params) {
1044  PCDHeader header;
1045  FILE *file = utility::filesystem::FOpen(filename.c_str(), "rb");
1046  if (file == NULL) {
1047  utility::LogWarning("Read PCD failed: unable to open file: {}",
1048  filename);
1049  return false;
1050  }
1051  if (ReadPCDHeader(file, header) == false) {
1052  utility::LogWarning("Read PCD failed: unable to parse header.");
1053  fclose(file);
1054  return false;
1055  }
1057  "PCD header indicates {:d} fields, {:d} bytes per point, and {:d} "
1058  "points in total.",
1059  (int)header.fields.size(), header.pointsize, header.points);
1060  for (const auto &field : header.fields) {
1061  utility::LogDebug("{}, {}, {:d}, {:d}, {:d}", field.name.c_str(),
1062  field.type, field.size, field.count, field.offset);
1063  }
1064  utility::LogDebug("Compression method is {:d}.", (int)header.datatype);
1065  utility::LogDebug("Points: {}; normals: {}; colors: {}",
1066  header.has_points ? "yes" : "no",
1067  header.has_normals ? "yes" : "no",
1068  header.has_colors ? "yes" : "no");
1069  if (!ReadPCDData(file, header, pointcloud, params)) {
1070  utility::LogWarning("Read PCD failed: unable to read data.");
1071  fclose(file);
1072  return false;
1073  }
1074  fclose(file);
1075  return true;
1076 }
1077 
1078 bool WritePointCloudToPCD(const std::string &filename,
1079  const ccPointCloud &pointcloud,
1080  const WritePointCloudOption &params) {
1081  PCDHeader header;
1082  if (!GenerateHeader(pointcloud, bool(params.write_ascii),
1083  bool(params.compressed), header)) {
1084  utility::LogWarning("Write PCD failed: unable to generate header.");
1085  return false;
1086  }
1087  FILE *file = utility::filesystem::FOpen(filename.c_str(), "wb");
1088  if (file == NULL) {
1089  utility::LogWarning("Write PCD failed: unable to open file.");
1090  return false;
1091  }
1092  if (!WritePCDHeader(file, header)) {
1093  utility::LogWarning("Write PCD failed: unable to write header.");
1094  fclose(file);
1095  return false;
1096  }
1097  if (!WritePCDData(file, header, pointcloud, params)) {
1098  utility::LogWarning("Write PCD failed: unable to write data.");
1099  fclose(file);
1100  return false;
1101  }
1102  fclose(file);
1103  return true;
1104 }
1105 
1106 /********************************* PCD IO *************************************/
1107 
1108 /********************************* XYZ IO *************************************/
1110  return CONTAINS_POINTS;
1111 }
1112 
1113 bool ReadPointCloudFromXYZ(const std::string &filename,
1114  ccPointCloud &pointcloud,
1115  const ReadPointCloudOption &params) {
1116  try {
1118  if (!file.Open(filename, "r")) {
1119  utility::LogWarning("Read XYZ failed: unable to open file: {}",
1120  filename);
1121  return false;
1122  }
1123  utility::CountingProgressReporter reporter(params.update_progress);
1124  reporter.SetTotal(file.GetFileSize());
1125 
1126  pointcloud.clear();
1127  int i = 0;
1128  double x, y, z;
1129  const char *line_buffer;
1130  while ((line_buffer = file.ReadLine())) {
1131  if (sscanf(line_buffer, "%lf %lf %lf", &x, &y, &z) == 3) {
1132  pointcloud.addEigenPoint(Eigen::Vector3d(x, y, z));
1133  }
1134  if (++i % 1000 == 0) {
1135  reporter.Update(file.CurPos());
1136  }
1137  }
1138  reporter.Finish();
1139 
1140  return true;
1141  } catch (const std::exception &e) {
1142  utility::LogWarning("Read XYZ failed with exception: {}", e.what());
1143  return false;
1144  }
1145 }
1146 
1147 bool ReadPointCloudInMemoryFromXYZ(const unsigned char *buffer,
1148  const size_t length,
1149  ccPointCloud &pointcloud,
1150  const ReadPointCloudOption &params) {
1151  try {
1152  utility::CountingProgressReporter reporter(params.update_progress);
1153  reporter.SetTotal(static_cast<int64_t>(length));
1154 
1155  std::string content(reinterpret_cast<const char *>(buffer), length);
1156  std::istringstream ibs(content);
1157  pointcloud.clear();
1158  int i = 0;
1159  double x, y, z;
1160  std::string line;
1161  while (std::getline(ibs, line)) {
1162  if (sscanf(line.c_str(), "%lf %lf %lf", &x, &y, &z) == 3) {
1163  pointcloud.addEigenPoint(Eigen::Vector3d(x, y, z));
1164  }
1165  if (++i % 1000 == 0) {
1166  reporter.Update(ibs.tellg());
1167  }
1168  }
1169  reporter.Finish();
1170 
1171  return true;
1172  } catch (const std::exception &e) {
1173  utility::LogWarning("Read XYZ failed with exception: {}", e.what());
1174  return false;
1175  }
1176 }
1177 
1178 bool WritePointCloudToXYZ(const std::string &filename,
1179  const ccPointCloud &pointcloud,
1180  const WritePointCloudOption &params) {
1181  try {
1183  if (!file.Open(filename, "w")) {
1184  utility::LogWarning("Write XYZ failed: unable to open file: {}",
1185  filename);
1186  return false;
1187  }
1188  utility::CountingProgressReporter reporter(params.update_progress);
1189  reporter.SetTotal(pointcloud.size());
1190 
1191  for (size_t i = 0; i < pointcloud.size(); i++) {
1192  const Eigen::Vector3d &point = pointcloud.getEigenPoint(i);
1193  if (fprintf(file.GetFILE(), "%.10f %.10f %.10f\n", point(0),
1194  point(1), point(2)) < 0) {
1196  "Write XYZ failed: unable to write file: {}", filename);
1197  return false; // error happened during writing.
1198  }
1199  if (i % 1000 == 0) {
1200  reporter.Update(i);
1201  }
1202  }
1203  reporter.Finish();
1204  return true;
1205  } catch (const std::exception &e) {
1206  utility::LogWarning("Write XYZ failed with exception: {}", e.what());
1207  return false;
1208  }
1209 }
1210 
1211 bool WritePointCloudInMemoryToXYZ(unsigned char *&buffer,
1212  size_t &length,
1213  const ccPointCloud &pointcloud,
1214  const WritePointCloudOption &params) {
1215  try {
1216  utility::CountingProgressReporter reporter(params.update_progress);
1217  reporter.SetTotal(pointcloud.size());
1218 
1219  std::string content;
1220  for (size_t i = 0; i < pointcloud.size(); i++) {
1221  const Eigen::Vector3d &point = pointcloud.getEigenPoint(i);
1222  std::string line = utility::FastFormatString(
1223  "%.10f %.10f %.10f\n", point(0), point(1), point(2));
1224  content.append(line);
1225  if (i % 1000 == 0) {
1226  reporter.Update(i);
1227  }
1228  }
1229  // nothing to report...
1230  if (content.length() == 0) {
1231  reporter.Finish();
1232  return false;
1233  }
1234  length = content.length();
1235  buffer = new unsigned char[length]; // we do this for the caller
1236  std::memcpy(buffer, content.c_str(), length);
1237 
1238  reporter.Finish();
1239  return true;
1240  } catch (const std::exception &e) {
1241  utility::LogWarning("Write XYZ failed with exception: {}", e.what());
1242  return false;
1243  }
1244 }
1245 /********************************* XYZ IO *************************************/
1246 
1247 /********************************* XYZN IO
1248  * *************************************/
1250  return CONTAINS_POINTS;
1251 }
1252 
1253 bool ReadPointCloudFromXYZN(const std::string &filename,
1254  ccPointCloud &pointcloud,
1255  const ReadPointCloudOption &params) {
1256  try {
1258  if (!file.Open(filename, "r")) {
1259  utility::LogWarning("Read XYZN failed: unable to open file: {}",
1260  filename);
1261  return false;
1262  }
1263  utility::CountingProgressReporter reporter(params.update_progress);
1264  reporter.SetTotal(file.GetFileSize());
1265 
1266  pointcloud.clear();
1267  int i = 0;
1268  double x, y, z, nx, ny, nz;
1269  const char *line_buffer;
1270  while ((line_buffer = file.ReadLine())) {
1271  if (sscanf(line_buffer, "%lf %lf %lf %lf %lf %lf", &x, &y, &z, &nx,
1272  &ny, &nz) == 6) {
1273  pointcloud.addEigenPoint(Eigen::Vector3d(x, y, z));
1274  pointcloud.addEigenNorm(Eigen::Vector3d(nx, ny, nz));
1275  }
1276  if (++i % 1000 == 0) {
1277  reporter.Update(file.CurPos());
1278  }
1279  }
1280  reporter.Finish();
1281 
1282  return true;
1283  } catch (const std::exception &e) {
1284  utility::LogWarning("Read XYZN failed with exception: {}", e.what());
1285  return false;
1286  }
1287 }
1288 
1289 bool WritePointCloudToXYZN(const std::string &filename,
1290  const ccPointCloud &pointcloud,
1291  const WritePointCloudOption &params) {
1292  if (!pointcloud.hasNormals()) {
1293  return false;
1294  }
1295 
1296  try {
1298  if (!file.Open(filename, "w")) {
1299  utility::LogWarning("Write XYZN failed: unable to open file: {}",
1300  filename);
1301  return false;
1302  }
1303  utility::CountingProgressReporter reporter(params.update_progress);
1304  reporter.SetTotal(pointcloud.size());
1305 
1306  for (size_t i = 0; i < pointcloud.size(); i++) {
1307  const Eigen::Vector3d &point = pointcloud.getEigenPoint(i);
1308  const Eigen::Vector3d &normal = pointcloud.getEigenNormal(i);
1309  if (fprintf(file.GetFILE(), "%.10f %.10f %.10f %.10f %.10f %.10f\n",
1310  point(0), point(1), point(2), normal(0), normal(1),
1311  normal(2)) < 0) {
1313  "Write XYZN failed: unable to write file: {}",
1314  filename);
1315  return false; // error happened during writing.
1316  }
1317  if (i % 1000 == 0) {
1318  reporter.Update(i);
1319  }
1320  }
1321  reporter.Finish();
1322  return true;
1323  } catch (const std::exception &e) {
1324  utility::LogWarning("Write XYZN failed with exception: {}", e.what());
1325  return false;
1326  }
1327 }
1328 /********************************* XYZN IO
1329  * *************************************/
1330 
1331 /********************************* XYZRGB IO
1332  * *************************************/
1334  return CONTAINS_POINTS;
1335 }
1336 
1337 bool ReadPointCloudFromXYZRGB(const std::string &filename,
1338  ccPointCloud &pointcloud,
1339  const ReadPointCloudOption &params) {
1340  try {
1342  if (!file.Open(filename, "r")) {
1343  utility::LogWarning("Read XYZRGB failed: unable to open file: {}",
1344  filename);
1345  return false;
1346  }
1347  utility::CountingProgressReporter reporter(params.update_progress);
1348  reporter.SetTotal(file.GetFileSize());
1349 
1350  pointcloud.clear();
1351  int i = 0;
1352  double x, y, z, r, g, b;
1353  const char *line_buffer;
1354  while ((line_buffer = file.ReadLine())) {
1355  if (sscanf(line_buffer, "%lf %lf %lf %lf %lf %lf", &x, &y, &z, &r,
1356  &g, &b) == 6) {
1357  pointcloud.addEigenPoint(Eigen::Vector3d(x, y, z));
1358  pointcloud.addEigenColor(Eigen::Vector3d(r, g, b));
1359  }
1360  if (++i % 1000 == 0) {
1361  reporter.Update(file.CurPos());
1362  }
1363  }
1364  reporter.Finish();
1365 
1366  return true;
1367  } catch (const std::exception &e) {
1368  utility::LogWarning("Read XYZ failed with exception: {}", e.what());
1369  return false;
1370  }
1371 }
1372 
1373 bool WritePointCloudToXYZRGB(const std::string &filename,
1374  const ccPointCloud &pointcloud,
1375  const WritePointCloudOption &params) {
1376  if (!pointcloud.hasColors()) {
1377  return false;
1378  }
1379 
1380  try {
1382  if (!file.Open(filename, "w")) {
1383  utility::LogWarning("Write XYZRGB failed: unable to open file: {}",
1384  filename);
1385  return false;
1386  }
1387  utility::CountingProgressReporter reporter(params.update_progress);
1388  reporter.SetTotal(pointcloud.size());
1389 
1390  for (size_t i = 0; i < pointcloud.size(); i++) {
1391  const Eigen::Vector3d &point = pointcloud.getEigenPoint(i);
1392  const Eigen::Vector3d &color = pointcloud.getEigenColor(i);
1393  if (fprintf(file.GetFILE(), "%.10f %.10f %.10f %.10f %.10f %.10f\n",
1394  point(0), point(1), point(2), color(0), color(1),
1395  color(2)) < 0) {
1397  "Write XYZRGB failed: unable to write file: {}",
1398  filename);
1399  return false; // error happened during writing.
1400  }
1401  if (i % 1000 == 0) {
1402  reporter.Update(i);
1403  }
1404  }
1405  reporter.Finish();
1406  return true;
1407  } catch (const std::exception &e) {
1408  utility::LogWarning("Write XYZRGB failed with exception: {}", e.what());
1409  return false;
1410  }
1411 }
1412 /********************************* XYZRGB IO
1413  * *************************************/
1414 
1415 /********************************* PLY IO *************************************/
1417  p_ply ply_file = ply_open(path.c_str(), NULL, 0, NULL);
1418  if (!ply_file) {
1419  return CONTENTS_UNKNOWN;
1420  }
1421  if (!ply_read_header(ply_file)) {
1422  ply_close(ply_file);
1423  return CONTENTS_UNKNOWN;
1424  }
1425 
1426  auto nVertices =
1427  ply_set_read_cb(ply_file, "vertex", "x", nullptr, nullptr, 0);
1428  auto nLines =
1429  ply_set_read_cb(ply_file, "edge", "vertex1", nullptr, nullptr, 0);
1430  auto nFaces = ply_set_read_cb(ply_file, "face", "vertex_indices", nullptr,
1431  nullptr, 0);
1432  if (nFaces == 0) {
1433  nFaces = ply_set_read_cb(ply_file, "face", "vertex_index", nullptr,
1434  nullptr, 0);
1435  }
1436  ply_close(ply_file);
1437 
1438  int contents = 0;
1439  if (nFaces > 0) {
1440  contents |= CONTAINS_TRIANGLES;
1441  } else if (nLines > 0) {
1442  contents |= CONTAINS_LINES;
1443  } else if (nVertices > 0) {
1444  contents |= CONTAINS_POINTS;
1445  }
1446  return FileGeometry(contents);
1447 }
1448 
1449 bool ReadPointCloudFromPLY(const std::string &filename,
1450  ccPointCloud &pointcloud,
1451  const ReadPointCloudOption &params) {
1452  using namespace ply_pointcloud_reader;
1453 
1454  p_ply ply_file = ply_open(filename.c_str(), NULL, 0, NULL);
1455  if (!ply_file) {
1456  utility::LogWarning("Read PLY failed: unable to open file: {}",
1457  filename.c_str());
1458  return false;
1459  }
1460  if (!ply_read_header(ply_file)) {
1461  utility::LogWarning("Read PLY failed: unable to parse header.");
1462  ply_close(ply_file);
1463  return false;
1464  }
1465 
1466  PLYReaderState state;
1467  state.pointcloud_ptr = &pointcloud;
1468  state.vertex_num = ply_set_read_cb(ply_file, "vertex", "x",
1469  ReadVertexCallback, &state, 0);
1470  ply_set_read_cb(ply_file, "vertex", "y", ReadVertexCallback, &state, 1);
1471  ply_set_read_cb(ply_file, "vertex", "z", ReadVertexCallback, &state, 2);
1472 
1473  state.normal_num = ply_set_read_cb(ply_file, "vertex", "nx",
1474  ReadNormalCallback, &state, 0);
1475  ply_set_read_cb(ply_file, "vertex", "ny", ReadNormalCallback, &state, 1);
1476  ply_set_read_cb(ply_file, "vertex", "nz", ReadNormalCallback, &state, 2);
1477 
1478  state.color_num = ply_set_read_cb(ply_file, "vertex", "red",
1479  ReadColorCallback, &state, 0);
1480  ply_set_read_cb(ply_file, "vertex", "green", ReadColorCallback, &state, 1);
1481  ply_set_read_cb(ply_file, "vertex", "blue", ReadColorCallback, &state, 2);
1482 
1483  if (state.vertex_num <= 0) {
1484  utility::LogWarning("Read PLY failed: number of vertex <= 0.");
1485  ply_close(ply_file);
1486  return false;
1487  }
1488 
1489  state.vertex_index = 0;
1490  state.normal_index = 0;
1491  state.color_index = 0;
1492 
1493  pointcloud.clear();
1494  pointcloud.resize(state.vertex_num);
1495 
1496  if (state.color_num > 1) {
1497  pointcloud.resizeTheRGBTable();
1498  }
1499 
1500  if (state.normal_num > 1) {
1501  pointcloud.reserveTheNormsTable();
1502  }
1503 
1504  utility::CountingProgressReporter reporter(params.update_progress);
1505  reporter.SetTotal(state.vertex_num);
1506  state.progress_bar = &reporter;
1507 
1508  if (!ply_read(ply_file)) {
1509  utility::LogWarning("Read PLY failed: unable to read file: {}",
1510  filename);
1511  ply_close(ply_file);
1512  return false;
1513  }
1514 
1515  ply_close(ply_file);
1516  reporter.Finish();
1517  return true;
1518 }
1519 
1520 bool WritePointCloudToPLY(const std::string &filename,
1521  const ccPointCloud &pointcloud,
1522  const WritePointCloudOption &params) {
1523  if (pointcloud.IsEmpty()) {
1524  utility::LogWarning("Write PLY failed: point cloud has 0 points.");
1525  return false;
1526  }
1527 
1528  p_ply ply_file =
1529  ply_create(filename.c_str(),
1530  bool(params.write_ascii) ? PLY_ASCII : PLY_LITTLE_ENDIAN,
1531  NULL, 0, NULL);
1532  if (!ply_file) {
1533  utility::LogWarning("Write PLY failed: unable to open file: {}",
1534  filename);
1535  return false;
1536  }
1537  ply_add_comment(ply_file, "Created by CloudViewer");
1538  ply_add_element(ply_file, "vertex", static_cast<long>(pointcloud.size()));
1539  ply_add_property(ply_file, "x", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1540  ply_add_property(ply_file, "y", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1541  ply_add_property(ply_file, "z", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1542  if (pointcloud.hasNormals()) {
1543  ply_add_property(ply_file, "nx", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1544  ply_add_property(ply_file, "ny", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1545  ply_add_property(ply_file, "nz", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
1546  }
1547  if (pointcloud.hasColors()) {
1548  ply_add_property(ply_file, "red", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
1549  ply_add_property(ply_file, "green", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
1550  ply_add_property(ply_file, "blue", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
1551  }
1552  if (!ply_write_header(ply_file)) {
1553  utility::LogWarning("Write PLY failed: unable to write header.");
1554  ply_close(ply_file);
1555  return false;
1556  }
1557 
1558  utility::CountingProgressReporter reporter(params.update_progress);
1559  reporter.SetTotal(pointcloud.size());
1560 
1561  bool printed_color_warning = false;
1562  for (size_t i = 0; i < pointcloud.size(); i++) {
1563  const Eigen::Vector3d &point = pointcloud.getEigenPoint(i);
1564  ply_write(ply_file, point(0));
1565  ply_write(ply_file, point(1));
1566  ply_write(ply_file, point(2));
1567  if (pointcloud.hasNormals()) {
1568  const Eigen::Vector3d &normal = pointcloud.getEigenNormal(i);
1569  ply_write(ply_file, normal(0));
1570  ply_write(ply_file, normal(1));
1571  ply_write(ply_file, normal(2));
1572  }
1573  if (pointcloud.hasColors()) {
1574  const Eigen::Vector3d &color = pointcloud.getEigenColor(i);
1575  if (!printed_color_warning &&
1576  (color(0) < 0 || color(0) > 1 || color(1) < 0 || color(1) > 1 ||
1577  color(2) < 0 || color(2) > 1)) {
1579  "Write Ply clamped color value to valid range");
1580  printed_color_warning = true;
1581  }
1582  auto rgb = utility::ColorToUint8(color);
1583  ply_write(ply_file, rgb(0));
1584  ply_write(ply_file, rgb(1));
1585  ply_write(ply_file, rgb(2));
1586  }
1587  if (i % 1000 == 0) {
1588  reporter.Update(i);
1589  }
1590  }
1591 
1592  reporter.Finish();
1593  ply_close(ply_file);
1594  return true;
1595 }
1596 /********************************* PLY IO *************************************/
1597 
1598 /********************************* PTS IO *************************************/
1600  return CONTAINS_POINTS;
1601 }
1602 
1603 bool ReadPointCloudFromPTS(const std::string &filename,
1604  ccPointCloud &pointcloud,
1605  const ReadPointCloudOption &params) {
1606  try {
1608  if (!file.Open(filename, "r")) {
1609  utility::LogWarning("Read PTS failed: unable to open file: {}",
1610  filename);
1611  return false;
1612  }
1613  size_t num_of_pts = 0;
1614  int num_of_fields = 0;
1615  const char *line_buffer;
1616  if ((line_buffer = file.ReadLine())) {
1617  sscanf(line_buffer, "%zu", &num_of_pts);
1618  }
1619  if (num_of_pts <= 0) {
1620  utility::LogWarning("Read PTS failed: unable to read header.");
1621  return false;
1622  }
1623  utility::CountingProgressReporter reporter(params.update_progress);
1624  reporter.SetTotal(num_of_pts);
1625 
1626  pointcloud.clear();
1627  size_t idx = 0;
1628  while (idx < num_of_pts && (line_buffer = file.ReadLine())) {
1629  if (num_of_fields == 0) {
1630  std::vector<std::string> st;
1631  utility::SplitString(st, line_buffer, " ");
1632  num_of_fields = (int)st.size();
1633  if (num_of_fields < 3) {
1635  "Read PTS failed: insufficient data fields.");
1636  return false;
1637  }
1638  pointcloud.resize(static_cast<unsigned>(num_of_pts));
1639  if (num_of_fields >= 7) {
1640  // X Y Z I R G B
1641  pointcloud.resizeTheRGBTable();
1642  }
1643  }
1644  double x, y, z;
1645  int i, r, g, b;
1646  if (num_of_fields < 7) {
1647  if (sscanf(line_buffer, "%lf %lf %lf", &x, &y, &z) == 3) {
1648  pointcloud.setPoint(idx, Eigen::Vector3d(x, y, z));
1649  }
1650  } else {
1651  if (sscanf(line_buffer, "%lf %lf %lf %d %d %d %d", &x, &y, &z,
1652  &i, &r, &g, &b) == 7) {
1653  pointcloud.setPoint(idx, Eigen::Vector3d(x, y, z));
1654  pointcloud.setPointColor(static_cast<unsigned>(idx),
1655  ecvColor::Rgb(r, g, b));
1656  }
1657  }
1658  idx++;
1659  if (idx % 1000 == 0) {
1660  reporter.Update(idx);
1661  }
1662  }
1663  reporter.Finish();
1664 
1665  return true;
1666  } catch (const std::exception &e) {
1667  utility::LogWarning("Read PTS failed with exception: {}", e.what());
1668  return false;
1669  }
1670 }
1671 
1672 bool WritePointCloudToPTS(const std::string &filename,
1673  const ccPointCloud &pointcloud,
1674  const WritePointCloudOption &params) {
1675  try {
1677  if (!file.Open(filename, "w")) {
1678  utility::LogWarning("Write PTS failed: unable to open file: {}",
1679  filename);
1680  return false;
1681  }
1682  utility::CountingProgressReporter reporter(params.update_progress);
1683  reporter.SetTotal(pointcloud.size());
1684 
1685  if (fprintf(file.GetFILE(), "%zu\r\n", (size_t)pointcloud.size()) < 0) {
1686  utility::LogWarning("Write PTS failed: unable to write file: {}",
1687  filename);
1688  return false;
1689  }
1690  for (size_t i = 0; i < pointcloud.size(); i++) {
1691  const auto &point = pointcloud.getEigenPoint(i);
1692  if (!pointcloud.hasColors()) {
1693  if (fprintf(file.GetFILE(), "%.10f %.10f %.10f\r\n", point(0),
1694  point(1), point(2)) < 0) {
1696  "Write PTS failed: unable to write file: {}",
1697  filename);
1698  return false;
1699  }
1700  } else {
1701  auto color = utility::ColorToUint8(pointcloud.getEigenColor(i));
1702  if (fprintf(file.GetFILE(), "%.10f %.10f %.10f %d %d %d %d\r\n",
1703  point(0), point(1), point(2), 0, (int)color(0),
1704  (int)color(1), (int)(color(2))) < 0) {
1706  "Write PTS failed: unable to write file: {}",
1707  filename);
1708  return false;
1709  }
1710  }
1711  if (i % 1000 == 0) {
1712  reporter.Update(i);
1713  }
1714  }
1715  reporter.Finish();
1716  return true;
1717  } catch (const std::exception &e) {
1718  utility::LogWarning("Write PTS failed with exception: {}", e.what());
1719  return false;
1720  }
1721 }
1722 
1723 /********************************* PTS IO *************************************/
1724 
1725 } // namespace io
1726 } // namespace cloudViewer
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
IsAscii write_ascii
Compressed compressed
std::string filename
double normal[3]
int width
long vertex_num
ccPointCloud * pointcloud_ptr
int size
PCDDataType datatype
bool has_ring
bool has_colors
long normal_index
bool has_intensity
utility::CountingProgressReporter * progress_bar
std::string version
int pointsize
int elementnum
std::vector< PCLPointField > fields
std::string name
long vertex_index
std::string viewpoint
bool has_normals
long normal_num
int height
int count
int count_offset
int offset
long color_index
int points
bool has_points
char type
long color_num
math::float4 color
#define PLY_ASCII
Definition: Ply.h:51
#define PLY_DOUBLE
Definition: Ply.h:69
#define PLY_UCHAR
Definition: Ply.h:65
cmdLineReadable * params[]
#define NULL
virtual void release()
Decrease counter and deletes object when 0.
Definition: CVShareable.cpp:35
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual void showSF(bool state)
Sets active scalarfield visibility.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addEigenNorm(const Eigen::Vector3d &N)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
void addNorms(const std::vector< CCVector3 > &Ns)
void addEigenColor(const Eigen::Vector3d &color)
Eigen::Vector3d getEigenNormal(size_t index) const
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasNormals() const override
Returns whether normals are enabled or not.
virtual bool IsEmpty() const override
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
void clear() override
Clears the entity from all its points and features.
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void deleteScalarField(int index) override
Deletes a specific scalar field.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
Eigen::Vector3d getEigenColor(size_t index) const
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
virtual bool hasPoints() const
Definition: GenericCloud.h:37
void addEigenPoint(const Eigen::Vector3d &point)
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
CCVector3 * getPointPtr(size_t index)
unsigned size() const override
Definition: PointCloudTpl.h:38
Eigen::Vector3d getEigenPoint(size_t index) const
void setPoint(size_t index, const CCVector3 &P)
const CCVector3 * getPoint(unsigned index) const override
void addElement(ScalarType value)
Definition: ScalarField.h:99
bool reserveSafe(std::size_t count)
Reserves memory (no exception thrown)
Definition: ScalarField.cpp:71
bool Open(const std::string &filename, const std::string &mode)
Open a file.
Definition: FileSystem.cpp:739
int64_t CurPos()
Returns current position in the file (ftell).
Definition: FileSystem.cpp:760
FILE * GetFILE()
Returns the underlying C FILE pointer.
Definition: FileSystem.h:264
int64_t GetFileSize()
Returns the file size in bytes.
Definition: FileSystem.cpp:772
RGB color structure.
Definition: ecvColorTypes.h:49
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define DEFAULT_IO_BUFFER_SIZE
Definition: Logging.h:31
#define LogDebug(...)
Definition: Logging.h:90
__host__ __device__ float length(float2 v)
Definition: cutil_math.h:1162
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
Helper functions for the ml ops.
bool ReadPointCloudFromPLY(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool ReadPointCloudFromXYZN(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool WritePointCloudToPCD(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
FileGeometry ReadFileGeometryTypePCD(const std::string &path)
FileGeometry ReadFileGeometryTypePTS(const std::string &path)
bool WritePointCloudToPTS(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
bool ReadPointCloudFromPCD(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool ReadPointCloudFromXYZ(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool ReadPointCloudFromXYZRGB(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool WritePointCloudToXYZ(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
bool WritePointCloudToPLY(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
FileGeometry ReadFileGeometryTypeXYZ(const std::string &path)
bool WritePointCloudToXYZN(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
FileGeometry ReadFileGeometryTypeXYZN(const std::string &path)
FileGeometry ReadFileGeometryTypePLY(const std::string &path)
bool WritePointCloudToXYZRGB(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
bool WritePointCloudInMemoryToXYZ(unsigned char *&buffer, size_t &length, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
bool ReadPointCloudFromPTS(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
bool ReadPointCloudInMemoryFromXYZ(const unsigned char *buffer, const size_t length, ccPointCloud &pointcloud, const ReadPointCloudOption &params)
FileGeometry ReadFileGeometryTypeXYZRGB(const std::string &path)
static const std::string path
Definition: PointCloud.cpp:59
static bool ReadPCDData(FILE *file, PCDHeader &header, t::geometry::PointCloud &pointcloud, const ReadPointCloudOption &params)
Definition: FilePCD.cpp:457
static bool GenerateHeader(const t::geometry::PointCloud &pointcloud, const bool write_ascii, const bool compressed, PCDHeader &header)
Definition: FilePCD.cpp:684
static bool WritePCDHeader(FILE *file, const PCDHeader &header)
Definition: FilePCD.cpp:792
static bool ReadPCDHeader(FILE *file, PCDHeader &header)
Definition: FilePCD.cpp:197
static bool WritePCDData(FILE *file, const PCDHeader &header, const geometry::PointCloud &pointcloud, const WritePointCloudOption &params)
Definition: FilePCD.cpp:988
static void UnpackASCIIPCDElement(scalar_t &data, const char *data_ptr)=delete
FILE * FOpen(const std::string &filename, const std::string &mode)
Definition: FileSystem.cpp:609
void SplitString(std::vector< std::string > &tokens, const std::string &str, const std::string &delimiters=" ", bool trim_empty_str=true)
Definition: Helper.cpp:197
Eigen::Vector3uint8 ColorToUint8(const Eigen::Vector3d &color)
Definition: Eigen.cpp:269
std::string FastFormatString(const std::string &format, Args... args)
Format string fast (Unix / BSD Only)
Definition: Helper.h:191
Generic file read and write utility for python interface.
RgbTpl< ColorCompType > Rgb
3 components, default type
Optional parameters to ReadPointCloud.
Definition: FileIO.h:39
Optional parameters to WritePointCloud.
Definition: FileIO.h:77
Definition: lsd.c:149
int y
Definition: lsd.c:149
int x
Definition: lsd.c:149