如何在 CameraX analyze() 之外提取调用 MLKit process() 的方法?

How to extract methods that call MLKit process(), outside of CameraX analyze()?

为了代码清晰和更好的可重用性,我想要这样的东西

class MyAnalyzer : ImageAnalysis.Analyzer {
    private val faceDetector = FaceDetection.getClient()

    @androidx.camera.core.ExperimentalGetImage
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image ?: return
        val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

        if (getFace(image) != null) {
            // do stuff
        }

        imageProxy.close()
    }

    private fun getFace(image: InputImage): Face? {
        var face: Face? = null

        faceDetector.process(image).addOnSuccessListener { faces ->
            face = faces.firstOrNull()
        }

        return face
    }   
}

但是当我 运行 应用程序时,它总是引发此异常

E/NativeFaceDetectorV2Imp: Native face detection v2 failed
    java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.nio.ByteBuffer.isDirect()' on a null object reference
        at com.google.android.gms.vision.face.NativeFaceDetectorV2Impl.zza(com.google.android.gms:play-services-vision-face-contour-internal@@16.1.0:96)
        at com.google.android.gms.vision.face.internal.client.zzb.zza(com.google.android.gms:play-services-vision@@20.1.3:34)
        at com.google.android.gms.vision.face.FaceDetector.detect(com.google.android.gms:play-services-vision@@20.1.3:27)
        at com.google.mlkit.vision.face.internal.zzg.zzc(com.google.android.gms:play-services-mlkit-face-detection@@16.1.2:23)
        at com.google.mlkit.vision.face.internal.zzg.zzd(com.google.android.gms:play-services-mlkit-face-detection@@16.1.2:3)
        at com.google.mlkit.vision.face.internal.zzg.run(com.google.android.gms:play-services-mlkit-face-detection@@16.1.2)
        at com.google.mlkit.vision.common.internal.MobileVisionBase.zza(com.google.mlkit:vision-common@@16.2.0)
        at com.google.mlkit.vision.common.internal.zzc.call(com.google.mlkit:vision-common@@16.2.0)
        at com.google.mlkit.common.sdkinternal.ModelResource.zza(com.google.mlkit:common@@17.1.0)
        at com.google.mlkit.common.sdkinternal.zzn.run(com.google.mlkit:common@@17.1.0)
        at com.google.mlkit.common.sdkinternal.zzp.run(com.google.mlkit:common@@17.1.0:2)
        at com.google.mlkit.common.sdkinternal.MlKitThreadPool.zze(com.google.mlkit:common@@17.1.0:4)
        at com.google.mlkit.common.sdkinternal.MlKitThreadPool.zzc(com.google.mlkit:common@@17.1.0)
        at com.google.mlkit.common.sdkinternal.zzj.run(com.google.mlkit:common@@17.1.0)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at com.google.mlkit.common.sdkinternal.MlKitThreadPool.zzd(com.google.mlkit:common@@17.1.0)
        at com.google.mlkit.common.sdkinternal.zzk.run(com.google.mlkit:common@@17.1.0)
        at java.lang.Thread.run(Thread.java:761)

编辑: 我找到了异常的原因并修复了它(参见下面的 ),但是代码仍然有问题,因为我仍然得不到我期待的结果。

我找到了罪魁祸首。我错误地粘贴了第一个代码示例,在块调用 addOnSuccessListener { } 之后粘贴了 imageProxy.close(),但实际上它总是在其中。目前的情况是

class MyAnalyzer : ImageAnalysis.Analyzer {
    private val faceDetector = FaceDetection.getClient()

    @androidx.camera.core.ExperimentalGetImage
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image ?: return
        val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

        if (getFace(image, imageProxy) != null) {
            // do stuff
        }
    }

    private fun getFace(image: InputImage, imageProxy: ImageProxy): Face? {
        var face: Face? = null

        /* old code
         *
         * faceDetector.process(image).addOnSuccessListener { faces ->
         *     face = faces.firstOrNull()
         *     imageProxy.close()
         * }
         */

        // solution code
        runBlocking {
            withContext(Dispatchers.IO) {
                val faces = Tasks.await(faceDetector.process(image))

                face = faces.firstOrNull()
                imageProxy.close()
            }
        }

        return face
    }   
}

现在至少它不再引发异常,如果我在侦听器块中记录人脸就没问题,但 if (getFace(image, imageProxy) != null) 中的代码不会 运行。我认为这是因为 faceDetector.process() 还没有完成,所以 getFace() returns 立即 face 这仍然是 null 从初始化开始,当监听器被触发时它也是晚了。有什么建议吗?

编辑:又找到罪魁祸首了。和我想象的一模一样,我需要做的就是找到另一种等待faceDetector.process()完成的方法。因为它 returns 一个 Task<T>,在摸索了一段时间后我发现我可以只使用 Tasks.await() 并且在将它包装在一个很好的协程中之后一切似乎都很好。我已修复此答案中的代码以反映我的最新更改。