智慧交通day02-车流量检测实现10:多目标追踪实现

在这里我们主要实现了一个多目标跟踪器,管理多个卡尔曼滤波器对象,主要包括以下内容:

  • 初始化:最大检测数,目标未被检测的最大帧数
  • 目标跟踪结果的更新,即跟踪成功和失败的目标的更新

  • 初始化

     def __init__(self, max_age=1, min_hits=3):"""初始化:设置SORT算法的关键参数       """# 最大检测数:目标未被检测到的帧数,超过之后会被删 self.max_age = max_age# 目标命中的最小次数,小于该次数不返回self.min_hits = min_hits  # 卡尔曼跟踪器self.trackers = []  # 帧计数self.frame_count = 0

1、目标跟踪结果的更新:

该方法实现了SORT算法,输入是当前帧中所有物体的检测框的集合,包括目标的score,输出是当前帧标的跟踪框集合,包括目标的跟踪的id要求是即使检测框为空,也必须对每一帧调用此方法,返回一个类似的输出数组,最后一列是目标对像的id。需要注意的是,返回的目标对象数量可能与检测框的数量不同.

    def update(self, dets):self.frame_count += 1# 在当前帧逐个预测轨迹位置,记录状态异常的跟踪器索引# 根据当前所有的卡尔曼跟踪器个数(即上一帧中跟踪的目标个数)创建二维数组:行号为卡尔曼滤波器的标识索引,列向量为跟踪框的位置和IDtrks = np.zeros((len(self.trackers), 5))  # 存储跟踪器的预测to_del = []   # 存储要删除的目标框ret = []    # 存储要返回的追踪目标框# 循环遍历卡尔曼跟踪器列表for t, trk in enumerate(trks):# 使用卡尔曼跟踪器t产生对应目标的跟踪框pos = self.trackers[t].predict()[0]# 遍历完成后,trk中存储了上一帧中跟踪的目标的预测跟踪框trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]# 如果跟踪框中包含空值则将该跟踪框添加到要删除的列表中if np.any(np.isnan(pos)):to_del.append(t)# numpy.ma.masked_invalid 屏蔽出现无效值的数组(NaN 或 inf)# numpy.ma.compress_rows 压缩包含掩码值的2-D 数组的整行,将包含掩码值的整行去除# trks中存储了上一帧中跟踪的目标并且在当前帧中的预测跟踪框trks = np.ma.compress_rows(np.ma.masked_invalid(trks))# 逆向删除异常的跟踪器,防止破坏索引for t in reversed(to_del):self.trackers.pop(t)# 将目标检测框与卡尔曼滤波器预测的跟踪框关联获取跟踪成功的目标,新增的目标,离开画面的目标matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets, trks)# 将跟踪成功的目标框更新到对应的卡尔曼滤波器for t, trk in enumerate(self.trackers):if t not in unmatched_trks:d = matched[np.where(matched[:, 1] == t)[0], 0]# 使用观测的边界框更新状态向量trk.update(dets[d, :][0])# 为新增的目标创建新的卡尔曼滤波器对象进行跟踪for i in unmatched_dets:trk = KalmanBoxTracker(dets[i, :])self.trackers.append(trk)# 自后向前遍历,仅返回在当前帧出现且命中周期大于self.min_hits(除非跟踪刚开始)的跟踪结果;如果未命中时间大于self.max_age则删除跟踪器。# hit_streak忽略目标初始的若干帧i = len(self.trackers)for trk in reversed(self.trackers):# 返回当前边界框的估计值d = trk.get_state()[0]# 跟踪成功目标的box与id放入ret列表中if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):ret.append(np.concatenate((d, [trk.id + 1])).reshape(1, -1))  # +1 as MOT benchmark requires positivei -= 1# 跟踪失败或离开画面的目标从卡尔曼跟踪器中删除if trk.time_since_update > self.max_age:self.trackers.pop(i)# 返回当前画面中所有目标的box与id,以二维矩阵形式返回if len(ret) > 0:return np.concatenate(ret)return np.empty((0, 5))

我们将上述两个方法封装在一个类中。


总结

了解sort进行多目标跟踪的实现


功能实现

