Skip to content

Commit f2b9662

Browse files
facontidavideclaude
andcommitted
Rename transport to depthz; apply review cleanups; add package README
The transport, package, classes and parameters are now "depthz" (depthz_image_transport). The vendored codec keeps its internal method names ("dpred"/"dpred16"), so blobs stay interchangeable with the standalone library. Cleanups from a four-angle review (reuse/simplification/efficiency/ altitude): - bytes_per_pixel(PixelFormat) in the codec header replaces the ad-hoc bpp ternaries in publisher and subscriber - default CMAKE_BUILD_TYPE=RelWithDebInfo (never build the codec at -O0) and target-level cxx_std_20 instead of a directory-wide standard - decode reads the dictionary in place instead of copying it; the dictionary-overflow fallback no longer zero-fills its staging buffer - dict_size serialized via memcpy like the surrounding fields; begin_blob drops a temporary std::string; dead n==0 payload branch removed - dead ParameterDescriptor::name assignment and two unused includes removed; the gtest round-trip harness is now one template instead of two near-verbatim copies - new README: motivation vs compressedDepth and how to select the transport (image_transport parameter, enable_pub_plugins, republish) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a96dfcc commit f2b9662

15 files changed

Lines changed: 182 additions & 127 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Currently provided are:
5757

5858
- [compressed_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/rolling/compressed_image_transport)
5959

60-
- [dpred_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/rolling/dpred_image_transport) - A library compressing 32FC1 and 16UC1 depth images losslessly (value dictionary + 2D prediction + zstd).
60+
- [depthz_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/rolling/depthz_image_transport) - A library compressing 32FC1 and 16UC1 depth images losslessly (value dictionary + 2D prediction + zstd).
6161

6262
- [zstd_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/rolling/zstd_image_transport) - A libraory using ZSTD to compress the pointclouds.
6363

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
cmake_minimum_required(VERSION 3.20)
22

3-
project(dpred_image_transport)
3+
project(depthz_image_transport)
44

5-
# The vendored depth codec uses C++20 (std::bit_cast, std::countr_zero).
6-
if(NOT CMAKE_CXX_STANDARD)
7-
set(CMAKE_CXX_STANDARD 20)
5+
# The codec is performance-critical: never build it unoptimized by default.
6+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
7+
set(CMAKE_BUILD_TYPE RelWithDebInfo)
88
endif()
99

1010
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@@ -39,11 +39,13 @@ include_directories(include)
3939
add_library(
4040
${PROJECT_NAME} SHARED
4141
src/depth_codec.cpp
42-
src/dpred_publisher.cpp
43-
src/dpred_subscriber.cpp
42+
src/depthz_publisher.cpp
43+
src/depthz_subscriber.cpp
4444
src/manifest.cpp
4545
)
4646

