这篇博客是http://wiki.ros.org/cv_bridge/Tutorials/UsingCvBridgeToConvertBetweenROSImagesAndOpenCVImages的翻译
本教程介绍如何使用cv_bridge在ROS和OpenCV之间转换图像的格式。 包括一个示例节点,可以用作您自己的节点的模板。
ROS以自己的图像消息格式(sensor_msgs/Image)传递图像,但是许多用户将希望将图像与OpenCV结合使用。 CvBridge是一个ROS库,提供了ROS和OpenCV之间的接口。 CvBridge可以在visual_opencv堆栈中的cv_bridge包中找到。
在这个教程中,你讲学习如何写一个node使用CvBridge讲ros图像转换为Opencv图像格式(cv::Mat)
您还将学习如何将OpenCV图像转换为ROS格式以通过ROS进行发布。
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图像编码:
对于流行的图像编码,CvBridge将根据需要选择进行颜色或像素深度转换。 要使用此功能,请将编码指定为以下字符串之一:
注意:mono8和bgr8是大多数OpenCV功能预期的两个图像编码。
最后,CvBridge将识别Bayer模式编码为OpenCV类型8UC1(8位无符号,一个通道)。 它不会执行到Bayer模式或从Bayer模式转换; 在典型的ROS系统中,这通过image_proc来代替。 CvBridge识别以下Bayer编码:
为了将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创建的例子,请参考教程
这是一个监听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;
}
注意: opencv一般用的通道顺序为BGR, 您应该始终将您的调用包含到CvCopy()/ toCvShared()以捕获转换错误,因为这些函数将不会检查数据的有效性。
要运行节点,您将需要一个图像流。 运行相机或播放bag文件以生成图像流。 现在您可以运行此节点,将“in”重新映射到实际的图像流主题。
如果您已成功将图像转换为OpenCV格式,您将看到一个名为“Image window”的HighGui窗口,并显示您的图像+圈。
您可以使用rostopic或通过使用image_view查看图像来查看您的节点是否正确地在ROS上发布图像。
在上面的完整示例中,我们复制了图像数据,但共享(如果可能)同样容易:
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”,我们会避免复制数据。