网络视频直播系统 Yarn object tree jestjs rss Fries vue循环数组 品优购电商系统开发 android项目实例 jquery去除空格 jq获取元素宽度 linux自动获取ip map删除指定元素 maven配置eclipse java二维数组赋值 flutter项目案例 python集合操作 python加注释 python中import用法 java的基本数据类型 java索引 java生成当前时间 java重命名 java怎么获取当前时间 linuxshell linuxtar命令 vs2010sp1 不寻常的指南针 kms神龙版 电脑必备软件排行榜 手机主题之家 方正兰亭字体下载 winterboard php购物车 cfqq网吧任务 系统工具箱 汽车配件查询软件 正当防卫4存档 光头强换肤助手
当前位置: 首页 > 学习教程  > 编程语言

ROS 图像相关的命令与应用

2020/8/11 20:35:47 文章标签:

本文翻译自 xx大学的机器人课程,以及个人使用的Ros进行图像处理时的一些问题总结:

1. ROS 中表示图像

1.1 查看图像

ROS中最基本的图像表示方式为sensor_mags/Image 类型,可以使用RVIZ对该topic所对应的图像画面进行查看,RVIZ中的Camera display功能;或者更简便的,可以使用模块image_view :

具体命令为: rosrun image_view image_view image:=topic_name

例如, 如果想要查看 Turtlebot‘s KInect sensor 捕捉的图像,可以使用命令:

rosrun image_view image_view image:=/camera/rgb/image_color compressed

rosrun image_view image_view image:=/camera/rgb/image_mono compressed

1.2. 转换图像 

大家可能已经知道,在ROS中订阅其他电脑上发出的某个topic,在传输的的时候是会占用一定的网络带宽的, 例如,在我的电脑上, 订阅  /camera/rgb/image color 话题,每秒钟会消耗掉2.5 megabytes;

之所以会出现如上的状况,是因为我们传输的都是未压缩的视频或图像数据。 当然,ROS也提供了对应的转换方法,可以将topic中的图像转换成对应的压缩格式,这种压缩方式基本只会降低图像或者视频的大小,而并不会损失掉一些图像的细节,同时,ROS将该模块都统一打包,因此我们并不需要关心具体的压缩或者解压过程。

关于image transport的最简单的例子如下:

#include <ros/ros.h>
#include <image_transport/image_transport.h>

void callback(const sensor_msgs::ImageConstPtr& msg) {
    ROS_INFO("Got image.");
}
int main(int argc, char **argv) {
    ros::init(argc, argv, "nodeName");
    ros::NodeHandle nh;
    image_transport::ImageTransport it(nh);
    image_transport::Subscriber sub = it.subscribe(topicName, queueSize, callback);
    ros::spin();
}

如果使用以上代码,必须进行对在package.xml 和CMakeLists.txt 中添加依赖库:image_transport.

注意,在上面的代码中我们使用的topicName依然是原先的topic内容,但是代码在执行的时候 会比之前的那种直接subscribe一个topic的方法多一层判断,会先调用参数∼image transport判断是否需要订阅压缩图像;

关于 ∼image transport的几种情况:

1. 如果对参数 ∼image transport不进行任何的说明或者是赋值,那么最后的结果 跟没使用这个模块是一样的效果, ROS最终会follow的topic依然是未压缩的图像;

2. 如果参数 ∼image transport设置成compressed的类型,每副图像都会以JPEG或者PNG的形式进行压缩,而后在callback之前,也就是接收到数据之后进行解压缩decompression;

3. 如果参数 ∼image transport设置成theora,视频流将会被压缩成为更小的格式,这种方式比compressed更高效一点,因为它会推算连续帧中的相似性。

使用Image_transport的另一个方法或者好处是,我们可以仅仅通过命令实现压缩或者改变压缩的方式,如下:

<launch>
    <node name="view" pkg="J5" type="view" output="screen">
        <param name="image_transport" value="theora" />
    </node>
</launch> 

上面的讲解中Image_transport前的~表示这是一个node的private变量,因此使用时一般是加载node之间,作为参数出现。

 1.3 OpenCV

OpenCV是什么这里就不介绍了,ROS中其实已经整合了OpenCV,并且可以在ROS代码中进行使用;

1.3.1 编译使用opencv

如果要在ROS代码中使用Opencv,需要额外地包含OPencv的库,对应的包含方法如下:

find_package(OpenCV_REQUIRED)
target_link_libraries(your executable-name ${OpenCV_LIBS}) ​

同时还需要包含另一个包cv_bridge, 主要是用于在ROS图像与OpenCV图像之间做一个转换;

1.3.2 转换成cv::Mat的格式:

cv_bridge的一个好处是将sensor_msgs::Image类型的消息转换成为cv::Mat的形式;例如,我们可以在callback函数中进行如下设置:

#include <cv_bridge/cv_bridge.h>
#include <opencv/cv.h>
...
cv_bridge::CvImagePtr cvImagePtr;
try {
    cvImagePtr = cv_bridge::toCvCopy(msg);
} catch (cv_bridge::Exception &e) {
    ROS_ERROR("cv_bridge exception: %s", e.what());
}

cvImagePtr 是cv_bridge的图像数据表达,我们可以直接使用cvImagePtr->image 来获得cv::Mat;

1.4  图像访问

一张图像其实就是一个二维数组,对cv::Mat 格式的图像,可以通过rows 和 cols 进行数据访问:

cv::Mat &Mat = cv::ImagePtr->image;
int width = mat.cols;
int height = mat.rows;  

