将android.media.Image(YUV_420_888)转换为位图

将android.media.Image(YUV_420_888)转换为位图,android,bitmap,android-camera,yuv,Android,Bitmap,Android Camera,Yuv,我正在尝试使用camera2 api实现相机预览图像数据处理,如下所示: 我使用onImageAvailableListener成功接收回调,但为了将来的处理,我需要从YUV_420_888 android.media.Image获取位图。我寻找类似的问题,但没有一个有用 您能建议我如何将android.media.Image(YUV_420_888)转换为位图,或者有更好的方法来监听预览帧吗?我写了一些代码,这是YUV数据预览,并将其转换为JPEG数据,我可以使用它保存为位图、字节[]或其他格

我正在尝试使用camera2 api实现相机预览图像数据处理,如下所示:

我使用onImageAvailableListener成功接收回调,但为了将来的处理,我需要从YUV_420_888 android.media.Image获取位图。我寻找类似的问题,但没有一个有用


您能建议我如何将android.media.Image(YUV_420_888)转换为位图,或者有更好的方法来监听预览帧吗?

我写了一些代码,这是YUV数据预览,并将其转换为JPEG数据,我可以使用它保存为位图、字节[]或其他格式(您可以看到类“分配”)

SDK文档说:
“为了使用android.renderscript高效处理YUV:使用支持的YUV类型、IO_输入标志和getOutputSizes(Allocation.class)返回的一个大小创建一个renderscript分配,然后使用getSurface()获取曲面。”


以下是代码,希望它能帮助您:

有关更简单的解决方案,请参阅我的实现:

该函数将media.image作为输入,并基于y、u和v平面创建三个RenderScript分配。它遵循YUV_420_888逻辑,如本维基百科插图所示


然而,这里我们有三个单独的图像平面用于Y、U和V通道,因此我将它们视为三字节[],即U8分配。y分配有大小-宽度*高度字节,而u和v分配各有大小-宽度*高度/4字节,反映了每个u字节覆盖4个像素(与每个v字节相同)的事实。

您可以使用内置的Renderscript内部实现这一点。代码取自:

