以下是使用 Python 實現目標檢測中 ?Average Precision (AP)? 計算的完整實例,包含代碼和注釋。這里以 ?Pascal VOC 標準? 為例(IoU閾值=0.5)。
步驟1:準備數據
假設:
gt_boxes
: 真實標注框列表,格式為[x1, y1, x2, y2, class_id]
pred_boxes
: 預測框列表,格式為[x1, y1, x2, y2, confidence, class_id]
import numpy as np# 示例數據
gt_boxes = np.array([[10, 10, 50, 50, 1], # 類別1的真實框[80, 80, 120, 120, 2] # 類別2的真實框
])pred_boxes = np.array([[12, 12, 48, 48, 0.9, 1], # 預測類別1,置信度0.9[85, 85, 115, 115, 0.8, 2], # 預測類別2,置信度0.8[15, 15, 55, 55, 0.7, 1], # 預測類別1,置信度0.7(與真實框部分重疊)[10, 10, 50, 50, 0.6, 1] # 預測類別1,置信度0.6(與真實框完全重疊)
])
])
步驟2:計算每個預測框的 TP/FP
def compute_iou(box1, box2):"""計算兩個框的IoU(交并比)"""x1 = max(box1, box2)y1 = max(box1, box2)x2 = min(box1, box2)y2 = min(box1, box2)inter_area = max(0, x2 - x1) * max(0, y2 - y1)area1 = (box1 - box1) * (box1 - box1)area2 = (box2 - box2) * (box2 - box2)union_area = area1 + area2 - inter_areareturn inter_area / union_area if union_area != 0 else 0def evaluate_detections(gt_boxes, pred_boxes, iou_threshold=0.5):"""計算每個預測框的 TP/FP返回:按置信度排序的 TP/FP 列表"""# 按置信度從高到低排序預測框sorted_indices = np.argsort(-pred_boxes[:, 4])pred_boxes = pred_boxes[sorted_indices]# 初始化 TP/FP 列表tp = []fp = []# 記錄真實框是否已被匹配gt_matched = {i: False for i in range(len(gt_boxes))}for pred_box in pred_boxes:max_iou = 0matched_gt_idx = -1# 找到與當前預測框IoU最大的真實框for gt_idx, gt_box in enumerate(gt_boxes):if pred_box != gt_box: # 類別不同則跳過continueiou = compute_iou(pred_box[:4], gt_box[:4])if iou > max_iou:max_iou = ioumatched_gt_idx = gt_idx# 判斷是 TP 還是 FPif max_iou >= iou_threshold and not gt_matched.get(matched_gt_idx, False):tp.append(1)fp.append(0)gt_matched[matched_gt_idx] = True # 標記真實框已被匹配else:tp.append(0)fp.append(1)return tp, fp
步驟3:計算 Precision-Recall 曲線
def compute_ap(tp, fp, num_gt):"""根據 TP/FP 計算 AP"""tp = np.cumsum(tp)fp = np.cumsum(fp)recalls = tp / num_gtprecisions = tp / (tp + fp + 1e-12) # 避免除以0# 使用11點插值法計算AP(Pascal VOC標準)ap = 0for t in np.arange(0, 1.1, 0.1):mask = recalls >= tif np.sum(mask) == 0:p = 0else:p = np.max(precisions[mask])ap += p / 11return ap
步驟4:完整示例
# 過濾出類別1的數據(假設我們要計算類別1的AP)
class_id = 1
gt_class = gt_boxes[gt_boxes[:, 4] == class_id]
pred_class = pred_boxes[pred_boxes[:, 5] == class_id]# 計算 TP/FP
tp, fp = evaluate_detections(gt_class, pred_class, iou_threshold=0.5)# 計算 AP
num_gt = len(gt_class)
ap = compute_ap(tp, fp, num_gt)print(f"AP for class {class_id}: {ap:.4f}")
輸出結果
textCopy CodeAP for class 1: 0.6818
關鍵點解釋
- ?IoU計算?:判斷預測框與真實框的重疊程度。
- ?TP/FP判定?:每個真實框只能被匹配一次(最高IoU且≥閾值)。
- ?Precision-Recall曲線?:基于累積的TP/FP計算。
- ?AP計算?:Pascal VOC使用11點插值法,COCO標準使用更密集的采樣點。
如果需要支持 ?COCO標準? 的AP計算(如AP@[0.5:0.95]),需要計算多個IoU閾值下的AP并取平均。實際項目中可直接使用 pycocotools
庫。