目标跟踪之sort算法(3)

这里写目录标题

  • 1 流程
      • 1 预处理
      • 2 跟踪
  • 2 代码

在这里插入图片描述
参考:sort代码 https://github.com/abewley/sort

1 流程

1 预处理

    1.1 获取离线检测数据。1.2 实例化跟踪器。

2 跟踪

2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹,剔除到当前轨迹中为空的轨迹得到当前有效轨迹。
2.2 匹配。用匈牙利算法对有效轨迹和检测框匹配,得到匹配id、新检测id、未匹配ida. 如果跟踪器的个数为零,即第一帧图像,返回值为0的匹配id、新检测id、值为0的未匹配id。b. 如果跟踪器的个数为不为0,则计算检测框与当前轨迹的iou,如果iou不为空,得到iou大于阈值的掩码矩阵,判断掩码矩阵每行是否跟每列是一一对应,如果是则不需要匈牙利算法匹配;反之,用匈牙利算法得到匹配的检测框和轨迹的索引。c. 根据匹配索引得到新检测的框的id和为匹配的轨迹的id。d.根据iou再筛选一次。
2.3 更新轨迹。a. 对匹配上的轨迹,根据匹配id得到当前帧的最优估计。b. 添加新的检测。对于没有被匹配上的检测框生成新的跟踪器,并添加到轨迹中。c. 筛选轨迹。

2 代码