# 1.SORT目标跟踪:
#       1.第一帧刚开始时:对第一帧所有的检测框生成对应的新跟踪框。
#       2.第二帧开始到以后所有帧:
#           上一帧成功跟踪并且保留下来的的跟踪框 在当前帧中 进行新一轮的预测新的跟踪框,
#           并且针对所预测的新跟踪框和当前帧中的检测框进行iou计算和使用匈牙利算法对该两者进行关联匹配,
#           通过上述操作后成功返回跟踪目标成功的跟踪框(即和当前帧中的目标检测框相匹配的跟踪框),
#           并且另外发现了新出现目标的检测框、跟踪目标失败的跟踪框(即目标离开了画面/两者匹配度IOU值小于iou阈值),
#           那么首先使用当前帧中的检测框对“成功关联匹配的跟踪框中的”状态向量进行更新,
#           然后对新增目标的检测框生成对应新的跟踪框,最后把跟踪目标失败的跟踪框从跟踪器链列表中移除出去。
# 2.传入的检测框dets:[检测框的左上角的x/y坐标, 检测框的右下角的x/y坐标, 检测框的预测类别的概率值]
# 3.返回值tracks:
#   当前帧中跟踪目标成功的跟踪框/预测框的集合,包含目标的跟踪的id(也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个)
#   第一种返回值方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度, trk.id] ...]
#   第二种返回值方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, trk.id] ...]
#   d:[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]
#   trk.id:卡尔曼滤波器的个数/目标框的个数,也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个。
#Sort多目标跟踪 管理多个卡尔曼滤波器
class Sort(object):"""Sort 是一个多目标跟踪器的管理类,管理多个 跟踪器链中的多个 KalmanBoxTracker 卡尔曼滤波对象"""#设置Sort算法的参数def __init__(self,max_age = 1,min_hits = 3):"""初始化:设置SORT算法的关键参数:param max_age: 最大检测数:目标未被检测到的帧数,超过之后会被删除:param min_hits: 目标命中的最小次数,小于该次数update函数不返回该目标的KalmanBoxTracker卡尔曼滤波对象""""""max_age:跟踪框的最大连续跟丢帧数。如果当前跟踪框连续N帧大于最大连续跟丢帧数的话,则从跟踪器链中删除该卡尔曼滤波对象的预测框(跟踪框)。min_hits:跟踪框连续成功跟踪到目标的最小次数(目标连续命中的最小次数),也即跟踪框至少需要连续min_hits次成功跟踪到目标。trackers:卡尔曼滤波跟踪器链,存储多个 KalmanBoxTracker 卡尔曼滤波对象frame_count:当前视频经过了多少帧的计数"""# 最大检测数:目标未被检测到的帧数,超过之后会被删set.max_age = max_age# 目标连续命中的最小次数,小于该次数update函数不返回该目标的KalmanBoxTracker卡尔曼滤波对象self.min_hits=min_hits# 卡尔曼滤波跟踪器链,存储多个 KalmanBoxTracker 卡尔曼滤波对象self.trackers = []#帧计数self.frane_count = 0"""update(dets):输入dets:当前帧中yolo所检测出的所有目标的检测框的集合,包含每个目标的score以[[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]形式输入的numpy.arrayx1、y1 代表检测框的左上角坐标;x2、y2代表检测框的右上角坐标;score代表检测框对应预测类别的概率值。输出ret:当前帧中跟踪目标成功的跟踪框/预测框的集合,包含目标的跟踪的id(也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个)第一种返回值方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度, trk.id] ...]第二种返回值方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, trk.id] ...]d:[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]trk.id:卡尔曼滤波器的个数/目标框的个数,也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个。注意:即使检测框为空,也必须对每一帧调用此方法,返回一个类似的输出数组,最后一列是目标对像的id。返回的目标对象数量可能与检测框的数量不同。"""#更新数值def update(self,dets):"""该方法实现了SORT算法,输入是当前帧中所有物体的检测框的集合,包括目标的score,输出是当前帧目标的跟踪框集合,包括目标的跟踪的id要求是即使检测框为空,也必须对每一帧调用此方法,返回一个类似的输出数组,最后一列是目标对像的id注意:返回的目标对象数量可能与检测框的数量不同:param dets:以[[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]形式输入的numpy.array:return:"""""" 每经过一帧,frame_count+=1"""self.frane_count +=1"""1.trackers:上一帧中的跟踪器链(列表),保存的是上一帧中成功跟踪目标的跟踪框,也即上一帧中成功跟踪目标的KalmanBoxTracker卡尔曼滤波对象。2.trks = np.zeros((len(trackers), 5))上一帧中的跟踪器链(列表)中的所有跟踪框(卡尔曼滤波对象)在当前帧中成功进行predict预测新跟踪框后返回的值。所有新跟踪框的左上角的x坐标和y坐标、右下角的x坐标和y坐标、置信度 的一共5个值。1.因为一开始第一帧时,trackers跟踪器链(列表)仍然为空,所以此时的trks初始化如下:np.zeros((0, 5))     输出值:array([], shape=(0, 5), dtype=float64)输出值类型:<class 'numpy.ndarray'>2.np.zeros((len(trackers), 5)) 创建目的:1.用于存储上一帧中的跟踪器链中所有跟踪框(KalmanBoxTracker卡尔曼滤波对象)在当前帧中进行predict预测新跟踪框后返回的值,之所以创建的numpy数组的列数为5,是因为一个跟踪框在当前帧中进行predict预测新跟踪框后返回的值为1行5列的矩阵,返回值分别为新跟踪框的左上角的x坐标和y坐标、右下角的x坐标和y坐标、置信度 的一共5个值。2.如果是在视频的第一帧中,那么因为跟踪器链不存在任何跟踪框(KalmanBoxTracker卡尔曼滤波对象),因此np.zeros((len(trackers), 5))创建的是空列表:array([], shape=(0, 5), dtype=float64)。3.trackers:跟踪器链(列表)1.跟踪器链中存储了上一帧中成功跟踪目标并且在当前帧中的预测框(跟踪框),同时也存储了“为了当前帧中的检测框中的新增目标所创建的”新预测框(新跟踪框),但是同时不存储当前帧中预测跟踪失败的预测框(跟踪框),同时也不存储2.跟踪器链实际就是多个的卡尔曼滤波KalmanBoxTracker自定义类的实例对象组成的列表。每个目标框都有对应的一个卡尔曼滤波器(KalmanBoxTracker实例对象),KalmanBoxTracker类中的实例属性专门负责记录其对应的一个目标框中各种统计参数,并且使用类属性负责记录卡尔曼滤波器的创建个数,增加一个目标框就增加一个卡尔曼滤波器(KalmanBoxTracker实例对象)。把每个卡尔曼滤波器(KalmanBoxTracker实例对象)都存储到跟踪器链(列表)中。"""# 存储跟踪器在当前帧逐个预测轨迹位置,记录状态异常的跟踪器索引# 根据当前所有的卡尔曼跟踪器个数(即上一帧中跟踪的目标个数)创建二维数组:行号为卡尔曼滤波器的标识索引,列向量为跟踪框的位置和IDtrks = np.zeros(len(self.trackers),5)#跟踪器对当前帧的图像预测结果""" to_del:存储“跟踪器链中某个要删除的”KalmanBoxTracker卡尔曼滤波对象的索引 """to_del = []#存储要删除的目标框ret = []#返回的跟踪目标#遍历卡尔曼滤波器中的跟踪框"""for t, trk in enumerate(ndarray类型的trks)t:为从0到列表长度-1的索引值trk:ndarray类型的trks中每个(1, 5)形状的一维数组"""""" 遍历trks 用于存储上一帧中的跟踪器链中所有跟踪框(KalmanBoxTracker卡尔曼滤波对象)在当前帧中进行predict预测新跟踪框后返回的值 """for t,trk in enumerate(trks):""" 上一帧中的跟踪器链中所有跟踪框(KalmanBoxTracker卡尔曼滤波对象)在当前帧中进行predict预测新跟踪框 """#使用卡尔曼跟踪器t产生对应目标的跟踪框,即对目标进行预测pos = self.trackers[t].predict()[0]""" 新跟踪框的左上角的x坐标和y坐标、右下角的x坐标和y坐标、置信度 的一共5个值。trk中存储了上一帧中目标的跟踪框在当前帧中新的跟踪框的信息值。"""# 遍历完成后,trk中存储了上一帧中跟踪的目标的预测结果的跟踪框trk[:] = [pos[0],pos[1],pos[2],pos[3],0]""" 如果预测的新的跟踪框的信息(1行5列一共5个值)中包含空值的话,则将该跟踪框在跟踪器链(列表)中的索引值t放到to_del列表中。使用np.any(np.isnan(pos))即能判断这1行5列一共5个值是否包含空值。后面下一步将会根据to_del列表中保存的跟踪框的索引值到跟踪器链(列表)中将该跟踪框从其中移除出去。"""#若预测结果pos中包含空值,添加到del中if np.any(np.isnan(pos)):to_del.append(t)""" np.ma.masked_invalid(跟踪器链trks矩阵):将会对跟踪器链trks矩阵中出现了NaN或inf的某行进行生成掩码,用于屏蔽出现无效值该整行的跟踪器框。np.ma.compress_rows(包含掩码值的跟踪器链trks矩阵):将包含掩码值的整行从中进行移除出去。最终跟踪器链trks矩阵:只包含“上一帧中的跟踪器链中所有跟踪框在当前帧中成功进行predict预测”的新跟踪框。"""#trks中去除无效值的行,保存根据上一帧结果预测当前帧的内容# numpy.ma.masked_invalid 屏蔽出现无效值的数组(NaN 或 inf)# numpy.ma.compress_rows 压缩包含掩码值的2-D 数组的整行,将包含掩码值的整行去除# trks中存储了上一帧中跟踪的目标并且在当前帧中的预测跟踪框trks = np.ma.compress_rows(np.ma.masked_invalid(trks))"""1.for t in reversed(列表):1.t:列表中的元素值2.要想从List列表中删除任意索引位置的元素的话,必须不能从列表头开始遍历删除元素,必须从列表尾向列表头的方向进行遍历删除元素,因为如果从列表头开始遍历删除元素的话,便会导致后面的元素会移动补充到被删除元素的索引位置上,那么再向后进行遍历时便会出现漏遍历的元素,也即防止破坏索引,因此删除列表中元素时需要从列表尾向列表头的方向进行遍历。2.for t in reversed(to_del)1.t:列表中的元素值2.此处to_del列表中的元素值保存的是trackers跟踪器链(列表)中要删除元素的索引值,因此从to_del列表的列表尾向列表头的方向进行遍历出“trackers跟踪器链(列表)中要删除元素的”索引值。然后使用trackers.pop(t)根据trackers跟踪器链(列表)中元素的索引值t自动从列表中移除该元素。3.List pop()方法1.pop()方法语法:list.pop([index=-1])2.pop()函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。3.pop(可选参数)中参数:可选参数,要移除列表元素的索引值,不能超过列表总长度,默认为 index=-1,删除最后一个列表值。4.pop()返回值:该方法返回从列表中被移除的元素对象。5.pop(要移除的列表中元素的索引值):根据列表中元素的索引值自动从列表中移除"""#删除nan的结果,逆向删除异常的跟踪器,防止破坏索引for t in reversed(to_del):"""根据to_del列表中保存的跟踪框的索引值到跟踪器链(列表)中将该跟踪框从其中移除出去。trackers:上一帧中的跟踪器链(列表),保存的是上一帧中成功跟踪目标的跟踪框,也即成功跟踪目标的KalmanBoxTracker卡尔曼滤波对象。trackers.pop(要移除的某个跟踪框的索引值):即能根据该索引值从跟踪器链(列表)中把该跟踪框移除出去"""# pop(要移除的列表中元素的索引值):根据列表中元素的索引值自动从列表中移除self.trackers.pop(t)"""matches:[[检测框的索引值, 跟踪框的索引值] [检测框的索引值, 跟踪框的索引值] 。。。]跟踪成功并且两两匹配组合的IOU值大于iou阈值的检测框和跟踪框组成的矩阵unmatched_detections:[检测框的索引值,。。。]1.新增目标的检测框在detections检测框列表中的索引位置2.两两匹配组合的IOU值小于iou阈值的检测框在detections检测框列表中的索引位置unmatched_trackers:[跟踪框的索引值,。。。]1.跟踪失败的跟踪框/预测框在trackers跟踪框列表中的索引位置2.两两匹配组合的IOU值小于iou阈值的跟踪框/预测框在trackers跟踪框列表中的索引位置1.matched:跟踪成功目标的矩阵。即前后帧都存在的目标,并且匹配成功同时大于iou阈值。2.unmatched_detections(列表):1.检测框中出现新目标,但此时预测框(跟踪框)中仍不不存在该目标,那么就需要在创建新目标对应的预测框/跟踪框(KalmanBoxTracker类的实例对象),然后把新目标对应的KalmanBoxTracker类的实例对象放到跟踪器链(列表)中。2.同时如果因为“跟踪框和检测框之间的”两两组合的匹配度IOU值小于iou阈值,则也要把目标检测框放到unmatched_detections中。3.unmatched_trackers(列表):1.当跟踪目标失败或目标离开了画面时,也即目标从检测框中消失了,就应把目标对应的跟踪框(预测框)从跟踪器链中删除。unmatched_trackers列表中保存的正是跟踪失败即离开画面的目标,但该目标对应的预测框/跟踪框(KalmanBoxTracker类的实例对象)此时仍然存在于跟踪器链(列表)中,因此就需要把该目标对应的预测框/跟踪框(KalmanBoxTracker类的实例对象)从跟踪器链(列表)中删除出去。2.同时如果因为“跟踪框和检测框之间的”两两组合的匹配度IOU值小于iou阈值,则也要把跟踪目标框放到unmatched_trackers中。"""#使用匈牙利算法:将目标检测框和卡尔曼滤波器预测的跟踪框进行匹配,分别获取跟踪成功的目标,新增的目标,离开画面的目标matched,unmatched_dets,unmatche_trkes = associate_detection_to_tracker(dets,trks)"""for t, trk in enumerate(trackers列表)t:为从0到列表长度-1的索引值trk:trackers列表中每个KalmanBoxTracker卡尔曼滤波对象"""#将跟踪成功的目标更新到对应的卡尔曼滤波器for t,trk in enumerate(self.trackers):""" 1.trackers:上一帧中的跟踪器链(列表),保存的是上一帧中成功跟踪目标的跟踪框,也即成功跟踪目标的KalmanBoxTracker卡尔曼滤波对象。2.for t, trk in enumerate(trackers):遍历上一帧中的跟踪器链(列表)中从0到列表长度-1的索引值t 和 每个KalmanBoxTracker卡尔曼滤波对象trk。3.if t not in unmatched_trks:如果上一帧中的跟踪框(KalmanBoxTracker卡尔曼滤波对)的索引值不在当前帧中的unmatched_trackers(列表)中的话,即代表上一帧中的跟踪框在当前帧中成功跟踪到目标,并且代表了“上一帧中的跟踪框在当前帧中的”预测框和当前帧中的检测框的匹配度IOU值大于iou阈值。4.matched[:, 1]:获取的是跟踪框的索引值,即[[检测框的索引值, 跟踪框的索引值] 。。。]中的跟踪框的索引值。5.np.where(matched[:, 1] == t)[0]:where返回的为符合条件的“[检测框的索引值, 跟踪框的索引值]”数组在matched矩阵中的索引值,即行值。因此最后使用[0]就是从array([索引值/行值])中把索引值/行值取出来。6.matched[索引值/行值, 0]:根据索引值/行值获取出matched矩阵中的[检测框的索引值, 跟踪框的索引值],然后获取出第一列的“检测框的索引值”。7.dets[d, :]:根据检测框的索引值/行值从当前帧中的dets检测框列表获取出该检测框的所有列值,最终返回的是一个二维矩阵如下所示:第一种方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度]]第二种方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]]8.dets[d, :][0]:获取出[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]9.trk.update(检测框的5个值的列表):使用检测框进行更新状态更新向量x(状态变量x),也即使用检测框更新跟踪框。"""if t not in unmatche_trkes:d = matched[np.where(matched[:, 1] == t)[0], 0]# 使用观测的边界框更新状态向量trk.update(dets[d, :][0])"""unmatched_detections(列表)保存了出现新目标的检测框的索引值,还保存了“因为跟踪框和检测框之间的两两组合的匹配度IOU值小于iou阈值的”目标检测框的索引值。dets[i, :]:根据索引值从当前帧中的检测框列表dets中获取对应的检测框,即该行的所有列值。该检测框的值为:第一种方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度]]第二种方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]]KalmanBoxTracker(dets[i, :]):传入检测框进行创建该新目标对应的跟踪框KalmanBoxTracker卡尔曼滤波对象trk。每个目标框都有对应的一个卡尔曼滤波器(KalmanBoxTracker实例对象),增加一个目标框就增加一个卡尔曼滤波器(KalmanBoxTracker实例对象)。trackers.append(trk):把新增的卡尔曼滤波器(KalmanBoxTracker实例对象trk)存储到跟踪器链(列表)trackers中"""#为新增目标创建新的卡尔曼滤波器的跟踪器for i in unmatched_dets:trk = KalmanBoxTracker(dets[i,0])self.trackers.append(trk)# 自后向前遍历,仅返回在当前帧出现且命中周期大于self.min_hits(除非跟踪刚开始)的跟踪结果;如果未命中时间大于self.max_age则删除跟踪器。# hit_streak忽略目标初始的若干帧""" i为trackers跟踪器链(列表)长度,从列表尾向列表头的方向 每遍历trackers跟踪器链(列表)一次 即进行 i-=1 """i = len(self.trackers)""" reversed逆向遍历trackers跟踪器链(列表),目的为删除列表中的元素的同时不会造成漏遍历元素的问题 """# 逆向遍历for trk in reversed(self.trackers):""" (跟踪框)KalmanBoxTracker卡尔曼滤波对象trk.get_state():获取跟踪框所预测的在当前帧中的预测结果(已经从[x,y,s,r]转换为[x1,y1,x2,y2]) [x1,y1,x2,y2]即为[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]。get_state()[0] 中使用[0] 是因为返回的为二维矩阵如下:   第一种方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度]]第二种方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]]"""#返回当前边界框的估计值d = trk.get_state()[0]"""1.trk.time_since_update < 1:1.time_since_update:记录了该目标对应的卡尔曼滤波器中的预测框(跟踪框)进行连续预测的次数,每执行predict一次即进行time_since_update+=1。在连续预测(连续执行predict)的过程中,一旦执行update的话,time_since_update就会被重置为0。2. time_since_update < 1:该目标对应的卡尔曼滤波器一旦update更新的话该变量值便重置为0,因此要求该目标对应的卡尔曼滤波器必须执行update更新步骤。update更新代表了使用检测框来更新状态更新向量x(状态变量x)的操作,实际即代表了使用“通过yoloV3得到的并且和预测框(跟踪框)相匹配的”检测框来更新该目标对应的卡尔曼滤波器中的预测框(跟踪框)。2.trk.hit_streak >= min_hits:1.hit_streak1.连续更新的次数,每执行update一次即进行hit_streak+=1。2.在连续更新(连续执行update)的过程中,一旦开始连续执行predict两次或以上的情况下,当连续第一次执行predict时,因为time_since_update仍然为0,并不会把hit_streak重置为0,然后才会进行time_since_update+=1;当连续第二次执行predict时,因为time_since_update已经为1,那么便会把hit_streak重置为0,然后继续进行time_since_update+=1。     2.min_hits跟踪框连续成功跟踪到目标的最小次数,也即跟踪框至少需要连续min_hits次成功跟踪到目标。3.hit_streak >= min_hits跟踪框连续更新的次数hit_streak必须大于等于min_hits。而小于该min_hits次数的话update函数不返回该目标的KalmanBoxTracker卡尔曼滤波对象。3.frame_count <= min_hits:因为视频的一开始frame_count为0,而需要每经过一帧frame_count才会+=1。因此在视频的一开始前N帧中,即使frame_count 小于等于min_hits 也可以。"""# 跟踪成功目标的box与id放入ret列表中if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):""" 1.ret:当前帧中跟踪目标成功的跟踪框/预测框的集合,包含目标的跟踪的id(也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个)第一种返回值方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度, trk.id] ...]第二种返回值方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, trk.id] ...]d:[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]trk.id:卡尔曼滤波器的个数/目标框的个数,也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个。2.np.concatenate((d, [trk.id + 1])).reshape(1, -1)[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, 该跟踪框是创建出来的第几个]]"""ret.append(np.concatenate((d, [trk.id + 1])).reshape(1, -1))  # +1 as MOT benchmark requires positive""" i为trackers跟踪器链(列表)长度,从列表尾向列表头的方向 每遍历trackers跟踪器链(列表)一次 即进行 i-=1 """i -= 1"""trk.time_since_update > max_age1.time_since_update:记录了该目标对应的卡尔曼滤波器中的预测框(跟踪框)进行连续预测的次数,每执行predict一次即进行time_since_update+=1。在连续预测(连续执行predict)的过程中,一旦执行update的话,time_since_update就会被重置为0。2.max_age:最大跟丢帧数。如果当前连续N帧大于最大跟丢帧数的话,则从跟踪器链中删除该卡尔曼滤波对象的预测框(跟踪框)。3.time_since_update > max_age:每预测一帧time_since_update就会+=1,只有预测的跟踪框跟踪到目标(即预测的跟踪框和检测框相似度匹配)才会执行update更新,那么time_since_update才会被重置为0。那么当连续time_since_update帧都没有跟踪到目标的话,即当连续time_since_update帧大于最大跟丢帧数时,那么就需要根据该跟踪失败的跟踪器框的索引把该跟踪器框从跟踪器链(列表)trackers中进行移除出去。"""# 跟踪失败或离开画面的目标从卡尔曼跟踪器中删除if trk.time_since_update > self.max_age:"""trackers:上一帧中的跟踪器链(列表),保存的是上一帧中成功跟踪目标的跟踪框,也即成功跟踪目标的KalmanBoxTracker卡尔曼滤波对象。trackers.pop(要移除的某个跟踪框的索引值):即能根据该索引值从跟踪器链(列表)中把该跟踪框移除出去"""# pop(要移除的列表中元素的索引值):根据列表中元素的索引值自动从列表中移除self.trackers.pop(i)# 返回当前画面中所有目标的box与id,以二维矩阵形式返回if len(ret) > 0:""" ret:当前帧中跟踪目标成功的跟踪框/预测框的集合,包含目标的跟踪的id(也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个)第一种返回值方案:[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度, trk.id] ...]第二种返回值方案(当前使用的为该种):[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, trk.id] ...]d:[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标]trk.id:卡尔曼滤波器的个数/目标框的个数,也即该跟踪框(卡尔曼滤波实例对象)是创建出来的第几个。[[左上角的x坐标, 左上角的x坐标y坐标, 右下角的x坐标, 右下角的y坐标, yolo识别目标是某种物体的可信度, 该跟踪框是创建出来的第几个] [...][...]]"""return np.concatenate(ret)return np.empty((0, 5))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧交通day02-车流量检测实现11:yoloV3模型

