将 android.media.Image (YUV_420_888) 转换为位图
Posted
技术标签:
【中文标题】将 android.media.Image (YUV_420_888) 转换为位图【英文标题】:Convert android.media.Image (YUV_420_888) to Bitmap 【发布时间】:2015-04-10 09:18:50 【问题描述】:我正在尝试使用此处建议的 camera2 api 实现相机预览图像数据处理:Camera preview image data processing with android L and Camera2 API。
我使用 onImageAvailableListener 成功接收回调,但为了将来的处理,我需要从 YUV_420_888 android.media.Image 获取位图。我搜索了类似的问题,但没有一个有帮助。
您能否建议我如何,或者是否有更好的方法来监听预览帧?
【问题讨论】:
【参考方案1】:您可以使用内置的 Renderscript 内在函数 ScriptIntrinsicYuvToRGB
来执行此操作。代码取自Camera2 api Imageformat.yuv_420_888 results on rotated image:
@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;
【讨论】:
imageToByteBuffer
根本没有效率
@user924 最好的方法是什么?
请注意 ScriptIntrinsicYuvToRGB
对于相机预览帧是错误的,因为它假定 video BT.610 颜色空间(其中 Y 在范围内[16…235]
)。【参考方案2】:
对于更简单的解决方案,请参阅我的实现:
Conversion YUV 420_888 to Bitmap (full code)
该函数将 media.image 作为输入,并基于 y、u 和 v 平面创建三个 RenderScript 分配。它遵循 Wikipedia 插图中所示的 YUV_420_888 逻辑。
然而,这里我们为 Y、U 和 V 通道提供了三个独立的图像平面,因此我将它们视为三个字节 [],即 U8 分配。 y 分配的大小为宽度 * 高度字节,而 u 和 v 分配的大小为宽度 * 高度/4 个字节,这反映了每个 u 字节覆盖 4 个像素的事实(每个 v 字节也是如此)。
【讨论】:
【参考方案3】:我为此编写了一些代码,它是 YUV 数据预览并将其更改为 JPEG 数据,我可以使用它来保存为位图、字节 [] 或其他。(您可以看到“分配”类) . 并且 SDK 文档说:“为了使用 android.renderscript 进行高效的 YUV 处理:使用支持的 YUV 类型、IO_INPUT 标志和 getOutputSizes(Allocation.class) 返回的大小之一创建一个 RenderScript Allocation,然后获取 Surface使用 getSurface()。” 这是代码,希望对你有帮助:https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs
【讨论】:
以上是关于将 android.media.Image (YUV_420_888) 转换为位图的主要内容,如果未能解决你的问题,请参考以下文章
html 为Yu Yu-Ren Pan撰写CRSC2014留言簿的前端! (仅用于调试)
MySQL(数据库)基础知识;关系型数据库yu非关系型数据库;连接认证;