【问题标题】:How to make CameraX Preview freeze when take a photo?拍照时如何让 CameraX Preview 冻结?
【发布时间】:2020-01-09 10:17:26
【问题描述】:

我的自定义 CameraX 有这样的流程:

  • 打开相机预览(实时)
  • 点击按钮拍照
  • 单击该按钮时有一个过程(将路径转换为位图,旋转图像,自动裁剪图像,保存到设备中)
  • 运行所有进程并成功后,将图像发送到其他Fragment并显示为幻灯片

问题是当running all the process (in step 3) 有一个delayed 2 seconds 并且相机预览仍然是livenot freezelock)。如何制作camera preview freeze or lock when running the process

这是我在 Camera X 中运行相机预览的代码:

class CameraFragment : Fragment() {

        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_camera, container, false)
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            viewFinder.post { setupCamera() }
        }

        private fun setupCamera() {
            CameraX.unbindAll()
            CameraX.bindToLifecycle(
                this,
                buildPreviewUseCase(),
                buildImageCaptureUseCase()
            )
        }

        private fun buildPreviewUseCase(): Preview {
            val preview = Preview(
                UseCaseConfigBuilder.buildPreviewConfig(
                    viewFinder.display
                )
            )
            preview.setOnPreviewOutputUpdateListener { previewOutput ->
                updateViewFinderWithPreview(previewOutput)
                correctPreviewOutputForDisplay(previewOutput.textureSize)
            }
            return preview
        }

        private fun buildImageCaptureUseCase(): ImageCapture {
            val capture = ImageCapture(
                UseCaseConfigBuilder.buildImageCaptureConfig(
                    viewFinder.display
                )
            )
            cameraCaptureImageButton.setOnClickListener {
                capture.takePicture(
                    FileCreator.createTempFile(JPEG_FORMAT),
                    Executors.newSingleThreadExecutor(),
                    object : ImageCapture.OnImageSavedListener {
                        override fun onImageSaved(file: File) {
                            // I want make a freeze camera preview when execute this before launch *launchGalleryFragment(path)*
                            val bitmap = BitmapFactory.decodeFile(file.absolutePath)
                            val rotatedBitmap = bitmap.rotate(90)
                            val croppedImage = cropImage(rotatedBitmap, viewFinder, rectangle)
                            val path = saveImage(croppedImage)
                            requireActivity().runOnUiThread {
                                launchGalleryFragment(path)
                            }
                        }

                        override fun onError(
                            imageCaptureError: ImageCapture.ImageCaptureError,
                            message: String,
                            cause: Throwable?
                        ) {
                            Toast.makeText(requireContext(), "Error: $message", Toast.LENGTH_LONG)
                                .show()
                            Log.e("CameraFragment", "Capture error $imageCaptureError: $message", cause)
                        }
                    })
            }
            return capture
        }

        private fun launchGalleryFragment(path: String) {
            val action = CameraFragmentDirections.actionLaunchGalleryFragment(path)
            findNavController().navigate(action)
        }

    }