yoloV3以V1&#xff0c;V2为基础进行的改进&#xff0c;主要有&#xff1a;利用多尺度特征进行目标检测&#xff1b;先验框更丰富&#xff1b;调整了网络结构&#xff1b;对象分类使用logistic代替了softmax,更适用于多标签分类任务。 1.算法简介 YOLOv3是YOLO (You Only Loo…

bzoj1992鬼谷子的钱袋(二分乱搞 二进制)

1192: [HNOI2006]鬼谷子的钱袋 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3223 Solved: 2333Descriptio 鬼谷子非常聪明&#xff0c;正因为这样&#xff0c;他非常繁忙&#xff0c;经常有各诸侯车的特派员前来向他咨询时政。有一天&#xff0c;他在咸阳游历的时候&…

聚类(Clustering): K-means算法

聚类(Clustering): K-means算法 1.归类: 聚类(clustering)属于非监督学习(unsupervised learning) 无类别标记( class label) 3. K-means 算法&#xff1a; 3.1 Clustering 中的经典算法&#xff0c;数据挖掘十大经典算法之一 3.2 算法接受参数 k &#xff1b;然后将事先输入…

智慧交通day02-车流量检测实现12:基于yoloV3的目标检测

在本章节代码编写中&#xff0c;发现之前的代码所处的环境是python3&#xff0c;因此导致了cv2.dnn.readNetFromDarknet()在代码运行中导致了i[0]的获值失败&#xff0c;故总结如下&#xff1a; cv2.dnn.readNetFromDarknet()在python3上遇到的问题_李大狗的读研日记-CSDN博客…

