• SORT4 SORT项目代码解析


    SORT系列

    SORT-1 项目配置运行-WINDOWS
    SORT-2 卡尔曼滤波推导和示例
    SORT-3 匈牙利算法和SORT类
    SORT-4 SORT项目代码解析
    本项目地址

    SORT项目逐层详解

    main

    if __name__ == '__main__'
    	# 设置交互模式、参数、文件路径
    		|
    	# 创建 SORT 对象
        mot_tracker = Sort(max_age=, min_hits=,iou_threshold) 
        	|
        # "循环"读取每一个文件(每一帧)
        for frame in range(...):
            # 参数准备
            frame += 1 	# 帧数+1
            dets[] = .. # 存放 检测框
            total_frames += 1
            	|
            # 获取检测框
            dets[] = ...
            	|
            # 用检测框 dets 做一次更新; 返回满足条件可显示的trackers([x1,y1,x2,y2,ID])
            trackers = mot_tracker.update(dets)
            	|
           	# 作画
            for d in trakcers:
                fig.canvas.flush_events()...
               
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.SORT 类

    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_age		# tracker 的最大寿命
        self.min_hits = min_hits	# 最小匹配次数
        self.iou_threshold = iou_threshold	# 匹配阈值
        self.trackers = []        	# 存放跟踪器类 KalmanBoxTracker 对象
        self.frame_count = 0
        
        
    	def update(self, dets=np.empty((0, 5))):
        # 使用 检测框 做一次更新; dets:[x1,y1,x2,y2,ID]
        	# 0.参数准备
            trks = np.zeros(len(self,trackers),5)	# 
            to_del = []			# 待删除的 预测框
            ret = []			# 可显示的 tracker
            	|
            # 1.现有的跟踪器全部做一次更新预测,获得预测框
            for t,trk in enumerate(trks):
                pos = self.trackers[t].predict()[0]      	# 已有跟踪器上做一次预测
          		trk[:]=[pos[0], pos[1], pos[2], pos[3], 0]  # 获得 "预测框" 的坐标
            	|
            	# 去除非法的预测框
            trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
            	|
            	# 删除 to_del 中待删除的 tracker
            for t in reversed(to_del):
          		self.trackers.pop(t)
                |
            # 2.预测框和 检测框 做一次匹配
            matched, unmatched_dets, unmatched_trks = \
            	associate_detections_to_trackers(dets,trks, self.iou_threshold)
        		|
                
            # 3.根据匹配结果 分别更新三类 trakcer
            	# 3.1 更新匹配成功的跟踪器
            for m in matched:
                 self.trackers[m[1]].update(dets[m[0], :])
        		# 3.2 为未匹配到的检测框创建一个新的跟踪器
            for i in unmatched_dets:
                trk = KalmanBoxTracker(dets[i,:])
    			self.trackers.append(trk)
                |
            	# 3.3 根据跟踪器的存在时间、匹配次数等决定是否加入显示列表
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    1.1卡尔曼跟踪器

    卡尔曼滤波器系统如下图所示。

    输入
    状态量初始值 x k − 1 {x_{k-1}} xk1和状态量协方差初始值 P k − 1 {P_{k-1}} Pk1
    卡尔曼滤波器中的参数设置: 状态转移矩阵 F F F,映射矩阵(或称测量矩阵) H H H
    迭代输入每一步的 观测值 Z k Z_k Zk
    输出
    每一步的状态预测 x k x_k xk

    在这里插入图片描述
    在SORT项目中,观测量 来自 YOLO 的检测结果 b b o x ( [ x 1 , y 1 , x 2 , y 2 ] ) bbox([x1,y1,x2,y2]) bbox([x1,y1,x2,y2]) 的转化形式 Z ( [ u , v , s , r ] ) Z([u,v,s,r]) Z([u,v,s,r])。其中 [ u , v ] [u,v] [u,v] 是中心点的坐标, s s s 是面积, r r r 是面积宽高比。这样四个量将是独立的变量,减弱了 bbox 四个变量之间的相关性。

    由此可以定义状态量 [ x , y , s , r , x ˙ , y ˙ , s ˙ ] T [x,y,s,r,\dot x,\dot y,\dot s]^T [x,y,s,r,x˙,y˙,s˙]T,其中 x ˙ , y ˙ , s ˙ \dot x,\dot y,\dot s x˙,y˙,s˙ 分别是 x , y , s x,y,s x,y,s 的一阶微分 d x , d y , d s dx,dy,ds dx,dy,ds
    在线性模型中,可将其视为 距离-速度 模型,即 x k = x k − 1 + d x d t x_k=x_{k-1}+dxdt xk=xk1+dxdt,由此可得状态转移矩阵 F F F
    F = [ 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 ] F=

    [1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1]" role="presentation">[1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1]
    F=1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1
    x k = F ⋅ x k − 1 = [ 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 ] [ x y s r x ˙ y ˙ s ˙ ] x_k=F\cdot x_{k-1}=
    [1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1]" role="presentation">[1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1]
    [xysrx˙y˙s˙]" role="presentation" style="position: relative;">[xysrx˙y˙s˙]
    xk=Fxk1=1,0,0,0,1,0,00,1,0,0,0,1,00,0,1,0,0,0,10,0,0,1,0,0,00,0,0,0,1,0,00,0,0,0,0,1,00,0,0,0,0,0,1xysrx˙y˙s˙

    即可得到 x k = x k − 1 + x ˙ x_k=x_{k-1}+\dot x xk=xk1+x˙ ( y , s y,s y,s同理), x ˙ = x ˙ \dot x=\dot x x˙=x˙ ( r , y ˙ r,\dot y r,y˙同理)。这就是卡尔曼滤波器是线性滤波器原因

    class KalmanBoxTracker(object):
        
        count = 0		# 跟踪器的存在数量,static
        
        def __init__(self,bbox):
        # 卡尔曼滤波器的主要参数设置
        self.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. 
        self.kf.P *= 10.
        self.kf.Q[-1,-1] *= 0.01
        self.kf.Q[4:,4:] *= 0.01
    
        self.kf.x[:4] = convert_bbox_to_z(bbox)     # [u,v,s,r,u',v',s']
        self.time_since_update = 0
        self.id = KalmanBoxTracker.count
    
        KalmanBoxTracker.count += 1
    
        self.history = []
        self.hits = 0
        self.hit_streak = 0
        self.age = 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
        def update(self,bbox):
        # 用检测框更新 tracker(kalman filter update)
        self.time_since_update = 0  # 自第一次 update(创建) 的匹配次数
        self.history = []			# 保存历次的
        self.hits += 1
        self.hit_streak += 1        # 与检测框成功匹配的次数
        self.kf.update(convert_bbox_to_z(bbox))	# 调用库函数的 update
        
        def predict(self):
         	if((self.kf.x[6]+self.kf.x[2])<=0):
          		self.kf.x[6] *= 0.0
            
        	self.kf.predict()
        
            self.age += 1
            if(self.time_since_update>0):
              self.hit_streak = 0
            self.time_since_update += 1
            self.history.append(convert_x_to_bbox(self.kf.x))
            return self.history[-1]     # 返回最后(新)一个 bbox
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.2检测框与预测框的关联

    def associate_detections_to_trackers(detections,trackers,iou_threshold = 0.3):
        
    
    • 1
    • 2
  • 相关阅读:
    网站SEO优化有哪些要点?
    MySQL常用运算符详细介绍
    iwebsec靶场 文件包含漏洞通关笔记8-php://input伪协议利用
    【算法速查】一篇文章带你快速入门八大排序(上)
    React给方法使用useState
    [AIGC] 深度优先搜索(DFS)详解及其在LeetCode问题中的应用
    netstat命令应用和ifconfig命令应用
    蓝桥等考Python组别十级006
    Opencv3.4版本+ffmpeg联合编译
    [附源码]java毕业设计氧气罐管理系统
  • 原文地址:https://blog.csdn.net/qq_43481884/article/details/128185090