You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter目标检测应用报错:Interpreter busy问题求助

Flutter目标检测应用运行时错误及文本显示问题解决

问题描述

运行Flutter目标检测应用时触发以下运行时错误:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Failed to run model, Interpreter busy, java.lang.RuntimeException: Interpreter busy)

同时检测出的目标文本无法正常显示,原代码如下:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter_tflite/flutter_tflite.dart';
//import 'package:tflite/tflite.dart';

String result = "";

class CameraScreen extends StatefulWidget {
  final CameraDescription camera;

  CameraScreen(this.camera);

  @override
  _CameraScreenState createState() => _CameraScreenState();
}

class _CameraScreenState extends State<CameraScreen> {
  loadModel() async {
    await Tflite.loadModel(
        model: "assets/mobilenet_v1_1.0_224.tflite",
        labels: "assets/mobilenet_v1_1.0_224.txt");
  }

  late CameraController _controller;
  late CameraImage imgCam;
  late Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    _controller = CameraController(widget.camera, ResolutionPreset.medium);
    _initializeControllerFuture = _controller.initialize().then((value) => {
          setState((() {
            _controller.startImageStream((imageFromStream) => {
                  imgCam = imageFromStream,
                  runModel();
                });
          }))
        });
    loadModel();
  }

  runModel() async {
    if (imgCam != null) {
      var recognition = await Tflite.runModelOnFrame(
        bytesList: imgCam.planes.map((plane) {
          return plane.bytes;
        }).toList(),
        imageHeight: imgCam.height,
        imageWidth: imgCam.width,
        imageMean: 127.5,
        imageStd: 127.5,
        rotation: 90,
        numResults: 1,
        threshold: 0.1,
        asynch: true,
      );
      result = "yes";
      recognition?.forEach((response) {
        result += response["label"] +
            " " +
            (response["confidence"] as double).toStringAsFixed(2) +
            "\n\n";
        print(result);
        print("\n\n\n\n\n\n\n\n\n");
      });
      setState(() {
        result;
      });
    }
  }

  @override
  void dispose() async {
    _controller.dispose();
    await Tflite.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final deviceRatio = size.width / size.height;
    return Scaffold(
        body: FutureBuilder<void>(
            future: _initializeControllerFuture,
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                // If the Future is complete, display the preview
                return AspectRatio(
                  aspectRatio: deviceRatio,
                  child: CameraPreview(
                      child: Center(
                        child: Text(result),
                      ),
                      _controller),
                );
              } else {
                // Otherwise, display a loading indicator
                return Center(child: CircularProgressIndicator());
              }
            }));
  }
}

问题分析及修复方案

1. Interpreter busy 异常原因及修复

相机流每秒推送大量帧,原代码每收到一帧就调用runModel(),导致TFLite解释器被并发请求占满,抛出Interpreter busy异常。

修复方式:添加任务锁标志位,确保同一时间只有一个模型推理任务在执行。

2. 检测文本无法显示原因及修复

  • setState(() { result; });写法错误,未触发状态更新;
  • 全局变量result在多线程环境下易出现状态混乱;
  • 文本直接叠加在相机预览上,无背景遮挡导致可读性差甚至被覆盖。

修复方式:

  • 将result改为State类的成员变量;
  • 修正setState写法,确保UI更新;
  • 使用Stack布局让文本显示在预览上方,添加半透明背景提升可读性。

修改后的完整代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter_tflite/flutter_tflite.dart';

class CameraScreen extends StatefulWidget {
  final CameraDescription camera;

  CameraScreen(this.camera);

  @override
  _CameraScreenState createState() => _CameraScreenState();
}

class _CameraScreenState extends State<CameraScreen> {
  String _result = "未检测到目标";
  bool _isProcessing = false; // 任务锁标志位
  late CameraController _controller;
  late Future<void> _initializeControllerFuture;

  loadModel() async {
    await Tflite.loadModel(
      model: "assets/mobilenet_v1_1.0_224.tflite",
      labels: "assets/mobilenet_v1_1.0_224.txt",
    );
  }

  @override
  void initState() {
    super.initState();
    _controller = CameraController(widget.camera, ResolutionPreset.medium);
    _initializeControllerFuture = _controller.initialize().then((_) {
      _controller.startImageStream((imageFromStream) {
        if (!_isProcessing) runModel(imageFromStream);
      });
    });
    loadModel();
  }

  runModel(CameraImage imgCam) async {
    _isProcessing = true;
    try {
      var recognition = await Tflite.runModelOnFrame(
        bytesList: imgCam.planes.map((plane) => plane.bytes).toList(),
        imageHeight: imgCam.height,
        imageWidth: imgCam.width,
        imageMean: 127.5,
        imageStd: 127.5,
        rotation: 90,
        numResults: 1,
        threshold: 0.1,
        asynch: true,
      );

      String newResult = "";
      recognition?.forEach((response) {
        newResult += "${response["label"]} ${(response["confidence"] as double).toStringAsFixed(2)}\n";
      });

      setState(() {
        _result = newResult.isNotEmpty ? newResult : "未检测到目标";
      });
    } catch (e) {
      print("模型推理出错: $e");
    } finally {
      _isProcessing = false; // 无论成功失败都释放锁
    }
  }

  @override
  void dispose() async {
    _controller.dispose();
    await Tflite.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final deviceRatio = size.width / size.height;
    return Scaffold(
      body: FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return AspectRatio(
              aspectRatio: deviceRatio,
              child: Stack(
                children: [
                  CameraPreview(_controller),
                  Center(
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                      color: Colors.black54,
                      child: Text(
                        _result,
                        style: TextStyle(color: Colors.white, fontSize: 18),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  ),
                ],
              ),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

关键修改点说明

  • 新增_isProcessing标志位,避免并发调用模型导致解释器繁忙;
  • 将全局result改为State成员变量_result,避免状态混乱;
  • 修正setState写法,确保正确触发UI更新;
  • 使用Stack布局让文本显示在相机预览上方,添加半透明背景提升可读性;
  • 在runModel中添加try-catch捕获异常,确保标志位能被重置;
  • 移除冗余的imgCam成员变量,直接使用流传递的帧数据。

内容的提问来源于stack exchange,提问作者20C079 Shakeel Akthar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:45:59