Skip to content

Commit 3ccc16e

Browse files
tfoldiahcorde
authored andcommitted
feat: python bindings for image_transport and publish (#323)
Co-authored-by: Alejandro Hernández Cordero <[email protected]> (cherry picked from commit c2952b0)
1 parent 7a0da75 commit 3ccc16e

File tree

10 files changed

+867
-4
lines changed

10 files changed

+867
-4
lines changed

image_transport/include/image_transport/image_transport.hpp

+11
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ class ImageTransport
111111
IMAGE_TRANSPORT_PUBLIC
112112
explicit ImageTransport(rclcpp::Node::SharedPtr node);
113113

114+
IMAGE_TRANSPORT_PUBLIC
115+
ImageTransport(const ImageTransport & other);
116+
117+
IMAGE_TRANSPORT_PUBLIC
118+
ImageTransport & operator=(const ImageTransport & other);
119+
114120
IMAGE_TRANSPORT_PUBLIC
115121
~ImageTransport();
116122

@@ -351,6 +357,11 @@ class ImageTransport
351357
std::unique_ptr<Impl> impl_;
352358
};
353359

360+
struct ImageTransport::Impl
361+
{
362+
rclcpp::Node::SharedPtr node_;
363+
};
364+
354365
} // namespace image_transport
355366

356367
#endif // IMAGE_TRANSPORT__IMAGE_TRANSPORT_HPP_

image_transport/src/image_transport.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,8 @@ std::vector<std::string> getLoadableTransports()
127127
return loadableTransports;
128128
}
129129

130-
struct ImageTransport::Impl
131-
{
132-
rclcpp::Node::SharedPtr node_;
133-
};
130+
ImageTransport::ImageTransport(const ImageTransport & other)
131+
: impl_(std::make_unique<Impl>(*other.impl_)) {}
134132

135133
ImageTransport::ImageTransport(rclcpp::Node::SharedPtr node)
136134
: impl_(std::make_unique<ImageTransport::Impl>())

image_transport_py/CHANGELOG.rst

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2+
Changelog for package image_transport_py
3+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

image_transport_py/CMakeLists.txt

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
project(image_transport_py)
3+
4+
# Default to C99
5+
if(NOT CMAKE_C_STANDARD)
6+
set(CMAKE_C_STANDARD 99)
7+
endif()
8+
9+
# Default to C++17
10+
if(NOT CMAKE_CXX_STANDARD)
11+
set(CMAKE_CXX_STANDARD 17)
12+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
13+
endif()
14+
15+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
16+
add_compile_options(-Wall -Wextra -Wpedantic)
17+
endif()
18+
19+
find_package(ament_cmake REQUIRED)
20+
find_package(ament_cmake_python REQUIRED)
21+
find_package(ament_cmake_ros REQUIRED)
22+
find_package(image_transport REQUIRED)
23+
find_package(rclcpp REQUIRED)
24+
find_package(sensor_msgs REQUIRED)
25+
26+
# By default, without the settings below, find_package(Python3) will attempt
27+
# to find the newest python version it can, and additionally will find the
28+
# most specific version. For instance, on a system that has
29+
# /usr/bin/python3.10, /usr/bin/python3.11, and /usr/bin/python3, it will find
30+
# /usr/bin/python3.11, even if /usr/bin/python3 points to /usr/bin/python3.10.
31+
# The behavior we want is to prefer the "system" installed version unless the
32+
# user specifically tells us othewise through the Python3_EXECUTABLE hint.
33+
# Setting CMP0094 to NEW means that the search will stop after the first
34+
# python version is found. Setting Python3_FIND_UNVERSIONED_NAMES means that
35+
# the search will prefer /usr/bin/python3 over /usr/bin/python3.11. And that
36+
# latter functionality is only available in CMake 3.20 or later, so we need
37+
# at least that version.
38+
cmake_policy(SET CMP0094 NEW)
39+
set(Python3_FIND_UNVERSIONED_NAMES FIRST)
40+
41+
# Find python before pybind11
42+
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
43+
44+
find_package(pybind11_vendor REQUIRED)
45+
find_package(pybind11 REQUIRED)
46+
47+
ament_python_install_package(${PROJECT_NAME})
48+
49+
pybind11_add_module(_image_transport MODULE
50+
src/image_transport_py/pybind_image_transport.cpp
51+
)
52+
53+
target_include_directories(_image_transport PUBLIC
54+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
55+
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
56+
57+
target_link_libraries(_image_transport PUBLIC
58+
image_transport::image_transport
59+
rclcpp::rclcpp
60+
${sensor_msgs_TARGETS}
61+
)
62+
63+
# Install cython modules as sub-modules of the project
64+
install(
65+
TARGETS
66+
_image_transport
67+
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}"
68+
)
69+
70+
if(BUILD_TESTING)
71+
find_package(ament_lint_auto REQUIRED)
72+
73+
list(APPEND AMENT_LINT_AUTO_EXCLUDE
74+
ament_cmake_cppcheck
75+
)
76+
ament_lint_auto_find_test_dependencies()
77+
78+
if(NOT WIN32)
79+
ament_cppcheck(LANGUAGE "c++")
80+
else()
81+
message(STATUS "Skipping ament_cppcheck on Windows")
82+
endif()
83+
endif()
84+
85+
ament_package()

