opencv 计数,基于opencv车辆检测计数
本文主要介绍如何使用PythonOpenCV创建一个更精确的staff计数器。文章中的示例代码有详细的解释。感兴趣的朋友来边肖学习吧。
00-1010 1.了解对象检测和跟踪2。组合目标检测和跟踪3。项目结构4。组合目标跟踪算法5。创建可追踪对象6。用OpenCV Python实现我们的五线谱计数器7。完成代码people _ counter . pycentroidtracker . pytrackableobject . py8 .运行结。9.提高我们的员工柜台申请奖金。在本教程中,您将学习如何使用OpenCV和Python构建一个人员计数器。使用OpenCV,我们将实时计算进出百货公司的人数。
在今天博文的第一部分,我们将讨论如何使用这两者来创建一个更准确的人员计数器。之后我们会检查项目的目录结构,然后实现整个人员清点项目。最后,我们将检查将OpenCV人数应用于实际视频的结果。
目录
在继续本教程的其余部分之前,您必须了解对象检测和对象跟踪之间的根本区别。
当我们应用对象检测时,我们确定对象在图像/帧中的位置。与目标跟踪算法相比,目标检测器通常计算量更大,因此速度更慢。目标检测算法的例子包括Haar cascade、HOG线性支持向量机(HOG线性SVM)和基于深度学习的目标检测器,如更快的R-CNN、YOLO和单次检测器(SSD)。
另一方面,对象跟踪器将接受图像中对象位置的输入(x,y)坐标,并将:
1.为该特定对象分配一个唯一的ID
2.当对象在视频流中移动时跟踪它,并根据帧的各种属性(梯度、光流等)预测新对象在下一帧中的位置。)
对象跟踪算法的例子包括MedianFlow、MOSSE、GOTURN、核相关滤波器和判别相关滤波器。
1.了解对象检测与对象跟踪
高精度目标跟踪器将目标检测和目标跟踪的概念结合在一个算法中,通常分为两个阶段:
1.阶段1 检测:在检测阶段,我们正在运行一个计算成本更高的对象跟踪器,以(1)检测是否有新的对象进入我们的视野,(2)看看我们能否在跟踪阶段找到“丢失”的对象。对于每个检测到的对象,我们用新的边界框坐标创建或更新对象跟踪器。因为我们的目标检测器的计算成本较高,所以我们每n帧才运行一次这个阶段。
2.阶段2 跟踪:当我们不在“探测”阶段时,我们是在“跟踪”阶段。对于我们检测到的每个对象,我们创建一个对象跟踪器来跟踪对象在帧中的运动。我们的目标跟踪器应该比目标探测器更快更有效。我们将继续跟踪,直到我们到达第n帧,然后重新运行我们的目标检测器。然后重复整个过程。
这种混合方法的优点是,我们可以应用高度精确的对象检测方法,而没有太多的计算负担。我们将实施这样一个跟踪系统来建立我们的员工柜台。
2.结合对象检测和对象跟踪
我们来回顾一下今天博文的项目结构。获得代码后,可以使用tree命令检查目录结构:
两个最重要的目录:
1.pyimagesearch/:这个模块包含质心跟踪算法。“组合目标跟踪算法”中介绍了质心跟踪算法。
2.mobilenet_ssd/:包含Caffe深度学习模型文件。
今天项目的核心包含在people_counter.py脚本3354中,这是我们将花费大部分时间的地方。今天,我们还将回顾trackableobject.py脚本。
3.项目结构
为了实现我们的人员计数器,我们将同时使用OpenCV和dlib。我们使用OpenCV作为标准
计算机视觉/图像处理功能,以及用于人数统计的深度学习对象检测器。
然后我们将使用 dlib 来实现相关过滤器。我们也可以在这里使用 OpenCV;但是,对于这个项目,dlib 对象跟踪实现更容易使用。
除了 dlib 的对象跟踪实现,我们还将使用质心跟踪实现。回顾整个质心跟踪算法超出了这篇博文的范围,但我在下面提供了一个简短的概述。
在步骤#1,我们接受一组边界框并计算它们对应的质心(即边界框的中心):
要使用 Python 通过质心脚本构建简单的对象跟踪,第一步是接受边界框坐标并使用它们来计算质心。
边界框本身可以由以下任一方式提供:
1.目标检测器(如 HOG + Linear SVM、Faster R-CNN、SSDs 等)
2.或对象跟踪器(例如相关过滤器)
在上图中,您可以看到我们在算法的初始迭代中有两个对象要跟踪。
在步骤#2中,我们计算任何新质心(黄色)和现有质心(紫色)之间的欧几里得距离:
此图像中存在三个对象。我们需要计算每对原始质心(紫色)和新质心(黄色)之间的欧几里得距离。
质心跟踪算法假设它们之间具有最小欧几里德距离的质心对必须是相同的对象 ID。
在上面的示例图像中,我们有两个现有的质心(紫色)和三个新的质心(黄色),这意味着已经检测到一个新对象(因为与旧质心相比,还有一个新质心)。
然后箭头表示计算所有紫色质心和所有黄色质心之间的欧几里得距离。一旦我们有了欧几里得距离,我们就会在步骤#3 中尝试关联对象 ID:
您可以看到我们的质心跟踪器已选择关联使它们各自的欧几里得距离最小化的质心。但是左下角的点呢?它没有与任何东西相关联——我们该怎么办? 要回答这个问题,我们需要执行步骤#4,注册新对象:
注册意味着我们通过以下方式将新对象添加到我们的跟踪对象列表中:
1.为其分配一个新的对象 ID
2.存储新对象的边界框坐标的质心
如果对象丢失或离开视野,我们可以简单地取消注册对象(步骤#5)。
5.创建可追踪对象
为了跟踪和计算视频流中的对象,我们需要一种简单的方法来存储有关对象本身的信息,包括:
对象ID
以前的质心(所以我们可以很容易地计算出物体移动的方向)
对象是否已被计数
为了实现所有这些目标,我们可以定义一个 TrackableObject 实例——打开 trackableobject.py 文件并插入以下代码:
class TrackableObject:
TrackableObject 构造函数接受 objectID + centroid 并存储它们。 centroids 变量是一个列表,因为它将包含对象的质心位置历史记录。 构造函数还将 counted 初始化为 False ,表示该对象还没有被计数。
6.使用 OpenCV + Python 实现我们的人员计数器
# import the necessary packages
我们首先导入必要的包:
从 pyimagesearch 模块,我们导入自定义的 CentroidTracker 和 TrackableObject 类。
imutils.video 中的 VideoStream 和 FPS 模块将帮助我们使用网络摄像头并计算估计的每秒帧数 (FPS) 吞吐率。
我们需要 imutils 的 OpenCV 便利功能。
dlib 库将用于其相关跟踪器实现。
OpenCV 将用于深度神经网络推理、打开视频文件、写入视频文件以及在我们的屏幕上显示输出帧。
现在所有工具都触手可及,让我们解析命令行参数:
# construct the argument parse and parse the arguments
我们有六个命令行参数,它们允许我们在运行时从终端将信息传递给我们的人员计数器脚本:
--prototxt :Caffe 部署 prototxt 文件的路径。
--model :Caffe 预训练 CNN 模型的路径。
--input : 可选的输入视频文件路径。如果未指定路径,将使用您的网络摄像头。
--output :可选的输出视频路径。如果未指定路径,则不会录制视频。
--confidence :默认值为 0.4 ,这是有助于过滤掉弱检测的最小概率阈值。
--skip-frames :在跟踪对象上再次运行我们的 DNN 检测器之前要跳过的帧数。请记住,对象检测的计算成本很高,但它确实有助于我们的跟踪器重新评估帧中的
对象。默认情况下,我们在使用 OpenCV DNN 模块和我们的 CNN 单次检测器模型检测对象之间跳过 30 帧。
现在我们的脚本可以在运行时动态处理命令行参数,让我们准备我们的 SSD:
# initialize the list of class labels MobileNet SSD was trained to detect
首先,我们将初始化 CLASSES——SSD 支持的类列表。我们只对人类感兴趣,但您也可以计算其他移动对象。
我们加载用于检测对象的预训练 MobileNet SSD(但同样,我们只对检测和跟踪人感兴趣,而不是任何其他类)。
我们可以初始化我们的视频流:
# if a video path was not supplied, grab a reference to the webcam
首先,我们处理使用网络摄像头视频流的情况。否则,我们将从视频文件中捕获帧。在开始循环帧之前,我们还有一些初始化要执行:
# initialize the video writer (well instantiate later if need be)
其余的初始化包括:
writer:我们的视频写入器。如果我们正在写入视频,我们稍后会实例化这个对象。
W 和 H:我们的帧尺寸。我们需要将这些插入到 cv2.VideoWriter 中。
ct:我们的 CentroidTracker。
trackers :存储 dlib 相关跟踪器的列表。
trackableObjects :将 objectID 映射到 TrackableObject 的字典。
totalFrames :处理的帧总数。
totalDown 和 totalUp :向下或向上移动的对象/人的总数。
fps :我们用于基准测试的每秒帧数估计器。
现在我们所有的初始化都处理好了,让我们循环传入的帧:
# loop over frames from the video stream
我们开始循环。在循环的顶部,我们抓取下一帧。如果我们已经到达视频的结尾,我们将跳出循环。
帧进行预处理。这包括调整大小和交换颜色通道,因为 dlib 需要 rgb 图像。我们为视频编写器获取帧的尺寸。 如果通过命令行参数提供了输出路径,我们将从那里实例化视频编写器。
现在让我们使用 SSD检测人:
# initialize the current status along with our list of bounding
我们将状态初始化为Waiting。可能的状态包括:
Waiting:在这种状态下,我们正在等待检测和跟踪人员。
Detecting:我们正在使用 MobileNet SSD 检测人员。
Tracking:人们在帧中被跟踪,我们正在计算 totalUp 和 totalDown 。
我们的 rects 列表将通过检测或跟踪来填充。我们继续初始化rects 。
重要的是要了解深度学习对象检测器的计算成本非常高,尤其是当您在 CPU 上运行它们时。
为了避免在每一帧上运行我们的目标检测器,并加快我们的跟踪管道,我们将跳过 N 帧(由命令行参数设置 --skip-frames ,其中 30 是默认值)。只有每 N 帧,我们才会使用 SSD 进行对象检测。否则,我们将只是跟踪中间的移动对象。
使用模运算符,我们确保每 N 帧执行一次 if 语句中的代码。 进入if语句后,我们会将状态更新为Detecting。 然后我们初始化新的跟踪器列表。
接下来,我们将通过对象检测进行推理。我们首先从图像中创建一个 blob,然后将该 blob 通过网络传递以获得检测。 现在我们将遍历每个检测,希望找到属于person类的对象:
# loop over the detections
循环检测,我们继续获取置信度并过滤掉那些不属于人类的结果。
现在我们可以为每个人计算一个边界框并开始相关性跟踪:
# compute the (x, y)-coordinates of the bounding box
计算我们的box。 然后实例化我们的 dlib 相关跟踪器,然后将对象的边界框坐标传递给 dlib.rectangle,将结果存储为 rect。 随后,我们开始跟踪,并将跟踪器附加到跟踪器列表中。 这是我们每 N 个跳帧执行的所有操作的封装! 让我们处理在 else 块中进行跟踪的典型操作:
# otherwise, we should utilize our object *trackers* rather than
大多数时候,并没有发生在跳帧倍数上。在此期间,我们将利用跟踪器来跟踪对象,而不是应用检测。 我们开始遍历可用跟踪器。 我们继续将状态更新为Tracking并获取对象位置。 我们提取位置坐标,然后在我们的 rects 列表中填充信息。 现在让我们画一条水平可视化线(人们必须穿过它才能被跟踪)并使用质心跟踪器来更新我们的对象质心:
# draw a horizontal line in the center of the frame -- once an
我们画一条水平线,我们将用它来可视化人们越过——一旦人们越过这条线,我们将增加各自的计数器 然后,我们利用 CentroidTracker 实例化来接受 rects 列表,无论它们是通过对象检测还是对象跟踪生成的。我们的质心跟踪器会将对象 ID 与对象位置相关联。 在下一个代码块中,我们将回顾一个人在帧中向上或向下移动的逻辑:
# loop over the tracked objects
我们首先遍历更新后的对象id的边界框坐标。我们尝试为当前的objectID获取TrackableObject。如果objectID的TrackableObject不存在,我们就创建一个。否则,已经存在一个 TrackableObject ,所以我们需要弄清楚对象(人)是向上还是向下移动。
为此,我们获取给定对象之前所有质心位置的y坐标值。然后,通过取当前质心位置与之前所有质心位置的平均值之间的差来计算方向。
我们取均值的原因是为了确保我们的方向跟踪更稳定。如果我们只存储这个人之前的质心位置,我们就有可能出现错误的方向计数。记住,目标检测和目标跟踪算法不是魔术——有时它们会预测出可能稍微偏离你预期的边界盒;因此,通过取均值,我们可以让我们的人计算得更准确。
如果TrackableObject还没有被计数,我们需要确定它是否已经准备好被计数,通过:
1.检查direction是否为负(表示对象向上移动)并且质心在中心线上方。在这种情况下,我们增加 totalUp。
2.或者检查direction是否为正(表示物体正在向下移动)且质心在中心线以下。如果这是真的,我们增加totalDown。
最后,我们将TrackableObject存储在trackableObjects字典中,这样我们就可以在捕获下一帧时获取并更新它。
接下来的三个代码块处理:
显示(绘图并向帧写入文本)
将帧写入磁盘上的视频文件(如果存在--output命令行参数)
捕获按键
清理
首先,我们将在框架上绘制一些信息以进行可视化:
# draw both the ID of the object and the centroid of the
在这里,我们在帧上覆盖以下数据:
ObjectID :每个对象的ID。
centroid :对象的中心将由一个点表示,该点是通过填充一个圆圈而创建的。
info : 包括 totalUp 、 totalDown 和 status
然后我们将把帧写入视频文件(如果需要的话)并处理按键:
# check to see if we should write the frame to disk
在这个代码块中我们:
如果需要,将帧写入输出视频文件
显示帧并处理按键。如果q被按下,我们将跳出帧处理循环。
更新我们的fps计数器
现在是时候清理了:
# stop the timer and display FPS information
为了完成脚本,我们向终端显示 FPS 信息,释放所有指针,并关闭所有打开的窗口。
7.完整代码
people_counter.py
from pyimagesearch.centroidtracker import CentroidTracker
centroidtracker.py
(1)质心跟踪器是最可靠的跟踪器之一。
(2)为了简单起见,质心跟踪器计算包围框的质心。
(3)也就是说,边界框是图像中对象的(x, y)坐标。
(4)一旦我们的SSD获得了坐标,跟踪器就会计算包围框的质心(中心)。换句话说,就是物体的中心。
(5)然后为每一个被检测到的特定对象分配一个唯一的ID,用于跟踪帧序列。
from scipy.spatial import distance as dist
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。