DownOL 软件仓库– 软件下载,字节世界与新知

详解:实时视频对象检测应用程序的构建

发表于:2024-04-26 作者:创始人
编辑最后更新 2024年04月26日,在这篇文章中,我们将用Java构建一个实时视频对象检测应用程序,用于汽车检测,这是自动驾驶系统的一个关键组件。在之前的文章中,我们能够构建一个图像分类器(猫与狗);现在,我们要检测对象(即汽车、行人)

在这篇文章中,我们将用Java构建一个实时视频对象检测应用程序,用于汽车检测,这是自动驾驶系统的一个关键组件。在之前的文章中,我们能够构建一个图像分类器(猫与狗);现在,我们要检测对象(即汽车、行人),并用边框(矩形)标记它们。可以随意下载代码或用自己的视频运行应用程序(简短的例子)。(http://ramok.tech/wp-content/uploads/2018/01/2018-01-22_15h29_24.mp4)

对象检测性质

对象分类

首先,我们有对象分类的问题,其中我们有一个图像,并想知道这个图像是否包含任何特定的类别,即查看图像是否包含一辆汽车。

对象定位

现在,我们可以高度自信地表示图像是否有特定的对象,我们面临的挑战是定位对象在图像中的位置。通常,这是通过用矩形或边界框标记对象来完成的。

除了图像分类之外,我们还需要指定图像中对象的位置。这是通过定义一个边界框来完成的。边界框通常由中心(bx,by)、矩形高度(bh)和矩形宽度(bw)表示。

现在,我们需要在图像中的每个对象的训练数据中定义这四个变数。另外,网络不仅输出图像类别编号(即20%cat [1],70%dog [2],10%tiger [3])的概率,而且还输出上面定义边界框的四个变数目的。

通过提供边界框点(中心、宽度、高度),我们的模型通过给我们更多的图像内容细节来输出、预测更多的信息。

不难想象,在图像训练数据中加入更多的点可以让我们更深入地了解图像。例如,在人脸(即嘴巴、眼睛)上放置点可以告诉我们这个人在微笑、哭泣等等。

对象检测

我们甚至可以更进一步,不仅在图像中定位一个对象,而且还可以定位多个对象。这导致我们进行对象检测。

尽管结构没有太大改变,但是这里的问题变得更加困难,因为我们需要更多的数据准备(多边界框)。原则上,我们只是把图像分成更小的矩形,对于每个矩形,我们有相同的额外的五个变数,我们已经看到了Pc,(bx,by),bh,bw和正常预测概率(20%cat [1 ],70%的狗[2])。稍后我们会看到更多细节,但现在让我们假设结构与对象定位相同,区别在于我们有多个这样的结构。

滑动窗口的解决方案

这是一个相当直观的解决方案,你可以自己提出。这个想法是,我们不是使用一般的汽车图像,而是尽可能多地剪裁,使图像只包含汽车。

我们训练类似于VGG-16的卷积神经网络或任何其他具有裁剪图像的深度网络。

这样做的效果很好,但是这个模型只是用来检测只有汽车的图像,所以在真实图像上会有麻烦,因为它们不仅仅包含一辆汽车(比如树、人、标志)。

另外,真实图像的尺寸也更大。

为了克服这些问题,我们只能分析一小部分图像,并试图弄清楚那部分是否有汽车。更确切地说,我们可以用一个滑动的矩形窗口来扫描图像,并且每次我们的模型都可以预测其中是否有汽车。我们来看一个例子:

总之,我们使用一个正常的卷积神经网络(VGG-16)来训练带有不同大小裁剪图像的模型,然后使用矩形扫描物体(汽车)的图像。我们可以看到,通过使用不同大小的矩形,我们可以计算出不同的车辆形状和位置。

这个算法不是那么复杂,而且很有效。但是它有两个缺点。

1.首先是性能。我们需要向这个模型提出很多次的预测。每当一个矩形移动时,我们需要执行这个模型以获得预测,同时我们还需要针对不同的矩形大小重新做一遍。也许解决性能的一个方法是增加矩形的步幅(更大的步幅),但是,我们可能无法检测到一些对象。在过去,模型大多是线性的,并且具有手工设计的特征,所以预测并不昂贵,这个算法用来做得很好。目前,network sizes (VGG-16的参数为1.38亿个),这种算法速度慢,对自动驾驶等实时视频对象检测几乎没有用处。另一种看出算法效率不高的方法是注重如何完成,当移动矩形(右和下)时,许多共享像素不被重用,而是重新重新计算。在接下来的部分中,我们将看到如何使用卷积克服这个问题的最新技术。

2.即使使用不同的边框尺寸,我们也可能无法用边框精确地标记对象。该模型可能不会输出非常精确的边界框。例如,盒子可以仅包括对象的一部分。接下来的部分将探讨YOLO(你只看一次)算法,这为我们解决了这个问题。

卷积滑动窗口的解决方案

我们看到了滑动窗口如何具有性能问题,因为它不能重用许多已经计算的值。每次移动窗口时,我们都必须执行所有像素的所有模型(数百万个参数)才能获得预测结果。实际上,大多数计算可以通过引入卷积来重用。

将完全连接的层转化为卷积

我们在之前的文章中看到,最终,图像分类体系结构(不论其大小和配置如何)为不同数量的层提供完全连接的神经网络,并根据类输出多个预测。

为简单起见,我们将以一个较小的网络模型为例,但对于任何卷积网络(VGG-16,AlexNet)来说,相同的逻辑是有效的。

这个简单的网络作为输入大小为32×32×3的彩色图像(RGB)。它使用相同的卷积(这个卷积首先两个维度,宽度X高度,不变)3×3×64得到一个32 X 32 X 64(注意第三维与卷积矩阵64相同,通常从输入增加)。它使用最大的合并层来减少宽度和高度,并保持第三维不变(16×16×64)。之后,我们提供一个完全连接的神经网络,每个神经网络有两个隐藏层,每层有256和128个神经元。最后,我们输出五个类的概率(通常使用soft-max)。

让我们看看如何用卷积层代替完全连接的层,同时保持数学效果相同(输入16×16×64的线性函数)。

我们用卷积滤波器替换了完全连接的层。实际上,16×16×256卷积滤波器是16×16×64×256矩阵(多个滤波器),因为第三维始终与输入的第三维相同。为了简单起见,我们将其称为16 x 16 x 256.这意味着实际上,相当于一个完全连接的层,因为输出1 x 1 x 256的每个元素都是输入16 x 16的每个元素的线性函数X 64。

我们为什么要把完全连接(FC)层转换成卷积层呢?这是因为这会给我们在复制输出的方式上更多的灵活性。使用FC,你将始终具有相同的输出大小,即类数。

卷积滑动窗口

为了看到用卷积滤波器替换FC后面的想法,我们将需要输入比32×32×3的原始输入图像更大的输入图像。让我们取一个36×36×3的输入图像:

此图像有四列四行(绿色36 X 36),比原始图像(蓝色32 X 32)多。如果我们使用步幅= 2和FC的滑动窗口,则需要将原始图像大小移动九次以覆盖所有内容(三个移动用黑色矩形表示)。因此,也要将模型执行九次。

现在让我们尝试将这个新的更大的矩阵作为输入应用于我们的仅具有卷积层的新模型。

正如我们所看到的,与FC相比,输出从1X1X5变化到3X3X5,其中输出总是1X1X5。回想一下,我们必须移动滑动窗口九次以覆盖所有图像。等等,这不是等于3X3?实际上,这些3X3单元分别表示滑动窗口的1X1X5类概率预测结果!

这是一种非常先进的技术,因为我们只需一次就可以立即获得全部九个结果,而无需多次执行数百万个参数的模型。

YOLO(你只看一次)

虽然我们已经通过引入卷积滑动窗口来解决性能问题,但是我们的模型仍然可能不能输出非常精确的边界框,即使具有几个边界框大小。现在,我们来看看YOLO如何解决这个问题。

首先,我们通常在每个图像上标记我们想要检测的对象。每个对象都由一个带有四个变数的边界框标记,记住对象的中心(bx,by)、矩形高度(bh)、矩形宽度(bw)。之后,每个图像被分割成更少数量的矩形(框),通常是19×19矩形,但是为了简单起见,采用8×9。

边界框(红色)和对象可以是几个框(蓝色)的一部分,所以我们只将对象和边界框分配给拥有对象中心的框(黄色框)。我们训练的模型有四个额外的变数(除了告诉对象是一辆车; bx,by,bh和bw),并将它们分配给拥有对象中心(bx,by)的框。在用这个标记的数据训练神经网络之后,我们让模型预测四个变数(除了对象是什么)的值或边界框。

我们不是使用预定义的边界框大小进行扫描,而是试图拟合对象,让模型学习如何用边界框标记对象;因此,边框现在是灵活的(被学习)。这样,边界框的精度就高得多,灵活多了。

现在可以看看我们如何表示输出,除了像1车,2步行者这样的类以外,还有四个变数(bx,by,bh,bw)。实际上,还增加了另一个变数Pc,它只是告诉我们图像是否有我们想要检测的对象。

·Pc = 1(红色)意味着至少有一个对象,所以值得看看概率和边界框。

·Pc = 0(红色)表示图像没有我们想要的对象,所以我们不关心概率或边界框。

边界框规范

我们需要以一些特定的方式标记我们的训练数据,以使YOLO算法正常工作。 YOLO V2格式要求边界框尺寸是bx,by和bh,bw相对于原始图像的宽度和高度。假设我们有一个图像300 X 400,边界框的尺寸是Bwidth = 30,Bheight = 15,Bx = 150,By = 80。这必须转化为

Bw = 30/300,Bh = 15/400,Bx = 150/300,By = 80/400。

这篇文章展示了如何使用BBox标签工具来标记数据,而且不那么痛苦。 YOLO V2格式的工具标签边界框有点不同(放弃左边的点和右下角的点),但是转换是相当直接的任务。

除了YOLO如何要求对训练数据进行标记之外,在内部,预测有点不同。

YOLO预测的边界框是相对于拥有对象中心的框(黄色)定义的。框的左上角从(0,0)开始,在右下角(1,1)。中心(bx,by)肯定在范围0-1(Sigmoid函数确定),因为点位于方框内。 bh,bw是按w和h值(黄色)的比例计算的,所以数值可以大于1(指数用于正值)。在图中,我们看到边框的宽度bw几乎是宽度w的1.8倍。同样,bh大约是盒子高度h的1.6倍。

经过预测,我们可以看到预测框与开始标记的实际边界框相交多少。基本上,我们试图最大化它们之间的交集,所以理想情况下,预测的边界框与标记的边界框完全相交。

原则上就是这样!你可以使用边界框(bx,by,bh,bw)提供更专门的标记数据,分割图像并分配给包含中心的框(负责检测对象),使用卷积滑动窗口网络进行训练,并预测对象及其位置。

还有两个问题

虽然在这篇文章中我们不会做出非常详细的解释,但实际上还有两个小问题需要解决:

首先,即使在训练时间,对象被分配到一个盒子(包含对象中心的盒子),在测试时间(当预测时),几个盒子(黄色)可能认为它们具有对象的中心),因此为同一个对象定义自己的边界框。这是用非最大抑制算法解决的。目前,Deeplearning4j没有提供实现,所以可以在GitHub

