47 lines
1.6 KiB
Dart
47 lines
1.6 KiB
Dart
import 'dart:typed_data';
|
||
|
||
import 'package:image/image.dart' as img;
|
||
|
||
/// 按一次快照帧:单平面 BGRA/RGBA 像素(两平台分析帧均为竖屏单平面 4 通道)。
|
||
/// 即喂给 YOLO 推理的同一帧,尺寸与推理输入同源。
|
||
class FrameSnapshot {
|
||
final Uint8List plane;
|
||
final int bytesPerRow;
|
||
final int width;
|
||
final int height;
|
||
|
||
/// true = BGRA 字节序(Android 原生通道默认 / iOS bgra8888),false = RGBA
|
||
final bool bgra;
|
||
|
||
const FrameSnapshot({
|
||
required this.plane,
|
||
required this.bytesPerRow,
|
||
required this.width,
|
||
required this.height,
|
||
required this.bgra,
|
||
});
|
||
}
|
||
|
||
/// 在 isolate 执行:整帧像素行拷贝 → 解码为 Image → jpg q85 重编码。
|
||
/// 与推理帧同源同尺寸、重编码天然剥离全部元数据(合规硬要求)
|
||
Uint8List encodeFrameJpeg(FrameSnapshot snap) {
|
||
final packed = Uint8List(snap.width * snap.height * 4);
|
||
for (var y = 0; y < snap.height; y++) {
|
||
final srcStart = y * snap.bytesPerRow;
|
||
final dstStart = y * snap.width * 4;
|
||
packed.setRange(dstStart, dstStart + snap.width * 4,
|
||
snap.plane.sublist(srcStart, srcStart + snap.width * 4));
|
||
}
|
||
final image = img.Image.fromBytes(
|
||
width: snap.width,
|
||
height: snap.height,
|
||
bytes: packed.buffer,
|
||
numChannels: 4,
|
||
order: snap.bgra ? img.ChannelOrder.bgra : img.ChannelOrder.rgba,
|
||
);
|
||
return Uint8List.fromList(img.encodeJpg(image, quality: 85));
|
||
}
|
||
|
||
/// 单独同意弹窗结果存储 key(FlutterSecureStorage,与 terms_accepted 同库)
|
||
const falseTargetConsentKey = 'false_target_consent';
|