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