Flutter通过tflite_flutter集成MoveNet Multipose遇precondition报错
Flutter tflite_flutter 接入MoveNet Multipose报错修复
问题现象
在Flutter 3.0项目中使用tflite_flutter包实现MoveNet Multipose多人姿态检测功能,已成功运行官方长期未更新的目标检测示例,接入姿态模型时抛出如下运行错误:
Bad state: failed precondition E/flutter (27000): #0 checkState (package:quiver/check.dart:74:5) E/flutter (27000): #1 Tensor.setTo (package:tflite_flutter/src/tensor.dart:146:5) E/flutter (27000): #2 Interpreter.runForMultipleInputs (package:tflite_flutter/src/interpreter.dart:186:33) E/flutter (27000): #3 Interpreter.run (package:tflite_flutter/src/interpreter.dart:157:5)
当前已编写实现代码如下:
import 'dart:math'; import 'dart:ui'; import 'package:image/image.dart'; import 'package:tflite_flutter/tflite_flutter.dart'; import 'package:tflite_flutter_helper/tflite_flutter_helper.dart'; import 'package:untitled/features/pose_detection/utils/recognition.dart'; class Classifier { static const String modelFileName = 'pose.tflite'; static const String labelFileName = 'labelmap.txt'; Interpreter? interpreter; List<String> labels = []; List<List<int>> outputShapes = []; ImageProcessor? imageProcessor; TensorBuffer? outputBuffer; TfLiteType? inputType; /// Input size of image (height = width = 300) static const int inputSize = 300; /// Number of results to show static const int numResults = 2; /// Result score threshold static const double threshold = 0.3; /// Types of output tensors List<TfLiteType> outputTypes = []; Classifier({List<String>? labels, Interpreter? interpreter}) { loadModel(interpreter: interpreter); } void loadModel({Interpreter? interpreter}) async { try { final localInterpreter = interpreter ?? await Interpreter.fromAsset( modelFileName, options: InterpreterOptions()..threads = 4, ); final outputTensors = localInterpreter.getOutputTensors(); this.interpreter = localInterpreter; outputBuffer = TensorBuffer.createFixedSize( outputTensors[0].shape, outputTensors[0].type, ); inputType = localInterpreter.getInputTensor(0).type; } catch (e) { print('Error creating interpreter: $e'); } } List<Recognition> predict(Image image) { if (interpreter == null || outputBuffer == null || inputType == null) { throw Exception('Interpreter is not loaded'); } var inputImage = TensorImage(inputType!); inputImage.loadImage(image); inputImage = getProcessedImage(inputImage); // MoveNet Multipose model spec interpreter!.run(inputImage.buffer, outputBuffer!.getBuffer()); return []; } TensorImage getProcessedImage(TensorImage inputImage) { const multiplier = 32; const defaultSize = 256; final isWidthGreater = inputImage.width > inputImage.height; final ratio = isWidthGreater ? inputImage.height / inputImage.width : inputImage.width / inputImage.height; final height = isWidthGreater ? (ratio * defaultSize) : defaultSize; final width = isWidthGreater ? defaultSize : (ratio * defaultSize); final widthMultiplier = (height / multiplier).ceil(); final heightMultiplier = (width / multiplier).ceil(); final finalHeight = (heightMultiplier * multiplier); final finalWidth = (widthMultiplier * multiplier); return ImageProcessorBuilder() .add(ResizeOp(finalHeight, finalWidth, ResizeMethod.BILINEAR)) .add(ResizeWithCropOrPadOp(finalHeight, finalWidth)) .build() .process(inputImage); } }
对应pubspec.yaml依赖配置如下:
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.17.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 get: ^4.6.5 rxdart: ^0.27.4 # provider: ^6.0.3 flutter_riverpod: ^1.0.4 camera: ^0.8.1+3 image: ^3.2.0 tflite_flutter: ^0.9.0 tflite_flutter_helper: git: url: https://github.com/filofan1/tflite_flutter_helper.git ref: 783f15e5a87126159147d8ea30b98eea9207ac70
错误根因
报错failed precondition是tflite运行时前置校验失败导致,核心原因有3个:
- 输入预处理逻辑不符合模型要求:MoveNet Multipose Lightning固定输入尺寸为256*256,现有代码动态计算的输入尺寸不固定,且预处理流程缺少像素归一化步骤,输入张量shape、数值范围和模型预期不匹配。
- 输出缓冲区初始化逻辑有缺陷:现有代码仅动态读取输出张量shape创建缓冲区,没有做类型和shape校验,容易出现缓冲区大小不匹配的问题。
- 异步初始化时序问题:
loadModel是异步方法,直接在构造函数中调用无法保证模型、缓冲区初始化完成后再调用推理逻辑,即使有判空逻辑也可能出现初始化不完整的问题。
修复方案
按以下步骤修改代码即可正常运行:
- 替换原有的异步构造逻辑,使用工厂构造方法确保模型初始化完成后再返回实例,修改后的类初始化部分代码如下:
class Classifier { static const String modelFileName = 'pose.tflite'; Interpreter? interpreter; TensorBuffer? outputBuffer; TfLiteType? inputType; static const int inputSize = 256; static const double threshold = 0.3; // 私有构造 Classifier._(); // 异步工厂构造,确保初始化完成 static Future<Classifier> create({Interpreter? interpreter}) async { final classifier = Classifier._(); await classifier.loadModel(interpreter: interpreter); return classifier; } Future<void> loadModel({Interpreter? interpreter}) async { try { final localInterpreter = interpreter ?? await Interpreter.fromAsset( modelFileName, options: InterpreterOptions()..threads = 4, ); this.interpreter = localInterpreter; // MoveNet Multipose输出固定shape为[1, 6, 56],对应最多6个人,每人56个关键点+框+置信度数据 outputBuffer = TensorBuffer.createFixedSize( [1, 6, 56], TfLiteType.float32, ); inputType = localInterpreter.getInputTensor(0).type; } catch (e) { print('Error creating interpreter: $e'); rethrow; } } }
- 修正图像预处理逻辑,固定resize到256*256尺寸,增加归一化操作将像素值从0-255映射到0-1区间,替换原有
getProcessedImage方法:
TensorImage getProcessedImage(TensorImage inputImage) { return ImageProcessorBuilder() // 固定resize到模型要求的256*256 .add(ResizeOp(inputSize, inputSize, ResizeMethod.BILINEAR)) // 像素值归一化到0-1 .add(NormalizeOp(0, 255)) .build() .process(inputImage); }
- 推理完成后增加输出解析逻辑,从输出缓冲区中提取每个人的关键点、检测框、置信度,过滤低置信度结果后返回。
注意:推理得到的关键点坐标是归一化到0-1区间的相对值,需要乘以原图对应的宽高才能得到实际绘制坐标。
内容的提问来源于stack exchange,提问作者Hariki
相关产品推荐
相关产品推荐

