ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilePCD.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 <FileSystem.h>
9 #include <Helper.h>
10 #include <Logging.h>
11 #include <ProgressReporters.h>
12 #include <liblzf/lzf.h>
13 
14 #include <cinttypes>
15 #include <cstdint>
16 #include <cstdio>
17 #include <sstream>
18 
19 #include "cloudViewer/core/Dtype.h"
24 
25 // References for PCD file IO
26 // http://pointclouds.org/documentation/tutorials/pcd_file_format.html
27 // https://github.com/PointCloudLibrary/pcl/blob/master/io/src/pcd_io.cpp
28 // https://www.mathworks.com/matlabcentral/fileexchange/40382-matlab-to-point-cloud-library
29 
30 namespace cloudViewer {
31 namespace t {
32 namespace io {
33 
34 enum class PCDDataType { ASCII = 0, BINARY = 1, BINARY_COMPRESSED = 2 };
35 
36 struct PCLPointField {
37 public:
38  std::string name;
39  int size;
40  char type;
41  int count;
42  // helper variable
43  int count_offset = 0;
44  int offset = 0;
45 };
46 
47 struct PCDHeader {
48 public:
49  std::string version;
50  std::vector<PCLPointField> fields;
51  int width;
52  int height;
53  int points;
55  std::string viewpoint;
56 
57  // helper variables
59  int pointsize;
60  std::unordered_map<std::string, bool> has_attr;
61  std::unordered_map<std::string, core::Dtype> attr_dtype;
62 };
63 
65  ReadAttributePtr(void *data_ptr = nullptr,
66  const int row_idx = 0,
67  const int row_length = 0,
68  const int size = 0)
69  : data_ptr_(data_ptr),
70  row_idx_(row_idx),
71  row_length_(row_length),
72  size_(size) {}
73 
74  void *data_ptr_;
75  const int row_idx_;
76  const int row_length_;
77  const int size_;
78 };
79 
82  const void *data_ptr,
83  const int group_size)
84  : dtype_(dtype), data_ptr_(data_ptr), group_size_(group_size) {}
85 
87  const void *data_ptr_;
88  const int group_size_;
89 };
90 
92  char type_uppercase = std::toupper(type, std::locale());
93  if (type_uppercase == 'I') {
94  if (size == 1) return core::Dtype::Int8;
95  if (size == 2) return core::Dtype::Int16;
96  if (size == 4) return core::Dtype::Int32;
97  if (size == 8)
98  return core::Dtype::Int64;
99  else
100  utility::LogError("Unsupported size {} for data type {}.", size,
101  type);
102  } else if (type_uppercase == 'U') {
103  if (size == 1) return core::Dtype::UInt8;
104  if (size == 2) return core::Dtype::UInt16;
105  if (size == 4) return core::Dtype::UInt32;
106  if (size == 8)
107  return core::Dtype::UInt64;
108  else
109  utility::LogError("Unsupported size {} for data type {}.", size,
110  type);
111  } else if (type_uppercase == 'F') {
112  if (size == 4) return core::Dtype::Float32;
113  if (size == 8)
114  return core::Dtype::Float64;
115  else
116  utility::LogError("Unsupported size {} for data type {}.", size,
117  type);
118  } else {
119  utility::LogError("Unsupported data type {}.", type);
120  }
121 }
122 
123 static bool InitializeHeader(PCDHeader &header) {
124  if (header.points <= 0 || header.pointsize <= 0) {
125  utility::LogWarning("[Write PCD] PCD has no data.");
126  return false;
127  }
128  if (header.fields.size() == 0 || header.pointsize <= 0) {
129  utility::LogWarning("[Write PCD] PCD has no fields.");
130  return false;
131  }
132  header.has_attr["positions"] = false;
133  header.has_attr["normals"] = false;
134  header.has_attr["colors"] = false;
135 
136  std::unordered_set<std::string> known_field;
137  std::unordered_map<std::string, core::Dtype> field_dtype;
138 
139  for (const auto &field : header.fields) {
140  if (field.name == "x" || field.name == "y" || field.name == "z" ||
141  field.name == "normal_x" || field.name == "normal_y" ||
142  field.name == "normal_z" || field.name == "rgb" ||
143  field.name == "rgba") {
144  known_field.insert(field.name);
145  field_dtype[field.name] =
146  GetDtypeFromPCDHeaderField(field.type, field.size);
147  } else {
148  header.has_attr[field.name] = true;
149  header.attr_dtype[field.name] =
150  GetDtypeFromPCDHeaderField(field.type, field.size);
151  }
152  }
153 
154  if (known_field.count("x") && known_field.count("y") &&
155  known_field.count("z")) {
156  if ((field_dtype["x"] == field_dtype["y"]) &&
157  (field_dtype["x"] == field_dtype["z"])) {
158  header.has_attr["positions"] = true;
159  header.attr_dtype["positions"] = field_dtype["x"];
160  } else {
162  "[Write PCD] Dtype for positions data are not "
163  "same.");
164  return false;
165  }
166  } else {
168  "[Write PCD] Fields for positions data are not "
169  "complete.");
170  return false;
171  }
172 
173  if (known_field.count("normal_x") && known_field.count("normal_y") &&
174  known_field.count("normal_z")) {
175  if ((field_dtype["normal_x"] == field_dtype["normal_y"]) &&
176  (field_dtype["normal_x"] == field_dtype["normal_z"])) {
177  header.has_attr["normals"] = true;
178  header.attr_dtype["normals"] = field_dtype["x"];
179  } else {
181  "[InitializeHeader] Dtype for normals data are not same.");
182  return false;
183  }
184  }
185 
186  if (known_field.count("rgb")) {
187  header.has_attr["colors"] = true;
188  header.attr_dtype["colors"] = field_dtype["rgb"];
189  } else if (known_field.count("rgba")) {
190  header.has_attr["colors"] = true;
191  header.attr_dtype["colors"] = field_dtype["rgba"];
192  }
193 
194  return true;
195 }
196 
197 static bool ReadPCDHeader(FILE *file, PCDHeader &header) {
198  char line_buffer[DEFAULT_IO_BUFFER_SIZE];
199  size_t specified_channel_count = 0;
200 
201  while (fgets(line_buffer, DEFAULT_IO_BUFFER_SIZE, file)) {
202  std::string line(line_buffer);
203  if (line == "") {
204  continue;
205  }
206  std::vector<std::string> st = utility::SplitString(line, "\t\r\n ");
207  std::stringstream sstream(line);
208  sstream.imbue(std::locale::classic());
209  std::string line_type;
210  sstream >> line_type;
211  if (line_type.substr(0, 1) == "#") {
212  } else if (line_type.substr(0, 7) == "VERSION") {
213  if (st.size() >= 2) {
214  header.version = st[1];
215  }
216  } else if (line_type.substr(0, 6) == "FIELDS" ||
217  line_type.substr(0, 7) == "COLUMNS") {
218  specified_channel_count = st.size() - 1;
219  if (specified_channel_count == 0) {
220  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
221  return false;
222  }
223  header.fields.resize(specified_channel_count);
224  int count_offset = 0, offset = 0;
225  for (size_t i = 0; i < specified_channel_count;
226  ++i, count_offset += 1, offset += 4) {
227  header.fields[i].name = st[i + 1];
228  header.fields[i].size = 4;
229  header.fields[i].type = 'F';
230  header.fields[i].count = 1;
231  header.fields[i].count_offset = count_offset;
232  header.fields[i].offset = offset;
233  }
234  header.elementnum = count_offset;
235  header.pointsize = offset;
236  } else if (line_type.substr(0, 4) == "SIZE") {
237  if (specified_channel_count != st.size() - 1) {
238  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
239  return false;
240  }
241  int offset = 0, col_type = 0;
242  for (size_t i = 0; i < specified_channel_count;
243  ++i, offset += col_type) {
244  sstream >> col_type;
245  header.fields[i].size = col_type;
246  header.fields[i].offset = offset;
247  }
248  header.pointsize = offset;
249  } else if (line_type.substr(0, 4) == "TYPE") {
250  if (specified_channel_count != st.size() - 1) {
251  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
252  return false;
253  }
254  for (size_t i = 0; i < specified_channel_count; ++i) {
255  header.fields[i].type = st[i + 1].c_str()[0];
256  }
257  } else if (line_type.substr(0, 5) == "COUNT") {
258  if (specified_channel_count != st.size() - 1) {
259  utility::LogWarning("[ReadPCDHeader] Bad PCD file format.");
260  return false;
261  }
262  int count_offset = 0, offset = 0, col_count = 0;
263  for (size_t i = 0; i < specified_channel_count; ++i) {
264  sstream >> col_count;
265  header.fields[i].count = col_count;
266  header.fields[i].count_offset = count_offset;
267  header.fields[i].offset = offset;
268  count_offset += col_count;
269  offset += col_count * header.fields[i].size;
270  }
271  header.elementnum = count_offset;
272  header.pointsize = offset;
273  } else if (line_type.substr(0, 5) == "WIDTH") {
274  sstream >> header.width;
275  } else if (line_type.substr(0, 6) == "HEIGHT") {
276  sstream >> header.height;
277  header.points = header.width * header.height;
278  } else if (line_type.substr(0, 9) == "VIEWPOINT") {
279  if (st.size() >= 2) {
280  header.viewpoint = st[1];
281  }
282  } else if (line_type.substr(0, 6) == "POINTS") {
283  sstream >> header.points;
284  } else if (line_type.substr(0, 4) == "DATA") {
285  header.datatype = PCDDataType::ASCII;
286  if (st.size() >= 2) {
287  if (st[1].substr(0, 17) == "binary_compressed") {
289  } else if (st[1].substr(0, 6) == "binary") {
290  header.datatype = PCDDataType::BINARY;
291  }
292  }
293  break;
294  }
295  }
296  if (!InitializeHeader(header)) {
297  return false;
298  }
299  return true;
300 }
301 
303  const PCLPointField &field,
304  const char *data_ptr,
305  const int index) {
306  std::uint8_t *attr_data_ptr = static_cast<std::uint8_t *>(attr.data_ptr_) +
307  index * attr.row_length_;
308 
309  if (field.size == 4) {
310  std::uint8_t data[4] = {0};
311  char *end;
312  char type_uppercase = std::toupper(field.type, std::locale());
313  if (type_uppercase == 'I') {
314  std::int32_t value = std::strtol(data_ptr, &end, 0);
315  std::memcpy(data, &value, sizeof(std::int32_t));
316  } else if (type_uppercase == 'U') {
317  std::uint32_t value = std::strtoul(data_ptr, &end, 0);
318  std::memcpy(data, &value, sizeof(std::uint32_t));
319  } else if (type_uppercase == 'F') {
320  float value = std::strtof(data_ptr, &end);
321  std::memcpy(data, &value, sizeof(float));
322  }
323 
324  // color data is packed in BGR order.
325  attr_data_ptr[0] = data[2];
326  attr_data_ptr[1] = data[1];
327  attr_data_ptr[2] = data[0];
328  } else {
329  attr_data_ptr[0] = 0;
330  attr_data_ptr[1] = 0;
331  attr_data_ptr[2] = 0;
332  }
333 }
334 
335 template <typename scalar_t>
336 static void UnpackASCIIPCDElement(scalar_t &data,
337  const char *data_ptr) = delete;
338 
339 template <>
340 void UnpackASCIIPCDElement<float>(float &data, const char *data_ptr) {
341  char *end;
342  data = std::strtof(data_ptr, &end);
343 }
344 
345 template <>
346 void UnpackASCIIPCDElement<double>(double &data, const char *data_ptr) {
347  char *end;
348  data = std::strtod(data_ptr, &end);
349 }
350 
351 template <>
352 void UnpackASCIIPCDElement<std::int8_t>(std::int8_t &data,
353  const char *data_ptr) {
354  char *end;
355  data = static_cast<std::int8_t>(std::strtol(data_ptr, &end, 0));
356 }
357 
358 template <>
359 void UnpackASCIIPCDElement<std::int16_t>(std::int16_t &data,
360  const char *data_ptr) {
361  char *end;
362  data = static_cast<std::int16_t>(std::strtol(data_ptr, &end, 0));
363 }
364 
365 template <>
366 void UnpackASCIIPCDElement<std::int32_t>(std::int32_t &data,
367  const char *data_ptr) {
368  char *end;
369  data = static_cast<std::int32_t>(std::strtol(data_ptr, &end, 0));
370 }
371 
372 template <>
373 void UnpackASCIIPCDElement<std::int64_t>(std::int64_t &data,
374  const char *data_ptr) {
375  char *end;
376  data = std::strtol(data_ptr, &end, 0);
377 }
378 
379 template <>
380 void UnpackASCIIPCDElement<std::uint8_t>(std::uint8_t &data,
381  const char *data_ptr) {
382  char *end;
383  data = static_cast<std::uint8_t>(std::strtoul(data_ptr, &end, 0));
384 }
385 
386 template <>
387 void UnpackASCIIPCDElement<std::uint16_t>(std::uint16_t &data,
388  const char *data_ptr) {
389  char *end;
390  data = static_cast<std::uint16_t>(std::strtoul(data_ptr, &end, 0));
391 }
392 
393 template <>
394 void UnpackASCIIPCDElement<std::uint32_t>(std::uint32_t &data,
395  const char *data_ptr) {
396  char *end;
397  data = static_cast<std::uint32_t>(std::strtoul(data_ptr, &end, 0));
398 }
399 
400 template <>
401 void UnpackASCIIPCDElement<std::uint64_t>(std::uint64_t &data,
402  const char *data_ptr) {
403  char *end;
404  data = std::strtoul(data_ptr, &end, 0);
405 }
406 
408  const PCLPointField &field,
409  const char *data_ptr,
410  const int index) {
412  GetDtypeFromPCDHeaderField(field.type, field.size), [&] {
413  scalar_t *attr_data_ptr =
414  static_cast<scalar_t *>(attr.data_ptr_) +
415  index * attr.row_length_ + attr.row_idx_;
416 
417  UnpackASCIIPCDElement<scalar_t>(attr_data_ptr[0], data_ptr);
418  });
419 }
420 
422  const PCLPointField &field,
423  const void *data_ptr,
424  const int index) {
425  std::uint8_t *attr_data_ptr = static_cast<std::uint8_t *>(attr.data_ptr_) +
426  index * attr.row_length_;
427 
428  if (field.size == 4) {
429  std::uint8_t data[4] = {0};
430  std::memcpy(data, data_ptr, 4 * sizeof(std::uint8_t));
431 
432  // color data is packed in BGR order.
433  attr_data_ptr[0] = data[2];
434  attr_data_ptr[1] = data[1];
435  attr_data_ptr[2] = data[0];
436  } else {
437  attr_data_ptr[0] = 0;
438  attr_data_ptr[1] = 0;
439  attr_data_ptr[2] = 0;
440  }
441 }
442 
444  const PCLPointField &field,
445  const void *data_ptr,
446  const int index) {
448  GetDtypeFromPCDHeaderField(field.type, field.size), [&] {
449  scalar_t *attr_data_ptr =
450  static_cast<scalar_t *>(attr.data_ptr_) +
451  index * attr.row_length_ + attr.row_idx_;
452 
453  std::memcpy(attr_data_ptr, data_ptr, sizeof(scalar_t));
454  });
455 }
456 
457 static bool ReadPCDData(FILE *file,
458  PCDHeader &header,
459  t::geometry::PointCloud &pointcloud,
460  const ReadPointCloudOption &params) {
461  // The header should have been checked
462  pointcloud.Clear();
463 
464  std::unordered_map<std::string, ReadAttributePtr> map_field_to_attr_ptr;
465 
466  if (header.has_attr["positions"]) {
468  {header.points, 3}, header.attr_dtype["positions"]));
469 
470  void *data_ptr = pointcloud.GetPointPositions().GetDataPtr();
471  ReadAttributePtr position_x(data_ptr, 0, 3, header.points);
472  ReadAttributePtr position_y(data_ptr, 1, 3, header.points);
473  ReadAttributePtr position_z(data_ptr, 2, 3, header.points);
474 
475  map_field_to_attr_ptr.emplace(std::string("x"), position_x);
476  map_field_to_attr_ptr.emplace(std::string("y"), position_y);
477  map_field_to_attr_ptr.emplace(std::string("z"), position_z);
478  } else {
480  "[ReadPCDData] Fields for point data are not complete.");
481  return false;
482  }
483  if (header.has_attr["normals"]) {
485  {header.points, 3}, header.attr_dtype["normals"]));
486 
487  void *data_ptr = pointcloud.GetPointNormals().GetDataPtr();
488  ReadAttributePtr normal_x(data_ptr, 0, 3, header.points);
489  ReadAttributePtr normal_y(data_ptr, 1, 3, header.points);
490  ReadAttributePtr normal_z(data_ptr, 2, 3, header.points);
491 
492  map_field_to_attr_ptr.emplace(std::string("normal_x"), normal_x);
493  map_field_to_attr_ptr.emplace(std::string("normal_y"), normal_y);
494  map_field_to_attr_ptr.emplace(std::string("normal_z"), normal_z);
495  }
496  if (header.has_attr["colors"]) {
497  // Colors stored in a PCD file is ALWAYS in UInt8 format.
498  // However it is stored as a single packed floating value.
499  pointcloud.SetPointColors(
500  core::Tensor::Empty({header.points, 3}, core::UInt8));
501 
502  void *data_ptr = pointcloud.GetPointColors().GetDataPtr();
503  ReadAttributePtr colors(data_ptr, 0, 3, header.points);
504 
505  map_field_to_attr_ptr.emplace(std::string("colors"), colors);
506  }
507 
508  // PCLPointField
509  for (const auto &field : header.fields) {
510  if (header.has_attr[field.name] && field.name != "x" &&
511  field.name != "y" && field.name != "z" &&
512  field.name != "normal_x" && field.name != "normal_y" &&
513  field.name != "normal_z" && field.name != "rgb" &&
514  field.name != "rgba") {
515  pointcloud.SetPointAttr(
516  field.name,
517  core::Tensor::Empty({header.points, 1},
518  header.attr_dtype[field.name]));
519 
520  void *data_ptr = pointcloud.GetPointAttr(field.name).GetDataPtr();
521  ReadAttributePtr attr(data_ptr, 0, 1, header.points);
522 
523  map_field_to_attr_ptr.emplace(field.name, attr);
524  }
525  }
526 
527  utility::CountingProgressReporter reporter(params.update_progress);
528  reporter.SetTotal(header.points);
529 
530  if (header.datatype == PCDDataType::ASCII) {
531  char line_buffer[DEFAULT_IO_BUFFER_SIZE];
532  int idx = 0;
533  while (fgets(line_buffer, DEFAULT_IO_BUFFER_SIZE, file) &&
534  idx < header.points) {
535  std::string line(line_buffer);
536  std::vector<std::string> strs =
537  utility::SplitString(line, "\t\r\n ");
538  if ((int)strs.size() < header.elementnum) {
539  continue;
540  }
541  for (size_t i = 0; i < header.fields.size(); ++i) {
542  const auto &field = header.fields[i];
543  if (field.name == "rgb" || field.name == "rgba") {
545  map_field_to_attr_ptr["colors"], field,
546  strs[field.count_offset].c_str(), idx);
547  } else {
549  map_field_to_attr_ptr[field.name], field,
550  strs[field.count_offset].c_str(), idx);
551  }
552  }
553  ++idx;
554  if (idx % 1000 == 0) {
555  reporter.Update(idx);
556  }
557  }
558  } else if (header.datatype == PCDDataType::BINARY) {
559  std::unique_ptr<char[]> buffer(new char[header.pointsize]);
560  for (int i = 0; i < header.points; ++i) {
561  if (fread(buffer.get(), header.pointsize, 1, file) != 1) {
563  "[ReadPCDData] Failed to read data record.");
564  pointcloud.Clear();
565  return false;
566  }
567  for (const auto &field : header.fields) {
568  if (field.name == "rgb" || field.name == "rgba") {
570  map_field_to_attr_ptr["colors"], field,
571  buffer.get() + field.offset, i);
572  } else {
574  map_field_to_attr_ptr[field.name], field,
575  buffer.get() + field.offset, i);
576  }
577  }
578  if (i % 1000 == 0) {
579  reporter.Update(i);
580  }
581  }
582  } else if (header.datatype == PCDDataType::BINARY_COMPRESSED) {
583  double reporter_total = 100.0;
584  reporter.SetTotal(int(reporter_total));
585  reporter.Update(int(reporter_total * 0.01));
586  std::uint32_t compressed_size;
587  std::uint32_t uncompressed_size;
588  if (fread(&compressed_size, sizeof(compressed_size), 1, file) != 1) {
589  utility::LogWarning("[ReadPCDData] Failed to read data record.");
590  pointcloud.Clear();
591  return false;
592  }
593  if (fread(&uncompressed_size, sizeof(uncompressed_size), 1, file) !=
594  1) {
595  utility::LogWarning("[ReadPCDData] Failed to read data record.");
596  pointcloud.Clear();
597  return false;
598  }
600  "PCD data with {:d} compressed size, and {:d} uncompressed "
601  "size.",
602  compressed_size, uncompressed_size);
603  std::unique_ptr<char[]> buffer_compressed(new char[compressed_size]);
604  reporter.Update(int(reporter_total * .1));
605  if (fread(buffer_compressed.get(), 1, compressed_size, file) !=
606  compressed_size) {
607  utility::LogWarning("[ReadPCDData] Failed to read data record.");
608  pointcloud.Clear();
609  return false;
610  }
611  std::unique_ptr<char[]> buffer(new char[uncompressed_size]);
612  reporter.Update(int(reporter_total * .2));
613  if (lzf_decompress(buffer_compressed.get(),
614  (unsigned int)compressed_size, buffer.get(),
615  (unsigned int)uncompressed_size) !=
616  uncompressed_size) {
617  utility::LogWarning("[ReadPCDData] Uncompression failed.");
618  pointcloud.Clear();
619  return false;
620  }
621  for (const auto &field : header.fields) {
622  const char *base_ptr = buffer.get() + field.offset * header.points;
623  double progress =
624  double(base_ptr - buffer.get()) / uncompressed_size;
625  reporter.Update(int(reporter_total * (progress + .2)));
626  if (field.name == "rgb" || field.name == "rgba") {
627  for (int i = 0; i < header.points; ++i) {
629  map_field_to_attr_ptr["colors"], field,
630  base_ptr + i * field.size * field.count, i);
631  }
632  } else {
633  for (int i = 0; i < header.points; ++i) {
635  map_field_to_attr_ptr[field.name], field,
636  base_ptr + i * field.size * field.count, i);
637  }
638  }
639  }
640  }
641  reporter.Finish();
642  return true;
643 }
644 
645 // Write functions.
646 
648  PCLPointField &field) {
649  if (dtype == core::Int8) {
650  field.type = 'I';
651  field.size = 1;
652  } else if (dtype == core::Int16) {
653  field.type = 'I';
654  field.size = 2;
655  } else if (dtype == core::Int32) {
656  field.type = 'I';
657  field.size = 4;
658  } else if (dtype == core::Int64) {
659  field.type = 'I';
660  field.size = 8;
661  } else if (dtype == core::UInt8) {
662  field.type = 'U';
663  field.size = 1;
664  } else if (dtype == core::UInt16) {
665  field.type = 'U';
666  field.size = 2;
667  } else if (dtype == core::UInt32) {
668  field.type = 'U';
669  field.size = 4;
670  } else if (dtype == core::UInt64) {
671  field.type = 'U';
672  field.size = 8;
673  } else if (dtype == core::Float32) {
674  field.type = 'F';
675  field.size = 4;
676  } else if (dtype == core::Float64) {
677  field.type = 'F';
678  field.size = 8;
679  } else {
680  utility::LogError("Unsupported data type.");
681  }
682 }
683 
684 static bool GenerateHeader(const t::geometry::PointCloud &pointcloud,
685  const bool write_ascii,
686  const bool compressed,
687  PCDHeader &header) {
688  if (!pointcloud.HasPointPositions()) {
689  return false;
690  }
691 
692  header.version = "0.7";
693  header.width = static_cast<int>(pointcloud.GetPointPositions().GetLength());
694  header.height = 1;
695  header.points = header.width;
696  header.fields.clear();
697 
698  PCLPointField field_x, field_y, field_z;
699  field_x.name = "x";
700  field_x.count = 1;
702  pointcloud.GetPointPositions().GetDtype(), field_x);
703  header.fields.push_back(field_x);
704 
705  field_y = field_x;
706  field_y.name = "y";
707  header.fields.push_back(field_y);
708 
709  field_z = field_x;
710  field_z.name = "z";
711  header.fields.push_back(field_z);
712 
713  header.elementnum = 3 * field_x.count;
714  header.pointsize = 3 * field_x.size;
715 
716  if (pointcloud.HasPointNormals()) {
717  PCLPointField field_normal_x, field_normal_y, field_normal_z;
718 
719  field_normal_x.name = "normal_x";
720  field_normal_x.count = 1;
722  pointcloud.GetPointPositions().GetDtype(), field_normal_x);
723  header.fields.push_back(field_normal_x);
724 
725  field_normal_y = field_normal_x;
726  field_normal_y.name = "normal_y";
727  header.fields.push_back(field_normal_y);
728 
729  field_normal_z = field_normal_x;
730  field_normal_z.name = "normal_z";
731  header.fields.push_back(field_normal_z);
732 
733  header.elementnum += 3 * field_normal_x.count;
734  header.pointsize += 3 * field_normal_x.size;
735  }
736  if (pointcloud.HasPointColors()) {
737  PCLPointField field_colors;
738  field_colors.name = "rgb";
739  field_colors.count = 1;
740  field_colors.type = 'F';
741  field_colors.size = 4;
742  header.fields.push_back(field_colors);
743 
744  header.elementnum++;
745  header.pointsize += 4;
746  }
747 
748  // Custom attribute support of shape {num_points, 1}.
749  for (auto &kv : pointcloud.GetPointAttr()) {
750  if (kv.first != "positions" && kv.first != "normals" &&
751  kv.first != "colors") {
752  if (kv.second.GetShape() ==
754  {pointcloud.GetPointPositions().GetLength(), 1})) {
755  PCLPointField field_custom_attr;
756  field_custom_attr.name = kv.first;
757  field_custom_attr.count = 1;
758  SetPCDHeaderFieldTypeAndSizeFromDtype(kv.second.GetDtype(),
759  field_custom_attr);
760  header.fields.push_back(field_custom_attr);
761 
762  header.elementnum++;
763  header.pointsize += field_custom_attr.size;
764  } else {
766  "Write PCD : Skipping {} attribute. PointCloud "
767  "contains {} attribute which is not supported by PCD "
768  "IO. Only points, normals, colors and attributes with "
769  "shape (num_points, 1) are supported. Expected shape: "
770  "{} but got {}.",
771  kv.first, kv.first,
773  {pointcloud.GetPointPositions().GetLength(), 1})
774  .ToString(),
775  kv.second.GetShape().ToString());
776  }
777  }
778  }
779 
780  if (write_ascii) {
781  header.datatype = PCDDataType::ASCII;
782  } else {
783  if (compressed) {
785  } else {
786  header.datatype = PCDDataType::BINARY;
787  }
788  }
789  return true;
790 }
791 
792 static bool WritePCDHeader(FILE *file, const PCDHeader &header) {
793  fprintf(file, "# .PCD v%s - Point Cloud Data file format\n",
794  header.version.c_str());
795  fprintf(file, "VERSION %s\n", header.version.c_str());
796  fprintf(file, "FIELDS");
797  for (const auto &field : header.fields) {
798  fprintf(file, " %s", field.name.c_str());
799  }
800  fprintf(file, "\n");
801  fprintf(file, "SIZE");
802  for (const auto &field : header.fields) {
803  fprintf(file, " %d", field.size);
804  }
805  fprintf(file, "\n");
806  fprintf(file, "TYPE");
807  for (const auto &field : header.fields) {
808  fprintf(file, " %c", field.type);
809  }
810  fprintf(file, "\n");
811  fprintf(file, "COUNT");
812  for (const auto &field : header.fields) {
813  fprintf(file, " %d", field.count);
814  }
815  fprintf(file, "\n");
816  fprintf(file, "WIDTH %d\n", header.width);
817  fprintf(file, "HEIGHT %d\n", header.height);
818  fprintf(file, "VIEWPOINT 0 0 0 1 0 0 0\n");
819  fprintf(file, "POINTS %d\n", header.points);
820 
821  switch (header.datatype) {
822  case PCDDataType::BINARY:
823  fprintf(file, "DATA binary\n");
824  break;
826  fprintf(file, "DATA binary_compressed\n");
827  break;
828  case PCDDataType::ASCII:
829  default:
830  fprintf(file, "DATA ascii\n");
831  break;
832  }
833  return true;
834 }
835 
836 template <typename scalar_t>
837 static void ColorToUint8(const scalar_t *input_color,
838  std::uint8_t *output_color) {
840  "Color format not supported. Supported color format includes "
841  "Float32, Float64, UInt8, UInt16, UInt32.");
842 }
843 
844 template <>
845 void ColorToUint8<float>(const float *input_color, std::uint8_t *output_color) {
846  const float normalisation_factor =
847  static_cast<float>(std::numeric_limits<std::uint8_t>::max());
848  for (int i = 0; i < 3; ++i) {
849  output_color[i] = static_cast<std::uint8_t>(
850  std::round(std::min(1.f, std::max(0.f, input_color[2 - i])) *
851  normalisation_factor));
852  }
853  output_color[3] = 0;
854 }
855 
856 template <>
857 void ColorToUint8<double>(const double *input_color,
858  std::uint8_t *output_color) {
859  const double normalisation_factor =
860  static_cast<double>(std::numeric_limits<std::uint8_t>::max());
861  for (int i = 0; i < 3; ++i) {
862  output_color[i] = static_cast<std::uint8_t>(
863  std::round(std::min(1., std::max(0., input_color[2 - i])) *
864  normalisation_factor));
865  }
866  output_color[3] = 0;
867 }
868 
869 template <>
870 void ColorToUint8<std::uint8_t>(const std::uint8_t *input_color,
871  std::uint8_t *output_color) {
872  for (int i = 0; i < 3; ++i) {
873  output_color[i] = input_color[2 - i];
874  }
875  output_color[3] = 0;
876 }
877 
878 template <>
879 void ColorToUint8<std::uint16_t>(const std::uint16_t *input_color,
880  std::uint8_t *output_color) {
881  const float normalisation_factor =
882  static_cast<float>(std::numeric_limits<std::uint8_t>::max()) /
883  static_cast<float>(std::numeric_limits<std::uint16_t>::max());
884  for (int i = 0; i < 3; ++i) {
885  output_color[i] = static_cast<std::uint16_t>(input_color[2 - i] *
886  normalisation_factor);
887  }
888  output_color[3] = 0;
889 }
890 
891 template <>
892 void ColorToUint8<std::uint32_t>(const std::uint32_t *input_color,
893  std::uint8_t *output_color) {
894  const float normalisation_factor =
895  static_cast<float>(std::numeric_limits<std::uint8_t>::max()) /
896  static_cast<float>(std::numeric_limits<std::uint32_t>::max());
897  for (int i = 0; i < 3; ++i) {
898  output_color[i] = static_cast<std::uint32_t>(input_color[2 - i] *
899  normalisation_factor);
900  }
901  output_color[3] = 0;
902 }
903 
904 static core::Tensor PackColorsToFloat(const core::Tensor &colors_contiguous) {
905  core::Tensor packed_color =
906  core::Tensor::Empty({colors_contiguous.GetLength(), 1},
907  core::Float32, core::Device("CPU:0"));
908  auto packed_color_ptr = packed_color.GetDataPtr<float>();
909 
910  DISPATCH_DTYPE_TO_TEMPLATE(colors_contiguous.GetDtype(), [&]() {
911  auto colors_ptr = colors_contiguous.GetDataPtr<scalar_t>();
912  core::ParallelFor(core::Device("CPU:0"), colors_contiguous.GetLength(),
913  [&](std::int64_t workload_idx) {
914  std::uint8_t rgba[4] = {0};
915  ColorToUint8<scalar_t>(
916  colors_ptr + 3 * workload_idx, rgba);
917  float val = 0;
918  std::memcpy(&val, rgba, 4 * sizeof(std::uint8_t));
919  packed_color_ptr[workload_idx] = val;
920  });
921  });
922 
923  return packed_color;
924 }
925 
926 template <typename scalar_t>
927 static int WriteElementDataToFileASCII(const scalar_t &data,
928  FILE *file) = delete;
929 
930 template <>
931 int WriteElementDataToFileASCII<float>(const float &data, FILE *file) {
932  return fprintf(file, "%.10g ", data);
933 }
934 
935 template <>
936 int WriteElementDataToFileASCII<double>(const double &data, FILE *file) {
937  return fprintf(file, "%.10g ", data);
938 }
939 
940 template <>
941 int WriteElementDataToFileASCII<std::int8_t>(const std::int8_t &data,
942  FILE *file) {
943  return fprintf(file, "%" PRId8 " ", data);
944 }
945 
946 template <>
947 int WriteElementDataToFileASCII<std::int16_t>(const std::int16_t &data,
948  FILE *file) {
949  return fprintf(file, "%" PRId16 " ", data);
950 }
951 
952 template <>
953 int WriteElementDataToFileASCII<std::int32_t>(const std::int32_t &data,
954  FILE *file) {
955  return fprintf(file, "%" PRId32 " ", data);
956 }
957 
958 template <>
959 int WriteElementDataToFileASCII<std::int64_t>(const std::int64_t &data,
960  FILE *file) {
961  return fprintf(file, "%" PRId64 " ", data);
962 }
963 
964 template <>
965 int WriteElementDataToFileASCII<std::uint8_t>(const std::uint8_t &data,
966  FILE *file) {
967  return fprintf(file, "%" PRIu8 " ", data);
968 }
969 
970 template <>
971 int WriteElementDataToFileASCII<std::uint16_t>(const std::uint16_t &data,
972  FILE *file) {
973  return fprintf(file, "%" PRIu16 " ", data);
974 }
975 
976 template <>
977 int WriteElementDataToFileASCII<std::uint32_t>(const std::uint32_t &data,
978  FILE *file) {
979  return fprintf(file, "%" PRIu32 " ", data);
980 }
981 
982 template <>
983 int WriteElementDataToFileASCII<std::uint64_t>(const std::uint64_t &data,
984  FILE *file) {
985  return fprintf(file, "%" PRIu64 " ", data);
986 }
987 
988 static bool WritePCDData(FILE *file,
989  const PCDHeader &header,
990  const geometry::PointCloud &pointcloud,
991  const WritePointCloudOption &params) {
992  if (pointcloud.IsEmpty()) {
993  utility::LogWarning("Write PLY failed: point cloud has 0 points.");
994  return false;
995  }
996 
997  // TODO: Add device transfer in tensor map and use it here.
998  geometry::TensorMap t_map(pointcloud.GetPointAttr().Contiguous());
999  const std::int64_t num_points = static_cast<std::int64_t>(
1000  pointcloud.GetPointPositions().GetLength());
1001 
1002  std::vector<WriteAttributePtr> attribute_ptrs;
1003 
1004  attribute_ptrs.emplace_back(t_map["positions"].GetDtype(),
1005  t_map["positions"].GetDataPtr(), 3);
1006 
1007  if (pointcloud.HasPointNormals()) {
1008  attribute_ptrs.emplace_back(t_map["normals"].GetDtype(),
1009  t_map["normals"].GetDataPtr(), 3);
1010  }
1011 
1012  if (pointcloud.HasPointColors()) {
1013  t_map["colors"] = PackColorsToFloat(t_map["colors"]);
1014  attribute_ptrs.emplace_back(core::Float32, t_map["colors"].GetDataPtr(),
1015  1);
1016  }
1017 
1018  // Sanity check for the attributes is done before adding it to the
1019  // `header.fields`.
1020  for (auto &field : header.fields) {
1021  if (field.name != "x" && field.name != "y" && field.name != "z" &&
1022  field.name != "normal_x" && field.name != "normal_y" &&
1023  field.name != "normal_z" && field.name != "rgb" &&
1024  field.name != "rgba" && field.count == 1) {
1025  attribute_ptrs.emplace_back(t_map[field.name].GetDtype(),
1026  t_map[field.name].GetDataPtr(), 1);
1027  }
1028  }
1029 
1030  utility::CountingProgressReporter reporter(params.update_progress);
1031  reporter.SetTotal(num_points);
1032  if (header.datatype == PCDDataType::ASCII) {
1033  for (std::int64_t i = 0; i < num_points; ++i) {
1034  for (auto &it : attribute_ptrs) {
1035  DISPATCH_DTYPE_TO_TEMPLATE(it.dtype_, [&]() {
1036  const scalar_t *data_ptr =
1037  static_cast<const scalar_t *>(it.data_ptr_);
1038 
1039  for (int idx_offset = it.group_size_ * i;
1040  idx_offset < it.group_size_ * (i + 1); ++idx_offset) {
1041  WriteElementDataToFileASCII<scalar_t>(
1042  data_ptr[idx_offset], file);
1043  }
1044  });
1045  }
1046 
1047  fprintf(file, "\n");
1048  if (i % 1000 == 0) {
1049  reporter.Update(i);
1050  }
1051  }
1052  } else if (header.datatype == PCDDataType::BINARY) {
1053  std::vector<char> buffer((header.pointsize * header.points));
1054  std::uint32_t buffer_index = 0;
1055  for (std::int64_t i = 0; i < num_points; ++i) {
1056  for (auto &it : attribute_ptrs) {
1057  DISPATCH_DTYPE_TO_TEMPLATE(it.dtype_, [&]() {
1058  const scalar_t *data_ptr =
1059  static_cast<const scalar_t *>(it.data_ptr_);
1060 
1061  for (int idx_offset = it.group_size_ * i;
1062  idx_offset < it.group_size_ * (i + 1); ++idx_offset) {
1063  std::memcpy(buffer.data() + buffer_index,
1064  reinterpret_cast<const char *>(
1065  &data_ptr[idx_offset]),
1066  sizeof(scalar_t));
1067  buffer_index += sizeof(scalar_t);
1068  }
1069  });
1070  }
1071 
1072  if (i % 1000 == 0) {
1073  reporter.Update(i);
1074  }
1075  }
1076 
1077  fwrite(buffer.data(), sizeof(char), buffer.size(), file);
1078  } else if (header.datatype == PCDDataType::BINARY_COMPRESSED) {
1079  // BINARY_COMPRESSED data contains attributes in column layout
1080  // for better compression.
1081  // For example instead of X Y Z NX NY NZ X Y Z NX NY NZ, the data is
1082  // stored as X X Y Y Z Z NX NX NY NY NZ NZ.
1083 
1084  const std::int64_t report_total = attribute_ptrs.size() * 2;
1085  // 0%-50% packing into buffer
1086  // 50%-75% compressing buffer
1087  // 75%-100% writing compressed buffer
1088  reporter.SetTotal(report_total);
1089 
1090  const std::uint32_t buffer_size_in_bytes =
1091  header.pointsize * header.points;
1092  std::vector<char> buffer(buffer_size_in_bytes);
1093  std::vector<char> buffer_compressed(2 * buffer_size_in_bytes);
1094 
1095  std::uint32_t buffer_index = 0;
1096  std::int64_t count = 0;
1097  for (auto &it : attribute_ptrs) {
1098  DISPATCH_DTYPE_TO_TEMPLATE(it.dtype_, [&]() {
1099  const scalar_t *data_ptr =
1100  static_cast<const scalar_t *>(it.data_ptr_);
1101 
1102  for (int idx_offset = 0; idx_offset < it.group_size_;
1103  ++idx_offset) {
1104  for (std::int64_t i = 0; i < num_points; ++i) {
1105  std::memcpy(buffer.data() + buffer_index,
1106  reinterpret_cast<const char *>(
1107  &data_ptr[i * it.group_size_ +
1108  idx_offset]),
1109  sizeof(scalar_t));
1110  buffer_index += sizeof(scalar_t);
1111  }
1112  }
1113  });
1114 
1115  reporter.Update(count++);
1116  }
1117 
1118  std::uint32_t size_compressed = lzf_compress(
1119  buffer.data(), buffer_size_in_bytes, buffer_compressed.data(),
1120  buffer_size_in_bytes * 2);
1121  if (size_compressed == 0) {
1122  utility::LogWarning("[WritePCDData] Failed to compress data.");
1123  return false;
1124  }
1125 
1127  "[WritePCDData] {:d} bytes data compressed into {:d} bytes.",
1128  buffer_size_in_bytes, size_compressed);
1129 
1130  reporter.Update(static_cast<std::int64_t>(report_total * 0.75));
1131 
1132  fwrite(&size_compressed, sizeof(size_compressed), 1, file);
1133  fwrite(&buffer_size_in_bytes, sizeof(buffer_size_in_bytes), 1, file);
1134  fwrite(buffer_compressed.data(), 1, size_compressed, file);
1135  }
1136  reporter.Finish();
1137  return true;
1138 }
1139 
1140 bool ReadPointCloudFromPCD(const std::string &filename,
1141  t::geometry::PointCloud &pointcloud,
1142  const ReadPointCloudOption &params) {
1143  PCDHeader header;
1144  FILE *file = utility::filesystem::FOpen(filename.c_str(), "rb");
1145  if (file == NULL) {
1146  utility::LogWarning("Read PCD failed: unable to open file: {}",
1147  filename);
1148  return false;
1149  }
1150 
1151  if (!ReadPCDHeader(file, header)) {
1152  utility::LogWarning("Read PCD failed: unable to parse header.");
1153  fclose(file);
1154  return false;
1155  }
1157  "PCD header indicates {:d} fields, {:d} bytes per point, and {:d} "
1158  "points in total.",
1159  (int)header.fields.size(), header.pointsize, header.points);
1160  for (const auto &field : header.fields) {
1161  utility::LogDebug("{}, {}, {:d}, {:d}, {:d}", field.name.c_str(),
1162  field.type, field.size, field.count, field.offset);
1163  }
1164  utility::LogDebug("Compression method is {:d}.", (int)header.datatype);
1165  utility::LogDebug("Points: {}; normals: {}; colors: {}",
1166  header.has_attr["positions"] ? "yes" : "no",
1167  header.has_attr["normals"] ? "yes" : "no",
1168  header.has_attr["colors"] ? "yes" : "no");
1169  if (!ReadPCDData(file, header, pointcloud, params)) {
1170  utility::LogWarning("Read PCD failed: unable to read data.");
1171  fclose(file);
1172  return false;
1173  }
1174  fclose(file);
1175  return true;
1176 }
1177 
1178 bool WritePointCloudToPCD(const std::string &filename,
1179  const geometry::PointCloud &pointcloud,
1180  const WritePointCloudOption &params) {
1182  core::Device("CPU:0"));
1183 
1184  PCDHeader header;
1185  if (!GenerateHeader(pointcloud, bool(params.write_ascii),
1186  bool(params.compressed), header)) {
1187  utility::LogWarning("Write PCD failed: unable to generate header.");
1188  return false;
1189  }
1190  FILE *file = utility::filesystem::FOpen(filename.c_str(), "wb");
1191  if (file == NULL) {
1192  utility::LogWarning("Write PCD failed: unable to open file.");
1193  return false;
1194  }
1195  if (!WritePCDHeader(file, header)) {
1196  utility::LogWarning("Write PCD failed: unable to write header.");
1197  fclose(file);
1198  return false;
1199  }
1200  if (!WritePCDData(file, header, pointcloud, params)) {
1201  utility::LogWarning("Write PCD failed: unable to write data.");
1202  fclose(file);
1203  return false;
1204  }
1205  fclose(file);
1206  return true;
1207 }
1208 
1209 } // namespace io
1210 } // namespace t
1211 } // namespace cloudViewer
IsAscii write_ascii
Compressed compressed
std::string filename
#define DISPATCH_DTYPE_TO_TEMPLATE(DTYPE,...)
Definition: Dispatch.h:31
int size
int count
int count_offset
int offset
char type
cmdLineReadable * params[]
#define AssertTensorDevice(tensor,...)
Definition: TensorCheck.h:45
#define NULL
static const Dtype Int8
Definition: Dtype.h:26
static const Dtype UInt8
Definition: Dtype.h:30
static const Dtype Float32
Definition: Dtype.h:24
static const Dtype UInt16
Definition: Dtype.h:31
static const Dtype UInt64
Definition: Dtype.h:33
static const Dtype Int16
Definition: Dtype.h:27
static const Dtype UInt32
Definition: Dtype.h:32
static const Dtype Float64
Definition: Dtype.h:25
static const Dtype Int32
Definition: Dtype.h:28
static const Dtype Int64
Definition: Dtype.h:29
std::string ToString() const
Definition: SizeVector.cpp:132
int64_t GetLength() const
Definition: Tensor.h:1125
Dtype GetDtype() const
Definition: Tensor.h:1164
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
Definition: Tensor.cpp:400
A point cloud contains a list of 3D points.
Definition: PointCloud.h:82
void SetPointNormals(const core::Tensor &value)
Set the value of the "normals" attribute. Convenience function.
Definition: PointCloud.h:198
core::Tensor & GetPointNormals()
Get the value of the "normals" attribute. Convenience function.
Definition: PointCloud.h:130
void SetPointColors(const core::Tensor &value)
Set the value of the "colors" attribute. Convenience function.
Definition: PointCloud.h:192
void SetPointPositions(const core::Tensor &value)
Set the value of the "positions" attribute. Convenience function.
Definition: PointCloud.h:186
core::Tensor & GetPointPositions()
Get the value of the "positions" attribute. Convenience function.
Definition: PointCloud.h:124
bool IsEmpty() const override
Returns !HasPointPositions().
Definition: PointCloud.h:257
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: PointCloud.h:111
PointCloud & Clear() override
Clear all data in the point cloud.
Definition: PointCloud.h:251
void SetPointAttr(const std::string &key, const core::Tensor &value)
Definition: PointCloud.h:177
core::Tensor & GetPointColors()
Get the value of the "colors" attribute. Convenience function.
Definition: PointCloud.h:127
double colors[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#define DEFAULT_IO_BUFFER_SIZE
Definition: Logging.h:31
#define LogDebug(...)
Definition: Logging.h:90
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
Helper functions for the ml ops.
const Dtype Int8
Definition: Dtype.cpp:44
const Dtype Int64
Definition: Dtype.cpp:47
const Dtype UInt64
Definition: Dtype.cpp:51
const Dtype UInt32
Definition: Dtype.cpp:50
const Dtype UInt8
Definition: Dtype.cpp:48
const Dtype Int16
Definition: Dtype.cpp:45
const Dtype Float64
Definition: Dtype.cpp:43
const Dtype UInt16
Definition: Dtype.cpp:49
const Dtype Int32
Definition: Dtype.cpp:46
const Dtype Float32
Definition: Dtype.cpp:42
static void ReadBinaryPCDColorsFromField(ReadAttributePtr &attr, const PCLPointField &field, const void *data_ptr, const int index)
Definition: FilePCD.cpp:421
int WriteElementDataToFileASCII< double >(const double &data, FILE *file)
Definition: FilePCD.cpp:936
static core::Tensor PackColorsToFloat(const core::Tensor &colors_contiguous)
Definition: FilePCD.cpp:904
static void ReadASCIIPCDColorsFromField(ReadAttributePtr &attr, const PCLPointField &field, const char *data_ptr, const int index)
Definition: FilePCD.cpp:302
void ColorToUint8< double >(const double *input_color, std::uint8_t *output_color)
Definition: FilePCD.cpp:857
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
void UnpackASCIIPCDElement< float >(float &data, const char *data_ptr)
Definition: FilePCD.cpp:340
bool WritePointCloudToPCD(const std::string &filename, const geometry::PointCloud &pointcloud, const WritePointCloudOption &params)
Definition: FilePCD.cpp:1178
void UnpackASCIIPCDElement< double >(double &data, const char *data_ptr)
Definition: FilePCD.cpp:346
bool ReadPointCloudFromPCD(const std::string &filename, t::geometry::PointCloud &pointcloud, const ReadPointCloudOption &params)
Definition: FilePCD.cpp:1140
static void ReadBinaryPCDElementsFromField(ReadAttributePtr &attr, const PCLPointField &field, const void *data_ptr, const int index)
Definition: FilePCD.cpp:443
static bool WritePCDHeader(FILE *file, const PCDHeader &header)
Definition: FilePCD.cpp:792
static core::Dtype GetDtypeFromPCDHeaderField(char type, int size)
Definition: FilePCD.cpp:91
static int WriteElementDataToFileASCII(const scalar_t &data, FILE *file)=delete
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 bool InitializeHeader(PCDHeader &header)
Definition: FilePCD.cpp:123
static void SetPCDHeaderFieldTypeAndSizeFromDtype(const core::Dtype &dtype, PCLPointField &field)
Definition: FilePCD.cpp:647
static void UnpackASCIIPCDElement(scalar_t &data, const char *data_ptr)=delete
int WriteElementDataToFileASCII< float >(const float &data, FILE *file)
Definition: FilePCD.cpp:931
static void ReadASCIIPCDElementsFromField(ReadAttributePtr &attr, const PCLPointField &field, const char *data_ptr, const int index)
Definition: FilePCD.cpp:407
void ColorToUint8< float >(const float *input_color, std::uint8_t *output_color)
Definition: FilePCD.cpp:845
static void ColorToUint8(const scalar_t *input_color, std::uint8_t *output_color)
Definition: FilePCD.cpp:837
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
Generic file read and write utility for python interface.
Optional parameters to ReadPointCloud.
Definition: FileIO.h:39
Optional parameters to WritePointCloud.
Definition: FileIO.h:77
std::unordered_map< std::string, core::Dtype > attr_dtype
Definition: FilePCD.cpp:61
std::vector< PCLPointField > fields
Definition: FilePCD.cpp:50
std::unordered_map< std::string, bool > has_attr
Definition: FilePCD.cpp:60
ReadAttributePtr(void *data_ptr=nullptr, const int row_idx=0, const int row_length=0, const int size=0)
Definition: FilePCD.cpp:65
WriteAttributePtr(const core::Dtype &dtype, const void *data_ptr, const int group_size)
Definition: FilePCD.cpp:81