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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:22:16