Flutter中使用TensorFlow Hub food_V1模型闪退问题求助
Flutter中使用TensorFlow Hub food_V1模型闪退问题排查与解决
问题描述
- 在Flutter项目中尝试使用TensorFlow Hub的food_V1模型,下载后仅得到tflite文件,自行提取标签并配置到
pubspec.yaml的资源中后,程序运行直接闪退 - 替换为Teachable Machine生成的tflite模型时,代码可正常执行
- 相关代码如下:
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:tflite/tflite.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Tensor Flow Example'), backgroundColor: Color.fromRGBO(61, 62, 61, 1)), body: Center(child: HomePage()))); } } class HomePage extends StatefulWidget { @override HomePageState createState() => HomePageState(); } class HomePageState extends State { File? imageURI; var result; String? path; Future getImageFromCamera() async { try { var image = await ImagePicker().pickImage(source: ImageSource.camera); File img = File(image!.path); setState(() { imageURI = img; path = image.path; }); } catch (e) { print("Error getting image from camera"); } classifyImage(); } Future getImageFromGallery() async { try { var image = await ImagePicker().pickImage(source: ImageSource.gallery); File img = File(image!.path); setState(() { imageURI = img; path = image.path; }); } catch (e) { print("Error getting image from gallery"); } classifyImage(); } Future classifyImage() async { await Tflite.loadModel( model: "assets/model_unquant.tflite", labels: "assets/labels.txt"); var output = await Tflite.runModelOnImage( path: imageURI!.path, imageMean: 127.5, imageStd: 127.5, numResults: 2, threshold: 0.2, ); setState(() { result = output; print(output![0]["confidence"].toStringAsFixed(3)); }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromRGBO(48, 48, 48, 1), body: Center( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ imageURI == null ? Text( 'please select image from gallery or camera.', style: TextStyle(color: Colors.white), ) : ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.file(imageURI!, width: 300, height: 200, fit: BoxFit.cover), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsets.fromLTRB(0, 30, 0, 20), child: IconButton( onPressed: () => getImageFromCamera(), icon: Icon( Icons.camera, color: Colors.white, ), iconSize: 50, color: Colors.blue, padding: EdgeInsets.fromLTRB(12, 12, 12, 12), )), Container( margin: EdgeInsets.fromLTRB(0, 0, 0, 0), child: IconButton( icon: Icon(Icons.image, color: Colors.white), iconSize: 50, onPressed: () => getImageFromGallery(), color: Colors.blue, padding: EdgeInsets.fromLTRB(12, 12, 12, 12), )), ], ), SizedBox( height: 10, ), result == null ? Text( 'Result', style: TextStyle(color: Colors.white), ) : Container( child: Text( "${result[0]['label'].substring(2)}", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 30), ), ) ]), ))); } }
可能原因
- 重复加载模型:每次调用
classifyImage()都执行Tflite.loadModel(),导致模型资源重复加载引发冲突 - 图像预处理参数不匹配:当前设置的
imageMean=127.5、imageStd=127.5与food_V1模型要求的预处理参数不符 - 标签文件格式问题:自行提取的labels.txt格式不符合tflite插件的解析要求(比如每行格式错误、存在多余字符)
- 输入尺寸不匹配:food_V1模型要求输入图像尺寸为224x224,代码中未指定导致尺寸不兼容
解决步骤
- 仅加载一次模型:将模型加载逻辑移到
initState()中,避免重复加载 - 调整预处理参数:将
imageMean和imageStd改为food_V1模型要求的128.0 - 指定输入图像尺寸:在
runModelOnImage()中添加inputSize: 224参数 - 验证标签文件格式:确保labels.txt每行仅包含一个标签名称,无编号或额外符号
修改后的代码
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:tflite/tflite.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Tensor Flow Example'), backgroundColor: Color.fromRGBO(61, 62, 61, 1)), body: Center(child: HomePage()))); } } class HomePage extends StatefulWidget { @override HomePageState createState() => HomePageState(); } class HomePageState extends State<HomePage> { File? imageURI; var result; String? path; @override void initState() { super.initState(); // 仅在初始化时加载一次模型 loadModel(); } Future loadModel() async { await Tflite.loadModel( model: "assets/model_unquant.tflite", labels: "assets/labels.txt", ); } Future getImageFromCamera() async { try { var image = await ImagePicker().pickImage(source: ImageSource.camera); if (image == null) return; File img = File(image.path); setState(() { imageURI = img; path = image.path; }); classifyImage(); } catch (e) { print("Error getting image from camera: $e"); } } Future getImageFromGallery() async { try { var image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image == null) return; File img = File(image.path); setState(() { imageURI = img; path = image.path; }); classifyImage(); } catch (e) { print("Error getting image from gallery: $e"); } } Future classifyImage() async { if (imageURI == null) return; var output = await Tflite.runModelOnImage( path: imageURI!.path, imageMean: 128.0, // 匹配food_V1模型的预处理参数 imageStd: 128.0, numResults: 2, threshold: 0.2, inputSize: 224, // 指定模型要求的输入尺寸 ); setState(() { result = output; if (output != null) { print(output[0]["confidence"].toStringAsFixed(3)); } }); } @override void dispose() { // 页面销毁时释放模型资源 Tflite.close(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromRGBO(48, 48, 48, 1), body: Center( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ imageURI == null ? const Text( 'please select image from gallery or camera.', style: TextStyle(color: Colors.white), ) : ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.file(imageURI!, width: 300, height: 200, fit: BoxFit.cover), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: const EdgeInsets.fromLTRB(0, 30, 0, 20), child: IconButton( onPressed: getImageFromCamera, icon: const Icon( Icons.camera, color: Colors.white, ), iconSize: 50, color: Colors.blue, padding: const EdgeInsets.fromLTRB(12, 12, 12, 12), )), Container( margin: const EdgeInsets.fromLTRB(0, 0, 0, 0), child: IconButton( icon: const Icon(Icons.image, color: Colors.white), iconSize: 50, onPressed: getImageFromGallery, color: Colors.blue, padding: const EdgeInsets.fromLTRB(12, 12, 12, 12), )), ], ), const SizedBox( height: 10, ), result == null ? const Text( 'Result', style: TextStyle(color: Colors.white), ) : Container( child: Text( "${result[0]['label'].substring(2)}", style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 30), ), ) ]), ))); } }
额外注意事项
- 确认
pubspec.yaml中已正确配置资源路径:
assets: - assets/model_unquant.tflite - assets/labels.txt
- 测试前先清理项目缓存,重新构建应用
内容的提问来源于stack exchange,提问作者martin Hm
相关产品推荐
相关产品推荐

