Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: python bindings for image_transport and publish (backport #323) #328

Draft
wants to merge 1 commit into
base: jazzy
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions image_transport/include/image_transport/image_transport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ class ImageTransport
IMAGE_TRANSPORT_PUBLIC
explicit ImageTransport(rclcpp::Node::SharedPtr node);

IMAGE_TRANSPORT_PUBLIC
ImageTransport(const ImageTransport & other);

IMAGE_TRANSPORT_PUBLIC
ImageTransport & operator=(const ImageTransport & other);

IMAGE_TRANSPORT_PUBLIC
~ImageTransport();

Expand Down Expand Up @@ -364,6 +370,11 @@ class ImageTransport
std::unique_ptr<Impl> impl_;
};

struct ImageTransport::Impl
{
rclcpp::Node::SharedPtr node_;
};

} // namespace image_transport

#endif // IMAGE_TRANSPORT__IMAGE_TRANSPORT_HPP_
6 changes: 2 additions & 4 deletions image_transport/src/image_transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,8 @@ std::vector<std::string> getLoadableTransports()
return loadableTransports;
}

struct ImageTransport::Impl
{
rclcpp::Node::SharedPtr node_;
};
ImageTransport::ImageTransport(const ImageTransport & other)
: impl_(std::make_unique<Impl>(*other.impl_)) {}

ImageTransport::ImageTransport(rclcpp::Node::SharedPtr node)
: impl_(std::make_unique<ImageTransport::Impl>())
Expand Down
3 changes: 3 additions & 0 deletions image_transport_py/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package image_transport_py
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85 changes: 85 additions & 0 deletions image_transport_py/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
cmake_minimum_required(VERSION 3.20)
project(image_transport_py)

# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(image_transport REQUIRED)
find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)

# By default, without the settings below, find_package(Python3) will attempt
# to find the newest python version it can, and additionally will find the
# most specific version. For instance, on a system that has
# /usr/bin/python3.10, /usr/bin/python3.11, and /usr/bin/python3, it will find
# /usr/bin/python3.11, even if /usr/bin/python3 points to /usr/bin/python3.10.
# The behavior we want is to prefer the "system" installed version unless the
# user specifically tells us othewise through the Python3_EXECUTABLE hint.
# Setting CMP0094 to NEW means that the search will stop after the first
# python version is found. Setting Python3_FIND_UNVERSIONED_NAMES means that
# the search will prefer /usr/bin/python3 over /usr/bin/python3.11. And that
# latter functionality is only available in CMake 3.20 or later, so we need
# at least that version.
cmake_policy(SET CMP0094 NEW)
set(Python3_FIND_UNVERSIONED_NAMES FIRST)

# Find python before pybind11
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)

find_package(pybind11_vendor REQUIRED)
find_package(pybind11 REQUIRED)

ament_python_install_package(${PROJECT_NAME})

pybind11_add_module(_image_transport MODULE
src/image_transport_py/pybind_image_transport.cpp
)

target_include_directories(_image_transport PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")

target_link_libraries(_image_transport PUBLIC
image_transport::image_transport
rclcpp::rclcpp
${sensor_msgs_TARGETS}
)

# Install cython modules as sub-modules of the project
install(
TARGETS
_image_transport
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}"
)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)

list(APPEND AMENT_LINT_AUTO_EXCLUDE
ament_cmake_cppcheck
)
ament_lint_auto_find_test_dependencies()

if(NOT WIN32)
ament_cppcheck(LANGUAGE "c++")
else()
message(STATUS "Skipping ament_cppcheck on Windows")
endif()
endif()

ament_package()
128 changes: 128 additions & 0 deletions image_transport_py/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# image_transport_py: Python Bindings for ROS 2 Image Transport

## Introduction

`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`).
The package allows developers to handle image topics more efficiently and with less overhead than using standard ROS 2 topics.

## Usage

The detailed tutorial on `image_transport` and `image_transport_py` can be found at: https://github.com/ros-perception/image_transport_tutorials.

## Classes

### Publisher

A publisher for images.

#### Methods

- `get_topic()`

Returns the base image topic.

- `get_num_subscribers()`

Returns the number of subscribers this publisher is connected to.

- `shutdown()`

Unsubscribe the callback associated with this Publisher.

- `publish(img)`

Publish an image on the topics associated with this Publisher.

### CameraPublisher

A publisher for images with camera info.

#### Methods

- `get_topic()`

Returns the base (image) topic of this CameraPublisher.

- `get_num_subscribers()`

Returns the number of subscribers this camera publisher is connected to.

- `shutdown()`

Unsubscribe the callback associated with this CameraPublisher.

- `publish(img, info)`

Publish an image and camera info on the topics associated with this Publisher.

### ImageTransport

An object for image transport operations.

#### Constructor

- `__init__(node_name, image_transport="", launch_params_filepath="")`

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.

#### Methods

- `advertise(base_topic, queue_size, latch=False)`

Advertise an image topic.

- `advertise_camera(base_topic, queue_size, latch=False)`

Advertise an image topic with camera info.

- `subscribe(base_topic, queue_size, callback)`

Subscribe to an image topic.

- `subscribe_camera(base_topic, queue_size, callback)`

Subscribe to an image topic with camera info.

### Subscriber

A subscriber for images.

#### Methods

- `get_topic()`

Returns the base image topic.

- `get_num_publishers()`

Returns the number of publishers this subscriber is connected to.

- `get_transport()`

Returns the name of the transport being used.

- `shutdown()`

Unsubscribe the callback associated with this Subscriber.

### CameraSubscriber

A subscriber for images with camera info.

#### Methods

- `get_topic()`

Returns the base image topic.

- `get_num_publishers()`

Returns the number of publishers this subscriber is connected to.

- `get_transport()`

Returns the name of the transport being used.

- `shutdown()`

Unsubscribe the callback associated with this CameraSubscriber.
36 changes: 36 additions & 0 deletions image_transport_py/image_transport_py/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2024 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from rpyutils import add_dll_directories_from_env

# Since Python 3.8, on Windows we should ensure DLL directories are explicitly added
# to the search path.
# See https://docs.python.org/3/whatsnew/3.8.html#bpo-36085-whatsnew
with add_dll_directories_from_env('PATH'):
from image_transport_py._image_transport import (
ImageTransport,
Publisher,
Subscriber,
CameraPublisher,
CameraSubscriber,
)


__all__ = [
'ImageTransport',
'Publisher',
'Subscriber',
'CameraPublisher',
'CameraSubscriber',
]
Loading