计算机视觉库OpenCV和ROS[Python]

  • Post author:
  • Post category:python




计算机视觉库OpenCV和ROS[Python]

我们在这里真正感兴趣的是相机拍摄的RGB图像,因此本周我们将使用的关键主题是:

1.使用一系列ROS工具来查询ROS网络上的摄像头图像主题,并查看流式传输给他们的图像。

2.使用计算机视觉库OpenCV和ROS,获取相机图像并实时处理。

3.应用过滤过程来隔离图像中感兴趣的对象。

4.开发目标检测节点,并利用这些过程产生的信息来控制机器人的位置。

5.使用摄像头数据作为反馈信号,通过比例控制实现线路跟随行为。

对此运行rostopic info以识别消息类型。

rostopic list | grep /camera
rostopic info /camera/rgb/image_raw
rosmsg info sensor_msgs/Image
 rostopic info /camera/rgb/image_raw
Type: sensor_msgs/Image

Publishers:
 * /gazebo (http://localhost:40511/)

Subscribers: None

现在,在此消息类型上运行rosmsg info,以确切了解发布到主题的信息。你应该得到一个如下列表:

 rosmsg info sensor_msgs/Image
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data


使用rostopic echo和主题信息(如上所示)确定我们的机器人相机将捕获的图像大小(即尺寸,以像素为单位)。当我们稍后开始处理这些相机图像时,了解这一点非常重要。

RViz启动后,在左侧面板中找到相机项目,并勾选旁边的复选框。这将打开一个摄像头面板,其中包含从机器人摄像头获取的实时图像流!这样做的好处是,实时激光雷达数据也将覆盖在图像之上!

练习1:在更改机器人的视点时使用rqt_图像_视图节点

我们可以用来查看相机数据流的另一个工具是rqt_image_view节点。

要启动此功能,请按如下方式使用rosrun:

rosrun rqt_image_view rqt_image_view

这是一个很好的工具,可以让我们轻松查看发布到ROS网络上任何摄像头主题的图像。另一个有用的特性是能够捕获这些图像并将它们(作为.jpg文件)保存到文件系统:请参见上图中突出显示的“另存为图像”按钮。这可能会在本周稍后的课程中有所帮助。

单击窗口左上角的下拉框,选择要显示的图像主题。选择/camera/rgb/image_raw(如果尚未选中)。

现在保持此窗口打开,并启动一个新的终端实例(终端3)。

使用完整命令或方便的tb3_teleop别名启动turtlebot3_teleop节点!在现场旋转机器人,同时注意rqt_图像_视图窗口。一旦竞技场中的一根彩色柱子大致位于机器人视野的中心,停止机器人,然后分别在终端3和终端2输入Ctrl+C,关闭turtlebot3_teleop节点和rqt_image_view节点。

OpenCV和ROS

OpenCV是一个成熟而强大的计算机视觉库,用于执行实时图像分析,因此对于机器人应用非常有用。这个库是跨平台的,有一个Python API(cv2),我们将在本次实验中使用它来完成一些我们自己的计算机视觉任务。虽然我们可以直接使用Python(通过API)使用OpenCV,但该库无法直接解释发布到ROS/摄像头主题的图像消息所使用的本机图像格式,因此我们需要使用一个接口。该接口称为CvBridge,是一个ROS包,用于处理ROS和OpenCV图像格式之间的转换。因此,在开发ROS节点以执行计算机视觉相关任务时,我们需要同时使用这两个库(OpenCV和CvBridge)。

练习2:目标检测

我们经常希望机器人执行的一项常见工作是物体检测,我们将说明如何通过颜色过滤来检测机器人现在应该看到的彩色柱子来实现这一点。在本练习中,您将学习如何使用OpenCV捕捉图像、过滤图像并执行其他分析,以确认我们可能感兴趣的功能的存在和位置。

第一步

首先在catkin_ws/src目录中创建一个名为week6_vision的新包,并将rospy、cv_bridge、sensor_MSG和geometry_MSG作为依赖项。

然后,在软件包上运行catkin build,然后重新为您的环境提供资源(就像您在前几周所做的那样)。

在刚刚创建的包的src文件夹中,创建一个名为object_detection的新Python文件。皮耶。我们还需要对这个文件做什么才能运行它?!现在就做!

在此处复制代码,保存文件,然后查看代码解释程序,以便了解此节点的工作方式以及运行时应发生的情况。

使用rosrun运行节点。

确保关闭弹出窗口!此节点应捕获图像并在弹出窗口中显示。节点将等待您关闭弹出窗口,然后再执行其他操作(例如将映像保存到文件系统)!

通过阅读解释程序,您应该知道,节点刚刚获得了一个图像,并将其保存到文件系统上的一个位置。导航到此文件系统位置,并使用eog查看映像。

运行对象检测时,您可能从终端输出中注意到了什么。py节点是指机器人的摄像头以1080×1920像素的本机大小捕捉图像(您应该已经从之前使用rostopic echo查询/camera/rgb/image_raw/width and/height消息中了解到这一点)。在一张图像中,总共有200多万像素(准确地说,每张图像有2073600像素),每个像素都有一个蓝色、绿色和红色的值,所以在一张图像文件中有很多数据!运行对象检测时,图像文件的大小(以字节为单位)实际上已打印到终端。py节点。。。它到底有多大?

因此,对机器人来说,处理如此大小的图像是一项艰巨的工作:我们所做的任何分析都会很慢,我们捕获的任何原始图像都会占用相当大的存储空间。然后,下一步是通过将图像裁剪到更易于管理的大小来减少这种影响。

第二步

我们将修改对象检测。现在将节点复制到:

以原始大小捕获新图像

把它裁剪下来,集中在一个特别感兴趣的领域

保存两幅图像(裁剪后的图像应该比原始图像小得多)。


在catkin_ws/src目录中创建一个名为week6_vision的新包,并将rospy、cv_bridge、sensor_MSG和geometry_MSG作为依赖项。

catkin_create_pkg week6_vision rospy cv_bridge sensor_msgs geometry_m
catkin build week6_vision

运行对象检测时,您可能从终端输出中注意到了什么。py节点是指机器人的摄像头以1080×1920像素的本机大小捕捉图像(您应该已经从之前使用rostopic echo查询/camera/rgb/image_raw/width and/height消息中了解到这一点)。在一张图像中,总共有200多万像素(准确地说,每张图像有2073600像素),每个像素都有一个蓝色、绿色和红色的值,所以在一张图像文件中有很多数据!运行对象检测时,图像文件的大小(以字节为单位)实际上已打印到终端。py节点。。。它到底有多大?

因此,对机器人来说,处理如此大小的图像是一项艰巨的工作:我们所做的任何分析都会很慢,我们捕获的任何原始图像都会占用相当大的存储空间。然后,下一步是通过将图像裁剪到更易于管理的大小来减少这种影响。

第二步

我们将修改对象检测。现在将节点复制到:

以原始大小捕获新图像

把它裁剪下来,集中在一个特别感兴趣的领域

保存两幅图像(裁剪后的图像应该比原始图像小得多)。

在你的目标探测中。py节点定位该行:

show_and_save_image(cv_img, img_name = "step1_original")
        crop_width = width - 400
        crop_height = 400
        crop_y0 = int((width / 2) - (crop_width / 2))
        crop_z0 = int((height / 2) - (crop_height / 2))
        cropped_img = cv_img[crop_z0:crop_z0+crop_height, crop_y0:crop_y0+crop_width]

        show_and_save_image(cropped_img, img_name = "step2_cropping")

再次运行节点 rosrun week6_vision object_detection.py。

记住:只有关闭相应的弹出窗口,图像才会被保存。确保在查看这些弹出窗口后关闭所有这些窗口(即,不要只是最小化它们),以确保图像保存到文件系统中,并且节点成功执行。

刚才添加的代码通过指定相对于原始图像尺寸的所需裁剪高度和裁剪宽度,从原始图像的子集创建了一个名为裁剪img的新图像对象。此外,我们还使用crop_y0和crop_z0指定了在原始图像中(以像素坐标表示)希望该子集开始的位置。

第三步

如上所述,图像本质上是一系列像素,每个像素都有一个蓝色、绿色和红色值,代表实际的图像颜色。从我们刚刚获得并裁剪的原始图像中,我们想要得到任何颜色的装备,而不是与我们希望机器人检测到的柱子相关的颜色。因此,我们需要对像素应用一个过滤器,我们最终将使用它来丢弃与彩色柱无关的任何像素数据,同时保留与彩色柱无关的数据。

这个过程叫做掩蔽,为了达到这个目的,我们需要设置一些颜色阈值。在标准的蓝绿红(BGR)或红绿蓝(RGB)颜色空间中,这可能很难做到,您可以在本文的RealPython中看到一个很好的例子。通用域名格式。我们将应用本文中讨论的一些步骤,将裁剪后的图像转换为色调饱和度值(HSV)颜色空间,这使颜色掩蔽的过程更容易一些。

首先,分析裁剪图像的色调和饱和度值。为此,运行位于ROS节点,将剪切图像的路径作为附加参数提供:

$ rosrun com2009_examples image_colours.py /home/student/myrosdata/week6_images/step2_cropping.jpg

节点应生成散点图,说明图像中每个像素的色调和饱和度值。图中的每个数据点代表一个图像像素,每个数据点的颜色与RGB值匹配:

你应该从图像中看到,所有与我们想要检测的彩色柱相关的像素都聚集在一起。我们可以使用这些信息来指定一系列色调和饱和度值,这些值可以用来遮掩我们的图像:过滤掉任何超出这个范围的颜色,从而使我们能够隔离柱子。像素也有一个值(或“亮度”),该值未在该图中显示。根据经验,亮度值在100到255之间的范围通常运行良好。

在这种情况下,我们选择HSV上限和下限,如下所示:

下_阈值=(115225100)

上限_阈值=(130、255、255)

使用此处生成的绘图来确定自己的上限和下限。

OpenCV包含一个内置函数,用于检测图像的哪些像素位于指定的HSV范围内:cv2。inRange()。这将输出一个矩阵,其大小和形状与图像中的像素数相同,但只包含真(1)或假(0)值,说明哪些像素的值在指定范围内,哪些不在指定范围内。这被称为布尔掩码(基本上是一系列的1或0)。然后,我们可以使用逐位AND操作将此掩码应用于图像,以去除掩码值为False的任何图像像素,并将任何标记为True(或在范围内)。

要做到这一点,首先在对象检测中找到以下行。py节点:

show_and_save_image(cropped_img, img_name = "step2_cropping")
        hsv_img = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2HSV)
        lower_threshold = (115, 225, 100)
        upper_threshold = (130, 255, 255)
        img_mask = cv2.inRange(hsv_img, lower_threshold, upper_threshold)

        show_and_save_image(img_mask, img_name = "step3_image_mask")