47+
# The vendored depth codec uses C++20 (std::countr_zero, std::endian).
48+
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
4749
target_link_libraries(${PROJECT_NAME}
4850
${ZSTD_DEPENDENCY}
4951
image_transport::image_transport
@@ -62,14 +64,15 @@ install(
6264
DIRECTORY "include/"
6365
DESTINATION include
6466
)
65-
pluginlib_export_plugin_description_file(image_transport dpred_plugins.xml)
67+
pluginlib_export_plugin_description_file(image_transport depthz_plugins.xml)
6668

6769
if(BUILD_TESTING)
6870
find_package(ament_lint_auto REQUIRED)
6971
ament_lint_auto_find_test_dependencies()
7072

7173
find_package(ament_cmake_gtest REQUIRED)
7274
ament_add_gtest(test_depth_codec test/test_depth_codec.cpp)
75+
target_compile_features(test_depth_codec PRIVATE cxx_std_20)
7376
target_include_directories(test_depth_codec PRIVATE src)
7477
target_link_libraries(test_depth_codec ${PROJECT_NAME})
7578
endif()

depthz_image_transport/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# depthz_image_transport
2+
3+
Lossless `image_transport` plugin for depth images (`32FC1` and `16UC1`).
4+
5+
Compared to `compressedDepth` (PNG), `depthz` compresses better, encodes
6+
roughly 10× faster, and is truly lossless: `compressedDepth` quantizes
7+
32FC1 to 16 bits before the PNG stage, `depthz` reproduces the input
8+
bit-exactly (NaN included). The codec is vendored from
9+
[facontidavide/depth_image_compression](https://github.com/facontidavide/depth_image_compression),
10+
where the algorithm is documented.
11+
12+
## Usage
13+
14+
Subscribers select the transport with the standard `image_transport`
15+
parameter (in rviz2: the *Transport Hint* dropdown of the Image/Camera
16+
display):
17+
18+
```bash
19+
ros2 run my_pkg my_depth_consumer --ros-args -p image_transport:=depthz
20+
```
21+
22+
Publishers advertise all installed transports, so `<base_topic>/depthz`
23+
appears automatically. To publish only selected transports (saving encoder
24+
CPU), use the publishing node's `enable_pub_plugins` parameter:
25+
26+
```yaml
27+
/camera_node:
28+
ros__parameters:
29+
depth.image_rect.enable_pub_plugins:
30+
- image_transport/raw
31+
- image_transport/depthz
32+
```
33+
34+
An existing stream can be converted with
35+
`ros2 run image_transport republish` (`out_transport:=depthz`), e.g. for
36+
bag recording: record `<base_topic>/depthz` instead of
37+
`<base_topic>/compressedDepth`.
38+
39+
Parameters: `<base_topic>.depthz.zstd_level` (1–3, default 1).
40+
41+
Only `32FC1` and `16UC1` encodings are accepted; other encodings are
42+
declined with an error log (use `compressed` or `zstd` for color images).
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<library path="depthz_image_transport">
2+
<class name="image_transport/depthz_pub" type="depthz_image_transport::DepthzPublisher" base_class_type="image_transport::PublisherPlugin">
3+
<description>
4+
This plugin losslessly compresses 32FC1 and 16UC1 depth images with the dpred codec
5+
(value dictionary + 2D MED prediction + zstd).
6+
</description>
7+
</class>
8+
<class name="image_transport/depthz_sub" type="depthz_image_transport::DepthzSubscriber" base_class_type="image_transport::SubscriberPlugin">
9+
<description>
10+
This plugin decodes depthz-compressed depth images back to 32FC1 or 16UC1.
11+
</description>
12+
</class>
13+
</library>

dpred_image_transport/include/dpred_image_transport/dpred_publisher.hpp renamed to depthz_image_transport/include/depthz_image_transport/depthz_publisher.hpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,10 @@
2727
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2828
// POSSIBILITY OF SUCH DAMAGE.
2929

30-
#ifndef DPRED_IMAGE_TRANSPORT__DPRED_PUBLISHER_HPP_
31-
#define DPRED_IMAGE_TRANSPORT__DPRED_PUBLISHER_HPP_
30+
#ifndef DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_
31+
#define DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_
3232

3333
#include <string>
34-
#include <vector>
3534

3635
#include <sensor_msgs/msg/compressed_image.hpp>
3736
#include <sensor_msgs/msg/image.hpp>
@@ -40,20 +39,20 @@
4039

4140
#include <rclcpp/node.hpp>
4241

43-
namespace dpred_image_transport
42+
namespace depthz_image_transport
4443
{
4544

4645
using CompressedImage = sensor_msgs::msg::CompressedImage;
4746

48-
class DpredPublisher : public image_transport::SimplePublisherPlugin<CompressedImage>
47+
class DepthzPublisher : public image_transport::SimplePublisherPlugin<CompressedImage>
4948
{
5049
public:
51-
DpredPublisher();
52-
~DpredPublisher() override = default;
50+
DepthzPublisher();
51+
~DepthzPublisher() override = default;
5352

5453
std::string getTransportName() const override
5554
{
56-
return "dpred";
55+
return "depthz";
5756
}
5857

5958
protected:
@@ -74,6 +73,6 @@ class DpredPublisher : public image_transport::SimplePublisherPlugin<CompressedI
7473
std::string level_param_name_;
7574
};
7675

77-
} // namespace dpred_image_transport
76+
} // namespace depthz_image_transport
7877

79-
#endif // DPRED_IMAGE_TRANSPORT__DPRED_PUBLISHER_HPP_
78+
#endif // DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_

dpred_image_transport/include/dpred_image_transport/dpred_subscriber.hpp renamed to depthz_image_transport/include/depthz_image_transport/depthz_subscriber.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2828
// POSSIBILITY OF SUCH DAMAGE.
2929

30-
#ifndef DPRED_IMAGE_TRANSPORT__DPRED_SUBSCRIBER_HPP_
31-
#define DPRED_IMAGE_TRANSPORT__DPRED_SUBSCRIBER_HPP_
30+
#ifndef DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_
31+
#define DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_
3232

3333
#include <string>
3434

@@ -38,20 +38,20 @@
3838

3939
#include <rclcpp/node.hpp>
4040

41-
namespace dpred_image_transport
41+
namespace depthz_image_transport
4242
{
4343

4444
using CompressedImage = sensor_msgs::msg::CompressedImage;
4545

46-
class DpredSubscriber : public image_transport::SimpleSubscriberPlugin<CompressedImage>
46+
class DepthzSubscriber : public image_transport::SimpleSubscriberPlugin<CompressedImage>
4747
{
4848
public:
49-
DpredSubscriber();
50-
~DpredSubscriber() override = default;
49+
DepthzSubscriber();
50+
~DepthzSubscriber() override = default;
5151

5252
std::string getTransportName() const override
5353
{
54-
return "dpred";
54+
return "depthz";
5555
}
5656

5757
protected:
@@ -62,6 +62,6 @@ class DpredSubscriber : public image_transport::SimpleSubscriberPlugin<Compresse
6262
rclcpp::Logger logger_;
6363
};
6464

65-
} // namespace dpred_image_transport
65+
} // namespace depthz_image_transport
6666

