在机器人系统中,视觉是非常重要的一部分(人的眼睛获取信息占全部信息的78%,机器人可以类比下)。因此,用前面四篇文章打下些许基础后,本人便迫不及待的想学习怎么在ROS上用上OpenCV视觉库。

 

好消息是我们第一篇文章选择了全部安装,这其中就把OpenCV库装进去了。那么剩下的就只是应用了。

 

为了操作方便,我们新建一个包,叫做rosOpenCV:

 
catkin_create_pkg rosOpenCV sensor_msgs cv_bridge roscpp std_msgs image_transport
 

先编译一下(这一步很关键):

 
catkin_make
 

在包下放一个cpp文件,叫做rosOpenCV.cpp,内容如下:

 
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <stdio.h>
 
int main(int argc, char** argv)
{
  ros::init(argc, argv, "image_publisher");
  ros::NodeHandle nh;
  cv::Mat image = cv::imread("/home/weixin/图片/壁纸/3.jpg", CV_LOAD_IMAGE_COLOR);
  if(image.empty())
  {
    printf("open error\n");
  }
  cv::imshow("",image);
  cv::waitKey(3000);
  cv::destroyWindow("");
  ros::spin();
}
 

在CMakeLists.txt末尾加上:

 
find_package(OpenCV)
include_directories(${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS})
add_executable(rosOpenCV ./src/rosOpenCV.cpp)
target_link_libraries(rosOpenCV ${OpenCV_LIBS})
target_link_libraries(rosOpenCV ${catkin_LIBRARIES})
 

到此,我们就能用OpenCV读取一张本地的图像并显示出来了。当然这样的操作也并没有什么意义,下面我们来做点有意义的。

 

我们将从本地读取图片数据,然后通过话题将这张图片发送出去。

 

其中,因为OpenCV的格式不是ROS通用的,因此我们需要通过cv_bridge转换一下。

 

具体的,我们只需要将rosOpenCV.cpp稍做修改即可:

 
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <stdio.h>
 
int main(int argc, char** argv)
{
  ros::init(argc, argv, "image_publisher");
  ros::NodeHandle nh;
  image_transport::ImageTransport it(nh);//用之前声明的节点句柄初始化it,其实这里的it和nh的功能基本一样,可以像之前一样使用it来发布和订阅相消息。
  image_transport::Publisher pub = it.advertise("camera/image", 1);
 
  cv::Mat image = cv::imread("/home/weixin/图片/壁纸/3.jpg", CV_LOAD_IMAGE_COLOR);
  if(image.empty())
  {
    printf("open error\n");
  }
  sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image).toImageMsg();//图像格式转换
 
  ros::Rate loop_rate(5);//每秒5帧
  while (nh.ok()) 
  {
    pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
  }
}
 

运行起来之后,可以用

 
rostopic echo /camera/image
 

查看我们发布出来的图片话题,但因为数据太多,效果相当要命。

 

我们可以用rivz来看,因为我们之前把ros的工具全装了,直接打开就可以:

 
rosrun rviz rviz
 

打开后点击左下方的"Add",在弹出的对话框中选"By topic",里面有"Image"选中后点击"OK",然后便可以看到下面的画面:

 

也可以自己编写接受节点去接收图像并显示出来,代码如下:

 
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
 
void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{
  cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image);
  cv::waitKey(30);
}
 
int main(int argc, char **argv)
{
  ros::init(argc, argv, "image_listener");
  ros::NodeHandle nh;
  image_transport::ImageTransport it(nh);
  image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback);
  ros::spin();
}
 

针对压缩格式的图像消息用一下代码:

 
#include "ros/ros.h"
#include "sensor_msgs/CompressedImage.h"
#include "sensor_msgs/image_encodings.h"
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <iostream>
 
cv::Mat imgCallback;
static void ImageCallback(const sensor_msgs::CompressedImageConstPtr &msg)
{
  cv_bridge::CvImagePtr cv_ptr_compressed = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
  imgCallback = cv_ptr_compressed->image;
  cv::imshow("imgCallback", imgCallback);
  cv::waitKey(1);
}
int main(int argc, char **argv)
{
  ros::init(argc, argv, "CompressedImage");
  ros::NodeHandle nh;
  ros::Subscriber image_sub;
  std::string image_topic = "/camera_front/image_color/compressed";
  image_sub = nh.subscribe(image_topic, 10, ImageCallback);
 
  ros::Rate loop_rate(10);
  while (ros::ok())
  {
    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}
  Ok,到此最简单的OpenCV的使用就讲完了。