YOLO11 模型 C++ 部署:ONNX 导出、NMS 判别与推理实战
1. 现象与本质
现象:C++ 推理出现'满屏框'或'0 框'。
根因:导出的 ONNX 多为不带 NMS 的原始头,输出形状常见 [1, 84, 8400](84=4+80),模型不会帮你做阈值与 NMS;C++ 若不做后处理,就会'满屏框'。

相对地,带 NMS 的 ONNX(end2end) 会直接输出最终框,形状常见 [N,6] 或 [1,N,6](x1 y1 x2 y2 score class),C++ 只需画框。
2. NMS 是什么?为什么一定要做?
YOLO 在多尺度(如 80×80、40×40、20×20)上为每个网格点预测很多候选框。同一目标附近会出现大量位置相近、得分也不低的候选。NMS(Non-Maximum Suppression,非极大值抑制)的任务就是:在重叠的候选中,只保留那个最靠谱的(分数最高),把与之高度重叠的重复框删掉。
2.1 NMS 是怎么做的(硬 NMS 伪代码)
- IoU(交并比):IoU(A,B) = Area(A∩B) / Area(A∪B) ∈ [0, 1]
- 硬 NMS 两步:
- 按置信度降序排序;
- 依次取最高分框,删除与它 IoU 大于阈值(如 0.45)的后续框。
# boxes: [N, 4] in x1y1x2y2
# scores: [N]
# thr_conf: 置信度阈值(如 0.25)
# thr_iou: NMS IoU 阈值(如 0.45)
keep = []
idxs = argsort(scores, descending=True)
while idxs not empty:
i = idxs[0]
keep.append(i)
ious = IoU(boxes[i], boxes[idxs[1:]])
idxs = idxs[1:][ious <= thr_iou]
return keep
常见做法是按类别分别做 NMS(class-wise NMS)。Ultralytics 默认如此。
2.2 一个极小数值例子
| idx | score | box(x1,y1,x2,y2) |
|---|---|---|
| 0 | 0.90 | (10,10,50,50) |