现在,再次运行节点。现在应该生成并保存三张图像。如下图所示,第三幅图像应该只是裁剪图像的黑白表示,其中白色区域应该指示图像中像素值在前面指定的HSV范围内的区域。请注意(从打印到终端的文本来看),裁剪后的图像和图像遮罩的尺寸相同,但图像遮罩文件的大小要小得多。虽然遮罩包含相同数量的像素,但这些像素的值仅为1或0,而在相同像素大小的裁剪图像中,每个像素都有一个红色、绿色和蓝色的值:每个值在0到255之间,这代表了更多的数据。

rosrun week6_vision object_detection.py
Launched the 'object_detection_node' node. Currently waiting for an image...
Obtained an image of height 1080px and width 1920px.
Saved an image to '/home/student/myrosdata/week6_images/step1_original.jpg'
image dims = 1080x1920px
file size = 116996 bytes
Saved an image to '/home/student/myrosdata/week6_images/step2_cropping.jpg'
image dims = 400x1520px
file size = 49616 bytes
Saved an image to '/home/student/myrosdata/week6_images/step3_image_mask.jpg'
image dims = 400x1520px
file size = 7457 bytes

第四步

最后,我们可以将此遮罩应用于裁剪后的图像,生成最终版本的图像,其中只有遮罩中标记为True的像素保留其RGB值,其余的仅被删除。如前所述,我们使用一个按位AND操作来实现这一点,而且OpenCV还有一个内置函数来实现这一点:cv2。按位_和()。

 show_and_save_image(img_mask, img_name = "step3_image_mask")
        filtered_img = cv2.bitwise_and(cropped_img, cropped_img, mask = img_mask)
