From 2ddf0a75c3a4f995adc44271e5b415ecef7b48a9 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Thu, 16 Apr 2026 23:14:57 +0200 Subject: [PATCH] Refactor image_transport tutorial repository Signed-off-by: Alejandro Hernandez Cordero --- README.md | 410 +----------------- doc/cpp_publisher.md | 78 ++++ doc/cpp_subscriber.md | 68 +++ doc/custom_plugin.md | 83 ++++ doc/different_transports.md | 138 ++++++ doc/installation.md | 20 + doc/python_publisher.md | 59 +++ doc/python_subscriber.md | 52 +++ image_transport_tutorial_msgs/CMakeLists.txt | 18 + .../msg/ResizedImage.msg | 0 image_transport_tutorial_msgs/package.xml | 25 ++ image_transport_tutorials/CMakeLists.txt | 47 +- image_transport_tutorials/package.xml | 22 +- image_transport_tutorials/resized_plugins.xml | 13 - resize_image_transport/CMakeLists.txt | 70 +++ .../resized_publisher.hpp | 22 +- .../resized_subscriber.hpp | 20 +- resize_image_transport/package.xml | 27 ++ resize_image_transport/resized_plugins.xml | 21 + .../src/manifest.cpp | 11 +- .../src/resized_publisher.cpp | 11 +- .../src/resized_subscriber.cpp | 7 +- 22 files changed, 723 insertions(+), 499 deletions(-) create mode 100644 doc/cpp_publisher.md create mode 100644 doc/cpp_subscriber.md create mode 100644 doc/custom_plugin.md create mode 100644 doc/different_transports.md create mode 100644 doc/installation.md create mode 100644 doc/python_publisher.md create mode 100644 doc/python_subscriber.md create mode 100644 image_transport_tutorial_msgs/CMakeLists.txt rename {image_transport_tutorials => image_transport_tutorial_msgs}/msg/ResizedImage.msg (100%) create mode 100644 image_transport_tutorial_msgs/package.xml delete mode 100644 image_transport_tutorials/resized_plugins.xml create mode 100644 resize_image_transport/CMakeLists.txt rename {image_transport_tutorials/include/image_transport_tutorials => resize_image_transport/include/resize_image_transport}/resized_publisher.hpp (68%) rename {image_transport_tutorials/include/image_transport_tutorials => resize_image_transport/include/resize_image_transport}/resized_subscriber.hpp (66%) create mode 100644 resize_image_transport/package.xml create mode 100644 resize_image_transport/resized_plugins.xml rename {image_transport_tutorials => resize_image_transport}/src/manifest.cpp (63%) rename {image_transport_tutorials => resize_image_transport}/src/resized_publisher.cpp (86%) rename {image_transport_tutorials => resize_image_transport}/src/resized_subscriber.cpp (86%) diff --git a/README.md b/README.md index c24f00f..2f0bc70 100644 --- a/README.md +++ b/README.md @@ -1,399 +1,27 @@ # image_transport_tutorials -# Table of Contents -1. [Installation](#installation) -2. [Writing a Simple Image Publisher (C++)](#cpp_simple_image_pub) -3. [Writing a Simple Image Subscriber (C++)](#cpp_simple_image_sub) -4. [Running the Simple Image Publisher and Subscriber with Different Transport](#different_publisher_subscriber) -5. [Writing a Simple Image Publisher (Python)](#py_simple_image_pub) -6. [Writing a Simple Image Subscriber (Python)](#py_simple_image_sub) +Tutorials for using [image_transport](https://github.com/ros-perception/image_transport) in ROS 2. -## Installation - -Before starting any of the tutorials below, create a workspace and clone this repository so you can inspect and manipulate the code: - -``` -$ mkdir -p ~/image_transport_tutorials_ws/src -$ cd ~/image_transport_tutorials_ws/src -$ git clone https://github.com/ros-perception/image_transport_tutorials.git -``` - -Install needed dependencies: - -``` -$ cd ~/image_transport_tutorials_ws/ -$ source /opt/ros/iron/setup.bash -$ rosdep install -i --from-path src --rosdistro iron -y -$ colcon build -``` - -Make sure to include the correct setup file (in the above example it is for Iron on Ubuntu and for bash). - -## Writing a Simple Image Publisher (C++) -Description: This tutorial shows how to create a publisher node that will continually publish an image. - -Tutorial Level: Beginner - -Take a look at [my_publisher.cpp](image_transport_tutorials/src/my_publisher.cpp). - -### The code explained -Now, let's break down the code piece by piece. -For lines not explained here, review [Writing a Simple Publisher and Subscriber (C++)](https://docs.ros.org/en/galactic/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html). - -``` -#include "cv_bridge/cv_bridge.h" -#include "image_transport/image_transport.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "rclcpp/rclcpp.hpp" -``` - -These headers will allow us to load an image using OpenCV, convert it to the ROS message format, and publish it. - -``` -rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("image_publisher", options); -image_transport::ImageTransport it(node); -``` - -We create an `ImageTransport` instance, initializing it with our node. -We use methods of `ImageTransport` to create image publishers and subscribers, much as we use methods of `Node` to create generic ROS publishers and subscribers. - -``` -image_transport::Publisher pub = it.advertise("camera/image", 1); -``` - -Advertise that we are going to be publishing images on the base topic `camera/image`. -Depending on whether more plugins are built, additional (per-plugin) topics derived from the base topic may also be advertised. -The second argument is the size of our publishing queue. - -`advertise()` returns an `image_transport::Publisher` object, which serves two purposes: -1. It contains a `publish()` method that lets you publish images onto the base topic it was created with -2. When it goes out of scope, it will automatically unadvertise - -``` -cv::Mat image = cv::imread(argv[1], cv::IMREAD_COLOR); -std_msgs::msg::Header hdr; -sensor_msgs::msg::Image::SharedPtr msg; -msg = cv_bridge::CvImage(hdr, "bgr8", image).toImageMsg(); -``` - -We load a user-specified (on the command line) color image from disk using OpenCV, then convert it to the ROS type `sensor_msgs/msg/Image`. - -``` -rclcpp::WallRate loop_rate(5); -rclcpp::executors::SingleThreadedExecutor executor; -executor.add_node(node); -while (rclcpp::ok()) { - pub.publish(msg); - executor.spin_some(); - loop_rate.sleep(); -} -``` - -We broadcast the image to anyone connected to one of our topics, exactly as we would have using an `rclcpp::Publisher`. - -### Adding video stream from a webcam -The example above requires a path to an image file to be added as a command line parameter. -This image will be converted and sent as a message to an image subscriber. -In most cases, however, this is not a very practical example as you are often required to handle streaming data. -(For example: multiple webcams mounted on a robot record the scene around it and you have to pass the image data to some other node for further analysis). - -The publisher example can be modified quite easily to make it work with a video device supported by `cv::VideoCapture` (in case it is not, you have to handle it accordingly). -Take a look at [publisher_from_video.cpp](image_transport_tutorials/src/publisher_from_video.cpp) to see how a video device can be passed in as a command line argument and used as the image source. - -If you have a single device, you do not need to do the whole routine with passing a command line argument. -In this case, you can hard-code the index/address of the device and directly pass it to the video capturing structure in OpenCV (example: `cv::VideoCapture(0)` if `/dev/video0` is used). -Multiple checks are also included here to make sure that the publisher does not break if the camera is shut down. -If the retrieved frame from the video device is not empty, it will then be converted to a ROS message which will be published by the publisher. - -## Writing a Simple Image Subscriber (C++) -Description: This tutorial shows how to create a subscriber node that will display an image on the screen. -By using the `image_transport` subscriber to subscribe to images, any image transport can be used at runtime. -To learn how to actually use a specific image transport, see the next tutorial. - -Tutorial Level: Beginner - -Take a look at [my_subscriber.cpp](image_transport_tutorials/src/my_subscriber.cpp). - -### The code explained -Now, let's break down the code piece by piece. - -``` -#include "cv_bridge/cv_bridge.h" -#include "image_transport/image_transport.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "rclcpp/logging.hpp" -#include "rclcpp/rclcpp.hpp" -``` - -These headers will allow us to subscribe to image messages, display images using OpenCV's simple GUI capabilities, and log errors. - -``` -void imageCallback(const sensor_msgs::msg::Image::ConstSharedPtr & msg) -``` - -This is the callback function that will be called when a new image has arrived on the `camera/image` topic. -Although the image may have been sent in some arbitrary transport-specific message type, notice that the callback need only handle the normal `sensor_msgs/msg/Image` type. -All image encoding/decoding is handled automatically for you. - -``` -try { - cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image); - cv::waitKey(10); -} catch (cv_bridge::Exception & e) { - auto logger = rclcpp::get_logger("my_subscriber"); - RCLCPP_ERROR(logger, "Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str()); -``` - -The body of the callback. -We convert the ROS image message into an OpenCV image with BGR pixel encoding, then show it in a display window. - -``` -rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("image_listener", options); -image_transport::ImageTransport it(node); -``` - -We create an `ImageTransport` instance, initializing it with our node. - -``` -image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback); -``` - -Subscribe to the `camera/image` base topic. -The actual ROS topic subscribed to depends on which transport is used. -In the default case, "raw" transport, the topic is `camera/image` with type `sensor_msgs/msg/Image`. -ROS will call the `imageCallback` function whenever a new image arrives. -The 2nd argument is the queue size. - -`subscribe()` returns an `image_transport::Subscriber` object that you must hold on to until you want to unsubscribe. -When the Subscriber object is destructed, it will automatically unsubscribe from the `camera/image` base topic. - -In just a few lines of code, we have written a ROS image viewer that can handle images in both raw and a variety of compressed forms. - -## Running the Simple Image Publisher and Subscriber with Different Transports -Description: This tutorial discusses running the simple image publisher and subscriber using multiple transports. - -Tutorial Level: Beginner - -### Running the publisher -In a previous tutorial we made a publisher node called `my_publisher`. -Now run the node with an image file as the command-line argument: - -``` -$ ros2 run image_transport_tutorials my_publisher path/to/some/image.jpg -``` - -To check that your node is running properly, list the topics being published: - -``` -$ ros2 topic list -``` - -You should see `/camera/image` in the output. -You can also get more information about the topic: - -``` -$ ros2 topic info /camera/image -``` - -The output should be: - -``` -Type: sensor_msgs/msg/Image -Publisher count: 1 -Subscription count: 0 -``` - -### Running the subscriber -In the last tutorial, we made a subscriber node called `my_subscriber`. Now run it: - -``` -$ ros2 run image_transport_tutorials my_subscriber -``` - -You should see a window pop up with the image you gave to the publisher. +## Packages -### Finding available transports -`image_transport` searches your ROS installation for transport plugins at runtime and dynamically loads all that are built. -This affords you great flexibility in adding additional transports, but makes it unclear which are available on your system. -`image_transport` provides a `list_transports` executable for this purpose: +| Package | Description | +|---|---| +| `image_transport_tutorial_msgs` | Custom message definitions (`ResizedImage.msg`) used by the resized transport plugin | +| `image_transport_tutorials` | C++ tutorial nodes: simple image publisher and subscriber | +| `resize_image_transport` | `image_transport` plugin that publishes/subscribes to half-resolution images | +| `image_transport_tutorials_py` | Python tutorial nodes: simple image publisher and subscriber | -``` -$ ros2 run image_transport list_transports -``` - -Which should show: - -``` -Declared transports: -image_transport/raw - -Details: ----------- -"image_transport/raw" - - Provided by package: image_transport - - Publisher: - This is the default publisher. It publishes the Image as-is on the base topic. - - - Subscriber: - This is the default pass-through subscriber for topics of type sensor_msgs/Image. -``` - -This the expected output for an otherwise new ROS installation after completing the previous tutorials. -Depending on your setup, you may already have "theora" or other transports available. - -### Adding new transports -Our nodes are currently communicating raw `sensor_msgs/msg/Image` messages, so we are not gaining anything over using `rclcpp::Publisher` and `rclcpp::Subscriber`. -Let's change that by introducing a new transport. - -The `compressed_image_transport` package provides plugins for the "compressed" transport, which sends images over the wire in either JPEG- or PNG-compressed form. -Notice that `compressed_image_transport` is not a dependency of your package; `image_transport` will automatically discover all transport plugins built in your ROS system. - -The easiest way to add the "compressed" transport is to install the package: - -``` -$ sudo apt-get install ros-iron-compressed-image-transport -``` - -Or install all the transport plugins at once: - -``` -$ sudo apt-get install ros-iron-image-transport-plugins -``` - -But you can also build from source. - -### Changing the transport used -Now let's start up a new subscriber, this one using compressed transport. -The key is that `image_transport` subscribers check the parameter `_image_transport` for the name of a transport to use in place of "raw". -Let's set this parameter and start a subscriber node with name "compressed_listener": - -``` -$ ros2 run image_transport_tutorials my_subscriber --ros-args --remap __name:=compressed_listener -p image_transport:=compressed -``` - -You should see an identical image window pop up. - -`compressed_listener` is listening to a separate topic carrying JPEG-compressed versions of the same images published on `/camera/image`. - -### Changing transport-specific behavior -For a particular transport, we may want to tweak settings such as compression level, bit rate, etc. -Transport plugins can expose such settings through ROS parameters. -For example, `/camera/image/compressed` allows you to change the compression format and quality on the fly; see the package documentation for full details. - -For now let's adjust the JPEG quality. -By default, the "compressed" transport uses JPEG compression at 80% quality. -Let's change it to 15%. -We can use the GUI, `rqt_reconfigure`, to change the quality: - -``` -$ ros2 run rqt_reconfigure rqt_reconfigure -``` - -Now pick `/image_publisher` in the drop-down menu and move the `jpeg_quality` slider down to 15%. -Do you see the compression artifacts in your second view window? - -The `rqt_reconfigure` GUI has updated the ROS parameter `/image_publisher/jpeg_quality`. -You can verify this by running: - -``` -$ ros2 param get /image_publisher jpeg_quality -``` - -This should display 15. - -## Writing a Simple Image Publisher (Python) - -Description: This tutorial shows how to create a publisher node that will continually publish an image with random contents from Python. - -Tutorial Level: Beginner - -Take a look at [my_publisher.py](image_transport_tutorials_py/image_transport_tutorials_py/my_publisher.py). - -To publish images using `image_transport_py`, you create an `ImageTransport` object and use it to advertise an image topic. The first parameter for `ImageTransport` is the image transport -node's name which needs to be unique in the namespace. - -Steps: - -1. Import Necessary Modules: - -```python -import rclpy -from rclpy.node import Node -from sensor_msgs.msg import Image -from image_transport_py import ImageTransport -``` - -2. Initialize the Node and ImageTransport: - -```python - def __init__(self): - super().__init__('my_publisher') - - self.image_transport = ImageTransport( - 'imagetransport_pub', image_transport='compressed' - ) - self.img_pub = self.image_transport.advertise('camera/image', 10) - - self.bridge = CvBridge() - - timer_period = 0.5 - self.timer = self.create_timer(timer_period, self.timer_callback) -``` - -3. Publish Images in the Callback: -```python - def publish_image(self): - image_msg = .. # Read image from your devices - - image_msg.header.stamp = self.get_clock().now().to_msg() - self.publisher.publish(image_msg) -``` - -`advertise_camera` can publish `CameraInfo` along with `Image` message. - -## Writing a Simple Image Subscriber (Python) - -Description: This tutorial shows how to create a subscriber node that will receive the contents of the published -image. By using the `image_transport` subscriber to subscribe to images, any image transport can be used at runtime. - -Tutorial Level: Beginner - -Take a look at [my_subscriber.py](image_transport_tutorials_py/image_transport_tutorials_py/my_subscriber.py). - -To subscribe to images, use `ImageTransport` to create a subscription to the image topic. - -Steps: - -1. Import Necessary Modules: - -```python -import rclpy -from rclpy.node import Node -from image_transport_py import ImageTransport - -``` - -2. Initialize the Node and ImageTransport: -```python -class MySubscriber(Node): - def __init__(self): - super().__init__('my_subscriber') - - image_transport = ImageTransport( - 'imagetransport_sub', image_transport='compressed' - ) - image_transport.subscribe('camera/image', 10, self.image_callback) -``` - -3. Handle Incoming Images: -```python - def image_callback(self, msg): - self.get_logger().info('got a new image from frame_id:=%s' % msg.header.frame_id) -``` +## Installation -`subscribe_camera` will add `CameraInfo` along with `Image` message for the callback. +See [doc/installation.md](doc/installation.md). -### Transport Selection +## Tutorials -By default, `image_transport` uses the `raw` transport. You can specify a different transport by passing `image_transport` parameter to `ImageTransport`. Alternatively, -you can use your own ROS2 parameter file for the imagetransport node via `launch_params_filepath` parameter. +| Tutorial | Level | +|---|---| +| [Writing a Simple Image Publisher (C++)](doc/cpp_publisher.md) | Beginner | +| [Writing a Simple Image Subscriber (C++)](doc/cpp_subscriber.md) | Beginner | +| [Running the Publisher and Subscriber with Different Transports](doc/different_transports.md) | Beginner | +| [Writing a Custom Image Transport Plugin](doc/custom_plugin.md) | Intermediate | +| [Writing a Simple Image Publisher (Python)](doc/python_publisher.md) | Beginner | +| [Writing a Simple Image Subscriber (Python)](doc/python_subscriber.md) | Beginner | diff --git a/doc/cpp_publisher.md b/doc/cpp_publisher.md new file mode 100644 index 0000000..f42155a --- /dev/null +++ b/doc/cpp_publisher.md @@ -0,0 +1,78 @@ +# Writing a Simple Image Publisher (C++) + +Description: This tutorial shows how to create a publisher node that will continually publish an image. + +Tutorial Level: Beginner + +Take a look at [my_publisher.cpp](../image_transport_tutorials/src/my_publisher.cpp). + +## The code explained + +Now, let's break down the code piece by piece. +For lines not explained here, review [Writing a Simple Publisher and Subscriber (C++)](https://docs.ros.org/en/rolling/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html). + +```cpp +#include "cv_bridge/cv_bridge.hpp" +#include "image_transport/image_transport.hpp" +#include "opencv2/core/mat.hpp" +#include "opencv2/imgcodecs.hpp" +#include "rclcpp/rclcpp.hpp" +``` + +These headers will allow us to load an image using OpenCV, convert it to the ROS message format, and publish it. + +```cpp +rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("image_publisher", options); +image_transport::ImageTransport it{*node}; +``` + +We create an `ImageTransport` instance, initializing it with our node. +We use methods of `ImageTransport` to create image publishers and subscribers, much as we use methods of `Node` to create generic ROS publishers and subscribers. + +```cpp +image_transport::Publisher pub = it.advertise("camera/image", 1); +``` + +Advertise that we are going to be publishing images on the base topic `camera/image`. +Depending on whether more plugins are built, additional (per-plugin) topics derived from the base topic may also be advertised. +The second argument is the size of our publishing queue. + +`advertise()` returns an `image_transport::Publisher` object, which serves two purposes: +1. It contains a `publish()` method that lets you publish images onto the base topic it was created with +2. When it goes out of scope, it will automatically unadvertise + +```cpp +cv::Mat image = cv::imread(argv[1], cv::IMREAD_COLOR); +std_msgs::msg::Header hdr; +sensor_msgs::msg::Image::SharedPtr msg = cv_bridge::CvImage(hdr, "bgr8", image).toImageMsg(); +``` + +We load a user-specified (on the command line) color image from disk using OpenCV, then convert it to the ROS type `sensor_msgs/msg/Image`. + +```cpp +rclcpp::WallRate loop_rate(5); +rclcpp::executors::SingleThreadedExecutor executor; +executor.add_node(node); +while (rclcpp::ok()) { + pub.publish(msg); + executor.spin_some(); + loop_rate.sleep(); +} +``` + +We broadcast the image to anyone connected to one of our topics, exactly as we would have using an `rclcpp::Publisher`. + +## Adding video stream from a webcam + +The example above requires a path to an image file to be added as a command line parameter. +This image will be converted and sent as a message to an image subscriber. +In most cases, however, this is not a very practical example as you are often required to handle streaming data. +(For example: multiple webcams mounted on a robot record the scene around it and you have to pass the image data to some other node for further analysis). + +The publisher example can be modified quite easily to make it work with a video device supported by `cv::VideoCapture` (in case it is not, you have to handle it accordingly). +Take a look at [publisher_from_video.cpp](../image_transport_tutorials/src/publisher_from_video.cpp) to see how a video device can be passed in as a command line argument and used as the image source. + +If you have a single device, you do not need to do the whole routine with passing a command line argument. +In this case, you can hard-code the index/address of the device and directly pass it to the video capturing structure in OpenCV (example: `cv::VideoCapture(0)` if `/dev/video0` is used). +Multiple checks are also included here to make sure that the publisher does not break if the camera is shut down. +If the retrieved frame from the video device is not empty, it will then be converted to a ROS message which will be published by the publisher. diff --git a/doc/cpp_subscriber.md b/doc/cpp_subscriber.md new file mode 100644 index 0000000..354ad2a --- /dev/null +++ b/doc/cpp_subscriber.md @@ -0,0 +1,68 @@ +# Writing a Simple Image Subscriber (C++) + +Description: This tutorial shows how to create a subscriber node that will display an image on the screen. +By using the `image_transport` subscriber to subscribe to images, any image transport can be used at runtime. +To learn how to actually use a specific image transport, see the [next tutorial](different_transports.md). + +Tutorial Level: Beginner + +Take a look at [my_subscriber.cpp](../image_transport_tutorials/src/my_subscriber.cpp). + +## The code explained + +Now, let's break down the code piece by piece. + +```cpp +#include "cv_bridge/cv_bridge.hpp" +#include "image_transport/image_transport.hpp" +#include "opencv2/highgui.hpp" +#include "rclcpp/logging.hpp" +#include "rclcpp/rclcpp.hpp" +#include "sensor_msgs/msg/image.hpp" +``` + +These headers will allow us to subscribe to image messages, display images using OpenCV's simple GUI capabilities, and log errors. + +```cpp +void imageCallback(const sensor_msgs::msg::Image::ConstSharedPtr & msg) +``` + +This is the callback function that will be called when a new image has arrived on the `camera/image` topic. +Although the image may have been sent in some arbitrary transport-specific message type, notice that the callback need only handle the normal `sensor_msgs/msg/Image` type. +All image encoding/decoding is handled automatically for you. + +```cpp +try { + cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image); + cv::waitKey(10); +} catch (const cv_bridge::Exception & e) { + auto logger = rclcpp::get_logger("my_subscriber"); + RCLCPP_ERROR(logger, "Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str()); +} +``` + +The body of the callback. +We convert the ROS image message into an OpenCV image with BGR pixel encoding, then show it in a display window. + +```cpp +rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("image_listener", options); +image_transport::ImageTransport it{*node}; +``` + +We create an `ImageTransport` instance, initializing it with our node. + +```cpp +image_transport::TransportHints hints{*node}; +image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback, &hints); +``` + +Subscribe to the `camera/image` base topic. +`TransportHints` reads the `image_transport` node parameter to select the active transport at runtime (defaults to `"raw"`). +The actual ROS topic subscribed to depends on which transport is in use. +ROS will call the `imageCallback` function whenever a new image arrives. +The 2nd argument is the queue size. + +`subscribe()` returns an `image_transport::Subscriber` object that you must hold on to until you want to unsubscribe. +When the Subscriber object is destructed, it will automatically unsubscribe from the `camera/image` base topic. + +In just a few lines of code, we have written a ROS image viewer that can handle images in both raw and a variety of compressed forms. diff --git a/doc/custom_plugin.md b/doc/custom_plugin.md new file mode 100644 index 0000000..688ce46 --- /dev/null +++ b/doc/custom_plugin.md @@ -0,0 +1,83 @@ +# Writing a Custom Image Transport Plugin + +Description: This tutorial shows how to write a custom `image_transport` plugin that publishes and subscribes to a custom message type. +The example plugin (`resize_image_transport`) decimates the image by a factor of 2 on publish, and restores it to the original size on subscribe. + +Tutorial Level: Intermediate + +The plugin spans two packages: + +- **`image_transport_tutorial_msgs`** — defines the `ResizedImage.msg` message used on the wire: + + ``` + uint32 original_height + uint32 original_width + sensor_msgs/Image image + ``` + +- **`resize_image_transport`** — implements the plugin itself. + +## Publisher plugin + +Take a look at [resized_publisher.hpp](../resize_image_transport/include/resize_image_transport/resized_publisher.hpp) and [resized_publisher.cpp](../resize_image_transport/src/resized_publisher.cpp). + +The publisher inherits from `image_transport::SimplePublisherPlugin`, templated on the wire message type: + +```cpp +class ResizedPublisher : public image_transport::SimplePublisherPlugin + +``` + +The `publish()` override receives a `sensor_msgs/msg/Image`, halves its resolution with OpenCV, and publishes a `ResizedImage`: + +```cpp +void ResizedPublisher::publish( + const sensor_msgs::msg::Image & message, + const PublisherT & publisher) const +{ + // ... + image_transport_tutorial_msgs::msg::ResizedImage resized_image; + resized_image.original_height = cv_image.rows; + resized_image.original_width = cv_image.cols; + resized_image.image = *(cv_bridge::CvImage(message.header, "bgr8", cv_image).toImageMsg()); + publisher->publish(resized_image); +} +``` + +## Subscriber plugin + +Take a look at [resized_subscriber.hpp](../resize_image_transport/include/resize_image_transport/resized_subscriber.hpp) and [resized_subscriber.cpp](../resize_image_transport/src/resized_subscriber.cpp). + +The subscriber inherits from `image_transport::SimpleSubscriberPlugin` and its `internalCallback()` override receives a `ResizedImage`, restores the original resolution, and forwards a `sensor_msgs/msg/Image` to the user callback: + +```cpp +void ResizedSubscriber::internalCallback( + const image_transport_tutorial_msgs::msg::ResizedImage::ConstSharedPtr & msg, + const Callback & user_cb) +{ + // ... + cv::resize(img_rsz, img_restored, cv::Size(msg->original_width, msg->original_height)); + cv_bridge::CvImage cv_img(msg->image.header, msg->image.encoding, img_restored); + user_cb(cv_img.toImageMsg()); +} +``` + +## Registering the plugin + +[manifest.cpp](../resize_image_transport/src/manifest.cpp) registers both classes with pluginlib: + +```cpp +PLUGINLIB_EXPORT_CLASS(resize_image_transport::ResizedPublisher, image_transport::PublisherPlugin) +PLUGINLIB_EXPORT_CLASS(resize_image_transport::ResizedSubscriber, image_transport::SubscriberPlugin) +``` + +The plugin description file [resized_plugins.xml](../resize_image_transport/resized_plugins.xml) declares the transport name and wire message type so `image_transport` can discover the plugin at runtime. + +## Using the resized transport + +After building, run the publisher and subscriber with the resized transport: + +``` +$ ros2 run image_transport_tutorials my_publisher path/to/some/image.jpg +$ ros2 run image_transport_tutorials my_subscriber --ros-args -p image_transport:=resized +``` diff --git a/doc/different_transports.md b/doc/different_transports.md new file mode 100644 index 0000000..0e6b4da --- /dev/null +++ b/doc/different_transports.md @@ -0,0 +1,138 @@ +# Running the Simple Image Publisher and Subscriber with Different Transports + +Description: This tutorial discusses running the simple image publisher and subscriber using multiple transports. + +Tutorial Level: Beginner + +## Running the publisher + +In a previous tutorial we made a publisher node called `my_publisher`. +Now run the node with an image file as the command-line argument: + +``` +$ ros2 run image_transport_tutorials my_publisher path/to/some/image.jpg +``` + +To check that your node is running properly, list the topics being published: + +``` +$ ros2 topic list +``` + +You should see `/camera/image` in the output. +You can also get more information about the topic: + +``` +$ ros2 topic info /camera/image +``` + +The output should be: + +``` +Type: sensor_msgs/msg/Image +Publisher count: 1 +Subscription count: 0 +``` + +## Running the subscriber + +In the last tutorial, we made a subscriber node called `my_subscriber`. Now run it: + +``` +$ ros2 run image_transport_tutorials my_subscriber +``` + +You should see a window pop up with the image you gave to the publisher. + +## Finding available transports + +`image_transport` searches your ROS installation for transport plugins at runtime and dynamically loads all that are built. +This affords you great flexibility in adding additional transports, but makes it unclear which are available on your system. +`image_transport` provides a `list_transports` executable for this purpose: + +``` +$ ros2 run image_transport list_transports +``` + +Which should show at minimum: + +``` +Declared transports: +image_transport/raw + +Details: +---------- +"image_transport/raw" + - Provided by package: image_transport + - Publisher: + This is the default publisher. It publishes the Image as-is on the base topic. + + - Subscriber: + This is the default pass-through subscriber for topics of type sensor_msgs/Image. +``` + +Depending on your setup, you may already have "compressed", "theora", or other transports available. +After building the packages in this repository, `image_transport/resized` will also be listed. + +## Adding new transports + +Our nodes are currently communicating raw `sensor_msgs/msg/Image` messages, so we are not gaining anything over using `rclcpp::Publisher` and `rclcpp::Subscriber`. +Let's change that by introducing a new transport. + +The `compressed_image_transport` package provides plugins for the "compressed" transport, which sends images over the wire in either JPEG- or PNG-compressed form. +Notice that `compressed_image_transport` is not a dependency of your package; `image_transport` will automatically discover all transport plugins built in your ROS system. + +The easiest way to add the "compressed" transport is to install the package: + +``` +$ sudo apt-get install ros-rolling-compressed-image-transport +``` + +Or install all the transport plugins at once: + +``` +$ sudo apt-get install ros-rolling-image-transport-plugins +``` + +But you can also build from source. + +## Changing the transport used + +Now let's start up a new subscriber, this one using compressed transport. +The key is that `image_transport` subscribers check the parameter `image_transport` for the name of a transport to use in place of "raw". +Let's set this parameter and start a subscriber node with name "compressed_listener": + +``` +$ ros2 run image_transport_tutorials my_subscriber --ros-args --remap __name:=compressed_listener -p image_transport:=compressed +``` + +You should see an identical image window pop up. + +`compressed_listener` is listening to a separate topic carrying JPEG-compressed versions of the same images published on `/camera/image`. + +## Changing transport-specific behavior + +For a particular transport, we may want to tweak settings such as compression level, bit rate, etc. +Transport plugins can expose such settings through ROS parameters. +For example, `/camera/image/compressed` allows you to change the compression format and quality on the fly; see the package documentation for full details. + +For now let's adjust the JPEG quality. +By default, the "compressed" transport uses JPEG compression at 80% quality. +Let's change it to 15%. +We can use the GUI, `rqt_reconfigure`, to change the quality: + +``` +$ ros2 run rqt_reconfigure rqt_reconfigure +``` + +Now pick `/image_publisher` in the drop-down menu and move the `jpeg_quality` slider down to 15%. +Do you see the compression artifacts in your second view window? + +The `rqt_reconfigure` GUI has updated the ROS parameter `/image_publisher/jpeg_quality`. +You can verify this by running: + +``` +$ ros2 param get /image_publisher jpeg_quality +``` + +This should display 15. diff --git a/doc/installation.md b/doc/installation.md new file mode 100644 index 0000000..10f82a8 --- /dev/null +++ b/doc/installation.md @@ -0,0 +1,20 @@ +# Installation + +Before starting any of the tutorials below, create a workspace and clone this repository so you can inspect and manipulate the code: + +``` +$ mkdir -p ~/image_transport_tutorials_ws/src +$ cd ~/image_transport_tutorials_ws/src +$ git clone https://github.com/ros-perception/image_transport_tutorials.git +``` + +Install needed dependencies: + +``` +$ cd ~/image_transport_tutorials_ws/ +$ source /opt/ros/rolling/setup.bash +$ rosdep install -i --from-path src --rosdistro rolling -y +$ colcon build +``` + +Make sure to include the correct setup file (in the above example it is for rolling on Ubuntu and for bash). diff --git a/doc/python_publisher.md b/doc/python_publisher.md new file mode 100644 index 0000000..6e5603d --- /dev/null +++ b/doc/python_publisher.md @@ -0,0 +1,59 @@ +# Writing a Simple Image Publisher (Python) + +Description: This tutorial shows how to create a publisher node that will continually publish an image with random contents from Python. + +Tutorial Level: Beginner + +Take a look at [my_publisher.py](../image_transport_tutorials_py/image_transport_tutorials_py/my_publisher.py). + +To publish images using `image_transport_py`, you create an `ImageTransport` object and use it to advertise an image topic. +The first parameter for `ImageTransport` is the image transport node's name, which must be unique in the namespace. + +## Steps + +1. Import Necessary Modules: + +```python +from cv_bridge import CvBridge +from image_transport_py import ImageTransport +import numpy as np +import rclpy +from rclpy.node import Node +``` + +2. Initialize the Node and ImageTransport: + +```python +class MyPublisher(Node): + def __init__(self): + super().__init__('my_publisher') + + self.image_transport = ImageTransport( + 'imagetransport_pub', image_transport='compressed' + ) + self.img_pub = self.image_transport.advertise('camera/image', 10) + + self.bridge = CvBridge() + + timer_period = 0.5 + self.timer = self.create_timer(timer_period, self.timer_callback) +``` + +3. Publish Images in the Timer Callback: + +```python + def timer_callback(self): + original = np.uint8(np.random.randint(0, 255, size=(640, 480, 3))) + image_msg = self.bridge.cv2_to_imgmsg(original, encoding='bgr8') + image_msg.header.stamp = self.get_clock().now().to_msg() + image_msg.header.frame_id = 'camera' + + self.img_pub.publish(image_msg) + self.get_logger().info('Publishing image') +``` + +## Running the publisher + +``` +$ ros2 run image_transport_tutorials_py my_publisher +``` diff --git a/doc/python_subscriber.md b/doc/python_subscriber.md new file mode 100644 index 0000000..91ac429 --- /dev/null +++ b/doc/python_subscriber.md @@ -0,0 +1,52 @@ +# Writing a Simple Image Subscriber (Python) + +Description: This tutorial shows how to create a subscriber node that will receive the contents of the published image. +By using the `image_transport` subscriber to subscribe to images, any image transport can be used at runtime. + +Tutorial Level: Beginner + +Take a look at [my_subscriber.py](../image_transport_tutorials_py/image_transport_tutorials_py/my_subscriber.py). + +To subscribe to images, use `ImageTransport` to create a subscription to the image topic. + +## Steps + +1. Import Necessary Modules: + +```python +from image_transport_py import ImageTransport +import rclpy +from rclpy.node import Node +``` + +2. Initialize the Node and ImageTransport: + +```python +class MySubscriber(Node): + def __init__(self): + super().__init__('my_subscriber') + + image_transport = ImageTransport( + 'imagetransport_sub', image_transport='compressed' + ) + image_transport.subscribe('camera/image', 10, self.image_callback) +``` + +3. Handle Incoming Images: + +```python + def image_callback(self, msg): + self.get_logger().info('got a new image from frame_id:=%s' % msg.header.frame_id) +``` + +## Running the subscriber + +``` +$ ros2 run image_transport_tutorials_py my_subscriber +``` + +## Transport Selection + +By default, `image_transport` uses the `raw` transport. +You can specify a different transport by passing the `image_transport` parameter to `ImageTransport`. +Alternatively, you can use your own ROS 2 parameter file for the imagetransport node via the `launch_params_filepath` parameter. diff --git a/image_transport_tutorial_msgs/CMakeLists.txt b/image_transport_tutorial_msgs/CMakeLists.txt new file mode 100644 index 0000000..41affaa --- /dev/null +++ b/image_transport_tutorial_msgs/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.8) +project(image_transport_tutorial_msgs) + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(sensor_msgs REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + "msg/ResizedImage.msg" + DEPENDENCIES sensor_msgs +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/image_transport_tutorials/msg/ResizedImage.msg b/image_transport_tutorial_msgs/msg/ResizedImage.msg similarity index 100% rename from image_transport_tutorials/msg/ResizedImage.msg rename to image_transport_tutorial_msgs/msg/ResizedImage.msg diff --git a/image_transport_tutorial_msgs/package.xml b/image_transport_tutorial_msgs/package.xml new file mode 100644 index 0000000..b68228c --- /dev/null +++ b/image_transport_tutorial_msgs/package.xml @@ -0,0 +1,25 @@ + + + + image_transport_tutorial_msgs + 0.0.0 + Messages for image_transport_tutorials. + Jacob Perron + Apache 2.0 + + ament_cmake + rosidl_default_generators + + sensor_msgs + + rosidl_default_runtime + + ament_lint_auto + ament_lint_common + + rosidl_interface_packages + + + ament_cmake + + diff --git a/image_transport_tutorials/CMakeLists.txt b/image_transport_tutorials/CMakeLists.txt index d30fe95..380ae62 100644 --- a/image_transport_tutorials/CMakeLists.txt +++ b/image_transport_tutorials/CMakeLists.txt @@ -13,26 +13,11 @@ endif() find_package(ament_cmake REQUIRED) find_package(cv_bridge REQUIRED) find_package(image_transport REQUIRED) -find_package(OpenCV REQUIRED COMPONENTS highgui imgcodecs imgproc videoio) -find_package(pluginlib REQUIRED) +find_package(OpenCV REQUIRED COMPONENTS highgui imgcodecs videoio) find_package(rclcpp REQUIRED) -find_package(rosidl_default_generators REQUIRED) find_package(sensor_msgs REQUIRED) find_package(std_msgs REQUIRED) -include_directories(include) - -# add the resized image message -set(msg_files - "msg/ResizedImage.msg" -) -rosidl_generate_interfaces(${PROJECT_NAME} - ${msg_files} - DEPENDENCIES sensor_msgs -) - -rosidl_get_typesupport_target(cpp_typesupport_target "${PROJECT_NAME}" "rosidl_typesupport_cpp") - # add the publisher example add_executable(my_publisher src/my_publisher.cpp) target_link_libraries(my_publisher PRIVATE @@ -44,28 +29,16 @@ target_link_libraries(my_publisher PRIVATE # add the subscriber example add_executable(my_subscriber src/my_subscriber.cpp) -target_link_libraries(my_subscriber +target_link_libraries(my_subscriber PRIVATE cv_bridge::cv_bridge image_transport::image_transport opencv_highgui rclcpp::rclcpp ) -# add the plugin example -add_library(resized_plugins src/manifest.cpp src/resized_publisher.cpp src/resized_subscriber.cpp) -target_link_libraries(resized_plugins - "${cpp_typesupport_target}" - cv_bridge::cv_bridge - image_transport::image_transport - opencv_imgproc - pluginlib::pluginlib - rclcpp::rclcpp - "${sensor_msgs_TARGETS}" -) - # add the publisher from video example add_executable(publisher_from_video src/publisher_from_video.cpp) -target_link_libraries(publisher_from_video +target_link_libraries(publisher_from_video PRIVATE cv_bridge::cv_bridge image_transport::image_transport opencv_highgui @@ -75,24 +48,12 @@ target_link_libraries(publisher_from_video "${std_msgs_TARGETS}" ) -# Install plugin descriptions -pluginlib_export_plugin_description_file(${PROJECT_NAME} resized_plugins.xml) - # Install executables install( - TARGETS my_publisher my_subscriber resized_plugins publisher_from_video + TARGETS my_publisher my_subscriber publisher_from_video RUNTIME DESTINATION lib/${PROJECT_NAME} ) -# Install include directories -install( - DIRECTORY include/ - DESTINATION include/${PROJECT_NAME} -) - -ament_export_include_directories(include) -ament_export_dependencies(cv_bridge image_transport pluginlib rosidl_default_runtime rclcpp sensor_msgs std_msgs) - if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() diff --git a/image_transport_tutorials/package.xml b/image_transport_tutorials/package.xml index c0f5a8c..4f1634d 100644 --- a/image_transport_tutorials/package.xml +++ b/image_transport_tutorials/package.xml @@ -7,28 +7,18 @@ Apache 2.0 ament_cmake_ros - rosidl_default_generators - cv_bridge - image_transport - libopencv-dev - sensor_msgs - std_msgs - - rosidl_default_runtime - cv_bridge - image_transport - libopencv-dev - sensor_msgs - std_msgs + cv_bridge + image_transport + libopencv-dev + rclcpp + sensor_msgs + std_msgs ament_lint_auto ament_lint_common - rosidl_interface_packages - ament_cmake - diff --git a/image_transport_tutorials/resized_plugins.xml b/image_transport_tutorials/resized_plugins.xml deleted file mode 100644 index bd2dff1..0000000 --- a/image_transport_tutorials/resized_plugins.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - This plugin publishes a decimated version of the image. - - - - - - This plugin rescales a decimated image to its original size. - - - diff --git a/resize_image_transport/CMakeLists.txt b/resize_image_transport/CMakeLists.txt new file mode 100644 index 0000000..7c393b0 --- /dev/null +++ b/resize_image_transport/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.8) +project(resize_image_transport) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +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(cv_bridge REQUIRED) +find_package(image_transport REQUIRED) +find_package(image_transport_tutorial_msgs REQUIRED) +find_package(OpenCV REQUIRED COMPONENTS imgproc) +find_package(pluginlib REQUIRED) +find_package(rclcpp REQUIRED) +find_package(sensor_msgs REQUIRED) + +add_library(${PROJECT_NAME} SHARED + src/manifest.cpp + src/resized_publisher.cpp + src/resized_subscriber.cpp +) +target_include_directories(${PROJECT_NAME} PUBLIC + "$" + "$" +) +target_link_libraries(${PROJECT_NAME} + PUBLIC + image_transport::image_transport + image_transport_tutorial_msgs::image_transport_tutorial_msgs + ${sensor_msgs_TARGETS} + PRIVATE + cv_bridge::cv_bridge + opencv_imgproc + pluginlib::pluginlib + rclcpp::rclcpp +) + +pluginlib_export_plugin_description_file(image_transport resized_plugins.xml) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install( + DIRECTORY include/ + DESTINATION include/${PROJECT_NAME} +) + +ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET) +ament_export_dependencies( + image_transport + image_transport_tutorial_msgs + sensor_msgs +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/image_transport_tutorials/include/image_transport_tutorials/resized_publisher.hpp b/resize_image_transport/include/resize_image_transport/resized_publisher.hpp similarity index 68% rename from image_transport_tutorials/include/image_transport_tutorials/resized_publisher.hpp rename to resize_image_transport/include/resize_image_transport/resized_publisher.hpp index bdeba56..2365499 100644 --- a/image_transport_tutorials/include/image_transport_tutorials/resized_publisher.hpp +++ b/resize_image_transport/include/resize_image_transport/resized_publisher.hpp @@ -12,30 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef IMAGE_TRANSPORT_TUTORIALS__RESIZED_PUBLISHER_HPP_ -#define IMAGE_TRANSPORT_TUTORIALS__RESIZED_PUBLISHER_HPP_ +#ifndef RESIZE_IMAGE_TRANSPORT__RESIZED_PUBLISHER_HPP_ +#define RESIZE_IMAGE_TRANSPORT__RESIZED_PUBLISHER_HPP_ #include #include "sensor_msgs/msg/image.hpp" #include "image_transport/simple_publisher_plugin.hpp" -#include "image_transport_tutorials/msg/resized_image.hpp" +#include "image_transport_tutorial_msgs/msg/resized_image.hpp" +namespace resize_image_transport +{ class ResizedPublisher : public image_transport::SimplePublisherPlugin - + { -public: - virtual std::string getTransportName() const - { - return "resized"; - } - protected: virtual void publish( const sensor_msgs::msg::Image & message, - const PublishFn & publish_fn) const; + const PublisherT & publisher) const; }; - -#endif // IMAGE_TRANSPORT_TUTORIALS__RESIZED_PUBLISHER_HPP_ +} // namespace resize_image_transport +#endif // RESIZE_IMAGE_TRANSPORT__RESIZED_PUBLISHER_HPP_ diff --git a/image_transport_tutorials/include/image_transport_tutorials/resized_subscriber.hpp b/resize_image_transport/include/resize_image_transport/resized_subscriber.hpp similarity index 66% rename from image_transport_tutorials/include/image_transport_tutorials/resized_subscriber.hpp rename to resize_image_transport/include/resize_image_transport/resized_subscriber.hpp index a5ecdf5..49205f3 100644 --- a/image_transport_tutorials/include/image_transport_tutorials/resized_subscriber.hpp +++ b/resize_image_transport/include/resize_image_transport/resized_subscriber.hpp @@ -12,29 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef IMAGE_TRANSPORT_TUTORIALS__RESIZED_SUBSCRIBER_HPP_ -#define IMAGE_TRANSPORT_TUTORIALS__RESIZED_SUBSCRIBER_HPP_ +#ifndef RESIZE_IMAGE_TRANSPORT__RESIZED_SUBSCRIBER_HPP_ +#define RESIZE_IMAGE_TRANSPORT__RESIZED_SUBSCRIBER_HPP_ #include #include "image_transport/simple_subscriber_plugin.hpp" -#include "image_transport_tutorials/msg/resized_image.hpp" +#include "image_transport_tutorial_msgs/msg/resized_image.hpp" +namespace resize_image_transport +{ class ResizedSubscriber : public image_transport::SimpleSubscriberPlugin - + { public: virtual ~ResizedSubscriber() {} - virtual std::string getTransportName() const - { - return "resized"; - } - protected: virtual void internalCallback( - const typename image_transport_tutorials::msg::ResizedImage::ConstSharedPtr & message, + const typename image_transport_tutorial_msgs::msg::ResizedImage::ConstSharedPtr & message, const Callback & user_cb); }; +} // namespace resize_image_transport -#endif // IMAGE_TRANSPORT_TUTORIALS__RESIZED_SUBSCRIBER_HPP_ +#endif // RESIZE_IMAGE_TRANSPORT__RESIZED_SUBSCRIBER_HPP_ diff --git a/resize_image_transport/package.xml b/resize_image_transport/package.xml new file mode 100644 index 0000000..1ea64ac --- /dev/null +++ b/resize_image_transport/package.xml @@ -0,0 +1,27 @@ + + + + resize_image_transport + 0.0.0 + Image transport plugin that publishes/subscribes to resized images. + Jacob Perron + Apache 2.0 + + ament_cmake + + cv_bridge + image_transport + image_transport_tutorial_msgs + libopencv-dev + pluginlib + rclcpp + sensor_msgs + + ament_lint_auto + ament_lint_common + + + ament_cmake + + + diff --git a/resize_image_transport/resized_plugins.xml b/resize_image_transport/resized_plugins.xml new file mode 100644 index 0000000..4ab87d4 --- /dev/null +++ b/resize_image_transport/resized_plugins.xml @@ -0,0 +1,21 @@ + + resized + image_transport_tutorial_msgs/msg/ResizedImage + + + This plugin publishes a decimated version of the image. + + + + + + This plugin rescales a decimated image to its original size. + + + diff --git a/image_transport_tutorials/src/manifest.cpp b/resize_image_transport/src/manifest.cpp similarity index 63% rename from image_transport_tutorials/src/manifest.cpp rename to resize_image_transport/src/manifest.cpp index 634b604..3ccc0d9 100644 --- a/image_transport_tutorials/src/manifest.cpp +++ b/resize_image_transport/src/manifest.cpp @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "pluginlib/class_list_macros.hpp" +#include +#include "resize_image_transport/resized_publisher.hpp" +#include "resize_image_transport/resized_subscriber.hpp" -#include "image_transport_tutorials/resized_publisher.hpp" -#include "image_transport_tutorials/resized_subscriber.hpp" - -PLUGINLIB_EXPORT_CLASS(ResizedPublisher, image_transport::PublisherPlugin) -PLUGINLIB_EXPORT_CLASS(ResizedSubscriber, image_transport::SubscriberPlugin) +PLUGINLIB_EXPORT_CLASS(resize_image_transport::ResizedPublisher, image_transport::PublisherPlugin) +PLUGINLIB_EXPORT_CLASS(resize_image_transport::ResizedSubscriber, image_transport::SubscriberPlugin) diff --git a/image_transport_tutorials/src/resized_publisher.cpp b/resize_image_transport/src/resized_publisher.cpp similarity index 86% rename from image_transport_tutorials/src/resized_publisher.cpp rename to resize_image_transport/src/resized_publisher.cpp index beba405..154a47c 100644 --- a/image_transport_tutorials/src/resized_publisher.cpp +++ b/resize_image_transport/src/resized_publisher.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "image_transport_tutorials/resized_publisher.hpp" +#include "resize_image_transport/resized_publisher.hpp" #include @@ -21,9 +21,11 @@ #include "opencv2/imgproc.hpp" #include "rclcpp/logging.hpp" +namespace resize_image_transport +{ void ResizedPublisher::publish( const sensor_msgs::msg::Image & message, - const PublishFn & publish_fn) const + const PublisherT & publisher) const { cv::Mat cv_image; std::shared_ptr tracked_object; @@ -45,9 +47,10 @@ void ResizedPublisher::publish( cv::resize(cv_image, buffer, cv::Size(new_width, new_height)); // Set up ResizedImage and publish - image_transport_tutorials::msg::ResizedImage resized_image; + image_transport_tutorial_msgs::msg::ResizedImage resized_image; resized_image.original_height = cv_image.rows; resized_image.original_width = cv_image.cols; resized_image.image = *(cv_bridge::CvImage(message.header, "bgr8", cv_image).toImageMsg()); - publish_fn(resized_image); + publisher->publish(resized_image); } +} // namespace resize_image_transport diff --git a/image_transport_tutorials/src/resized_subscriber.cpp b/resize_image_transport/src/resized_subscriber.cpp similarity index 86% rename from image_transport_tutorials/src/resized_subscriber.cpp rename to resize_image_transport/src/resized_subscriber.cpp index e7c711e..79a79e1 100644 --- a/image_transport_tutorials/src/resized_subscriber.cpp +++ b/resize_image_transport/src/resized_subscriber.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "image_transport_tutorials/resized_subscriber.hpp" +#include "resize_image_transport/resized_subscriber.hpp" #include @@ -20,8 +20,10 @@ #include "opencv2/core/mat.hpp" #include "opencv2/imgproc.hpp" +namespace resize_image_transport +{ void ResizedSubscriber::internalCallback( - const image_transport_tutorials::msg::ResizedImage::ConstSharedPtr & msg, + const image_transport_tutorial_msgs::msg::ResizedImage::ConstSharedPtr & msg, const Callback & user_cb) { // This is only for optimization, not to copy the image @@ -35,3 +37,4 @@ void ResizedSubscriber::internalCallback( cv_bridge::CvImage cv_img(msg->image.header, msg->image.encoding, img_restored); user_cb(cv_img.toImageMsg()); } +} // namespace resize_image_transport