智慧交通day02-车流量检测实现10:多目标追踪实现
生活随笔
收集整理的這篇文章主要介紹了
智慧交通day02-车流量检测实现10:多目标追踪实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在這里我們主要實現了一個多目標跟蹤器,管理多個卡爾曼濾波器對象,主要包括以下內容:
- 初始化:最大檢測數,目標未被檢測的最大幀數
-
目標跟蹤結果的更新,即跟蹤成功和失敗的目標的更新
-
初始化
def __init__(self, max_age=1, min_hits=3):"""初始化:設置SORT算法的關鍵參數 """# 最大檢測數:目標未被檢測到的幀數,超過之后會被刪 self.max_age = max_age# 目標命中的最小次數,小于該次數不返回self.min_hits = min_hits # 卡爾曼跟蹤器self.trackers = [] # 幀計數self.frame_count = 0
1、目標跟蹤結果的更新:
該方法實現了SORT算法,輸入是當前幀中所有物體的檢測框的集合,包括目標的score,輸出是當前幀標的跟蹤框集合,包括目標的跟蹤的id要求是即使檢測框為空,也必須對每一幀調用此方法,返回一個類似的輸出數組,最后一列是目標對像的id。需要注意的是,返回的目標對象數量可能與檢測框的數量不同.
def update(self, dets):self.frame_count += 1# 在當前幀逐個預測軌跡位置,記錄狀態異常的跟蹤器索引# 根據當前所有的卡爾曼跟蹤器個數(即上一幀中跟蹤的目標個數)創建二維數組:行號為卡爾曼濾波器的標識索引,列向量為跟蹤框的位置和IDtrks = np.zeros((len(self.trackers), 5)) # 存儲跟蹤器的預測to_del = [] # 存儲要刪除的目標框ret = [] # 存儲要返回的追蹤目標框# 循環遍歷卡爾曼跟蹤器列表for t, trk in enumerate(trks):# 使用卡爾曼跟蹤器t產生對應目標的跟蹤框pos = self.trackers[t].predict()[0]# 遍歷完成后,trk中存儲了上一幀中跟蹤的目標的預測跟蹤框trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]# 如果跟蹤框中包含空值則將該跟蹤框添加到要刪除的列表中if np.any(np.isnan(pos)):to_del.append(t)# numpy.ma.masked_invalid 屏蔽出現無效值的數組(NaN 或 inf)# numpy.ma.compress_rows 壓縮包含掩碼值的2-D 數組的整行,將包含掩碼值的整行去除# trks中存儲了上一幀中跟蹤的目標并且在當前幀中的預測跟蹤框trks = np.ma.compress_rows(np.ma.masked_invalid(trks))# 逆向刪除異常的跟蹤器,防止破壞索引for t in reversed(to_del):self.trackers.pop(t)# 將目標檢測框與卡爾曼濾波器預測的跟蹤框關聯獲取跟蹤成功的目標,新增的目標,離開畫面的目標matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets, trks)# 將跟蹤成功的目標框更新到對應的卡爾曼濾波器for t, trk in enumerate(self.trackers):if t not in unmatched_trks:d = matched[np.where(matched[:, 1] == t)[0], 0]# 使用觀測的邊界框更新狀態向量trk.update(dets[d, :][0])# 為新增的目標創建新的卡爾曼濾波器對象進行跟蹤for i in unmatched_dets:trk = KalmanBoxTracker(dets[i, :])self.trackers.append(trk)# 自后向前遍歷,僅返回在當前幀出現且命中周期大于self.min_hits(除非跟蹤剛開始)的跟蹤結果;如果未命中時間大于self.max_age則刪除跟蹤器。# hit_streak忽略目標初始的若干幀i = len(self.trackers)for trk in reversed(self.trackers):# 返回當前邊界框的估計值d = trk.get_state()[0]# 跟蹤成功目標的box與id放入ret列表中if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):ret.append(np.concatenate((d, [trk.id + 1])).reshape(1, -1)) # +1 as MOT benchmark requires positivei -= 1# 跟蹤失敗或離開畫面的目標從卡爾曼跟蹤器中刪除if trk.time_since_update > self.max_age:self.trackers.pop(i)# 返回當前畫面中所有目標的box與id,以二維矩陣形式返回if len(ret) > 0:return np.concatenate(ret)return np.empty((0, 5))我們將上述兩個方法封裝在一個類中。
總結
了解sort進行多目標跟蹤的實現
功能實現
# 1.SORT目標跟蹤: # 1.第一幀剛開始時:對第一幀所有的檢測框生成對應的新跟蹤框。 # 2.第二幀開始到以后所有幀: # 上一幀成功跟蹤并且保留下來的的跟蹤框 在當前幀中 進行新一輪的預測新的跟蹤框, # 并且針對所預測的新跟蹤框和當前幀中的檢測框進行iou計算和使用匈牙利算法對該兩者進行關聯匹配, # 通過上述操作后成功返回跟蹤目標成功的跟蹤框(即和當前幀中的目標檢測框相匹配的跟蹤框), # 并且另外發現了新出現目標的檢測框、跟蹤目標失敗的跟蹤框(即目標離開了畫面/兩者匹配度IOU值小于iou閾值), # 那么首先使用當前幀中的檢測框對“成功關聯匹配的跟蹤框中的”狀態向量進行更新, # 然后對新增目標的檢測框生成對應新的跟蹤框,最后把跟蹤目標失敗的跟蹤框從跟蹤器鏈列表中移除出去。 # 2.傳入的檢測框dets:[檢測框的左上角的x/y坐標, 檢測框的右下角的x/y坐標, 檢測框的預測類別的概率值] # 3.返回值tracks: # 當前幀中跟蹤目標成功的跟蹤框/預測框的集合,包含目標的跟蹤的id(也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個) # 第一種返回值方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度, trk.id] ...] # 第二種返回值方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, trk.id] ...] # d:[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標] # trk.id:卡爾曼濾波器的個數/目標框的個數,也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個。 #Sort多目標跟蹤 管理多個卡爾曼濾波器 class Sort(object):"""Sort 是一個多目標跟蹤器的管理類,管理多個 跟蹤器鏈中的多個 KalmanBoxTracker 卡爾曼濾波對象"""#設置Sort算法的參數def __init__(self,max_age = 1,min_hits = 3):"""初始化:設置SORT算法的關鍵參數:param max_age: 最大檢測數:目標未被檢測到的幀數,超過之后會被刪除:param min_hits: 目標命中的最小次數,小于該次數update函數不返回該目標的KalmanBoxTracker卡爾曼濾波對象""""""max_age:跟蹤框的最大連續跟丟幀數。如果當前跟蹤框連續N幀大于最大連續跟丟幀數的話,則從跟蹤器鏈中刪除該卡爾曼濾波對象的預測框(跟蹤框)。min_hits:跟蹤框連續成功跟蹤到目標的最小次數(目標連續命中的最小次數),也即跟蹤框至少需要連續min_hits次成功跟蹤到目標。trackers:卡爾曼濾波跟蹤器鏈,存儲多個 KalmanBoxTracker 卡爾曼濾波對象frame_count:當前視頻經過了多少幀的計數"""# 最大檢測數:目標未被檢測到的幀數,超過之后會被刪set.max_age = max_age# 目標連續命中的最小次數,小于該次數update函數不返回該目標的KalmanBoxTracker卡爾曼濾波對象self.min_hits=min_hits# 卡爾曼濾波跟蹤器鏈,存儲多個 KalmanBoxTracker 卡爾曼濾波對象self.trackers = []#幀計數self.frane_count = 0"""update(dets):輸入dets:當前幀中yolo所檢測出的所有目標的檢測框的集合,包含每個目標的score以[[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]形式輸入的numpy.arrayx1、y1 代表檢測框的左上角坐標;x2、y2代表檢測框的右上角坐標;score代表檢測框對應預測類別的概率值。輸出ret:當前幀中跟蹤目標成功的跟蹤框/預測框的集合,包含目標的跟蹤的id(也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個)第一種返回值方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度, trk.id] ...]第二種返回值方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, trk.id] ...]d:[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]trk.id:卡爾曼濾波器的個數/目標框的個數,也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個。注意:即使檢測框為空,也必須對每一幀調用此方法,返回一個類似的輸出數組,最后一列是目標對像的id。返回的目標對象數量可能與檢測框的數量不同。"""#更新數值def update(self,dets):"""該方法實現了SORT算法,輸入是當前幀中所有物體的檢測框的集合,包括目標的score,輸出是當前幀目標的跟蹤框集合,包括目標的跟蹤的id要求是即使檢測框為空,也必須對每一幀調用此方法,返回一個類似的輸出數組,最后一列是目標對像的id注意:返回的目標對象數量可能與檢測框的數量不同:param dets:以[[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]形式輸入的numpy.array:return:"""""" 每經過一幀,frame_count+=1"""self.frane_count +=1"""1.trackers:上一幀中的跟蹤器鏈(列表),保存的是上一幀中成功跟蹤目標的跟蹤框,也即上一幀中成功跟蹤目標的KalmanBoxTracker卡爾曼濾波對象。2.trks = np.zeros((len(trackers), 5))上一幀中的跟蹤器鏈(列表)中的所有跟蹤框(卡爾曼濾波對象)在當前幀中成功進行predict預測新跟蹤框后返回的值。所有新跟蹤框的左上角的x坐標和y坐標、右下角的x坐標和y坐標、置信度 的一共5個值。1.因為一開始第一幀時,trackers跟蹤器鏈(列表)仍然為空,所以此時的trks初始化如下:np.zeros((0, 5)) 輸出值:array([], shape=(0, 5), dtype=float64)輸出值類型:<class 'numpy.ndarray'>2.np.zeros((len(trackers), 5)) 創建目的:1.用于存儲上一幀中的跟蹤器鏈中所有跟蹤框(KalmanBoxTracker卡爾曼濾波對象)在當前幀中進行predict預測新跟蹤框后返回的值,之所以創建的numpy數組的列數為5,是因為一個跟蹤框在當前幀中進行predict預測新跟蹤框后返回的值為1行5列的矩陣,返回值分別為新跟蹤框的左上角的x坐標和y坐標、右下角的x坐標和y坐標、置信度 的一共5個值。2.如果是在視頻的第一幀中,那么因為跟蹤器鏈不存在任何跟蹤框(KalmanBoxTracker卡爾曼濾波對象),因此np.zeros((len(trackers), 5))創建的是空列表:array([], shape=(0, 5), dtype=float64)。3.trackers:跟蹤器鏈(列表)1.跟蹤器鏈中存儲了上一幀中成功跟蹤目標并且在當前幀中的預測框(跟蹤框),同時也存儲了“為了當前幀中的檢測框中的新增目標所創建的”新預測框(新跟蹤框),但是同時不存儲當前幀中預測跟蹤失敗的預測框(跟蹤框),同時也不存儲2.跟蹤器鏈實際就是多個的卡爾曼濾波KalmanBoxTracker自定義類的實例對象組成的列表。每個目標框都有對應的一個卡爾曼濾波器(KalmanBoxTracker實例對象),KalmanBoxTracker類中的實例屬性專門負責記錄其對應的一個目標框中各種統計參數,并且使用類屬性負責記錄卡爾曼濾波器的創建個數,增加一個目標框就增加一個卡爾曼濾波器(KalmanBoxTracker實例對象)。把每個卡爾曼濾波器(KalmanBoxTracker實例對象)都存儲到跟蹤器鏈(列表)中。"""# 存儲跟蹤器在當前幀逐個預測軌跡位置,記錄狀態異常的跟蹤器索引# 根據當前所有的卡爾曼跟蹤器個數(即上一幀中跟蹤的目標個數)創建二維數組:行號為卡爾曼濾波器的標識索引,列向量為跟蹤框的位置和IDtrks = np.zeros(len(self.trackers),5)#跟蹤器對當前幀的圖像預測結果""" to_del:存儲“跟蹤器鏈中某個要刪除的”KalmanBoxTracker卡爾曼濾波對象的索引 """to_del = []#存儲要刪除的目標框ret = []#返回的跟蹤目標#遍歷卡爾曼濾波器中的跟蹤框"""for t, trk in enumerate(ndarray類型的trks)t:為從0到列表長度-1的索引值trk:ndarray類型的trks中每個(1, 5)形狀的一維數組"""""" 遍歷trks 用于存儲上一幀中的跟蹤器鏈中所有跟蹤框(KalmanBoxTracker卡爾曼濾波對象)在當前幀中進行predict預測新跟蹤框后返回的值 """for t,trk in enumerate(trks):""" 上一幀中的跟蹤器鏈中所有跟蹤框(KalmanBoxTracker卡爾曼濾波對象)在當前幀中進行predict預測新跟蹤框 """#使用卡爾曼跟蹤器t產生對應目標的跟蹤框,即對目標進行預測pos = self.trackers[t].predict()[0]""" 新跟蹤框的左上角的x坐標和y坐標、右下角的x坐標和y坐標、置信度 的一共5個值。trk中存儲了上一幀中目標的跟蹤框在當前幀中新的跟蹤框的信息值。"""# 遍歷完成后,trk中存儲了上一幀中跟蹤的目標的預測結果的跟蹤框trk[:] = [pos[0],pos[1],pos[2],pos[3],0]""" 如果預測的新的跟蹤框的信息(1行5列一共5個值)中包含空值的話,則將該跟蹤框在跟蹤器鏈(列表)中的索引值t放到to_del列表中。使用np.any(np.isnan(pos))即能判斷這1行5列一共5個值是否包含空值。后面下一步將會根據to_del列表中保存的跟蹤框的索引值到跟蹤器鏈(列表)中將該跟蹤框從其中移除出去。"""#若預測結果pos中包含空值,添加到del中if np.any(np.isnan(pos)):to_del.append(t)""" np.ma.masked_invalid(跟蹤器鏈trks矩陣):將會對跟蹤器鏈trks矩陣中出現了NaN或inf的某行進行生成掩碼,用于屏蔽出現無效值該整行的跟蹤器框。np.ma.compress_rows(包含掩碼值的跟蹤器鏈trks矩陣):將包含掩碼值的整行從中進行移除出去。最終跟蹤器鏈trks矩陣:只包含“上一幀中的跟蹤器鏈中所有跟蹤框在當前幀中成功進行predict預測”的新跟蹤框。"""#trks中去除無效值的行,保存根據上一幀結果預測當前幀的內容# numpy.ma.masked_invalid 屏蔽出現無效值的數組(NaN 或 inf)# numpy.ma.compress_rows 壓縮包含掩碼值的2-D 數組的整行,將包含掩碼值的整行去除# trks中存儲了上一幀中跟蹤的目標并且在當前幀中的預測跟蹤框trks = np.ma.compress_rows(np.ma.masked_invalid(trks))"""1.for t in reversed(列表):1.t:列表中的元素值2.要想從List列表中刪除任意索引位置的元素的話,必須不能從列表頭開始遍歷刪除元素,必須從列表尾向列表頭的方向進行遍歷刪除元素,因為如果從列表頭開始遍歷刪除元素的話,便會導致后面的元素會移動補充到被刪除元素的索引位置上,那么再向后進行遍歷時便會出現漏遍歷的元素,也即防止破壞索引,因此刪除列表中元素時需要從列表尾向列表頭的方向進行遍歷。2.for t in reversed(to_del)1.t:列表中的元素值2.此處to_del列表中的元素值保存的是trackers跟蹤器鏈(列表)中要刪除元素的索引值,因此從to_del列表的列表尾向列表頭的方向進行遍歷出“trackers跟蹤器鏈(列表)中要刪除元素的”索引值。然后使用trackers.pop(t)根據trackers跟蹤器鏈(列表)中元素的索引值t自動從列表中移除該元素。3.List pop()方法1.pop()方法語法:list.pop([index=-1])2.pop()函數用于移除列表中的一個元素(默認最后一個元素),并且返回該元素的值。3.pop(可選參數)中參數:可選參數,要移除列表元素的索引值,不能超過列表總長度,默認為 index=-1,刪除最后一個列表值。4.pop()返回值:該方法返回從列表中被移除的元素對象。5.pop(要移除的列表中元素的索引值):根據列表中元素的索引值自動從列表中移除"""#刪除nan的結果,逆向刪除異常的跟蹤器,防止破壞索引for t in reversed(to_del):"""根據to_del列表中保存的跟蹤框的索引值到跟蹤器鏈(列表)中將該跟蹤框從其中移除出去。trackers:上一幀中的跟蹤器鏈(列表),保存的是上一幀中成功跟蹤目標的跟蹤框,也即成功跟蹤目標的KalmanBoxTracker卡爾曼濾波對象。trackers.pop(要移除的某個跟蹤框的索引值):即能根據該索引值從跟蹤器鏈(列表)中把該跟蹤框移除出去"""# pop(要移除的列表中元素的索引值):根據列表中元素的索引值自動從列表中移除self.trackers.pop(t)"""matches:[[檢測框的索引值, 跟蹤框的索引值] [檢測框的索引值, 跟蹤框的索引值] 。。。]跟蹤成功并且兩兩匹配組合的IOU值大于iou閾值的檢測框和跟蹤框組成的矩陣unmatched_detections:[檢測框的索引值,。。。]1.新增目標的檢測框在detections檢測框列表中的索引位置2.兩兩匹配組合的IOU值小于iou閾值的檢測框在detections檢測框列表中的索引位置unmatched_trackers:[跟蹤框的索引值,。。。]1.跟蹤失敗的跟蹤框/預測框在trackers跟蹤框列表中的索引位置2.兩兩匹配組合的IOU值小于iou閾值的跟蹤框/預測框在trackers跟蹤框列表中的索引位置1.matched:跟蹤成功目標的矩陣。即前后幀都存在的目標,并且匹配成功同時大于iou閾值。2.unmatched_detections(列表):1.檢測框中出現新目標,但此時預測框(跟蹤框)中仍不不存在該目標,那么就需要在創建新目標對應的預測框/跟蹤框(KalmanBoxTracker類的實例對象),然后把新目標對應的KalmanBoxTracker類的實例對象放到跟蹤器鏈(列表)中。2.同時如果因為“跟蹤框和檢測框之間的”兩兩組合的匹配度IOU值小于iou閾值,則也要把目標檢測框放到unmatched_detections中。3.unmatched_trackers(列表):1.當跟蹤目標失敗或目標離開了畫面時,也即目標從檢測框中消失了,就應把目標對應的跟蹤框(預測框)從跟蹤器鏈中刪除。unmatched_trackers列表中保存的正是跟蹤失敗即離開畫面的目標,但該目標對應的預測框/跟蹤框(KalmanBoxTracker類的實例對象)此時仍然存在于跟蹤器鏈(列表)中,因此就需要把該目標對應的預測框/跟蹤框(KalmanBoxTracker類的實例對象)從跟蹤器鏈(列表)中刪除出去。2.同時如果因為“跟蹤框和檢測框之間的”兩兩組合的匹配度IOU值小于iou閾值,則也要把跟蹤目標框放到unmatched_trackers中。"""#使用匈牙利算法:將目標檢測框和卡爾曼濾波器預測的跟蹤框進行匹配,分別獲取跟蹤成功的目標,新增的目標,離開畫面的目標matched,unmatched_dets,unmatche_trkes = associate_detection_to_tracker(dets,trks)"""for t, trk in enumerate(trackers列表)t:為從0到列表長度-1的索引值trk:trackers列表中每個KalmanBoxTracker卡爾曼濾波對象"""#將跟蹤成功的目標更新到對應的卡爾曼濾波器for t,trk in enumerate(self.trackers):""" 1.trackers:上一幀中的跟蹤器鏈(列表),保存的是上一幀中成功跟蹤目標的跟蹤框,也即成功跟蹤目標的KalmanBoxTracker卡爾曼濾波對象。2.for t, trk in enumerate(trackers):遍歷上一幀中的跟蹤器鏈(列表)中從0到列表長度-1的索引值t 和 每個KalmanBoxTracker卡爾曼濾波對象trk。3.if t not in unmatched_trks:如果上一幀中的跟蹤框(KalmanBoxTracker卡爾曼濾波對)的索引值不在當前幀中的unmatched_trackers(列表)中的話,即代表上一幀中的跟蹤框在當前幀中成功跟蹤到目標,并且代表了“上一幀中的跟蹤框在當前幀中的”預測框和當前幀中的檢測框的匹配度IOU值大于iou閾值。4.matched[:, 1]:獲取的是跟蹤框的索引值,即[[檢測框的索引值, 跟蹤框的索引值] 。。。]中的跟蹤框的索引值。5.np.where(matched[:, 1] == t)[0]:where返回的為符合條件的“[檢測框的索引值, 跟蹤框的索引值]”數組在matched矩陣中的索引值,即行值。因此最后使用[0]就是從array([索引值/行值])中把索引值/行值取出來。6.matched[索引值/行值, 0]:根據索引值/行值獲取出matched矩陣中的[檢測框的索引值, 跟蹤框的索引值],然后獲取出第一列的“檢測框的索引值”。7.dets[d, :]:根據檢測框的索引值/行值從當前幀中的dets檢測框列表獲取出該檢測框的所有列值,最終返回的是一個二維矩陣如下所示:第一種方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度]]第二種方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]]8.dets[d, :][0]:獲取出[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]9.trk.update(檢測框的5個值的列表):使用檢測框進行更新狀態更新向量x(狀態變量x),也即使用檢測框更新跟蹤框。"""if t not in unmatche_trkes:d = matched[np.where(matched[:, 1] == t)[0], 0]# 使用觀測的邊界框更新狀態向量trk.update(dets[d, :][0])"""unmatched_detections(列表)保存了出現新目標的檢測框的索引值,還保存了“因為跟蹤框和檢測框之間的兩兩組合的匹配度IOU值小于iou閾值的”目標檢測框的索引值。dets[i, :]:根據索引值從當前幀中的檢測框列表dets中獲取對應的檢測框,即該行的所有列值。該檢測框的值為:第一種方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度]]第二種方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]]KalmanBoxTracker(dets[i, :]):傳入檢測框進行創建該新目標對應的跟蹤框KalmanBoxTracker卡爾曼濾波對象trk。每個目標框都有對應的一個卡爾曼濾波器(KalmanBoxTracker實例對象),增加一個目標框就增加一個卡爾曼濾波器(KalmanBoxTracker實例對象)。trackers.append(trk):把新增的卡爾曼濾波器(KalmanBoxTracker實例對象trk)存儲到跟蹤器鏈(列表)trackers中"""#為新增目標創建新的卡爾曼濾波器的跟蹤器for i in unmatched_dets:trk = KalmanBoxTracker(dets[i,0])self.trackers.append(trk)# 自后向前遍歷,僅返回在當前幀出現且命中周期大于self.min_hits(除非跟蹤剛開始)的跟蹤結果;如果未命中時間大于self.max_age則刪除跟蹤器。# hit_streak忽略目標初始的若干幀""" i為trackers跟蹤器鏈(列表)長度,從列表尾向列表頭的方向 每遍歷trackers跟蹤器鏈(列表)一次 即進行 i-=1 """i = len(self.trackers)""" reversed逆向遍歷trackers跟蹤器鏈(列表),目的為刪除列表中的元素的同時不會造成漏遍歷元素的問題 """# 逆向遍歷for trk in reversed(self.trackers):""" (跟蹤框)KalmanBoxTracker卡爾曼濾波對象trk.get_state():獲取跟蹤框所預測的在當前幀中的預測結果(已經從[x,y,s,r]轉換為[x1,y1,x2,y2]) [x1,y1,x2,y2]即為[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]。get_state()[0] 中使用[0] 是因為返回的為二維矩陣如下: 第一種方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度]]第二種方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]]"""#返回當前邊界框的估計值d = trk.get_state()[0]"""1.trk.time_since_update < 1:1.time_since_update:記錄了該目標對應的卡爾曼濾波器中的預測框(跟蹤框)進行連續預測的次數,每執行predict一次即進行time_since_update+=1。在連續預測(連續執行predict)的過程中,一旦執行update的話,time_since_update就會被重置為0。2. time_since_update < 1:該目標對應的卡爾曼濾波器一旦update更新的話該變量值便重置為0,因此要求該目標對應的卡爾曼濾波器必須執行update更新步驟。update更新代表了使用檢測框來更新狀態更新向量x(狀態變量x)的操作,實際即代表了使用“通過yoloV3得到的并且和預測框(跟蹤框)相匹配的”檢測框來更新該目標對應的卡爾曼濾波器中的預測框(跟蹤框)。2.trk.hit_streak >= min_hits:1.hit_streak1.連續更新的次數,每執行update一次即進行hit_streak+=1。2.在連續更新(連續執行update)的過程中,一旦開始連續執行predict兩次或以上的情況下,當連續第一次執行predict時,因為time_since_update仍然為0,并不會把hit_streak重置為0,然后才會進行time_since_update+=1;當連續第二次執行predict時,因為time_since_update已經為1,那么便會把hit_streak重置為0,然后繼續進行time_since_update+=1。 2.min_hits跟蹤框連續成功跟蹤到目標的最小次數,也即跟蹤框至少需要連續min_hits次成功跟蹤到目標。3.hit_streak >= min_hits跟蹤框連續更新的次數hit_streak必須大于等于min_hits。而小于該min_hits次數的話update函數不返回該目標的KalmanBoxTracker卡爾曼濾波對象。3.frame_count <= min_hits:因為視頻的一開始frame_count為0,而需要每經過一幀frame_count才會+=1。因此在視頻的一開始前N幀中,即使frame_count 小于等于min_hits 也可以。"""# 跟蹤成功目標的box與id放入ret列表中if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):""" 1.ret:當前幀中跟蹤目標成功的跟蹤框/預測框的集合,包含目標的跟蹤的id(也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個)第一種返回值方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度, trk.id] ...]第二種返回值方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, trk.id] ...]d:[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]trk.id:卡爾曼濾波器的個數/目標框的個數,也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個。2.np.concatenate((d, [trk.id + 1])).reshape(1, -1)[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, 該跟蹤框是創建出來的第幾個]]"""ret.append(np.concatenate((d, [trk.id + 1])).reshape(1, -1)) # +1 as MOT benchmark requires positive""" i為trackers跟蹤器鏈(列表)長度,從列表尾向列表頭的方向 每遍歷trackers跟蹤器鏈(列表)一次 即進行 i-=1 """i -= 1"""trk.time_since_update > max_age1.time_since_update:記錄了該目標對應的卡爾曼濾波器中的預測框(跟蹤框)進行連續預測的次數,每執行predict一次即進行time_since_update+=1。在連續預測(連續執行predict)的過程中,一旦執行update的話,time_since_update就會被重置為0。2.max_age:最大跟丟幀數。如果當前連續N幀大于最大跟丟幀數的話,則從跟蹤器鏈中刪除該卡爾曼濾波對象的預測框(跟蹤框)。3.time_since_update > max_age:每預測一幀time_since_update就會+=1,只有預測的跟蹤框跟蹤到目標(即預測的跟蹤框和檢測框相似度匹配)才會執行update更新,那么time_since_update才會被重置為0。那么當連續time_since_update幀都沒有跟蹤到目標的話,即當連續time_since_update幀大于最大跟丟幀數時,那么就需要根據該跟蹤失敗的跟蹤器框的索引把該跟蹤器框從跟蹤器鏈(列表)trackers中進行移除出去。"""# 跟蹤失敗或離開畫面的目標從卡爾曼跟蹤器中刪除if trk.time_since_update > self.max_age:"""trackers:上一幀中的跟蹤器鏈(列表),保存的是上一幀中成功跟蹤目標的跟蹤框,也即成功跟蹤目標的KalmanBoxTracker卡爾曼濾波對象。trackers.pop(要移除的某個跟蹤框的索引值):即能根據該索引值從跟蹤器鏈(列表)中把該跟蹤框移除出去"""# pop(要移除的列表中元素的索引值):根據列表中元素的索引值自動從列表中移除self.trackers.pop(i)# 返回當前畫面中所有目標的box與id,以二維矩陣形式返回if len(ret) > 0:""" ret:當前幀中跟蹤目標成功的跟蹤框/預測框的集合,包含目標的跟蹤的id(也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個)第一種返回值方案:[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度, trk.id] ...]第二種返回值方案(當前使用的為該種):[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, trk.id] ...]d:[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標]trk.id:卡爾曼濾波器的個數/目標框的個數,也即該跟蹤框(卡爾曼濾波實例對象)是創建出來的第幾個。[[左上角的x坐標, 左上角的x坐標y坐標, 右下角的x坐標, 右下角的y坐標, yolo識別目標是某種物體的可信度, 該跟蹤框是創建出來的第幾個] [...][...]]"""return np.concatenate(ret)return np.empty((0, 5))總結
以上是生活随笔為你收集整理的智慧交通day02-车流量检测实现10:多目标追踪实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [已解决]fdfs-client-py=
- 下一篇: mysql 数据库引擎介绍_MYSQL