cv2.dnn.readNetFromDarknet()在python3上遇到的问题

问题描述&#xff1a; 代码如下 net cv2.dnn.readNetFromDarknet(configPath,weightsPath) #获取YOLO每一层的名称 #getLayerNames&#xff08;&#xff09;&#xff1a;获取网络所有层的名称。 ln net.getLayerNames() # 获取输出层的名称: [yolo-82,yolo-94,yolo-106] # …

智慧交通day02-车流量检测实现13:基于虚拟线圈法的车辆统计+视频中的车流量统计原理解析

1.基于虚拟线圈法的车辆统计 基于虚拟线圈的车流量统计算法原理与交通道路上的常见的传统的物理线圈类似&#xff0c;由于物理线圈需要埋设在路面之下&#xff0c;因此会有安装、维护费用高&#xff0c;造成路面破坏等问题&#xff0c;而采用基于视频的虚拟线圈的车辆计数方法…

ubuntu 12.04 eclipse 安装

方法二&#xff1a;(优点是安装内容清爽&#xff0c;缺点是配置麻烦) 1、安装JDK&#xff0c;参考 Ubuntu 12.04 下安装 JDK 7 2、下载 Eclipse 从 http://www.eclipse.org/downloads/index-developer.php下载合适版本&#xff0c;如&#xff1a;Eclipse IDE for C/C Develope…