image_transport_py/README.md

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# image_transport_py: Python Bindings for ROS 2 Image Transport
2+
3+
## Introduction
4+
5+
`image_transport_py` is a Python package that provides bindings for `image_transport`. It enables efficient publishing and subscribing of images in Python, leveraging various transport plugins (e.g., `raw`, `compressed`).
6+
The package allows developers to handle image topics more efficiently and with less overhead than using standard ROS 2 topics.
7+
8+
## Usage
9+
10+
The detailed tutorial on `image_transport` and `image_transport_py` can be found at: https://github.com/ros-perception/image_transport_tutorials.
11+
12+
## Classes
13+
14+
### Publisher
15+
16+
A publisher for images.
17+
18+
#### Methods
19+
20+
- `get_topic()`
21+
22+
Returns the base image topic.
23+
24+
- `get_num_subscribers()`
25+
26+
Returns the number of subscribers this publisher is connected to.
27+
28+
- `shutdown()`
29+
30+
Unsubscribe the callback associated with this Publisher.
31+
32+
- `publish(img)`
33+
34+
Publish an image on the topics associated with this Publisher.
35+
36+
### CameraPublisher
37+
38+
A publisher for images with camera info.
39+
40+
#### Methods
41+
42+
- `get_topic()`
43+
44+
Returns the base (image) topic of this CameraPublisher.
45+
46+
- `get_num_subscribers()`
47+
48+
Returns the number of subscribers this camera publisher is connected to.
49+
50+
- `shutdown()`
51+
52+
Unsubscribe the callback associated with this CameraPublisher.
53+
54+
- `publish(img, info)`
55+
56+
Publish an image and camera info on the topics associated with this Publisher.
57+
58+
### ImageTransport
59+
60+
An object for image transport operations.
61+
62+
#### Constructor
63+
64+
- `__init__(node_name, image_transport="", launch_params_filepath="")`
65+
66+
Initialize an ImageTransport object with its node name, `image_transport` and launch params file path. If no `image_transport` specified, the default `raw` plugin will be initialized.
67+
68+
#### Methods
69+
70+
- `advertise(base_topic, queue_size, latch=False)`
71+
72+
Advertise an image topic.
73+
74+
- `advertise_camera(base_topic, queue_size, latch=False)`
75+
76+
Advertise an image topic with camera info.
77+
78+
- `subscribe(base_topic, queue_size, callback)`
79+
80+
Subscribe to an image topic.
81+
82+
- `subscribe_camera(base_topic, queue_size, callback)`
83+
84+
Subscribe to an image topic with camera info.
85+
86+
### Subscriber
87+
88+
A subscriber for images.
89+
90+
#### Methods
91+
92+
- `get_topic()`
93+
94+
Returns the base image topic.
95+
96+
- `get_num_publishers()`
97+
98+
Returns the number of publishers this subscriber is connected to.
99+
100+
- `get_transport()`
101+
102+
Returns the name of the transport being used.
103+
104+
- `shutdown()`
105+
106+
Unsubscribe the callback associated with this Subscriber.
107+
108+
### CameraSubscriber
109+
110+
A subscriber for images with camera info.
111+
112+
#### Methods
113+
114+
- `get_topic()`
115+
116+
Returns the base image topic.
117+
118+
- `get_num_publishers()`
119+
120+
Returns the number of publishers this subscriber is connected to.
121+
122+
- `get_transport()`
123+
124+
Returns the name of the transport being used.
125+
126+
- `shutdown()`
127+
128+
Unsubscribe the callback associated with this CameraSubscriber.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2024 Open Source Robotics Foundation, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from rpyutils import add_dll_directories_from_env
16+
17+
# Since Python 3.8, on Windows we should ensure DLL directories are explicitly added
18+
# to the search path.
19+
# See https://docs.python.org/3/whatsnew/3.8.html#bpo-36085-whatsnew
20+
with add_dll_directories_from_env('PATH'):
21+
from image_transport_py._image_transport import (
22+
ImageTransport,
23+
Publisher,
24+
Subscriber,
25+
CameraPublisher,
26+
CameraSubscriber,
27+
)
28+
29+
30+
__all__ = [
31+
'ImageTransport',
32+
'Publisher',
33+
'Subscriber',
34+
'CameraPublisher',
35+
'CameraSubscriber',
36+
]

0 commit comments

Comments
 (0)