"""  sort代码   https://github.com/abewley/sortSORT: A Simple, Online and Realtime TrackerCopyright (C) 2016-2020 Alex Bewley alex@bewley.aiThis program is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation, either version 3 of the License, or(at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program.  If not, see <http://www.gnu.org/licenses/>.-i https://pypi.tuna.tsinghua.edu.cn/simple
filterpy==1.4.5
scikit-image==0.14.0
lap==0.4.0
numba==0.38.1
scikit-learn==0.19.1
"""
from __future__ import print_functionimport os
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from skimage import ioimport glob
import time
import argparse
from filterpy.kalman import KalmanFilternp.random.seed(0)def linear_assignment(cost_matrix):try:import lap_, x, y = lap.lapjv(cost_matrix, extend_cost=True)return np.array([[y[i],i] for i in x if i >= 0])except ImportError:from scipy.optimize import linear_sum_assignmentx, y = linear_sum_assignment(cost_matrix)return np.array(list(zip(x, y)))def iou_batch(bb_test, bb_gt):"""From SORT: Computes IOU between two bboxes in the form [x1,y1,x2,y2]"""bb_gt = np.expand_dims(bb_gt, 0)bb_test = np.expand_dims(bb_test, 1)xx1 = np.maximum(bb_test[..., 0], bb_gt[..., 0])yy1 = np.maximum(bb_test[..., 1], bb_gt[..., 1])xx2 = np.minimum(bb_test[..., 2], bb_gt[..., 2])yy2 = np.minimum(bb_test[..., 3], bb_gt[..., 3])w = np.maximum(0., xx2 - xx1)h = np.maximum(0., yy2 - yy1)wh = w * ho = wh / ((bb_test[..., 2] - bb_test[..., 0]) * (bb_test[..., 3] - bb_test[..., 1])                                      + (bb_gt[..., 2] - bb_gt[..., 0]) * (bb_gt[..., 3] - bb_gt[..., 1]) - wh)                                              return(o)  def convert_bbox_to_z(bbox):"""Takes a bounding box in the form [x1,y1,x2,y2] and returns z in the form[x,y,s,r] where x,y is the centre of the box and s is the scale/area and r isthe aspect ratio"""w = bbox[2] - bbox[0]h = bbox[3] - bbox[1]x = bbox[0] + w/2.y = bbox[1] + h/2.s = w * h    #scale is just arear = w / float(h)return np.array([x, y, s, r]).reshape((4, 1))def convert_x_to_bbox(x,score=None):"""Takes a bounding box in the centre form [x,y,s,r] and returns it in the form[x1,y1,x2,y2] where x1,y1 is the top left and x2,y2 is the bottom right"""w = np.sqrt(x[2] * x[3])h = x[2] / wif(score==None):return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.]).reshape((1,4))else:return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.,score]).reshape((1,5))class KalmanBoxTracker(object):"""This class represents the internal state of individual tracked objects observed as bbox."""count = 0def __init__(self,bbox):"""Initialises a tracker using initial bounding box."""#define constant velocity modelself.kf = KalmanFilter(dim_x=7, dim_z=4) self.kf.F = np.array([[1,0,0,0,1,0,0],[0,1,0,0,0,1,0],[0,0,1,0,0,0,1],[0,0,0,1,0,0,0],  [0,0,0,0,1,0,0],[0,0,0,0,0,1,0],[0,0,0,0,0,0,1]])self.kf.H = np.array([[1,0,0,0,0,0,0],[0,1,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,0,1,0,0,0]])self.kf.R[2:,2:] *= 10.self.kf.P[4:,4:] *= 1000. #give high uncertainty to the unobservable initial velocitiesself.kf.P *= 10.self.kf.Q[-1,-1] *= 0.01self.kf.Q[4:,4:] *= 0.01self.kf.x[:4] = convert_bbox_to_z(bbox)self.time_since_update = 0self.id = KalmanBoxTracker.countKalmanBoxTracker.count += 1self.history = []self.hits = 0self.hit_streak = 0self.age = 0def update(self,bbox):"""Updates the state vector with observed bbox."""self.time_since_update = 0self.history = []self.hits += 1self.hit_streak += 1   # 连续匹配并更新的次数self.kf.update(convert_bbox_to_z(bbox))def predict(self):"""Advances the state vector and returns the predicted bounding box estimate."""if((self.kf.x[6]+self.kf.x[2])<=0):self.kf.x[6] *= 0.0self.kf.predict()self.age += 1if(self.time_since_update>0):  # 上一次更新距离现在的时间self.hit_streak = 0          # 匹配次数归0self.time_since_update += 1    # 轨迹只预测没有匹配的的次数➕1self.history.append(convert_x_to_bbox(self.kf.x))return self.history[-1]def get_state(self):"""Returns the current bounding box estimate."""return convert_x_to_bbox(self.kf.x)def associate_detections_to_trackers(detections,trackers,iou_threshold = 0.3):"""Assigns detections to tracked object (both represented as bounding boxes)Returns 3 lists of matches, unmatched_detections and unmatched_trackers"""if(len(trackers)==0):return np.empty((0,2),dtype=int), np.arange(len(detections)), np.empty((0,5),dtype=int)iou_matrix = iou_batch(detections, trackers)if min(iou_matrix.shape) > 0:a = (iou_matrix > iou_threshold).astype(np.int32)if a.sum(1).max() == 1 and a.sum(0).max() == 1:matched_indices = np.stack(np.where(a), axis=1)  # 如果正好是一个检测与一个轨迹匹配,则找出匹配的索引else:matched_indices = linear_assignment(-iou_matrix)   # 匈牙利匹配,matched_indices存储的是每个检测框对应的轨迹,第一列存储的是检测框的id;第二列存储的是检测框匹配的轨迹idelse:matched_indices = np.empty(shape=(0,2))    unmatched_detections = []   # 寻找没有被匹配上的检测框for d, det in enumerate(detections):   # 这一步写的麻烦,不用枚举if(d not in matched_indices[:,0]):unmatched_detections.append(d)unmatched_trackers = []       # 寻找没有被匹配上的轨迹for t, trk in enumerate(trackers):if(t not in matched_indices[:,1]):unmatched_trackers.append(t)#filter out matched with low IOUmatches = []               # 寻找被匹配上的检测框的idfor m in matched_indices:  # 根据iou再进行一次筛选if(iou_matrix[m[0], m[1]]<iou_threshold):unmatched_detections.append(m[0])unmatched_trackers.append(m[1])else:matches.append(m.reshape(1,2))if(len(matches)==0):matches = np.empty((0,2),dtype=int)else:matches = np.concatenate(matches,axis=0)return matches, np.array(unmatched_detections), np.array(unmatched_trackers)class Sort(object):def __init__(self, max_age=1, min_hits=3, iou_threshold=0.3):"""Sets key parameters for SORT"""self.max_age = max_ageself.min_hits = min_hitsself.iou_threshold = iou_thresholdself.trackers = []self.frame_count = 0def update(self, dets=np.empty((0, 5))):"""Params:dets - a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]Requires: this method must be called once for each frame even with empty detections (use np.empty((0, 5)) for frames without detections).Returns the a similar array, where the last column is the object ID.NOTE: The number of objects returned may differ from the number of detections provided."""self.frame_count += 1# get predicted locations from existing trackers.trks = np.zeros((len(self.trackers), 5))   # 存储筛选后的轨迹。第一帧shape=(0, 5);to_del = []          # 没有匹配的轨迹ret = []             # 存放检测所有合格的轨迹for t, trk in enumerate(trks):pos = self.trackers[t].predict()[0]            # 根据上一帧的轨迹当前帧的轨迹.trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]if np.any(np.isnan(pos)):to_del.append(t)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, self.iou_threshold)   # 第一帧没有轨迹,# update matched trackers with assigned detections  对匹配的轨迹更新for m in matched:  # 根据当前轨迹和检测得到当前最优估计self.trackers[m[1]].update(dets[m[0], :])# create and initialise new trackers for unmatched detectionsfor i in unmatched_dets:    # 对于没有被匹配上的检测框生成新的跟踪器,并添加到轨迹中trk = KalmanBoxTracker(dets[i,:])self.trackers.append(trk)i = len(self.trackers)for trk in reversed(self.trackers):d = trk.get_state()[0]if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits): #(当前更新的轨迹)and (连续匹配超过min_hits or 检测帧数小于min_hits)ret.append(np.concatenate((d,[trk.id+1])).reshape(1,-1)) # +1 as MOT benchmark requires positivei -= 1# remove dead trackletif(trk.time_since_update > self.max_age):self.trackers.pop(i)if(len(ret)>0):return np.concatenate(ret)return np.empty((0,5))def parse_args():"""Parse input arguments."""parser = argparse.ArgumentParser(description='SORT demo')parser.add_argument('--display', dest='display', help='Display online tracker output (slow) [False]',action='store_true')parser.add_argument("--seq_path", help="Path to detections.", type=str, default='data')parser.add_argument("--phase", help="Subdirectory in seq_path.", type=str, default='train')parser.add_argument("--max_age", help="Maximum number of frames to keep alive a track without associated detections.", type=int, default=1)parser.add_argument("--min_hits", help="Minimum number of associated detections before track is initialised.", type=int, default=3)parser.add_argument("--iou_threshold", help="Minimum IOU for match.", type=float, default=0.3)args = parser.parse_args()return argsif __name__ == '__main__':# all trainargs = parse_args()display = args.display     # 是否显示结果phase = args.phase         # 'trian'total_time = 0.0           # 总时长total_frames = 0           # 记录检测的帧数colours = np.random.rand(32, 3) # \used only for display  [32,3]if(display):if not os.path.exists('mot_benchmark'):print('\n\tERROR: mot_benchmark link not found!\n\n    Create a symbolic link to the MOT benchmark\n    (https://motchallenge.net/data/2D_MOT_2015/#download). E.g.:\n\n    $ ln -s /path/to/MOT2015_challenge/2DMOT2015 mot_benchmark\n\n')exit()plt.ion()fig = plt.figure()ax1 = fig.add_subplot(111, aspect='equal')if not os.path.exists('output'):os.makedirs('output')pattern = os.path.join(args.seq_path, phase, '*', 'det', 'det.txt')   # 相对路径 'data/train/*/det/det.txt'# 1. 数据准备for seq_dets_fn in glob.glob(pattern):mot_tracker = Sort(max_age=args.max_age,              # 1.1 初始化跟踪器min_hits=args.min_hits,iou_threshold=args.iou_threshold)  # create instance of the SORT trackerseq_dets = np.loadtxt(seq_dets_fn, delimiter=',')     # 1.2 加载数据seq = seq_dets_fn[pattern.find('*'):].split(os.path.sep)[0]   # 'data/train/ETH-Bahnhof/det/det.txt' --> ['ETH-Bahnhof', 'det', 'det.txt'] --> 'ETH-Bahnhof'with open(os.path.join('output', '%s.txt'%(seq)),'w') as out_file:   # 'output/ETH-Bahnhof.txt'print("Processing %s."%(seq))for frame in range(int(seq_dets[:,0].max())):        # seq_dets[:,0]第一列为图片的序列号,遍历每一帧的检测结果frame += 1                       # detection and frame numbers begin at 1dets = seq_dets[seq_dets[:, 0]==frame, 2:7]   # x1,y1,w,h,cdets[:, 2:4] += dets[:, 0:2]     # convert to [x1,y1,w,h] to [x1,y1,x2,y2]total_frames += 1if(display):fn = os.path.join('mot_benchmark', phase, seq, 'img1', '%06d.jpg'%(frame))im = io.imread(fn)ax1.imshow(im)plt.title(seq + ' Tracked Targets')start_time = time.time()trackers = mot_tracker.update(dets)  # 2. 获取跟踪结果cycle_time = time.time() - start_timetotal_time += cycle_timefor d in trackers:  # 画的是跟踪到的轨迹print('%d,%d,%.2f,%.2f,%.2f,%.2f,1,-1,-1,-1'%(frame,d[4],d[0],d[1],d[2]-d[0],d[3]-d[1]),file=out_file)if(display):d = d.astype(np.int32)ax1.add_patch(patches.Rectangle((d[0],d[1]),d[2]-d[0],d[3]-d[1],fill=False,lw=3,ec=colours[d[4]%32,:]))if(display):fig.canvas.flush_events()plt.draw()ax1.cla()print("Total Tracking took: %.3f seconds for %d frames or %.1f FPS" % (total_time, total_frames, total_frames / total_time))if(display):print("Note: to get real runtime results run without the option: --display")

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

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

