如何将CameraX捕获的ImageProxy转换为Bitmap?
ImageProxy 转 Bitmap 的可行方案(CameraX 场景)
我之前在开发CameraX拍照功能时,也在ImageProxy转Bitmap这步卡了好久,试过好几种方法,下面给你分享几个靠谱的实现,尤其是官方推荐的最优方案:
方案一:官方推荐的高效方案(使用YuvToRgbConverter)
CameraX提供了YuvToRgbConverter工具类,专门用来处理YUV格式的Image转RGB Bitmap,这是最省心也最高效的方式,不需要自己手动处理YUV的复杂转换逻辑。
步骤:
- 确保已添加CameraX核心依赖(如果还没加的话):
implementation "androidx.camera:camera-core:1.3.0" // 替换成你项目中使用的CameraX版本即可
- 在
onCaptureSuccess里实现转换:
override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) { image?.let { imageProxy -> // 获取ImageProxy内部的Image对象,为空则直接返回 val rawImage = imageProxy.image ?: return // 创建Yuv转RGB的转换器实例 val converter = YuvToRgbConverter(this) // 创建与原图尺寸匹配的空Bitmap val bitmap = Bitmap.createBitmap( rawImage.width, rawImage.height, Bitmap.Config.ARGB_8888 ) // 执行转换:YUV格式Image -> RGB格式Bitmap converter.yuvToRgb(rawImage, bitmap) // 处理图片旋转(CameraX返回的图片可能和预览方向不一致) val rotatedBitmap = rotateBitmap(bitmap, rotationDegrees.toFloat()) // 这里就可以使用转换后的Bitmap了,比如设置到ImageView imageView.setImageBitmap(rotatedBitmap) // 务必释放资源!否则会导致Camera资源泄漏 rawImage.close() imageProxy.close() } } // 辅助方法:根据旋转角度调整Bitmap方向 private fun rotateBitmap(bitmap: Bitmap, degrees: Float): Bitmap { val matrix = Matrix() matrix.postRotate(degrees) return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) }
方案二:手动处理YUV转Bitmap(低版本兼容)
如果你的项目需要兼容Android P以下的系统,可以用手动解析YUV数据的方式,不过代码量更大,也容易出问题,仅作参考:
override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) { image?.let { imageProxy -> val rawImage = imageProxy.image ?: return val planes = rawImage.planes // 提取YUV各通道的数据 val yBuffer = planes[0].buffer val uBuffer = planes[1].buffer val vBuffer = planes[2].buffer val ySize = yBuffer.remaining() val uSize = uBuffer.remaining() val vSize = vBuffer.remaining() // 拼接成NV21格式的字节数组 val nv21 = ByteArray(ySize + uSize + vSize) yBuffer.get(nv21, 0, ySize) vBuffer.get(nv21, ySize, vSize) uBuffer.get(nv21, ySize + vSize, uSize) // 将NV21数据转成Bitmap val yuvImage = YuvImage(nv21, ImageFormat.NV21, rawImage.width, rawImage.height, null) val outputStream = ByteArrayOutputStream() yuvImage.compressToJpeg(Rect(0, 0, rawImage.width, rawImage.height), 100, outputStream) val jpegByteArray = outputStream.toByteArray() var bitmap = BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.size) // 同样处理图片旋转 bitmap = rotateBitmap(bitmap, rotationDegrees.toFloat()) imageView.setImageBitmap(bitmap) // 释放资源 rawImage.close() imageProxy.close() } }
关键注意事项
- 必须释放资源:
ImageProxy和它内部的Image对象都需要调用close(),否则会导致Camera资源泄漏,后续拍照可能失败。 - 处理旋转角度:CameraX返回的
rotationDegrees是图片需要旋转的角度,一定要处理,不然图片方向会和预览不一致。 - Bitmap内存优化:如果你的目标尺寸不是原图大小,可以在转换后对Bitmap进行压缩或者裁剪,避免内存占用过高。
内容的提问来源于stack exchange,提问作者Stefano Mtangoo
相关产品推荐
相关产品推荐

