主頁 >  其他 > 【AI全堆疊二】視頻流多目標多類別無延遲高精度高召回目標追蹤 YOLO+Deepsort 全解

【AI全堆疊二】視頻流多目標多類別無延遲高精度高召回目標追蹤 YOLO+Deepsort 全解

2021-04-20 10:29:28 其他

文章目錄

  • 首先介紹跟蹤:
  • 淺析 SORT
    • SORT 部分原理:
    • SORT 代碼實作+解讀:
  • DeepSORT 深入解讀
    • IOU 匹配流程圖
    • yolo 的總體思想歸納:
    • YOLO V5 使用 GIOU Loss 作為 bounding box 的損失,
    • 2:資料標注與預處理
    • 為什么要使用預訓練模型?
    • 檢測:
  • 全部代碼介紹
  • 總結

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

首先介紹跟蹤:

目標跟蹤又分為單目標跟蹤和多目標跟蹤

單目標跟蹤在視頻的初始幀畫面上框出單個目標,預測后續幀中該目標的大小與位置,典型演算法有 Mean shift(用卡爾曼濾波、粒子濾波進行狀態預測)、TLD(基于在線學習的跟蹤)、KCF(基于相關性濾波)等,

像 Opencv 等庫內置了許多跟蹤演算法,KCF 是一種很經典的單目標跟蹤演算法,速度不是很快,但是精度不錯,但是他也

多目標追蹤不像單目標追蹤一樣先在初始幀上框出單個目標,而是追蹤多個目標的大小和位置,且每一幀中目標的數量和位置都可能變化,此外,多目標的追蹤中還存在下列問題:

處理新目標的出現和老目標的消失;

跟蹤目標的運動預測和相似度判別,即上一幀與下一幀目標的匹配;

跟蹤目標之間的重疊和遮擋處理;

跟蹤目標丟失一段時間后再重新出現的再識別,

針對這樣的情況,有哪些解決方法呢?下面來綜合敘述,

淺析 SORT

在這里插入圖片描述

在這里插入圖片描述

SORT 部分原理:

在跟蹤之前,對所有目標已經完成檢測,實作了特征建模程序,1 第一幀進來時,以檢測到的目標初始化并創建新的跟蹤器,標注 id,2 后面幀進來時,先到卡爾曼濾波器中得到由前面幀 box 產生的狀態預測和協方差預測,求跟蹤器所有目標狀態預測與本幀檢測的 box 的 IOU,通過匈牙利指派演算法得到 IOU 最大的唯一匹配(資料關聯部分),再去掉匹配值小于 iou_threshold 的匹配對,3 用本幀中匹配到的目標檢測 box 去更新卡爾曼跟蹤器,計算卡爾曼增益、狀態更新和協方差更新,并將狀態更新值輸出,作為本幀的跟蹤 box,對于本幀中沒有匹配到的目標重新初始化跟蹤器,

其中,卡爾曼跟蹤器聯合了歷史跟蹤記錄,調節歷史 box 與本幀 box 的殘差,更好的匹配跟蹤 id,

SORT 的貢獻主要有三:
利用強大的 CNN 檢測器的檢測結果來進行多目標跟蹤

使用基于卡爾曼濾波(Kalman filter)與匈牙利演算法(Hungarian algorithm)的方法來進行跟蹤

開源了代碼,為 MOT 領域提供一個新的 baseline

SORT 將這種二階段匹配演算法改進為了一階段方法,并且可以在線跟蹤,

具體而言,SORT 引入了線性速度模型與卡爾曼濾波來進行位置預測,在無合適匹配檢測框的情況下,使用運動模型來預測物體的位置,

SORT 代碼實作+解讀:

from __future__ import print_function
 
from numba import jit      #是python的一個JIT庫,通過裝飾器來實作運行時的加速
import os.path
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches  #用于繪制常見影像(如矩形,橢圓,圓形,多邊形)
from skimage import io
from sklearn.utils.linear_assignment_ import linear_assignment
import glob
import time
import argparse
from filterpy.kalman import KalmanFilter  #filterpy包含了一些常用濾波器的庫
 
@jit  #用了jit裝飾器,可加速for回圈的計算
def iou(bb_test,bb_gt):
  """
  Computes IOU between two bboxes in the form [x1,y1,x2,y2]
  """
  xx1 = np.maximum(bb_test[0], bb_gt[0])
  yy1 = np.maximum(bb_test[1], bb_gt[1])
  xx2 = np.minimum(bb_test[2], bb_gt[2])
  yy2 = np.minimum(bb_test[3], bb_gt[3])
  w = np.maximum(0., xx2 - xx1)
  h = np.maximum(0., yy2 - yy1)
  wh = w * h
  o = wh / ((bb_test[2]-bb_test[0])*(bb_test[3]-bb_test[1])   #IOU=(bb_test和bb_gt框相交部分面積)/(bb_test框面積+bb_gt框面積 - 兩者相交面積)
    + (bb_gt[2]-bb_gt[0])*(bb_gt[3]-bb_gt[1]) - wh)
  return(o)
 
def convert_bbox_to_z(bbox): #將bbox由[x1,y1,x2,y2]形式轉為 [框中心點x,框中心點y,框面積s,寬高比例r]^T
  """
  Takes a bounding box in the form [x1,y1,x2,y2] and returns z in the form
    [x,y,s,r] where x,y is the centre of the box and s is the scale/area and r is
    the aspect ratio
  """
  w = bbox[2]-bbox[0]
  h = bbox[3]-bbox[1]
  x = bbox[0]+w/2.
  y = bbox[1]+h/2.
  s = w*h    #scale is just area
  r = w/float(h)
  return np.array([x,y,s,r]).reshape((4,1))  #將陣列轉為4行一列形式,即[x,y,s,r]^T
 
def convert_x_to_bbox(x,score=None): #將[x,y,s,r]形式的bbox,轉為[x1,y1,x2,y2]形式
  """
  Takes a bounding box in the centre form [x,y,s,r] and returns it in the form
    [x1,y1,x2,y2] where x1,y1 is the top left and x2,y2 is the bottom right
  """
  w = np.sqrt(x[2]*x[3])  #w=sqrt(w*h * w/h)
  h = x[2]/w              #h=w*h/w
  if(score==None): #如果檢測框不帶置信度
    return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.]).reshape((1,4))  #回傳[x1,y1,x2,y2]
  else:            #如果加測框帶置信度
    return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.,score]).reshape((1,5)) #回傳[x1,y1,x2,y2,score]
 
 
class KalmanBoxTracker(object):
  """
  This class represents the internel state of individual tracked objects observed as bbox.
  """
  count = 0
  def __init__(self,bbox):
    """
    Initialises a tracker using initial bounding box.  使用初始邊界框初始化跟蹤器
    """
    #define constant velocity model                #定義勻速模型
    self.kf = KalmanFilter(dim_x=7, dim_z=4)       #狀態變數是7維, 觀測值是4維的,按照需要的維度構建目標
    self.kf.F = np.array([[1,0,0,0,1,0,0],[0,1,0,0,0,1,0],[0,0,1,0,0,0,1],[0,0,0,1,0,0,0],[0,0,0,0,1,0,0],[0,0,0,0,0,1,0],[0,0,0,0,0,0,1]])
    self.kf.H = np.array([[1,0,0,0,0,0,0],[0,1,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,0,1,0,0,0]])
 
    self.kf.R[2:,2:] *= 10.
    self.kf.P[4:,4:] *= 1000. #give high uncertainty to the unobservable initial velocities 對未觀測到的初始速度給出高的不確定性
    self.kf.P *= 10.          # 默認定義的協方差矩陣是np.eye(dim_x),將P中的數值與10, 1000相乘,賦值不確定性
    self.kf.Q[-1,-1] *= 0.01
    self.kf.Q[4:,4:] *= 0.01
 
    self.kf.x[:4] = convert_bbox_to_z(bbox)  #將bbox轉為 [x,y,s,r]^T形式,賦給狀態變數X的前4位
    self.time_since_update = 0
    self.id = KalmanBoxTracker.count
    KalmanBoxTracker.count += 1
    self.history = []
    self.hits = 0
    self.hit_streak = 0
    self.age = 0
 
  def update(self,bbox):
    """
    Updates the state vector with observed bbox.
    """
    self.time_since_update = 0
    self.history = []
    self.hits += 1
    self.hit_streak += 1
    self.kf.update(convert_bbox_to_z(bbox))
 
  def predict(self):
    """
    Advances the state vector and returns the predicted bounding box estimate.
    """
    if((self.kf.x[6]+self.kf.x[2])<=0):
      self.kf.x[6] *= 0.0
    self.kf.predict()
    self.age += 1
    if(self.time_since_update>0):
      self.hit_streak = 0
    self.time_since_update += 1
    self.history.append(convert_x_to_bbox(self.kf.x))
    return self.history[-1]
 
  def get_state(self):
    """
    Returns the current bounding box estimate.
    """
    return convert_x_to_bbox(self.kf.x)
 
def associate_detections_to_trackers(detections,trackers,iou_threshold = 0.3):  #用于將檢測與跟蹤進行關聯
  """
  Assigns detections to tracked object (both represented as bounding boxes)
  Returns 3 lists of matches, unmatched_detections and unmatched_trackers
  """
  if(len(trackers)==0):  #如果跟蹤器為空
    return np.empty((0,2),dtype=int), np.arange(len(detections)), np.empty((0,5),dtype=int)
  iou_matrix = np.zeros((len(detections),len(trackers)),dtype=np.float32) # 檢測器與跟蹤器IOU矩陣
 
  for d,det in enumerate(detections):
    for t,trk in enumerate(trackers):
      iou_matrix[d,t] = iou(det,trk)   #計算檢測器與跟蹤器的IOU并賦值給IOU矩陣對應位置
  matched_indices = linear_assignment(-iou_matrix)    # 參考:https://blog.csdn.net/herr_kun/article/details/86509591    加上負號是因為linear_assignment求的是最小代價組合,而我們需要的是IOU最大的組合方式,所以取負號
 
  unmatched_detections = []    #未匹配上的檢測器
  for d,det in enumerate(detections):
    if(d not in matched_indices[:,0]):  #如果檢測器中第d個檢測結果不在匹配結果索引中,則d未匹配上
      unmatched_detections.append(d)
  unmatched_trackers = []      #未匹配上的跟蹤器
  for t,trk in enumerate(trackers):
    if(t not in matched_indices[:,1]):  #如果跟蹤器中第t個跟蹤結果不在匹配結果索引中,則t未匹配上
      unmatched_trackers.append(t)
 
  #filter out matched with low IOU   過濾掉那些IOU較小的匹配對
  matches = []  #存放過濾后的匹配結果
  for m in matched_indices:   #遍歷粗匹配結果
    if(iou_matrix[m[0],m[1]]<iou_threshold):   #m[0]是檢測器ID, m[1]是跟蹤器ID,如它們的IOU小于閾值則將它們視為未匹配成功
      unmatched_detections.append(m[0])
      unmatched_trackers.append(m[1])
    else:
      matches.append(m.reshape(1,2))          #將過濾后的匹配對維度變形成1x2形式
  if(len(matches)==0):           #如果過濾后匹配結果為空,那么回傳空的匹配結果
    matches = np.empty((0,2),dtype=int)  
  else:                          #如果過濾后匹配結果非空,則按0軸方向繼續添加匹配對
    matches = np.concatenate(matches,axis=0)
 
  return matches, np.array(unmatched_detections), np.array(unmatched_trackers)  #其中跟蹤器陣列是5列的(最后一列是ID)
 
 
 
class Sort(object):
  def __init__(self,max_age=1,min_hits=3):
    """
    Sets key parameters for SORT
    """
    self.max_age = max_age
    self.min_hits = min_hits
    self.trackers = []
    self.frame_count = 0
 
  def update(self,dets):  #輸入的是檢測結果[x1,y1,x2,y2,score]形式
    """
    Params:
      dets - a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]
    Requires: this method must be called once for each frame even with empty detections.   #每一幀都得呼叫一次,即便檢測結果為空
    Returns the a similar array, where the last column is the object ID.                    #回傳相似的陣列,最后一列是目標ID
    NOTE: The number of objects returned may differ from the number of detections provided.  #回傳的目標數量可能與提供的檢測數量不同
    """
    self.frame_count += 1   #幀計數
    #get predicted locations from existing trackers.
    trks = np.zeros((len(self.trackers),5)) # 根據當前所有卡爾曼跟蹤器的個數創建二維零矩陣,維度為:卡爾曼跟蹤器ID個數x 5 (這5列內容為bbox與ID)
    to_del = []                             #存放待洗掉
    ret = []                                #存放最后回傳的結果
    for t,trk in enumerate(trks):      #回圈遍歷卡爾曼跟蹤器串列
      pos = self.trackers[t].predict()[0]           #用卡爾曼跟蹤器t 預測 對應物體在當前幀中的bbox
      trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
      if(np.any(np.isnan(pos))):                     #如果預測的bbox為空,那么將第t個卡爾曼跟蹤器洗掉
        to_del.append(t)
    trks = np.ma.compress_rows(np.ma.masked_invalid(trks))  #將預測為空的卡爾曼跟蹤器所在行洗掉,最后trks中存放的是上一幀中被跟蹤的所有物體在當前幀中預測的非空bbox
    for t in reversed(to_del): #對to_del陣列進行倒序遍歷
      self.trackers.pop(t)   #從跟蹤器中洗掉 to_del中的上一幀跟蹤器ID
    matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets,trks)  #對傳入的檢測結果 與 上一幀跟蹤物體在當前幀中預測的結果做關聯,回傳匹配的目標矩陣matched, 新增目標的矩陣unmatched_dets, 離開畫面的目標矩陣unmatched_trks
 
    #update matched trackers with assigned detections
    for t,trk in enumerate(self.trackers):    # 對卡爾曼跟蹤器做遍歷
      if(t not in unmatched_trks):                   #如果上一幀中的t還在當前幀畫面中(即不在當前預測的離開畫面的矩陣unmatched_trks中)
        d = matched[np.where(matched[:,1]==t)[0],0]  #說明卡爾曼跟蹤器t是關聯成功的,在matched矩陣中找到與其關聯的檢測器d
        trk.update(dets[d,:][0])                     #用關聯的檢測結果d來更新卡爾曼跟蹤器(即用后驗來更新先驗)
 
    #create and initialise new trackers for unmatched detections  #對于新增的未匹配的檢測結果,創建并初始化跟蹤器
    for i in unmatched_dets:                  #新增目標
        trk = KalmanBoxTracker(dets[i,:])     #將新增的未匹配的檢測結果dets[i,:]傳入KalmanBoxTracker
        self.trackers.append(trk)             #將新創建和初始化的跟蹤器trk 傳入trackers
    i = len(self.trackers)
    for trk in reversed(self.trackers):       #對新的卡爾曼跟蹤器集進行倒序遍歷
        d = trk.get_state()[0]                #獲取trk跟蹤器的狀態 [x1,y1,x2,y2]       
        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 positive
        i -= 1
        #remove dead tracklet
        if(trk.time_since_update > self.max_age):
          self.trackers.pop(i)
    if(len(ret)>0):
      return np.concatenate(ret)
    return np.empty((0,5))
    
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='SORT demo')
    parser.add_argument('--display', dest='display', help='Display online tracker output (slow) [False]',action='store_true')
    args = parser.parse_args()
    return args
 
if __name__ == '__main__':
  # all train
  sequences = ['PETS09-S2L1','TUD-Campus','TUD-Stadtmitte','ETH-Bahnhof','ETH-Sunnyday','ETH-Pedcross2','KITTI-13','KITTI-17','ADL-Rundle-6','ADL-Rundle-8','Venice-2']
  args = parse_args()
  display = args.display
  phase = 'train'
  total_time = 0.0
  total_frames = 0
  colours = np.random.rand(32,3) #used only for display
  if(display):
    if not os.path.exists('mot_benchmark'):
      print('\n\tERROR: mot_benchmark link not found!\n\n    Create a symbolic link to the MOT benchmark\n    (https://motchallenge.net/data/2D_MOT_2015/#download). E.g.:\n\n    $ ln -s /path/to/MOT2015_challenge/2DMOT2015 mot_benchmark\n\n')
      exit()
    plt.ion()  #用于動態繪制顯示影像
    fig = plt.figure() 
  
  if not os.path.exists('output'):
    os.makedirs('output')
  
  for seq in sequences:
    mot_tracker = Sort() #create instance of the SORT tracker    創建Sort 跟蹤實體
    seq_dets = np.loadtxt('data/%s/det.txt'%(seq),delimiter=',') #load detections    #加載檢測結果
    with open('output/%s.txt'%(seq),'w') as out_file:
      print("Processing %s."%(seq))
      for frame in range(int(seq_dets[:,0].max())):   #確定視頻序列總幀數,并進行for回圈
        frame += 1 #detection and frame numbers begin at 1  #由于視頻序列幀數是從1開始的,因此加1
        dets = seq_dets[seq_dets[:,0]==frame,2:7]     #提取檢測結果中的[x1,y1,w,h,score]到dets
        dets[:,2:4] += dets[:,0:2] #convert to [x1,y1,w,h] to [x1,y1,x2,y2]   將dets中的第2,3列的數加上第0,1列的數后賦值給2,3列;
        total_frames += 1          #總幀數累計
 
        if(display):        #如果要求顯示結果
          ax1 = fig.add_subplot(111, aspect='equal')
          fn = 'mot_benchmark/%s/%s/img1/%06d.jpg'%(phase,seq,frame)   #原影像路徑名
          im =io.imread(fn)      #加載影像
          ax1.imshow(im)         #顯示影像
          plt.title(seq+' Tracked Targets')
 
        start_time = time.time()
        trackers = mot_tracker.update(dets)  #sort跟蹤器更新
        cycle_time = time.time() - start_time  #sort跟蹤器耗時
        total_time += cycle_time               #sort跟蹤器總共耗費時間
 
        for d in trackers:
          print('%d,%d,%.2f,%.2f,%.2f,%.2f,1,-1,-1,-1'%(frame,d[4],d[0],d[1],d[2]-d[0],d[3]-d[1]),file=out_file) #列印: frame,ID,x1,y1,x2,y2,1,-1,-1,-1
          if(display):             #如果顯示,將目標檢測框畫上
            d = d.astype(np.int32)
            ax1.add_patch(patches.Rectangle((d[0],d[1]),d[2]-d[0],d[3]-d[1],fill=False,lw=3,ec=colours[d[4]%32,:]))
            ax1.set_adjustable('box-forced')
 
        if(display):
          fig.canvas.flush_events()
          plt.draw()
          ax1.cla()
 
  print("Total Tracking took: %.3f for %d frames or %.1f FPS"%(total_time,total_frames,total_frames/total_time))
  if(display):
    print("Note: to get real runtime results run without the option: --display")

DeepSORT 深入解讀

在這里插入圖片描述

在這里插入圖片描述

一年之后,原團隊發布了 SORT 的續作 DeepSORT,到現在都有很多人在用這個跟蹤器,

整體框架沒有大改,還是延續了卡爾曼濾波加匈牙利演算法的思路,在這個基礎上增加了 Deep Association Metric,Deep Association Metric 其實就是在大型行人重識別網路上學習的一個行人鑒別網路,目的是區分出不同的行人,個人感覺很類似于典型的行人重識別網路,輸出行人圖片,輸出一組向量,通過比對兩個向量之間的距離,來判斷兩副輸入圖片是否是同一個行人,

此外還加入了外觀資訊(Appearance Information)以實作較長時間遮擋的目標跟蹤,

跟蹤流程延續上作,在卡爾曼濾波的預測結果的基礎上,繼續使用了匈牙利演算法進行目標分配,但在這個程序中加入了運動資訊和外觀資訊,這個說起來簡單,實作起來比較復雜,感興趣的讀者可以細看論文和代碼,在這里就不贅述了,

其他方面沒有太多變化,還是使用了標準的卡爾曼濾波和固定速度模型等來進行預測,

最終實作了較好的跟蹤效果,并且能夠實時運行(40FPS),

IOU 匹配流程圖

在這里插入圖片描述
在這里插入圖片描述

作者代碼也以檢測結果為輸入:bounding box、confidence、feature ,conf 主要用于進行一部分的檢測框的篩選;bounding box 與 feature(ReID)用于后面與跟蹤器的 match 計算;首先是預測模塊,會對跟蹤器使用卡爾曼濾波器進行預測,作者在這里使用的是卡爾曼濾波器的勻速運動和線性觀測模型(意味著只有四個量且在初始化時會使用檢測器進行恒值初始化),其次是更新模塊,其中包括匹配,追蹤器更新與特征集更新,在更新模塊的部分,根本的方法還是使用 IOU 來進行匈牙利演算法的匹配

1.使用級聯匹配演算法:
針對每一個檢測器都會分配一個跟蹤器,每個跟蹤器會設定一個 time_since_update 引數,如果跟蹤器完成匹配并進行更新,那么引數會重置為 0,否則就會+1,實際上,級聯匹配換句話說就是不同優先級的匹配,在級聯匹配中,會根據這個引數來對跟蹤器分先后順序,引數小的先來匹配,引數大的后匹配,也就是給上一幀最先匹配的跟蹤器高的優先權,給好幾幀都沒匹配上的跟蹤器降低優先權(慢慢放棄),至于使用級聯匹配的目的,我參考一下博客②里的解釋:

當一個目標長時間被遮擋之后,kalman 濾波預測的不確定性就會大大增加,狀態空間內的可觀察性就會大大降低,假如此時兩個追蹤器競爭同一個檢測結果的匹配權,往往遮擋時間較長的那條軌跡的馬氏距離更小,使得檢測結果更可能和遮擋時間較長的那條軌跡相關聯,這種不理想的效果往往會破壞追蹤的持續性,這么理解吧,假設本來協方差矩陣是一個正態分布,那么連續的預測不更新就會導致這個正態分布的方差越來越大,那么離均值歐氏距離遠的點可能和之前分布中離得較近的點獲得同樣的馬氏距離值,所以,作者使用了級聯匹配來對更加頻繁出現的目標賦予優先權,當然同樣也有弊端:可能導致一些新產生的軌跡被連接到了一些舊的軌跡上,但這種情況較少,

2.添加馬氏距離與余弦距離:
實際上是針對運動資訊與外觀資訊的計算,兩個名詞聽著較為陌生,而實際上換句話解釋,馬氏距離就是“加強版的歐氏距離”,它實際上是規避了歐氏距離中對于資料特征方差不同的風險,在計算中添加了協方差矩陣,其目的就是進行方差歸一化,從而使所謂的“距離”更加符合資料特征以及實際意義,馬氏距離是對于差異度的衡量中,的一種距離度量方式,而不同于馬氏距離,余弦距離則是一種相似度度量方式,前者是針對于位置進行區分,而后者則是針對于方向,換句話說,我們使用余弦距離的時候,可以用來衡量不同個體在維度之間的差異,而一個個體中,維度與維度的差異我們卻不好判斷,此時我們可以使用馬氏距離進行彌補,從而在整體上可以達到一個相對于全面的差異性衡量,而我們之所以要進行差異性衡量,根本目的也是想比較檢測器與跟蹤器的相似程度,優化度量方式,也可以更好地完成匹配,

3.添加深度學習特征:
這一部分也就是 ReID 的模塊,也是 deepsort 的亮點之一,deepsort 在對于 sort 的改進中加入了一個深度學習的特征提取網路,網路結構部分各位看官可以移步論文,作者將所有 confirmed 的追蹤器(其中一個狀態)每次完成匹配對應的 detection 的 feature map 存盤進一個 list,存盤的數量作者使用 budget 超引數(100 幀)進行限制(我認為如果實時性效果不好的話,可以調低這個引數加快速度),從而我們在每次匹配之后都會更新這個 feature map 的 list,比如去除掉一些已經出鏡頭的目標的特征集,保留最新的特征將老的特征 pop 掉等等,這個特征集在進行余弦距離計算的時候將會發揮作用,實際上,在當前幀,會計算第 i 個物體跟蹤的所有 Feature 向量和第 j 個物體檢測之間的最小余弦距離,

4.IOU 與匈牙利演算法匹配:
這個方法是在 sort 中被提出的,又是比較陌生的名詞,我接著“換句話說”,實際上匈牙利演算法可以理解成“盡量多”的一種思路,比如說 A 檢測器可以和 a,c 跟蹤器完成匹配(與 a 匹配置信度更高),但是 B 檢測器只能和 a 跟蹤器完成匹配,那在演算法中,就會讓 A 與 c 完成匹配,B 與 a 完成匹配,而降低對于置信度的考慮,所以演算法的根本目的并不是在于匹配的準不準,而是在于盡量多的匹配上,這也就是在 deepsort 中作者添加級聯匹配與馬氏距離與余弦距離的根本目的,因為僅僅使用匈牙利演算法進行匹配特別容易造成 ID switch,就是一個檢測框 id 不停地進行更換,缺乏準確性與魯棒性,那什么是匹配的置信度高呢,其實在這里,作者使用的是 IOU 進行衡量,計算檢測器與跟蹤器的 IOU,將這個作為置信度的高低(比較粗糙),

還有一些超引數,我也已經在代碼流程的部分明確講解了,這些超引數或進行閾值的作用,或進行回圈次數的限定,不斷幫助演算法完成最優匹配與追蹤器更新,之后,將未匹配的追蹤器 delete,將未匹配的檢測器初始化,將匹配的追蹤器使用對應的檢測器進行賦值,作為輸出,進入下次回圈,

Deepsort 代碼解讀
另一篇寫的很棒的 Deepsort 原理解讀:https://blog.csdn.net/sgfmby1994/article/details/98517210

yolo 的總體思想歸納:

首先,將輸入圖片壓縮到 416×416,通過特征提取網路(Darknet53 without FC layer)對輸入影像提取特征得到大小一定的特征圖,比如 13×13,然后將輸入影像分成 13×13 個網格(grid cells),接著如果 GT 中某個目標的中心坐標落在哪個 grid cell 中,那么就由該 grid cell 來預測該目標,每個 grid cell 都會預測 3 個邊界框,預測得到的輸出特征圖共有三個維度,第三個維度是深度,

Yolov3 輸出了三個不同尺度的特征圖,采用多尺度對不同大小的目標進行檢測,越精細的 grid cell 就可以檢測出越精細的物體,三個尺度的深度都是 255(3×(5+80)),

根據圖來說明程序:

在這里插入圖片描述

流程:首先輸入尺寸 416×416,然后進入 darknet 特征提取網路,右邊圖(不算分支,也是左邊圖虛線中內容),其中經過五次下采樣,還是用了殘差結構,目的是使網路結構在很深的情況下,仍能收斂,繼續訓練下去,然后到左邊圖,虛線中輸出的尺寸是 13×13,然后在經過 DBL 特征提取,以及最后藍色的卷積(我猜是用來代替全連接分類的),輸出第一個尺度 13×13,

接著用虛線輸出的特征圖經過 DBL 后的 13×13 的特征圖上采樣后與倒數第二次下采樣的結果相加,二者都為 26×26,然后在進行和尺度一同樣的后續操作,

最后是 26×26 的特征圖上采樣后與倒數第三次下采樣的特征圖相加,即還為 26×26,在進行后續操作,

總的來說會輸出 3 個不同尺度的特征圖,每個尺度的特征圖負責預測不同大小的目標,每個特征圖對應 3 種 anchor 大小不同的負責預測目標,最初影像還被分成 13×13 個網格,目標落在哪個網格中,哪個網格就負責預測目標,一個網格對應 3 個 anchor(anchor 的尺寸根據特征圖相對于原圖的比例等比縮小),

預測時,yolov3 采用多個獨立的邏輯分類器來計算屬于特定標簽的可能性,在計算分類損失時,它對每個標簽使用二元交叉熵損失,降低了計算的復雜度,

YOLOv3 和 YOLOv5 整體思想類似,不過 YOLOV5 增加了 PaNet 等 tricks,以及 Mossiac 等大量資料增強,

在這里插入圖片描述

YOLO V5 使用 GIOU Loss 作為 bounding box 的損失,

YOLO V5 使用二進制交叉熵和 Logits 損失函式計算類概率和目標得分的損失,同時我們也可以使用 fl _ gamma 引數來激活 Focal loss 計算損失函式,

在 YOLO V5 中,中間/隱藏層使用了 Leaky ReLU 激活函式,最后的檢測層使用了 Sigmoid 形激活函式,

YOLOv3/YOLOv5 訓練教程:
以 github Ultralytics 作者的 Yolov3 和 Yolo v5 為例子,可以很迅速的訓練資料:1:配置環境 pip install -U -r requirements.txt

2:資料標注與預處理

將我們的 XML 檔案放至 Annotations

將我們的圖片放到 images

在這里插入圖片描述

然后就可以轉換 Annotations 為 Yolo 的訓練格式,百度有很多,這里要注意看一下代碼,有的地方需要修改,

并切分訓練驗證集,代碼也給了,Voc_Labels.py 請看看代碼,沒怎么寫注釋,

接下來撰寫 data/檔案夾的 yaml 檔案(可以新建):

在上面我們做好資料預處理后,就可以開始訓練了,上面的一些處理步驟,每個人都可能不同,不過大體上思路是一致的,

在這里插入圖片描述

接下來我們可以進行預訓練,下載官方的預訓練模型:yolov5s yolov5m yolov5l yolov5x 來 我在我的 github 中方了 yolov5s,比較小,只有 25mb,專門為移動端考慮了,真好,
在這里插入圖片描述

當然也可以不使用預訓練模型,使用與否,在總時間上是差不多的,不過

為什么要使用預訓練模型?

作者已盡其所能設計了基準模型,我們可以在自己的資料集上使用預訓練模型,而不是從頭構建模型來解決類似的自然語言處理問題,

盡管仍然需要進行一些微調,但它已經為我們節省了大量的時間:通常是每個損失下降更快和計算資源節省,

加快梯度下降的收斂速度

更有可能獲得一個低模型誤差,或者低泛化誤差的模型

降低因未初始化或初始化不當導致的梯度消失或者梯度爆炸問題,此情況會導致模型訓練速度變慢,崩潰,直至失敗,

其中隨機初始化,可以打破對稱性,從而保證不同的隱藏單元可以學習到不同的東西

接下來開始訓練:

python train.py --data data/smoke.yaml --cfg models/yolov5s.yaml --weights weights/yolov5s.pt --batch-size 10 --epochs 100

我們–data data/smoke.yaml 中就是在 smoke.yaml 中撰寫的訓練代碼路徑和類別等 data,通過這個獲取訓練的圖片和 label 標簽等,

然后-cfg models/yolov5s.yaml 和 --weights weights/yolov5s.pt 是獲取配置和預訓練模型權重

batch-size 10 大家都懂,default 是 16,大家可以改成 16,在 yolov5s 中模型參施不多,百萬左右,所以顯存消耗不多,我的配置很差,顯存 4g,在使用 yolov5m 中以及不能調到 16,

會報 cuda out of memory 報錯,就把 batch size 降低就行,

然后最后是 epoch,這個也不用解釋,我在使用 yolov5m 訓練 5k 張圖片在 100epoch 中花費 了 24 小時,一個 epoch13 分鐘,

訓練程序中,會慢慢在 runs 中生成 tensorboard,可視化損失下降

當然也可以在本地稍微看看

這幅圖中,我們的類別只有 1 個,第三幅圖顯示了我們資料中的寬高比,歸一化后,普遍在 0.1 左右,說明資料確實很小,也會面臨模糊問題,導致資料質量降低,

檢測:

運行 detect.py 即可
在這里插入圖片描述

在這里插入圖片描述

全部代碼介紹

來回顧一下代碼:

在這里插入圖片描述
在這里插入圖片描述在這里插入圖片描述

在這里插入圖片描述

驗證腳本
在這里插入圖片描述

特征提取類

在這里插入圖片描述

以【batch size,3,128,64】輸入,回傳類別結果的網路處理層,被上圖特征提取類使用,

在這里插入圖片描述

上圖目前網路較原網路稍稍優化了,下圖為原始網路,
在這里插入圖片描述

網路性能驗證與測驗

在這里插入圖片描述

網路訓練,

在這里插入圖片描述

追蹤部分:
追蹤部分的資料轉換

在這里插入圖片描述

匹配 Iou,用來區別框是否為待追蹤目標的途徑之一,

在這里插入圖片描述

卡爾曼濾波

在這里插入圖片描述

追蹤類,回傳識別結果
在這里插入圖片描述

同追蹤類,回傳匹配追蹤目標,和不匹配目標

YOLOv3 部分,darknet 版本,的 pytorch 版,

原始類別的檢測腳本

在這里插入圖片描述

更改后用于追蹤的檢測腳本

在這里插入圖片描述

YOLOv3 網路層

在這里插入圖片描述

處理資料,bdbox,nms 等操作的配置類 utils

在這里插入圖片描述

決議 config 類

在這里插入圖片描述

Rtsp 推流等方式 web 端部署,

V5 區域

在這里插入圖片描述

在這里插入圖片描述

訓練部分代碼和引數

在這里插入圖片描述

圖片,視頻,web,相機等多方式檢測

在這里插入圖片描述

配置多比例模型和配置 utils,

在這里插入圖片描述

V5 匯出模型 onnx

在這里插入圖片描述

演示

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

總結

原創好文,從原理到技術到原始碼到實際應用,工程,統統放送,在視頻流處理中,更是大放異彩~

在視頻流的實時追蹤中,我們雖然解決了一些問題,但是發現,還是有許多道阻且長的問題,

大家一起加油干吧~

歡迎關注 cv 君公眾號,原始碼歡迎公眾號回復 跟蹤 獲取哦
在這里插入圖片描述

致謝,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/278011.html

標籤:AI

上一篇:2021-04-19

下一篇:我眼中的線性代數

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more