先引入多目標跟蹤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維狀態空間表示:
其中,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條軌跡之間的運動匹配程度。公式如下圖所示:
其中,
dj表示第j個檢測的狀態;
yi是軌跡在當前時刻的預測值;
si是軌跡,由kalman濾波器預測得到的;
通過該馬氏距離對檢測框進行篩選,使用卡方分佈的0.95分位點作爲閾值。
1.2.2外觀匹配
在實際中,比如相機運動,都會導致馬氏距離匹配失效,因此引入餘弦距離(第i次跟蹤和第j次檢測的最小余弦距離)來進行外觀匹配,該匹配對長時間遮擋後恢復尤其有用,公式如下:
最後,利用加權的方式對這兩個距離進行融合。關聯度量的總公式如下所示:
其中,λ設置爲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濾波的不確定性就會大大增加,爲了解決該問題 ,論文采用級聯匹配的策略來提高匹配精度。文中算法如下圖所示:
其中,T表示目標跟蹤集合
D表示目標檢測集合
C矩陣存放所有目標跟蹤與目標檢測之間距離的計算結果
B矩陣存放所有目標跟蹤與目標檢測之間是否關聯的判斷(0或者1)
M,U爲返回值,分別表示匹配集合和非匹配集合。
1.4深度表觀特徵(deep appearance descriptor)
論文中,作者用一個深度卷積神經網絡去提取目標的特徵信息,論文中的預訓練的網絡是在一個ReID的大數據集上訓練得到的,包含1261個人的1100000幅圖像,非常適合對人物目標跟蹤。
網絡結構如下:
該網絡有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
目標跟蹤。