Point Cloud Library (PCL)  1.14.0-dev
conversions.h
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2010-2012, Willow Garage, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the copyright holder(s) nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * $Id$
37  *
38  */
39 
40 #pragma once
41 
42 #ifdef __GNUC__
43 #pragma GCC system_header
44 #endif
45 
46 #include <pcl/PCLPointField.h>
47 #include <pcl/PCLPointCloud2.h>
48 #include <pcl/PCLImage.h>
49 #include <pcl/point_cloud.h>
50 #include <pcl/type_traits.h>
51 #include <pcl/for_each_type.h>
52 #include <pcl/console/print.h>
53 
54 #include <algorithm>
55 #include <iterator>
56 #include <numeric> // for accumulate
57 
58 namespace pcl
59 {
60  namespace detail
61  {
62  // For converting template point cloud to message.
63  template<typename PointT>
64  struct FieldAdder
65  {
66  FieldAdder (std::vector<pcl::PCLPointField>& fields) : fields_ (fields) {};
67 
68  template<typename U> void operator() ()
69  {
71  f.name = pcl::traits::name<PointT, U>::value;
72  f.offset = pcl::traits::offset<PointT, U>::value;
73  f.datatype = pcl::traits::datatype<PointT, U>::value;
74  f.count = pcl::traits::datatype<PointT, U>::size;
75  fields_.push_back (f);
76  }
77 
78  std::vector<pcl::PCLPointField>& fields_;
79  };
80 
81  // For converting message to template point cloud.
82  template<typename PointT>
83  struct FieldMapper
84  {
85  FieldMapper (const std::vector<pcl::PCLPointField>& fields,
86  std::vector<FieldMapping>& map)
87  : fields_ (fields), map_ (map)
88  {
89  }
90 
91  template<typename Tag> void
93  {
94  for (const auto& field : fields_)
95  {
96  if (FieldMatches<PointT, Tag>()(field))
97  {
98  FieldMapping mapping;
99  mapping.serialized_offset = field.offset;
100  mapping.struct_offset = pcl::traits::offset<PointT, Tag>::value;
101  mapping.size = sizeof (typename pcl::traits::datatype<PointT, Tag>::type);
102  map_.push_back (mapping);
103  return;
104  }
105  }
106  // Disable thrown exception per #595: http://dev.pointclouds.org/issues/595
107  PCL_WARN ("Failed to find match for field '%s'.\n", pcl::traits::name<PointT, Tag>::value);
108  //throw pcl::InvalidConversionException (ss.str ());
109  }
110 
111  const std::vector<pcl::PCLPointField>& fields_;
112  std::vector<FieldMapping>& map_;
113  };
114 
115  inline bool
117  {
118  return (a.serialized_offset < b.serialized_offset);
119  }
120 
121  } //namespace detail
122 
123  template<typename PointT> void
124  createMapping (const std::vector<pcl::PCLPointField>& msg_fields, MsgFieldMap& field_map)
125  {
126  // Create initial 1-1 mapping between serialized data segments and struct fields
127  detail::FieldMapper<PointT> mapper (msg_fields, field_map);
128  for_each_type< typename traits::fieldList<PointT>::type > (mapper);
129 
130  // Coalesce adjacent fields into single memcpy's where possible
131  if (field_map.size() > 1)
132  {
133  std::sort(field_map.begin(), field_map.end(), detail::fieldOrdering);
134  MsgFieldMap::iterator i = field_map.begin(), j = i + 1;
135  while (j != field_map.end())
136  {
137  // This check is designed to permit padding between adjacent fields.
138  /// @todo One could construct a pathological case where the struct has a
139  /// field where the serialized data has padding
140  if (j->serialized_offset - i->serialized_offset == j->struct_offset - i->struct_offset)
141  {
142  i->size += (j->struct_offset + j->size) - (i->struct_offset + i->size);
143  j = field_map.erase(j);
144  }
145  else
146  {
147  ++i;
148  ++j;
149  }
150  }
151  }
152  }
153 
154  /** \brief Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud<T> object using a field_map.
155  * \param[in] msg the PCLPointCloud2 binary blob (note that the binary point data in msg.data will not be used!)
156  * \param[out] cloud the resultant pcl::PointCloud<T>
157  * \param[in] field_map a MsgFieldMap object
158  * \param[in] msg_data pointer to binary blob data, used instead of msg.data
159  *
160  * \note Use fromPCLPointCloud2 (PCLPointCloud2, PointCloud<T>) instead, except if you have a binary blob of
161  * point data that you do not want to copy into a pcl::PCLPointCloud2 in order to use fromPCLPointCloud2.
162  */
163  template <typename PointT> void
165  const MsgFieldMap& field_map, const std::uint8_t* msg_data)
166  {
167  // Copy info fields
168  cloud.header = msg.header;
169  cloud.width = msg.width;
170  cloud.height = msg.height;
171  cloud.is_dense = msg.is_dense == 1;
172 
173  // Resize cloud
174  cloud.resize (msg.width * msg.height);
175 
176  // check if there is data to copy
177  if (msg.width * msg.height == 0)
178  {
179  PCL_WARN("[pcl::fromPCLPointCloud2] No data to copy.\n");
180  return;
181  }
182 
183  // Copy point data
184  std::uint8_t* cloud_data = reinterpret_cast<std::uint8_t*>(cloud.data());
185 
186  // Check if we can copy adjacent points in a single memcpy. We can do so if there
187  // is exactly one field to copy and it is the same size as the source and destination
188  // point types.
189  if (field_map.size() == 1 &&
190  field_map[0].serialized_offset == 0 &&
191  field_map[0].struct_offset == 0 &&
192  field_map[0].size == msg.point_step &&
193  field_map[0].size == sizeof(PointT))
194  {
195  const auto cloud_row_step = (sizeof (PointT) * cloud.width);
196  // Should usually be able to copy all rows at once
197  if (msg.row_step == cloud_row_step)
198  {
199  memcpy (cloud_data, msg_data, msg.width * msg.height * sizeof(PointT));
200  }
201  else
202  {
203  for (uindex_t i = 0; i < msg.height; ++i, cloud_data += cloud_row_step, msg_data += msg.row_step)
204  memcpy (cloud_data, msg_data, cloud_row_step);
205  }
206 
207  }
208  else
209  {
210  // If not, memcpy each group of contiguous fields separately
211  for (std::size_t row = 0; row < msg.height; ++row)
212  {
213  const std::uint8_t* row_data = msg_data + row * msg.row_step;
214  for (std::size_t col = 0; col < msg.width; ++col)
215  {
216  const std::uint8_t* msg_data = row_data + col * msg.point_step;
217  for (const detail::FieldMapping& mapping : field_map)
218  {
219  std::copy(msg_data + mapping.serialized_offset, msg_data + mapping.serialized_offset + mapping.size,
220  cloud_data + mapping.struct_offset);
221  }
222  cloud_data += sizeof (PointT);
223  }
224  }
225  }
226  }
227 
228  /** \brief Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud<T> object using a field_map.
229  * \param[in] msg the PCLPointCloud2 binary blob
230  * \param[out] cloud the resultant pcl::PointCloud<T>
231  * \param[in] field_map a MsgFieldMap object
232  *
233  * \note Use fromPCLPointCloud2 (PCLPointCloud2, PointCloud<T>) directly or create you
234  * own MsgFieldMap using:
235  *
236  * \code
237  * MsgFieldMap field_map;
238  * createMapping<PointT> (msg.fields, field_map);
239  * \endcode
240  */
241  template <typename PointT> void
243  const MsgFieldMap& field_map)
244  {
245  fromPCLPointCloud2 (msg, cloud, field_map, msg.data.data());
246  }
247 
248  /** \brief Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud<T> object.
249  * \param[in] msg the PCLPointCloud2 binary blob
250  * \param[out] cloud the resultant pcl::PointCloud<T>
251  */
252  template<typename PointT> void
254  {
255  MsgFieldMap field_map;
256  createMapping<PointT> (msg.fields, field_map);
257  fromPCLPointCloud2 (msg, cloud, field_map);
258  }
259 
260  namespace detail {
261  /** \brief Used together with `pcl::for_each_type`, copies all point fields from `cloud_data` (respecting each field offset) to `msg_data` (tightly packed).
262  */
263  template<typename PointT>
264  struct FieldCopier {
265  FieldCopier(std::uint8_t*& msg_data, const std::uint8_t*& cloud_data) : msg_data_ (msg_data), cloud_data_ (cloud_data) {};
266 
267  template<typename U> void operator() () {
268  memcpy(msg_data_, cloud_data_ + pcl::traits::offset<PointT, U>::value, sizeof(typename pcl::traits::datatype<PointT, U>::type));
269  msg_data_ += sizeof(typename pcl::traits::datatype<PointT, U>::type);
270  }
271 
272  std::uint8_t*& msg_data_;
273  const std::uint8_t*& cloud_data_;
274  };
275 
276  /** \brief Used together with `pcl::for_each_type`, creates list of all fields, and list of size of each field.
277  */
278  template<typename PointT>
280  {
281  FieldAdderAdvanced (std::vector<pcl::PCLPointField>& fields, std::vector<std::size_t>& field_sizes) : fields_ (fields), field_sizes_ (field_sizes) {};
282 
283  template<typename U> void operator() ()
284  {
286  f.name = pcl::traits::name<PointT, U>::value;
287  f.offset = pcl::traits::offset<PointT, U>::value;
288  f.datatype = pcl::traits::datatype<PointT, U>::value;
289  f.count = pcl::traits::datatype<PointT, U>::size;
290  fields_.push_back (f);
291  field_sizes_.push_back (sizeof(typename pcl::traits::datatype<PointT, U>::type)); // If field is an array, then this is the size of all array elements
292  }
293 
294  std::vector<pcl::PCLPointField>& fields_;
295  std::vector<std::size_t>& field_sizes_;
296  };
297  } // namespace detail
298 
299  /** \brief Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
300  * \param[in] cloud the input pcl::PointCloud<T>
301  * \param[out] msg the resultant PCLPointCloud2 binary blob
302  * \param[in] padding Many point types have padding to ensure alignment and SIMD compatibility. Setting this to true will copy the padding to the `PCLPointCloud2` (the default in older PCL versions). Setting this to false will make the data blob in `PCLPointCloud2` smaller, while still keeping all information (useful e.g. when sending msg over network or storing it). The amount of padding depends on the point type, and can in some cases be up to 50 percent.
303  */
304  template<typename PointT> void
306  {
307  // Ease the user's burden on specifying width/height for unorganized datasets
308  if (cloud.width == 0 && cloud.height == 0)
309  {
310  msg.width = cloud.size ();
311  msg.height = 1;
312  }
313  else
314  {
315  assert (cloud.size () == cloud.width * cloud.height);
316  msg.height = cloud.height;
317  msg.width = cloud.width;
318  }
319  // Fill fields metadata
320  msg.fields.clear ();
321  std::vector<std::size_t> field_sizes;
322  for_each_type<typename pcl::traits::fieldList<PointT>::type>(pcl::detail::FieldAdderAdvanced<PointT>(msg.fields, field_sizes));
323  // Check if padding should be kept, or if the point type does not contain padding (then the single memcpy is faster)
324  if (padding || std::accumulate(field_sizes.begin(), field_sizes.end(), static_cast<std::size_t>(0)) == sizeof (PointT)) {
325  // Fill point cloud binary data (padding and all)
326  std::size_t data_size = sizeof (PointT) * cloud.size ();
327  msg.data.resize (data_size);
328  if (data_size)
329  {
330  memcpy(msg.data.data(), cloud.data(), data_size);
331  }
332 
333  msg.point_step = sizeof (PointT);
334  msg.row_step = (sizeof (PointT) * msg.width);
335  } else {
336  std::size_t point_size = 0;
337  for(std::size_t i=0; i<msg.fields.size(); ++i) {
338  msg.fields[i].offset = point_size; // Adjust offset when padding is removed
339  point_size += field_sizes[i];
340  }
341  msg.data.resize (point_size * cloud.size());
342  std::uint8_t* msg_data = &msg.data[0];
343  const std::uint8_t* cloud_data=reinterpret_cast<const std::uint8_t*>(&cloud[0]);
344  const std::uint8_t* end = cloud_data + sizeof (PointT) * cloud.size ();
345  pcl::detail::FieldCopier<PointT> copier(msg_data, cloud_data); // copier takes msg_data and cloud_data as references, so the two are shared
346  for (; cloud_data<end; cloud_data+=sizeof(PointT)) {
347  for_each_type< typename traits::fieldList<PointT>::type > (copier);
348  }
349 
350  msg.point_step = point_size;
351  msg.row_step = point_size * msg.width;
352  }
353  msg.header = cloud.header;
354  msg.is_dense = cloud.is_dense;
355  /// @todo msg.is_bigendian = ?;
356  }
357 
358  /** \brief Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
359  * \param[in] cloud the input pcl::PointCloud<T>
360  * \param[out] msg the resultant PCLPointCloud2 binary blob
361  */
362  template<typename PointT> void
364  {
365  toPCLPointCloud2 (cloud, msg, true); // true is the default in older PCL version
366  }
367 
368  /** \brief Copy the RGB fields of a PointCloud into pcl::PCLImage format
369  * \param[in] cloud the point cloud message
370  * \param[out] msg the resultant pcl::PCLImage
371  * CloudT cloud type, CloudT should be akin to pcl::PointCloud<pcl::PointXYZRGBA>
372  * \note will throw std::runtime_error if there is a problem
373  */
374  template<typename CloudT> void
375  toPCLPointCloud2 (const CloudT& cloud, pcl::PCLImage& msg)
376  {
377  // Ease the user's burden on specifying width/height for unorganized datasets
378  if (cloud.width == 0 && cloud.height == 0)
379  throw std::runtime_error("Needs to be a dense like cloud!!");
380  else
381  {
382  if (cloud.size () != cloud.width * cloud.height)
383  throw std::runtime_error("The width and height do not match the cloud size!");
384  msg.height = cloud.height;
385  msg.width = cloud.width;
386  }
387 
388  // ensor_msgs::image_encodings::BGR8;
389  msg.header = cloud.header;
390  msg.encoding = "bgr8";
391  msg.step = msg.width * sizeof (std::uint8_t) * 3;
392  msg.data.resize (msg.step * msg.height);
393  for (std::size_t y = 0; y < cloud.height; y++)
394  {
395  for (std::size_t x = 0; x < cloud.width; x++)
396  {
397  std::uint8_t * pixel = &(msg.data[y * msg.step + x * 3]);
398  memcpy (pixel, &cloud (x, y).rgb, 3 * sizeof(std::uint8_t));
399  }
400  }
401  }
402 
403  /** \brief Copy the RGB fields of a PCLPointCloud2 msg into pcl::PCLImage format
404  * \param cloud the point cloud message
405  * \param msg the resultant pcl::PCLImage
406  * will throw std::runtime_error if there is a problem
407  */
408  inline void
410  {
411  const auto predicate = [](const auto& field) { return field.name == "rgb"; };
412  const auto result = std::find_if(cloud.fields.cbegin (), cloud.fields.cend (), predicate);
413  if (result == cloud.fields.end ())
414  throw std::runtime_error ("No rgb field!!");
415 
416  const auto rgb_index = std::distance(cloud.fields.begin (), result);
417  if (cloud.width == 0 && cloud.height == 0)
418  throw std::runtime_error ("Needs to be a dense like cloud!!");
419  else
420  {
421  msg.height = cloud.height;
422  msg.width = cloud.width;
423  }
424  auto rgb_offset = cloud.fields[rgb_index].offset;
425  const auto point_step = cloud.point_step;
426 
427  // pcl::image_encodings::BGR8;
428  msg.header = cloud.header;
429  msg.encoding = "bgr8";
430  msg.step = (msg.width * sizeof (std::uint8_t) * 3);
431  msg.data.resize (msg.step * msg.height);
432 
433  for (std::size_t y = 0; y < cloud.height; y++)
434  {
435  for (std::size_t x = 0; x < cloud.width; x++, rgb_offset += point_step)
436  {
437  std::uint8_t * pixel = &(msg.data[y * msg.step + x * 3]);
438  std::copy(&cloud.data[rgb_offset], &cloud.data[rgb_offset] + 3, pixel);
439  }
440  }
441  }
442 }
PointCloud represents the base class in PCL for storing collections of 3D points.
Definition: point_cloud.h:173
PointT * data() noexcept
Definition: point_cloud.h:447
bool is_dense
True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields).
Definition: point_cloud.h:403
void resize(std::size_t count)
Resizes the container to contain count elements.
Definition: point_cloud.h:462
std::uint32_t width
The point cloud width (if organized as an image-structure).
Definition: point_cloud.h:398
pcl::PCLHeader header
The point cloud header.
Definition: point_cloud.h:392
std::uint32_t height
The point cloud height (if organized as an image-structure).
Definition: point_cloud.h:400
std::size_t size() const
Definition: point_cloud.h:443
bool fieldOrdering(const FieldMapping &a, const FieldMapping &b)
Definition: conversions.h:116
float distance(const PointT &p1, const PointT &p2)
Definition: geometry.h:60
detail::int_type_t< detail::index_type_size, false > uindex_t
Type used for an unsigned index in PCL.
Definition: types.h:120
void fromPCLPointCloud2(const pcl::PCLPointCloud2 &msg, pcl::PointCloud< PointT > &cloud, const MsgFieldMap &field_map, const std::uint8_t *msg_data)
Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud<T> object using a field_map.
Definition: conversions.h:164
void toPCLPointCloud2(const pcl::PointCloud< PointT > &cloud, pcl::PCLPointCloud2 &msg, bool padding)
Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
Definition: conversions.h:305
void createMapping(const std::vector< pcl::PCLPointField > &msg_fields, MsgFieldMap &field_map)
Definition: conversions.h:124
std::vector< detail::FieldMapping > MsgFieldMap
Definition: point_cloud.h:72
uindex_t step
Definition: PCLImage.h:21
uindex_t height
Definition: PCLImage.h:16
std::string encoding
Definition: PCLImage.h:18
std::vector< std::uint8_t > data
Definition: PCLImage.h:23
uindex_t width
Definition: PCLImage.h:17
::pcl::PCLHeader header
Definition: PCLImage.h:14
std::uint8_t is_dense
std::vector<::pcl::PCLPointField > fields
::pcl::PCLHeader header
std::vector< std::uint8_t > data
std::string name
Definition: PCLPointField.h:14
std::uint8_t datatype
Definition: PCLPointField.h:17
A point structure representing Euclidean xyz coordinates, and the RGB color.
Used together with pcl::for_each_type, creates list of all fields, and list of size of each field.
Definition: conversions.h:280
FieldAdderAdvanced(std::vector< pcl::PCLPointField > &fields, std::vector< std::size_t > &field_sizes)
Definition: conversions.h:281
std::vector< std::size_t > & field_sizes_
Definition: conversions.h:295
std::vector< pcl::PCLPointField > & fields_
Definition: conversions.h:294
FieldAdder(std::vector< pcl::PCLPointField > &fields)
Definition: conversions.h:66
std::vector< pcl::PCLPointField > & fields_
Definition: conversions.h:78
Used together with pcl::for_each_type, copies all point fields from cloud_data (respecting each field...
Definition: conversions.h:264
FieldCopier(std::uint8_t *&msg_data, const std::uint8_t *&cloud_data)
Definition: conversions.h:265
const std::uint8_t *& cloud_data_
Definition: conversions.h:273
std::uint8_t *& msg_data_
Definition: conversions.h:272
FieldMapper(const std::vector< pcl::PCLPointField > &fields, std::vector< FieldMapping > &map)
Definition: conversions.h:85
const std::vector< pcl::PCLPointField > & fields_
Definition: conversions.h:111
std::vector< FieldMapping > & map_
Definition: conversions.h:112
std::size_t serialized_offset
Definition: point_cloud.h:64