ros图像传递总结

转载于:https://www.jianshu.com/p/40d44fe290da

本文主要是总结7.15日到7.21日期间对ROS图像数据传递的学习,美其名曰总结,说白了就是搬砖。网页链接如下:

上述四个链接均是官网关于ROS图像传递的相关功能包和教程,最后一个链接还没有用到,所以没有详细看,只是记录在案。

1. Converting between ROS images and OpenCV images (C++)

该链接教程阐述了如何实现ROS图像和OpenCV之间的图像转换。ROS以sensor_msgs/image消息格式传递图像信息,而ROS库cvBridge为ROS和OpenCV的交互提供了接口。


image
1.1. ROS images => OpenCV images

CvBridge定义一个包含OpenCV图像、编码和ROS头的CvImage类。类定义如下:

namespace cv_bridge {
    class CvImage
    {
        public:
          std_msgs::Header header;
          std::string encoding;
          cv::Mat image;
    };
<span class="token keyword">typedef</span> boost<span class="token operator">::</span>shared_ptr<span class="token operator">&lt;</span>CvImage<span class="token operator">&gt;</span> CvImagePtr<span class="token punctuation">;</span>
<span class="token keyword">typedef</span> boost<span class="token operator">::</span>shared_ptr<span class="token operator">&lt;</span>CvImage <span class="token keyword">const</span><span class="token operator">&gt;</span> CvImageConstPtr<span class="token punctuation">;</span>

}

将ROS sensor_msgs/Image消息转换为CvImage时,CvBridge会采用两种不用的方式:

1. 如果需要修改本地数据,则必须制作一个ROS消息数据的副本
2. 如果无需修改本地数据,则可以直接共享ROS消息数据

CvBridge提供了以下函数实现CvImage转换:

// 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。即使源数据格式和目标编码匹配,toCvCopy函数也会对ROS消息的图像数据进行拷贝,用户可以自由修改返回的CvImage数据;如果原图像数据格式和目标图像数据格式匹配,toCvShare函数将返回指向ROS消息数据中cv::Mat,只要您持有返回的CvImage数据的副本,就不会释放ROS消息数据。如果编码不匹配,它将分配一个新缓冲区并执行转换。返回的CvImage无法被修改,因为它可能与ROS图像消息共享数据,而ROS图像消息坑你与其他回调函数共享。

PS:常见使用案例如下:
//方法1:
cv_bridge::CvImagePtr cv_ptr;
cv_ptr = cv_bridge::toCvCopy(msg, sensors_msgs::image_encodings::BGR8);
cv::imshow("view", cv_ptr->image);

//方法2:
cv_bridge::CvImagePtr cv_ptr;
cv_ptr = cv_bridge::toCvShare(msg, sensors_msgs::image_encodings::BGR8);
cv::imshow(“view”, cv_ptr->image);

PS:当指向包含要转换的sensor_msgs/Image的其他消息类型(如stereo_msgs/DisparityImage)时,toCvShare的第二重载方式更加方便

如果没有给出编码,则目标图像编码将与图像消息编码相同。在这种情况下,toCvShare()保证不复制图像数据。图像编码可以是以下OpenCV图像编码中的任何一种:

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

对于常见的图像编码,CvBridge会进行必要的颜色和像素深度变换,为了使用该功能,指定编码为下列字符串:

字符串编码颜色格式
mono8CV_8UC1grayscale image
mono16CV_16UC116-bit grayscale image
bgr8CV_8UC3color image with blue-green-red color order
rgb8CV_8UC3color image with red-green-blue color order
bgra8CV_8UC4BGR color image with an alpha channel
rgba8CV_8UC4RGB color image with an alpha channel
PS:mono8和bgr8是OpenCV大多数函数使用图像编码格式

最后,CvBridge会将Bayer模式编码作为8UC1编码格式,不会对Bayer格式进行互相转换;在典型的ROS系统中,这一步通过image_proc来实现。CvBridge识别以下Bayer编码:

  • bayer_rggb8
  • bayer_bggr8
  • bayer_gbrg8
  • bayer_grbg8
1.2. OpenCV images => ROS images

将CvImage图像转变为ROS图像,使用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,需要填写对应的头文件和编码域。

PS:常见使用案例如下:
sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", cv::Mat srcImage).toImageMsg();
1.3. ROS图像节点实战

该节点监听ROS图像信息话题,将图像转换为cv::Mat,并在其上画圆,用OpenCV显示,然后重新借助ROS发布。

  • 节点配置
    需要在节点中添加如下依赖项:
sensor_msgs
cv_bridge
roscpp
std_msgs
image_transport
  • 在SRC文件夹下创建 image_convert.cpp
#include <ros/ros.h>
#include <image_transport/image_transport.h>        
//image_transport.h:在ROS中使用image_transport发布和订阅图像可以
//图像进行压缩
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
//上述两个头文件,包含Cv::Bridge头以及一些有用的常量和图像编码相关的函数
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
//上述头文件,包含进行图像处理和图像用户界面的模块,一定要将opencv2包含到package.xml文件中
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);

<span class="token comment">//使用image_tranport订阅图像话题和发布图像话题</span>

cv<span class="token operator">::</span><span class="token function">namedWindow</span><span class="token punctuation">(</span>OPENCV_WINDOW<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

~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;
}

<span class="token comment">// Draw an example circle on the video stream</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>cv_ptr<span class="token operator">-&gt;</span>image<span class="token punctuation">.</span>rows <span class="token operator">&gt;</span> <span class="token number">60</span> <span class="token operator">&amp;&amp;</span> cv_ptr<span class="token operator">-&gt;</span>image<span class="token punctuation">.</span>cols <span class="token operator">&gt;</span> <span class="token number">60</span><span class="token punctuation">)</span>
  cv<span class="token operator">::</span><span class="token function">circle</span><span class="token punctuation">(</span>cv_ptr<span class="token operator">-&gt;</span>image<span class="token punctuation">,</span> cv<span class="token operator">::</span><span class="token function">Point</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token function">CV_RGB</span><span class="token punctuation">(</span><span class="token number">255</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Update GUI Window</span>
cv<span class="token operator">::</span><span class="token function">imshow</span><span class="token punctuation">(</span>OPENCV_WINDOW<span class="token punctuation">,</span> cv_ptr<span class="token operator">-&gt;</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span>
cv<span class="token operator">::</span><span class="token function">waitKey</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Output modified video stream</span>
image_pub_<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span>cv_ptr<span class="token operator">-&gt;</span><span class="token function">toImageMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}
//在订阅到图像信息后,进行函数回调。首先将ROS图像信息转换到适合OpenCV调用的CvImage,
//由于需要在图像上画圆,所以需要进行图像数据的副本拷贝,使用toCvCopy()。注意OpenCV
//要求彩色图像使用BGR信道规则。
};

int main(int argc, char argv)
{
ros::init(argc, argv, “image_converter”);
ImageConverter ic;
ros::spin();
return 0;
}

  • 在上例中使用数据共享实例
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将不会对数据进行复制。如果消息的编码格式不同,但可以进行转换编码,CvBridge将会为cv_ptr分配一个新的缓冲区并执行转换。如果没有异常处理,则只需要一行代码就够了;但如果是一个不支持的传入消息编码格式,则会中断该节点。

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,可以避免数据拷贝。

1.2. Writing a Simple Image Publisher (C++)
1.3. Writing a Simple Image Subscriber (C++)

上述两个链接指向ROS官方的ROS图像节点的教程。为了避免文章太长,发布在另一篇博客中,ROS的相机节点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值