除了检查图像的size,最主要的是我们需要对获取来的图像中的数值进行访问, OPencv中cv::Mat 可以用at进行单个像素的数值访问,cv::Mat中图像的存储可以有很多种形式,例如单通道的灰色图像以单字节的形式存储,而三通道的彩色图像,每个像素的位置存放的都是一个1*3的数组, 不仅如此,cv::Mat中还可以存储float或者double型的数据。

以Turtlebot 的/camera/rgb/image mono 为例,这是一个单通道Mono8的图像,每一个像素中存储的是一个无符号字符 unsigned char, 对这种类型数据的访问为:

cv::Mat &mat = cvImagePtr->image; ROS_INFO("image[%d,%d] = %d", i, j, (int) mat.at(i, j));

这样输出的数据中都是从0 到 255的数字,代表了不同的像素值, 0为black, 255 为white;

而话题 /camera/rgb/image color 传输的确实bgr8的图像,每个像素中存放的是三个无符号char,分别代表了red,green,blue,这种形式的图像一般通过cv::Vec3b 的形式进行访问;

cv::Mat &mat = cvImagePtr->image; 
cv::Vec3b pixel = mat.at(i,j); 
ROS_INFO("image[%d,%d] = (r=%d,g=%d,b=%d)", i, j, pixel[2], pixel[1], pixel[0]);  

1.5 图像显示 

opencv的另一个好处是可以很方便地显示图像,这里用一段代码举例:

#include <opencv/highgui.h>  
cvNamedWindow("windowName", CV_WINDOW_AUTOSIZE);
cvStartWindowThread();
cv::imshow("windowName", mat);
cvDestroyWindow("windowName");

这样的显示方法 对比 直接在ROS中进行图像显示 的一个好处就是: 在图像显示之前,可以对图像进行编辑, 例如我们可以通过如下代码,将一副彩色图像中的某个像素值变成0 ,也就是三个通道的数值都变成黑色:

cv::Vec3b &pixel = mat.at<cv::Vec3b>(i,j);
pixel[0] = 0;
pixel[1] = 0;
pixel[2] = 0;

结束。

上面的教程相对是比较基础的,接下来是我自己的一些总结:

1. 首先是压缩图像部分,这部分 分为发布压缩图像订阅/查看压缩图像

发布:

将一个正常的图像topic转换成压缩图像的形式,这里用compressed形式进行举例,可以在launch文件中直接进行设置:

 <param name="image_transport" type="string" value="compressed" />

 也是同样放在一个node中作为参数出现;

订阅/显示:

如果要是查看一个压缩图像,上面说道可以用ros自己的模块image_view进行显示,但是如果要访问一个压缩图像,则需要在image_view后面进行参数添加:

rosrun image_view image_view image:=/camera/rgb/image_color  ∼image transport = compressed  

 使用:

上面从Ros的sensor_msg形式转换成opencv的cv::Mat 都是针对与原图像,这里主要说以下针对压缩图像的转换,具体代码如下:

  ros::NodeHandle nodeHandler;
  ros::Subscriber encoder_sub = nodeHandler.subscribe(
      "/odom", 10, &ImageGrabber::GrabOdom, &igb);
  ros::Subscriber image_sub = nodeHandler.subscribe(
      "/usb_cam_left/image_raw/compressed", 1, &ImageGrabber::GrabImage, &igb);
  ros::spin();

上面是main函数的部分,接下来是回调函数:

void ImageGrabber::GrabImage(const sensor_msgs::CompressedImageConstPtr &msgRGB) {
  cv::Mat image;
  try{
    image = cv::imdecode(cv::Mat(msgRGB->data),1);
  }
  catch(cv_bridge::Exception& e){
    ROS_ERROR("Could not convert to image! ");
  }
  
  cout << "get image data" << endl;
  if(!image.empty() && finishedCurTrack)
  {
    /* DO SOMETHING*/
  }
}

另外,如果是SLAM中,我们经常使用到双目,这是后就需要对双目图像进行压缩和转换,压缩部分与单目一样,但是在订阅的时候,因为SLAM双目跟踪的特殊性质,需要对双目采集的图像进行一个同步,具体如下:

    ros::NodeHandle nh;
    message_filters::Subscriber<sensor_msgs::CompressedImage> left_sub(nh, "/left_cam/image_raw/compressed", 1);
    message_filters::Subscriber<sensor_msgs::CompressedImage> right_sub(nh, "/right_cam/image_raw/compressed", 1);
    typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::CompressedImage, sensor_msgs::CompressedImage> sync_pol;
    message_filters::Synchronizer<sync_pol> sync(sync_pol(10), left_sub,right_sub);
    sync.registerCallback(boost::bind(&ImageGrabber::GrabStereo,&igb,_1,_2));
    ros::spin();

这里需要注意在 message_filters中订阅的是sensor_msgs::CompressedImage类型的消息, 而在接下来的回调函数中使用的是指针类型的消息:

void ImageGrabber::GrabStereo(const sensor_msgs::CompressedImageConstPtr& msgLeft,const sensor_msgs::CompressedImageConstPtr& msgRight)
{
    cv::Mat left_img;
    try
    {
        left_img = cv::imdecode(cv::Mat(msgLeft->data),1);
    }
    catch (cv_bridge::Exception& e)
    {
        ROS_ERROR("cv_bridge exception: %s", e.what());
        return;
    }

    cv::Mat right_img;
    try
    {
        right_img = cv::imdecode(cv::Mat(msgRight->data),1);
    }
    catch (cv_bridge::Exception& e)
    {
        ROS_ERROR("cv_bridge exception: %s", e.what());
        return;
    }
    /*DO SOMETHING*/
}

 


本文链接: http://www.dtmao.cc/news_show_100386.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?