Skip to content

Commit 64ac461

Browse files
Add writeBinary to ostream for PCDWriter (PointCloudLibrary#5598)
* Add writeBinary to ostream for PCDWriter Add method writeBinary to serialize point clouds in memory uncompressed. * Revert "Add writeBinary to ostream for PCDWriter" This reverts commit 55f3f80. * Add independent writeBinary to ostream function Add method writeBinary to serialize point clouds in memory uncompressed. * Add unit test for writeBinary to ostream * Fix register macros to match struct definitions Fix the sequence order in POINT_CLOUD_REGISTER macros to match structs * Revert "Fix register macros to match struct definitions" This reverts commit b522e74. * Sort fields in generateHeaderBinary Sort cloud.fields by their offset value to generate correct fake fields
1 parent 1d39403 commit 64ac461

File tree

3 files changed

+120
-12
lines changed

3 files changed

+120
-12
lines changed

io/include/pcl/io/pcd_io.h

+11
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,17 @@ namespace pcl
402402
const Eigen::Vector4f &origin = Eigen::Vector4f::Zero (),
403403
const Eigen::Quaternionf &orientation = Eigen::Quaternionf::Identity ());
404404

405+
/** \brief Save point cloud data to a std::ostream containing n-D points, in BINARY format
406+
* \param[out] os the stream into which to write the data
407+
* \param[in] cloud the point cloud data message
408+
* \param[in] origin the sensor acquisition origin
409+
* \param[in] orientation the sensor acquisition orientation
410+
*/
411+
int
412+
writeBinary (std::ostream &os, const pcl::PCLPointCloud2 &cloud,
413+
const Eigen::Vector4f &origin = Eigen::Vector4f::Zero (),
414+
const Eigen::Quaternionf &orientation = Eigen::Quaternionf::Identity ());
415+
405416
/** \brief Save point cloud data to a PCD file containing n-D points, in BINARY_COMPRESSED format
406417
* \param[in] file_name the output file name
407418
* \param[in] cloud the point cloud data message

io/src/pcd_io.cpp

+41-12
Original file line numberDiff line numberDiff line change
@@ -930,9 +930,15 @@ pcl::PCDWriter::generateHeaderBinary (const pcl::PCLPointCloud2 &cloud,
930930
"\nVERSION 0.7"
931931
"\nFIELDS";
932932

933+
auto fields = cloud.fields;
934+
std::sort(fields.begin(), fields.end(), [](const auto& field_a, const auto& field_b)
935+
{
936+
return field_a.offset < field_b.offset;
937+
});
938+
933939
// Compute the total size of the fields
934940
unsigned int fsize = 0;
935-
for (const auto &field : cloud.fields)
941+
for (const auto &field : fields)
936942
fsize += field.count * getFieldSize (field.datatype);
937943

938944
// The size of the fields cannot be larger than point_step
@@ -945,20 +951,20 @@ pcl::PCDWriter::generateHeaderBinary (const pcl::PCLPointCloud2 &cloud,
945951
std::stringstream field_names, field_types, field_sizes, field_counts;
946952
// Check if the size of the fields is smaller than the size of the point step
947953
std::size_t toffset = 0;
948-
for (std::size_t i = 0; i < cloud.fields.size (); ++i)
954+
for (std::size_t i = 0; i < fields.size (); ++i)
949955
{
950956
// If field offsets do not match, then we need to create fake fields
951-
if (toffset != cloud.fields[i].offset)
957+
if (toffset != fields[i].offset)
952958
{
953959
// If we're at the last "valid" field
954960
int fake_offset = (i == 0) ?
955961
// Use the current_field offset
956-
(cloud.fields[i].offset)
962+
(fields[i].offset)
957963
:
958964
// Else, do cur_field.offset - prev_field.offset + sizeof (prev_field)
959-
(cloud.fields[i].offset -
960-
(cloud.fields[i-1].offset +
961-
cloud.fields[i-1].count * getFieldSize (cloud.fields[i-1].datatype)));
965+
(fields[i].offset -
966+
(fields[i-1].offset +
967+
fields[i-1].count * getFieldSize (fields[i-1].datatype)));
962968

963969
toffset += fake_offset;
964970

@@ -969,11 +975,11 @@ pcl::PCDWriter::generateHeaderBinary (const pcl::PCLPointCloud2 &cloud,
969975
}
970976

971977
// Add the regular dimension
972-
toffset += cloud.fields[i].count * getFieldSize (cloud.fields[i].datatype);
973-
field_names << " " << cloud.fields[i].name;
974-
field_sizes << " " << pcl::getFieldSize (cloud.fields[i].datatype);
975-
field_types << " " << pcl::getFieldType (cloud.fields[i].datatype);
976-
int count = std::abs (static_cast<int> (cloud.fields[i].count));
978+
toffset += fields[i].count * getFieldSize (fields[i].datatype);
979+
field_names << " " << fields[i].name;
980+
field_sizes << " " << pcl::getFieldSize (fields[i].datatype);
981+
field_types << " " << pcl::getFieldType (fields[i].datatype);
982+
int count = std::abs (static_cast<int> (fields[i].count));
977983
if (count == 0) count = 1; // check for 0 counts (coming from older converter code)
978984
field_counts << " " << count;
979985
}
@@ -1173,6 +1179,29 @@ pcl::PCDWriter::writeASCII (const std::string &file_name, const pcl::PCLPointClo
11731179
return (0);
11741180
}
11751181

1182+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1183+
int
1184+
pcl::PCDWriter::writeBinary (std::ostream &os, const pcl::PCLPointCloud2 &cloud,
1185+
const Eigen::Vector4f &origin, const Eigen::Quaternionf &orientation)
1186+
{
1187+
if (cloud.data.empty ())
1188+
{
1189+
PCL_WARN ("[pcl::PCDWriter::writeBinary] Input point cloud has no data!\n");
1190+
}
1191+
if (cloud.fields.empty())
1192+
{
1193+
PCL_ERROR ("[pcl::PCDWriter::writeBinary] Input point cloud has no field data!\n");
1194+
return (-1);
1195+
}
1196+
1197+
os.imbue (std::locale::classic ());
1198+
os << generateHeaderBinary (cloud, origin, orientation) << "DATA binary\n";
1199+
std::copy (cloud.data.cbegin(), cloud.data.cend(), std::ostream_iterator<char> (os));
1200+
os.flush ();
1201+
1202+
return (os ? 0 : -1);
1203+
}
1204+
11761205
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11771206
int
11781207
pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCloud2 &cloud,

test/io/test_io.cpp

+68
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,74 @@ TEST (PCL, LZFExtended)
13791379
remove ("test_pcl_io_compressed.pcd");
13801380
}
13811381

1382+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1383+
TEST (PCL, WriteBinaryToOStream)
1384+
{
1385+
PointCloud<PointXYZRGBNormal> cloud;
1386+
cloud.width = 640;
1387+
cloud.height = 480;
1388+
cloud.resize (cloud.width * cloud.height);
1389+
cloud.is_dense = true;
1390+
1391+
srand (static_cast<unsigned int> (time (nullptr)));
1392+
const auto nr_p = cloud.size ();
1393+
// Randomly create a new point cloud
1394+
for (std::size_t i = 0; i < nr_p; ++i)
1395+
{
1396+
cloud[i].x = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1397+
cloud[i].y = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1398+
cloud[i].z = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1399+
cloud[i].normal_x = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1400+
cloud[i].normal_y = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1401+
cloud[i].normal_z = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1402+
cloud[i].rgb = static_cast<float> (1024 * rand () / (RAND_MAX + 1.0));
1403+
}
1404+
1405+
pcl::PCLPointCloud2 blob;
1406+
pcl::toPCLPointCloud2 (cloud, blob);
1407+
1408+
std::ostringstream oss;
1409+
PCDWriter writer;
1410+
int res = writer.writeBinary (oss, blob);
1411+
EXPECT_EQ (res, 0);
1412+
std::string pcd_str = oss.str ();
1413+
1414+
Eigen::Vector4f origin;
1415+
Eigen::Quaternionf orientation;
1416+
int pcd_version = -1;
1417+
int data_type = -1;
1418+
unsigned int data_idx = 0;
1419+
std::istringstream iss (pcd_str, std::ios::binary);
1420+
PCDReader reader;
1421+
pcl::PCLPointCloud2 blob2;
1422+
res = reader.readHeader (iss, blob2, origin, orientation, pcd_version, data_type, data_idx);
1423+
EXPECT_EQ (res, 0);
1424+
EXPECT_EQ (blob2.width, blob.width);
1425+
EXPECT_EQ (blob2.height, blob.height);
1426+
EXPECT_EQ (data_type, 1); // since it was written by writeBinary (), it should be uncompressed.
1427+
1428+
const auto *data = reinterpret_cast<const unsigned char *> (pcd_str.data ());
1429+
res = reader.readBodyBinary (data, blob2, pcd_version, data_type == 2, data_idx);
1430+
PointCloud<PointXYZRGBNormal> cloud2;
1431+
pcl::fromPCLPointCloud2 (blob2, cloud2);
1432+
EXPECT_EQ (res, 0);
1433+
EXPECT_EQ (cloud2.width, blob.width);
1434+
EXPECT_EQ (cloud2.height, blob.height);
1435+
EXPECT_EQ (cloud2.is_dense, cloud.is_dense);
1436+
EXPECT_EQ (cloud2.size (), cloud.size ());
1437+
1438+
for (std::size_t i = 0; i < cloud2.size (); ++i)
1439+
{
1440+
EXPECT_EQ (cloud2[i].x, cloud[i].x);
1441+
EXPECT_EQ (cloud2[i].y, cloud[i].y);
1442+
EXPECT_EQ (cloud2[i].z, cloud[i].z);
1443+
EXPECT_EQ (cloud2[i].normal_x, cloud[i].normal_x);
1444+
EXPECT_EQ (cloud2[i].normal_y, cloud[i].normal_y);
1445+
EXPECT_EQ (cloud2[i].normal_z, cloud[i].normal_z);
1446+
EXPECT_EQ (cloud2[i].rgb, cloud[i].rgb);
1447+
}
1448+
}
1449+
13821450
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
13831451
TEST (PCL, LZFInMem)
13841452
{

0 commit comments

Comments
 (0)