1
This commit is contained in:
@@ -39,7 +39,7 @@ class _CameraScreenState extends State<CameraScreen> {
|
||||
String? _initError;
|
||||
|
||||
/// 置信度阈值(设置页滑块调整,worker 内实时生效)
|
||||
double _minScore = 0.10;
|
||||
double _minScore = 0.015;
|
||||
|
||||
/// 原生侧帧状态轮询结果(诊断用;无帧时诊断行也能实时刷新)
|
||||
Map<dynamic, dynamic> _nativeStats = const {};
|
||||
@@ -181,9 +181,9 @@ class _CameraScreenState extends State<CameraScreen> {
|
||||
),
|
||||
Slider(
|
||||
value: _minScore,
|
||||
min: 0.05,
|
||||
min: 0.01,
|
||||
max: 0.50,
|
||||
divisions: 45,
|
||||
divisions: 49,
|
||||
activeColor: Colors.greenAccent,
|
||||
onChanged: (v) {
|
||||
setSheetState(() => _minScore = v);
|
||||
|
||||
@@ -173,7 +173,10 @@ class CameraViewModel extends ChangeNotifier {
|
||||
int now) {
|
||||
final matched = <int>{};
|
||||
for (final r in results) {
|
||||
if (!_plausible(r)) continue;
|
||||
// 仅挡退化数据(零宽/零高):尺寸与宽高比不设限——过近/过远的真实
|
||||
// 目标同样要显示(2026-09-12 用户定案:原几何门把远处小鸟和近处
|
||||
// 大目标一并丢弃;质量交给置信度 + 轨迹确认把关)
|
||||
if (r.width <= 0 || r.height <= 0) continue;
|
||||
_Track? best;
|
||||
var bestD = associateRadius;
|
||||
for (final t in _tracks.values) {
|
||||
@@ -241,17 +244,6 @@ class CameraViewModel extends ChangeNotifier {
|
||||
motionRegions.any((m) => MotionAggregator.centerInRegion(r, m)) ||
|
||||
noveltyRegions.any((m) => MotionAggregator.centerInRegion(r, m));
|
||||
|
||||
/// 物理合理性过滤:宽高比与相对尺寸(目标物种 20-100px@720 量级)
|
||||
bool _plausible(DetectionResult r) {
|
||||
final h = r.height;
|
||||
final w = r.width;
|
||||
if (w <= 0 || h <= 0) return false;
|
||||
final aspect = w / h;
|
||||
if (aspect < 0.3 || aspect > 3.0) return false;
|
||||
if (r.label == 'suspect') return h >= 0.01 && h <= 0.5;
|
||||
return h >= 0.01 && h <= 0.3;
|
||||
}
|
||||
|
||||
double _centerDist(DetectionResult a, DetectionResult b) =>
|
||||
math.sqrt(math.pow(a.centerX - b.centerX, 2) +
|
||||
math.pow(a.centerY - b.centerY, 2));
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:sensors_plus/sensors_plus.dart';
|
||||
|
||||
/// 陀螺仪全局运动追踪(主 isolate):对角速度按 dt 积分得到两次运动分析帧
|
||||
/// 之间的累计转角,折算为缩略图像素位移,供 MotionDetector 做全局运动补偿——
|
||||
/// 补偿后差分 = 目标独立运动(手持/走动的旋转分量被抵消)。
|
||||
///
|
||||
/// 符号约定(背部相机、竖屏、假设无镜像):绕设备竖轴(y)偏航 → 画面水平位移;
|
||||
/// 绕设备横轴(x)俯仰 → 画面垂直位移。kPanSign/kTiltSign 待真机校准:
|
||||
/// 若补偿后误报反而变多(差分被放大),把对应符号取反即可。
|
||||
/// 位移超限(缩略图短边 1/4)时由消费方丢弃该帧——运动过快时像素级补偿不可靠。
|
||||
class GyroTracker {
|
||||
static const double assumedVfovDeg = 52; // 与测距口径一致
|
||||
static const double kPanSign = 1;
|
||||
static const double kTiltSign = 1;
|
||||
|
||||
StreamSubscription<GyroscopeEvent>? _gyroSub;
|
||||
StreamSubscription<AccelerometerEvent>? _accSub;
|
||||
|
||||
double _panRad = 0; // 两次 takeShift 之间的累计偏航
|
||||
double _tiltRad = 0; // 累计俯仰
|
||||
DateTime? _lastGyroMs;
|
||||
|
||||
double _accLowZ = 9.8; // 加速度计低通(重力在设备 z 轴分量)
|
||||
double _accLowY = 0; // 低通 y 轴分量(算俯仰用)
|
||||
bool _hasAcc = false;
|
||||
|
||||
/// 当前分析窗口内的角速度峰值(|x|+|y|+|z|,rad/s):走动/车载时显著抬升。
|
||||
/// takeShift 消费时清零(按分析窗口计量),供曝光联动等判定
|
||||
double _recentOmega = 0;
|
||||
|
||||
bool _running = false;
|
||||
bool get hasData => _hasGyro;
|
||||
bool _hasGyro = false;
|
||||
|
||||
/// 角速度峰值超阈值(rad/s)→ 机位正在明显运动(手持快走/车载)
|
||||
bool get recentlyMoving => _recentOmega > 0.5;
|
||||
|
||||
void start() {
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_gyroSub = gyroscopeEventStream().listen((e) {
|
||||
final now = DateTime.now();
|
||||
final last = _lastGyroMs;
|
||||
if (last != null) {
|
||||
final dt =
|
||||
(now.millisecondsSinceEpoch - last.millisecondsSinceEpoch) / 1000.0;
|
||||
if (dt > 0 && dt < 0.5) {
|
||||
_panRad += e.y * dt;
|
||||
_tiltRad += e.x * dt;
|
||||
_hasGyro = true;
|
||||
final mag = e.x.abs() + e.y.abs() + e.z.abs();
|
||||
if (mag > _recentOmega) _recentOmega = mag;
|
||||
}
|
||||
}
|
||||
_lastGyroMs = now;
|
||||
});
|
||||
_accSub = accelerometerEventStream().listen((e) {
|
||||
// 低通估重力方向 → 俯仰角(镜头朝上为正)
|
||||
const a = 0.1;
|
||||
_accLowZ = _accLowZ * (1 - a) + e.z * a;
|
||||
_accLowY = _accLowY * (1 - a) + e.y * a;
|
||||
_hasAcc = true;
|
||||
});
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_running = false;
|
||||
_gyroSub?.cancel();
|
||||
_accSub?.cancel();
|
||||
_gyroSub = null;
|
||||
_accSub = null;
|
||||
}
|
||||
|
||||
/// 消费累计转角 → 缩略图像素位移 (dx, dy)。
|
||||
/// dx>0 表示画面内容向右移动(前帧采样点左移补偿)。
|
||||
(int, int) takeShift(double thumbW, double thumbH) {
|
||||
final pan = _panRad;
|
||||
final tilt = _tiltRad;
|
||||
_panRad = 0;
|
||||
_tiltRad = 0;
|
||||
_recentOmega = 0;
|
||||
if (!_hasGyro || (pan == 0 && tilt == 0)) return (0, 0);
|
||||
final focalPx = (thumbH / 2) / math.tan(assumedVfovDeg * math.pi / 180 / 2);
|
||||
final limit = math.max(thumbW, thumbH) / 4;
|
||||
final dx = (kPanSign * pan * focalPx).clamp(-limit, limit).round();
|
||||
final dy = (kTiltSign * tilt * focalPx).clamp(-limit, limit).round();
|
||||
return (dx, dy);
|
||||
}
|
||||
|
||||
/// 俯仰角(度):0=平举,正=镜头朝上。无加速度计数据返回 0。
|
||||
double get pitchDeg {
|
||||
if (!_hasAcc) return 0;
|
||||
return math.atan2(_accLowY, _accLowZ) * 180 / math.pi;
|
||||
}
|
||||
|
||||
/// 角速度是否持续偏大(走动/车载判定,供曝光联动等使用)
|
||||
bool get moving => recentlyMoving;
|
||||
}
|
||||
Reference in New Issue
Block a user