技术分享 | 机器人工程师,都是如何进行图像处理的?

相信大家在图像处理在机器人领域中是很常见的一个功能。通过计算机视觉获取周围环境。从而让机器人“看见”世界,并且实施监控。本文将会详细介绍ROS中的cv_bridge功能包,并给出使用的例子。帮助大家快速上手在ROS中使用OpenCV进行图像处理。

1. 什么是cv_bridge 顾名思义,cv_bridge是一个在ROS中,将ROS图像消息和OpenCV图像连接起来的一个桥梁。如图所示: file

在ROS中,图像消息的传递形式是ROS本身的消息类型sensor_msgs/Image传递。但是大多数用户都希望使用OpenCV。因此,cv_bridge就提供了一个ROS和OpenCV之间的接口。

2. cv_bridge使用详解(C++) cv_bridge定义了一个包含OpenCV的图像,并且包含ROS头的编码。CvImage类包含了ROS的图像消息 sensor_msgs/Image的格式,因此,我们可以之间使用并且转换。CvImage类定义如下: file

而当我们在转换图像的时候,有以下两种情形: a) 直接复制一份ROS消息数据,然后对复制的图像进行修改。修改的是复制后的数据。此时并不影响原图像。 b) 安全共享ROS消息拥有的数据,而不是复制。意味着当你修改图像时,会影响原图像。

因此,针对这个情况,cv_bridge提供了针对上述情况的两种函数,如下图所示: file

两种类型的函数,传入的参数均是保存图像信息矩阵的指针,以及一个图像编码参数(8UC3,8位,3通道)。两个函数不同之处在于:

a) toCvCopy函数将会从ROS消息复制一个图像数据的副本,即使原图像和编码完全一致,也可以自由修改返回的CvImage,不对原图像造成影响。但是会消耗更多的系统资源。 b) toCvShare函数为了避免复制,返回的是cv::Mat类型且指向ROS图像消息的数据。当原图像和编码完全一致的时候,此时如果内存中有ROS图像消息的副本,将不会释放内存。如果编码不匹配,则会新开辟一块缓冲区去执行转换操作。无论是哪种情况,都禁止去修改返回的CvImage,因为它可能与ROS图像消息共享数据。

如果没有指定图像编码,转换的目标图像将会和原图像的编码保持一致。toCvShare函数将不会复制图像数据。图像的编码可以是下面OpenCV的任意一种:

8UC[1-4]:8位无符号整数1-4通道,即对应OpenCV中的CV_8UC1、CV_8UC2、CV_8UC3、CV_8UC4 8SC[1-4] 16UC[1-4] 16SC[1-4] :16位符号整数1-4通道 32SC[1-4] 32FC[1-4] 64FC[1-4]

对于流行的图像编码来说,cv_bridge将根据需要选择做出颜色或者深度转换,如果想要使用次特性,指定以下图像编码:

mono8:CV_8UC1,即8位单通道(灰度图) mono16:CV_16UC1,即16位单通道(灰度图) bgr8:CV_8UC3,即8位蓝绿红色序彩色图 rgb8:CV_8UC3,即8位红绿蓝色序彩色图 brga8:CV_8UC4,即带有alpha通道的8位BGR彩色图 rcba8:CV_8UC4,即带有alpha通道的8位RGB彩色图

备注:mono8和bgr8是大多数OpenCV函数所使用的图像编码。

转换OpenCV图像到ROS图像消息,使用的是toImageMsg()函数,其定义如下: file

3. 使用示例 该功能包下有两个节点,一个是图像发布节点,将OpenCV图像转换为ROS消息图像并发布,一个是图像订阅节点,用于订阅ROS发布的图像,并且将ROS图像转换为OpenCV图像,并在上面画一个圆圈,然后通过OpenCV显示。使用该功能包需要用到的依赖有

sensor_msgs cv_bridge roscpp std_msgs image_transport

发布者源码如下:

