Нажмите, чтобы настроить фокус / экспозицию в CameraX
Я хотел бы реализовать стандартный UX касания точки в предварительном просмотре, чтобы настроить точки автофокуса и автоэкспозиции в соответствии с местом, где они постукивали. Я обнаружил функцию Preview.focus(), однако она говорит, что ей нужны размеры в "координатной рамке датчика", что, как я предполагаю, не совпадает с пиксельными координатами TouchView в TextureView.
Как преобразовать сенсорные координаты предварительного просмотра TextureView в "координатную рамку сенсора", ожидаемую Preview.focus()?
Было бы замечательно, если бы этот пример был частью примера кода, так как он кажется довольно распространенным вариантом использования, которого ожидают почти все.
3 ответа
Это сообщение в блоге, написанное инженером Google, объясняет, как именно это сделать (на Kotlin).
Вот как добиться того же в Java:
private void setUpTapToFocus() {
textureView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP) {
/* Original post returns false here, but in my experience this makes
onTouch not being triggered for ACTION_UP event */
return true;
}
TextureViewMeteringPointFactory factory = new TextureViewMeteringPointFactory(textureView);
MeteringPoint point = factory.createPoint(event.getX(), event.getY());
FocusMeteringAction action = FocusMeteringAction.Builder.from(point).build();
cameraControl.startFocusAndMetering(action);
return true;
}
});
}
В cameraControl
объект можно создать следующим образом:
CameraControl cameraControl = CameraX.getCameraControl(CameraX.LensFacing.BACK);
но убедитесь, что у вас есть
implementation "androidx.camera:camera-view:1.0.0-alpha03"
внутри вашего build.gradle
зависимости.
Для справки, вот оригинальный код Kotlin из сообщения в блоге Husayn Hakeem:
private fun setUpTapToFocus() {
textureView.setOnTouchListener { _, event ->
if (event.action != MotionEvent.ACTION_UP) {
return@setOnTouchListener false
}
val factory = TextureViewMeteringPointFactory(textureView)
val point = factory.createPoint(event.x, event.y)
val action = FocusMeteringAction.Builder.from(point).build()
cameraControl.startFocusAndMetering(action)
return@setOnTouchListener true
}
}
В настоящее время это может быть достигнуто очень легко с помощью
LifecycleCameraController.setController(cameraController)
.
Из документации:
После установки контроллер будет использовать PreviewView для отображения канала предварительного просмотра камеры. Он также использует размер макета PreviewView, чтобы установить прямоугольник обрезки для всех вариантов использования, чтобы выходные данные из других вариантов использования соответствовали тому, что конечный пользователь видит в PreviewView. Он также включает такие функции, как фокусировка касанием и масштабирование.
Вот краткий пример того, как его использовать (я использую Kotlin, но то же самое и в Java).
fun startCamera(
context: Context,
lifecycleOwner: LifecycleOwner,
previewView: PreviewView
): LifecycleCameraController {
//create camera instance
val cameraController = LifecycleCameraController(context)
//start camera
cameraController.bindToLifecycle(lifecycleOwner)
//enable camera preview feed and features like tap-to-focus and pinch-to-zoom
previewView.controller = cameraController
return cameraController
//...
//cameraController.takePicture(OutputFileOptions, Executor, OnImageSavedCallback) //take picture
//cameraController.unbind() //close camera
}
Координаты TextureView не совпадают с координатами датчика. Пожалуйста, обратитесь к образцам кодов здесь (обратите внимание, что "CameraView" еще не является общедоступным в репозитории maven. Поэтому мы не рекомендуем вам использовать его сейчас). Мы понимаем, что это большая работа, поэтому команда CameraX также разрабатывает более удобную для разработчиков версию API фокусировки / измерения.
Базовый поток выглядит следующим образом:
(1) получить x, y из события касания просмотра.
(2) Рассчитайте относительную ориентацию камеры, используя ориентацию устройства и camera2 CameraCharacteristics.SENSOR_ORIENTATION. Значение представляет собой угол по часовой стрелке, на который необходимо повернуть изображение датчика, чтобы оно было вертикальным в текущей ориентации устройства.
(3) поменять местами x, y на 90 / 270 градусов и правильно поменять местами x, y по ориентации. инвертировать x для зеркального отображения (передняя камера)
(4) преобразовать в координаты датчика, используя CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE), ширину / высоту обзора.
примечание: для идентификатора камеры сейчас вы можете найти первый camera_id в mCameraManager.getCameraIdList() с правильной ориентацией объектива. однако алгоритм может быть изменен.