Files
observer/flutter_app/test/model_merge_test.dart
T
2026-09-03 17:50:23 +08:00

112 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:observer/detection/detection_result.dart';
import 'package:observer/detection/detector_worker.dart';
import 'package:flutter_test/flutter_test.dart';
DetectionResult box(String label, double score, double x, double y,
{int modelId = -1, String modelName = ''}) =>
DetectionResult(
label: label,
score: score,
left: x,
top: y,
right: x + 0.1,
bottom: y + 0.1,
modelId: modelId,
modelName: modelName,
classId: 0,
);
DetectionResult rect(String label, double score, double l, double t, double r,
double b, {int modelId = -1}) =>
DetectionResult(
label: label,
score: score,
left: l,
top: t,
right: r,
bottom: b,
modelId: modelId,
classId: 0,
);
void main() {
test('不同模型同标签重复框:NMS 去重取高分', () {
// 两个模型都对同一目标检出同标签框(重复框需去重)
final all = [
box('target', 0.18, 0.3, 0.3, modelId: 1, modelName: '模型A'),
box('target', 0.55, 0.31, 0.3, modelId: 2, modelName: '模型B'),
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 1);
expect(merged.first.score, 0.55);
expect(merged.first.modelName, '模型B');
});
test('不同类别重叠:去重取高分(实测多模型对同一目标检异类别)', () {
final all = [
box('target', 0.3, 0.5, 0.5, modelId: 1),
box('second', 0.7, 0.5, 0.5, modelId: 2), // 同位置但不同类别
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 1);
expect(merged.first.label, 'second');
expect(merged.first.score, 0.7);
});
test('不同类别不重叠:都保留', () {
final all = [
box('target', 0.3, 0.1, 0.1, modelId: 1),
box('second', 0.7, 0.8, 0.8, modelId: 2), // 远处互不重叠
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 2);
});
test('同模型内部与跨模型合并一致:远处不重叠保留', () {
final all = [
box('target', 0.2, 0.1, 0.1, modelId: 1, modelName: '模型A'),
box('target', 0.3, 0.8, 0.8, modelId: 1, modelName: '模型A'),
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 2);
expect(merged.first.score, 0.3); // 按分排序
});
test('单条结果原样返回', () {
final single = [box('suspect', 0.11, 0.2, 0.2)];
final merged = mergeAcrossModels(single, 0.45);
expect(identical(merged, single), isTrue);
});
test('不同模型偏移框(小框中心在大框内):0.45 阈值外的窗口也去重', () {
// 覆盖 ≈ 0.31 < 0.45:纯阈值会漏;小框中心在大框内 → 同一目标取高分
// 2026-09-03:不同输入分辨率模型对同一目标的框几何系统性偏移)
final all = [
rect('target', 0.5, 0.38, 0.445, 0.62, 0.535, modelId: 1),
rect('second', 0.6, 0.2, 0.2, 0.5, 0.5, modelId: 2),
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 1);
expect(merged.first.label, 'second');
expect(merged.first.score, 0.6);
});
test('同模型偏移框:不套用跨模型窗口,正常保留', () {
final all = [
rect('target', 0.5, 0.38, 0.445, 0.62, 0.535, modelId: 1),
rect('second', 0.6, 0.2, 0.2, 0.5, 0.5, modelId: 1),
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 2, reason: '同模型框对由模型内类内 NMS 负责');
});
test('无来源模型(modelId -1):不套用跨模型窗口(存量语义不变)', () {
final all = [
rect('target', 0.5, 0.38, 0.445, 0.62, 0.535),
rect('second', 0.6, 0.2, 0.2, 0.5, 0.5),
];
final merged = mergeAcrossModels(all, 0.45);
expect(merged.length, 2);
});
}