ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
VoxelGridIO.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 "VoxelGridIO.h"
9 
10 #include <FileSystem.h>
11 #include <Logging.h>
12 #include <rply.h>
13 
14 #include <unordered_map>
15 
16 namespace cloudViewer {
17 
18 namespace {
19 using namespace io;
20 
21 namespace ply_voxelgrid_reader {
22 
23 struct PLYReaderState {
25  std::vector<geometry::Voxel> *voxelgrid_ptr;
26  Eigen::Vector3d origin;
27  Eigen::Matrix3d rotation;
28  double voxel_size;
30  long voxel_num;
32  long color_num;
33 };
34 
35 int ReadOriginCallback(p_ply_argument argument) {
36  PLYReaderState *state_ptr;
37  long index;
38  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
39  &index);
40 
41  double value = ply_get_argument_value(argument);
42  state_ptr->origin(index) = value;
43  return 1;
44 }
45 
46 int ReadOriginRotationCallback(p_ply_argument argument) {
47  PLYReaderState *state_ptr;
48  long index;
49  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
50  &index);
51 
52  double value = ply_get_argument_value(argument);
53  int row = index / 3;
54  int col = index % 3;
55  state_ptr->rotation(row, col) = value;
56  return 1;
57 }
58 
59 int ReadScaleCallback(p_ply_argument argument) {
60  PLYReaderState *state_ptr;
61  long index;
62  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
63  &index);
64 
65  double value = ply_get_argument_value(argument);
66  state_ptr->voxel_size = value;
67  return 1;
68 }
69 
70 int ReadVoxelCallback(p_ply_argument argument) {
71  PLYReaderState *state_ptr;
72  long index;
73  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
74  &index);
75  if (state_ptr->voxel_index >= state_ptr->voxel_num) {
76  return 0; // some sanity check
77  }
78 
79  double value = ply_get_argument_value(argument);
80  auto &ptr = *(state_ptr->voxelgrid_ptr);
81  ptr[state_ptr->voxel_index].grid_index_(index) = int(value);
82  if (index == 2) { // reading 'z'
83  state_ptr->voxel_index++;
84  ++(*state_ptr->progress_bar);
85  }
86  return 1;
87 }
88 
89 int ReadColorCallback(p_ply_argument argument) {
90  PLYReaderState *state_ptr;
91  long index;
92  ply_get_argument_user_data(argument, reinterpret_cast<void **>(&state_ptr),
93  &index);
94  if (state_ptr->color_index >= state_ptr->color_num) {
95  return 0;
96  }
97 
98  double value = ply_get_argument_value(argument);
99  auto &ptr = *(state_ptr->voxelgrid_ptr);
100  ptr[state_ptr->color_index].color_(index) = value / 255.0;
101  if (index == 2) { // reading 'blue'
102  state_ptr->color_index++;
103  ++(*state_ptr->progress_bar);
104  }
105  return 1;
106 }
107 
108 } // namespace ply_voxelgrid_reader
109 
110 static const std::unordered_map<
111  std::string,
112  std::function<bool(const std::string &, geometry::VoxelGrid &, bool)>>
113  file_extension_to_voxelgrid_read_function{
114  {"ply", ReadVoxelGridFromPLY},
115  };
116 
117 static const std::unordered_map<std::string,
118  std::function<bool(const std::string &,
119  const geometry::VoxelGrid &,
120  const bool,
121  const bool,
122  const bool)>>
123  file_extension_to_voxelgrid_write_function{
124  {"ply", WriteVoxelGridToPLY},
125  };
126 } // unnamed namespace
127 
128 namespace io {
129 
130 using namespace cloudViewer;
131 
132 std::shared_ptr<geometry::VoxelGrid> CreateVoxelGridFromFile(
133  const std::string &filename,
134  const std::string &format,
135  bool print_progress) {
136  auto voxelgrid = std::make_shared<geometry::VoxelGrid>();
137  ReadVoxelGrid(filename, *voxelgrid, format, print_progress);
138  return voxelgrid;
139 }
140 
141 bool ReadVoxelGrid(const std::string &filename,
142  geometry::VoxelGrid &voxelgrid,
143  const std::string &format,
144  bool print_progress) {
145  std::string filename_ext;
146  if (format == "auto") {
147  filename_ext =
149  } else {
150  filename_ext = format;
151  }
152  if (filename_ext.empty()) {
154  "Read geometry::VoxelGrid failed: unknown file extension.");
155  return false;
156  }
157  auto map_itr = file_extension_to_voxelgrid_read_function.find(filename_ext);
158  if (map_itr == file_extension_to_voxelgrid_read_function.end()) {
160  "Read geometry::VoxelGrid failed: unknown file extension.");
161  return false;
162  }
163  bool success = map_itr->second(filename, voxelgrid, print_progress);
164  utility::LogDebug("Read geometry::VoxelGrid: {:d} voxels.",
165  (int)voxelgrid.voxels_.size());
166  return success;
167 }
168 
169 bool WriteVoxelGrid(const std::string &filename,
170  const geometry::VoxelGrid &voxelgrid,
171  bool write_ascii /* = false*/,
172  bool compressed /* = false*/,
173  bool print_progress) {
174  std::string filename_ext =
176  if (filename_ext.empty()) {
178  "Write geometry::VoxelGrid failed: unknown file extension.");
179  return false;
180  }
181  auto map_itr =
182  file_extension_to_voxelgrid_write_function.find(filename_ext);
183  if (map_itr == file_extension_to_voxelgrid_write_function.end()) {
185  "Write geometry::VoxelGrid failed: unknown file extension.");
186  return false;
187  }
188  bool success = map_itr->second(filename, voxelgrid, write_ascii, compressed,
189  print_progress);
190  utility::LogDebug("Write geometry::VoxelGrid: {:d} voxels.",
191  (int)voxelgrid.voxels_.size());
192  return success;
193 }
194 
195 bool ReadVoxelGridFromPLY(const std::string &filename,
196  geometry::VoxelGrid &voxelgrid,
197  bool print_progress) {
198  using namespace ply_voxelgrid_reader;
199 
200  p_ply ply_file = ply_open(filename.c_str(), nullptr, 0, nullptr);
201  if (!ply_file) {
202  utility::LogWarning("Read PLY failed: unable to open file: {}",
203  filename);
204  return false;
205  }
206  if (!ply_read_header(ply_file)) {
207  utility::LogWarning("Read PLY failed: unable to parse header.");
208  ply_close(ply_file);
209  return false;
210  }
211 
212  PLYReaderState state;
213  std::vector<geometry::Voxel> voxelgrid_ptr;
214  state.voxelgrid_ptr = &voxelgrid_ptr;
215  state.voxel_num = ply_set_read_cb(ply_file, "vertex", "x",
216  ReadVoxelCallback, &state, 0);
217  ply_set_read_cb(ply_file, "vertex", "y", ReadVoxelCallback, &state, 1);
218  ply_set_read_cb(ply_file, "vertex", "z", ReadVoxelCallback, &state, 2);
219 
220  if (state.voxel_num <= 0) {
221  utility::LogWarning("Read PLY failed: number of vertex <= 0.");
222  ply_close(ply_file);
223  return false;
224  }
225 
226  state.color_num = ply_set_read_cb(ply_file, "vertex", "red",
227  ReadColorCallback, &state, 0);
228  ply_set_read_cb(ply_file, "vertex", "green", ReadColorCallback, &state, 1);
229  ply_set_read_cb(ply_file, "vertex", "blue", ReadColorCallback, &state, 2);
230 
231  ply_set_read_cb(ply_file, "origin", "x", ReadOriginCallback, &state, 0);
232  ply_set_read_cb(ply_file, "origin", "y", ReadOriginCallback, &state, 1);
233  ply_set_read_cb(ply_file, "origin", "z", ReadOriginCallback, &state, 2);
234  ply_set_read_cb(ply_file, "rotation", "r00", ReadOriginRotationCallback,
235  &state, 0);
236  ply_set_read_cb(ply_file, "rotation", "r01", ReadOriginRotationCallback,
237  &state, 1);
238  ply_set_read_cb(ply_file, "rotation", "r02", ReadOriginRotationCallback,
239  &state, 2);
240  ply_set_read_cb(ply_file, "rotation", "r10", ReadOriginRotationCallback,
241  &state, 3);
242  ply_set_read_cb(ply_file, "rotation", "r11", ReadOriginRotationCallback,
243  &state, 4);
244  ply_set_read_cb(ply_file, "rotation", "r12", ReadOriginRotationCallback,
245  &state, 5);
246  ply_set_read_cb(ply_file, "rotation", "r20", ReadOriginRotationCallback,
247  &state, 6);
248  ply_set_read_cb(ply_file, "rotation", "r21", ReadOriginRotationCallback,
249  &state, 7);
250  ply_set_read_cb(ply_file, "rotation", "r22", ReadOriginRotationCallback,
251  &state, 8);
252  ply_set_read_cb(ply_file, "voxel_size", "val", ReadScaleCallback, &state,
253  0);
254 
255  state.voxel_index = 0;
256  state.color_index = 0;
257 
258  voxelgrid_ptr.clear();
259  voxelgrid_ptr.resize(state.voxel_num);
260  utility::ConsoleProgressBar progress_bar(state.voxel_num + state.color_num,
261  "Reading PLY: ", print_progress);
262  state.progress_bar = &progress_bar;
263 
264  if (!ply_read(ply_file)) {
265  utility::LogWarning("Read PLY failed: unable to read file: {}",
266  filename);
267  ply_close(ply_file);
268  return false;
269  }
270 
271  voxelgrid.Clear();
272  for (auto &it : voxelgrid_ptr) {
273  if (state.color_num > 0)
274  voxelgrid.AddVoxel(geometry::Voxel(it.grid_index_, it.color_));
275  else
276  voxelgrid.AddVoxel(geometry::Voxel(it.grid_index_));
277  }
278  voxelgrid.origin_ = state.origin;
279  voxelgrid.rotation_ = state.rotation;
280  voxelgrid.voxel_size_ = state.voxel_size;
281 
282  ply_close(ply_file);
283  return true;
284 }
285 
286 bool WriteVoxelGridToPLY(const std::string &filename,
287  const geometry::VoxelGrid &voxelgrid,
288  bool write_ascii /* = false*/,
289  bool compressed /* = false*/,
290  bool print_progress) {
291  if (voxelgrid.IsEmpty()) {
292  utility::LogWarning("Write PLY failed: voxelgrid has 0 voxels.");
293  return false;
294  }
295 
296  p_ply ply_file = ply_create(filename.c_str(),
297  write_ascii ? PLY_ASCII : PLY_LITTLE_ENDIAN,
298  nullptr, 0, nullptr);
299  if (!ply_file) {
300  utility::LogWarning("Write PLY failed: unable to open file: {}",
301  filename);
302  return false;
303  }
304  ply_add_comment(ply_file, "Created by cloudViewer");
305  ply_add_element(ply_file, "origin", 1);
306  ply_add_property(ply_file, "x", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
307  ply_add_property(ply_file, "y", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
308  ply_add_property(ply_file, "z", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
309  ply_add_element(ply_file, "rotation", 1);
310  ply_add_property(ply_file, "r00", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
311  ply_add_property(ply_file, "r01", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
312  ply_add_property(ply_file, "r02", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
313  ply_add_property(ply_file, "r10", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
314  ply_add_property(ply_file, "r11", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
315  ply_add_property(ply_file, "r12", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
316  ply_add_property(ply_file, "r20", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
317  ply_add_property(ply_file, "r21", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
318  ply_add_property(ply_file, "r22", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
319  ply_add_element(ply_file, "voxel_size", 1);
320  ply_add_property(ply_file, "val", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
321 
322  ply_add_element(ply_file, "vertex",
323  static_cast<long>(voxelgrid.voxels_.size()));
324  // PLY_UINT could be used for x, y, z but PLY_DOUBLE used instead due to
325  // compatibility issue.
326  ply_add_property(ply_file, "x", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
327  ply_add_property(ply_file, "y", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
328  ply_add_property(ply_file, "z", PLY_DOUBLE, PLY_DOUBLE, PLY_DOUBLE);
329  if (voxelgrid.HasColors()) {
330  ply_add_property(ply_file, "red", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
331  ply_add_property(ply_file, "green", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
332  ply_add_property(ply_file, "blue", PLY_UCHAR, PLY_UCHAR, PLY_UCHAR);
333  }
334 
335  if (!ply_write_header(ply_file)) {
336  utility::LogWarning("Write PLY failed: unable to write header.");
337  ply_close(ply_file);
338  return false;
339  }
340 
342  static_cast<size_t>(voxelgrid.voxels_.size()),
343  "Writing PLY: ", print_progress);
344 
345  const Eigen::Vector3d &origin = voxelgrid.origin_;
346  ply_write(ply_file, origin(0));
347  ply_write(ply_file, origin(1));
348  ply_write(ply_file, origin(2));
349 
350  const Eigen::Matrix3d &rotation = voxelgrid.rotation_;
351  ply_write(ply_file, rotation(0, 0));
352  ply_write(ply_file, rotation(0, 1));
353  ply_write(ply_file, rotation(0, 2));
354  ply_write(ply_file, rotation(1, 0));
355  ply_write(ply_file, rotation(1, 1));
356  ply_write(ply_file, rotation(1, 2));
357  ply_write(ply_file, rotation(2, 0));
358  ply_write(ply_file, rotation(2, 1));
359  ply_write(ply_file, rotation(2, 2));
360 
361  ply_write(ply_file, voxelgrid.voxel_size_);
362 
363  for (auto &it : voxelgrid.voxels_) {
364  const geometry::Voxel &voxel = it.second;
365  ply_write(ply_file, voxel.grid_index_(0));
366  ply_write(ply_file, voxel.grid_index_(1));
367  ply_write(ply_file, voxel.grid_index_(2));
368 
369  auto rgb = utility::ColorToUint8(voxel.color_);
370  ply_write(ply_file, rgb(0));
371  ply_write(ply_file, rgb(1));
372  ply_write(ply_file, rgb(2));
373 
374  ++progress_bar;
375  }
376 
377  ply_close(ply_file);
378  return true;
379 }
380 
381 } // namespace io
382 } // namespace cloudViewer
IsAscii write_ascii
Compressed compressed
std::string filename
filament::Texture::InternalFormat format
long voxel_index
Definition: VoxelGridIO.cpp:29
cloudViewer::utility::ConsoleProgressBar * progress_bar
Definition: VoxelGridIO.cpp:24
long voxel_num
Definition: VoxelGridIO.cpp:30
Eigen::Matrix3d rotation
Definition: VoxelGridIO.cpp:27
std::vector< geometry::Voxel > * voxelgrid_ptr
Definition: VoxelGridIO.cpp:25
double voxel_size
Definition: VoxelGridIO.cpp:28
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
long color_index
Definition: VoxelGridIO.cpp:31
long color_num
Definition: VoxelGridIO.cpp:32
VoxelGrid is a collection of voxels which are aligned in grid.
Definition: VoxelGrid.h:64
std::unordered_map< Eigen::Vector3i, Voxel, cloudViewer::utility::hash_eigen< Eigen::Vector3i > > voxels_
Voxels contained in voxel grid.
Definition: VoxelGrid.h:274
double voxel_size_
Size of the voxel.
Definition: VoxelGrid.h:264
virtual bool IsEmpty() const override
Definition: VoxelGrid.h:82
bool HasColors() const
Returns true if the voxel grid contains voxel colors.
Definition: VoxelGrid.h:108
Eigen::Vector3d origin_
Coorindate of the origin point.
Definition: VoxelGrid.h:266
void AddVoxel(const Voxel &voxel)
Add a voxel with specified grid index and color.
Definition: VoxelGrid.cpp:233
Base Voxel class, containing grid id and color.
Definition: VoxelGrid.h:38
Eigen::Vector3i grid_index_
Grid coordinate index of the voxel.
Definition: VoxelGrid.h:56
Eigen::Vector3d color_
Color of the voxel.
Definition: VoxelGrid.h:58
#define LogWarning(...)
Definition: Logging.h:72
#define LogDebug(...)
Definition: Logging.h:90
normal_z rgb
bool WriteVoxelGridToPLY(const std::string &filename, const geometry::VoxelGrid &voxelgrid, bool write_ascii=false, bool compressed=false, bool print_progress=false)
std::shared_ptr< geometry::VoxelGrid > CreateVoxelGridFromFile(const std::string &filename, const std::string &format="auto", bool print_progress=false)
bool WriteVoxelGrid(const std::string &filename, const geometry::VoxelGrid &voxelgrid, bool write_ascii=false, bool compressed=false, bool print_progress=false)
bool ReadVoxelGridFromPLY(const std::string &filename, geometry::VoxelGrid &voxelgrid, bool print_progress=false)
bool ReadVoxelGrid(const std::string &filename, geometry::VoxelGrid &voxelgrid, const std::string &format="auto", bool print_progress=false)
std::string GetFileExtensionInLowerCase(const std::string &filename)
Definition: FileSystem.cpp:281
Eigen::Vector3uint8 ColorToUint8(const Eigen::Vector3d &color)
Definition: Eigen.cpp:269
Generic file read and write utility for python interface.