【多目標跟蹤】FairMOT(原理與代碼詳解)

建議先讀文獻[1],然後配合着看本文。

1. 前言

上一篇文章針對FairMOT進行了測試和訓練,測試的效果很好。這裏對FairMOT的訓練和測試原理進行剖析。

2. 訓練

FairMOT基本採用的是檢測+跟蹤的思路,檢測採用的centernet,跟蹤採用deepsort。但是將兩個任務進行了端到端訓練。其實訓練還是相對簡單的。
初始問題

  • 檢測和跟蹤是如何一起端到端訓練的?

2.1 模型結構圖

先來一張論文的模型圖
在這裏插入圖片描述

下圖是我自己理解所畫的輸出部分。詳細內部圖
在這裏插入圖片描述

2.2 詳解

  1. 檢測部分
    檢測部分,可以參考我的看代碼解讀CenterNet :Objects as Points

  2. reid部分
    這篇文章將它處理成了一個分類任務。假設訓練數據集中出現了10000個需要跟蹤的id(一個id,就是出現到消失的物體,不管其是什麼類別),在跟蹤分支中就對有object的點,採用線性層Linear(512×10000)輸出,然後在利用交叉熵損失函數計算id_loss

  3. loss 部分
    如我畫的圖

3. 推理

推理是相對較難的部分。其核心是採用deepsort,文獻1講的很好。

前期準備:

  • 卡爾曼濾波
  • 匈牙利算法
  • deepsort算法流程

3.1 變量說明

狀態變量說明
activated:激活,用於單次誤檢目標的判斷
track_state: 跟蹤狀態,有tracked, lost, remove

4種容器

  • unconfirmed_stracks(activated = F, track_state=tracked ) 只出現一次的目標(檢測器誤檢的目標)
  • activated_stracks(activate=T, track_state=tracked) 跟蹤狀態良好的tracker
  • lost_stracks(activate=T, track_state=lost)激活,但是跟蹤狀態丟失
  • refind_stracks(activated=T, track_state=lost->tracked)跟丟之後重新找回的目標

1種tracker容器
tracker[ activated(激活狀態), tracked(跟蹤狀態),mean(卡爾曼均值向量), covariance(卡爾曼協方差矩陣), smooth_feat(tracker的外觀特徵) ]

3.2 具體流程(代碼+解讀)


1. 第一幀初始化

  • 用centernet檢測得到目標(x,y,w,h)
  • 對每個目標初始化一個tracker,就用n個tracker
  • 將n個tracker放入activated_stracks容器中
for inew in u_detection:            # 對cosine/iou/uncofirmed_tracker都未匹配的detection重新初始化一個unconfimed_tracker
    track = detections[inew]
    if track.score < self.det_thresh:
        continue
    track.activate(self.kalman_filter, self.frame_id)       # 激活track,第一幀的activated=T,其他爲False
    activated_stacks.append(track)

第二幀
2. 對新檢測目標進行外觀+距離匹配

  • 對第2幀進行目標檢測得detections(這也是初始化成tracker的)
  • 將[activated_stracks lost_stracks]融合成pool_stracks
  • detections和pool_stracks根據feat計算外觀cost_矩陣,就是用feat計算cosine距離
  • 利用卡爾曼算法預測pool_stracks的新的mean,covariance
  • 計算pool_stracks和detection的距離cost,並將大於距離閾值外觀cost_矩陣賦值爲inf
  • 利用匈牙利算法進行匹配
    • 能匹配的
      • pool_stracks的track_state==tracked,更新smooth_feat,卡爾曼狀態更新mean,covariance(卡爾曼用),計入activated_stracks
      • pool_stracks的track_state==tracked,更新smooth_feat,卡爾曼狀態更新mean,covariance(卡爾曼用),
        計入refind_stracks
    • 不能匹配的
      • 提出不能匹配的,得到新的detections,r_tracked_stracks
strack_pool = joint_stracks(tracked_stracks, self.lost_stracks)
dists = matching.embedding_distance(strack_pool, detections)    # 計算新檢測出來的目標和tracked_tracker之間的cosine距離
STrack.multi_predict(strack_pool)  # 卡爾曼預測
dists = matching.fuse_motion(self.kalman_filter, dists, strack_pool, detections)        # 利用卡爾曼計算detection和pool_stacker直接的距離代價
matches, u_track, u_detection = matching.linear_assignment(dists, thresh=0.7)           # 匈牙利匹配 // 將跟蹤框和檢測框進行匹配 // u_track是未匹配的tracker的索引,

