Flutter中CameraImage转Image失败求助(附自定义转换代码)
CameraImage转Image时字节数组填充不匹配问题解决
我尝试用camera和image库把CameraImage转换成Image,试过多种代码片段(包括相关Stack Overflow方案)都没成功。自己编写的转换函数无法正常运行,问题出在字节数组大小因填充不匹配。以下是我写的转换函数:
imglib.Image convertCameraImage(CameraImage image) { int width = image.width; int height = image.height; Uint8List rgbData = Uint8List(width * height * 3); final int uvyButtonStride = image.planes[1].bytesPerRow; final int uvPixelStride = image.planes[1].bytesPerPixel!; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int uvIndex = uvPixelStride * (x / 2).floor() + uvyButtonStride * (y / 2).floor(); final int index = y * width + x; final yp = image.planes[0].bytes[index]; final up = image.planes[1].bytes[uvIndex]; final vp = image.planes[2].bytes[uvIndex]; // 计算像素颜色 int r = (yp + 1.4075 * (vp - 128)).toInt().clamp(0, 255); int g = (yp - 0.3455 * (up - 128) - (0.7169 * (vp - 128))) .toInt() .clamp(0, 255); int b = (yp + 1.7790 * (up - 128)).toInt().clamp(0, 255); rgbData[index * 3] = r; rgbData[index * 3 + 1] = g; rgbData[index * 3 + 2] = b; } } var img = imglib.Image.fromBytes( width: width, height: height, bytes: rgbData.buffer); return img; }
问题根源与修正方案
代码核心问题是忽略了CameraImage平面的行填充(bytesPerRow),直接用图像宽高计算索引,导致字节数组越界或数据错位。以下是修正后的完整代码:
import 'package:image/image.dart' as imglib; import 'package:camera/camera.dart'; imglib.Image convertCameraImage(CameraImage image) { final format = image.format; // 适配Android主流YUV420格式 if (format.group == ImageFormatGroup.yuv420) { return _convertYUV420(image); } // 适配iOS主流BGRA8888格式 else if (format.group == ImageFormatGroup.bgra8888) { return _convertBGRA8888(image); } throw UnsupportedError('不支持当前图像格式'); } imglib.Image _convertYUV420(CameraImage image) { final yPlane = image.planes[0]; final uPlane = image.planes[1]; final vPlane = image.planes[2]; final int width = image.width; final int height = image.height; final int yRowStride = yPlane.bytesPerRow; final int uvRowStride = uPlane.bytesPerRow; final int uvPixelStride = uPlane.bytesPerPixel!; final Uint8List rgbData = Uint8List(width * height * 3); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // 计算Y平面正确索引:加入行填充值 final int yIndex = y * yRowStride + x; final int yp = yPlane.bytes[yIndex]; // 计算UV平面索引:每个UV像素对应2x2的Y像素块 final int uvY = y ~/ 2; final int uvX = x ~/ 2; final int uvIndex = uvY * uvRowStride + uvX * uvPixelStride; final int up = uPlane.bytes[uvIndex]; final int vp = vPlane.bytes[uvIndex]; // 标准YUV转RGB系数,适配多数设备 int r = (yp + 1.370705 * (vp - 128)).toInt().clamp(0, 255); int g = (yp - 0.698001 * (vp - 128) - 0.337633 * (up - 128)).toInt().clamp(0, 255); int b = (yp + 1.732446 * (up - 128)).toInt().clamp(0, 255); final int rgbIndex = (y * width + x) * 3; rgbData[rgbIndex] = r; rgbData[rgbIndex + 1] = g; rgbData[rgbIndex + 2] = b; } } return imglib.Image.fromBytes( width: width, height: height, bytes: rgbData.buffer, format: imglib.Format.rgb, ); } imglib.Image _convertBGRA8888(CameraImage image) { final plane = image.planes[0]; return imglib.Image.fromBytes( width: image.width, height: image.height, bytes: plane.bytes.buffer, format: imglib.Format.bgra, ); }
关键修正点
- 分格式处理:区分Android的YUV420和iOS的BGRA8888格式,覆盖主流设备场景
- 行填充适配:Y平面索引计算加入
bytesPerRow,解决填充导致的索引错误 - UV索引修正:严格按2x2像素块映射UV数据,同时适配UV平面的行填充
- 转换系数优化:使用标准YUV转RGB系数,提升颜色准确性
- 指定图像格式:显式设置Image的格式参数,避免自动识别出错
内容的提问来源于stack exchange,提问作者nono
相关产品推荐
相关产品推荐

