Converting between ROS images and OpenCV images

写在前面的话

这篇博客是http://wiki.ros.org/cv_bridge/Tutorials/UsingCvBridgeToConvertBetweenROSImagesAndOpenCVImages的翻译

摘要

本教程介绍如何使用cv_bridge在ROS和OpenCV之间转换图像的格式。 包括一个示例节点,可以用作您自己的节点的模板。

1. 概念

ROS以自己的图像消息格式(sensor_msgs/Image)传递图像,但是许多用户将希望将图像与OpenCV结合使用。 CvBridge是一个ROS库,提供了ROS和OpenCV之间的接口。 CvBridge可以在visual_opencv堆栈中的cv_bridge包中找到。
在这个教程中,你讲学习如何写一个node使用CvBridge讲ros图像转换为Opencv图像格式(cv::Mat)
您还将学习如何将OpenCV图像转换为ROS格式以通过ROS进行发布。
Converting between ROS images and OpenCV images_第1张图片

2. 转化ros图像消息为opencv图像

CvBridge定义了一个CvImage类型,它包含了一个opencv图像类型,编码和ROS header。CvImage还包含了sensor_msgs/Image的信息,我们可以将他转化为其他格式。
CvImage类的定义:

namespace cv_bridge {

class CvImage
{
public:
  std_msgs::Header header;
  std::string encoding;
  cv::Mat image;
};

typedef boost::shared_ptr CvImagePtr;
typedef boost::shared_ptr const> CvImageConstPtr;

}

CvBridge提供了如下方法将ROS图像消息转化为Opencv格式:

// Case 1: Always copy, returning a mutable CvImage
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr& source,
                    const std::string& encoding = std::string());
CvImagePtr toCvCopy(const sensor_msgs::Image& source,
                    const std::string& encoding = std::string());

// Case 2: Share if possible, returning a const CvImage
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source,
                          const std::string& encoding = std::string());
CvImageConstPtr toCvShare(const sensor_msgs::Image& source,
                          const boost::shared_ptr<void const>& tracked_object,
                          const std::string& encoding = std::string());

这些方法的输入是图像消息的指针,以及一个可选的编码参数,编码参数与CvImage的目标有关。
1. 如果源和目标编码匹配,toCvCopy创建了一份ROS消息的拷贝,你可以任意修改返回的CvImage
2. 如果源和目标编码匹配,toCvShare将将返回的cv :: Mat指向ROS消息数据,避免复制。 只要持有返回的CvImage的副本,ROS消息数据就不会被释放。 如果编码不匹配,它将分配一个新的缓冲区并执行转换。 您不能修改返回的CvImage,因为它可能与ROS映像消息共享数据,而ROS映像消息又可能与其他回调共享。

注意:当您具有要转换的sensor_msgs / Image的其他消息类型(例如stereo_msgs / DisparityImage)指针的指针时,toCvShare的第二个重载更为方便。

如果不指定编码参数,目标图像的编码方式将和ROS消息的编码方式一样。在这种情况下,toCvShare保证不复制图像数据。 图像编码可以是以下任意OpenCV图像编码:

  • 8UC[1-4]
  • 8SC[1-4]
  • 16UC[1-4]
  • 16SC[1-4]
  • 32SC[1-4]
  • 32FC[1-4]
  • 64FC[1-4]

对于流行的图像编码,CvBridge将根据需要选择进行颜色或像素深度转换。 要使用此功能,请将编码指定为以下字符串之一:

  • mono8: CV_8UC1, grayscale image
  • mono16: CV_16UC1, 16-bit grayscale image
  • bgr8: CV_8UC3, color image with blue-green-red color order
  • rgb8: CV_8UC3, color image with red-green-blue color order
  • bgra8: CV_8UC4, BGR color image with an alpha channel
  • rgba8: CV_8UC4, RGB color image with an alpha channel

注意:mono8和bgr8是大多数OpenCV功能预期的两个图像编码。

最后,CvBridge将识别Bayer模式编码为OpenCV类型8UC1(8位无符号,一个通道)。 它不会执行到Bayer模式或从Bayer模式转换; 在典型的ROS系统中,这通过image_proc来代替。 CvBridge识别以下Bayer编码:

  • bayer_rggb8
  • bayer_bggr8
  • bayer_gbrg8
  • bayer_grbg8

3. 转换opencv图像为ROS图像消息

为了将CvImage转换为ROS图像消息,可以使用CvImage::toImageMsg()函数:

class CvImage
{
  sensor_msgs::ImagePtr toImageMsg() const;

  // Overload mainly intended for aggregate messages that contain
  // a sensor_msgs::Image as a member.
  void toImageMsg(sensor_msgs::Image& ros_image) const;
};

如果CvImage是您自己创建的,请不要忘记填写标题和编码字段。
关于CvImage创建的例子,请参考教程

4. 一个ros node的例子

