54 lines
1.9 KiB
Dart
54 lines
1.9 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import '../detection/detection_result.dart';
|
|
import '../detection/motion_aggregator.dart';
|
|
|
|
/// 轻量运动检测:相邻帧 Y 通道差分 + 分块聚合。
|
|
/// 小尺寸工作(约 128x128 内),在分析流中串行调用。
|
|
/// 相机大幅移动时(全屏帧差)自动忽略本帧,避免误报。
|
|
class MotionDetector {
|
|
final int maxWidth;
|
|
final int maxHeight;
|
|
|
|
List<int>? _prevGray;
|
|
|
|
MotionDetector({this.maxWidth = 128, this.maxHeight = 128});
|
|
|
|
/// 后台 isolate 用原始数据接口(不依赖 CameraImage)。
|
|
List<MotionRegion> detectMotionRaw(
|
|
Uint8List yPlane, int yStride, int width, int height) {
|
|
final w = width, h = height;
|
|
final scale = maxWidth / w < maxHeight / h ? maxWidth / w : maxHeight / h;
|
|
final tw = (w * scale).toInt().clamp(1, maxWidth);
|
|
final th = (h * scale).toInt().clamp(1, maxHeight);
|
|
if (tw == 0 || th == 0) return const [];
|
|
|
|
// 取 Y 平面缩放灰度(最近邻下采样到 128x128 内)
|
|
final y = yPlane;
|
|
final gray = List<int>.filled(tw * th, 0);
|
|
for (var oy = 0; oy < th; oy++) {
|
|
final sy = (oy / scale).toInt().clamp(0, h - 1);
|
|
for (var ox = 0; ox < tw; ox++) {
|
|
final sx = (ox / scale).toInt().clamp(0, w - 1);
|
|
gray[oy * tw + ox] = y[sy * yStride + sx];
|
|
}
|
|
}
|
|
|
|
final prev = _prevGray;
|
|
_prevGray = List.of(gray);
|
|
if (prev == null || prev.length != gray.length) return const [];
|
|
|
|
final diff = MotionAggregator.diffMask(gray, prev);
|
|
final motionTotal = diff.fold(0, (a, b) => a + b);
|
|
// 全屏大差异 → 相机移动/大范围变化,忽略本帧
|
|
if (motionTotal > tw * th / 2) return const [];
|
|
if (motionTotal < 12) return const [];
|
|
return MotionAggregator.aggregate(diff, tw, th);
|
|
}
|
|
|
|
/// 相机切换后重置参考帧,避免旧帧误差
|
|
void reset() {
|
|
_prevGray = null;
|
|
}
|
|
}
|