#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 . После такого переключения изображения будут поступать на другой детектор.
Обязательно закройте неиспользуемый детектор, чтобы освободить системные ресурсы.