多目標跟蹤-DeepSort分析(一)論文解讀及代碼架構

先引入多目標跟蹤DeepSort的論文地址及代碼鏈接(Python版):

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

代碼鏈接:https://github.com/nwojke/deep_sort

寫在前面:

    這些天看了deepsort的論文及源碼,並在網上檢索一些相關資料(不是很多),現結合論文、博客及自己的理解進行總結。
    本文爲第一篇,首先對論文進行解讀,然後對github的代碼進行簡要的流程分析及解讀,後期還會對所用算法進行具體的展開。
    
    本文結構:
        一、論文重點部分解讀
        二、代碼流程及算法分析

1. 論文重點部分解讀

1.1. 軌跡處理及狀態估計(track handing and state estimation)

第i時刻的狀態用8維狀態空間表示:
1
其中,u,v表示目標框的中心座標;
γ,h(第三和第四個值)表示寬高比(寬高比應該爲一個常量)、高度;
以上四個值構成觀測變量。
以及圖像座標系下的速度(導數)

對於每條軌跡 k 都有一個閾值a用於記錄軌跡從上一次成功匹配到當前時刻的時間。當該值大於提前設置的閾值 Amax 則認爲改軌跡終止,直觀上說就是長時間匹配不上的軌跡認爲已經結束。
軌跡的三種狀態:(在代碼中定義爲枚舉變量)
tentative(初始默認狀態)
confirmed
deleted

enum TrackState
{
     Tentative = 1
     Confirmed = 2
     Deleted = 3
}

在匹配時,對於沒有匹配成功的檢測都認爲可能產生新的軌跡。但由於這些檢測結果可能是一些錯誤警告,所以:
對這種新生成的軌跡標註狀態 tentative (初始默認狀態);
然後判定在接下來的連續3幀中是否連續匹配成功,若成功,則標註爲 confirmed ,認爲是新軌跡產生;
否則,標註爲 deleted,刪除 。另外,超過預先設置的Amax = 30 的軌跡,也被認爲離開場景,並標註爲 deleted,刪除 。

        if self.state == TrackState.Tentative:
            self.state = TrackState.Deleted
        elif self.time_since_update > self._max_age:
            self.state = TrackState.Deleted

1.2分配(匹配)問題(assignment problem)

這裏的匹配,是隻當前被標註爲“ confirmed ”的軌跡(即有效軌跡)與當前的檢測之間的匹配。
使用匈牙利算法進行目標框的匹配;
使用運動匹配和外觀匹配對目標框進行匹配。

1.2.1運動匹配

用Mahalanobis距離(馬氏距離)來表示第j個檢測和第i條軌跡之間的運動匹配程度。公式如下圖所示:
1
其中,
dj表示第j個檢測的狀態;
yi是軌跡在當前時刻的預測值;
si是軌跡,由kalman濾波器預測得到的;
通過該馬氏距離對檢測框進行篩選,使用卡方分佈的0.95分位點作爲閾值。

1.2.2外觀匹配

在實際中,比如相機運動,都會導致馬氏距離匹配失效,因此引入餘弦距離(第i次跟蹤和第j次檢測的最小余弦距離)來進行外觀匹配,該匹配對長時間遮擋後恢復尤其有用,公式如下:
1

最後,利用加權的方式對這兩個距離進行融合。關聯度量的總公式如下所示:
1
其中,λ設置爲0(論文中)。