rosrun week6_vision object_detection.py
Launched the 'object_detection_node' node. Currently waiting for an image...
Obtained an image of height 1080px and width 1920px.
Saved an image to '/home/student/myrosdata/week6_images/step1_original.jpg'
image dims = 1080x1920px
file size = 117147 bytes
Saved an image to '/home/student/myrosdata/week6_images/step2_cropping.jpg'
image dims = 400x1520px
file size = 49733 bytes
Saved an image to '/home/student/myrosdata/week6_images/step3_image_mask.jpg'
image dims = 400x1520px
file size = 7457 bytes
Saved an image to '/home/student/myrosdata/week6_images/step4_filtered_image.jpg'
image dims = 400x1520px
file size = 10127 bytes

影像瞬间

你现在已经成功地在你的机器人的视野内隔离了一个感兴趣的物体,但也许我们想让我们的机器人朝它移动,或者——相反地——让我们的机器人在它周围导航,避免撞到它!因此,我们还需要知道物体相对于机器人视点的位置,我们可以利用图像矩来实现这一点。

我们刚才所做的工作使我们获得了所谓的色块。OpenCV还内置了一些工具,让我们能够计算像这样的一团颜色的质心,让我们能够确定感兴趣的对象在图像中的确切位置(以像素为单位)。这是使用图像矩原理完成的:基本上是与图像相关的统计参数,告诉我们像素集合(即我们刚刚分离的颜色团)是如何分布在其中的。你可以在这里阅读更多关于图像时刻的信息,从中我们可以了解到,通过考虑我们之前通过阈值化获得的图像遮罩的一些关键时刻,可以获得色块的中心坐标:

