目标跟踪入门:使用OpenCV实现质心跟踪
存储该对象的边界框坐标的质心
然后我们可以返回到步骤2,重复执行。
上图演示了使用最小欧几里得距离关联现有对象 ID,然后注册新对象的过程。
步骤5:注销旧对象
当旧的对象超出范围时,注销旧对象、
===============================================================
ObjectTracking
├── Model
│ ├── init.py
│ └── centroidtracker.py
├── object_tracker.py
├── deploy.prototxt
└──res10_300x300_ssd_iter_140000_fp16.caffemodel
===========================================================================
新建 centroidtracker.py,写入代码:
import the necessary packages
from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as np
class CentroidTracker():
def init(self, maxDisappeared=50):
self.nextObjectID = 0
self.objects = OrderedDict()
self.disappeared = OrderedDict()
存储给定对象被允许标记为“消失”的最大连续帧数,直到我们需要从跟踪中注销该对象
self.maxDisappeared = maxDisappeared
导入了所需的包和模块:distance 、 OrderedDict 和 numpy 。
定义类 CentroidTracker 。构造函数接受一个参数maxDisappeared,即给定对象必须丢失/消失的最大连续帧数,如果超过这个数值就将这个对象删除。
四个类变量:
-
nextObjectID :用于为每个对象分配唯一 ID 的计数器。如果一个对象离开帧并且没有返回 maxDisappeared 帧,则将分配一个新的(下一个)对象 ID。
-
objects :使用对象 ID 作为键和质心 (x, y) 坐标作为值的字典。
-
disappeared:保持特定对象 ID(键)已被标记为“丢失”的连续帧数(值)。
-
maxDisappeared :在我们取消注册对象之前,允许将对象标记为“丢失/消失”的连续帧数。
让我们定义负责向我们的跟踪器添加新对象的 register 方法:
def register(self, centroid):
注册对象时,我们使用下一个可用的对象ID来存储质心
self.objects[self.nextObjectID] = centroid
self.disappeared[self.nextObjectID] = 0
self.nextObjectID += 1
def deregister(self, objectID):
要注销注册对象ID,我们从两个字典中都删除了该对象ID
del self.objects[objectID]
del self.disappeared[objectID]
register 方法接受一个质心,然后使用下一个可用的对象 ID 将其添加到对象字典中。
对象消失的次数在消失字典中初始化为 0。
最后,我们增加 nextObjectID,这样如果一个新对象进入视野,它将与一个唯一的 ID 相关联。
与我们的注册方法类似,我们也需要一个注销方法。
deregister 方法分别删除对象和消失字典中的 objectID。
质心跟踪器实现的核心位于update方法中
def update(self, rects):
检查输入边界框矩形的列表是否为空
if len(rects) == 0:
遍历任何现有的跟踪对象并将其标记为消失
for objectID in list(self.disappeared.keys()):
self.disappeared[objectID] += 1
如果达到给定对象被标记为丢失的最大连续帧数,请取消注册
if self.disappeared[objectID] > self.maxDisappeared:
self.deregister(objectID)
由于没有质心或跟踪信息要更新,请尽早返回
return self.objects
初始化当前帧的输入质心数组
inputCentroids = np.zeros((len(rects), 2), dtype=“int”)
在边界框矩形上循环
for (i, (startX, startY, endX, endY)) in enumerate(rects):
use the bounding box coordinates to derive the centroid
cX = int((startX + endX) / 2.0)
cY = int((startY + endY) / 2.0)
inputCentroids[i] = (cX, cY)
如果我们当前未跟踪任何对象,请输入输入质心并注册每个质心
if len(self.objects) == 0:
for i in range(0, len(inputCentroids)):
self.register(inputCentroids[i])
否则,当前正在跟踪对象,因此我们需要尝试将输入质心与现有对象质心进行匹配
else:
抓取一组对象ID和相应的质心
objectIDs = list(self.objects.keys())
objectCentroids = list(self.objects.values())
分别计算每对对象质心和输入质心之间的距离-我们的目标是将输入质心与现有对象质心匹配
D = dist.cdist(np.array(objectCentroids), inputCentroids)
为了执行此匹配,我们必须(1)在每行中找到最小值,然后(2)根据行索引的最小值对行索引进行排序,以使具有最小值的行位于索引列表的* front *处
rows = D.min(axis=1).argsort()
接下来,我们在列上执行类似的过程,方法是在每一列中找到最小值,然后使用先前计算的行索引列表进行排序
cols = D.argmin(axis=1)[rows]
为了确定是否需要更新,注册或注销对象,我们需要跟踪已经检查过的行索引和列索引
usedRows = set()
usedCols = set()
循环遍历(行,列)索引元组的组合
for (row, col) in zip(rows, cols):
如果我们之前已经检查过行或列的值,请忽略它
if row in usedRows or col in usedCols:
continue
否则,获取当前行的对象ID,设置其新的质心,然后重置消失的计数器
objectID = objectIDs[row]
self.objects[objectID] = inputCentroids[col]
self.disappeared[objectID] = 0
表示我们已经分别检查了行索引和列索引
usedRows.add(row)
usedCols.add(col)
计算我们尚未检查的行和列索引
unusedRows = set(range(0, D.shape[0])).difference(usedRows)
unusedCols = set(range(0, D.shape[1])).difference(usedCols)
如果对象质心的数量等于或大于输入质心的数量
我们需要检查一下其中的某些对象是否已潜在消失
if D.shape[0] >= D.shape[1]:
loop over the unused row indexes
for row in unusedRows:
抓取相应行索引的对象ID并增加消失的计数器
objectID = objectIDs[row]
self.disappeared[objectID] += 1
检查是否已将该对象标记为“消失”的连续帧数以用于注销该对象的手令
if self.disappeared[objectID] > self.maxDisappeared:
self.deregister(objectID)
否则,如果输入质心的数量大于现有对象质心的数量,我们需要将每个新的输入质心注册为可跟踪对象
else:
for col in unusedCols:
self.register(inputCentroids[col])
return the set of trackable objects
return self.objects
update方法接受边界框矩形列表。 rects 参数的格式假定为具有以下结构的元组: (startX, startY, endX, endY) 。
如果没有检测到,我们将遍历所有对象 ID 并增加它们的消失计数。 我们还将检查是否已达到给定对象被标记为丢失的最大连续帧数。 如果是,我们需要将其从我们的跟踪系统中删除。 由于没有要更新的跟踪信息,我们将提前返回。
否则,我们将初始化一个 NumPy 数组来存储每个 rect 的质心。 然后,我们遍历边界框矩形并计算质心并将其存储在 inputCentroids 列表中。 如果没有我们正在跟踪的对象,我们将注册每个新对象。
否则,我们需要根据最小化它们之间的欧几里得距离的质心位置更新任何现有对象 (x, y) 坐标。
接下来我们在else中计算所有 objectCentroids 和 inputCentroids 对之间的欧几里德距离:
获取 objectID 和 objectCentroid 值。
计算每对现有对象质心和新输入质心之间的距离。距离图 D 的输出 NumPy 数组形状将是 (# of object centroids, # of input centroids) 。要执行匹配,我们必须 (1) 找到每行中的最小值,以及 (2) 根据最小值对行索引进行排序。我们对列执行非常相似的过程,在每列中找到最小值,然后根据已排序的行对它们进行排序。我们的目标是在列表的前面具有最小对应距离的索引值。
下一步是使用距离来查看是否可以关联对象 ID:
初始化两个集合以确定我们已经使用了哪些行和列索引。
然后遍历 (row, col) 索引元组的组合以更新我们的对象质心:
如果我们已经使用了此行或列索引,请忽略它并继续循环。
否则,我们找到了一个输入质心:
- 到现有质心的欧几里得距离最小
- 并且没有与任何其他对象匹配
- 在这种情况下,我们更新对象质心(第 113-115 行)并确保将 row 和 col 添加到它们各自的 usedRows 和 usedCols 集合中。
在我们的 usedRows + usedCols 集合中可能有我们尚未检查的索引:
所以我们必须确定哪些质心索引我们还没有检查过,并将它们存储在两个新的方便集合(unusedRows 和 usedCols)中。
最终检查会处理任何丢失或可能消失的对象:
如果对象质心的数量大于或等于输入质心的数量:
我们需要循环遍历未使用的行索引来验证这些对象是否丢失或消失。
在循环中,我们将:
- 增加他们在字典中消失的次数。
- 检查消失计数是否超过 maxDisappeared 阈值,如果是,我们将注销该对象。
否则,输入质心的数量大于现有对象质心的数量,我们有新的对象要注册和跟踪.
循环遍历未使用的Cols 索引并注册每个新质心。 最后,我们将可跟踪对象集返回给调用方法。
=======================================================================
已经实现了 CentroidTracker 类,让我们将其与对象跟踪驱动程序脚本一起使用。
驱动程序脚本是您可以使用自己喜欢的对象检测器的地方,前提是它会生成一组边界框。 这可能是 Haar Cascade、HOG + 线性 SVM、YOLO、SSD、Faster R-CNN 等。
在这个脚本中,需要实现的功能:
1、使用实时 VideoStream 对象从网络摄像头中抓取帧
2、加载并使用 OpenCV 的深度学习人脸检测器
3、实例化 CentroidTracker 并使用它来跟踪视频流中的人脸对象并显示结果。
新建 object_tracker.py 插入代码:
from Model.centroidtracker import CentroidTracker
import numpy as np
import imutils
import time
import cv2
定义最低置信度
confidence_t=0.5
初始化质心跟踪器和框架尺寸
ct = CentroidTracker()
(H, W) = (None, None)
加载检测人脸的模型
print(“[INFO] loading model…”)
net = cv2.dnn.readNetFromCaffe(“deploy.prototxt”, “res10_300x300_ssd_iter_140000_fp16.caffemodel”)
初始化视频流并允许相机传感器预热
print(“[INFO] starting video stream…”)
vs = cv2.VideoCapture(‘11.mp4’)
time.sleep(2.0)
fps = 30 #保存视频的FPS,可以适当调整
size=(600,1066)#宽高,根据frame的宽和高确定。
fourcc = cv2.VideoWriter_fourcc(*“mp4v”)
videoWriter = cv2.VideoWriter(‘3.mp4’,fourcc,fps,size)#最后一个是保存图片的尺寸
导入需要的包。
定义最低置信度。
加载人脸检测模型
初始化视频流或者相机(设置成相机对应的ID就会启动相机)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。






既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)
**
[外链图片转存中…(img-upnwwttJ-1713245924277)]
[外链图片转存中…(img-z51JdVC3-1713245924278)]
[外链图片转存中…(img-H1Lssn5k-1713245924278)]
[外链图片转存中…(img-TQof2XzN-1713245924279)]


既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)

更多推荐
所有评论(0)