NV21到Bitmap使用ScriptIntrinsicYuvToRGB(支持lib)时损坏的图像

时间:2016-08-18 22:20:00

标签: android bitmap renderscript yuv

使用支持库中的ScriptIntrinsicYuvToRGB将图像从NV21格式转换为位图(ARGB_8888)时遇到问题。下面的代码可以说明问题。

假设我有以下50x50图像(下面的图片是设备截图,实际上不是50x50):

enter image description here

然后,如果我通过YuvImage#compressToJpeg + BitmapFactory.decodeByteArray将所述图片转换为位图:

YuvImage yuvImage = new YuvImage(example, android.graphics.ImageFormat.NV21, width, height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, os);
byte[] jpegByteArray = os.toByteArray();
return BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.length);

我得到了预期的图像。但如果我通过ScriptIntrinsicYuvToRGB转换它如下:

RenderScript rs = RenderScript.create(context);

Type.Builder tb = new Type.Builder(rs, Element.createPixel(rs,
        Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV));
tb.setX(width);
tb.setY(height);
tb.setYuvFormat(android.graphics.ImageFormat.NV21);
Allocation yuvAllocation = Allocation.createTyped(rs, tb.create(), Allocation.USAGE_SCRIPT);
yuvAllocation.copyFrom(example);

Type rgbType = Type.createXY(rs, Element.RGBA_8888(rs), width, height);
Allocation rgbAllocation = Allocation.createTyped(rs, rgbType);

ScriptIntrinsicYuvToRGB yuvToRgbScript = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(rs));
yuvToRgbScript.setInput(yuvAllocation);
yuvToRgbScript.forEach(rgbAllocation);

Bitmap convertedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
rgbAllocation.copyTo(convertedBitmap);

我收到以下损坏的图片:

enter image description here

我注意到这种情况发生在方形大小的图像上,而且从不具有2的幂(例如64x64,128x128等)。如果我没有尝试过方形尺寸,我就不会注意到这个问题,因为像2592x1728这样的图片尺寸可以正常工作。我错过了什么?

更新:按生成请求生成原始图像的代码:

int width = 50;
int height = 50;
int size = width * height;

byte[] example = new byte[size + size / 2];
for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
        example[y * width + x] = (byte) ((x*y / (float)size) * 255);
    }
}
for (int i = 0; i < size / 2; i++) {
    example[size + i] = (byte) (127);
}

2 个答案:

答案 0 :(得分:2)

以下代码的行为方式错误:

Type.Builder tb = new Type.Builder(rs, Element.createPixel(rs,
                    Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV));
tb.setX(width);
tb.setY(height);
tb.setYuvFormat(android.graphics.ImageFormat.NV21);
yuvAllocation = Allocation.createTyped(rs, tb.create(), Allocation.USAGE_SCRIPT);

如果使用&#34; raw&#34;创建分配的方式,转换将起作用:

int expectedBytes = width * height *
                ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;

Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.U8(rs))
                                                   .setX(expectedBytes);
Type yuvType = yuvTypeBuilder.create();
yuvAllocation = Allocation.createTyped(rs, yuvType, Allocation.USAGE_SCRIPT);

看来,如果使用PIXEL_YUV定义,则非16个维度存在大小问题。还在调查它。

答案 1 :(得分:0)

恕我直言,在Android上将NV21(或NV12)转换为ARGB的最佳方法是使用本机(C ++)LibYUV库 https://chromium.googlesource.com/libyuv/libyuv/

基于ARM v7(和最新的v8)Android设备的主要优势是NEON优化,这使得转换速度非常快。

您可以自己构建LibYUV(Build Instruction)或使用github中的任何预构建:https://github.com/search?q=libyuv+Android