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 aggregate(List diff, int width, int height) { final bw = width ~/ blockGrid; final bh = height ~/ blockGrid; if (bw == 0 || bh == 0) return const []; final active = List.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 = []; final visited = List.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 = [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 neighbors(int i) { final bx = i % blockGrid; final by = i ~/ blockGrid; final list = []; 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 diffMask(List gray, List prev, [int threshold = diffThreshold]) { final diff = List.filled(gray.length, 0); for (var i = 0; i < gray.length; i++) { diff[i] = (gray[i] - prev[i]).abs() > threshold ? 1 : 0; } return diff; } }