android - Firebase ML-Kit 并行运行多个人脸检测 session

标签 android firebase kotlin parallel-processing kotlin-coroutines

作为我的 ML 项目的一部分,我想生成训练数据,用于使用面部检测器 Google Firebase ML-Kit Face detection library 分析来自不同图像的多个人的面部。 .我创建了一个非常简单的服务类来封装初始化并启动人脸检测的过程:

class FaceDetectorService(private val act: MainActivity)  {

private var opts: FirebaseVisionFaceDetectorOptions? = null
private var detector: FirebaseVisionFaceDetector? = null


init {
    FirebaseApp.initializeApp(act)

    opts = FirebaseVisionFaceDetectorOptions.Builder()
        .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
        .setLandmarkMode(FirebaseVisionFaceDetectorOptions.NO_LANDMARKS)
        .setClassificationMode(FirebaseVisionFaceDetectorOptions.NO_CLASSIFICATIONS)
        .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS)
        .build()

    detector = FirebaseVision.getInstance()
        .getVisionFaceDetector(opts!!)
}


suspend fun analyzeAsync(cont: Context, uri: Uri) : Pair<String, Task<List<FirebaseVisionFace>>> {
    val image = FirebaseVisionImage.fromFilePath(cont, uri)

    // this is for the UI thread
    withContext(Main){
        act.addItemToAnalyze(uri.lastPathSegment)
    }

    // return the filename too
    return Pair(uri.lastPathSegment, detector!!.detectInImage(image))
}

}

函数检测器!!.detectInImage (FirebaseVisionImage.detectInImage ) 返回一个 Task表示异步操作。

在我的 MainActivity 的 onResume() 函数中,在 CoroutineScope 内,我启动 lib 并开始迭代图像,首先将它们转换为 Uri,然后将其传递给面部检测器:
CoroutineScope(IO).launch {
        val executeTime = measureTimeMillis {
            for (uri in uris){

                    val fileNameUnderAnalysis = uri.lastPathSegment
                    //val tsk = withContext(IO) {
                    //    detector!!.analyzeAsync(act, uri)
                    //}
                val tsk = detector!!.analyzeAsync(act, uri)
                tsk.second.addOnCompleteListener { task ->
                    if (task.isSuccessful && task.result!!.isNotEmpty()) {
                        try {
                           // my best
                        } catch (e: IllegalArgumentException) {
                           // fire
                        }
                    } else if (task.result!!.isEmpty()) {
                       // not today :(
                    }

                }

                tsk.second.addOnFailureListener { e ->
                    // on error
                }
            }

        }
        Log.i("MILLIS", executeTime.toString())
    }

现在,虽然我的实现同时运行(即同时开始),但我真正想要的是并行运行它们(同时运行取决于线程数,在我的情况下是 4 在模拟器上),所以我的目标是获取可用线程的数量,并为每个线程分配一个分析操作,将执行时间分成四等份。

到目前为止,我尝试的是,在 CoroutineScope(IO).launch block 内,将对库的调用封装在一个任务中:
val tsk = async {
    detector!!.analyzeAsync(act, uri)
}

val result = tsk.await()

和一份工作:
val tsk = withContext(IO) {
   detector!!.analyzeAsync(act, uri)
}

但是我手动启动的异步操作总是只有在 Firebase 任务启动时才会持续,而不是等待内部任务运行完成。我还尝试在 FaceDetectorService 类中添加不同的 withcontext(...) 和 ...launch {} 变体,但无济于事。

我显然对 kotlin 协程很陌生,所以我认为我在这里遗漏了一些非常基本的东西,但我无法理解它。

(PS:请不要评论代码的草率,这只是一个原型(prototype):))

最佳答案

analyzeAsync()suspend fun并且还返回一个类似 future 的 Task目的。相反,它应该返回 Task.await() 的结果,您可以通过分解出您的addOnCompleteListener 轻松实现。称呼:

suspend fun <T> Task<T>.await(): T = suspendCancellableCoroutine { cont ->
    addOnCompleteListener {
        val e = exception
        when {
            e != null -> cont.resumeWithException(e)
            isCanceled -> cont.cancel()
            else -> cont.resume(result)
        }
    }
}

kotlinx-coroutines-play-services 中提供了优化版本。模块)。

由于人脸检测 API 已经是异步的,这意味着您调用它的线程是无关紧要的,它会在内部处理其计算资源。因此,您无需在 IO 中启动调度员,您可以使用Main并在任何时候自由地做 GUI 工作,没有上下文切换。

至于你的主要观点:我找不到明确的细节,但很可能一个人脸检测调用已经使用了所有可用的 CPU,甚至现在出现在智能手机中的专用 ML 电路,这意味着没有什么可做的从外部并行化。只需一个人脸检测请求就已经让所有资源都在处理它。

关于android - Firebase ML-Kit 并行运行多个人脸检测 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60583180/

相关文章:

android - PRIMARY KEY 必须是调用更新时的唯一异常

android - 实现横向导航时应用程序崩溃

android - 警告数据库已被锁定为 0 :00:10. 000000。确保在事务期间始终使用事务对象进行数据库操作

javascript - Firebase Realtime Database - on event - Javascript Deltasnapshot 和 Android Java DataSnapshot 的区别

android - 将应用程序设置为与 UI 自动程序的默认按钮交互

kotlin - Kotlin装饰器/注释,以减少样板

Android 4.4 中断 jquery on(touchstart/touchmove/touchend)?

javascript - setState() 之后无法将 react 状态传递给子进程?

java - 打印源文件的当前内容(在 Android 上使用 Java 或 Kotlin)

android - 使用多个 Firebase 项目终止应用程序时未收到 FCM 通知