相关文章

双目立体校正和Q矩阵

立体校正 对两个摄像机的图像平面重投影&#xff0c;使二者位于同一平面&#xff0c;而且左右图像的行对准。 Bouguet 该算法需要用到双目标定后外参(R&#xff0c;T) 从上图中可以看出&#xff0c;该算法主要分为两步&#xff1a; 使成像平面共面 这个办法很直观&#xff…

C语言练习(29)

13个人围成一圈&#xff0c;从第1个人开始顺序报号1、2、3。凡报到“3”者退出圈子&#xff0c;找出最后留在圈子中的人原来的序号。本题要求用链表实现。 #include <stdio.h> #include <stdlib.h>// 定义链表节点结构体 typedef struct Node {int num;struct Nod…

5.2 软件需求分析

文章目录 需求分析的意义软件需求的组成需求分析的5个方面需求分析方法 需求分析的意义 需求分析解决软件“做什么”的问题。由于开发人员比较熟悉计算机而不熟悉领域业务&#xff0c;用户比较熟悉领域业务而不熟悉计算机&#xff0c;双方需要通过交流&#xff0c;制定出完整、…

理解神经网络:Brain.js 背后的核心思想

温馨提示 这篇文章篇幅较长,主要是为后续内容做铺垫和说明。如果你觉得文字太多,可以: 先收藏,等后面文章遇到不懂的地方再回来查阅。直接跳读,重点关注加粗或高亮的部分。放心,这种“文字轰炸”不会常有的,哈哈~ 感谢你的耐心阅读!😊 欢迎来到 brain.js 的学习之旅!…