智慧交通day02-车流量检测实现14:代码汇总+问题修正

代码权重文件资源https://download.csdn.net/download/qq_39237205/43072746https://download.csdn.net/download/qq_39237205/43072746 环境要求&#xff1a;python2.7 环境配置&#xff1a;见文末requirements.txt 1.YOLO.py # encoding:utf-8 import imutils import tim…

从资源池和管理的角度理解物理内存

早就想搞一下内存问题了&#xff01;这次正趁着搞bigmemory内核&#xff0c;可以写一篇文章了。本文旨在记录&#xff0c;不包含细节&#xff0c;细节的话&#xff0c;google&#xff0c;百度均可&#xff0c;很多人已经写了不少了。我只是按照自己的理解记录一下内存的点点滴滴…

从头开始学一个android activity

一、类层次结构&#xff1a; 二、什么是Activity&#xff0c;如何理解Activity 1、 用户与应用程序的交互的接口 2、 控件的容器&#xff0c;我们要把控件摆放在这个容器中 三、如何创建一个Activity 新建一个类&#xff1a; 1、 继承Activity类 [java] view plaincopyprint…

python3 numpy中矩阵np.dot(a,b)乘法运算

python np.dot(a,b)乘法运算 首先我们知道矩阵运算是不满足交换律的&#xff0c;np.dot(a, b)与np.dot(b, a)是不一样的 另外np.dot(a,b)和a.dot(b)果是一样的 1.numpy中数组&#xff08;矩阵&#xff09;相乘np.dot(a,b)运算&#xff1a; 对于两数组a和b &#xff1a; 示例…

