Переключение между различными моделями ML, которые выполняются на камере в kotlin во время выполнения

#android #kotlin #camera #google-mlkit

#Android #kotlin #камера #google-mlkit

Вопрос:

Я пытаюсь создать приложение, позволяющее пользователю переключаться между различными ML-моделями на лету. Прямо сейчас у меня есть простое приложение, которое может запускать camera без модели и camera с моделью обнаружения объекта. В конечном приложении будет пять таких моделей.

Это мой Home Activity

 class HomeActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks {
private val rqSpeechRec = 102
private var tts: TextToSpeech? = null
private lateinit var binding: ActivityHomeBinding
private lateinit var objectDetector: ObjectDetector
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Binding ViewData
    binding = ActivityHomeBinding.inflate(layoutInflater)
    setContentView(binding.root)

    supportActionBar?.hide() // Hiding App bar

    requestCameraPermission()   // Requesting Camera Permission
}
 

Это моя Request Camera Permission функция.

 private fun requestCameraPermission() {
    speakOut("Allow Eynetic to access the camera to take photos or videos.")

    EasyPermissions.requestPermissions(
        this,
        "This app can not work without camera.",
        Constants.PERMISSION_CAMERA_REQUEST_CODE,
        permission.CAMERA
    )
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}

override fun onPermissionsDenied(requestCode: Int, perms: List<String>) {
    speakOut("Permissions Denied.")

    if (EasyPermissions.somePermissionDenied(this, perms.first()))
        SettingsDialog.Builder(this).build()
            .show()  // If permissions are permanently denied show settings.
    else 
        requestCameraPermission()
}

override fun onPermissionsGranted(requestCode: Int, perms: List<String>) {
    cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    cameraProviderFuture.addListener({
        startCamera(cameraProviderFuture.get())
    }, ContextCompat.getMainExecutor(this))

    // Introduction
    val intro =
        "Welcome to Eyenetic. Please activate any module through voice command.The nodules are obstacle detection, scene recognition, currency detection, human object interaction and human human interaction."
    speakOut(intro)

    allButtons()
}
 

Я только addListener cameraProviderFuture тогда, когда разрешение предоставлено. И когда разрешение предоставлено, я запускаю камеру без запущенной модели. Обратите внимание, когда приложение открывается каждый раз, ни одна модель не будет запущена.

 @SuppressLint("UnsafeOptInUsageError")
private fun startCamera(cameraProvider: ProcessCameraProvider) {
    val preview = Preview.Builder().build()

    val cameraSelector =
        CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

    preview.setSurfaceProvider(binding.previewView.surfaceProvider)

    cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)

}
 

И код для выборки Object-Detection модели.

 private fun readyObjectDetectionModel()
{
    val localModel = LocalModel.Builder().setAbsoluteFilePath("object_detection.tflite").build()
    val customObjectDetectionOptions = CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .build()

    objectDetector = ObjectDetection.getClient(customObjectDetectionOptions)
}
 

Code for Object-Detection

     @SuppressLint("UnsafeOptInUsageError")
private fun startObjectDetection(cameraProvider: ProcessCameraProvider) {
    val preview = Preview.Builder().build()

    val cameraSelector =
        CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

    preview.setSurfaceProvider(binding.previewView.surfaceProvider)
    
    val imageAnalysis = ImageAnalysis.Builder().setTargetResolution(Size(1280, 720))
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build()

    imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this),
        {imageProxy ->
            val rotationDegrees =imageProxy.imageInfo.rotationDegrees
            val image = imageProxy.image

            if (image != null) {
                val processImage = InputImage.fromMediaImage(image, rotationDegrees)

                objectDetector.process(processImage)
                    .addOnSuccessListener {
                        imageProxy.close()
                    }.addOnFailureListener{
                        imageProxy.close()
                    }
            }
    })
    cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
}
 

Я могу запускать оба кода для camera, вызывая каждый из них по очереди и комментируя другой. Но я сделал разные кнопки для запуска разных моделей. Что я должен делать перед вызовом каждого метода.

Прямо сейчас единственный способ, о котором я могу думать, — это отменить предыдущий жизненный цикл и создать новый. Но как насчет избыточного кода, такого как

 val preview = Preview.Builder().build()

val cameraSelector =
    CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

preview.setSurfaceProvider(binding.previewView.surfaceProvider)
 

Является ли их способ изменить usecase только для текущего bindedlifecycle?

Должен ли я создавать camerafragment и переключаться между разными фрагментами?

Любые советы по лучшему решению этой проблемы.

Ответ №1:

Мне приходит в голову одна идея — создать ImageAnalysis.Подкласс Analyzer, который может обрабатывать различные функции ML (в ваших моделях word — ML).

В этом случае вы настраиваете вариант использования imageAnalysis только один раз, и камера будет продолжать передавать изображения в ваш анализатор.

В вашем анализаторе вы можете создать некоторые API, такие как switchTo(MLFeatureName name) . При нажатии кнопки пользовательского интерфейса вы можете вызвать метод switchTo . После такого переключения изображения будут поступать на другой детектор.

Обязательно закройте неиспользуемый детектор, чтобы освободить системные ресурсы.