将 CameraX 捕获的 ImageProxy 转换为位图

Convert CameraX Captured ImageProxy to Bitmap

我在使用 CameraX 时很难将捕获的 ImageProxy 转换为位图。经过搜索和尝试,我制定了解决方案。后来我发现它不是最优的,所以我改变了设计。这迫使我放弃工作时间。

由于我(或其他人)将来可能需要它,所以我决定在这里post作为问题和post并回答它以供参考和审查。如果您有更好的答案,请随时添加。

相关代码为:

class ImagePickerActivity : AppCompatActivity() {
    private var width = 325
    private var height = 205

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_image_picker)

        view_finder.post { startCamera() }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun startCamera() {
        // Create configuration object for the viewfinder use case
        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(Rational(1, 1))
            //setTargetResolution(Size(width, height))
            setLensFacing(CameraX.LensFacing.BACK)
            setTargetAspectRatio(Rational(width, height))
        }.build()

        }

        // Create configuration object for the image capture use case
        val imageCaptureConfig = ImageCaptureConfig.Builder()
            .apply {
                setTargetAspectRatio(Rational(1, 1))
                // We don't set a resolution for image capture instead, we
                // select a capture mode which will infer the appropriate
                // resolution based on aspect ration and requested mode
                setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
            }.build()

        // Build the image capture use case and attach button click listener
        val imageCapture = ImageCapture(imageCaptureConfig)
        capture_button.setOnClickListener {

            imageCapture.takePicture(object : ImageCapture.OnImageCapturedListener() {
                override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
                        //How do I get the bitmap here?
                        //imageView.setImageBitmap(someBitmap)
                }

                override fun onError(useCaseError: ImageCapture.UseCaseError?, message: String?, cause: Throwable?) {
                    val msg = "Photo capture failed: $message"
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.e(localClassName, msg)
                    cause?.printStackTrace()
                }
            })
        }
        CameraX.bindToLifecycle(this, preview, imageCapture)
    }

}

所以解决方案是向 Image 添加扩展方法,这里是代码

class ImagePickerActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_image_picker)
    }

    private fun startCamera() {

        val imageCapture = ImageCapture(imageCaptureConfig)
        capture_button.setOnClickListener {

            imageCapture.takePicture(object : ImageCapture.OnImageCapturedListener() {
                override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
                    imageView.setImageBitmap(image.image?.toBitmap())
                }
                //.....
            })
        }
    }

}

fun Image.toBitmap(): Bitmap {
    val buffer = planes[0].buffer
    buffer.rewind()
    val bytes = ByteArray(buffer.capacity())
    buffer.get(bytes)
    return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}

略有修改的版本。在 Closable ImageProxy

上使用 inline 函数 use
imageCapture.takePicture(
           object : ImageCapture.OnImageCapturedListener() {
               override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
                     image.use { image ->
                           val bitmap: Bitmap? = image?.let {
                                imageProxyToBitmap(it)
                            } ?: return
                      }
          }
       })

  private fun imageProxyToBitmap(image: ImageProxy): Bitmap {
        val buffer: ByteBuffer = image.planes[0].buffer
        val bytes = ByteArray(buffer.remaining())
        buffer.get(bytes)
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
    } 

有第二个版本的 takePicture method at the moment (CameraX version 1.0.0-beta03). It provides several ways 可以保留图像(OutputStream 或者 File 可能对你的情况有用)。

如果您仍想将 ImageProxy 转换为 Bitmap 我对类似问题的回答给出了此转换的正确实现。

Java Backbelt 的实施

private Bitmap imageProxyToBitmap(ImageProxy image) {
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    return BitmapFactory.decodeByteArray(bytes,0,bytes.length,null);
}

这是最安全的方法,使用 MLKit 自己的实现。 测试并使用 MLKit 版本 1.0.1

import com.google.mlkit.vision.common.internal.ImageConvertUtils;

Image mediaImage = imageProxy.getImage();
InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
Bitmap bitmap = ImageConvertUtils.getInstance().getUpRightBitmap(image)

请看一看这个answer。将它应用于您的问题所需要做的就是从您的 ImageProxy

中获取图像
Image img = imaget.getImage();