这是一个监听ROS映像消息topic的节点,将图像转换为cv :: Mat,在其上绘制一个圆圈,并使用OpenCV显示图像。 然后,图像通过ROS重新发布。
在您的package.xml和CMakeLists.xml中(或使用catkin_create_pkg时),添加以下依赖项:

sensor_msgs
cv_bridge
roscpp
std_msgs
image_transport

在src文件夹下创建image_converter.cpp:

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

static const std::string OPENCV_WINDOW = "Image window";

class ImageConverter
{
  ros::NodeHandle nh_;
  image_transport::ImageTransport it_;
  image_transport::Subscriber image_sub_;
  image_transport::Publisher image_pub_;

public:
  ImageConverter()
    : it_(nh_)
  {
    // Subscrive to input video feed and publish output video feed
    image_sub_ = it_.subscribe("/camera/image_raw", 1,
      &ImageConverter::imageCb, this);
    image_pub_ = it_.advertise("/image_converter/output_video", 1);

    cv::namedWindow(OPENCV_WINDOW);
  }

  ~ImageConverter()
  {
    cv::destroyWindow(OPENCV_WINDOW);
  }

  void imageCb(const sensor_msgs::ImageConstPtr& msg)
  {
    cv_bridge::CvImagePtr cv_ptr;
    try
    {
      cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

    // Draw an example circle on the video stream
    if (cv_ptr->image.rows > 60 && cv_ptr->image.cols > 60)
      cv::circle(cv_ptr->image, cv::Point(50, 50), 10, CV_RGB(255,0,0));

    // Update GUI Window
    cv::imshow(OPENCV_WINDOW, cv_ptr->image);
    cv::waitKey(3);

    // Output modified video stream
    image_pub_.publish(cv_ptr->toImageMsg());
  }
};

int main(int argc, char** argv)
{
  ros::init(argc, argv, "image_converter");
  ImageConverter ic;
  ros::spin();
  return 0;
}
  • 错误: No code_block found Using image_transport for publishing and subscribing to images in ROS allows you to subscribe to compressed image streams。 记得在你的package.xml中加入image_transport。
  • 错误: No code_block found Includes the header for CvBridge as well as some useful constants and functions related to image encodings. 记得在你的package.xml中加入cv_bridge。
  • 错误: No code_block found Includes the headers for OpenCV’s image processing and GUI modules. 记得在你的package.xml中加入opencv2。

注意: opencv一般用的通道顺序为BGR, 您应该始终将您的调用包含到CvCopy()/ toCvShared()以捕获转换错误,因为这些函数将不会检查数据的有效性。

要运行节点,您将需要一个图像流。 运行相机或播放bag文件以生成图像流。 现在您可以运行此节点,将“in”重新映射到实际的图像流主题。

如果您已成功将图像转换为OpenCV格式,您将看到一个名为“Image window”的HighGui窗口,并显示您的图像+圈。

您可以使用rostopic或通过使用image_view查看图像来查看您的节点是否正确地在ROS上发布图像。

5. 共享图像数据的示例

在上面的完整示例中,我们复制了图像数据,但共享(如果可能)同样容易:

namespace enc = sensor_msgs::image_encodings;

void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
  cv_bridge::CvImageConstPtr cv_ptr;
  try
  {
    cv_ptr = cv_bridge::toCvShare(msg, enc::BGR8);
  }
  catch (cv_bridge::Exception& e)
  {
    ROS_ERROR("cv_bridge exception: %s", e.what());
    return;
  }

  // Process cv_ptr->image using OpenCV
}

如果传入的消息具有“bgr8”编码,则cv_ptr将不会复制它的数据。 如果它具有不同但可转换的编码,例如“mono8”,CvBridge将为cv_ptr分配一个新的缓冲区并执行转换。 没有异常处理,这只会是一行代码,但是后来有一个格式不正确(或不支持的)编码的传入消息会导致节点掉线。 例如,如果传入的图像来自Bayer模式相机的image_raw主题,CvBridge将抛出异常,因为它(有意)不自动支持Bayer-to-color的转换。

一个稍微复杂的例子:

namespace enc = sensor_msgs::image_encodings;

void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
  cv_bridge::CvImageConstPtr cv_ptr;
  try
  {
    if (enc::isColor(msg->encoding))
      cv_ptr = cv_bridge::toCvShare(msg, enc::BGR8);
    else
      cv_ptr = cv_bridge::toCvShare(msg, enc::MONO8);
  }
  catch (cv_bridge::Exception& e)
  {
    ROS_ERROR("cv_bridge exception: %s", e.what());
    return;
  }

  // Process cv_ptr->image using OpenCV
}

在这种情况下,我们可以使用彩色图像格式(如果有的话),否则会回到单色。 如果传入的图像是“bgr8”或“mono8”,我们会避免复制数据。

你可能感兴趣的:(ROS)