31 struct PLYReaderState {
46 int ReadAttributeCallback(p_ply_argument argument) {
47 PLYReaderState *state_ptr;
49 ply_get_argument_user_data(argument,
reinterpret_cast<void **
>(&state_ptr),
51 std::shared_ptr<PLYReaderState::AttrState> &attr_state =
52 state_ptr->id_to_attr_state_[id];
53 if (attr_state->current_size_ >= attr_state->size_) {
57 T *data_ptr =
static_cast<T *
>(attr_state->data_ptr_);
58 const int64_t index = attr_state->stride_ * attr_state->current_size_ +
60 data_ptr[index] =
static_cast<T
>(ply_get_argument_value(argument));
62 ++attr_state->current_size_;
64 if (attr_state->offset_ == 0 && attr_state->current_size_ % 1000 == 0) {
65 state_ptr->progress_bar_->Update(attr_state->current_size_);
72 std::string GetDtypeString(e_ply_type
type) {
73 if (
type == PLY_INT8) {
75 }
else if (
type == PLY_UINT8) {
77 }
else if (
type == PLY_INT16) {
79 }
else if (
type == PLY_UINT16) {
81 }
else if (
type == PLY_INT32) {
83 }
else if (
type == PLY_UIN32) {
85 }
else if (
type == PLY_FLOAT32) {
87 }
else if (
type == PLY_FLOAT64) {
112 core::Dtype GetDtype(e_ply_type
type) {
119 }
else if (
type == PLY_UINT16) {
133 std::tuple<std::string, int, int> GetNameStrideOffsetForAttribute(
134 const std::string &
name) {
136 if (
name ==
"x")
return std::make_tuple(
"positions", 3, 0);
137 if (
name ==
"y")
return std::make_tuple(
"positions", 3, 1);
138 if (
name ==
"z")
return std::make_tuple(
"positions", 3, 2);
141 if (
name ==
"nx")
return std::make_tuple(
"normals", 3, 0);
142 if (
name ==
"ny")
return std::make_tuple(
"normals", 3, 1);
143 if (
name ==
"nz")
return std::make_tuple(
"normals", 3, 2);
146 if (
name ==
"red")
return std::make_tuple(
"colors", 3, 0);
147 if (
name ==
"green")
return std::make_tuple(
"colors", 3, 1);
148 if (
name ==
"blue")
return std::make_tuple(
"colors", 3, 2);
151 if (
name.rfind(std::string(
"f_dc") +
"_", 0) == 0) {
152 int offset = std::stoi(
name.substr(F_DC_SUFFIX_INDEX));
153 return std::make_tuple(
"f_dc", 3,
offset);
156 if (
name.rfind(std::string(
"f_rest") +
"_", 0) == 0) {
157 int offset = std::stoi(
name.substr(F_REST_SUFFIX_INDEX));
158 return std::make_tuple(
"f_rest", 0,
offset);
161 if (
name.rfind(std::string(
"scale") +
"_", 0) == 0) {
162 int offset = std::stoi(
name.substr(SCALE_SUFFIX_INDEX));
163 return std::make_tuple(
"scale", 3,
offset);
166 if (
name.rfind(std::string(
"rot") +
"_", 0) == 0) {
167 int offset = std::stoi(
name.substr(ROTATION_SUFFIX_INDEX));
168 return std::make_tuple(
"rot", 4,
offset);
171 return std::make_tuple(
name, 1, 0);
180 p_ply ply_file = ply_open(
filename.c_str(),
nullptr, 0,
nullptr);
186 if (!ply_read_header(ply_file)) {
192 PLYReaderState state;
194 const char *element_name;
195 long element_size = 0;
197 p_ply_element element = ply_get_next_element(ply_file,
nullptr);
199 ply_get_element_info(element, &element_name, &element_size);
200 if (std::string(element_name) ==
"vertex") {
203 element = ply_get_next_element(ply_file, element);
214 std::unordered_map<std::string, bool> primary_attr_init = {
215 {
"positions",
false}, {
"normals",
false}, {
"colors",
false},
216 {
"opacity",
false}, {
"rot",
false}, {
"scale",
false},
217 {
"f_dc",
false}, {
"f_rest",
false},
220 int f_rest_count = 0;
221 p_ply_property attribute = ply_get_next_property(element,
nullptr);
226 ply_get_property_info(attribute, &
name, &
type,
nullptr,
nullptr);
230 "Read PLY warning: skipping property \"{}\", unsupported "
234 auto attr_state = std::make_shared<PLYReaderState::AttrState>();
236 long id =
static_cast<long>(state.id_to_attr_state_.size());
238 size = ply_set_read_cb(ply_file, element_name,
name,
239 ReadAttributeCallback<scalar_t>, &state,
242 if (
size != element_size) {
244 "Total size of property {} ({}) is not equal to "
246 name,
size, element_name, element_size);
249 const std::string attr_name = std::string(
name);
251 GetNameStrideOffsetForAttribute(attr_name);
252 if (
name ==
"f_rest") {
255 attr_state->name_ =
name;
256 attr_state->stride_ =
stride;
257 attr_state->offset_ =
offset;
259 if (primary_attr_init.count(
name)) {
260 if (primary_attr_init.at(
name) ==
false) {
264 primary_attr_init[
name] =
true;
272 attr_state->size_ = element_size;
273 attr_state->current_size_ = 0;
274 state.id_to_attr_state_.push_back(attr_state);
276 attribute = ply_get_next_property(element, attribute);
279 if (f_rest_count > 0) {
280 if (f_rest_count % 3 != 0) {
282 "Read PLY failed: 3DGS f_rest attribute has {} elements "
283 "per point, which is not divisible by 3.",
292 for (
auto &attr_state : state.id_to_attr_state_) {
293 if (attr_state->name_ ==
"f_rest") {
294 attr_state->data_ptr_ =
296 attr_state->stride_ = f_rest_count;
303 state.progress_bar_ = &reporter;
305 if (!ply_read(ply_file)) {
336 "Data-type {} is not supported in WritePointCloudToPLY. "
337 "Supported data-types include UInt8, UInt16, Int32, Float32 "
343 struct AttributePtr {
344 AttributePtr(
const core::Dtype &dtype,
345 const void *data_ptr,
346 const int &group_size)
377 for (
auto const &it : t_map) {
378 if (it.first ==
"positions" || it.first ==
"normals" ||
379 it.first ==
"colors") {
380 if (it.second.GetLength() != num_points) {
382 "Write PLY failed: Points ({}) and {} ({}) have "
383 "different lengths.",
384 num_points, it.first, it.second.GetLength());
388 auto shape = it.second.GetShape();
391 if (shape.size() < 2 || shape[0] != num_points) {
393 "Write PLY failed. PointCloud contains {} attribute "
394 "which is not supported by PLY IO. Only points, "
395 "normals, colors and attributes with shape "
396 "(num_points, channels, ...) are supported. Expected "
397 "shape: {{{}, ...}} but got {}.",
398 it.first, num_points, it.second.GetShape().ToString());
414 ply_add_comment(ply_file,
"Created by CloudViewer");
415 ply_add_element(ply_file,
"vertex", num_points);
417 std::vector<AttributePtr> attribute_ptrs;
418 attribute_ptrs.emplace_back(t_map[
"positions"].GetDtype(),
419 t_map[
"positions"].GetDataPtr(), 3);
421 e_ply_type pointType = GetPlyType(t_map[
"positions"].GetDtype());
422 ply_add_property(ply_file,
"x", pointType, pointType, pointType);
423 ply_add_property(ply_file,
"y", pointType, pointType, pointType);
424 ply_add_property(ply_file,
"z", pointType, pointType, pointType);
427 attribute_ptrs.emplace_back(t_map[
"normals"].GetDtype(),
428 t_map[
"normals"].GetDataPtr(), 3);
430 e_ply_type pointNormalsType = GetPlyType(t_map[
"normals"].GetDtype());
431 ply_add_property(ply_file,
"nx", pointNormalsType, pointNormalsType,
433 ply_add_property(ply_file,
"ny", pointNormalsType, pointNormalsType,
435 ply_add_property(ply_file,
"nz", pointNormalsType, pointNormalsType,
440 attribute_ptrs.emplace_back(t_map[
"colors"].GetDtype(),
441 t_map[
"colors"].GetDataPtr(), 3);
443 e_ply_type pointColorType = GetPlyType(t_map[
"colors"].GetDtype());
444 ply_add_property(ply_file,
"red", pointColorType, pointColorType,
446 ply_add_property(ply_file,
"green", pointColorType, pointColorType,
448 ply_add_property(ply_file,
"blue", pointColorType, pointColorType,
457 e_ply_type attributeType;
458 for (
auto const &it : t_map) {
459 if (it.first ==
"positions" || it.first ==
"colors" ||
460 it.first ==
"normals")
462 auto shape = it.second.GetShape();
464 if (shape.size() == 2) {
465 group_size = shape[1];
466 }
else if (shape.size() >= 3) {
467 group_size = shape[1] * shape[2];
469 if (group_size == 1) {
470 attribute_ptrs.emplace_back(it.second.GetDtype(),
471 it.second.GetDataPtr(), 1);
472 attributeType = GetPlyType(it.second.GetDtype());
473 ply_add_property(ply_file, it.first.c_str(), attributeType,
474 attributeType, attributeType);
476 for (
int ch = 0; ch < group_size; ch++) {
478 attributeType = GetPlyType(it.second.GetDtype());
479 ply_add_property(ply_file, prop_name.c_str(), attributeType,
480 attributeType, attributeType);
482 attribute_ptrs.emplace_back(it.second.GetDtype(),
483 it.second.GetDataPtr(), group_size);
487 if (!ply_write_header(ply_file)) {
496 for (int64_t i = 0; i < num_points; i++) {
497 for (
auto it : attribute_ptrs) {
499 const scalar_t *data_ptr =
500 static_cast<const scalar_t *>(it.data_ptr_);
501 for (int idx_offset = it.group_size_ * i;
502 idx_offset < it.group_size_ * (i + 1); ++idx_offset) {
503 ply_write(ply_file, data_ptr[idx_offset]);
#define DISPATCH_DTYPE_TO_TEMPLATE(DTYPE,...)
std::vector< std::shared_ptr< AttrState > > id_to_attr_state_
utility::CountingProgressReporter * progress_bar_
cmdLineReadable * params[]
std::string ToString() const
int64_t GetLength() const
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
A point cloud contains a list of 3D points.
bool IsGaussianSplat() const
core::Tensor & GetPointPositions()
Get the value of the "positions" attribute. Convenience function.
bool IsEmpty() const override
Returns !HasPointPositions().
PointCloud To(const core::Device &device, bool copy=false) const
core::Device GetDevice() const override
Returns the device attribute of this PointCloud.
bool HasPointNormals() const
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
void SetPointAttr(const std::string &key, const core::Tensor &value)
bool HasPointColors() const
TensorMap Contiguous() const
void SetTotal(int64_t total)
bool Update(int64_t count)
__host__ __device__ float length(float2 v)
bool WritePointCloudToPLY(const std::string &filename, const geometry::PointCloud &pointcloud, const cloudViewer::io::WritePointCloudOption ¶ms)
bool ReadPointCloudFromPLY(const std::string &filename, geometry::PointCloud &pointcloud, const cloudViewer::io::ReadPointCloudOption ¶ms)
Generic file read and write utility for python interface.
std::string to_string(const T &n)
Optional parameters to ReadPointCloud.
Optional parameters to WritePointCloud.