camera2 api将yuv420转换为rgb green

时间:2015-11-05 10:53:10

标签: android image android-camera yuv

我尝试将图像从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: enter image description here

N21: enter image description here

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);

但我保存的图片是绿色和粉红色的: capture image capture image

我错过了什么?

4 个答案:

答案 0 :(得分:3)

转化尝试有两个主要问题:

  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及更高版本中修复,因此值得检查您正在使用的版本修理代码。

答案 1 :(得分:3)

我在RenderScript中实现了YUV_420逻辑(完全如上图所示),请参阅此处的完整代码:

Conversion YUV_420 _888 to Bitmap, complete code

它为API 22提供了完美的bimaps,但对于API 21,它显示了“绿色田园诗”。从此我可以确认,你找到的结果。正如Silvaren上面已经提到的,其原因似乎是API 21中的一个Android错误。看看我的rs代码很清楚,如果U和V信息丢失(即为零),G(reen)ARGB组件会变得很大转换。

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

我得到了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();
}