Преобразование CameraX Captured ImageProxy в растровое изображение

Я работал с CameraX, и мне было трудно преобразовать захваченный ImageProxy в Bitmap. После поиска и попыток я сформулировал решение. Позже я обнаружил, что это не оптимально, поэтому изменил дизайн. Это заставило меня бросить часы работы.

Поскольку мне (или кому-то еще) он может понадобиться в будущем, я решил опубликовать здесь как вопрос, а также опубликовать и ответить на него для справки и изучения. Не стесняйтесь добавлять лучший ответ, если он у вас есть.

Соответствующий код:

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)
    }

}

person Stefano Mtangoo    schedule 09.08.2019    source источник


Ответы (4)


Итак, решение заключалось в том, чтобы добавить метод расширения к 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)
}
person Stefano Mtangoo    schedule 09.08.2019

Немного измененная версия. Использование функции inline use на Closable ImageProxy

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)
    } 
person Blackbelt    schedule 09.08.2019
comment
@Bender super.onCaptureSuccess закрывает изображение, поэтому убедитесь, что он не вызывается перед получением буфера изображения. - person marc.garcia; 06.03.2020
comment
@Blackbelt год спустя, и все еще нет другого выхода, или я что-то упускаю? - person Sarthak Mittal; 16.08.2020

Реализация в 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);
}
person NishanW    schedule 05.06.2020

Существует вторая версия _ 1_ в настоящий момент (CameraX версия 1.0.0-beta03). Он предоставляет несколько способов для сохранения изображения (OutputStream или возможно File может быть полезным в вашем случае).

Если вы все еще хотите преобразовать ImageProxy в Bitmap вот мой ответ на аналогичный вопрос, который дает правильную реализацию этого преобразования.

person art    schedule 30.05.2020