This commit is contained in:
2026-09-01 17:47:03 +08:00
parent ed6bfdc460
commit 046ce2f1ab
10 changed files with 185 additions and 67 deletions
+63 -46
View File
@@ -40,7 +40,12 @@ class _CameraScreenState extends State<CameraScreen> {
/// 当前 worker 已加载的模型 id 集合(激活集变化对比用)
Set<int> _loadedModelIds = const {};
bool _reloadingWorker = false;
/// 进行中的 worker 重建(并发调用共享,避免重复建/漏建)
Future<void>? _reloadInFlight;
/// ModelManager revision 快照:模型文件更新(自动更新下载新版)也需重建 worker
int _lastRevision = -1;
void _openSettings() {
final vm = _viewModel;
@@ -90,8 +95,7 @@ class _CameraScreenState extends State<CameraScreen> {
),
const SizedBox(height: 8),
const Text(
'阈值越低识别越灵敏(低分框越多,误报也可能增加)'
'不同模型的置信度分布不同,识别不到目标时可适当调低阈值。',
'阈值越低识别越灵敏(低分框越多,误报也可能增加)',
style: TextStyle(color: Colors.white54, fontSize: 12),
),
const SizedBox(height: 16),
@@ -138,59 +142,72 @@ class _CameraScreenState extends State<CameraScreen> {
setState(() => _permissionGranted = granted);
if (!granted) return;
// 模型热更新:启动拉取目录(只拉不下载),此处兜底等待;下载模型时仅预览
if (!ModelManager.instance.ready) {
try {
await ModelManager.instance
.refresh()
.timeout(const Duration(seconds: 15));
} catch (_) {}
}
// 模型热更新:启动拉取目录(只拉不下载),兜底等待;下载/已激活模型
// 有新版本时 autoUpdate 自动重下,变化经 _onModelsChanged 重建 worker
try {
await ModelManager.instance
.refresh()
.timeout(const Duration(seconds: 15));
} catch (_) {}
await _reloadWorker();
await _startCamera();
}
/// 用当前激活模型重建推理 worker(激活集变化/启动时调用);
/// worker 为 null(无激活模型或加载失败)时仅预览并提示。
Future<void> _reloadWorker() async {
if (_reloadingWorker) return;
_reloadingWorker = true;
try {
final models = ModelManager.instance.models;
final ids = models.map((m) => m.datasetId).toSet();
if (_viewModel != null &&
_loadedModelIds.length == ids.length &&
_loadedModelIds.containsAll(ids)) {
return; // 激活集未变(进度/目录刷新通知)直接跳过
}
final worker = await DetectorWorker.create(models: models);
_loadedModelIds = ids;
final vm = _viewModel ?? CameraViewModel(reminder: Reminder());
vm.setModelReady(worker != null);
final analyzer = FrameAnalyzer(worker: worker, viewModel: vm);
final old = _analyzer;
if (!mounted) {
analyzer.dispose();
if (vm != _viewModel) vm.dispose();
return;
}
setState(() {
_viewModel = vm;
_analyzer = analyzer;
});
old?.worker?.dispose();
// 相机已启动:重启帧流绑定新 analyzer(start 内部先 stop 再订阅)
if (mounted && _cameraController != null) {
await _cameraController!.start(analyzer);
}
} finally {
_reloadingWorker = false;
/// 并发调用共享同一进行中的重建:refresh 通知触发的重建与 _init 的等待
/// 共用一个 Future,相机等重建完成后再启动(避免绑定旧 analyzer)。
Future<void> _reloadWorker() {
final inFlight = _reloadInFlight;
if (inFlight != null) {
// 重建进行中:完成后按最新状态复查,期间的变化不丢失
return inFlight.then((_) => _reloadWorker()).catchError((_) {});
}
_reloadInFlight =
_doReloadWorker().whenComplete(() => _reloadInFlight = null);
return _reloadInFlight!;
}
Future<void> _doReloadWorker() async {
final mgr = ModelManager.instance;
final models = mgr.models;
final ids = models.map((m) => m.datasetId).toSet();
final revChanged = mgr.revision != _lastRevision;
if (_viewModel != null &&
!revChanged &&
_loadedModelIds.length == ids.length &&
_loadedModelIds.containsAll(ids)) {
return; // 激活集/模型文件未变(进度/目录刷新通知)直接跳过
}
_lastRevision = mgr.revision;
final worker = await DetectorWorker.create(models: models);
_loadedModelIds = ids;
final vm = _viewModel ?? CameraViewModel(reminder: Reminder());
vm.setModelReady(worker != null);
final analyzer = FrameAnalyzer(worker: worker, viewModel: vm);
final old = _analyzer;
if (!mounted) {
analyzer.dispose();
if (vm != _viewModel) vm.dispose();
return;
}
setState(() {
_viewModel = vm;
_analyzer = analyzer;
});
old?.worker?.dispose();
// 相机已启动:重启帧流绑定新 analyzer(start 内部先 stop 再订阅)
if (mounted && _cameraController != null) {
await _cameraController!.start(analyzer);
}
}
void _onModelsChanged() {
final ids = ModelManager.instance.models.map((m) => m.datasetId).toSet();
if (_loadedModelIds.length == ids.length &&
final mgr = ModelManager.instance;
final ids = mgr.models.map((m) => m.datasetId).toSet();
final revChanged = mgr.revision != _lastRevision;
if (!revChanged &&
_loadedModelIds.length == ids.length &&
_loadedModelIds.containsAll(ids)) {
return;
}