draco_publisher.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: BSD-3-Clause
2 // SPDX-FileCopyrightText: Czech Technical University in Prague .. 2019, paplhjak
3 
4 #include <memory>
5 #include <string>
6 #include <unordered_map>
7 #include <utility>
8 #include <vector>
9 
10 #include <draco/compression/expert_encode.h>
11 #include <draco/compression/encode.h>
12 #include <draco/point_cloud/point_cloud_builder.h>
13 
18 
19 #include <draco_point_cloud_transport/CompressedPointCloud2.h>
22 
24 {
25 
26 static std::unordered_map<std::string, draco::GeometryAttribute::Type> attributeTypes = {
27  {"x", draco::GeometryAttribute::Type::POSITION},
28  {"y", draco::GeometryAttribute::Type::POSITION},
29  {"z", draco::GeometryAttribute::Type::POSITION},
30  {"pos", draco::GeometryAttribute::Type::POSITION},
31  {"position", draco::GeometryAttribute::Type::POSITION},
32  {"vp_x", draco::GeometryAttribute::Type::POSITION},
33  {"vp_y", draco::GeometryAttribute::Type::POSITION},
34  {"vp_z", draco::GeometryAttribute::Type::POSITION},
35  {"rgb", draco::GeometryAttribute::Type::COLOR},
36  {"rgba", draco::GeometryAttribute::Type::COLOR},
37  {"r", draco::GeometryAttribute::Type::COLOR},
38  {"g", draco::GeometryAttribute::Type::COLOR},
39  {"b", draco::GeometryAttribute::Type::COLOR},
40  {"a", draco::GeometryAttribute::Type::COLOR},
41  {"nx", draco::GeometryAttribute::Type::NORMAL},
42  {"ny", draco::GeometryAttribute::Type::NORMAL},
43  {"nz", draco::GeometryAttribute::Type::NORMAL},
44  {"normal_x", draco::GeometryAttribute::Type::NORMAL},
45  {"normal_y", draco::GeometryAttribute::Type::NORMAL},
46  {"normal_z", draco::GeometryAttribute::Type::NORMAL},
47 };
48 
49 cras::expected<std::unique_ptr<draco::PointCloud>, std::string> convertPC2toDraco(
50  const sensor_msgs::PointCloud2& PC2, const std::string& topic, bool deduplicate, bool expert_encoding)
51 {
52  // object for conversion into Draco Point Cloud format
53  draco::PointCloudBuilder builder;
54  // number of points in point cloud
55  uint64_t number_of_points = PC2.height * PC2.width;
56  // initialize builder object, requires prior knowledge of point cloud size for buffer allocation
57  builder.Start(number_of_points);
58  // vector to hold IDs of attributes for builder object
59  std::vector<int> att_ids;
60 
61  // initialize to invalid
62  draco::GeometryAttribute::Type attribute_type = draco::GeometryAttribute::INVALID;
63  draco::DataType attribute_data_type = draco::DT_INVALID;
64 
65  // tracks if rgb/rgba was encountered (4 colors saved as one variable,
66  bool rgba_tweak = false;
67  bool rgba_tweak_64bit = false;
68 
69  // tracks if all necessary parameters were set for expert encoder
70  bool expert_settings_ok = true;
71 
72  std::string expert_attribute_data_type;
73 
74  // fill in att_ids with attributes from PointField[] fields
75  for (const auto& field : PC2.fields)
76  {
77  if (expert_encoding) // find attribute type in user specified parameters
78  {
79  rgba_tweak = false;
80  if (ros::param::getCached(topic + "/draco/attribute_mapping/attribute_type/" + field.name,
81  expert_attribute_data_type))
82  {
83  if (expert_attribute_data_type == "POSITION") // if data type is POSITION
84  {
85  attribute_type = draco::GeometryAttribute::POSITION;
86  }
87  else if (expert_attribute_data_type == "NORMAL") // if data type is NORMAL
88  {
89  attribute_type = draco::GeometryAttribute::NORMAL;
90  }
91  else if (expert_attribute_data_type == "COLOR") // if data type is COLOR
92  {
93  attribute_type = draco::GeometryAttribute::COLOR;
94  ros::param::getCached(topic + "/draco/attribute_mapping/rgba_tweak/" + field.name, rgba_tweak);
95  }
96  else if (expert_attribute_data_type == "TEX_COORD") // if data type is TEX_COORD
97  {
98  attribute_type = draco::GeometryAttribute::TEX_COORD;
99  }
100  else if (expert_attribute_data_type == "GENERIC") // if data type is GENERIC
101  {
102  attribute_type = draco::GeometryAttribute::GENERIC;
103  }
104  else
105  {
106  CRAS_ERROR_STREAM("Attribute data type not recognized for " + field.name + " field entry. "
107  "Using regular type recognition instead.");
108  expert_settings_ok = false;
109  }
110  }
111  else
112  {
113  CRAS_ERROR_STREAM("Attribute data type not specified for " + field.name + " field entry."
114  "Using regular type recognition instead.");
115  CRAS_INFO_STREAM("To set attribute type for " + field.name + " field entry, set " + topic +
116  "/draco/attribute_mapping/attribute_type/" + field.name);
117  expert_settings_ok = false;
118  }
119  }
120 
121  // find attribute type in recognized names
122  if ((!expert_encoding) || (!expert_settings_ok))
123  {
124  rgba_tweak = field.name == "rgb" || field.name == "rgba";
125  attribute_type = draco::GeometryAttribute::GENERIC;
126  const auto& it = attributeTypes.find(field.name);
127  if (it != attributeTypes.end())
128  {
129  attribute_type = it->second;
130  }
131  }
132 
133  // attribute data type switch
134  switch (field.datatype)
135  {
136  case sensor_msgs::PointField::INT8:attribute_data_type = draco::DT_INT8;
137  break;
138  case sensor_msgs::PointField::UINT8:attribute_data_type = draco::DT_UINT8;
139  break;
140  case sensor_msgs::PointField::INT16:attribute_data_type = draco::DT_INT16;
141  break;
142  case sensor_msgs::PointField::UINT16:attribute_data_type = draco::DT_UINT16;
143  break;
144  case sensor_msgs::PointField::INT32:attribute_data_type = draco::DT_INT32;
145  rgba_tweak_64bit = false;
146  break;
147  case sensor_msgs::PointField::UINT32:attribute_data_type = draco::DT_UINT32;
148  rgba_tweak_64bit = false;
149  break;
150  case sensor_msgs::PointField::FLOAT32:attribute_data_type = draco::DT_FLOAT32;
151  rgba_tweak_64bit = false;
152  break;
153  case sensor_msgs::PointField::FLOAT64:attribute_data_type = draco::DT_FLOAT64;
154  rgba_tweak_64bit = true;
155  break;
156  default:return cras::make_unexpected("Invalid data type in PointCloud2 to Draco conversion");
157  } // attribute data type switch end
158 
159  // add attribute to point cloud builder
160  if (rgba_tweak) // attribute is rgb/rgba color
161  {
162  if (rgba_tweak_64bit) // attribute data type is 64bits long, each color is encoded in 16bits
163  {
164  att_ids.push_back(builder.AddAttribute(attribute_type, 4 * field.count, draco::DT_UINT16));
165  }
166  else // attribute data type is 32bits long, each color is encoded in 8bits
167  {
168  att_ids.push_back(builder.AddAttribute(attribute_type, 4 * field.count, draco::DT_UINT8));
169  }
170  }
171  else // attribute is not rgb/rgba color, this is the default behavior
172  {
173  att_ids.push_back(builder.AddAttribute(attribute_type, field.count, attribute_data_type));
174  }
175  // Set attribute values for the last added attribute
176  if ((!att_ids.empty()) && (attribute_data_type != draco::DT_INVALID))
177  {
178  builder.SetAttributeValuesForAllPoints(
179  static_cast<int>(att_ids.back()), &PC2.data[0] + field.offset, PC2.point_step);
180  }
181  }
182  // finalize point cloud *** builder.Finalize(bool deduplicate) ***
183  std::unique_ptr<draco::PointCloud> pc = builder.Finalize(deduplicate);
184 
185  if (pc == nullptr)
186  {
187  return cras::make_unexpected("Conversion from sensor_msgs::PointCloud2 to Draco::PointCloud failed");
188  }
189 
190  // add metadata to point cloud
191  std::unique_ptr<draco::GeometryMetadata> metadata = std::make_unique<draco::GeometryMetadata>();
192 
193  if (deduplicate)
194  {
195  metadata->AddEntryInt("deduplicate", 1); // deduplication=true flag
196  }
197  else
198  {
199  metadata->AddEntryInt("deduplicate", 0); // deduplication=false flag
200  }
201  pc->AddMetadata(std::move(metadata));
202 
203  if ((pc->num_points() != number_of_points) && !deduplicate)
204  {
205  return cras::make_unexpected("Number of points in Draco::PointCloud differs from sensor_msgs::PointCloud2!");
206  }
207 
208  return std::move(pc); // std::move() has to be here for GCC 7
209 }
210 
212 {
213  return "draco";
214 }
215 
217  const sensor_msgs::PointCloud2& raw, const draco_point_cloud_transport::DracoPublisherConfig& config) const
218 {
219  // Remove invalid points if the cloud contains them - draco cannot cope with them
220  sensor_msgs::PointCloud2Ptr rawCleaned;
221  if (!raw.is_dense) {
222  rawCleaned = boost::make_shared<sensor_msgs::PointCloud2>();
223  CREATE_FILTERED_CLOUD(raw, *rawCleaned, false, std::isfinite(*x_it) && std::isfinite(*y_it) && std::isfinite(*z_it))
224  }
225 
226  const sensor_msgs::PointCloud2& rawDense = raw.is_dense ? raw : *rawCleaned;
227 
228  // Compressed message
229  draco_point_cloud_transport::CompressedPointCloud2 compressed;
230 
231  copyCloudMetadata(compressed, rawDense);
232 
233  auto res = convertPC2toDraco(rawDense, base_topic_, config.deduplicate, config.expert_attribute_types);
234  if (!res)
235  {
236  return cras::make_unexpected(res.error());
237  }
238 
239  const auto& pc = res.value();
240 
241  if (config.deduplicate)
242  {
243  compressed.height = 1;
244  compressed.width = pc->num_points();
245  compressed.row_step = compressed.width * compressed.point_step;
246  compressed.is_dense = true;
247  }
248 
249  draco::EncoderBuffer encode_buffer;
250 
251  // tracks if all necessary parameters were set for expert encoder
252  bool expert_settings_ok = true;
253 
254  // expert encoder
255  if (config.expert_quantization)
256  {
257  draco::ExpertEncoder expert_encoder(*pc);
258  expert_encoder.SetSpeedOptions(config.encode_speed, config.decode_speed);
259 
260  // default
261  if ((config.encode_method == 0) && (!config.force_quantization))
262  {
263  // let draco handle method selection
264  }
265  // force kd tree
266  else if ((config.encode_method == 1) || (config.force_quantization))
267  {
268  if (config.force_quantization)
269  {
270  // keep track of which attribute is being processed
271  int att_id = 0;
272  int attribute_quantization_bits;
273 
274  for (const auto& field : rawDense.fields)
275  {
276  if (ros::param::getCached(base_topic_ + "/draco/attribute_mapping/quantization_bits/" + field.name,
277  attribute_quantization_bits))
278  {
279  expert_encoder.SetAttributeQuantization(att_id, attribute_quantization_bits);
280  }
281  else
282  {
283  CRAS_ERROR_STREAM("Attribute quantization not specified for " + field.name + " field entry. "
284  "Using regular encoder instead.");
285  CRAS_INFO_STREAM("To set quantization for " + field.name + " field entry, set " + base_topic_ +
286  "/draco/attribute_mapping/quantization_bits/" + field.name);
287  expert_settings_ok = false;
288  }
289  att_id++;
290  }
291  }
292  expert_encoder.SetEncodingMethod(draco::POINT_CLOUD_KD_TREE_ENCODING);
293  }
294  // force sequential encoding
295  else
296  {
297  expert_encoder.SetEncodingMethod(draco::POINT_CLOUD_SEQUENTIAL_ENCODING);
298  }
299 
300  // encodes point cloud and raises error if encoding fails
301  // draco::Status status = encoder.EncodePointCloudToBuffer(*pc, &encode_buffer);
302  draco::Status status = expert_encoder.EncodeToBuffer(&encode_buffer);
303 
304  if (status.code() != 0)
305  {
306  return cras::make_unexpected(
307  cras::format("Draco encoder returned code %i: %s.", status.code(), status.error_msg()));
308  }
309  }
310  // expert encoder end
311 
312  // regular encoder
313  if ((!config.expert_quantization) || (!expert_settings_ok))
314  {
315  draco::Encoder encoder;
316  encoder.SetSpeedOptions(config.encode_speed, config.decode_speed);
317 
318  // default
319  if ((config.encode_method == 0) && (!config.force_quantization))
320  {
321  // let draco handle method selection
322  }
323  // force kd tree
324  else if ((config.encode_method == 1) || (config.force_quantization))
325  {
326  if (config.force_quantization)
327  {
328  encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, config.quantization_POSITION);
329  encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, config.quantization_NORMAL);
330  encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, config.quantization_COLOR);
331  encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, config.quantization_TEX_COORD);
332  encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, config.quantization_GENERIC);
333  }
334  encoder.SetEncodingMethod(draco::POINT_CLOUD_KD_TREE_ENCODING);
335  }
336  // force sequential encoding
337  else
338  {
339  encoder.SetEncodingMethod(draco::POINT_CLOUD_SEQUENTIAL_ENCODING);
340  }
341 
342  // encodes point cloud and raises error if encoding fails
343  draco::Status status = encoder.EncodePointCloudToBuffer(*pc, &encode_buffer);
344 
345  if (!status.ok())
346  {
347  return cras::make_unexpected(
348  cras::format("Draco encoder returned code %i: %s.", status.code(), status.error_msg()));
349  }
350  }
351  // regular encoder end
352 
353  uint32_t compressed_data_size = encode_buffer.size();
354  auto cast_buffer = reinterpret_cast<const unsigned char*>(encode_buffer.data());
355  std::vector<unsigned char> vec_data(cast_buffer, cast_buffer + compressed_data_size);
356  compressed.compressed_data = vec_data;
357 
358  return compressed;
359 }
360 
361 void DracoPublisher::registerPositionField(const std::string& field)
362 {
363  attributeTypes[field] = draco::GeometryAttribute::Type::POSITION;
364 }
365 
366 void DracoPublisher::registerColorField(const std::string& field)
367 {
368  attributeTypes[field] = draco::GeometryAttribute::Type::COLOR;
369 }
370 
371 void DracoPublisher::registerNormalField(const std::string& field)
372 {
373  attributeTypes[field] = draco::GeometryAttribute::Type::NORMAL;
374 }
375 
376 }
std::string getTransportName() const override
#define CRAS_INFO_STREAM(args)
static void registerColorField(const std::string &field)
static void registerNormalField(const std::string &field)
void copyCloudMetadata(PC1 &target, const PC2 &source)
copies header, width, ... between clouds
static std::unordered_map< std::string, draco::GeometryAttribute::Type > attributeTypes
cras::expected< cras::optional< CompressedPointCloud2 >, std::string > TypedEncodeResult
#define CRAS_ERROR_STREAM(args)
ROSCPP_DECL bool getCached(const std::string &key, std::string &s)
inline ::std::string format(const char *format, ::va_list args)
#define CREATE_FILTERED_CLOUD(IN, OUT, KEEP_ORGANIZED, FILTER)
static void registerPositionField(const std::string &field)
TypedEncodeResult encodeTyped(const sensor_msgs::PointCloud2 &raw, const draco_point_cloud_transport::DracoPublisherConfig &config) const override
cras::expected< std::unique_ptr< draco::PointCloud >, std::string > convertPC2toDraco(const sensor_msgs::PointCloud2 &PC2, const std::string &topic, bool deduplicate, bool expert_encoding)


draco_point_cloud_transport
Author(s): Jakub Paplham
autogenerated on Sat Jun 17 2023 02:29:18