Skip to content

Commit a33c173

Browse files
authored
Merge pull request #5913 from mvieth/toPCLPointCloud2_padding
Enhance toPCLPointCloud2 to remove padding on request
2 parents bcae7ea + 9280e6f commit a33c173

File tree

2 files changed

+129
-13
lines changed

2 files changed

+129
-13
lines changed

common/include/pcl/conversions.h

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353

5454
#include <algorithm>
5555
#include <iterator>
56+
#include <numeric> // for accumulate
5657

5758
namespace pcl
5859
{
@@ -256,12 +257,52 @@ namespace pcl
256257
fromPCLPointCloud2 (msg, cloud, field_map);
257258
}
258259

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>
279+
struct FieldAdderAdvanced
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+
{
285+
pcl::PCLPointField f;
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+
259299
/** \brief Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
260300
* \param[in] cloud the input pcl::PointCloud<T>
261301
* \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.
262303
*/
263304
template<typename PointT> void
264-
toPCLPointCloud2 (const pcl::PointCloud<PointT>& cloud, pcl::PCLPointCloud2& msg)
305+
toPCLPointCloud2 (const pcl::PointCloud<PointT>& cloud, pcl::PCLPointCloud2& msg, bool padding)
265306
{
266307
// Ease the user's burden on specifying width/height for unorganized datasets
267308
if (cloud.width == 0 && cloud.height == 0)
@@ -275,26 +316,55 @@ namespace pcl
275316
msg.height = cloud.height;
276317
msg.width = cloud.width;
277318
}
278-
279-
// Fill point cloud binary data (padding and all)
280-
std::size_t data_size = sizeof (PointT) * cloud.size ();
281-
msg.data.resize (data_size);
282-
if (data_size)
283-
{
284-
memcpy(msg.data.data(), cloud.data(), data_size);
285-
}
286-
287319
// Fill fields metadata
288320
msg.fields.clear ();
289-
for_each_type<typename traits::fieldList<PointT>::type> (detail::FieldAdder<PointT>(msg.fields));
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+
}
290349

350+
msg.point_step = point_size;
351+
msg.row_step = point_size * msg.width;
352+
}
291353
msg.header = cloud.header;
292-
msg.point_step = sizeof (PointT);
293-
msg.row_step = (sizeof (PointT) * msg.width);
294354
msg.is_dense = cloud.is_dense;
295355
/// @todo msg.is_bigendian = ?;
296356
}
297357

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
363+
toPCLPointCloud2 (const pcl::PointCloud<PointT>& cloud, pcl::PCLPointCloud2& msg)
364+
{
365+
toPCLPointCloud2 (cloud, msg, true); // true is the default in older PCL version
366+
}
367+
298368
/** \brief Copy the RGB fields of a PointCloud into pcl::PCLImage format
299369
* \param[in] cloud the point cloud message
300370
* \param[out] msg the resultant pcl::PCLImage

test/common/test_io.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,52 @@ TEST (PCL, CopyPointCloudWithSameTypes)
250250
ASSERT_EQ (0, cloud_out.size ());
251251
}
252252

253+
TEST (toPCLPointCloud2NoPadding, PointXYZI)
254+
{
255+
pcl::PointCloud<pcl::PointXYZI> cloud;
256+
cloud.resize(static_cast<pcl::uindex_t>(2), static_cast<pcl::uindex_t>(2));
257+
cloud[0].x = 1.0; cloud[0].y = 2.0; cloud[0].z = 3.0; cloud[0].intensity = 123.0;
258+
cloud[1].x = -1.0; cloud[1].y = -2.0; cloud[1].z = -3.0; cloud[1].intensity = -123.0;
259+
cloud[2].x = 0.1; cloud[2].y = 0.2; cloud[2].z = 0.3; cloud[2].intensity = 12.3;
260+
cloud[3].x = 0.0; cloud[3].y = -1.7; cloud[3].z = 100.0; cloud[3].intensity = 3.14;
261+
pcl::PCLPointCloud2 msg;
262+
pcl::toPCLPointCloud2(cloud, msg, false);
263+
EXPECT_EQ (msg.height, cloud.height);
264+
EXPECT_EQ (msg.width, cloud.width);
265+
EXPECT_EQ (msg.fields.size(), 4);
266+
EXPECT_EQ (msg.fields[0].name, "x");
267+
EXPECT_EQ (msg.fields[0].offset, 0);
268+
EXPECT_EQ (msg.fields[0].datatype, pcl::PCLPointField::FLOAT32);
269+
EXPECT_EQ (msg.fields[0].count, 1);
270+
EXPECT_EQ (msg.fields[1].name, "y");
271+
EXPECT_EQ (msg.fields[1].offset, 4);
272+
EXPECT_EQ (msg.fields[1].datatype, pcl::PCLPointField::FLOAT32);
273+
EXPECT_EQ (msg.fields[1].count, 1);
274+
EXPECT_EQ (msg.fields[2].name, "z");
275+
EXPECT_EQ (msg.fields[2].offset, 8);
276+
EXPECT_EQ (msg.fields[2].datatype, pcl::PCLPointField::FLOAT32);
277+
EXPECT_EQ (msg.fields[2].count, 1);
278+
EXPECT_EQ (msg.fields[3].name, "intensity");
279+
EXPECT_EQ (msg.fields[3].offset, 12);
280+
EXPECT_EQ (msg.fields[3].datatype, pcl::PCLPointField::FLOAT32);
281+
EXPECT_EQ (msg.fields[3].count, 1);
282+
EXPECT_EQ (msg.point_step, 16);
283+
EXPECT_EQ (msg.row_step, 16*cloud.width);
284+
EXPECT_EQ (msg.data.size(), 16*cloud.width*cloud.height);
285+
EXPECT_EQ (msg.at<float>(0, 0), 1.0f);
286+
EXPECT_EQ (msg.at<float>(3, 4), -1.7f);
287+
EXPECT_EQ (msg.at<float>(1, 8), -3.0f);
288+
EXPECT_EQ (msg.at<float>(2, 12), 12.3f);
289+
pcl::PointCloud<pcl::PointXYZI> cloud2;
290+
pcl::fromPCLPointCloud2(msg, cloud2);
291+
for(std::size_t i=0; i<cloud2.size(); ++i) {
292+
EXPECT_EQ (cloud[i].x, cloud2[i].x);
293+
EXPECT_EQ (cloud[i].y, cloud2[i].y);
294+
EXPECT_EQ (cloud[i].z, cloud2[i].z);
295+
EXPECT_EQ (cloud[i].intensity, cloud2[i].intensity);
296+
}
297+
}
298+
253299
/* ---[ */
254300
int
255301
main (int argc, char** argv)

0 commit comments

Comments
 (0)