Skip to content

Commit 93ffa71

Browse files
committed
Added encoder/decoder tutorial.
1 parent 6c6f128 commit 93ffa71

File tree

3 files changed

+138
-1
lines changed

3 files changed

+138
-1
lines changed

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.10.2)
22
project(point_cloud_transport_tutorial)
33

4-
find_package(catkin REQUIRED COMPONENTS point_cloud_transport rosbag sensor_msgs)
4+
find_package(catkin REQUIRED COMPONENTS cras_cpp_common point_cloud_transport rosbag sensor_msgs topic_tools)
55

66
catkin_package()
77

@@ -16,3 +16,8 @@ target_link_libraries(publisher_test ${catkin_LIBRARIES})
1616
add_executable(subscriber_test src/my_subscriber.cpp)
1717
add_dependencies(subscriber_test ${catkin_EXPORTED_TARGETS})
1818
target_link_libraries(subscriber_test ${catkin_LIBRARIES})
19+
20+
# encoder
21+
add_executable(encoder_test src/my_encoder.cpp)
22+
add_dependencies(encoder_test ${catkin_EXPORTED_TARGETS})
23+
target_link_libraries(encoder_test ${catkin_LIBRARIES})

package.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@
88

99
<buildtool_depend>catkin</buildtool_depend>
1010

11+
<build_depend>cras_cpp_common</build_depend>
1112
<build_depend>point_cloud_transport</build_depend>
1213
<build_depend>sensor_msgs</build_depend>
1314
<build_depend>rosbag</build_depend>
15+
<build_depend>topic_tools</build_depend>
1416

17+
<exec_depend>cras_cpp_common</exec_depend>
1518
<exec_depend>point_cloud_transport</exec_depend>
19+
<exec_depend>point_cloud_transport_plugins</exec_depend>
1620
<exec_depend>sensor_msgs</exec_depend>
1721
<exec_depend>rosbag</exec_depend>
22+
<exec_depend>topic_tools</exec_depend>
1823
</package>

