• DeepSORT



    源码解读:

    一、 第三帧的检测结果与跟踪的预测结果相匹配

    二、根据级联匹配的结果更新跟踪器的集合【frame 3】:

    三、 更新Cosine矩阵【需要保存的特征】

    四、 可视化及结果的保存


      论文地址:https://arxiv.org/pdf/1703.07402.pdf

      论文官方源码地址:https://github.com/nwojke/deep_sort

      MOT16 benchmark地址:https://motchallenge.net/data/MOT16/

      npy文件地址:https://storage.googleapis.com/drive-bulk-export-anonymous/20191204T070345Z/4133399871716478688/157f00aa-f838-48e5-b6a3-11179ffcc66c/1/bc2e206d-fd99-4e00-b01c-9ca030acc581?authuser

      上述最后两个文件的内容解压缩后:

    • MOT16 benchmark 解压为MOT16文件夹
    • npy文件解压后将其中的detections文件夹剪切,并放置在resources文件夹中。(新建resources)  

    源码解读:

      源码的入口是deep_sort_app.py 程序,在该程序的入口包含args = parse_args()函数,说明需要在命令行运行时需要输入命令行参数。如下图所示:

      也正如作者github中关于运行的描述,在命令行或终端运行时需要输入如下指令。

    1 python deep_sort_app.py 
    2     --sequence_dir=./MOT16/test/MOT16-06 
    3     --detection_file=./resources/detections/MOT16_POI_test/MOT16-06.npy 
    4     --min_confidence=0.3 
    5     --nn_budget=100 
    6     --display=True

      如果想要在pycharm中运行,需要需要在pycharm中设置命令行参数。步骤如下:

    1. 选择Run - Edit Configurations...

      2. 在界面中的Parameters:中填写命令行参数。 如果不懂argparse命令行相关设置也不要紧,可以直接将github中给出的命令行参数去掉 "python deep_sort_app.py " 即可。

      以MOT16-14的数据集为切入点进行解读,之后不再赘述。  

      deep_sort_app.py主要由以下几个函数组成,其中核心程序在于def run()程序中的frame_callba ck()函数中,用于实现帧与帧之间跟踪器的处理。

     

    *************************************Processing frame 00001******************************************

      1. 首先,获取当前帧数frame_idx的检测信息,并过滤掉置信度小于阈值的bbox。

      这里会将检测信息中包含的bbox坐标的信息、置信度、features存到一个检测类的对象中。并将多个bbox的对象存入列表中。[坐标info(array) 置信度(float) features(array) ]

      检测的坐标info中存储的是bbox[左下坐标的x,y,宽,高]

      在第一帧中,一共有18个b-box。

      

      2. 对这些b-box进行NMS操作

      为了便于操作,将各对象中包含的坐标信息和置信度读取成列表的形式。NMS的操作与yolo中有所不同。NMS操作如下:

    • 将box info转换为[x1, y1, x2, y2]的形式。 【x,y,w,h】 2 【x1, y1, x2, y2】
    • 计算bbox的面积。 需要注意的是,使用像素坐标点计算面积需要+1噢 (y2-y1+1)*(x2-x1+1)
    • 对置信度进行排序,返回索引的排序结果。
    • 进行NMS,每次取置信度最大的bbox与其余的bbox进行iou处理,通过相交面积 / 做iou的bbox的面积产生 overlap。
    overlap = (w * h) / area[idxs[:last]] # 相交的面积/比较的框的面积
    • 去掉当前置信度最大的和overlap超过1的索引。【个人感觉此处的NMS并不会有什么作用,面积比超过或等于1的检测框一般在行人检测不存在】 

      3. 进行kalman的相关运算:

      主要包含两个部分,分别代表了Kalman滤波中的时间更新和状态更新。

      3.1 时间更新

      由于此时并没有tracker,不需要对状态进行估计。下述循环也就不存在,该函数直接跳出。

      3.2 状态更新

      首先,会进行匹配级联,返回匹配成功的matches,未匹配的跟踪器unmatched_tracks,未匹配的检测器unmatched_detections。

      由于第一帧还没有tracker,在匹配的过程中就不存在已确认和未确认tracker 。(论文中描述,新生成的跟踪器前三帧是试探期,只有在三帧内都可以匹配成功才会确认,否则将会被删除。

      自然,也就不会存在匹配成功的和未匹配的tracker, 只会有未匹配到的检测器18个(全都是未匹配的检测框)。

      利用检测框创建新的tracker:

    # Update track set.

      由于此时匹配的跟踪器和未匹配的跟踪器均为[],并不会对匹配的跟踪器进行更新。只是对未匹配的检测框创建其对应的新tracker。

      detection.to_xyah 是将检测框的(左下坐标x1, y1, w, h)转化为(中心点坐标 x, y, 长宽比a, h)

      初始化kalman相关的变量。mean是一组8维的kalman滤波状态变量x。

      然后,为当前检测框 生成新的Track对象,并存储在tracks列表中:

      Tracker类的初始化如下图所示,可以看出,刚生成的Tracker对象是Tentative试探性的,同论文中所述一致,刚建立的tracker处于试探期,需要在后续三帧中都可以成功关联,该tracker才会确认;否则将会被删除。)另外,每一帧的特征feature也会进行存储,用于后续余弦距离的计算。

      第一帧根据18个detections生成了18个处于试探期的trackers。

      由于此时的trackers均处于试探期,没有确认,所以不会更新其马氏距离。

      在可视化时,会绘制检测框和跟踪框两种。

    其中,检测框绘制颜色为0,0,255。由于此时是BGR的色域,所以检测框为红色框。 跟踪框会为每一个id设置不同的颜色。但需要当前的tracker处于确认的状态。 所以在前三帧中,只会绘制检测框,即仅有红色框,如下图所示。

      第一帧除了创建了Tracker,没有什么有含金量的内容。步入第二帧。

    *************************************Processing frame 00002******************************************

      第二帧中,关于检测结果的提取和NMS操作与第一帧相同,不再赘述。经过NMS后剩余的检测框detections有12个:

      此时,由于第一帧构建了18个trackers,会利用kalman滤波进行估计,得到18个跟踪预测结果。【即时间更新,tracker.predict()】。而且预测完成后会对每一个tracker的time_since_update+=1。 该操作与SORT中相同,用于衡量其每一次是否关联成功,后续在匹配中会用到。

    其中,kalman滤波的时间更新,self._std_weight_position为初始化的1/20(0.05),self. _std_weight_velocity为初始化的1/160(0.00625),mean[3]是上一状态估计值的长宽比。其中可以对应到kalman fitter的时间更新第一步和时间更新第二步的操作。【mean 和 covariance的计算】

      

      目前,有18个trackers, 12个detections,将会涉及到对tracker进行update的相关操作。【tracker.update(detections)

    • 首先要进行检测结果和跟踪预测结果的匹配级联(Matching Cascade)

      步骤如下:

      part 1 : 将现有的trackers划分为confirmed_tracks和unconfirmed_tracks两种:

      此时18个trackers均是unconfirmed_tracks。confirmed_trackers为空列表。

      part 2:针对confirmed_trakers使用外观特征进行级联匹配

      但由于在第二帧,还没有confirmed_trackers,所以该步操作并不会进行。返回的匹配结果均为未匹配的检测框。

      part 3:unconfirmed tracks与还没有匹配上的检测结果通过IOU计算的方式进行关联

      unmatched_tracks_a并不参与IOU关联的运算,仅用于后续unmatched_tracks的构成。

      iou_track_candidates共有18个(源自第一帧建立的track);

      unmatched_detections共有12个 (源自第二帧的检测结果)。

      首先,计算这些框两两之间的iou值,并通过1-iou得到matrix_cost。

      然后,再将cost_matrix中的值大于0.7的,全部置为0.70001。而这些值表示检测框与trackers的IOU较小

       P.S. 该矩阵为(18,12)的矩阵

      将cost_matrix矩阵作为匈牙利算法的输入,得到匹配的结果:

      从匹配结果上可以看出,第0列是iou_track_candidates的索引(18个), 第1列是unmatched_ detections(12个)的索引。

      再对匹配的结果进行进一步的处理: (进一步的关联处理

      Ⅰ. 将仍然没有匹配的检测结果和trackers进行统计。

      Ⅱ. 将cost_matrix>0.7的过滤器与追踪器之间的关联去掉。【cost_matrix>0.7,说明二者的IOU小于0.3,即二者之间的关联不大】

      Ⅲ. 记录匹配成功的track id 和 detection id

      Ⅳ. 最终返回处理后的结果

      part 4 part 2中处理的结果与part 3 中处理的结果进行整合,得到最终的匹配结果和未匹配结果。

    • 匹配级联完成之后,更新多目标跟踪器trackers集合

      part 1 针对match的,根据检测的结果更新track的参数。 【Kalman 状态更新过程】

       主要包含以下步骤:

      Ⅰ. 与SORT一样,更新Kalman滤波中的一系列运动变量、关联次数以及重置time_since_update.

      Ⅱ. 将当前帧的features保存到该跟踪器中。

      Ⅲ.如果已经连续关联3帧,将该track的状态由tentative改为confirmed。由于此时还没有成功关联3帧,各tracker还是1 tentative的状态。

      此时,每个track中将包含2帧中目标的features

      part 2: 针对unmatched tracks:

      对于未匹配的跟踪器,处理手段主要包含以下两种情况:

      Ⅰ. 当tracker处于tentative(试探期)时, 直接将该tracker和其id删除;

      Ⅱ. 当tracker处于确认状态,并且self.time_since_update大于max_age(源码中设置的是30),也就是说,如果已确认的tracker 30帧以上未关联成功,就将该tracker即其id删除。

      此时,[9, 10, 11, 15, 16, 17]这些id的tracker将会被删除。

      part 3: 针对unmatched_detections:

      对于未匹配的检测框,要为其创建新的tracker【处于试探期的】。

       因为当前帧没有unmatched_detection,所以该操作没有进行。 【该操作与第一帧根据detections 生成tracker的操作相同】

    • 更新已确认的距离度量矩阵

      由于此时的trackers仍然都处于试探期,所以并不会进行更新。

      同样,该帧的trackers都未得到确认,在显示时仅会显示检测框的红色,并且在结果保存时,并不会写到txt中。

      

    *************************************Processing frame 00003******************************************

      第三帧中,有14个检测框,12个未确认的tracker(处于tentative状态)。

      首先,要对12个未确认的tracker进行Kalman状态估计。然后对每一个tracker的time_since_ update += 1。【更新完成后匹配成功的会置0,不成功的将会进行删除,或者死缓的操作。】

      【tracker.predict()

      重点还是放在12个未确认的tracker和14个detections如何匹配,以及tracker如何更新上!【tracker.update()

    一、 第三帧的检测结果与跟踪的预测结果相匹配

      step 1: 将现有的trackers划分为已确认和未确认两组。

      step 2:首先对已经确认的trackers进行匹配级联。

      由于此时没有已经确认的trackers,所以该步依然不会有实质性的操作。返回结果中,匹配的tracker,未匹配的tracker均为空列表[],未匹配的检测框detections为当前的检测结果。

      

      step 3: unconfirmed tracks与还没有匹配上的检测结果通过IOU计算的方式进行关联

      unmatched_tracks_a并不参与IOU关联的运算,仅用于后续unmatched_tracks的构成。

      此时,unconfirmed_tracks包含12个,未匹配的检测器包含14个。对它们之间进行IOU关联运算。

      IOU关联运算,首先计算iou_track_candidates与unmatched_detections两两之间的cost_matrix矩阵。cost_matrix矩阵的计算通过iou计算,再通过1-iou。

      然后,将cost_matrix矩阵中>0.7的值全部赋值为0.70001。

      再将处理后的cost_matrix输入到匈牙利算法中进行匹配,得到线性的匹配结果indices。左侧为跟踪器的id,右侧为检测器的id。

      step 4:对匹配结果进一步处理,获取最终的匹配,未匹配【检测、跟踪】的结果。

      首先,对所有的检测结果id 在线性匹配结果indices中遍历,将没有出现的检测结果记录到未匹配检测unmatched_detections中。

      其次,对所有跟踪结果id 在线性匹配结果indices中遍历,将没有出现的检测结果记录到未匹配跟踪器unmatched_tracker中。

      最后,对cost_matrix中值大于0.7 对应的跟踪id和检测id加入到未匹配的行列中。【因为它们之间的iou太小】 将cost_matrix中值小于0.7对应的跟踪id和检测id结果对加入到匹配的结果中。并返回结果。

      这些计算都是基于cost_matrix和匈牙利算法计算的结果indices。

      step 5:step2 step4的结果进行整合

      第三帧级联匹配的结果:

      成功匹配的结果有11个,未匹配的检测结果有3个,未匹配的跟踪器有1个

    二、根据级联匹配的结果更新跟踪器的集合【frame 3】:

      1. 状态的变更(试探 确认 删除 1 2 3 )

      2. 新跟踪器的创建

      step 1:针对matches,根据检测结果更新track参数。(11个)

      A. 根据检测结果,更新Kalman参数

      主要包括以下步骤:

      a. 根据检测的结果更新Kalman状态参数。 主要是计算状态变量x(mean)和Pk(covariance);

      b. 将当前帧标定的特征保存到当前的跟踪器中;

      c. 更新当前track的状态是否更新。 是否从试探期转化到确认期。(3帧 存在)

      此时,有11个track已经经历了3帧,状态将会被修改为确认状态Confirmed,在代码中以2进行保存。

      step 2:针对unmatched_tracks (删1个)

      对于没有成功匹配的跟踪器,主要由两种情况:

      A. 没有匹配的跟踪器是确认状态Confirmed: 30帧的死缓,如果30帧都没有成功关联,则修改其状态为Delete,在代码中以3进行保存。

      B. 没有匹配的跟踪器是试探状态Tentative: 修改其状态为Delete,在代码中以3进行保存。

      状态表:

      在第三帧中,一中最后的结果,将会有1个跟踪器被删除

      step 3: 针对unmatched_detections: (建3个)

      需要创建3个新的跟踪器(试探期)。  

    三、 更新Cosine矩阵【需要保存的特征】

      此时,已经包含状态为Confirmed的tracks,便需要对distance metric进行更新。tracks即active_targets中:

      A. 对于已经确认的跟踪器,提取器特征集

      B. 计算Cosine metric

      最多会保存100条特征集合。   

    四、 可视化及结果的保存

      由于此时已经包含了状态为Confirmed的跟踪器,所以在显示的过程中不会仅显示红色的检测框。并且这些已经确认的跟踪器也会保存在txt中。

      可以看出,第3帧与前两帧不同的是在更新时,产生了Confirmed状态的跟踪器。增添了Cosine矩阵的计算。同时,在显示时,也增添了 追踪的结果。

    *************************************Processing frame 00004******************************************

      由于第3帧中,产生了状态为Confirmed trackers,所以在第4帧中,将会增添已确认trackers的级联匹配操作。

      关于前3帧相同的操作,这里将会进行简述,只对新增的对Confirmed状态的trackers的级联匹配进行详述。

      第4帧中,一共有14个检测框。

      根据现有的trackers,对每一个trackers进行kalman跟踪结果预测,也就是时间更新的状态估计。预测完之后,对每一个trackers的self.time_since_update += 1。 其中,有11个是Confirmed 的状态,有3个是tentative状态。

        

      进入14的detections与14个trk的匹配过程。【update】

      1. 将现有的跟踪器划分为已确认和未确认两组;

      此时,已经存在confirmed_tracks。

        

      2. 对confirmed_tracks与detections进行级联匹配。

      前3帧中,由于此时没有confirmed_tracks,所以不进行该过程的实际操作。第4帧中,存在confirmed_tracks,需要对其进行级联匹配,这也是第四帧中新增的一部分。

      A. 首先获取处于确认状态的trackers的indices和 检测框的indices,将检测框的indices命名为unmatched_detections:

      B. 对已经确认的tracker进行级联匹配

      级联匹配是什么呢?为什么会有级联匹配呢?

      由于已经确认的tracker存在可能没有匹配到的状态,即处于死缓的tracker。所以需要对那些死缓期间的trackers,需要对几帧没有匹配到的tracker依次进行级联

      所以cascade_depth为30,与死缓的帧数一致。当cascade_depth为0时,即为非死缓的tracker。

      track_indices_1列表的作用就是统计不同level的tracker。【正常状态的和不同死缓的】,对他们进行关联操作,可以看出,正常状态-1帧未关联-2帧未关联- .... - 29帧未关联,优先级递减。

      关联操作与检测框与未确认的跟踪器的关联操作是一致的。

      结果如下:

      可以看出,在有已确认的trackers时,基本上已经把detections的结果大多数都关联了。留给没有确认的trackers的detections不多了。这种优胜劣汰的机制也与人的感知十分的神似~

      其中,min_cost_matching函数中,包含distance_metric的传入,其调用函数为gated_metric():

      其中,包含余弦距离(外观特征)和马氏距离(运动信息)的计算。

      余弦距离:

      self._metric()函数中调用下述函数实现余弦距离的计算:

      

      3. 对未确认的trackers进行IOU匹配。

      匹配结果如下:

      4. 获得最终的匹配情况

      5. 进行跟踪器集合的更新

         A. 状态的转换

         B. 新跟踪器的生成

      6. 更新现有已确认的跟踪器的x和Pk

      7.进行可视化和保存

        

      

  • 相关阅读:
    追踪CPU跑满
    巧用Systemtap注入延迟模拟IO设备抖动
    用户态函数跟踪 (无调试符号)
    C#(同步调用、异步调用、异步回调)
    如何安装Python环境以及为Visual Studio 2012安装Python插件
    [转] FTP主动模式和被动模式的区别
    ORA-12154: TNS: 无法解析指定的连接标识符
    C# Enum,Int,String的互相转换 枚举转换
    2.Maven之(二)Maven生命周期
    1.Maven之(一)Maven是什么
  • 原文地址:https://www.cnblogs.com/monologuesmw/p/13534798.html
Copyright © 2020-2023  润新知