ML Backpropagation算法的计算过程举例

Backpropagation计算过程举例 初始权重(initialize weights)是随机产生的(如-1~1之间) 初始化可以选择均值为0&#xff0c;方差为1/n_in的正态分布&#xff0c;n_in为输入的实例个数&#xff0c;Python中可使用np.random.normal函数来初始化权重&#xff1a; np.random.normal…

Python基础知识__字符串

字符串介绍 一. 认识字符串 字符串是 Python 中最常用的数据类型。我们一般使用引号来创建字符串。创建字符串很简单&#xff0c;只要为变量分配一个值即可。 a hello world b "abcdefg" print(type(a)) print(type(b)) 注意&#xff1a;控制台显示结果为<cl…

智慧交通day03-车道线检测实现01:车道线检测概述

项目简介 汽车的日益普及在给人们带来极大便利的同时&#xff0c;也导致了拥堵的交通路况&#xff0c;以及更为频发的交通事故。而自动驾驶技术的出现可以有效的缓解了此类问题&#xff0c;减少交通事故&#xff0c;提升出行效率。 国内外检测车道线的方法主要有两类&#xf…

vsphere平台windows虚拟机克隆的小插曲(无法登陆系统)

问题&#xff1a; 1、克隆完windows虚拟化后输入法乱码。 2、开启远程的情况下远程登录输入正确的密码也无法登录。 解决&#xff1a; 1、更改管理员用户密码&#xff08;不输入原win7密码更改win7密码&#xff09;。 2、重新启用管理员。 3、重启系统 不输入当前密码改win密码…