@覆盖
公共图像可用(图像阅读器)
{
//获取YUV数据
最终图像=reader.acquiredlatestimage();
final ByteBuffer yuvBytes=this.imageToByteBuffer(图像);
//将YUV转换为RGB
final RenderScript rs=RenderScript.create(this.mContext);
最终位图Bitmap=Bitmap.createBitmap(image.getWidth()、image.getHeight()、Bitmap.Config.ARGB_8888);
最终分配allocationRgb=Allocation.createFromBitmap(rs,位图);
最终分配allocationYuv=Allocation.createSize(rs,Element.U8(rs),yuvBytes.array().length);
allocationYuv.copyFrom(yuvBytes.array());
ScriptIntrinsicYuvToRGB scriptYuvToRgb=ScriptIntrinsicYuvToRGB.create(rs,Element.U8_4(rs));
scriptYuvToRgb.setInput(分配yuv);
scriptYuvToRgb.forEach(分配RGB);
allocationRgb.copyTo(位图);
//释放
bitmap.recycle();
allocationYuv.destroy();
allocationRgb.destroy();
rs.destroy();
image.close();
}
专用ByteBuffer imageToByteBuffer(最终图像)
{
最终矩形裁剪=image.getCropRect();
最终整型宽度=裁剪宽度();
最终整型高度=裁剪高度();
final Image.Plane[]planes=Image.getPlanes();
最后一个字节[]行数据=新字节[planes[0]。getRowStride()];
final int bufferSize=width*height*ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888)/8;
最终ByteBuffer输出=ByteBuffer.allocateDirect(bufferSize);
int channelOffset=0;
int outputStride=0;
对于(int planeIndex=0;planeIndex<3;planeIndex++)
{
如果(平面索引==0)
{
channelOffset=0;
输出步幅=1;
}
否则如果(平面索引==1)
{
通道偏移=宽度*高度+1;
输出步幅=2;
}
否则如果(平面索引==2)
{
通道偏移=宽度*高度;
输出步幅=2;
}
final ByteBuffer buffer=平面[planeIndex].getBuffer();
final int rowStride=平面[planeIndex].getRowStride();
final int pixelStride=平面[planeIndex].getPixelStride();
最终整数移位=(平面索引==0)?0:1;
最终整数宽度移位=宽度>>移位;
最终内部高度偏移=高度>>偏移;
buffer.position(rowStride*(crop.top>>shift)+pixelStride*(crop.left>>shift));
对于(int row=0;row

imageToByteBuffer
在以下位置无效:all@user924最好的方法是什么?请注意,
ScriptIntrinsicYuvToRGB
对于摄影机预览帧是错误的,因为它假定视频颜色空间(其中Y在范围
[16…235]
)。
@Override
public void onImageAvailable(ImageReader reader)
{
    // Get the YUV data

    final Image image = reader.acquireLatestImage();
    final ByteBuffer yuvBytes = this.imageToByteBuffer(image);

    // Convert YUV to RGB

    final RenderScript rs = RenderScript.create(this.mContext);

    final Bitmap        bitmap     = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
    final Allocation allocationRgb = Allocation.createFromBitmap(rs, bitmap);

    final Allocation allocationYuv = Allocation.createSized(rs, Element.U8(rs), yuvBytes.array().length);
    allocationYuv.copyFrom(yuvBytes.array());

    ScriptIntrinsicYuvToRGB scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
    scriptYuvToRgb.setInput(allocationYuv);
    scriptYuvToRgb.forEach(allocationRgb);

    allocationRgb.copyTo(bitmap);

    // Release

    bitmap.recycle();

    allocationYuv.destroy();
    allocationRgb.destroy();
    rs.destroy();

    image.close();
}

private ByteBuffer imageToByteBuffer(final Image image)
{
    final Rect crop   = image.getCropRect();
    final int  width  = crop.width();
    final int  height = crop.height();

    final Image.Plane[] planes     = image.getPlanes();
    final byte[]        rowData    = new byte[planes[0].getRowStride()];
    final int           bufferSize = width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8;
    final ByteBuffer    output     = ByteBuffer.allocateDirect(bufferSize);

    int channelOffset = 0;
    int outputStride = 0;

    for (int planeIndex = 0; planeIndex < 3; planeIndex++)
    {
        if (planeIndex == 0)
        {
            channelOffset = 0;
            outputStride = 1;
        }
        else if (planeIndex == 1)
        {
            channelOffset = width * height + 1;
            outputStride = 2;
        }
        else if (planeIndex == 2)
        {
            channelOffset = width * height;
            outputStride = 2;
        }

        final ByteBuffer buffer      = planes[planeIndex].getBuffer();
        final int        rowStride   = planes[planeIndex].getRowStride();
        final int        pixelStride = planes[planeIndex].getPixelStride();

        final int shift         = (planeIndex == 0) ? 0 : 1;
        final int widthShifted  = width >> shift;
        final int heightShifted = height >> shift;

        buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));

        for (int row = 0; row < heightShifted; row++)
        {
            final int length;

            if (pixelStride == 1 && outputStride == 1)
            {
                length = widthShifted;
                buffer.get(output.array(), channelOffset, length);
                channelOffset += length;
            }
            else
            {
                length = (widthShifted - 1) * pixelStride + 1;
                buffer.get(rowData, 0, length);

                for (int col = 0; col < widthShifted; col++)
                {
                    output.array()[channelOffset] = rowData[col * pixelStride];
                    channelOffset += outputStride;
                }
            }

            if (row < heightShifted - 1)
            {
                buffer.position(buffer.position() + rowStride - length);
            }
        }
    }

    return output;
}