You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将CameraX捕获的ImageProxy转换为Bitmap?

ImageProxy 转 Bitmap 的可行方案(CameraX 场景)

我之前在开发CameraX拍照功能时,也在ImageProxy转Bitmap这步卡了好久,试过好几种方法,下面给你分享几个靠谱的实现,尤其是官方推荐的最优方案:


方案一:官方推荐的高效方案(使用YuvToRgbConverter)

CameraX提供了YuvToRgbConverter工具类,专门用来处理YUV格式的Image转RGB Bitmap,这是最省心也最高效的方式,不需要自己手动处理YUV的复杂转换逻辑。

步骤:

  1. 确保已添加CameraX核心依赖(如果还没加的话):
implementation "androidx.camera:camera-core:1.3.0"
// 替换成你项目中使用的CameraX版本即可
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:46:40