В рамках моего проекта ML я хочу создать обучающие данные для анализа лица нескольких людей на разных изображениях с помощью детектора лиц Google Firebase ML-Kit Библиотека распознавания лиц . Я создал очень простой класс обслуживания для инкапсуляции инициализации и запуска процесса обнаружения лиц:
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))
}
}
Функция детектор !!. DeteInImage ( FirebaseVisionImage.detectInImage ) возвращает Task , который представляет асинхронные c операции.
В функции onResume () моей функции MainActivity внутри CoroutineScope я запускаю библиотеку и начинаю перебирать изображения, сначала преобразовывая их в 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 , инкапсулирующего вызов библиотеки в задачу:
val tsk = async {
detector!!.analyzeAsync(act, uri)
}
val result = tsk.await()
и job:
val tsk = withContext(IO) {
detector!!.analyzeAsync(act, uri)
}
, но асинхронные c операции, которые я запускаю вручную, всегда выполняются только до тех пор, пока запускаются задачи Firebase, не дожидаясь завершения внутренней задачи. Я также попытался добавить различные withcontext (...) и ... launch {} вариантов внутри класса FaceDetectorService , но безрезультатно.
Я, очевидно, очень плохо знаком с kotlin сопрограммами, поэтому я думаю, что мне здесь не хватает чего-то очень базового c, но я просто не могу обернуться вокруг него.
(PS: пожалуйста не комментируйте небрежность кода, это всего лишь прототип :))