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.進行可視化和保存

    

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM