天天看點

3D點雲目标跟蹤的評價名額及詳細代碼

作者:3D視覺工坊

作者:大森林 | 來源:計算機視覺工坊

添加微信:dddvisiona,備注:3D點雲,拉你入群。文末附行業細分群。

3D點雲目标跟蹤的評價名額,可以根據跟蹤的目标是單個還是多個,分為單目标跟蹤(SOT)和多目标跟蹤(MOT)兩種。一般來說,SOT的評價名額主要關注跟蹤的準确性和魯棒性,而MOT的評價名額則需要考慮跟蹤的完整性和一緻性。

SOT的常用評價名額有:

  • 平均重疊率(Average Overlap Rate, AOR):表示預測的3D邊界框與真實的3D邊界框之間的重疊比例的平均值。
  • 平均中心誤差(Average Center Error, ACE):表示預測的3D邊界框與真實的3D邊界框之間的中心點距離的平均值。
  • 成功率(Success Rate, SR):表示預測的3D邊界框與真實的3D邊界框之間的重疊比例超過某個門檻值(如0.5)的幀數占總幀數的比例。
  • 精确率(Precision Rate, PR):表示預測的3D邊界框與真實的3D邊界框之間的中心點距離小于某個門檻值(如1米)的幀數占總幀數的比例。

MOT的常用評價名額有:

  • 多目标跟蹤精度(Multiple Object Tracking Accuracy, MOTA):綜合考慮了漏檢率、誤檢率和ID切換率對跟蹤精度的影響。
  • 多目标跟蹤精确度(Multiple Object Tracking Precision, MOTP):表示預測的3D邊界框與真實的3D邊界框之間的重疊比例或中心點距離的平均值。
  • 跟蹤長度(Track Length, TL):表示每個目标被成功跟蹤的幀數。
  • 跟蹤片段(Track Fragment, TF):表示每個目标被中斷跟蹤的次數。
  • ID切換率(ID Switch Rate, ISR):表示每個目标被錯誤地配置設定給另一個ID或從另一個ID切換過來的次數。

評價名額詳細代碼:

首先,我們需要導入一些必要的庫,如numpy, scipy和sklearn。然後,我們需要定義一些輔助函數,如計算兩個3D邊界框之間的重疊比例(IoU),計算兩個3D點之間的歐氏距離,以及使用匈牙利算法進行資料關聯。

這裡也推薦「3D視覺工坊」新課程《面向自動駕駛領域的3D點雲目标檢測全棧學習路線!(單模态+多模态/資料+代碼)》

import numpy as npfrom scipy.spatial.transform import Rotation as Rfrom scipy.optimize import linear_sum_assignmentfrom sklearn.metrics import pairwise_distances# 計算兩個3D邊界框之間的重疊比例(IoU)def iou_3d(box1, box2):    # box1和box2都是7維向量,表示(x, y, z, w, l, h, yaw)    # 其中(x, y, z)是中心點坐标,(w, l, h)是寬度、長度和高度,yaw是偏航角    # 傳回兩個邊界框之間的IoU值,範圍在[0, 1]    # 将邊界框轉換為8個頂點的矩陣    box1_corners = box_to_corners(box1)    box2_corners = box_to_corners(box2)    # 計算兩個邊界框在每個軸上的投影區間    box1_xmin = np.min(box1_corners[:, 0])    box1_xmax = np.max(box1_corners[:, 0])    box1_ymin = np.min(box1_corners[:, 1])    box1_ymax = np.max(box1_corners[:, 1])    box1_zmin = np.min(box1_corners[:, 2])    box1_zmax = np.max(box1_corners[:, 2])    box2_xmin = np.min(box2_corners[:, 0])    box2_xmax = np.max(box2_corners[:, 0])    box2_ymin = np.min(box2_corners[:, 1])    box2_ymax = np.max(box2_corners[:, 1])    box2_zmin = np.min(box2_corners[:, 2])    box2_zmax = np.max(box2_corners[:, 2])    # 計算兩個邊界框在每個軸上的交集區間    inter_xmin = max(box1_xmin, box2_xmin)    inter_xmax = min(box1_xmax, box2_xmax)    inter_ymin = max(box1_ymin, box2_ymin)    inter_ymax = min(box1_ymax, box2_ymax)    inter_zmin = max(box1_zmin, box2_zmin)    inter_zmax = min(box1_zmax, box2_zmax)    # 如果沒有交集,傳回0    if inter_xmax < inter_xmin or inter_ymax < inter_ymin or inter_zmax < inter_zmin:        return 0.0    # 計算交集區域的體積    inter_vol = (inter_xmax - inter_xmin) * (inter_ymax - inter_ymin) * (inter_zmax - inter_zmin)    # 計算兩個邊界框的體積    box1_vol = (box1_xmax - box1_xmin) * (box1_ymax - box1_ymin) * (box1_zmax - box1_zmin)    box2_vol = (box2_xmax - box2_xmin) * (box2_ymax - box2_ymin) * (box2_zmax - box2_zmin)    # 計算并傳回IoU值    iou = inter_vol / (box1_vol + box2_vol - inter_vol)    return iou# 将7維向量表示的邊界框轉換為8個頂點的矩陣表示def box_to_corners(box):    # 輸入是一個7維向量,表示(x, y, z, w, l, h, yaw)    # 輸出是一個8x3的矩陣,表示8個頂點的坐标    # 提取邊界框的參數    x, y, z, w, l, h, yaw = box    # 計算旋轉矩陣    rot = R.from_euler('z', yaw).as_matrix()    # 計算邊界框的中心點    center = np.array([x, y, z])    # 計算邊界框的8個頂點的相對坐标    x_corners = np.array([w, w, -w, -w, w, w, -w, -w]) / 2    y_corners = np.array([l, -l, -l, l, l, -l, -l, l]) / 2    z_corners = np.array([h, h, h, h, -h, -h, -h, -h]) / 2    corners = np.vstack((x_corners, y_corners, z_corners))    # 通過旋轉和平移,将相對坐标轉換為絕對坐标    corners = np.dot(rot, corners).T + center    return corners# 計算兩個3D點之間的歐氏距離def euclidean_distance(point1, point2):    # point1和point2都是3維向量,表示(x, y, z)    # 傳回兩個點之間的歐氏距離    # 計算兩個點之間的差異向量    diff = point1 - point2    # 計算并傳回歐氏距離    dist = np.sqrt(np.sum(diff ** 2))    return dist# 使用匈牙利算法進行資料關聯def data_association(cost_matrix):    # cost_matrix是一個m x n的矩陣,表示m個預測和n個觀測之間的代價(如距離或者負IoU)    # 傳回一個長度為m的向量,表示每個預測比對的觀測的索引,如果沒有比對,則為-1    # 使用scipy庫中的linear_sum_assignment函數,求解最小化總代價的比對方案    row_ind, col_ind = linear_sum_assignment(cost_matrix)    # 初始化比對結果為-1    matches = np.full(cost_matrix.shape[0], -1)    # 将比對方案指派給比對結果    matches[row_ind] = col_ind    return matches      

接下來,我們需要定義一些評價名額的計算函數,如AOR,ACE,SR,PR,MOTA,MOTP,TL,TF,ISR等。我們已經有了預測的3D邊界框和真實的3D邊界框的清單,以及每個邊界框的置信度得分。我們還需要定義一些門檻值,如重疊比例門檻值(iou_threshold),中心點距離門檻值(dist_threshold),置信度得分門檻值(score_threshold)等,這些門檻值将在代碼中具體給出。

這裡再次推薦「3D視覺工坊」新課程《面向自動駕駛領域的3D點雲目标檢測全棧學習路線!(單模态+多模态/資料+代碼)》

