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

Flutter中YUV420转RGB适配问题:诺基亚X10转换异常排查

问题根源分析

你的代码核心问题是忽略了YUV420_888格式中平面数据的行对齐填充:

  • YUV420_888的每个平面(Y、U、V)的bytesPerRow不一定等于图像实际宽度,很多设备(比如诺基亚X10)会为内存对齐要求,让bytesPerRow取4的倍数或更大的对齐值。你之前直接用y * width + x计算Y平面索引,会误取每行末尾的填充无效数据,最终导致颜色错乱。
  • 从日志能看出,诺基亚X10的Y平面bytesPerRow=512,但图像实际宽度远小于这个值(三星设备对应宽度为320),每行存在大量填充字节,你的代码把这些无效数据当成了有效Y分量,才出现异常图像。
修正后的转换代码
static img.Image convertYUV420toImageColor(CameraImage image) {
  try {
    final int width = image.width;
    final int height = image.height;
    
    // 提取三个平面的关键参数
    final yPlane = image.planes[0];
    final uPlane = image.planes[1];
    final vPlane = image.planes[2];
    
    final int yRowStride = yPlane.bytesPerRow;
    final int yPixelStride = yPlane.bytesPerPixel!;
    final int uvRowStride = uPlane.bytesPerRow;
    final int uvPixelStride = uPlane.bytesPerPixel!;

    // 创建图像缓冲区
    var img2 = img.Image(width: width, height: height);

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        // 计算Y分量的正确索引:行步长*行数 + 像素步长*列数
        final int yIndex = y * yRowStride + x * yPixelStride;
        // 计算UV分量索引(UV为半分辨率,坐标取整)
        final int uvY = y ~/ 2;
        final int uvX = x ~/ 2;
        final int uvIndex = uvY * uvRowStride + uvX * uvPixelStride;

        final yp = yPlane.bytes[yIndex];
        final up = uPlane.bytes[uvIndex];
        final vp = vPlane.bytes[uvIndex];

        // YUV转RGB计算逻辑保持不变
        int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255);
        int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91)
            .round()
            .clamp(0, 255);
        int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255);

        img2.setPixelRgba(x, y, r, g, b, 255);
      }
    }

    return img.copyRotate(img2, angle: 90);
  } catch (e) {
    Log.logger.d("ERROR: " + e.toString());
  }
  throw Exception("error during YUV420 conversion");
}
关键改动说明
  • Y平面索引修正:将原有的y * width + x替换为y * yRowStride + x * yPixelStride,严格遵循每个平面的行步长和像素步长计算索引,彻底避开填充的无效数据。
  • UV索引优化:用Dart整数除法~/替代浮点除法后取整,运算效率更高且索引计算更准确。
  • 参数解耦:单独提取每个平面的参数,代码结构更清晰,也便于后续适配不同的YUV子格式。
额外注意事项
  • YUV420_888的U、V平面存在多种排列方式(交错或独立平面),你的代码中UV像素步长为2,对应NV21/NV12交错格式,当前索引计算完全适配该场景。
  • 不同设备的相机预览分辨率、内存对齐规则差异很大,永远不要假设bytesPerRow等于图像宽度,必须依赖每个平面提供的bytesPerRow和bytesPerPixel来计算索引。

内容的提问来源于stack exchange,提问作者finisinfinitatis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 06:23:11