在 Android 上使用 CameraX 进行 MLKit 文本识别的最佳用法

Best usage of CameraX for MLKit Text Recognition on Android

我需要在 Android 上使用 MLKit 实现文本识别,我决定使用新的 CameraX api 作为相机库。我正在为 classes 的正确 "pipeline" 或图像的数据流而苦苦挣扎,因为 CameraX 非常新,而且那里的资源不多。用例是我拍摄照片,在中间裁剪 UI 中可见的一些边界,然后将裁剪后的图像传递给将处理图像的 MLKit。

鉴于此,ImageAnalysis.Analyzer 有地方吗? api?据我了解,此分析仪仅用于预览,而不用于捕获的图像。

我的第一个想法是使用接受 OnImageCapturedCallbacktakePicture 方法,但是当我尝试访问时,例如。 ImageProxy.height 应用程序因异常而崩溃 java.lang.IllegalStateException: Image is already closed,我找不到任何解决方法。

然后我决定使用 takePicture method and now I save image to the file, then read it to Bitmap, crop this image and now I have an image that can be passed to MLKit. But when I take a look at FirebaseVisionImage that is passed to FirebaseVisionTextRecognizer it has a factory method 的另一个重载,我可以将我从 OnImageCapturedCallback 获得的图像传递给它,这似乎我正在做一些不必要的步骤。

所以我的问题是:

  1. 是否有一些 class(CaptureProcessor?)可以处理拍摄图像的裁剪?我想我可以使用 OnImageCapturedCallback 来接收已经裁剪过的图像。
  2. 如果我不进行实时处理而进行 post 处理,我还应该使用 ImageAnalysis.Analyzer 吗?

我想我可以用我目前的方法实现我想要的,但我觉得我可以使用比现在更多的 CameraX。

谢谢!

Is there some class (CaptureProcessor?) that will take care of the cropping of taken image?

您可以在使用 setCropAspectRatio(Rational) 方法构建 ImageCapture 用例后设置裁剪纵横比。这个方法crops从旋转输出图像的中心开始。所以基本上你在调用 takePicture() 后得到的就是我认为你所要求的。

Should I even use ImageAnalysis.Analyzer if I am not doing realtime processing and I am doing post processing?

不,它在您的场景中没有意义。如您所述,只有在进行实时图像处理时,您才想使用 ImageAnalysis.Analyzer.

ps:我很想看看您用于 takePicture() 的导致 IllegalStateException 的代码。

[编辑]

看看你的代码

imageCapture?.takePicture(executor, object : ImageCapture.OnImageCapturedCallback() {
    override fun onCaptureSuccess(image: ImageProxy) {
        // 1
        super.onCaptureSuccess(image)

        // 2
        Log.d("MainActivity", "Image captured: ${image.width}x${image.height}")
    }
})

在 (1) 处,如果您看一下 super.onCaptureSuccess(imageProxy) 的实现,它实际上关闭了传递给该方法的 imageProxy。在 (2) 中访问图像的宽度和高度会引发异常,这是正常的 - 因为图像已关闭 -。文档指出:

The application is responsible for calling ImageProxy.close() to close the image.

所以在使用这个回调的时候,最好不要调用super...,直接使用imageProxy,然后在方法返回前,手动关闭它(ImageProxy.close())。