M00:图像遮罩中所有非零像素的总和(即色块的大小,以像素为单位)

M10:水平(y)轴上所有非零像素的总和,按行号加权

M01:垂直(z)轴上所有非零像素的总和,按列数加权

注:我们在这里将水平方向称为y轴,垂直方向称为z轴,以匹配我们之前用于定义机器人主轴的术语。

不过,我们真的不需要太过担心这些时刻的起源。OpenCV有一个内置的矩()函数,我们可以使用它从图像遮罩(例如我们之前生成的遮罩)中获取这些信息:

m = cv2.moments(img_mask)
cy = m['m10']/(m['m00']+1e-5)
cz = m['m01']/(m['m00']+1e-5) 

注意,我们在M00矩上加了一个非常小的数字,以确保上述方程中的除数永远不会为零,从而确保我们永远不会被任何“除以零”的错误所困扰。什么时候会这样?

同样,有一个内置的OpenCV工具,我们可以使用它在图像上添加一个圆,以说明机器人视点内的质心位置:cv2。圆圈()。这就是我们制作红色圆圈的方法,你可以在上图中看到。您可以在一个完整的对象检测示例中看到这是如何实现的。上一个练习中的py节点。

在我们的例子中,我们实际上无法改变机器人在z轴上的位置,因此cz质心分量对于我们的导航目的可能没有那么重要。然而,我们可能希望使用质心坐标cy来了解特征在机器人视野中的水平位置,并使用该信息转向它(或远离它,取决于我们试图实现的目标)。我们现在将更详细地了解这一点。

练习3:使用图像矩定位图像特征

在com2009_examples软件包中,有一个节点已经开发出来,用于说明如何使用您迄今为止探索的所有OpenCV工具来搜索环境,并在机器人直接注视感兴趣的对象时停止机器人。到现在为止,您应该已经熟悉了此节点中使用的所有工具,现在您将复制此节点并对其进行修改以增强其功能。

该节点称为颜色搜索。py,它位于com2009_examples包的src文件夹中。通过首先确保您位于所需的目标文件夹中,将其复制到您自己的week6_vision软件包的src文件夹中

我们有一个Python类结构,一个带有回调函数的订阅服务器,一个所有机器人控制都发生在其中的主循环,以及您在本课程中迄今为止探索过的许多OpenCV工具。基本上,该节点的功能如下:

机器人在从相机获取图像的同时打开现场(通过订阅/camera/rgb/image_raw主题)。

获取摄像头图像并进行裁剪,然后对裁剪后的图像应用阈值,以便在模拟环境中检测蓝柱。

如果机器人看不到一根蓝色的柱子,那么它会很快在原地转动。

一旦检测到,代表支柱的蓝色斑点的质心将被计算,以获得其在机器人视点中的当前位置。

蓝色的柱子一出现,机器人就开始转得更慢。

一旦机器人确定支柱位于其正前方,它就会停止转动,使用蓝色斑点质心的cy分量来确定这一点。

然后,机器人等待一段时间,然后再次开始转动。

整个过程不断重复,直到它再次找到蓝柱。

按原样运行节点以查看此操作。在整个执行过程中,观察打印到终端的消息。

你的任务是修改节点,使其停在竞技场中每根彩色柱子的前面(总共有四根)。为此,您可能需要使用我们本周迄今为止探索的一些方法。

您可能首先想使用我们用来从您的机器人摄像头获取和分析一些图像的一些方法:

使用turtlebot3_teleop节点手动移动机器人,使其分别查看竞技场中的每个彩色柱子。

运行对象检测。在上一个练习中开发的py节点,用于捕获图像、裁剪图像、将其保存到文件系统,然后将裁剪后的图像输入到图像中。com2009_示例包中的py节点(如您之前所做的)

或者,您可以使用rqt_image_view节点获取一些图像,并将它们保存到文件系统中。

注意:如果选择此选项,则最好从/camera/rgb/image_raw/compressed主题中捕获图像,以使文件更易于管理,并稍微加快分析速度。

根据图像颜色生成的绘图。py节点,确定一些适当的HSV阈值,以适用于竞技场中的每个彩色支柱。

一旦你有了正确的阈值,你就可以把这些添加到你的颜色搜索中。py节点,使其能够以当前检测蓝色支柱的相同方式检测每个支柱。



版权声明:本文为kirsten111111原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。