Skip to content

Commit baa7870

Browse files
committed
Add depthz_image_transport: lossless depth compression (32FC1 and 16UC1)
New image_transport plugin for depth images. depthz losslessly compresses 32FC1 (bit-exact, NaN payloads included) and 16UC1 depth images: per-image value dictionary sorted by float total order (32FC1 only; a 16UC1 value is already its own index), 2D MED prediction on the index plane, zigzag residual byte planes, and a zstd entropy stage. The codec is vendored in src/ as a contrib library from https://github.com/facontidavide/depth_image_compression Performance: runtime AVX2 dispatch of the dictionary probe (portable release binaries keep SIMD speed) and wavefront decode (4 rows reconstructed as independent dependency chains). Encode writes the blob directly into the CompressedImage data field; decode writes pixels in place into image->data after a cheap header parse. Includes a gtest round-trip suite (special float values, residual wrap extremes, dictionary-overflow fallback, degenerate shapes, pixel-format mismatch, malformed input), a zstd_level (1-3) transport parameter and a README covering transport selection (image_transport parameter, enable_pub_plugins, republish). Verified end-to-end with raw -> depthz -> raw republish chains reproducing both pixel formats bit-exactly.
1 parent e458fa5 commit baa7870

14 files changed

Lines changed: 1528 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Currently provided are:
5757

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

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).
61+
6062
- [zstd_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/rolling/zstd_image_transport) - A libraory using ZSTD to compress the pointclouds.
6163

