97 lines
3.2 KiB
Dart
97 lines
3.2 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'detection_result.dart';
|
|
|
|
/// 帧差运动聚合:每像素 0/1 差分掩码 → 8x8 分块统计 → 连通块聚合为运动区域。
|
|
class MotionAggregator {
|
|
static const int blockGrid = 8;
|
|
static const double blockActiveRatio = 0.30;
|
|
static const int maxRegions = 3;
|
|
static const int diffThreshold = 25;
|
|
|
|
static List<MotionRegion> aggregate(List<int> diff, int width, int height) {
|
|
final bw = width ~/ blockGrid;
|
|
final bh = height ~/ blockGrid;
|
|
if (bw == 0 || bh == 0) return const [];
|
|
|
|
final active = List<bool>.filled(blockGrid * blockGrid, false);
|
|
for (var by = 0; by < blockGrid; by++) {
|
|
for (var bx = 0; bx < blockGrid; bx++) {
|
|
final blockW = bx == blockGrid - 1 ? width - bx * bw : bw;
|
|
final blockH = by == blockGrid - 1 ? height - by * bh : bh;
|
|
var motion = 0;
|
|
for (var y = by * bh; y < by * bh + blockH; y++) {
|
|
var idx = y * width + bx * bw;
|
|
for (var x = 0; x < blockW; x++) {
|
|
motion += diff[idx + x];
|
|
}
|
|
idx += width;
|
|
}
|
|
active[by * blockGrid + bx] =
|
|
motion > blockW * blockH * blockActiveRatio;
|
|
}
|
|
}
|
|
|
|
final regions = <MotionRegion>[];
|
|
final visited = List<bool>.filled(active.length, false);
|
|
for (var i = 0; i < active.length; i++) {
|
|
if (!active[i] || visited[i]) continue;
|
|
var minX = blockGrid, minY = blockGrid, maxX = -1, maxY = -1;
|
|
final stack = <int>[i];
|
|
visited[i] = true;
|
|
while (stack.isNotEmpty) {
|
|
final cur = stack.removeLast();
|
|
final bx = cur % blockGrid;
|
|
final by = cur ~/ blockGrid;
|
|
if (bx < minX) minX = bx;
|
|
if (bx > maxX) maxX = bx;
|
|
if (by < minY) minY = by;
|
|
if (by > maxY) maxY = by;
|
|
for (final nb in neighbors(cur)) {
|
|
if (active[nb] && !visited[nb]) {
|
|
visited[nb] = true;
|
|
stack.add(nb);
|
|
}
|
|
}
|
|
}
|
|
if (maxX - minX > 3 || maxY - minY > 3) continue; // 全屏噪声过滤
|
|
regions.add(MotionRegion(
|
|
minX * bw / width,
|
|
minY * bh / height,
|
|
math.min((maxX + 1) * bw, width) / width,
|
|
math.min((maxY + 1) * bh, height) / height,
|
|
));
|
|
if (regions.length >= maxRegions) break;
|
|
}
|
|
return regions;
|
|
}
|
|
|
|
static List<int> neighbors(int i) {
|
|
final bx = i % blockGrid;
|
|
final by = i ~/ blockGrid;
|
|
final list = <int>[];
|
|
if (bx > 0) list.add(i - 1);
|
|
if (bx < blockGrid - 1) list.add(i + 1);
|
|
if (by > 0) list.add(i - blockGrid);
|
|
if (by < blockGrid - 1) list.add(i + blockGrid);
|
|
return list;
|
|
}
|
|
|
|
/// 检测框中心是否落在运动区域内(用于置信度提升判定)
|
|
static bool centerInRegion(DetectionResult box, MotionRegion region) =>
|
|
box.centerX >= region.left &&
|
|
box.centerX <= region.right &&
|
|
box.centerY >= region.top &&
|
|
box.centerY <= region.bottom;
|
|
|
|
/// 帧差掩码:|g - prev| > threshold → 1
|
|
static List<int> diffMask(List<int> gray, List<int> prev,
|
|
[int threshold = diffThreshold]) {
|
|
final diff = List<int>.filled(gray.length, 0);
|
|
for (var i = 0; i < gray.length; i++) {
|
|
diff[i] = (gray[i] - prev[i]).abs() > threshold ? 1 : 0;
|
|
}
|
|
return diff;
|
|
}
|
|
}
|