YOLO是可以直接实现一张图片检测多个目标对吗,支持多分类吗

最后发布时间:2026-10-26 15:09:11 浏览量:

Read , lines 1 to 80

两个都是「是」,但第二个有个重要限制。

1. 单图多目标 —— 是,而且不需要任何额外代码

YOLO 的输出不是「一张图一个结果」,而是 N 个 anchor 各带一套 (位置 + 所有类别分数)。我们的解码器 detect/yolo.rs::decode 就是逐 anchor 扫描:

for a in 0..anchors {              // 遍历 N 个 anchor
    // 在 80 个类别里找分数最高的
    for c in 0..num_classes {
        let s = out[(4 + c) * anchors + a];
        ...
    }
    if best_s < score_thr { continue; }
    raw.push(Detection { ... });    // 每个合格 anchor 都是一个独立目标
}

所以一帧里有多少只动物,就会返回多少个 Detection,天然支持多目标。multi-object 这件事在检测器层面是免费的 —— 真正要额外做的是多目标追踪(同一只鼠跨帧保持同一 ID),那就是我们已经实现的部分。

顺带:NMS 是按 class_id 分组的(k.class_id == d.class_id),这是标准做法 —— 避免「一只鼠被识别成 cat 和 dog 两个框」时它们因为 IoU 高而互相压制掉其中一个。

2. 多分类 —— 支持,但当前代码只取 Top-1

先说支持什么:

  • 网络的输出确实是完整的 80 维类别分数(rows = 4 + num_classes);
  • 类名从 ONNX metadata 自动读取,所以换成「mouse / immobile / rearing」这种自训练类别也能直接用;
  • 追踪器按 class_id 做匹配门控(self.tracks[ti].class_id != d.class_id → 禁配),不会把不同类别串到一起。

但有一个真实限制:解码时每个 anchor 只保留分数最高的那一类:

let mut best_c = 0usize;
let mut best_s = 0.0f32;
for c in 0..num_classes {
    if s > best_s { best_s = s; best_c = c; }   // ← 只留最大值
}

这意味着:

场景是否会丢结果
一帧有多只鼠 → 每个 anchor 独立判断✅ 不丢
同一只鼠可能同时是「鼠」和「趴着」⚠️ 只输出概率最高那个
两个类别分数都超过阈值(如 0.6 / 0.5)⚠️ 低分那个被丢弃

对 TST 使用场景,这个限制基本无害,因为检测器只负责「找出鼠在哪」,行为分类(不动/活动)是后面 0.5 s 窗口 + XGBoost 做的独立任务,两者不共用一套类别。

需要改的话,改动很小

如果你想做真正的 multi-label(同一目标保留多个类别),decode 里把内层循环改成收集所有超阈值的类别即可:

for c in 0..num_classes {
    let s = out[(4 + c) * anchors + a];
    if s >= score_thr {
        raw.push(Detection { bbox, score: s, class_id: c });
    }
}

代价是检测数量会成倍增长,NMS 之后的 max_det=100 更容易被打满,需要相应调高。


顺便注意到你刚才跑的是:

./target/release/rodent-act track -i TST.mp4 --detector yolo --class-name any --min-hits 1

用了真实的 TST.mp4,且 --class-name any + --min-hits 1(最宽松设置)。这个结果是 COCO 模型在真实悬尾画面上的鲁棒性/可用性基线,拿到它的数字应该会很有参考价值。要我把 ST 这一步的结果解读一下,或者跑一次 --min-hits 1 和 --class-name mouse 的对照,看 COCO 模型在真实 TST 素材上的具体命中率吗?