基于 OpenCV 使用 YOLOv3 进行深度学习中的物体检测

在这篇文章中,我们将学习如何在OpenCV中使用YOLOv3(一种最先进的目标探测器)。

YOLOv3是目前流行的物体检测算法YOLO——-“ 你只能看一次”的最新变种。目前已经发布的YOLO模型可以识别图像和视频中80多种不同的对象,更重要的是,它的运行速度非常快,而且准确率几乎和单次多盒检测器(SSD)一样高。

从OpenCV 3.4.2开始,您可以在自己的OpenCV应用程序中轻松使用YOLOv3模型。

那么YOLO是如何运作的呢?

我们可以将目标检测器视为是一种目标定位器和目标识别器的组合。

在传统的计算机视觉方法中,我们使用滑动窗口来寻找位置和尺寸都不同的物体。这个操作计算量非常大,所以通常假设物体的纵横比是固定的。早期

基于深度学习的目标检测算法(如R-CNN和Fast R-CNN)都是使用一种被称为选择性搜索的方法来缩小算法必须测试的边界框的数量。

另一种称为Overfeat的算法使用滑动窗口式按多个比例对图像进行扫描。

紧随其后的是Faster R-CNN算法,它使用区域提议网络(RPN)来识别需要测试的边界框。 通过巧妙的设计,识别目标的特征提取器也被RPN用于选出候选边界框,从而节省了大量的计算

但在另一方面,YOLO以完全不同的方式来处理目标检测问题。 它只把图像输入神经网络一次。SSD是另一种目标检测算法,它也是只让图片通过深度学习神经网络一次,但YOLOv3比SSD快得多,同时实现了可与之媲美的精度。在M40,TitanX和1080 Ti GPU上进行测试, YOLOv3的速度甚至高于实时检测。

接下来,让我们看看YOLO如何检测给定图像中的目标。

首先,将图像划分为13×13网格的单元格。 这169个单元的大小取决于输入图像的大小。 对于我们在实验中使用的416×416的图像输入尺寸,单元尺寸为32×32。 每个单元负责预测图像中的多个框。

对于每个边界框,网络还预测边界框实际包围对象的置信度,以及封闭对象是特定种类的概率。

大多数这些边界框最后都会被清除,因为它们的置信度很低,或者因为它们与另一个具有非常高置信度得分的边界框包围相同的对象。 该技术称为非最大值抑制。

YOLOv3的作者,Joseph Redmon和Ali Farhadi让YOLOv3比以前的作品YOLOv2更快更准确。 YOLOv3可以更好地处理多对象。 他们还通过增加网络来改进网络,通过添加简单的连接将这个网络扩展到剩余网络。

我们为什么要在OpenCV上实现YOLO?

以下可能是你这么做的几个原因:

与 OpenCV 应用程序可以轻松进行集成:如果你的应用程序已经使用过OpenCV 而你只是想使用 YOLOv3 而已,那么就不用担心编译过程和编辑额外的Darknet代码。

OpenCV的 CPU 版本快 9 倍:OpenCV 的 DNN 模块的 CPU 实现速度惊人。例如,当与 OpenMP 一起使用时,Darknet在CPU上对单个图像进行处理大概需要花费2秒钟。 相比之下,OpenCV的实现只需0.22秒!详细数据请查看下表。

Python支持:Darknet是用C语言编写的,它并不官方支持Python。 相比之下,OpenCV确实如此。 虽然有Darknet可用的python端口。

在 Darknet 和 OpenCV 上对 YOLOv3 进行速度测试

下表显示了 YOLOv3 在 Darknet 与 OpenCV 上的性能。 所有情况下的输入大小为 416×416。 毫无疑问,Darknet 的 GPU 版本胜过其他一切。 使用 OpenMP 的 Darknet 比没有 OpenMP 的 Darknet 工作得更好也不足为奇,因为 OpenMP 允许使用多个处理器。

令人惊讶的是,OpenCV 的 DNN CPU 实现速度比使用 OpenML 的 Darknet 快9倍。

表1:分别在Darknet与OpenCV上YOLOv3的速度测试对比

注意:我们在使用OpenCV中进行DNN的GPU实现时遇到了一些问题。 资料表明它仅支持使用英特尔GPU进行测试,因此如果你的电脑中没有英特尔GPU,代码会将把你切换回CPU。

使用C ++ / Python语言的YOLOv3进行目标检测

现在让我们看看如何在OpenCV中使用YOLOv3来执行目标检测。

