Files
observer/flutter_app/lib/detection/motion_aggregator.dart
T

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;
}
}