将 android.media.Image (YUV_420_888) 转换为位图
Convert android.media.Image (YUV_420_888) to Bitmap
我正在尝试使用 camera2 api 实现相机预览图像数据处理,如此处所建议:Camera preview image data processing with Android L and Camera2 API。
我使用 onImageAvailableListener 成功接收到回调,但为了将来的处理,我需要从 YUV_420_888 android.media.Image 获取位图。我搜索了类似的问题,但 none 个问题对我有所帮助。
能否建议我如何将 android.media.Image (YUV_420_888) 转换为位图或者是否有更好的监听预览帧的方法?
我写了一些关于这个的代码,它是 YUV 数据预览并将其更改为 JPEG 数据,我可以用它来保存为位图,byte[] 或其他。(你可以看到 class "Allocation")。
SDK文档说:
"For efficient YUV processing with android.renderscript: Create a RenderScript Allocation with a supported YUV type, the IO_INPUT flag, and one of the sizes returned by getOutputSizes(Allocation.class), Then obtain the Surface with getSurface()."
这是代码,希望对你有帮助:https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs
有关更简单的解决方案,请在此处查看我的实现:
Conversion YUV 420_888 to Bitmap (full code)
该函数将 media.image 作为输入,并根据 y、u 和 v 平面创建三个 RenderScript 分配。它遵循维基百科插图中所示的 YUV_420_888 逻辑。
然而,这里我们有三个单独的图像平面用于 Y、U 和 V 通道,因此我将它们视为三个字节[],即 U8 分配。 y 分配的大小为 width * height 字节,而 u 和 v 分配的大小为 width * height/4 字节,反映了每个 u 字节覆盖 4 个像素(每个 v 字节也是如此)的事实。
您可以使用内置的 Renderscript intrinsic ScriptIntrinsicYuvToRGB
. Code taken from :
@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;
}
我正在尝试使用 camera2 api 实现相机预览图像数据处理,如此处所建议:Camera preview image data processing with Android L and Camera2 API。
我使用 onImageAvailableListener 成功接收到回调,但为了将来的处理,我需要从 YUV_420_888 android.media.Image 获取位图。我搜索了类似的问题,但 none 个问题对我有所帮助。
能否建议我如何将 android.media.Image (YUV_420_888) 转换为位图或者是否有更好的监听预览帧的方法?
我写了一些关于这个的代码,它是 YUV 数据预览并将其更改为 JPEG 数据,我可以用它来保存为位图,byte[] 或其他。(你可以看到 class "Allocation")。
SDK文档说:
"For efficient YUV processing with android.renderscript: Create a RenderScript Allocation with a supported YUV type, the IO_INPUT flag, and one of the sizes returned by getOutputSizes(Allocation.class), Then obtain the Surface with getSurface()."
这是代码,希望对你有帮助:https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs
有关更简单的解决方案,请在此处查看我的实现:
Conversion YUV 420_888 to Bitmap (full code)
该函数将 media.image 作为输入,并根据 y、u 和 v 平面创建三个 RenderScript 分配。它遵循维基百科插图中所示的 YUV_420_888 逻辑。
然而,这里我们有三个单独的图像平面用于 Y、U 和 V 通道,因此我将它们视为三个字节[],即 U8 分配。 y 分配的大小为 width * height 字节,而 u 和 v 分配的大小为 width * height/4 字节,反映了每个 u 字节覆盖 4 个像素(每个 v 字节也是如此)的事实。
您可以使用内置的 Renderscript intrinsic ScriptIntrinsicYuvToRGB
. Code taken from
@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; }