GPU上没程序在跑但是显存被占用

原因&#xff1a;存在僵尸线程&#xff0c;运行完但是没有释放内存 查看僵尸线程 fuser -v /dev/nvidia*关闭僵尸线程 pkill -9 -u 用户名 程序名 举例&#xff1a;pkill -9 -u grs python参考&#xff1a;https://blog.csdn.net/qq_40206371/article/details/143798866

大数据Hadoop入门3

第五部分&#xff08;Apache Hive DML语句和函数使用&#xff09; 1.课程内容大纲和学习目标 2.Hive SQL-DML-load加载数据操作 下面我们随机创建文件尝试一下 先创建一个hivedata文件夹 在这个文件夹中写一个1.txt文件 下面使用beeline创建一张表 只要将1.txt文件放在t_1文件…

网易云音乐歌名可视化:词云生成与GitHub-Pages部署实践

引言 本文将基于前一篇爬取的网易云音乐数据, 利用Python的wordcloud、matplotlib等库, 对歌名数据进行深入的词云可视化分析. 我们将探索不同random_state对词云布局的影响, 并详细介绍如何将生成的词云图部署到GitHub Pages, 实现数据可视化的在线展示. 介绍了如何从原始数据…

const的用法

文章目录 一、C和C中const修饰变量的区别二、const和一级指针的结合const修饰的量常出现的错误是:const和一级指针的结合总结&#xff1a;const和指针的类型转换公式 三、const和二级指针的结合 一、C和C中const修饰变量的区别 C中&#xff1a;const必须初始化&#xff0c;叫常…

