1
This commit is contained in:
@@ -17,7 +17,7 @@ import 'visual_prior.dart';
|
||||
/// 主 isolate 只投递帧数据、接收结果,UI 不被推理阻塞(iOS 真机卡顿根因)。
|
||||
///
|
||||
/// 多模型并行推理:传入 [models](各数据集下载模型)后,每帧逐模型推理,
|
||||
/// 结果按类别分组跨模型 NMS 合并(同标签重复框取高分,不同标签互不压制);
|
||||
/// 结果跨模型全局 NMS 合并(2026-09-01 修订:异类别重叠也去重取高分,实测多模型对同一目标检异类别);
|
||||
/// 无下载模型时不启动推理(仅预览)。
|
||||
class DetectorWorker {
|
||||
final Isolate _isolate;
|
||||
@@ -327,7 +327,7 @@ Future<void> _workerMain(SendPort mainPort) async {
|
||||
}
|
||||
}
|
||||
// 多模型并行推理:每模型先首帧自适应判定 YUV 值域/色序,再逐模型推理;
|
||||
// 汇总后按类别分组跨模型 NMS 合并(同标签重复框取高分,异标签互不压制)
|
||||
// 汇总后全局 NMS 合并(2026-09-01:异类别重叠也去重取高分,实测多模型对同一目标检异类别)
|
||||
var results = <DetectionResult>[];
|
||||
for (final d in detectors) {
|
||||
if (!isBgra && !d.yuvModeKnown) {
|
||||
@@ -460,19 +460,12 @@ Future<void> _workerMain(SendPort mainPort) async {
|
||||
}
|
||||
}
|
||||
|
||||
/// 多模型结果合并:按类别分组,组内 NMS(不同模型检出同一目标时取高分)。
|
||||
/// 各模型类别体系独立(如环颈雉鸡/疑似 vs 野兔/疑似),不同类别互不压制。
|
||||
/// 多模型结果合并:全局 NMS(不区分类别)。
|
||||
/// 实测多个模型会对同一目标检出不同类别(误检/歧义),若异类别互不压制
|
||||
/// 会出现重叠框;2026-09-01 用户实测定案:所有模型的框统一按 IoU 去重,
|
||||
/// 重叠时取高分(远处真实的多目标互不重叠,正常保留)。
|
||||
List<DetectionResult> mergeAcrossModels(
|
||||
List<DetectionResult> all, double iouThreshold) {
|
||||
if (all.length <= 1) return all;
|
||||
final byLabel = <String, List<DetectionResult>>{};
|
||||
for (final r in all) {
|
||||
byLabel.putIfAbsent(r.label, () => []).add(r);
|
||||
}
|
||||
final merged = <DetectionResult>[];
|
||||
for (final group in byLabel.values) {
|
||||
merged.addAll(nms(group, iouThreshold));
|
||||
}
|
||||
merged.sort((a, b) => b.score.compareTo(a.score));
|
||||
return merged;
|
||||
return nms(all, iouThreshold);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,27 @@ double iou(DetectionResult a, DetectionResult b) {
|
||||
return union <= 0 ? 0 : inter / union;
|
||||
}
|
||||
|
||||
/// 交叠/较小框面积(minIoU):同一目标的一大一小两框时比 IoU 更能命中
|
||||
/// (IoU = 小/大 会漏判;YOLO 同目标常输出大小两框,与 server 标注端
|
||||
/// localAi.overlapThreshold 同思路,2026-09-01 用户实测修订)。
|
||||
double boxOverlap(DetectionResult a, DetectionResult b) {
|
||||
final x0 = a.left > b.left ? a.left : b.left;
|
||||
final y0 = a.top > b.top ? a.top : b.top;
|
||||
final x1 = a.right < b.right ? a.right : b.right;
|
||||
final y1 = a.bottom < b.bottom ? a.bottom : b.bottom;
|
||||
if (x1 <= x0 || y1 <= y0) return 0;
|
||||
final inter = (x1 - x0) * (y1 - y0);
|
||||
final minArea = a.width * a.height < b.width * b.height
|
||||
? a.width * a.height
|
||||
: b.width * b.height;
|
||||
return minArea <= 0 ? 0 : inter / minArea;
|
||||
}
|
||||
|
||||
List<DetectionResult> nms(List<DetectionResult> boxes, double iouThreshold) {
|
||||
final sorted = [...boxes]..sort((a, b) => b.score.compareTo(a.score));
|
||||
final kept = <DetectionResult>[];
|
||||
for (final b in sorted) {
|
||||
if (!kept.any((k) => iou(b, k) > iouThreshold)) kept.add(b);
|
||||
if (!kept.any((k) => boxOverlap(b, k) > iouThreshold)) kept.add(b);
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user