E / flutter(8115):[错误:flutter/runtime/dart_vm_initializer.cc(41)]未处理的异常:PlatformException(100,检测错误,java.lang.IllegalArgumentException:无法复制到TensorFlowLite张量(serving_default_images:0)来自 23040 字节的 Java 缓冲区的 4915200 字节。
我正在尝试使用 fluttervision 包将我的 yolov8 模型集成到 flutter 中。当我在 netron 应用程序中看到我的模型时,它显示我的输入图像应该是张量:float32[1,3,640,640] 所以我添加了一个函数将unit8转换为float32,但当我从图库中选择图像并尝试执行检测时,仍然会出现此错误。我在这里附上我的代码的代码片段,请帮助!!!
`code for object detection using camera`
class YoloVideo extends StatefulWidget {
final FlutterVision vision;
const YoloVideo({Key? key, required this.vision}) : super(key: key);
@override
State<YoloVideo> createState() => _YoloVideoState();
}
class _YoloVideoState extends State<YoloVideo> {
late CameraController controller;
late List<Map<String, dynamic>> yoloResults;
CameraImage? cameraImage;
bool isLoaded = false;
bool isDetecting = false;
@override
void initState() {
super.initState();
init();
}
init() async {
cameras = await availableCameras();
controller = CameraController(cameras[0], ResolutionPreset.medium);
controller.initialize().then((value) {
loadYoloModel().then((value) {
setState(() {
isLoaded = true;
isDetecting = false;
yoloResults = [];
});
});
});
}
@override
void dispose() async {
super.dispose();
controller.dispose();
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
if (!isLoaded) {
return const Scaffold(
body: Center(
child: Text("Model not loaded, waiting for it"),
),
);
}
}
// Example image preprocessing function
Future<Uint8List> preprocessImage(File imageFile) async {
final imageBytes = await imageFile.readAsBytes();
final decodedImage = img.decodeImage(imageBytes);
// Resize image to 640x640
final resizedImage = img.copyResize(decodedImage!, width: 640, height: 640);
// Convert to RGB format
final pngBytes = img.encodePng(resizedImage!);
// Normalize pixel values to [0, 1]
final normalizedBytes = Float32List.fromList(pngBytes!.map((byte) => byte / 255.0).toList());
// Expand dimensions to match [1, 3, 640, 640]
final inputTensor = normalizedBytes.expand((value) => [value, value, value]).toList();
final reshapedTensor = Float32List.fromList([1.0] + inputTensor);
return Uint8List.fromList(reshapedTensor.map((value) => (value * 255).round()).toList());
}
Future<void> loadYoloModel() async {
await widget.vision.loadYoloModel(
labels: 'assets/task1.txt',
modelPath: 'assets/task1.tflite',
modelVersion: "yolov8",
numThreads: 2,
useGpu: true);
setState(() {
isLoaded = true;
});
}
Future<void> yoloOnFrame(CameraImage cameraImage) async {
final result = await widget.vision.yoloOnFrame(
bytesList: cameraImage.planes.map((plane) => plane.bytes).toList(),
imageHeight: cameraImage.height,
imageWidth: cameraImage.width,
iouThreshold: 0.4,
confThreshold: 0.4,
classThreshold: 0.5);
if (result.isNotEmpty) {
setState(() {
yoloResults = result;
});
}
}
Future<void> startDetection() async {
setState(() {
isDetecting = true;
});
if (controller.value.isStreamingImages) {
return;
}
await controller.startImageStream((image) async {
if (isDetecting) {
cameraImage = image;
yoloOnFrame(image);
}
});
}
Future<void> stopDetection() async {
setState(() {
isDetecting = false;
yoloResults.clear();
});
}
List<Widget> displayBoxesAroundRecognizedObjects(Size screen) {
if (yoloResults.isEmpty) return [];
double factorX = screen.width / (cameraImage?.height ?? 1);
double factorY = screen.height / (cameraImage?.width ?? 1);
Color colorPick = const Color.fromARGB(255, 50, 233, 30);
return yoloResults.map((result) {
return Positioned(
left: result["box"][0] * factorX,
top: result["box"][1] * factorY,
width: (result["box"][2] - result["box"][0]) * factorX,
height: (result["box"][3] - result["box"][1]) * factorY,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
border: Border.all(color: Colors.pink, width: 2.0),
),
child: Text(
"${result['tag']} ${(result['box'][4] * 100).toStringAsFixed(0)}%",
style: TextStyle(
background: Paint()..color = colorPick,
color: Colors.white,
fontSize: 18.0,
),
),
),
);
}).toList();
}
}
`code for image deection from gallary`
class YoloImageV8 extends StatefulWidget {
final FlutterVision vision;
const YoloImageV8({Key? key, required this.vision}) : super(key: key);
@override
State<YoloImageV8> createState() => _YoloImageV8State();
}
class _YoloImageV8State extends State<YoloImageV8> {
Future<Uint8List> preprocessImage(File imageFile) async {
final imageBytes = await imageFile.readAsBytes();
final decodedImage = img.decodeImage(imageBytes);
// Resize image to 640x640
final resizedImage = img.copyResize(decodedImage!, width: 640, height: 640);
// Convert to RGB format
final pngBytes = img.encodePng(resizedImage!);
// Normalize pixel values to [0, 1]
final normalizedBytes = Float32List.fromList(pngBytes!.map((byte) => byte / 255.0).toList());
// Expand dimensions to match [1, 3, 640, 640]
final inputTensor = normalizedBytes.expand((value) => [value, value, value]).toList();
final reshapedTensor = Float32List.fromList([1.0] + inputTensor);
return Uint8List.fromList(reshapedTensor.map((value) => (value * 255).round()).toList());
}
late List<Map<String, dynamic>> yoloResults;
File? imageFile;
int imageHeight = 1;
int imageWidth = 1;
bool isLoaded = false;
@override
void initState() {
super.initState();
loadYoloModel().then((value) {
setState(() {
yoloResults = [];
isLoaded = true;
});
});
}
@override
void dispose() async {
super.dispose();
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
if (!isLoaded) {
return const Scaffold(
body: Center(
child: Text("Model not loaded, waiting for it"),
),
);
}
return Stack(
fit: StackFit.expand,
children: [
imageFile != null ? Image.file(imageFile!) : const SizedBox(),
Align(
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: pickImage,
child: const Text("Pick an image"),
),
ElevatedButton(
onPressed: yoloOnImage,
child: const Text("Detect"),
)
],
),
),
...displayBoxesAroundRecognizedObjects(size),
],
);
}
Future<void> loadYoloModel() async {
await widget.vision.loadYoloModel(
labels: 'assets/task1.txt',
modelPath: 'assets/task1.tflite',
modelVersion: "yolov8",
quantization: false,
numThreads: 2,
useGpu: true);
setState(() {
isLoaded = true;
});
}
Future<void> pickImage() async {
final ImagePicker picker = ImagePicker();
// Capture a photo
final XFile? photo = await picker.pickImage(source: ImageSource.gallery);
if (photo != null) {
setState(() {
imageFile = File(photo.path);
});
}
}
yoloOnImage() async {
yoloResults.clear();
Uint8List byte = await imageFile!.readAsBytes();
final image = await decodeImageFromList(byte);
imageHeight = image.height;
imageWidth = image.width;
final result = await widget.vision.yoloOnImage(
bytesList: byte,
imageHeight: image.height,
imageWidth: image.width,
iouThreshold: 0.8,
confThreshold: 0.4,
classThreshold: 0.5);
if (result.isNotEmpty) {
setState(() {
yoloResults = result;
});
}
}
List<Widget> displayBoxesAroundRecognizedObjects(Size screen) {
if (yoloResults.isEmpty) return [];
double factorX = screen.width / (imageWidth);
double imgRatio = imageWidth / imageHeight;
double newWidth = imageWidth * factorX;
double newHeight = newWidth / imgRatio;
double factorY = newHeight / (imageHeight);
double pady = (screen.height - newHeight) / 2;
Color colorPick = const Color.fromARGB(255, 50, 233, 30);
return yoloResults.map((result) {
return Positioned(
left: result["box"][0] * factorX,
top: result["box"][1] * factorY + pady,
width: (result["box"][2] - result["box"][0]) * factorX,
height: (result["box"][3] - result["box"][1]) * factorY,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
border: Border.all(color: Colors.pink, width: 2.0),
),
child: Text(
"${result['tag']} ${(result['box'][4] * 100).toStringAsFixed(0)}%",
style: TextStyle(
background: Paint()..color = colorPick,
color: Colors.white,
fontSize: 18.0,
),
),
),
);
}).toList();
}
}
我不是专家,但从我(有限的)经验来看,干预cameraImage值从来没有帮助过我。您是否进行了所有这些转换以获得 yolo 检测?
我花了很多时间进行转换,但最终对我有用的是使我的解决方案适应图像格式。
如果您使用的是 Android,最好使用默认的 YUV_420_888 格式(至少是我的 Pixel 7 Pro 上的默认格式)效果很好。当我将其转换为 NV21 格式时(我需要这样做才能使用 google 的 ml_kit),我必须相应地调整我的输入字节列表。
我改变了:
bytesList: cameraImage.planes.map((plane) => plane.bytes).toList()
至:
bytesList: [image.planes.first.bytes, Uint8List(0), Uint8List(0)]
对该库(一般是颤振库)的在线支持并不是最好的,所以希望这会有所帮助。另外,请确保您正在等待回复