CameraX: 将照片捕获为位图

9
我正在尝试使用谷歌的CameraX示例应用程序(CameraXBasic,在Github上可找到),并希望将图像捕获为位图,以便在保存之前对其进行一些修改。是否有人有建议如何实现这一点?请参见下面谷歌原始代码来捕获和保存图像:
// Listener for button used to capture photo
        controls.findViewById<ImageButton>(R.id.camera_capture_button).setOnClickListener {

            // Get a stable reference of the modifiable image capture use case
            imageCapture?.let { imageCapture ->

                // Create output file to hold the image
                val photoFile = createFile(outputDirectory, FILENAME, PHOTO_EXTENSION)

                    // Create output options object which contains file + metadata
                    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
                            .build()

                    // Setup image capture listener which is triggered after photo has been taken
                    imageCapture.takePicture(
                            outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
                        override fun onError(exc: ImageCaptureException) {
                            Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
                        }

                        override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                            val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
                            Log.d(TAG, "Photo capture succeeded: $savedUri")

                            // We can only change the foreground Drawable using API level 23+ API
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                // Update the gallery thumbnail with latest picture taken
                                setGalleryThumbnail(savedUri)
                            }

                            // Implicit broadcasts will be ignored for devices running API level >= 24
                            // so if you only target API level 24+ you can remove this statement
                            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                                requireActivity().sendBroadcast(
                                        Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri)
                                )
                            }

                            // If the folder selected is an external media directory, this is
                            // unnecessary but otherwise other apps will not be able to access our
                            // images unless we scan them using [MediaScannerConnection]
                            val mimeType = MimeTypeMap.getSingleton()
                                    .getMimeTypeFromExtension(savedUri.toFile().extension)
                            MediaScannerConnection.scanFile(
                                    context,
                                    arrayOf(savedUri.toFile().absolutePath),
                                    arrayOf(mimeType)
                            ) { _, uri ->
                                Log.d(TAG, "Image capture scanned into media store: $uri")
                            }
                        }
                    })
                }

                // We can only change the foreground Drawable using API level 23+ API
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                    // Display flash animation to indicate that photo was captured
                    container.postDelayed({
                        container.foreground = ColorDrawable(Color.WHITE)
                        container.postDelayed(
                                { container.foreground = null }, ANIMATION_FAST_MILLIS)
                    }, ANIMATION_SLOW_MILLIS)
                }
            }

提前感谢您的帮助。


1
您可以使用in-memory method来拍摄照片,这将提供一个ImageProxy,您可以转换Bitmap,对其进行修改,然后存储。 - Husayn Hakeem
2个回答

14

takePicture 中还有另一个 回调函数 OnImageCapturedCallback 可以在 onCaptureSuccess 中返回 ImageProxy,您可以从该 ImageProxy 对象中获取位图。

 imageCapture.takePicture(cameraExecutor, object : 
 ImageCapture.OnImageCapturedCallback() {
                override fun onCaptureSuccess(image: ImageProxy) {
                    //get bitmap from image 
                    val bitmap = imageProxyToBitmap(image)
                    super.onCaptureSuccess(image)
                }

                override fun onError(exception: ImageCaptureException) {
                    super.onError(exception)
                }

 })

/**
 *  convert image proxy to bitmap
 *  @param image
 */
private fun imageProxyToBitmap(image: ImageProxy): Bitmap {
    val planeProxy = image.planes[0]
    val buffer: ByteBuffer = planeProxy.buffer
    val bytes = ByteArray(buffer.remaining())
    buffer.get(bytes)
    return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}

谢谢您的回答,它非常有效!然而,在这行代码" return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)"中,我曾经遇到过一个 NullPointerExeption 错误。您有什么想法是什么原因造成了这个错误以及如何解决它?再次感谢您的帮助。 - MichaelK
3
注意ImageProxy的格式。您可以使用getFormat()方法进行检查。这里显示的转换为位图的方法适用于JPEG(255)格式,如果我没有错的话。 - BCJuan
1
@BCJuan是正确的,这个imageProxyToBitmap代码适用于JPEG格式。一个小修正 - JPEG格式的常量为256。 - Carl Smith
2
有人使用这些方法遇到图像旋转问题吗? - issamux
因为某些原因,这在一些设备上无法正常工作,有人知道原因吗? - kallis
@issamux 我找到了。不知道为什么。我正在深入挖掘这个问题。 - Dev4Life

0

关于Kishore回答中旋转问题的补充:CameraX图像位图显示为水平而不是垂直。通过使用元数据来修复方向问题。在小米Poco X3上进行了测试。

    private fun imageProxyToBitmap(image: ImageProxy): Bitmap {
    val planeProxy = image.planes[0]
    val buffer: ByteBuffer = planeProxy.buffer
    val bytes = ByteArray(buffer.remaining())
    buffer.get(bytes)

    // Decode the image with EXIF orientation information
    val options = BitmapFactory.Options()
    options.inSampleSize = 1
    val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)

    // Check and handle EXIF orientation
    val exif = ExifInterface(bytes.inputStream())
    val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
    val rotatedBitmap = when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitmap(bitmap, 90)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitmap(bitmap, 180)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitmap(bitmap, 270)
        else -> bitmap
    }

    return rotatedBitmap
}

// Rotate the bitmap to the desired orientation
private fun rotateBitmap(source: Bitmap, degrees: Int): Bitmap {
    val matrix = Matrix()
    matrix.postRotate(degrees.toFloat())
    return Bitmap.createBitmap(source, 0, 0, source.width, source.height, matrix, true)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接