1.3級聯匹配(matching cascade)

 //# Run matching cascade.
 typedef std::vector<int> IDS;
 
 struct RR
{
    std::vector<std::pair<int, int>> matches;
    IDS unmatched_detections; 
    IDS unmatched_tracks;
};
 RR _match(const std::vector<Detection> &detections)
    {
        int64_t mtm1 = line_gtm();
        //Split track set into confirmed and unconfirmed kalmanTrackers.
        IDS confirmed_trackIds;
        IDS unconfirmed_trackIds;
        for (int i = 0; i < kalmanTrackers_.size(); i++)
        {
            KalmanTracker t = kalmanTrackers_[i];
            if (t->is_confirmed())
            {
                confirmed_trackIds.push_back(i);
            }
            else
            {
                unconfirmed_trackIds.push_back(i);
            }
        }

        //# Associate confirmed kalmanTrackers using appearance features.
        RR rr = linear_assignment::matching_cascade(
            getCostMatrixByNND,
            NearestNeighborDistanceMetric::Instance()->matching_threshold(),
            max_age_,
            kalmanTrackers_,
            detections,
            &confirmed_trackIds);
        std::vector<std::pair<int, int>> matches_a = rr.matches;
        IDS unmatched_tracks_a = rr.unmatched_tracks;
        IDS unmatched_detections = rr.unmatched_detections;

        int64_t mtm2 = line_gtm();

        //# Associate remaining kalmanTrackers together with unconfirmed kalmanTrackers using IOU.
        IDS iou_track_candidateIds, tmp;
        std::copy(unconfirmed_trackIds.begin(),
                  unconfirmed_trackIds.end(),
                  std::back_inserter(iou_track_candidateIds));
        for (int k = 0; k < unmatched_tracks_a.size(); k++)
        {
            int id = unmatched_tracks_a[k];
            if (kalmanTrackers_[id]->time_since_update_ == 1)
            {
                iou_track_candidateIds.push_back(id);
            }
            else
            {
                tmp.push_back(id);
            }
        }
        unmatched_tracks_a.clear();
        unmatched_tracks_a = tmp;

        int64_t mtm3 = line_gtm();
        
        RR rr1 = linear_assignment::min_cost_matching(
            iou_matching::getCostMatrixByIOU,
            max_iou_distance_,
            kalmanTrackers_,
            detections,
            &iou_track_candidateIds,
            &unmatched_detections);
        std::vector<std::pair<int, int>> matches_b = rr1.matches;
        IDS unmatched_tracks_b = rr1.unmatched_tracks;
        unmatched_detections = rr1.unmatched_detections;

        int64_t mtm4 = line_gtm();
        
        RR re;
        re.matches = matches_a;
        std::copy(matches_b.begin(), matches_b.end(),
                  std::back_inserter(re.matches));
        re.unmatched_detections = unmatched_detections;
        re.unmatched_tracks = unmatched_tracks_a;
        std::copy(unmatched_tracks_b.begin(),
                  unmatched_tracks_b.end(),
                  std::back_inserter(re.unmatched_tracks));
        int64_t mtm5 = line_gtm();
        return re;
    }

    int _NewTrack(const Detection &detection)
    {
        int id = _next_id_;
        std::pair<MEAN, VAR> pa =
            KF::Instance()->initiate(detection.to_xyah());
        KalmanTracker newt(new KalmanTrackerN(
            pa.first, pa.second, _next_id_, n_init_, max_age_,
            detection.feature_, true, detection.oriPos_));
        kalmanTrackers_.push_back(newt); 
        _next_id_ += 1;
        return id;
    }
};
RR rr = this->_match(detections);

當一個目標被遮擋很長時間,Kalman濾波的不確定性就會大大增加,爲了解決該問題 ,論文采用級聯匹配的策略來提高匹配精度。文中算法如下圖所示:
1
其中,T表示目標跟蹤集合
D表示目標檢測集合
C矩陣存放所有目標跟蹤與目標檢測之間距離的計算結果
B矩陣存放所有目標跟蹤與目標檢測之間是否關聯的判斷(0或者1)
M,U爲返回值,分別表示匹配集合和非匹配集合。

1.4深度表觀特徵(deep appearance descriptor)

論文中,作者用一個深度卷積神經網絡去提取目標的特徵信息,論文中的預訓練的網絡是在一個ReID的大數據集上訓練得到的,包含1261個人的1100000幅圖像,非常適合對人物目標跟蹤。
網絡結構如下:
CNN
該網絡有2,800,864參數和32個目標框,在NVIDIA GTX1050上需要30ms。

2.代碼流程及算法分析(待補充)

2.1detection

檢測基類。

2.2HungarianOper

匈牙利指派,採用的是匈牙利算法/Hungrain/帶權重的二分圖指派算法Munkres Alogrithm。

2.3iou_matching

IOU匹配模塊。(Iou–重疊區域面積)

2.4kalman_filter

卡爾曼濾波器,該模塊實現圖像空間的目標狀態的預測/創建及移除,即濾波的具體參數化。

2.5linear_assignment

線性匹配–用最小的cost-matirx匹配級聯。考慮了運動信息和外觀信息。

for (int row = 0; row < track_indices.size(); row++)
        {
            int track_idx = track_indices[row];
            KalmanTracker track = tracks[track_idx];
 //計算detection中邊框 dj(u,v,r,h)dj​(u,v,r,h)和Track中的邊框 yiyi​之間的馬氏距離
//計算 predicted Kalman states 和newly arrived measurements之間的馬氏距離
Eigen::Matrix<float, 1, -1> gating_distance = kalmanFilter.gating_distance(
              track->mean_, track->covariance_, measurements, only_position);
          for (int i = 0; i < gating_distance.cols(); i++)
          // gating_distance is a vector
          {
              if (gating_distance(0, i) > gating_threshold)
              {
                  cost_matrix(row, i) = gated_cost;
              }
          }
      }
      return cost_matrix;

2.6nn_matching

最近鄰匹配模塊。

2.7tracker

目標跟蹤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章