【问题讨论】:

    标签: kotlin android-camerax


    【解决方案1】:

    也许您可以尝试取消绑定预览用例:

    版本 1.0.0-alpha06:CameraX.unbind(preview);

    版本> 1.0.0-alpha07:cameraProvider.unbind(preview);

    在您的情况下,您需要将预览用例保存到变量中,然后解除绑定:

    // Declare the preview use case variable (as in the CameraXBasic example)
    private var preview: Preview? = null
    

    然后实例化变量(就像你做的那样):

    private fun buildPreviewUseCase(): Preview {
        preview = Preview(
            UseCaseConfigBuilder.buildPreviewConfig(
                viewFinder.display
            )
        )
        preview.setOnPreviewOutputUpdateListener { previewOutput ->
            updateViewFinderWithPreview(previewOutput)
            correctPreviewOutputForDisplay(previewOutput.textureSize)
        }
        return preview
    }
    

    然后,当您想冻结预览时,只需取消绑定用例:

    CameraX.unbind(preview);
    

    编辑 正如@Billda 在这篇文章中所说:CameraX - crash when unbinding Preview UseCase

    要冻结预览,您不应取消绑定预览用例。也许有 未来的 API,但目前推荐的方法是 存储来自 ImageAnalysis 的最新帧并将其放入 ImageView 重叠预览。

    所以我决定更新我的答案,以提供另一种使用 ImageAnalysis (1.0.0-beta02) 实现分析器的解决方案。

    1- 创建 FreezeAnalyzer 类:

    class FreezeAnalyzer(private val callback: FreezeCallback) : ImageAnalysis.Analyzer {
        private var flag = false
    
        override fun analyze(image: ImageProxy) {
            if(flag){
                flag = false
                val bitmap = toBitmap(image)
                callback.onLastFrameCaptured(bitmap)
            }
            image.close()
        }
    
        fun freeze(){
            flag = true
        }
    
        private fun toBitmap(image: ImageProxy): Bitmap {
            // Convert the imageProxy to Bitmap
            // ref https://stackoverflow.com/questions/56772967/converting-imageproxy-to-bitmap
            // ISSUE, on my android 7 when converting the imageProxy to Bitmap I have a problem with the colors...
            var bitmap = ...
    
            // Rotate the bitmap
            val rotationDegrees = image.imageInfo.rotationDegrees.toFloat()
            if (rotationDegrees != 0f) {
                val matrix = Matrix()
                matrix.postRotate(rotationDegrees)
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
            }
            return bitmap
        }
    }
    

    2-XML

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <ImageView
        android:id="@+id/image_view"
        android:visibility="invisible"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    

    3- 初始化图像分析器

    val resolutionSize = Size(preview_view.width, preview_view.height)
    
    // Set up analyser
    imageAnalysis = ImageAnalysis.Builder().apply {
        setTargetResolution(resolutionSize)
        setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    }.build()
    
    val analyzer = FreezeAnalyzer(object : FreezeCallback {
        override fun onLastFrameCaptured(bitmap: Bitmap) {
            runOnUiThread {
                preview_view.visibility = View.INVISIBLE
                image_view.visibility = View.VISIBLE
                image_view.setImageBitmap(bitmap)
            }
        }
    })
    imageAnalysis.setAnalyzer(executor, analyzer)
    

    4- 绑定 imageAnalysis 用例

    try {
        val camera = cameraProvider.bindToLifecycle(
            this,
            cameraSelector,
            preview,
            imageAnalysis,
            imageCapture
        )
        preview.setSurfaceProvider(preview_view.createSurfaceProvider(camera.cameraInfo))
    }
    

    5- 拍照

    btn_capture.setOnClickListener {
        file = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
        val outputFileOptions: ImageCapture.OutputFileOptions =
            ImageCapture.OutputFileOptions.Builder(file!!).build()
        analyzer.freeze()
        imageCapture.takePicture(outputFileOptions, executor, onImageSavedCallback)
    }
    

    6-发布

    btn_release.setOnClickListener {
        preview_view.visibility = View.VISIBLE
        image_view.visibility = View.INVISIBLE
    }
    

    希望对您有所帮助,我不是专家,所以如果您有一些改进,欢迎您!

    【讨论】:

    • preview 中有什么代码?能给我完整的源代码吗?
    • 我在哪里添加CameraX.unbind(preview)函数?我在 onImageSaved() 里面试试,它是 forclose
    • 是的,在 onImageSaved() 中,我使用 CameraXBasic 示例对其进行了测试,并且可以正常工作。我认为它来自您的执行者,您可以尝试像 CameraXBasic 示例中那样使用ContextCompat.getMainExecutor(requireContext())
    • 是的,在我将它添加到我的代码中之后它就可以工作了。但是,为什么在我点击相机按钮后不立即冻结呢?在立即冻结之前仍有延迟。
    • 嗯,我认为在您单击按钮的那一刻和调用回调的那一刻(相机拍照的时间)之间存在延迟。尝试在@987654338 中取消绑定预览@,就在调用capture.takePicture() 之后。不过,冻结的预览可能与拍摄的照片不同。
    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多