6264
- [theora_image_transport](https://github.com/ros-perception/image_transport_plugins/tree/master/theora_image_transport) - A library using theora to compress the pointclouds.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
3+
project(depthz_image_transport)
4+
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)
8+
endif()
9+
10+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
11+
add_compile_options(-Wall -Wextra -Wpedantic)
12+
endif()
13+
14+
find_package(ament_cmake REQUIRED)
15+
find_package(image_transport REQUIRED)
16+
find_package(pluginlib REQUIRED)
17+
find_package(rclcpp REQUIRED)
18+
find_package(sensor_msgs REQUIRED)
19+
20+
# zstd: use the CMake config package if the distro ships one, otherwise the
21+
# plain library. Prefer the shared target: distro static libzstd.a is not
22+
# built with -fPIC and cannot be linked into this shared plugin library.
23+
find_package(zstd CONFIG QUIET)
24+
if(TARGET zstd::libzstd)
25+
set(ZSTD_DEPENDENCY zstd::libzstd)
26+
elseif(TARGET zstd::libzstd_shared)
27+
set(ZSTD_DEPENDENCY zstd::libzstd_shared)
28+
elseif(TARGET zstd::libzstd_static)
29+
set(ZSTD_DEPENDENCY zstd::libzstd_static)
30+
else()
31+
find_path(ZSTD_INCLUDE_DIR zstd.h REQUIRED)
32+
find_library(ZSTD_LIBRARY NAMES zstd REQUIRED)
33+
set(ZSTD_DEPENDENCY ${ZSTD_LIBRARY})
34+
include_directories(${ZSTD_INCLUDE_DIR})
35+
endif()
36+
37+
include_directories(include)
38+
39+
add_library(
40+
${PROJECT_NAME} SHARED
41+
src/depth_codec.cpp
42+
src/depthz_publisher.cpp
43+
src/depthz_subscriber.cpp
44+
src/manifest.cpp
45+
)
46+
47+
# The vendored depth codec uses C++20 (std::countr_zero, std::endian).
48+
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
49+
target_link_libraries(${PROJECT_NAME}
50+
${ZSTD_DEPENDENCY}
51+
image_transport::image_transport
52+
rclcpp::rclcpp
53+
pluginlib::pluginlib
54+
${sensor_msgs_TARGETS}
55+
)
56+
57+
install(TARGETS ${PROJECT_NAME}
58+
ARCHIVE DESTINATION lib
59+
LIBRARY DESTINATION lib
60+
RUNTIME DESTINATION bin
61+
)
62+
63+
install(
64+
DIRECTORY "include/"
65+
DESTINATION include
66+
)
67+
pluginlib_export_plugin_description_file(image_transport depthz_plugins.xml)
68+
69+
if(BUILD_TESTING)
70+
find_package(ament_lint_auto REQUIRED)
71+
ament_lint_auto_find_test_dependencies()
72+
73+
find_package(ament_cmake_gtest REQUIRED)
74+
ament_add_gtest(test_depth_codec test/test_depth_codec.cpp)
75+
target_compile_features(test_depth_codec PRIVATE cxx_std_20)
76+
target_include_directories(test_depth_codec PRIVATE src)
77+
target_link_libraries(test_depth_codec ${PROJECT_NAME})
78+
endif()
79+
80+
ament_package()

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>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) 2026, Davide Faconti
2+
// All rights reserved.
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright
11+
// notice, this list of conditions and the following disclaimer in the
12+
// documentation and/or other materials provided with the distribution.
13+
//
14+
// * Neither the name of the copyright holder nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
// POSSIBILITY OF SUCH DAMAGE.
29+
30+
#ifndef DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_
31+
#define DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_
32+
33+
#include <string>
34+
35+
#include <sensor_msgs/msg/compressed_image.hpp>
36+
#include <sensor_msgs/msg/image.hpp>
37+
#include <image_transport/node_interfaces.hpp>
38+
#include <image_transport/simple_publisher_plugin.hpp>
39+
40+
#include <rclcpp/node.hpp>
41+
42+
namespace depthz_image_transport
43+
{
44+
45+
using CompressedImage = sensor_msgs::msg::CompressedImage;
46+
47+
class DepthzPublisher : public image_transport::SimplePublisherPlugin<CompressedImage>
48+
{
49+
public:
50+
DepthzPublisher();
51+
~DepthzPublisher() override = default;
52+
53+
std::string getTransportName() const override
54+
{
55+
return "depthz";
56+
}
57+
58+
protected:
59+
void advertiseImpl(
60+
image_transport::RequiredInterfaces node_interfaces,
61+
const std::string & base_topic,
62+
rclcpp::QoS custom_qos,
63+
rclcpp::PublisherOptions options) final;
64+
65+
void publish(
66+
const sensor_msgs::msg::Image & message,
67+
const PublisherT & publisher) const override;
68+
69+
rclcpp::Logger logger_;
70+
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_param_interface_;
71+
72+
private:
73+
std::string level_param_name_;
74+
};
75+
76+
} // namespace depthz_image_transport
77+
78+
#endif // DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_PUBLISHER_HPP_
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2026, Davide Faconti
2+
// All rights reserved.
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright
11+
// notice, this list of conditions and the following disclaimer in the
12+
// documentation and/or other materials provided with the distribution.
13+
//
14+
// * Neither the name of the copyright holder nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
// POSSIBILITY OF SUCH DAMAGE.
29+
30+
#ifndef DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_
31+
#define DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_
32+
33+
#include <string>
34+
35+
#include <sensor_msgs/msg/compressed_image.hpp>
36+
#include <sensor_msgs/msg/image.hpp>
37+
#include <image_transport/simple_subscriber_plugin.hpp>
38+
39+
#include <rclcpp/node.hpp>
40+
41+
namespace depthz_image_transport
42+
{
43+
44+
using CompressedImage = sensor_msgs::msg::CompressedImage;
45+
46+
class DepthzSubscriber : public image_transport::SimpleSubscriberPlugin<CompressedImage>
47+
{
48+
public:
49+
DepthzSubscriber();
50+
~DepthzSubscriber() override = default;
51+
52+
std::string getTransportName() const override
53+
{
54+
return "depthz";
55+
}
56+
57+
protected:
58+
void internalCallback(
59+
const CompressedImage::ConstSharedPtr & message,
60+
const Callback & user_cb) override;
61+
62+
rclcpp::Logger logger_;
63+
};
64+
65+
} // namespace depthz_image_transport
66+
67+
#endif // DEPTHZ_IMAGE_TRANSPORT__DEPTHZ_SUBSCRIBER_HPP_

depthz_image_transport/package.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0"?>
2+
<package format="3">
3+
<name>depthz_image_transport</name>
4+
<version>7.0.1</version>
5+
<description>
6+
depthz_image_transport provides a plugin to image_transport for transparently sending
7+
32FC1 and 16UC1 depth images losslessly compressed with the dpred codec (per-image value
8+
dictionary + 2D prediction + zstd).
9+
</description>
10+
<maintainer email="davide.faconti@gmail.com">Davide Faconti</maintainer>
11+
<license>BSD</license>
12+
13+
<url type="website">http://www.ros.org/wiki/image_transport_plugins</url>
14+
<author>Davide Faconti</author>
15+
16+
<buildtool_depend>ament_cmake</buildtool_depend>
17+
18+
<depend>image_transport</depend>
19+
<depend>libzstd-dev</depend>
20+
<depend>pluginlib</depend>
21+
<depend>rclcpp</depend>
22+
<depend>sensor_msgs</depend>
23+
24+
<test_depend>ament_cmake_gtest</test_depend>
25+
<test_depend>ament_lint_auto</test_depend>
26+
<test_depend>ament_lint_common</test_depend>
27+
28+
<export>
29+
<build_type>ament_cmake</build_type>
30+
<image_transport plugin="${prefix}/depthz_plugins.xml" />
31+
</export>
32+
</package>

0 commit comments

Comments
 (0)