(https://github.com/klevis/AutonomousDriving/blob/master/src/main/java/ramo/klevis/TinyYoloPrediction.java)上找到一个简单的实现(removeObjectsIntersectingWithMax)。它的作用是首先选择最大Pc概率的盒子作为预测(它不仅具有1或0的值,而是可以在0-1范围内)。然后,删除每个与该框相交超过一定阈值的框。它再次启动相同的逻辑,直到没有更多的边界框离开。

其次,由于我们预测多个物体(汽车,行人,交通信号灯等),所以可能发生两个或更多物体的中心是一个箱子。这些情况通过引入锚箱来解决。有了锚箱,我们选择了几个边界框的形状,发现更多的是用于我们想要检测的对象。 YOLO V2文件是用k-means算法做的,但也可以手动完成。之后,我们修改输出以包含我们先前看到的相同结构(Pc,bx,by,bh,bw,C1,C2 ...)。所以,我们现在可能有这样的事情:

应用程序

训练深度网络需要付出很大的努力,并且需要有意义的数据和处理能力。这一次,我们不打算修改架构和训练不同的数据,而是直接使用网络。

我们要用Tiny YOLO;从网站引用:

Tiny YOLO基于Darknet参考网络,速度比正常的YOLO模型快得多,但不太准确。要使用有关VOC训练的版本:

好吧,虽然不够完美,但是他的速度的确够快。 在GPU上,运行速度> 200 FPS。

Deeplearning4j 0.9.1的当前发行版本不提供TinyYOLO,但是0.9.2-SNAPSHOT。 所以首先,我们需要告诉Maven载入SNAPSHOT:

然后,我们准备用相当短的代码载入模型:

prepareLabels只是使用数据集PASCAL VOC中用于训练模型的标签。 随意运行preTrained.summary()来查看模型体系结构的详细信息。

视频帧是采用带CarVideoDetection的JavaCV进行视频检测:

所以代码是从视频获取帧并传递到TinyYOLO预训练模型。 从那里,图像帧首先被缩放到416 X 416 X 3(RGB),然后被赋予TinyYOLO用于预测和标记边界框:

预测完成后,我们应该准备好边界框尺寸的预测值。我们已经实现了非最大抑制(removeObjectsIntersectingWithMax),因为正如我们所提到的,在测试的时候,YOLO预测每个对象有多个边界框。而不是使用bx,by,bh和bw,我们将使用左下角和右下角。 gridWidth和gridHeight是我们分割图像的小框的数量,在我们的例子中是13X13。 w,h是原始图像帧尺寸。

之后,使用另一个线程(在播放视频的旁边),我们更新视频以获取检测到的对象的矩形和标签。

考虑到在CPU上运行,预测非常快(实时的),在GPU上,我们将有更好的实时检测。

正在运行的应用程序

应用程序可以在没有任何Java知识的情况下下载和执行,尽管Java必须安装在你的计算机上。看完文章是不是收获颇丰,现在可以试试你自己的视频。

只需简单执行RUN类,就可以从源代码运行。或者,如果你不想用IDE打开它,只需运行mvn clean install exec:java。

运行应用程序后,您应该能够看到下面的视图:

2022-05-09 12:51:09
0