67-
#endif // DPRED_IMAGE_TRANSPORT__DPRED_SUBSCRIBER_HPP_
67+
#endif // DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0"?>
22
<package format="3">
3-
<name>dpred_image_transport</name>
3+
<name>depthz_image_transport</name>
44
<version>7.0.1</version>
55
<description>
6-
dpred_image_transport provides a plugin to image_transport for transparently sending
6+
depthz_image_transport provides a plugin to image_transport for transparently sending
77
32FC1 and 16UC1 depth images losslessly compressed with the dpred codec (per-image value
88
dictionary + 2D prediction + zstd).
99
</description>
@@ -27,6 +27,6 @@
2727

2828
<export>
2929
<build_type>ament_cmake</build_type>
30-
<image_transport plugin="${prefix}/dpred_plugins.xml" />
30+
<image_transport plugin="${prefix}/depthz_plugins.xml" />
3131
</export>
3232
</package>

dpred_image_transport/src/depth_codec.cpp renamed to depthz_image_transport/src/depth_codec.cpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,11 @@ void dpred_encode(
382382
std::vector<uint16_t> idx;
383383
if (!build_value_dict(words, n, entries, idx)) {
384384
// > 65536 distinct values (e.g. full-precision float depth): store raw.
385-
std::vector<uint8_t> plain(1 + n * 4);
386-
plain[0] = 0;
387-
std::memcpy(plain.data() + 1, words, n * 4);
385+
const uint8_t * bytes = reinterpret_cast<const uint8_t *>(words);
386+
std::vector<uint8_t> plain;
387+
plain.reserve(1 + n * 4);
388+
plain.push_back(0);
389+
plain.insert(plain.end(), bytes, bytes + n * 4);
388390
zstd_append(out, plain.data(), plain.size(), level);
389391
return;
390392
}
@@ -432,10 +434,9 @@ void dpred_encode(
432434
std::vector<uint8_t> plain(6 + ds * 4 + n * 2);
433435
plain[0] = 1;
434436
plain[1] = 2; // residuals are always 2 bytes (split into two planes)
435-
for (int b = 0; b < 4; ++b) {
436-
plain[2 + b] = static_cast<uint8_t>(ds >> (8 * b));
437-
}
438437
static_assert(std::endian::native == std::endian::little, "format is little-endian");
438+
const uint32_t ds32 = static_cast<uint32_t>(ds);
439+
std::memcpy(plain.data() + 2, &ds32, 4);
439440
std::memcpy(plain.data() + 6, sorted_entries.data(), ds * 4);
440441

441442
uint8_t * lo = plain.data() + 6 + ds * 4;
@@ -448,9 +449,6 @@ void dpred_decode(const uint8_t * comp, size_t comp_size, float * out, uint32_t
448449
const size_t n = static_cast<size_t>(w) * h;
449450
std::vector<uint8_t> plain = zstd_unpack(comp, comp_size);
450451
if (plain.empty()) {
451-
if (n == 0) {
452-
return;
453-
}
454452
fail("dpred_decode: empty payload");
455453
}
456454
uint32_t * words = reinterpret_cast<uint32_t *>(out);
@@ -468,9 +466,7 @@ void dpred_decode(const uint8_t * comp, size_t comp_size, float * out, uint32_t
468466
if (plain.size() != 6 + ds * 4 + n * 2) {
469467
fail("dpred_decode: size mismatch");
470468
}
471-
static_assert(std::endian::native == std::endian::little, "format is little-endian");
472-
std::vector<uint32_t> dict(ds);
473-
std::memcpy(dict.data(), plain.data() + 6, ds * 4);
469+
const uint8_t * dict = plain.data() + 6; // read in place (get_u32 handles alignment)
474470
const uint8_t * lo = plain.data() + 6 + ds * 4;
475471

476472
std::vector<uint16_t> idx(n);
@@ -479,7 +475,7 @@ void dpred_decode(const uint8_t * comp, size_t comp_size, float * out, uint32_t
479475
if (idx[i] >= ds) {
480476
fail("dpred_decode: bad index");
481477
}
482-
words[i] = dict[idx[i]];
478+
words[i] = get_u32(dict + 4 * static_cast<size_t>(idx[i]));
483479
}
484480
}
485481

@@ -497,9 +493,9 @@ void begin_blob(
497493
out.clear();
498494
const char magic[4] = {'D', 'P', 'C', '1'};
499495
out.insert(out.end(), magic, magic + 4);
500-
const std::string name_str = name;
501-
out.push_back(static_cast<uint8_t>(name_str.size()));
502-
out.insert(out.end(), name_str.begin(), name_str.end());
496+
const size_t name_len = std::strlen(name);
497+
out.push_back(static_cast<uint8_t>(name_len));
498+
out.insert(out.end(), name, name + name_len);
503499
put_u32(out, static_cast<uint32_t>(level));
504500
put_u32(out, width);
505501
put_u32(out, height);

dpred_image_transport/src/depth_codec.hpp renamed to depthz_image_transport/src/depth_codec.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ struct BlobHeader
6464
PixelFormat format;
6565
};
6666

67+
constexpr size_t bytes_per_pixel(PixelFormat format)
68+
{
69+
return format == PixelFormat::FLOAT32 ? 4 : 2;
70+
}
71+
6772
/// Compress width*height float32 (32FC1) depth pixels into `out` (replacing
6873
/// its contents; existing capacity is reused, so a caller that keeps the
6974
/// vector alive across frames pays no steady-state output allocations).

0 commit comments

Comments
 (0)