ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
MessageUtils.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 
9 
10 #include <Logging.h>
11 
12 #include <zmq.hpp>
13 
16 
17 using namespace cloudViewer::utility;
19 
20 namespace cloudViewer {
21 namespace io {
22 namespace rpc {
23 
24 std::shared_ptr<messages::Status> UnpackStatusFromReply(
25  const zmq::message_t& msg, size_t& offset, bool& ok) {
26  ok = false;
27  if (msg.size() <= offset) {
28  return std::shared_ptr<messages::Status>();
29  };
30 
31  messages::Reply reply;
32  messages::Status status;
33  try {
34  auto obj_handle =
35  msgpack::unpack((char*)msg.data(), msg.size(), offset);
36  obj_handle.get().convert(reply);
37  if (reply.msg_id != status.MsgId()) {
38  LogDebug("Expected msg with id {} but got {}", status.MsgId(),
39  reply.msg_id);
40  } else {
41  auto status_obj_handle =
42  msgpack::unpack((char*)msg.data(), msg.size(), offset);
43  status_obj_handle.get().convert(status);
44  ok = true;
45  }
46  } catch (std::exception& e) {
47  LogDebug("Failed to parse message: {}", e.what());
48  offset = msg.size();
49  }
50  return std::make_shared<messages::Status>(status);
51 }
52 
53 bool ReplyIsOKStatus(const zmq::message_t& msg) {
54  size_t offset = 0;
55  return ReplyIsOKStatus(msg, offset);
56 }
57 
58 bool ReplyIsOKStatus(const zmq::message_t& msg, size_t& offset) {
59  bool ok;
60  auto status = UnpackStatusFromReply(msg, offset, ok);
61  if (ok && status && 0 == status->code) {
62  return true;
63  }
64  return false;
65 }
66 
67 std::string CreateSerializedRequestMessage(const std::string& msg_id) {
68  messages::Request request{msg_id};
69  msgpack::sbuffer sbuf;
70  msgpack::pack(sbuf, request);
71  return std::string(sbuf.data(), sbuf.size());
72 }
73 
74 std::tuple<const void*, size_t> GetZMQMessageDataAndSize(
75  const zmq::message_t& msg) {
76  return std::make_tuple(msg.data(), msg.size());
77 }
78 
79 std::tuple<int32_t, std::string> GetStatusCodeAndStr(
80  const messages::Status& status) {
81  return std::make_tuple(status.code, status.str);
82 }
83 
84 std::shared_ptr<zmq::message_t> CreateStatusOKMsg() {
85  auto OK = messages::Status::OK();
86  msgpack::sbuffer sbuf;
87  messages::Reply reply{OK.MsgId()};
88  msgpack::pack(sbuf, reply);
89  msgpack::pack(sbuf, OK);
90  return std::shared_ptr<zmq::message_t>(
91  new zmq::message_t(sbuf.data(), sbuf.size()));
92 }
93 
98  auto TypeStrToDtype = [](const std::string& ts) {
99  if ("<f4" == ts) {
100  return core::Float32;
101  } else if ("<f8" == ts) {
102  return core::Float64;
103  } else if ("|i1" == ts) {
104  return core::Int8;
105  } else if ("<i2" == ts) {
106  return core::Int16;
107  } else if ("<i4" == ts) {
108  return core::Int32;
109  } else if ("<i8" == ts) {
110  return core::Int64;
111  } else if ("|u1" == ts) {
112  return core::UInt8;
113  } else if ("<u2" == ts) {
114  return core::UInt16;
115  } else if ("<u4" == ts) {
116  return core::UInt32;
117  } else if ("<u8" == ts) {
118  return core::UInt64;
119  }
120  LogError("Unsupported type {}. Cannot convert to Tensor.", ts);
121  return core::Undefined;
122  };
123 
124  core::Tensor result(array.shape, TypeStrToDtype(array.type));
125  memcpy(result.GetDataPtr(), array.data.ptr, array.data.size);
126 
127  return result;
128 }
129 
131 static std::map<std::string, messages::Array> TensorMapToArrayMap(
132  const t::geometry::TensorMap& tensor_map) {
133  std::map<std::string, messages::Array> result;
134  for (auto item : tensor_map) {
135  result[item.first] = messages::Array::FromTensor(item.second);
136  }
137  return result;
138 }
139 
141  std::string& errstr) {
142  Material material(mesh_data.material);
143  if (mesh_data.material.empty()) return material;
144  for (const auto& scalar : mesh_data.material_scalar_attributes)
145  material.SetScalarProperty(scalar.first, scalar.second);
146  for (const auto& vec : mesh_data.material_vector_attributes)
147  material.SetVectorProperty(vec.first,
148  Eigen::Vector4f(vec.second.data()));
149  // Allow 2, 3 dim images. Don't restrict n_channels to allow channel packing
150  const std::vector<int64_t> expected_shapes[] = {{-1, -1}, {-1, -1, -1}};
151  for (const auto& texture : mesh_data.texture_maps) {
152  std::string es(texture.first + ": ");
153  bool is_right_shape = false;
154  for (const auto& shape : expected_shapes) {
155  is_right_shape = texture.second.CheckShape(shape, es);
156  if (is_right_shape) break;
157  }
158  if (!is_right_shape) {
159  errstr = errstr.empty() ? es : errstr + '\n' + es;
160  } else {
161  material.SetTextureMap(
162  texture.first,
163  t::geometry::Image(ArrayToTensor(texture.second)));
164  }
165  }
166  return material;
167 }
168 
169 std::shared_ptr<t::geometry::Geometry> MeshDataToGeometry(
170  const messages::MeshData& mesh_data) {
171  std::string errstr;
172  if (mesh_data.CheckMessage(errstr)) {
173  if (mesh_data.O3DTypeIsTriangleMesh() ||
174  mesh_data.faces.CheckNonEmpty()) {
175  auto mesh = std::make_shared<t::geometry::TriangleMesh>();
176  if (mesh_data.vertices.CheckNonEmpty()) {
177  mesh->SetVertexPositions(ArrayToTensor(mesh_data.vertices));
178  }
179  for (auto item : mesh_data.vertex_attributes) {
180  mesh->SetVertexAttr(item.first, ArrayToTensor(item.second));
181  }
182  if (mesh_data.faces.CheckNonEmpty()) {
183  if (mesh_data.faces.CheckShape({-1, 3}, errstr)) {
184  mesh->SetTriangleIndices(ArrayToTensor(mesh_data.faces));
185  } else {
186  errstr = "Invalid shape for faces, " + errstr;
187  }
188  }
189  for (auto item : mesh_data.face_attributes) {
190  mesh->SetTriangleAttr(item.first, ArrayToTensor(item.second));
191  }
192  mesh->SetMaterial(GetMaterialFromMeshData(mesh_data, errstr));
193  if (!errstr.empty()) {
194  LogWarning("MeshDataToGeometry: {}", errstr);
195  }
196  return mesh;
197  } else if (mesh_data.O3DTypeIsLineSet() ||
198  mesh_data.lines.CheckNonEmpty()) {
199  auto ls = std::make_shared<t::geometry::LineSet>();
200  if (mesh_data.vertices.CheckNonEmpty()) {
201  ls->SetPointPositions(ArrayToTensor(mesh_data.vertices));
202  }
203  for (auto item : mesh_data.vertex_attributes) {
204  ls->SetPointAttr(item.first, ArrayToTensor(item.second));
205  }
206  if (mesh_data.lines.CheckNonEmpty()) {
207  if (mesh_data.lines.CheckShape({-1, 2}, errstr)) {
208  ls->SetLineIndices(ArrayToTensor(mesh_data.lines));
209  } else {
210  errstr = "Invalid shape for lines, " + errstr;
211  }
212  }
213  for (auto item : mesh_data.line_attributes) {
214  ls->SetLineAttr(item.first, ArrayToTensor(item.second));
215  }
216  ls->SetMaterial(GetMaterialFromMeshData(mesh_data, errstr));
217  if (!errstr.empty()) {
218  LogWarning("MeshDataToGeometry: {}", errstr);
219  }
220  return ls;
221  } else if (mesh_data.O3DTypeIsPointCloud() ||
222  mesh_data.vertices.CheckNonEmpty()) {
223  auto pcd = std::make_shared<t::geometry::PointCloud>();
224  if (mesh_data.vertices.CheckNonEmpty()) {
225  pcd->SetPointPositions(ArrayToTensor(mesh_data.vertices));
226  }
227  for (auto item : mesh_data.vertex_attributes) {
228  pcd->SetPointAttr(item.first, ArrayToTensor(item.second));
229  }
230  pcd->SetMaterial(GetMaterialFromMeshData(mesh_data, errstr));
231  if (!errstr.empty()) {
232  LogWarning("MeshDataToGeometry: {}", errstr);
233  }
234  return pcd;
235  } else {
236  errstr += "MeshData has no triangles, lines, or vertices";
237  }
238  }
239  LogWarning("MeshDataToGeometry: {}", errstr);
240  return std::shared_ptr<t::geometry::Geometry>();
241 }
242 
244  const Material& material) {
245  if (!material.IsValid()) return;
246  mesh_data.material = material.GetMaterialName();
247  auto scalars = material.GetScalarProperties();
248  mesh_data.material_scalar_attributes =
249  std::map<std::string, float>(scalars.begin(), scalars.end());
250  for (const auto& item : material.GetVectorProperties()) {
251  mesh_data.material_vector_attributes[item.first] = {
252  item.second[0], item.second[1], item.second[2], item.second[3]};
253  }
254  for (const auto& item : material.GetTextureMaps()) {
255  mesh_data.texture_maps[item.first] =
256  messages::Array::FromTensor(item.second.AsTensor());
257  }
258 }
259 
261  const t::geometry::TriangleMesh& trimesh) {
262  messages::MeshData mesh_data;
263 
264  // vertices
265  auto vertex_attributes = trimesh.GetVertexAttr();
266  mesh_data.vertex_attributes = TensorMapToArrayMap(vertex_attributes);
267  if (mesh_data.vertex_attributes.count("positions")) {
268  mesh_data.vertices = mesh_data.vertex_attributes["positions"];
269  mesh_data.vertex_attributes.erase("positions");
270  } else {
271  LogError("GeometryToMeshData: TriangleMesh has no vertices!");
272  }
273 
274  // triangles
275  auto face_attributes = trimesh.GetTriangleAttr();
276  mesh_data.face_attributes = TensorMapToArrayMap(face_attributes);
277  if (mesh_data.face_attributes.count("indices")) {
278  mesh_data.faces = mesh_data.face_attributes["indices"];
279  mesh_data.face_attributes.erase("indices");
280  }
281 
282  mesh_data.SetO3DTypeToTriangleMesh();
283 
284  // material
285  AddMaterialToMeshData(mesh_data, trimesh.GetMaterial());
286 
287  return mesh_data;
288 }
289 
291  messages::MeshData mesh_data;
292 
293  // points
294  auto point_attributes = pcd.GetPointAttr();
295  mesh_data.vertex_attributes = TensorMapToArrayMap(point_attributes);
296  if (mesh_data.vertex_attributes.count("positions")) {
297  mesh_data.vertices = mesh_data.vertex_attributes["positions"];
298  mesh_data.vertex_attributes.erase("positions");
299  } else {
300  LogError("GeometryToMeshData: PointCloud has no points!");
301  }
302 
303  mesh_data.SetO3DTypeToPointCloud();
304 
305  // material
306  AddMaterialToMeshData(mesh_data, pcd.GetMaterial());
307 
308  return mesh_data;
309 }
310 
312  messages::MeshData mesh_data;
313 
314  // points
315  auto point_attributes = ls.GetPointAttr();
316  mesh_data.vertex_attributes = TensorMapToArrayMap(point_attributes);
317  if (mesh_data.vertex_attributes.count("positions")) {
318  mesh_data.vertices = mesh_data.vertex_attributes["positions"];
319  mesh_data.vertex_attributes.erase("positions");
320  } else {
321  LogError("GeometryToMeshData: LineSet has no points!");
322  }
323 
324  // lines
325  auto line_attributes = ls.GetLineAttr();
326  mesh_data.line_attributes = TensorMapToArrayMap(line_attributes);
327  if (mesh_data.line_attributes.count("indices")) {
328  mesh_data.lines = mesh_data.line_attributes["indices"];
329  mesh_data.line_attributes.erase("indices");
330  }
331 
332  mesh_data.SetO3DTypeToLineSet();
333 
334  // material
335  AddMaterialToMeshData(mesh_data, ls.GetMaterial());
336 
337  return mesh_data;
338 }
339 
340 std::tuple<std::string, double, std::shared_ptr<t::geometry::Geometry>>
341 DataBufferToMetaGeometry(std::string& data) {
342  const char* buffer = data.data();
343  size_t buffer_size = data.size();
344 
345  auto limits = msgpack::unpack_limit(0xffffffff, // array
346  0xffffffff, // map
347  65536, // str
348  0xffffffff, // bin
349  0xffffffff, // ext
350  100 // depth
351  );
352 
353  messages::Request req;
354  try {
355  size_t offset = 0;
356  auto obj_handle = msgpack::unpack(buffer, buffer_size, offset, nullptr,
357  nullptr, limits);
358  auto obj = obj_handle.get();
359  req = obj.as<messages::Request>();
360 
361  if (messages::SetMeshData::MsgId() == req.msg_id) {
362  auto oh = msgpack::unpack(buffer, buffer_size, offset, nullptr,
363  nullptr, limits);
364  auto mesh_obj = oh.get();
366  msg = mesh_obj.as<messages::SetMeshData>();
367  auto result = MeshDataToGeometry(msg.data);
368  double time = msg.time;
369  return std::tie(msg.path, time, result);
370  } else {
371  LogWarning(
372  "DataBufferToMetaGeometry: Wrong message id. Expected "
373  "'{}' but got '{}'.",
374  messages::SetMeshData::MsgId(), req.msg_id);
375  }
376  } catch (std::exception& err) {
377  LogWarning("DataBufferToMetaGeometry: {}", err.what());
378  }
379  return std::forward_as_tuple(std::string(), 0.,
380  std::shared_ptr<t::geometry::Geometry>());
381 }
382 
383 } // namespace rpc
384 } // namespace io
385 } // namespace cloudViewer
int offset
core::Tensor result
Definition: VtkUtils.cpp:76
visualization::rendering::Material & GetMaterial()
Get material associated with this Geometry.
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
A LineSet contains points and lines joining them and optionally attributes on the points and lines.
Definition: LineSet.h:85
const TensorMap & GetLineAttr() const
Getter for line_attr_ TensorMap. Used in Pybind.
Definition: LineSet.h:142
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: LineSet.h:124
A point cloud contains a list of 3D points.
Definition: PointCloud.h:82
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: PointCloud.h:111
A triangle mesh contains vertices and triangles.
Definition: TriangleMesh.h:98
const TensorMap & GetVertexAttr() const
Getter for vertex_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:133
const TensorMap & GetTriangleAttr() const
Getter for triangle_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:159
const std::string & GetMaterialName() const
Get the name of the material.
Definition: Material.h:57
const VectorPropertyMap & GetVectorProperties() const
Returns the map of vector properties.
Definition: Material.h:87
const ScalarPropertyMap & GetScalarProperties() const
Returns the map of scalar properties.
Definition: Material.h:74
void SetVectorProperty(const std::string &key, const Eigen::Vector4f &value)
Definition: Material.h:120
void SetScalarProperty(const std::string &key, float value)
Definition: Material.h:111
void SetTextureMap(const std::string &key, const t::geometry::Image &image)
Definition: Material.cpp:34
const TextureMaps & GetTextureMaps() const
Returns the texture map map.
Definition: Material.h:63
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
const Dtype Undefined
Definition: Dtype.cpp:41
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 Material GetMaterialFromMeshData(const messages::MeshData &mesh_data, std::string &errstr)
std::shared_ptr< t::geometry::Geometry > MeshDataToGeometry(const messages::MeshData &mesh_data)
static std::map< std::string, messages::Array > TensorMapToArrayMap(const t::geometry::TensorMap &tensor_map)
Converts a TensorMap to an Array map.
std::shared_ptr< zmq::message_t > CreateStatusOKMsg()
std::tuple< std::string, double, std::shared_ptr< t::geometry::Geometry > > DataBufferToMetaGeometry(std::string &data)
std::tuple< const void *, size_t > GetZMQMessageDataAndSize(const zmq::message_t &msg)
std::string CreateSerializedRequestMessage(const std::string &msg_id)
Creates a serialized Request message for testing purposes.
std::shared_ptr< messages::Status > UnpackStatusFromReply(const zmq::message_t &msg, size_t &offset, bool &ok)
static core::Tensor ArrayToTensor(const messages::Array &array)
messages::MeshData GeometryToMeshData(const t::geometry::LineSet &ls)
static void AddMaterialToMeshData(messages::MeshData &mesh_data, const Material &material)
std::tuple< int32_t, std::string > GetStatusCodeAndStr(const messages::Status &status)
bool ReplyIsOKStatus(const zmq::message_t &msg, size_t &offset)
Generic file read and write utility for python interface.
bool CheckShape(const std::vector< int64_t > &expected_shape, std::string &errstr) const
Definition: Messages.h:176
bool CheckNonEmpty(std::string &errstr) const
Definition: Messages.h:212
msgpack::type::raw_ref data
Definition: Messages.h:143
std::vector< int64_t > shape
Definition: Messages.h:142
struct for storing MeshData, e.g., PointClouds, TriangleMesh, ..
Definition: Messages.h:255
std::map< std::string, float > material_scalar_attributes
Material scalar properties.
Definition: Messages.h:294
std::map< std::string, Array > vertex_attributes
Definition: Messages.h:268
std::map< std::string, Array > face_attributes
stores arbitrary attributes for each face
Definition: Messages.h:278
bool CheckMessage(std::string &errstr) const
Definition: Messages.h:368
std::map< std::string, std::array< float, 4 > > material_vector_attributes
Material vector[4] properties.
Definition: Messages.h:296
std::string material
Material for DrawableGeometry.
Definition: Messages.h:292
std::map< std::string, Array > line_attributes
stores arbitrary attributes for each line
Definition: Messages.h:289
std::map< std::string, Array > texture_maps
map of arrays that can be interpreted as textures
Definition: Messages.h:298
Array vertices
shape must be [num_verts,3]
Definition: Messages.h:265
std::string path
Path defining the location in the scene tree.
Definition: Messages.h:397
int32_t time
The time associated with this data.
Definition: Messages.h:399
MeshData data
The data to be set.
Definition: Messages.h:404
std::string str
string representation of the code
Definition: Messages.h:541
int32_t code
return code. 0 means everything is OK.
Definition: Messages.h:539