目录
1、概念
2、将ROS图像转化为OpenCV图像
3、将OpenCV图像转化为ROS图像消息
4、例子
ROS的 sensor_msgs/Image消息格式本身就是为了传递图像,但是因为因为不能直接传递给Opencv所以ROS提供了CvBridge来进行之间的转化。
本文章将教您学会如何编写节点,使用CvBridge将ROS转化为OpenCV格式,还将学会如何将Opencv图像转化为ROS格式发布。
CvBridge定义了一个专门的CvImage类型,其中就包含了一个Opencv图像。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_ptrconst> CvImageConstPtr;
}
在ROS中我们有两个方式将sensor_msgs/Image转化为CvImage :
1、我们想修改数据,必须要复制消息
2、我们不想修改数据,我们可以安全的分享ROS消息数据,而不是复制
ROS提供以下转化为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());
可以看出两种函数输入都是消息指针(订阅者的回调函数的参数),另一个参数为可选的编码参数。
1、toCvCopy返回的是ROS消息创建图像数据的副本,可以自由修改的 CvImagePtr。
2、toCvShare返回的是一个不可修改的CvImageConstPtr
图像编码的方式可以是以下几种:
- 8UC[1-4]
- 8SC[1-4]
- 16UC[1-4]
- 16SC[1-4]
- 32SC[1-4]
- 32FC[1-4]
- 64FC[1-4]
对于常用的编码方式给出几个例子:
mono8: CV_8UC1, 灰度图像
mono16: CV_16UC1, 16-bit 灰度图像
bgr8: CV_8UC3, 蓝绿红顺序彩色图像
rgb8: CV_8UC3, 红绿蓝顺序彩色图像
bgra8: CV_8UC4, alpha 通道的BGR彩色图像
rgba8: CV_8UC4, alpha 通道的RGB彩色图像
注:mono8和bgr8是OpenCV最期望最常用的图像编码
要将CvImage转化为ROS图像消息,使用toImageMsg()成员函数:
class CvImage
{
sensor_msgs::ImagePtr toImageMsg() const;
//重载的函数
void toImageMsg(sensor_msgs::Image& ros_image) const;
};
ROS为我们提供了两个重载,我们可以使用对象直接调用。
本实例第一个为官网例子,另一个笔者有时间加入一个使用自己摄像头的例子。
官网给出的例子是在图像转化为cv::Mat类型,并且在图像上绘制一个圆圈给OpenCV显示图像,并且还给ros重新发布图像。在你创建功能包依赖时需要加上(catkin_create_pkg)添加依赖:
sensor_msgs cv_bridge roscpp std_msgs image_transport
头文件:
#include
#include
#include
#include
#include
#include
image_transport允许订阅压缩图像流。imgproc和hihgui为图像处理和gui也可以直接使用#include cv_bridge为本章的主题需要包含。接下来看定义的类和主函数。
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;//接受到ros图像的对象
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
//cv_ptr->image就是传递一个Mat类当opencv使用就可以了
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;
}