AI DeepSeek

DeepSeek 文字解析 上传图片解析 视乎结果出入很大啊&#xff0c;或许我们应该描述更加清楚自己的需求。

996引擎 - NPC-动态创建NPC

996引擎 - NPC-动态创建NPC 创建脚本服务端脚本客户端脚本添加自定义音效添加音效文件修改配置参考资料有个小问题,创建NPC时没有控制朝向的参数。所以。。。自己考虑怎么找补吧。 多重影分身 创建脚本 服务端脚本 Mir200\Envir\Market_Def\test\test001-3.lua -- NPC八门名…

css粘性定位超出指定宽度失效问题

展示效果 解决办法&#xff1a;外层容器添加display:grid即可 完整代码 <template><div class"box"><div class"line" v-for"items in 10"><div class"item" v-for"item in 8">drgg</div>&…

Git客户端工具

Git&#xff08;读音为/gɪt/&#xff09;是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 [1]也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 可使用工具TortoiseGit&#xff0c;官网下载…

Time Constant | RC、RL 和 RLC 电路中的时间常数

注&#xff1a;本文为 “Time Constant” 相关文章合辑。 机翻&#xff0c;未校。 How To Find The Time Constant in RC and RL Circuits June 8, 2024 &#x1f4a1; Key learnings: 关键学习点&#xff1a; Time Constant Definition: The time constant (τ) is define…

七、深入了解SpringBoot的配置文件

一、配置端口号 通过配置文件application.properties配置修改端口号 修改 application.properties 文件 #端口号修改成 9090 server.port9090运行结果&#xff0c;观察日志 二、配置文件格式 Spring Boot 配置⽂件有以下三种&#xff1a; • application.properties • ap…

set集合

set集合 Set系列集合&#xff1a; 无序&#xff1a;存取顺序不一致 不重复&#xff1a;可以去除重复 无索引&#xff1a;没有带索引的方法&#xff0c;所以不能使用普通for循环遍历&#xff0c;也不能通过索引来获取元素 可以看出set是无序的存和打印的顺序不一样 Set接中的…

马尔科夫模型和隐马尔科夫模型区别

我用一个天气预报和海藻湿度观测的比喻来解释&#xff0c;保证你秒懂&#xff01; 1. 马尔可夫模型&#xff08;Markov Model, MM&#xff09; 特点&#xff1a;状态直接可见 场景&#xff1a;天气预报&#xff08;晴天→雨天→阴天…&#xff09;核心假设&#xff1a; 下一个…

SimpleFOC STM32教程10|基于STM32F103+CubeMX,速度闭环控制(有电流环)

导言 SimpleFOC STM32教程09&#xff5c;基于STM32F103CubeMX&#xff0c;ADC采样相电流 如上图所示, 增加了电流环. 效果如下&#xff1a; 20250123-200906 RTT 如上图所示&#xff0c;三相占空比依然是马鞍波。当我用手去给电机施加阻力时&#xff0c;PID要维持目标转速&am…

基于蓝牙6.0的RSSI和UWB融合定位方法,可行性分析

融合RSSI&#xff08;接收信号强度指示&#xff09;和UWB&#xff08;超宽带&#xff09;两种技术进行蓝牙6.0定位是完全可行的&#xff0c;并且可以带来更高的定位精度和稳定性。本文给出分析和MATLAB仿真结果 文章目录 技术优势RSSIUWB融合的优势 实现方案数据融合算法硬件要…

富文本 tinyMCE Vue2 组件使用简易教程

参考官方教程 TinyMCE Vue.js integration technical reference Vue2 项目需要使用 tinyMCE Vue2 组件(tinymce/tinymce-vue)的第 3 版 安装组件 npm install --save "tinymce/tinymce-vue^3" 编写组件调用 <template><Editorref"editor"v-m…

项目集成Nacos

文章目录 1.环境搭建1.创建模块 sunrays-common-cloud-nacos-starter2.目录结构3.pom.xml4.自动配置1.NacosAutoConfiguration.java2.spring.factories 5.引入cloud模块通用依赖 2.测试1.创建模块 sunrays-common-cloud-nacos-starter-demo2.目录结构3.pom.xml4.application.ym…