迁移 Flutter 端与训练脚本,模型/训练产物移出 git(遵循纯代码约定)
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'detection_result.dart';
|
||||
|
||||
/// 运行时视觉先验:对低置信度野鸡框做多线索过滤,降低户外误报。
|
||||
///
|
||||
/// 仅对 score < [maxScore](0.35)的 pheasant 框生效;高分框与
|
||||
/// suspect(生境预警)不参与过滤,避免误杀。
|
||||
///
|
||||
/// 线索:
|
||||
/// - 颜色:绿色主导(草/叶)、蓝色主导(天空/水)、平坦低饱和(键盘/石头/文字)
|
||||
/// - 位置:中心在画面上部 15%(天空区)——野鸡是地栖动物,不会出现在天空
|
||||
///
|
||||
/// 采样在原始 planes 上进行(后台 isolate 内,不依赖 UI 线程)。
|
||||
class VisualPrior {
|
||||
static const double maxScore = 0.35;
|
||||
static const double skyTopRatio = 0.15;
|
||||
|
||||
// 颜色判定阈值(与 tflite_detector 的 YUV 有限范围展开一致)
|
||||
static const double greenDiff = 20;
|
||||
static const double blueDiff = 10;
|
||||
static const double flatRange = 10;
|
||||
|
||||
// 采样点中满足条件的比例超过即拒绝
|
||||
static const double greenRatio = 0.5;
|
||||
static const double blueRatio = 0.4;
|
||||
static const double flatRatio = 0.6;
|
||||
|
||||
static List<DetectionResult> filter(
|
||||
List<DetectionResult> results, {
|
||||
required List<Uint8List> planes,
|
||||
required List<int> strides,
|
||||
required int width,
|
||||
required int height,
|
||||
required bool isBgra,
|
||||
}) {
|
||||
if (results.isEmpty || width <= 0 || height <= 0) return results;
|
||||
final kept = <DetectionResult>[];
|
||||
for (final r in results) {
|
||||
final lowConfPheasant = r.label == 'pheasant' && r.score < maxScore;
|
||||
if (lowConfPheasant && _reject(r, planes, strides, width, height, isBgra)) {
|
||||
continue;
|
||||
}
|
||||
kept.add(r);
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
static bool _reject(DetectionResult r, List<Uint8List> planes,
|
||||
List<int> strides, int width, int height, bool isBgra) {
|
||||
// 位置线索:detectRaw 输出为图像坐标系,centerY 直接可判天空区
|
||||
if (r.centerY < skyTopRatio) return true;
|
||||
|
||||
// 颜色线索:框中心 ±20% 区域 5×5 采样(小框采样点重合也没关系)
|
||||
final cx = (r.centerX * width).round().clamp(0, width - 1).toInt();
|
||||
final cy = (r.centerY * height).round().clamp(0, height - 1).toInt();
|
||||
final halfW = math.max(1.0, r.width * width * 0.2);
|
||||
final halfH = math.max(1.0, r.height * height * 0.2);
|
||||
|
||||
var green = 0, blue = 0, flat = 0, total = 0;
|
||||
for (var gy = -2; gy <= 2; gy++) {
|
||||
for (var gx = -2; gx <= 2; gx++) {
|
||||
final px = (cx + gx * halfW / 2).round().clamp(0, width - 1).toInt();
|
||||
final py = (cy + gy * halfH / 2).round().clamp(0, height - 1).toInt();
|
||||
final (r_, g_, b_) = _pixel(planes, strides, px, py, width, height, isBgra);
|
||||
total++;
|
||||
final mn = math.min(r_, math.min(g_, b_));
|
||||
final mx = math.max(r_, math.max(g_, b_));
|
||||
if (g_ - r_ > greenDiff && g_ - b_ > greenDiff) green++;
|
||||
if (b_ > r_ + blueDiff) blue++;
|
||||
if (mx - mn < flatRange) flat++;
|
||||
}
|
||||
}
|
||||
if (total == 0) return false;
|
||||
if (green / total > greenRatio) return true;
|
||||
if (blue / total > blueRatio) return true;
|
||||
if (flat / total > flatRatio) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 读取单像素 RGB(0~255)。
|
||||
/// BGRA 单平面:每像素 4 字节 [b,g,r,a];
|
||||
/// YUV:y 平面 + 4:2:0 半分辨率 U/V(NV12 交错或 I420 分离)。
|
||||
static (double, double, double) _pixel(List<Uint8List> planes,
|
||||
List<int> strides, int x, int y, int width, int height, bool isBgra) {
|
||||
if (isBgra) {
|
||||
final src = planes[0];
|
||||
final i = y * strides[0] + x * 4;
|
||||
return (src[i + 2].toDouble(), src[i + 1].toDouble(), src[i].toDouble());
|
||||
}
|
||||
final yy =
|
||||
(planes[0][y * strides[0] + x] - 16.0) * (255.0 / 219.0);
|
||||
final nv12 = planes.length == 2;
|
||||
final ux = (x ~/ 2).clamp(0, width ~/ 2 - 1).toInt();
|
||||
final uy = (y ~/ 2).clamp(0, height ~/ 2 - 1).toInt();
|
||||
final uvStride = strides[1];
|
||||
final un = ((nv12
|
||||
? planes[1][uy * uvStride + ux * 2].toDouble()
|
||||
: planes[1][uy * uvStride + ux].toDouble()) -
|
||||
128.0) *
|
||||
(255.0 / 224.0);
|
||||
final vn = ((nv12
|
||||
? planes[1][uy * uvStride + ux * 2 + 1].toDouble()
|
||||
: planes[2][uy * uvStride + ux].toDouble()) -
|
||||
128.0) *
|
||||
(255.0 / 224.0);
|
||||
final r = yy + 1.402 * vn;
|
||||
final g = yy - 0.344136 * un - 0.714136 * vn;
|
||||
final b = yy + 1.772 * un;
|
||||
return (r, g, b);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user