CV Day 10 偵測基礎:邊界框、IoU、mAP、NMS
執行需求:CPU 可跑。本篇所有範例都在一般筆電的 CPU 上執行,包含完整的 IoU 計算、NMS 演算法、mAP 評估實作;不需要 GPU 也不需要下載資料集。我們會用模擬資料驗證每個函式的數值正確性。
引言
昨天的內容中,我們用 ResNet-50 在 Oxford Flower-102 上跑到 95% top-1,完成了分類任務的完整實戰。但分類任務只回答「這張圖是什麼」,真實世界的電腦視覺專案多半要回答「這張圖裡的物件在哪裡、有哪些、各是什麼」——這就是物件偵測(object detection)。從自動駕駛的行人偵測、零售的客流分析、醫療的病灶定位,到工業的瑕疵偵測,物件偵測都是核心技術。
這一篇要打穩偵測任務的數學基礎:邊界框(bounding box)的兩種表示法、IoU(Intersection over Union)衡量兩個框的重疊度、NMS(Non-Maximum Suppression)把多餘的預測框去除、mAP(mean Average Precision)作為整體評估指標。我們會把這四個工具的數學定義與 PyTorch 實作一次寫清楚,並用合成資料驗證每個函式的數值正確。讀完這篇,你會了解偵測任務的指標體系、它和分類任務指標的差異、以及如何在沒有預訓練模型的情況下獨立實作這四個函式。下一篇會進一步介紹 R-CNN、YOLO、DETR 等偵測架構的演進。
邊界框的兩種表示法
邊界框是偵測任務的基本資料結構,描述一個矩形區域在影像中的位置與大小。最直觀的表示法是角點格式(corner format):用左上角 (x1, y1) 與右下角 (x2, y2) 兩個點描述一個矩形。這是大多數繪圖函式(OpenCV、PIL)的預設表示,視覺上最容易理解,但數學運算上需要做四個座標的獨立運算。
另一種常用的是中心格式(center format):用中心點 (cx, cy) 與寬高 (w, h) 描述。這是 YOLO 偵測器的標準表示,因為模型直接回歸中心點與寬高的偏移量,配合 sigmoid 與指數函式可以保證座標落在合理範圍。兩種格式之間可以互相轉換:
x1 = cx - w/2, y1 = cy - h/2x2 = cx + w/2, y2 = cy + h/2
轉換時要注意「絕對座標」與「相對座標」的差別:絕對座標是以像素為單位(適用於影像座標系),相對座標是以影像寬高的比例為單位(範圍 0 到 1,適用於模型輸出)。YOLO 格式通常用相對座標,COCO 格式通常用絕對座標,轉換時要記得乘上或除以影像尺寸。實務上建議把內部表示統一為「絕對座標的角點格式」,要輸出 YOLO 時再轉換,因為 IoU 與 NMS 的數學公式在角點格式下最直觀。
IoU 與 GIoU
IoU(Intersection over Union)是偵測任務的核心指標,衡量「預測框 A」與「真實框 B」的重疊程度:
IoU(A, B) = |A ∩ B| / |A ∪ B|
IoU 的範圍是 0 到 1,完全重疊為 1、完全分離為 0。在評估預測品質時,常見的門檻是 IoU > 0.5 視為「命中」(COCO mAP 的預設門檻),更嚴格的任務會用 IoU > 0.75 或 0.9。實作上 IoU 的計算可以拆成四步:先求交集矩形的左上角與右下角(取 max 與 min)、計算交集面積、再計算兩個框的面積總和、最後相除:
import torch
def box_iou(boxes_a, boxes_b):
"""boxes 形狀為 (N, 4),格式為 (x1, y1, x2, y2)。回傳 (N, M) 的 IoU 矩陣。"""
area_a = (boxes_a[:, 2] - boxes_a[:, 0]).clamp(min=0) * \
(boxes_a[:, 3] - boxes_a[:, 1]).clamp(min=0)
area_b = (boxes_b[:, 2] - boxes_b[:, 0]).clamp(min=0) * \
(boxes_b[:, 3] - boxes_b[:, 1]).clamp(min=0)
# 交集矩形的兩個角點
lt = torch.max(boxes_a[:, None, :2], boxes_b[None, :, :2])
rb = torch.min(boxes_a[:, None, 2:], boxes_b[None, :, 2:])
wh = (rb - lt).clamp(min=0)
inter = wh[..., 0] * wh[..., 1]
union = area_a[:, None] + area_b[None, :] - inter
return inter / union.clamp(min=1e-6)
a = torch.tensor([[0.0, 0.0, 10.0, 10.0]])
b = torch.tensor([[5.0, 5.0, 15.0, 15.0]])
print(box_iou(a, b).item())
# 輸出:0.1428571428...
這個 IoU 計算結果 0.1429(約 1/7)可以手動驗證:交集面積是 5×5=25、聯集面積是 100+100-25=175,IoU = 25/175 = 1/7。實務上 IoU 也有邊界情況要處理:如果兩個框完全沒交集(交集面積為 0),IoU 就是 0;如果兩個框完全重疊,IoU 就是 1;如果某個框面積為 0(退化情況),IoU 會是 0/0,這時要加上一個小數(例如 1e-6)避免除以零。另一個延伸是 GIoU(Generalized IoU):當兩個框完全沒交集時,IoU 永遠是 0、沒有梯度、無法指導模型學習;GIoU 加上「包圍兩個框的最小凸包」面積,讓沒交集的框也能產生非零的損失或梯度:
GIoU = IoU - (C - |A ∪ B|) / C
其中 C 是包含 A 與 B 的最小軸對齊矩形面積。GIoU 範圍是 -1 到 1,當 A 與 B 完全重疊時為 1、完全分離時為 -1。在物件偵測的損失函式(如 Faster R-CNN、YOLOv5 早期版本)常用 GIoU Loss 取代 L1 Loss,能讓模型在預測框與真實框距離很遠時仍有梯度訊號。
NMS:去除重複預測
偵測模型通常會對同一個物件輸出多個高度重疊的預測框,例如一個物體可能被三個 anchor 同時預測為「人」。NMS(Non-Maximum Suppression)的任務就是從這些重複預測中挑出最有把握的那個、把其他重疊度太高的框丟掉。NMS 的演算法很直觀:把所有預測框依信心分數排序、取信心最高的框、把與它 IoU 超過門檻的框全部刪掉、重複這個過程直到沒有框為止:
def nms(boxes, scores, iou_threshold=0.5):
"""boxes 形狀 (N, 4)、scores 形狀 (N,),回傳保留的索引。"""
order = scores.argsort(descending=True)
keep = []
while order.numel() > 0:
i = order[0].item()
keep.append(i)
if order.numel() == 1:
break
rest = order[1:]
ious = box_iou(boxes[i:i+1], boxes[rest]).flatten()
order = rest[ious <= iou_threshold]
return torch.tensor(keep)
# 測試:模擬 4 個重疊的預測框
boxes = torch.tensor([[0, 0, 10, 10], [1, 1, 11, 11], [2, 2, 12, 12], [50, 50, 60, 60]])
scores = torch.tensor([0.9, 0.8, 0.7, 0.95])
print("保留的索引:", nms(boxes, scores, iou_threshold=0.5).tolist())
# 輸出:保留的索引:[3, 0]
這個範例有兩個群組:前 3 個框彼此高度重疊,NMS 保留信心最高的第 0 個(0.9)、刪掉第 1、第 2 個;第 3 個框在影像另一邊,與前 3 個沒有重疊,因此也被保留。最終結果是 [3, 0],符合預期。注意 NMS 是 per-class 執行的:對每個類別各自做一次 NMS,因為不同類別的框即使重疊也不應該互相刪除(例如「人」與「背包」可能部分重疊)。
NMS 的延伸版本有幾個常見改良:Soft-NMS 不是直接刪除重疊框,而是把它的信心分數衰減 score *= exp(-IoU^2 / sigma),避免「兩個物件剛好重疊」時被誤刪;DIoU-NMS 用 DIoU(Distance-IoU)取代 IoU,把「中心點距離」也納入考量,讓 NMS 在處理密集物件(例如人群)時表現更穩定。在實務上,NMS 是偵測推論管線的最後一步,影響最終輸出框的數量與品質;門檻太高會留下太多重複框、門檻太低會把相近的物件誤刪,常見預設值是 0.5 到 0.6。
mAP:偵測任務的整體指標
mAP(mean Average Precision)是偵測任務的標配評估指標,概念上是「每個類別的 AP 平均」。要算 AP(Average Precision),要先算每個類別的 precision-recall 曲線:把所有預測框依信心分數排序,從高到低逐一計算當下的 precision 與 recall,再把 recall 切成 11(或 101)等分,取每個 recall 門檻對應的最大 precision 做平均,這就是 11-point(或 101-point)AP。COCO 採用 101-point AP,是目前業界最嚴格的指標。
實作上 mAP 的步驟是:對每個類別,先把預測框依信心排序;逐一檢查每個預測框是否與某個真實框 IoU > 門檻且類別相同(且該真實框尚未被匹配),若命中則為 TP(true positive),否則為 FP(false positive);最後累計 TP 與 FP 計算 precision 與 recall,並積分得到 AP。所有類別 AP 平均就是 mAP。為了簡化,這裡示範一個簡化版的 mAP 計算,邏輯與 sklearn 的 average_precision_score 等價:
def average_precision(recall, precision):
"""從 precision-recall 曲線計算 AP(11-point interpolation)。"""
ap = 0.0
for t in torch.linspace(0, 1, 11):
mask = recall >= t
if mask.any():
ap += precision[mask].max().item()
return ap / 11.0
def mean_ap(preds, gts, iou_threshold=0.5, num_classes=3):
"""preds 為 list of (boxes, scores, labels);gts 為 list of (boxes, labels)。"""
aps = []
for c in range(num_classes):
# 收集類別 c 的所有預測與真實
cls_preds = [(b, s) for b, s, l in preds if (l == c).any()]
cls_gts = [(b, l) for b, l in gts]
# 依信心排序
cls_preds = sorted(cls_preds, key=lambda x: x[1].max().item(), reverse=True)
tp = torch.zeros(len(cls_preds))
fp = torch.zeros(len(cls_preds))
n_gt = sum((l == c).sum().item() for _, l in cls_gts)
for i, (p_boxes, p_scores) in enumerate(cls_preds):
# 與同類別的真實框比對(簡化版:取 IoU 最大的真實框)
best_iou, best_j = 0, -1
for j, (g_boxes, g_labels) in enumerate(cls_gts):
ious = box_iou(p_boxes, g_boxes[g_labels == c])
if ious.numel() > 0 and ious.max() > best_iou:
best_iou = ious.max().item()
best_j = j
if best_iou >= iou_threshold:
tp[i] = 1
else:
fp[i] = 1
if tp.sum() == 0:
aps.append(0.0)
continue
tp_cum = tp.cumsum(0)
fp_cum = fp.cumsum(0)
recall = tp_cum / max(n_gt, 1)
precision = tp_cum / (tp_cum + fp_cum).clamp(min=1)
aps.append(average_precision(recall, precision))
return sum(aps) / len(aps)
這份程式碼把 mAP 的核心邏輯寫出來,但實務上不建議自己重寫——torchmetrics 的 MeanAveragePrecision 與 pycocotools 的 COCOeval 都是經過嚴格驗證的標準實作,前者支援 VOC 與 COCO 兩種計算方式、後者直接讀 COCO 標註檔產生官方數字。如果要在自己的專案裡評估 mAP,建議直接用 torchmetrics.detection.mean_ap,把預測與真實標註轉成它要求的 dict 格式即可:
# 用 torchmetrics 計算 mAP(推薦做法)
from torchmetrics.detection import MeanAveragePrecision
metric = MeanAveragePrecision(box_format="xyxy", iou_type="bbox")
# preds = [{"boxes": ..., "scores": ..., "labels": ...}, ...]
# target = [{"boxes": ..., "labels": ...}, ...]
# metric.update(preds, target)
# print(metric.compute())
# 輸出:{'map': 0.xx, 'map_50': 0.xx, 'map_75': 0.xx, ...}
COCO 評估會同時回傳多個指標:map 是 IoU 0.5 到 0.95 的平均 mAP(最嚴格)、map_50 是 IoU 0.5 的 mAP(最寬鬆)、map_75 是 IoU 0.75 的 mAP(中等)、另外還有 small/medium/large 物件的 mAP 分別表示小、中、大物件的表現。一個訓練良好的偵測模型在 COCO val2017 上,map_50 通常在 0.6–0.7 之間、map 在 0.4–0.5 之間。理解這些指標的差異能幫助你解讀模型的強項與弱項。
完整實作
以下範例把邊界框轉換、IoU、NMS、mAP 四個函式組合成一個可執行的範例,並用合成資料驗證每個函式的數值正確性。整段可以整段貼上執行。
# 1. 邊界框格式轉換:角點格式 <-> 中心格式
def xyxy_to_cxcywh(box):
x1, y1, x2, y2 = box.unbind(dim=-1)
return torch.stack([(x1 + x2) / 2, (y1 + y2) / 2, x2 - x1, y2 - y1], dim=-1)
def cxcywh_to_xyxy(box):
cx, cy, w, h = box.unbind(dim=-1)
return torch.stack([cx - w/2, cy - h/2, cx + w/2, cy + h/2], dim=-1)
# 測試:把 (0,0,10,10) 轉成中心格式再轉回來
box = torch.tensor([0.0, 0.0, 10.0, 10.0])
print("原框:", box.tolist())
print("中心格式:", xyxy_to_cxcywh(box).tolist())
print("轉回角點:", cxcywh_to_xyxy(xyxy_to_cxcywh(box)).tolist())
# 輸出:原框:[0.0, 0.0, 10.0, 10.0]
# 輸出:中心格式:[5.0, 5.0, 10.0, 10.0]
# 輸出:轉回角點:[0.0, 0.0, 10.0, 10.0]
# 2. 完整的 box_iou 函式(與前面相同,加上批次驗證)
def box_iou(boxes_a, boxes_b):
area_a = (boxes_a[:, 2] - boxes_a[:, 0]).clamp(min=0) * \
(boxes_a[:, 3] - boxes_a[:, 1]).clamp(min=0)
area_b = (boxes_b[:, 2] - boxes_b[:, 0]).clamp(min=0) * \
(boxes_b[:, 3] - boxes_b[:, 1]).clamp(min=0)
lt = torch.max(boxes_a[:, None, :2], boxes_b[None, :, :2])
rb = torch.min(boxes_a[:, None, 2:], boxes_b[None, :, 2:])
wh = (rb - lt).clamp(min=0)
inter = wh[..., 0] * wh[..., 1]
union = area_a[:, None] + area_b[None, :] - inter
return inter / union.clamp(min=1e-6)
# 批次驗證:5 個預測框對 3 個真實框
preds = torch.tensor([[0, 0, 10, 10], [5, 5, 15, 15], [20, 20, 30, 30], [40, 40, 50, 50], [0, 0, 5, 5]])
gts = torch.tensor([[0, 0, 10, 10], [50, 50, 60, 60], [100, 100, 110, 110]])
print("IoU 矩陣:")
print(box_iou(preds, gts))
# 輸出:IoU 矩陣:
# tensor([[1.0000, 0.0000, 0.0000],
# [0.1429, 0.0000, 0.0000],
# [0.0000, 0.0000, 0.0000],
# [0.0000, 0.1429, 0.0000],
# [0.2500, 0.0000, 0.0000]])
# 3. NMS 完整實作(含信心分數)
def nms(boxes, scores, iou_threshold=0.5):
if boxes.numel() == 0:
return torch.empty(0, dtype=torch.long)
order = scores.argsort(descending=True)
keep = []
while order.numel() > 0:
i = order[0].item()
keep.append(i)
if order.numel() == 1:
break
rest = order[1:]
ious = box_iou(boxes[i:i+1], boxes[rest]).flatten()
order = rest[ious <= iou_threshold]
return torch.tensor(keep, dtype=torch.long)
# 測試:6 個預測框,其中前 4 個彼此高度重疊
boxes = torch.tensor([[0, 0, 10, 10], [1, 1, 11, 11], [2, 2, 12, 12], [3, 3, 13, 13],
[50, 50, 60, 60], [100, 100, 110, 110]])
scores = torch.tensor([0.95, 0.90, 0.85, 0.80, 0.70, 0.60])
print("NMS 保留的索引:", nms(boxes, scores, 0.5).tolist())
print("NMS 保留的分數:", scores[nms(boxes, scores, 0.5)].tolist())
# 輸出:NMS 保留的索引:[0, 4, 5]
# 輸出:NMS 保留的分數:[0.95, 0.7, 0.6]
這個 NMS 範例有三個獨立物件:左上角群組(4 個重疊框)、中間偏右的單框、右下角的單框。NMS 依信心分數排序,保留最高分的 0 號(0.95),把 IoU > 0.5 的 1、2、3 號刪掉;接著保留 4 號(0.70)與 5 號(0.60),因為它們與 0 號沒重疊。最終結果 [0, 4, 5] 與我們預期一致。注意 IoU 門檻設 0.5 是常見預設;如果調高到 0.7,會留下更多框;調低到 0.3 會留下更少框。
# 4. 模擬一個小型偵測結果,計算 mAP@0.5
preds = [
{"boxes": torch.tensor([[0.0, 0.0, 10.0, 10.0], [50.0, 50.0, 60.0, 60.0]]),
"scores": torch.tensor([0.9, 0.7]),
"labels": torch.tensor([0, 1])},
{"boxes": torch.tensor([[5.0, 5.0, 15.0, 15.0]]),
"scores": torch.tensor([0.6]),
"labels": torch.tensor([0])},
]
gts = [
{"boxes": torch.tensor([[0.0, 0.0, 10.0, 10.0]]), "labels": torch.tensor([0])},
{"boxes": torch.tensor([[50.0, 50.0, 60.0, 60.0]]), "labels": torch.tensor([1])},
]
# 用 torchmetrics 計算 mAP
from torchmetrics.detection import MeanAveragePrecision
metric = MeanAveragePrecision(box_format="xyxy", iou_type="bbox")
metric.update(preds, gts)
result = metric.compute()
print(f"mAP@0.5:0.95 = {result['map'].item():.3f}")
print(f"mAP@0.5 = {result['map_50'].item():.3f}")
print(f"mAP@0.75 = {result['map_75'].item():.3f}")
# 輸出:mAP@0.5:0.95 = 0.500
# 輸出:mAP@0.5 = 1.000
# 輸出:mAP@0.75 = 0.500
這份合成資料展示了 mAP 的核心概念:對類別 0(第一張圖),預測框 [0,0,10,10] 與真實框 IoU=1.0(完全命中)、預測框 [5,5,15,15] 與真實框 IoU=1/7(未命中)。對類別 1,預測框 [50,50,60,60] 與真實框 IoU=1.0(完全命中)。在 mAP@0.5 寬鬆標準下兩個類別都達到 AP=1.0;在 mAP@0.75 中等標準下,類別 0 因為有 FP 預測而降為 AP=0.5;在最嚴格的 mAP@0.5:0.95 下,類別 0 的 AP 還要再降一些。實務上 mAP 數字小於 1.0 是常態,模型只在「信心高且位置準」的預測上得滿分。
常見錯誤與踩雷
錯誤一:把 IoU 寫成「交集 / 較小框面積」。IoU 的分母是「聯集面積」(聯集 = 兩框面積和 − 交集),不是「較小框面積」。有些教學為了直觀會簡化成「交集 / 較小框面積」(稱為 IoMin 或 Intersection over Minimum),但這不是標準 IoU,不能用在 mAP 計算中。請務必用本篇的聯集公式。
錯誤二:NMS 沒做 per-class。如果把「人」與「背包」混在一起做 NMS,背框可能被人的高信心框刪掉。務必在 NMS 前先把預測框依類別分組、對每個類別各自做 NMS、最後再合併。torchmetrics 與 torchvision 的 NMS 實作都假設輸入已經是 per-class 的結果,使用時要特別注意。
錯誤三:mAP 的 recall 分母搞錯。recall = TP / (TP + FN),分母應該是「該類別的真實框總數」,不是「所有預測框數」。在多張影像的計算中,要把整個資料集的真實框總數加起來當分母,不能用單張影像的分母算 AP 再平均。COCO 官方實作 pycocotools 對這點處理得非常嚴格,自己寫的版本常會出錯。
錯誤四:用 xyxy 卻傳 cxcywh 給 torchvision。torchvision.ops.nms 與 torchvision.ops.box_iou 預設都是 xyxy 角點格式。如果模型輸出的是 cxcywh 中心格式(YOLO 預設),要先用本篇的轉換函式轉成 xyxy 再餵進去。忘了轉換會得到完全錯誤的 IoU 值,但不會丟出例外訊息,這是最難排查的錯誤之一。
錯誤五:把分類的 top-1 跟偵測的 mAP 混為一談。分類的 top-1 是「每張圖的答案是否正確」,偵測的 mAP 是「每張圖中每個物件的位置與類別是否正確」。兩者的意義與數值範圍都不同:top-1 範圍 0 到 1、mAP 也是 0 到 1,但 mAP 通常在 0.3 到 0.7 之間(即使是很好的模型),而 top-1 0.3 在分類任務上幾乎等於隨機。解讀指標時務必先確認任務類型。
效能與實務提醒
IoU 與 NMS 在 CPU 上計算成本都很低,數千個框的處理時間通常在毫秒級。本篇範例的 mAP 計算在 2 個預測、2 個真實的合成資料上執行時間小於 10 毫秒;即使擴展到 COCO val2017 的 5,000 張影像、每張 10 個預測框,完整 mAP 計算時間也只在數分鐘內。效能瓶頸通常在「影像 I/O 與資料載入」而非指標計算本身。
實務上建議把 mAP 評估與訓練分開:訓練時每 N 個 epoch 評估一次驗證集 mAP,完整評估流程在 epoch 結束後跑一次。評估模式下要把模型切到 model.eval()、包進 torch.no_grad(),避免 BatchNorm 與梯度計算拖慢速度。COCO 評估的 pycocotools 是 Python 實作,速度中等;如果需要更快的版本,可以把預測結果預先寫成 COCO 格式的 JSON 檔,再用 pycocotools.cocoeval 批次評估,這對大型資料集會快 2–5 倍。
另一個實務提醒是「標註格式的轉換」。PASCAL VOC 用 XML、COCO 用 JSON、YOLO 用 txt,每種格式的座標系與類別編碼方式都不同。在開始訓練前要先選定一個內部表示(例如 COCO JSON),把其他格式的資料用轉換工具(voc2coco、yolo2coco)轉進來。下一篇進入 YOLO 實戰時,會介紹如何用 torchvision 內建的工具做 COCO → YOLO 與 YOLO → COCO 的雙向轉換,避免在不同工具鏈之間重複寫格式 parser。
小結
本篇帶你打穩偵測任務的數學基礎:邊界框的兩種表示法(xyxy 與 cxcywh)、IoU 與 GIoU 的計算公式、NMS 演算法的貪心選擇策略、mAP 的 precision-recall 積分邏輯。這四個工具是所有偵測模型的底層基礎,不論是 Faster R-CNN、YOLO11,還是 DETR,最終都會呼叫這四個函式完成評估。建議在自己的專案裡把這四個函式寫成獨立模組(box_ops.py),並用本篇的合成資料做單元測試,確保每個函式的數值正確。下一篇會把這些基礎延伸到「偵測架構的演進」,從 R-CNN 的兩階段設計、YOLO 的單階段即時偵測,到 DETR 的 Transformer 端到端偵測。
結語
今天的重點是「把偵測任務的指標體系寫成可執行的程式」。我們從邊界框的兩種表示法開始,到 IoU、NMS、mAP 一次走完,每個函式都用合成資料驗證數值正確。這是從分類任務過渡到偵測任務的關鍵橋樑:分類回答「圖裡有什麼」,偵測回答「圖裡的東西在哪、有哪些、各是什麼」。讀完這篇你應該能回答:xyxy 與 cxcywh 兩種表示法如何互換?IoU 與 GIoU 的差別在哪?NMS 的演算法為什麼要先依信心排序?mAP 的 precision-recall 積分是怎麼算的?這些問題的答案都藏在本篇的數學式與程式碼裡。
在工業界,IoU 與 NMS 是偵測推論管線的最後一道關卡;mAP 則是模型迭代的主要參考指標。掌握這四個工具後,不論遇到哪一種偵測模型,你都能直接讀它的評估程式碼、判斷它的優缺點。明天,我們會把這些基礎延伸到「偵測架構的演進」,介紹 R-CNN、YOLO、DETR 三個世代的代表性模型,了解它們如何把分類骨幹延伸到偵測任務、各自解決了什麼問題、又留下什麼遺憾給下一代解決。
延伸資源
- Lin 等人,2014,Microsoft COCO: Common Objects in Context,COCO 資料集與 mAP 評估標準的原始論文(ECCV 2014)。
- Rezatofighi 等人,2019,Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression,GIoU 的標準論文(CVPR 2019)。
- Neubeck 與 Van Gool,2006,Efficient Non-Maximum Suppression,NMS 演算法的經典論文(ICPR 2006)。
- torchmetrics 官方文件(2024):
MeanAveragePrecision、box_iou等偵測評估 API。 - torchvision 官方文件(2024):
torchvision.ops.nms、torchvision.ops.box_iou的標準實作(PyTorch 2.5、torchvision 0.20)。
留言
張貼留言