转自:https://blog.****.net/qq_27050183/article/details/51141998
Ros图像与Opencv图像的相互转换(C++)(译文*来自wiki)(ROS为indigo版本)
摘要:此教程通过将ROS图像转换为OpenCV图像讲解了使ROS与OpenCV相结合的方法。教程包含一个示例节点,可以用作自己的节点模板。
关键词:ROS图像,OpenCV图像,CVBrideg
教程等级:中等难度
1.综述
ROS有其自己的消息格式为sensor_msgs/Image的显示图像,但是许多开发者想结合OpenCV来显示处理图像。CvBridge是ROS的一个类,此类提供了ROS与opencv相结合的接口。CvBridge能够在cv_bridge包中的vision_opencv栈中找到。
在本教程中,你将学会通过CvBridge编写一个节点并以此将ros图像转化为opencv中的CV::Mat格式。同样的,你也会学会将opencv图像转化为ros图像。
2.ros图像信息转化为opencv图像
CVBridge定义了一个包含opencv图像的CvImage类型。CvImage包含额外的sensor_msgs/Image信息,因此我们可以将上述俩者进行相互转换。CvImage类代码如下:
当将ros的sensor_msgs/Image信息转化为CvImage时,CvBridge提供俩种不同的用例。
- class CvImage
- {
- public:
- std_msgs::Header header;
- std::string encoding;
- cv::Mat image;
- };
- typedef boost::shared_ptr<CvImage> CvImagePtr;
- typedef boost::shared_ptr<CvImage const> CvImageConstPtr;
1.在我们要修改数据的地方。我们必须复制一份ros的信息数据。
2.如果我们不修改数据。我们可以安全地分享由ros消息所拥有的数据,而不用复制。
CvBridge为向CvImage转化提供如下函数:
- <span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">// 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());</span></span></span>
toCvCopy从ROS信息中创建图像数据的副本,即使当源和目的地encodings匹配。然而,你可以*修改返回的cvimage。
toCvShare将cv::Mat点返回在ROS的消息数据,避免复制,如果源和目的编码匹配。只要你保持一份返回的cvimage,ROS的消息数据将不会释放。如果编码不匹配时,它将分配一个新的缓冲区,执行转换。您不得修改返回的cvimage,因为它可能与ROS的图像信息共享数据,这反过来又可能与其他回调共享。
注意:对tocvshare二过载更方便,当你有一个指针指向其他信息类型。(例如:stereo_msgs/DisparityImage)包含sensor_msgs/Image你要去转换。
如果没有编码(或更确切地说,空字符串),目标图像编码将与图像信息编码相同。在这种情况下tocvshare保证不复制图像数据。图像编码可以是以下任何一个opencv图像编码:
8UC[1-4]
8SC[1-4]
16UC[1-4]
16SC[1-4]
32SC[1-4]
32FC[1-4]
64FC[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
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模式;在一个典型的ROS系统,这是由image_proc而做的。cvbridge识别以下Bayer编码:
bayer_rggb8
bayer_bggr8
bayer_gbrg8
bayer_grbg8
bayer_bggr8
bayer_gbrg8
bayer_grbg8
3OpenCV图像转换为ROS图像
将Cvimage图像转换为ros图像,使用toImageMsg()成员函数:
- <span style="font-size:18px;"><span style="font-size:18px;">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;
- };</span></span>
4 一个ROS节点例子
这里是一个节点,监听ROS图像信息话题,将图像转换为cv::Mat并在其上画圆,用opencv显示图像。然后图像将重新在ROS上显示。
在你的package.xml 和 CMakeLists.xml (或者你用 catkin_create_pkg),添加如下依赖:
- <span style="font-size:18px;"><span style="font-size:18px;">sensor_msgs
- cv_bridge
- roscpp
- std_msgs
- image_transport</span></span>
- <span style="font-size:18px;"><span style="font-size:18px;">#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;
- }</span></span>
- <span style="font-size:18px;">#include <image_transport/image_transport.h></span>
使用image_transport发布和订阅的图像在ROS允许你订阅压缩图像流。记住,将image_transport包含进你的 package.xml中。
- <span style="font-size:18px;">#include <cv_bridge/cv_bridge.h>
- #include <sensor_msgs/image_encodings.h></span>
包括cvbridge头以及一些有用的常量和函数图像编码相关。记住,将cv_bridge包含进 package.xml中。
- <span style="font-size:18px;">#include <opencv2/imgproc/imgproc.hpp>
- #include <opencv2/highgui/highgui.hpp></span>
- <span style="font-size:18px;">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>
- <span style="font-size:18px;">cv::namedWindow(OPENCV_WINDOW);
- }
- ~ImageConverter()
- {
- cv::destroyWindow(OPENCV_WINDOW);
- }</span>
- <span style="font-size:18px;">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>
注意,OpenCV要求彩色图像使用BGR信道规则。
你应该把你的调用使用tocvcopy() / tocvshared()来捕获转换错误,因为这些函数不会检查你的数据的有效性。
- <span style="font-size:18px;">// 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</span>
画一个红色圆圈的图像,然后显示在显示窗口。
- <span style="font-size:18px;">cv::waitKey(3);</span>
要运行这个节点,你需要一个图像流。运行一个摄像头或播放一个包文件来生成图像流。现在你可以运行这个节点“in”映射到运行的图像流的话题。
如果你已经成功地转换图像为opencv的格式,你会看到一个highgui窗口的名称为“image windows”,你的图像和圆会显示。
你可以看看你的节点是否在ros上正确发布图像,通过使用rostopic或通过使用image_view查看图像。
5图像数据共享实例
在上面的例子中,我们明确的复制了图像数据,但是共享(在可能的情况下)同样容易:
- <span style="font-size:18px;">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
- }</span>
- <span style="font-size:18px;">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
- }</span>