for itracked, idet in matches:      # matches:63*2 , 63:detections的維度,2:第一列爲tracked_tracker索引,第二列爲detection的索引
    track = strack_pool[itracked]
    det = detections[idet]
    if track.state == TrackState.Tracked:
        track.update(det, self.frame_id)       # 匹配的pool_tracker和detection,更新特徵和卡爾曼狀態
        activated_starcks.append(track)
    else:
        track.re_activate(det, self.frame_id, new_id=False)     # 如果是在lost中的,就重新激活
        refind_stracks.append(track)

  1. 對剩下的detections,r_tracked_stracks進行IOU匹配
  • 計算detections,r_tracked_stracks計算IOU cost矩陣
  • 針對IOU cost進行匈牙利匹配
    • 能匹配的
      • pool_stracks的track_state==tracked,更新smooth_feat,卡爾曼狀態更新mean,covariance(卡爾曼用),計入activated_stracks
      • pool_stracks的track_state==tracked,更新smooth_feat,卡爾曼狀態更新mean,covariance(卡爾曼用),
        計入refind_stracks
    • 不能匹配的
      • r_tracked_stracks狀態track_state改爲lost
      • detections再遺留到下一步進行繼續匹配
detections = [detections[i] for i in u_detection]           # u_detection是未匹配的detection的索引
r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked]
dists = matching.iou_distance(r_tracked_stracks, detections)
matches, u_track, u_detection = matching.linear_assignment(dists, thresh=0.5)

for itracked, idet in matches:
    track = r_tracked_stracks[itracked]
    det = detections[idet]
    if track.state == TrackState.Tracked:
        track.update(det, self.frame_id)
        activated_starcks.append(track)
    else:
        track.re_activate(det, self.frame_id, new_id=False)         # 前面已經限定了是TrackState.Tracked,這裏是不用運行到的。
        refind_stracks.append(track)

for it in u_track:
    track = r_tracked_stracks[it]
    if not track.state == TrackState.Lost:
        track.mark_lost()
        lost_stracks.append(track)      # 將和tracked_tracker iou未匹配的tracker的狀態改爲lost

  1. 上一步遺留的detection與unconfirmed_stracks進行IOU匹配
  • 計算IOU cost
  • 匈牙利匹配
    • 能匹配
      • 更新 unconfirmed_stracks,更新smooth_feat,卡爾曼狀態更新mean,covariance(卡爾曼用),計入activated_stracks
    • 不能匹配
      • unconfirmed_stracks直接計入removed_stracks
      • 不能匹配的detections,在遺留到下一步
detections = [detections[i] for i in u_detection]       # 將cosine/iou未匹配的detection和unconfirmed_tracker進行匹配
dists = matching.iou_distance(unconfirmed, detections)
matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)
for itracked, idet in matches:
    unconfirmed[itracked].update(detections[idet], self.frame_id)
    activated_starcks.append(unconfirmed[itracked])
for it in u_unconfirmed:
    track = unconfirmed[it]
    track.mark_removed()
    removed_stracks.append(track)

  1. 上一步遺留的detections,初始化成unconfirmed_stracks中的tracker
for inew inu_detection:            # 對cosine/iou/uncofirmed_tracker都未匹配的detection重新初始化一個unconfimed_tracker
    track = detections[inew]
    if track.score < self.det_thresh:
        continue
    track.activate(self.kalman_filter, self.frame_id)       # 激活track,第一幀的activated=T,其他爲False
    activated_starcks.append(track)

  1. 對15幀連續track_state=lost的tracker,進行刪除
for track in self.lost_stracks:
    if self.frame_id - track.end_frame > self.max_time_lost:        # 消失15幀之後
        track.mark_removed()
        removed_stracks.append(track)

4. 最後再來看一下效果

FAIRMOT多目標跟蹤

5. TO DO

  • 根據原理來說,該模型是支持多類別,多目標的跟蹤的,找時間實現一下。

reference

  1. https://zhuanlan.zhihu.com/p/90835266
  2. https://github.com/chenjun2hao/FairMOT,帶註釋項目
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章