1./* 2.* 创建一个ROS节点 3.* 将OpenCV图像转换为ROS图像消息并发布 4.* OpenCV版本为4.1.0 5.* 阿木实验室-青石 6./ 7.
8.//ROS头文件
9.#include <ros/ros.h>
10.
11.//图像相关文件
12.#include <image_transport/image_transport.h>
13.#include <cv_bridge/cv_bridge.h>
14.#include <sensor_msgs/image_encodings.h>
15.#include <opencv2/imgproc/imgproc.hpp>
16.#include <opencv2/highgui/highgui.hpp>
17.
18.//C++标准头文件
19.#include
20.
21.
22.int main(int argc, char*
argv)
23.{
24. //初始化ROS节点
25. ros::init(argc, argv, "image_converter_pub_node");
26. ros::NodeHandle n;
27.
28. //创建图像发布者对象
29. image_transport::ImageTransport image(n);
30. image_transport::Publisher image_pub = image.advertise("/camera/image",1);
31.
32. //OpenCV模块
33. //读取原始图像,并将图像矩阵赋值给 src_image
34. cv::Mat src_image = cv::imread("/home/loren/ros_ws/src/ros_vision/src/image/background.png",-1);
35.
36. //判断图像是否读取成功
37. if(src_image.empty())
38. {
39. std::cout << "图片不存在,请确认图片路径是否正确!" << std::endl;
40. return -1;
41. }
42. else
43. {
44. std::cout << "图像加载成功!" << std::endl;
45. }
46.
47. cv::waitKey(0); //等待用户按键,结束程序
48.
49. //使用cv_bridge转换图片
50. //创建ROS图像对象,用于保存从OpenCV转换到ROS的图像
51. sensor_msgs::ImagePtr image_msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", src_image).toImageMsg();
52.
53. ros::Rate loop_rate(5); //5Hz
54.
55. while(ros::ok())
56. {
57. //循环发布图像
58. image_pub.publish(image_msg);
59. loop_rate.sleep(); //保证发布频率为5Hz
60. }
61.
62. return 0;
63.}

订阅者源码如下: 1./* 2.* 创建一个ROS节点,它将订阅ROS发布的图像 3.* 并将ROS图像转换为cv::Mat(即:OpenCV图像), 4.* 并在上面画一个圆,然后通过OpenCV显示 5.* OpenCV版本为4.1.0 6.* 阿木实验室-青石 7./ 8.
9.//ROS头文件
10.#include <ros/ros.h>
11.
12.//图像相关文件
13.#include <image_transport/image_transport.h>
14.#include <cv_bridge/cv_bridge.h>
15.#include <sensor_msgs/image_encodings.h>
16.#include <opencv2/imgproc/imgproc.hpp>
17.#include <opencv2/highgui/highgui.hpp>
18.
19.//C++标准头文件
20.#include
21.
22.
23.void ImageCallback(const sensor_msgs::ImageConstPtr& msg)
24.{
25. //创建cv_brigde对象,用于保存从ROS转换成OpenCV的图像数据
26. cv_bridge::CvImagePtr cv_ptr;
27.
28. try
29. {
30. //这里选择toCvCopy函数,是为了在其副本上画图,而不影响原图像
31. //图像编码格式为 BGR8
32. cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
33. }
34. catch(cv_bridge::Exception& e)
35. {
36. ROS_ERROR("cv_bridge exception: %s", e.what());
37. return;
38. }
39.
40. //在图像上画圆
41. //画一个直径为50的圆,故需要图像尺寸大于60
60
42. if(cv_ptr->image.rows> 60
43. && cv_ptr->image.cols > 60)
44. {
45. std::cout << "开始画圆!" << std::endl;
46. cv::circle(cv_ptr->image, cv::Point(50, 50), 10, CV_RGB(255, 0, 0));
47. }
48.
49.
50. //GUI窗口显示图像
51. cv::imshow("image view", cv_ptr->image);
52. cv::waitKey(0); //等待用户按键,结束程序
53.}
54.
55.int main(int argc, char** argv)
56.{
57. //初始化ROS节点
58. ros::init(argc, argv, "image_converter_sub_node");
59. ros::NodeHandle n;
60.
61. //创建图像订阅者对象
62. image_transport::ImageTransport image(n);
63. image_transport::Subscriber image_sub = image.subscribe("/camera/image", 1, ImageCallback);
64. ros::spin(); //循环调用回调函数,该行后面的代码均不会被执行
65.
66. return 0; //不会被执行
67.}

相信经过本文的梳理和案例,可以给读者提供一个如何使用cv_bridge功能包的思路。通过这个包,将ROS和OpenCV结合起来,让ROS机器人的开发有了更多,更加宽广的选择。也让机器人的世界,变得丰富多彩!

  • End -

技术发展的日新月异,阿木实验室将紧跟技术的脚步,不断把机器人行业最新的技术和硬件推荐给大家。看到经过我们培训的学员在技术上突飞猛进,是我们培训最大的价值。如果你在机器人行业,就请关注我们的公众号,我们将持续发布机器人行业最有价值的信息和技术。

阿木实验室致力于前沿IT科技的教育和智能装备,让机器人研发更高效!

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值