第1步:下载模型

我们从在命令行中使用脚本文件getModels.sh下载模型开始。

这将下载yolov3.weights文件(包含预先训练的网络权重),yolov3.cfg文件(包含网络配置)和coco.names文件,其中包含COCO数据集中使用的80个不同的类名。

第2步:初始化参数

YOLOv3算法生成边界框作为预测的检测输出。 每个预测框都与置信度得分相关联。 为了方便后续处理,在第一阶段所有置信度低于阈值的边界框都会被忽略。

其余的边界框则进行非极大值抑制,通过这个操作消除多余的重叠边界框。 非极大值抑制由参数Threshold进行控制,你可以尝试调整这个值的大小,查看输出预测框的数量会如何变化。

接下来,设置输入图像的输入宽度(inpWidth)和高度(inpHeight)的默认值。 我们将它们都设置为416,这样我们就可以将我们的运行结果与YOLOv3作者在Darknet中给出的的C语言代码结果进行比较。 你也可以将它们更改为320来获得更快的结果,或更改为608来获得更准确的结果。

第3步:加载模型和分类

coco.names文件包含着模型要训练的所有对象。 我们读取分类名字。
接下来,我们加载有如下两个部分的网络 :
1、yolov3.weights:预训练的权重。
2、yolov3.cfg:配置文件。

我们在这里将DNN后端设置为OpenCV,将目标设置为CPU。 你也可以尝试将首选目标设置为cv.dnn.DNN_TARGET_OPENCL以在GPU上运行它。 但请记住,目前的OpenCV版本仅使用英特尔的GPU进行测试,如果您没有英特尔GPU,它会自动切换到CPU。

第4步:阅读输入

在这个步骤中,我们将读取图像,视频流或网络摄像头。 此外,我们还打开了视频编写器来保存检测到的具有输出边界框的帧。

第4步:对每个帧进行处理

神经网络的输入图像需要采用称一种被为blob的特定格式。

从输入图像或视频流中读取帧后,通过blobFromImage函数将其转换为神经网络所要求的输入格式blob。在此过程中,它使用1/255的比例因子将图像像素值缩放到0到1范围之间。它还通过调整而不是裁剪的方式把输入的图像大小变为给定的(416,416)。请注意,我们在这里不执行任何均值减法,把[0,0,0]传递给函数的mean参数,并将swapRB参数保持为其默认值1。

接下来,我们把这里输出的blob格式作为输入传递到神经网络中,并进行正向传递,运行获得的预测边界框列表作为网络输出。这些框经过一些后续处理,滤除具有置信度较低的那些框。我们将在下一节中更详细地介绍这些后处理步骤。我们打印出左上角每帧图像的处理时间。然后将具有最终边界框的图像保存到磁盘,作为图像输入的图像或使用视频流输入的视频读入器。


接下来,让我们详细了解一下上面使用的一些函数调用。

步骤4a:获取输出层的名称

在OpenCV 中,Net类的forward函数需要一个在网络中运行的结束层。 由于我们想要遍历整个网络,我们需要识别出网络的最后一层。 我们通过使用getUnconnectedOutLayers()函数来实现这一点,这个函数给出了那些没有连接输出层的层名称,这些层基本上就是网络的最后一层。 然后我们运行网络的正向传递来从输出层中获得输出,如前面的代码片段(net.forward(getOutputsNames(net)))。

步骤4b:后处理网络的输出

神经网络的输出边界框通常是由一组5个或更多元素的向量表示。

前4个元素代表center_x,center_y,width和height。 第五个元素表示边界框包围对象的置信度。

其余元素是与每个类相关的置信度(即对象类型)。 这个框被分配到它得分最高相对应的那个类。

边界框的最高分也被称为置信度。 如果框的置信度小于给定阈值,则删除这个边界框并且不再考虑进行后续处理。

然后对其置信度等于或大于置信度阈值的框进行非最大抑制。 这将减少重叠框的数量。

非极大值抑制由nmsThreshold进行控制,如果这个值设置得太低,比如 0.1,那我们可能无法检测到相同或不同类的重叠对象。 但如果设置得太高,如 1,那我们会得到同一个对象的多个框。 所以我们在上面的代码中使用了0.4的中间值。 下面的gif显示了改变NMS阈值的效果的变化。

步骤4c:绘制预测框

最后,我们在输入框架上绘制经过非极大值抑制过滤的边界框,并指定其类别标签和置信度分数。

推荐文章

沪公网安备 31010702002009号