【问题标题】:camera2 api convert yuv420 to rgb green outcamera2 api 将 yuv420 转换为 rgb 绿出
【发布时间】:2016-02-06 04:16:59
【问题描述】:

我尝试将图像从 YUV_420_888 转换为 rgb,但我在输出图像时遇到了一些问题。在 ImageReader 中,我以 YUV_420_888 格式获取图像(使用相机 2 api 获取此图像预览)。

imageReader = ImageReader.newInstance(1920,1080,ImageFormat.YUV_420_888,10);

在android sdk 中为YuvImage 类编写,即YuvImage 只使用NV21、YUY2。

我们可以看到 N21 和 yuv420 之间的差异不大,我尝试将数据转换为 N21

YUV420:

N21:

onImageAvailable 中,我分别获取每个平面并将它们放在正确的位置(如图所示)

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

ByteBuffer bufferY = image.getPlanes()[0].getBuffer();
byte[] data0 = new byte[bufferY.remaining()];
bufferY.get(data0);

ByteBuffer bufferU = image.getPlanes()[1].getBuffer();
byte[] data1 = new byte[bufferU.remaining()];
bufferU.get(data1);

ByteBuffer bufferV = image.getPlanes()[2].getBuffer();
byte[] data2 = new byte[bufferV.remaining()];
bufferV.get(data2);
...
outputStream.write(data0);
for (int i=0;i<bufferV.remaining();i++) {
    outputStream.write(data1[i]);
    outputStream.write(data2[i]);
}

创建YuvImage后,转换为Bitmap,查看并保存

final YuvImage yuvImage = new YuvImage(outputStream.toByteArray(), ImageFormat.NV21, 1920,1080, null);
ByteArrayOutputStream outBitmap = new ByteArrayOutputStream();

yuvImage.compressToJpeg(new Rect(0, 0,1920, 1080), 95, outBitmap);

byte[] imageBytes = outBitmap.toByteArray();

final Bitmap imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
mImageView.setImageBitmap(imageBitmap);
...
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 95, out);

但我保存的图像是绿色和粉红色:

我错过了什么?

【问题讨论】:

  • 次要点:NV21 的图表实际上是错误的,它描述了 NV12 编码。 NV21是一样的,只是U和V互换了,即VUVUVUVUV而不是UVUVUVUVUV。

标签: android image android-camera yuv


【解决方案1】:

我已经在 RenderScript 中实现了 YUV_420 逻辑(如上图所示),完整代码见这里:

Conversion YUV_420 _888 to Bitmap, complete code

它为 API 22 生成完美的 bimap,但对于 API 21,它显示了“绿色田园诗”。从这里我可以确认,你找到的结果。正如上面 Silvaren 已经提到的,原因似乎是 API 21 中的一个 Android 错误。查看我的 rs 代码很明显,如果 U 和 V 信息丢失(即零),则 G(reen) ARGB 分量在转换。

我在我的 Galaxy S5(仍然是 API 21)上看到了类似的绿色图片 - 这里甚至是颠倒的 ;-)。我怀疑 API 21 的大多数设备目前还没有将 Camera2 用于他们的设备相机应用程序。有一个名为“手动相机兼容性”的免费应用程序允许对此进行测试。从这里我看到确实 S5/API21 仍然没有使用 Camera2...幸好没有...

【讨论】:

    【解决方案2】:

    您的转换尝试有两个主要问题:

    1. 我们不能假设 U 和 V 平面是隔离的,它们可能包含交错的数据(例如 U-PLANE = {U1, V1, U2, V2, ...} )。事实上,它甚至可能已经是 NV21 风格的交错。这里的关键是查看飞机的row stridepixel stride,并检查我们可以对YUV_420_888 format 做出什么假设。
    2. 您已评论大多数 U 和 V 平面数据为零的事实表明您遇到了Android bug on the generation of images in YUV_420_888。这意味着即使您转换正确,如果您受到该错误的影响,图像仍然会显示为绿色,该错误仅在 Android 5.1.1 及更高版本中修复,因此值得检查您使用的是哪个版本修复代码。

    【讨论】:

    • 感谢与 android bug 的链接,我检查了我的版本,我也有 5.0.1。所以我会尝试更新我的android,看看有什么变化
    【解决方案3】:

    bufferV.get(data2) 增加 ByteBuffer 的位置。这就是循环 for (int i=0;i&lt;bufferV.remaining();i++) 产生 0 次迭代的原因。您可以轻松地将其重写为

    for (int i=0; i<data1.length; i++) {
        outputStream.write(data1[i]);
        outputStream.write(data2[i]);
    }
    

    【讨论】:

    • 我改变了这个......但还是一样。我检查了数组 data1data2 里面的内容,它们几乎为 0。数组长度为 518400,从这个长度 517440 开始为 0 值。可能只能使用前 960?
    • 这可能是您设备上 camera2 API 的错误实现。
    【解决方案4】:

    我得到了 ImageFormat.YUV_420_888 的图像,并成功保存为 jpeg 文件,并且可以在 windows 上正确查看。
    我在这里分享:

    private final Image mImage;
    private final File mFile;
    private final int mImageFormat;
    
    
    ByteArrayOutputStream outputbytes = new ByteArrayOutputStream();
    
    ByteBuffer bufferY = mImage.getPlanes()[0].getBuffer();
    byte[] data0 = new byte[bufferY.remaining()];
    bufferY.get(data0);
    
    ByteBuffer bufferU = mImage.getPlanes()[1].getBuffer();
    byte[] data1 = new byte[bufferU.remaining()];
    bufferU.get(data1);
    
    ByteBuffer bufferV = mImage.getPlanes()[2].getBuffer();
    byte[] data2 = new byte[bufferV.remaining()];
    bufferV.get(data2);
    
    try
    {
        outputbytes.write(data0);
        outputbytes.write(data2);
        outputbytes.write(data1);
    
    
        final YuvImage yuvImage = new YuvImage(outputbytes.toByteArray(), ImageFormat.NV21, mImage.getWidth(),mImage.getHeight(), null);
        ByteArrayOutputStream outBitmap = new ByteArrayOutputStream();
    
        yuvImage.compressToJpeg(new Rect(0, 0,mImage.getWidth(), mImage.getHeight()), 95, outBitmap);
    
    
        FileOutputStream outputfile = null;
        outputfile = new FileOutputStream(mFile);
        outputfile.write(outBitmap.toByteArray());
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    finally
    {
        mImage.close();
    }
    

    【讨论】:

      猜你喜欢
      • 2022-12-22
      • 2015-08-11
      • 2021-04-13
      • 2019-11-12
      • 2020-12-18
      • 2011-09-27
      • 2020-01-03
      • 2012-11-28
      • 2014-05-16
      相关资源
      最近更新 更多