智慧交通day03-车道线检测实现02-1:相机校正

1. 相机标定的意义 我们所处的世界是三维的&#xff0c;而照片是二维的&#xff0c;我们可以把相机认为是一个函数&#xff0c;输入量是一个场景&#xff0c;输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。 相机标定的一个目的是要找一个合适的数学模型&#…

把输入字符的小写转换成大写并输出

#include <stdio.h> #include <stdlib.h> #include <string.h> /******************************* *把小写字母转成大写字母 ********************************/ int main(void) { char *srcNULL;unsigned char i0;char *new_1NULL;char *new_2NULL;char *dN…

nginx服务无法启动,启动时报错1067以外终止

解决方法&#xff1a; 首先cmd命令打开&#xff0c;输入nginx -t测试配置文件是否能运行OK&#xff0c;如果报错bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a …) 则说明配置文件中的端口0.0.0.0:80被占用 可使用下面两种方法解决&#xff1a; cmd输…

智慧交通day03-车道线检测实现02-2:张氏标定法+双目标定

6.张氏标定法 张氏标定法是张正友博士在1999年发表在国际顶级会议ICCV上的论文《Flexible Camera Calibration By Viewing a Plane From Unknown Orientations》中&#xff0c;提出的一种利用平面棋盘格进行相机标定的实用方法。 该方法介于传统标定法和自标定法之间&#xff…

指针的基本操作(10.1 Basic Pointer Operations)

[This section corresponds to K&R Sec. 5.1] The first things to do with pointers are to declare a pointer variable, set it to point somewhere, and finally manipulate the value that it points to. A simple pointer declaration looks like this: 对指针的第…