From 9485f5f3ac80b351dade396f5d89b65a76d22c70 Mon Sep 17 00:00:00 2001 From: Michael Ferguson Date: Thu, 8 Feb 2024 16:18:33 -0500 Subject: [PATCH] migrate image_view docs (#934) * migrate docs from ROS wiki * FIX: video recorder start/end services should only be advertised when feature is enabled * FIX: image remapping didn't work as expected/documented in stereo_image_proc --- image_view/doc/changes.rst | 8 ++ image_view/doc/components.rst | 161 +++++++++++++++++++++++ image_view/doc/conf.py | 193 ++++++++++++++++++++++++++++ image_view/doc/index.rst | 69 ++++++++++ image_view/src/image_saver_node.cpp | 24 ++-- image_view/src/stereo_view_node.cpp | 5 +- 6 files changed, 448 insertions(+), 12 deletions(-) create mode 100644 image_view/doc/changes.rst create mode 100644 image_view/doc/components.rst create mode 100644 image_view/doc/conf.py create mode 100644 image_view/doc/index.rst diff --git a/image_view/doc/changes.rst b/image_view/doc/changes.rst new file mode 100644 index 000000000..3f14b6aaf --- /dev/null +++ b/image_view/doc/changes.rst @@ -0,0 +1,8 @@ +Changelog Notes +=============== + +Jazzy Jalisco +------------- +There are several major change between ``Iron`` and ``Jazzy``: + + * All components now properly support ``image_transport`` parameter. diff --git a/image_view/doc/components.rst b/image_view/doc/components.rst new file mode 100644 index 000000000..ef7f26e1e --- /dev/null +++ b/image_view/doc/components.rst @@ -0,0 +1,161 @@ +Nodes and Components +==================== + +image_view::DisparityViewNode +----------------------------- +Simple image viewer for ROS stereo_msgs/DisparityImage topics. +Color-maps the disparity image for visualization. +Node name is ``disparity_view``. + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **image** (sensor_msgs/DisparityImage): The disparity image topic. + +Parameters +^^^^^^^^^^ + * **autosize** (bool, default: false): Whether the window should autosize + itself to the image or be resizeable by the user. + * **window_name** (string, default: name of the image topic): + The name of the display window. + +image_view::ExtractImagesNode +----------------------------- +This tool also allows you to save images as jpg/png file from +streaming (ROS sensor_msgs/Image topic) to a file. +``image_saver`` node provide very similar functionalities, +such as providing service call to trigger the node to save +images, save images other than JPEG format, etc. + +This tool allows you to save images as jpg/png file from streaming +(ROS sensor_msgs/Image topic) to a file. From command line, you +can run with: + +.. code-block:: bash + + ros2 run image_view image_saver --ros-args -r image:=[your topic] + +or see this answer to control the timing of capture. + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **image** (sensor_msgs/Image): Image topic to visualize. + +Parameters +---------- + * **filename_format** (string, default: "frame%04i.jpg"): File name for + saved images, you must add use '%04i' for sequence number. + * **image_transport** (string, default: raw): Image transport to use. + * **sec_per_frame** (double, default: 0.1): Seconds between saved frames. + +image_view::ImageViewNode +------------------------- +Simple image viewer for ROS sensor_msgs/Image topics. Node name +is ``image_view``. + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **image** (sensor_msgs/Image): Image topic to visualize. + +Parameters +^^^^^^^^^^ + * **autosize** (bool, default: false): Whether the window should autosize + itself to the image or be resizeable by the user. + * **filename_format** (string, default: "frame%04i.jpg"): printf-style + format for saved image names. Use to control name, location and format + of saved images. + * **image_transport** (string, default: raw): Image transport to use. + * **window_name** (string, default: name of the image topic): + The name of the display window. + +image_view::ImageSaverNode +-------------------------- +This tool allows you to save images as jpg/png file from streaming +(ROS sensor_msgs/Image topic) to a file. From command line, you +can run with: + +.. code-block:: bash + + ros2 run image_view image_saver --ros-args -r image:=[your topic] + +or see this answer to control the timing of capture. + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **image** (sensor_msgs/Image): Image topic to visualize. + +Services +^^^^^^^^ + * **save** (std_srvs/Empty): Save images, you need to set + the ``save_all_images`` parameter to false. + * **start** (std_srvs/Trigger): Start saving images. + * **end** (std_srvs/Trigger): Stop saving images. + +Parameters +---------- + * **encoding** (string, default:"bgr8"): Encoding type of input image topic. + * **filename_format** (string, default: "left%04i.jpg"): File name for + saved images, you can use '%04i' for sequence number, and '%s' for default + file format, you can use 'jpg' ,'png', 'pgm' as filename suffixes. + * **image_transport** (string, default: raw): Image transport to use. + * **save_all_images** (bool, default: true): If set to false, images + are only saved when 'save' service is called. + * **stamped_filename** (bool, default: false): If set to true, a timestamp + is appended to each filename. + * **request_start_end** (bool, default: false): If set to true, the start + and end services will be advertised and can be used to start and Stop + saving images. NOTE: ``save_all_images`` must be set to true, or these + services won't do anything. + +image_view::StereoImageViewNode +------------------------------- +Viewer for stereo images. Shows the synchronized left/right image pair +and the disparity image (color-mapped) computed from them. +Node name is ``stereo_image_view``. + +It is expected that ```` and ```` will be remapped to the +appropriate names (as show in :ref:`Viewing Stereo Images`). + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **/left/** (sensor_msgs/Image): The left image topic. + * **/right/** (sensor_msgs/Image): The right image topic. + * **/disparity** (stereo_msgs/DisparityImage): The disparity image + computed from the left/right stereo pair. + +Parameters +^^^^^^^^^^ + * **approximate_sync** (bool, default: false): Whether to use approximate + synchronization. Set to true if the left and right cameras do not + produce exactly synced timestamps. + * **autosize** (bool, default: false): Whether the window should autosize + itself to the image or be resizeable by the user. + * **filename_format** (string, default: "%s%04i.jpg"): printf-style + format for saved image names. Use to control name, location and format + of saved images. The string argument is "left" or "right". + * **image_transport** (string, default: raw): Image transport to use. + * **queue_size** (int, default: 5): Size of message queue for each + synchronized topic. You may need to raise this if disparity processing + takes too long, or if there are significant network delays. + +image_view::VideoRecorderNode +----------------------------- +This tool allows you to record a video stream (ROS sensor_msgs/Image topic) +to a file. It relies on OpenCV's VideoWriter class. With the default options, +it encodes the video as MPG, encapsulated in a AVI container at 15 fps, +and produces a file called output.avi in the current directory. + +Subscribed Topics +^^^^^^^^^^^^^^^^^ + * **image** (sensor_msgs/Image): Image topic to save to file. + +Parameters +^^^^^^^^^^ + * **codec** (string, default: MJPG): The FOURCC identifier of the codec. + * **encoding** (string, default:"bgr8"): Encoding type of input image topic. + * **filename** (string, default: output.avi): Path and name of the + output video. + * **fps** (int, default: 15): Framerate of the video. + * **image_transport** (string, default: raw): Image transport to use. + * **queue_size** (int, default: 5): Size of message queue for each + synchronized topic. You may need to raise this if disparity processing + takes too long, or if there are significant network delays. diff --git a/image_view/doc/conf.py b/image_view/doc/conf.py new file mode 100644 index 000000000..065619e02 --- /dev/null +++ b/image_view/doc/conf.py @@ -0,0 +1,193 @@ +# Copyright 2024 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +import os +import sys +sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'image_view' +copyright = '2008-2024, Open Source Robotics Foundation, Inc.' # noqa +author = 'Open Source Robotics Foundation, Inc.' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '3.2.1' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx_rtd_theme', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'image_view_doc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'image_view', 'image_view Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'image_view', 'image_view Documentation', + author, 'image_view', 'ROS 2 components for visualising images.', + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- + +autoclass_content = 'both' + +autodoc_default_options = { + 'members': True, # document members + 'undoc-members': True, # also document members without documentation +} diff --git a/image_view/doc/index.rst b/image_view/doc/index.rst new file mode 100644 index 000000000..9805e4378 --- /dev/null +++ b/image_view/doc/index.rst @@ -0,0 +1,69 @@ +Overview +======== + +This package contains a number of ROS 2 components and nodes for viewing image topics. + +Viewing a Single Image Topic +---------------------------- + +.. code-block:: bash + + ros 2 run image_view image_view --ros-args -r image:= + +For example, to view raw images on the topic ``/camera/image``, use: + +.. code-block:: bash + + ros2 run image_view image_view --ros-args -r image:=/camera/image + +You may save the current image by right-clicking on the display window. By default, +images will be saved as ``frame0000.jpg``, ``frame0001.jpg``, .... + +If you want to view a compressed image stream (usually a good idea over wireless!) +using the capabilities of ``image_transport``, specify the transport type as a +command-line parameter. For example, if ``theora_image_transport`` is built on the +publisher's side, you can use theora transport: + +.. code-block:: bash + + ros2 run image_view image_view --ros-args -r image:=/camera/image -p image_transport:=theora + +.. _`Viewing Stereo Images`: + +Viewing Stereo Images +--------------------- + +.. code-block:: bash + + ros2 run image_view stereo_view --ros-args -p stereo:= -p image:= + +For example, to view stereo image pairs on topics +``/my_stereo_cam/left/image_rect_color`` and ``/my_stereo_cam/right/image_rect_color``, +use: + +.. code-block:: bash + + ros2 run image_view stereo_view --ros-args -r stereo:=/my_stereo_cam -r image:=image_rect_color + +``stereo_view`` also shows the disparity image computed from the stereo pair + color-mapped for clarity. + +You may save the current image pair by right-clicking on any display window. +By default, images will be saved as ``left0000.jpg``, ``right0000.jpg``, ``disp0000.jpg``, +``left0001.jpg``, ``right0001.jpg``, ``disp0001.jpg``.... As with ``image_view``, +you can specify an image transport to use for the left and right image as an optional +parameter. + +.. toctree:: + :maxdepth: 2 + + self + components + changes + image_view + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/image_view/src/image_saver_node.cpp b/image_view/src/image_saver_node.cpp index 4982732d0..075d84853 100644 --- a/image_view/src/image_saver_node.cpp +++ b/image_view/src/image_saver_node.cpp @@ -104,16 +104,20 @@ ImageSaverNode::ImageSaverNode(const rclcpp::NodeOptions & options) std::bind( &ImageSaverNode::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - start_srv_ = this->create_service( - "start", - std::bind( - &ImageSaverNode::callbackStartSave, this, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)); - end_srv_ = this->create_service( - "end", - std::bind( - &ImageSaverNode::callbackEndSave, this, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)); + + // Advertise start/end services if the feature is enabled + if (request_start_end_) { + start_srv_ = this->create_service( + "start", + std::bind( + &ImageSaverNode::callbackStartSave, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + end_srv_ = this->create_service( + "end", + std::bind( + &ImageSaverNode::callbackEndSave, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + } } bool ImageSaverNode::saveImage( diff --git a/image_view/src/stereo_view_node.cpp b/image_view/src/stereo_view_node.cpp index 5b876d859..5113da7f2 100644 --- a/image_view/src/stereo_view_node.cpp +++ b/image_view/src/stereo_view_node.cpp @@ -114,14 +114,15 @@ StereoViewNode::StereoViewNode(const rclcpp::NodeOptions & options) // fully expanded and remapped topic name to image_transport auto node_base = this->get_node_base_interface(); std::string stereo_ns = node_base->resolve_topic_or_service_name("stereo", false); + std::string image = node_base->resolve_topic_or_service_name("image", false); std::string left_topic = rclcpp::expand_topic_or_service_name( stereo_ns + "/left" + rclcpp::expand_topic_or_service_name( - "image", this->get_name(), this->get_namespace()), + image, this->get_name(), this->get_namespace()), this->get_name(), this->get_namespace()); std::string right_topic = rclcpp::expand_topic_or_service_name( stereo_ns + "/right" + rclcpp::expand_topic_or_service_name( - "image", this->get_name(), this->get_namespace()), + image, this->get_name(), this->get_namespace()), this->get_name(), this->get_namespace()); std::string disparity_topic = rclcpp::expand_topic_or_service_name( stereo_ns + "/disparity", this->get_name(), this->get_namespace());