在机器人系统中,视觉是非常重要的一部分(人的眼睛获取信息占全部信息的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的使用就讲完了。