src/my_encoder.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Czech Technical University in Prague .. 2019, paplhjak
3+
4+
#include <dynamic_reconfigure/Config.h>
5+
#include <point_cloud_transport/point_cloud_codec.h>
6+
#include <ros/console.h>
7+
#include <ros/time.h>
8+
#include <rosbag/bag.h>
9+
#include <rosbag/view.h>
10+
#include <sensor_msgs/PointCloud2.h>
11+
12+
int main(int argc, char** argv)
13+
{
14+
ros::console::initialize();
15+
ros::Time::init();
16+
17+
point_cloud_transport::PointCloudCodec pct;
18+
19+
std::string codec = "draco";
20+
dynamic_reconfigure::Config config;
21+
if (argc > 2)
22+
codec = argv[2];
23+
else
24+
{
25+
config.ints.emplace_back();
26+
config.ints[0].name = "quantization_POSITION";
27+
config.ints[0].value = 10;
28+
}
29+
auto encoder = pct.getEncoderByName(codec);
30+
if (!encoder)
31+
return 1;
32+
33+
ROS_INFO("Found encoder: %s", encoder->getTransportName().c_str());
34+
35+
rosbag::Bag bag;
36+
bag.open(argv[1], rosbag::bagmode::Read);
37+
38+
for (const auto& m: rosbag::View(bag))
39+
{
40+
sensor_msgs::PointCloud2::ConstPtr i = m.instantiate<sensor_msgs::PointCloud2>();
41+
if (i != nullptr)
42+
{
43+
//
44+
// Encode the message normally via C++ API
45+
//
46+
auto rawLen = ros::serialization::serializationLength(*i);
47+
auto msg = encoder->encode(*i, config);
48+
if (!msg) // msg is of type cras::expected; if it evaluates to false, it has .error(), otherwise it has .value()
49+
ROS_ERROR("%s", msg.error().c_str());
50+
else if (!msg.value()) // .value() can be nullptr if the encoder did not return anything for this input
51+
ROS_INFO("No message produced");
52+
else // ->value() is shorthand for .value().value() (unpacking cras::expected, and then cras::optional)
53+
ROS_INFO_THROTTLE(1.0, "ENCODE Raw size: %zu, compressed size: %u, ratio: %.2f %%, compressed type: %s",
54+
i->data.size(), msg->value().size(), 100.0 * msg->value().size() / rawLen,
55+
msg->value().getDataType().c_str());
56+
57+
//
58+
// Decode using C API; this is much more cumbersome, but it allows writing bindings in other languages.
59+
//
60+
61+
// Convert config to shapeshifter
62+
dynamic_reconfigure::Config configMsg;
63+
topic_tools::ShapeShifter configShifter;
64+
cras::msgToShapeShifter(configMsg, configShifter);
65+
66+
// Prepare output allocators; this is a very rough idea, they can probably be written much better.
67+
// Each dynamically sized output of the C API is exported via calling the allocator with a suitable size and
68+
// writing the result to the allocated buffer.
69+
sensor_msgs::PointCloud2 raw;
70+
uint32_t numFields;
71+
static std::vector<char*> fieldNames;
72+
fieldNames.clear();
73+
cras::allocator_t fieldNamesAllocator = [](size_t size) {
74+
fieldNames.push_back(new char[size]); return reinterpret_cast<void*>(fieldNames.back());};
75+
static std::vector<uint32_t> fieldOffsets;
76+
fieldOffsets.clear();
77+
cras::allocator_t fieldOffsetsAllocator = [](size_t) {
78+
fieldOffsets.push_back(0); return reinterpret_cast<void*>(&fieldOffsets.back());};
79+
static std::vector<uint8_t> fieldDatatypes;
80+
fieldDatatypes.clear();
81+
cras::allocator_t fieldDatatypesAllocator = [](size_t) {
82+
fieldDatatypes.push_back(0); return reinterpret_cast<void*>(&fieldDatatypes.back());};
83+
static std::vector<uint32_t> fieldCounts;
84+
fieldCounts.clear();
85+
cras::allocator_t fieldCountsAllocator = [](size_t) {
86+
fieldCounts.push_back(0); return reinterpret_cast<void*>(&fieldCounts.back());};
87+
static std::vector<uint8_t> data;
88+
data.clear();
89+
cras::allocator_t dataAllocator = [](size_t size) {
90+
data.resize(size); return reinterpret_cast<void*>(data.data());};
91+
static char* errorString = nullptr;
92+
cras::allocator_t errorStringAllocator = [](size_t size) {
93+
errorString = new char[size]; return reinterpret_cast<void*>(errorString); };
94+
95+
// Call the C API
96+
bool success = pointCloudTransportCodecsDecode(
97+
"draco", msg->value().getDataType().c_str(), msg->value().getMD5Sum().c_str(),
98+
cras::getBufferLength(msg->value()), cras::getBuffer(msg->value()), raw.height, raw.width,
99+
numFields, fieldNamesAllocator, fieldOffsetsAllocator, fieldDatatypesAllocator, fieldCountsAllocator,
100+
raw.is_bigendian, raw.point_step, raw.row_step, dataAllocator, raw.is_dense,
101+
cras::getBufferLength(configShifter), cras::getBuffer(configShifter), errorStringAllocator
102+
);
103+
104+
if (success)
105+
{
106+
// Reconstruct the fields
107+
for (size_t j = 0; j < numFields; ++j)
108+
{
109+
sensor_msgs::PointField field;
110+
field.name = fieldNames[j];
111+
field.offset = fieldOffsets[j];
112+
field.datatype = fieldDatatypes[j];
113+
field.count = fieldCounts[j];
114+
raw.fields.push_back(field);
115+
}
116+
ROS_INFO_THROTTLE(1.0, "DECODE Raw size: %zu, compressed size: %zu, num_fields %zu, field1 %s",
117+
data.size(), cras::getBufferLength(msg->value()), fieldNames.size(), fieldNames[0]);
118+
// ROS_INFO_STREAM_THROTTLE(1.0, raw);
119+
raw.data = data;
120+
}
121+
else
122+
{
123+
ROS_ERROR("%s", errorString);
124+
}
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)