# 計算平均重疊率(AOR)def average_overlap_rate(pred_boxes, gt_boxes):   
 # pred_boxes是一個p x 7的矩陣,表示p個預測的3D邊界框 
    # gt_boxes是一個g x 7的矩陣,表示g個真實的3D邊界框  
      # 傳回平均重疊率(AOR)值    
      # 如果沒有預測或真實邊界框,傳回0  
        if pred_boxes.shape[0] == 0 or gt_boxes.shape[0] == 0:      
          return 0.0    # 計算預測和真實邊界框之間的重疊比例矩陣,大小為p x g   
           iou_matrix = np.zeros((pred_boxes.shape[0], gt_boxes.shape[0]))   
            for i in range(pred_boxes.shape[0]):      
              for j in range(gt_boxes.shape[0]):          
                iou_matrix[i][j] = iou_3d(pred_boxes[i], gt_boxes[j]) 
                   # 使用匈牙利算法進行資料關聯,得到比對結果  
                     matches = data_association(-iou_matrix)  
                       # 計算并傳回平均重疊率(AOR)值 
                       ace = np.mean(dist_matrix[matches != -1]) 
                          return ace# 計算成功率(SR)
                          def success_rate(pred_boxes, gt_boxes, iou_threshold=0.5):   
                           # pred_boxes是一個p x 7的矩陣,表示p個預測的3D邊界框  
                             # gt_boxes是一個g x 7的矩陣,表示g個真實的3D邊界框 
                                # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5 
                                   # 傳回成功率(SR)值  
                                     # 如果沒有預測或真實邊界框,傳回0  
                                       if pred_boxes.shape[0] == 0 or gt_boxes.shape[0] == 0:
                                               return 0.0 
                                                  # 計算預測和真實邊界框之間的重疊比例矩陣,大小為p x g    iou_matrix = np.zeros((pred_boxes.shape[0], gt_boxes.shape[0]))    for i in range(pred_boxes.shape[0]):        for j in range(gt_boxes.shape[0]):            iou_matrix[i][j] = iou_3d(pred_boxes[i], gt_boxes[j])    # 使用匈牙利算法進行資料關聯,得到比對結果    matches = data_association(-iou_matrix)    # 計算并傳回成功率(SR)值    sr = np.sum(iou_matrix[matches != -1] >= iou_threshold) / pred_boxes.shape[0]    return sr# 計算精确率(PR)def precision_rate(pred_boxes, gt_boxes, dist_threshold=1.0):    # pred_boxes是一個p x 7的矩陣,表示p個預測的3D邊界框    # gt_boxes是一個g x 7的矩陣,表示g個真實的3D邊界框    # dist_threshold是一個浮點數,表示中心點距離的門檻值,預設為1.0    # 傳回精确率(PR)值    # 如果沒有預測或真實邊界框,傳回0    if pred_boxes.shape[0] == 0 or gt_boxes.shape[0] == 0:        return 0.0    # 提取預測和真實邊界框的中心點坐标    pred_centers = pred_boxes[:, :3]    gt_centers = gt_boxes[:, :3]    # 計算預測和真實邊界框之間的中心點距離矩陣,大小為p x g    dist_matrix = pairwise_distances(pred_centers, gt_centers)    # 使用匈牙利算法進行資料關聯,得到比對結果    matches = data_association(dist_matrix)    # 計算并傳回精确率(PR)值    pr = np.sum(dist_matrix[matches != -1] <= dist_threshold) / pred_boxes.shape[0]    return pr# 計算多目标跟蹤精度(MOTA)def multiple_object_tracking_accuracy(pred_boxes, gt_boxes, iou_threshold=0.5):    # pred_boxes是一個清單,長度為t,表示t個時間步的預測的3D邊界框    # gt_boxes是一個清單,長度為t,表示t個時間步的真實的3D邊界框    # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5    # 傳回多目标跟蹤精度(MOTA)值    # 如果沒有預測或真實邊界框,傳回0    if len(pred_boxes) == 0 or len(gt_boxes) == 0:        return 0.0    # 初始化漏檢數、誤檢數、ID切換數和總真實數為0    miss_count = 0    false_count = 0    switch_count = 0    total_count = 0    # 初始化上一時間步的比對結果為空字典    prev_matches = {}    # 周遊每個時間步    for t in range(len(pred_boxes)):        # 擷取目前時間步的預測和真實邊界框        pred_box = pred_boxes[t]        gt_box = gt_boxes[t]        # 計算目前時間步的真實邊界框的數量,并累加到總真實數中        total_count += gt_box.shape[0]        # 如果目前時間步沒有預測或真實邊界框,跳過該時間步        if pred_box.shape[0] == 0 or gt_box.shape[0] == 0:            continue        # 計算目前時間步的預測和真實邊界框之間的重疊比例矩陣,大小為p x g        iou_matrix = np.zeros((pred_box.shape[0], gt_box.shape[0]))        for i in range(pred_box.shape[0]):            for j in range(gt_box.shape[0]):                iou_matrix[i][j] = iou_3d(pred_box[i], gt_box[j])        # 使用匈牙利算法進行資料關聯,得到比對結果        matches = data_association(-iou_matrix)        # 初始化目前時間步的比對結果為空字典        curr_matches = {}        # 周遊每個預測邊界框        for i in range(pred_box.shape[0]):            # 如果沒有比對的真實邊界框,或者重疊比例低于門檻值,累加誤檢數,并跳過該預測邊界框            if matches[i] == -1 or iou_matrix[i][matches[i]] < iou_threshold:                false_count += 1                continue            # 擷取比對的真實邊界框的索引            j = matches[i]            # 如果上一時間步有比對的真實邊界框,并且ID不同,累加ID切換數            if j in prev_matches and prev_matches[j] != i:                switch_count += 1            # 将目前的比對結果儲存到字典中            curr_matches[j] = i        # 周遊每個真實邊界框        for j in range(gt_box.shape[0]):            # 如果沒有比對的預測邊界框,累加漏檢數            if j not in curr_matches:                miss_count += 1        # 更新上一時間步的比對結果為目前的比對結果        prev_matches = curr_matches    # 計算并傳回多目标跟蹤精度(MOTA)值    mota = 1 - (miss_count + false_count + switch_count) / total_count    return mota# 計算多目标跟蹤精确度(MOTP)def multiple_object_tracking_precision(pred_boxes, gt_boxes, iou_threshold=0.5):    # pred_boxes是一個清單,長度為t,表示t個時間步的預測的3D邊界框    # gt_boxes是一個清單,長度為t,表示t個時間步的真實的3D邊界框    # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5    # 傳回多目标跟蹤精确度(MOTP)值    # 如果沒有預測或真實邊界框,傳回0    if len(pred_boxes) == 0 or len(gt_boxes) == 0:        return 0.0    # 初始化總比對數和總重疊率為0    match_count = 0    sum_iou = 0.0    # 周遊每個時間步    for t in range(len(pred_boxes)):        # 擷取目前時間步的預測和真實邊界框        pred_box = pred_boxes[t]        gt_box = gt_boxes[t]        # 如果目前時間步沒有預測或真實邊界框,跳過該時間步        if pred_box.shape[0] == 0 or gt_box.shape[0] == 0:            continue        # 計算目前時間步的預測和真實邊界框之間的重疊比例矩陣,大小為p x g        iou_matrix = np.zeros((pred_box.shape[0], gt_box.shape[0]))        for i in range(pred_box.shape[0]):            for j in range(gt_box.shape[0]):                iou_matrix[i][j] = iou_3d(pred_box[i], gt_box[j])        # 使用匈牙利算法進行資料關聯,得到比對結果        matches = data_association(-iou_matrix)        # 周遊每個預測邊界框        for i in range(pred_box.shape[0]):            # 如果沒有比對的真實邊界框,或者重疊比例低于門檻值,跳過該預測邊界框            if matches[i] == -1 or iou_matrix[i][matches[i]] < iou_threshold:                continue            # 擷取比對的真實邊界框的索引            j = matches[i]            # 累加比對數和重疊率            match_count += 1            sum_iou += iou_matrix[i][j]    # 計算并傳回多目标跟蹤精确度(MOTP)值    motp = sum_iou / match_count    return motp# 計算跟蹤長度(TL)def track_length(pred_boxes, gt_boxes, iou_threshold=0.5):    # pred_boxes是一個清單,長度為t,表示t個時間步的預測的3D邊界框    # gt_boxes是一個清單,長度為t,表示t個時間步的真實的3D邊界框    # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5    # 傳回一個字典,鍵為真實目标的ID,值為對應的跟蹤長度    # 如果沒有預測或真實邊界框,傳回空字典    if len(pred_boxes) == 0 or len(gt_boxes) == 0:        return {}    # 初始化跟蹤長度字典為空字典    tl_dict = {}    # 周遊每個時間步    for t in range(len(pred_boxes)):        # 擷取目前時間步的預測和真實邊界框        pred_box = pred_boxes[t]        gt_box = gt_boxes[t]        # 如果目前時間步沒有預測或真實邊界框,跳過該時間步        if pred_box.shape[0] == 0 or gt_box.shape[0] == 0:            continue        # 計算目前時間步的預測和真實邊界框之間的重疊比例矩陣,大小為p x g        iou_matrix = np.zeros((pred_box.shape[0], gt_box.shape[0]))        for i in range(pred_box.shape[0]):            for j in range(gt_box.shape[0]):                iou_matrix[i][j] = iou_3d(pred_box[i], gt_box[j])        # 使用匈牙利算法進行資料關聯,得到比對結果        matches = data_association(-iou_matrix)        # 周遊每個預測邊界框        for i in range(pred_box.shape[0]):            # 如果沒有比對的真實邊界框,或者重疊比例低于門檻值,跳過該預測邊界框            if matches[i] == -1 or iou_matrix[i][matches[i]] < iou_threshold:                continue            # 擷取比對的真實邊界框的索引            j = matches[i]            # 如果真實目标的ID已經在跟蹤長度字典中,累加1            if j in tl_dict:                tl_dict[j] += 1            # 否則,初始化為1            else:                tl_dict[j] = 1    # 傳回跟蹤長度字典    return tl_dict# 計算跟蹤片段(TF)def track_fragment(pred_boxes, gt_boxes, iou_threshold=0.5):    # pred_boxes是一個清單,長度為t,表示t個時間步的預測的3D邊界框    # gt_boxes是一個清單,長度為t,表示t個時間步的真實的3D邊界框    # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5    # 傳回一個字典,鍵為真實目标的ID,值為對應的跟蹤片段數    # 如果沒有預測或真實邊界框,傳回空字典    if len(pred_boxes) == 0 or len(gt_boxes) == 0:        return {}    # 初始化跟蹤片段字典為空字典    tf_dict = {}    # 初始化上一時間步的比對結果為空字典    prev_matches = {}    # 周遊每個時間步    for t in range(len(pred_boxes)):        # 擷取目前時間步的預測和真實邊界框        pred_box = pred_boxes[t]        gt_box = gt_boxes[t]        # 如果目前時間步沒有預測或真實邊界框,跳過該時間步        if pred_box.shape[0] == 0 or gt_box.shape[0] == 0:            continue        # 計算目前時間步的預測和真實邊界框之間的重疊比例矩陣,大小為p x g        iou_matrix = np.zeros((pred_box.shape[0], gt_box.shape[0]))        for i in range(pred_box.shape[0]):            for j in range(gt_box.shape[0]):                iou_matrix[i][j] = iou_3d(pred_box[i], gt_box[j])        # 使用匈牙利算法進行資料關聯,得到比對結果        matches = data_association(-iou_matrix)        # 初始化目前時間步的比對結果為空字典        curr_matches = {}        # 周遊每個預測邊界框        for i in range(pred_box.shape[0]):            # 如果沒有比對的真實邊界框,或者重疊比例低于門檻值,跳過該預測邊界框            if matches[i] == -1 or iou_matrix[i][matches[i]] < iou_threshold:                continue            # 擷取比對的真實邊界框的索引            j = matches[i]            # 将目前的比對結果儲存到字典中            curr_matches[j] = i            # 如果真實目标的ID已經在跟蹤片段字典中,且上一時間步沒有比對該目标,累加1            if j in tf_dict and j not in prev_matches:                tf_dict[j] += 1            # 否則,初始化為1            elif j not in tf_dict:                tf_dict[j] = 1        # 更新上一時間步的比對結果為目前的比對結果        prev_matches = curr_matches    # 傳回跟蹤片段字典    return tf_dict# 計算ID切換率(ISR)def id_switch_rate(pred_boxes, gt_boxes, iou_threshold=0.5):    # pred_boxes是一個清單,長度為t,表示t個時間步的預測的3D邊界框    # gt_boxes是一個清單,長度為t,表示t個時間步的真實的3D邊界框    # iou_threshold是一個浮點數,表示重疊比例的門檻值,預設為0.5    # 傳回一個字典,鍵為真實目标的ID,值為對應的ID切換次數    # 如果沒有預測或真實邊界框,傳回空字典    if len(pred_boxes) == 0 or len(gt_boxes) == 0:        return {}    # 初始化ID切換率字典為空字典    isr_dict = {}    # 初始化上一時間步的比對結果為空字典    prev_matches = {}    # 周遊每個時間步    for t in range(len(pred_boxes)):        # 擷取目前時間步的預測和真實邊界框        pred_box = pred_boxes[t]        gt_box = gt_boxes[t]        # 如果目前時間步沒有預測或真實邊界框,跳過該時間步        if pred_box.shape[0] == 0 or gt_box.shape[0] == 0:            continue        # 計算目前時間步的預測和真實邊界框之間的重疊比例矩陣,大小為p x g        iou_matrix = np.zeros((pred_box.shape[0], gt_box.shape[0]))        for i in range(pred_box.shape[0]):            for j in range(gt_box.shape[0]):                iou_matrix[i][j] = iou_3d(pred_box[i], gt_box[j])        # 使用匈牙利算法進行資料關聯,得到比對結果        matches = data_association(-iou_matrix)        # 初始化目前時間步的比對結果為空字典        curr_matches = {}        # 周遊每個預測邊界框        for i in range(pred_box.shape[0]):            # 如果沒有比對的真實邊界框,或者重疊比例低于門檻值,跳過該預測邊界框            if matches[i] == -1 or iou_matrix[i][matches[i]] < iou_threshold:                continue            # 擷取比對的真實邊界框的索引            j = matches[i]            # 将目前的比對結果儲存到字典中            curr_matches[j] = i            # 如果上一時間步有比對的真實邊界框,并且ID不同,累加1            if j in prev_matches and prev_matches[j] != i:                # 如果真實目标的ID已經在ID切換率字典中,累加1                if j in isr_dict:                    isr_dict[j] += 1                # 否則,初始化為1                else:                    isr_dict[j] = 1        # 更新上一時間步的比對結果為目前的比對結果        prev_matches = curr_matches    # 傳回ID切換率字典    return isr_dict      

以上就是我幫大家總結的3D點雲目标跟蹤中常見的評價名額和代碼詳解,希望可以幫助到大家。

目前工坊已經建立了3D視覺方向多個社群,包括SLAM、工業3D視覺、自動駕駛方向,細分群包括:[工業方向]三維點雲、結構光、機械臂、缺陷檢測、三維測量、TOF、相機标定、綜合群;[SLAM方向]多傳感器融合、ORB-SLAM、雷射SLAM、機器人導航、RTK|GPS|UWB等傳感器交流群、SLAM綜合讨論群;[自動駕駛方向]深度估計、Transformer、毫米波|雷射雷達|視覺攝像頭傳感器讨論群、多傳感器标定、自動駕駛綜合群等。[三維重建方向]NeRF、colmap、OpenMVS等。除了這些,還有求職、硬體選型、視覺産品落地等交流群。大家可以添加小助理微信: dddvisiona,備注:加群+方向+學校|公司, 小助理會拉你入群。

繼續閱讀