Вы настраиваете каждый вариант использования CameraX для управления различными аспектами операций варианта использования.
Например, при фотосъемке можно задать целевое соотношение сторон и режим вспышки. Пример кода:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
Помимо параметров конфигурации, в некоторых сценариях использования предоставляются API для динамического изменения настроек после создания сценария. Информацию о конфигурации для отдельных сценариев использования можно найти в статьях Как реализовать предварительный просмотр, Как анализировать изображения и Как реализовать фотосъемку.
CameraXConfig
Для простоты CameraX имеет конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако если у вашего приложения есть особые требования или вы хотите настроить конфигурации, используйте интерфейс CameraXConfig.
С помощью CameraXConfig приложение может:
- Оптимизируйте задержку при запуске с помощью
setAvailableCameraLimiter(). - Предоставьте CameraX исполнитель приложения с помощью
setCameraExecutor(). - Замените обработчик планировщика по умолчанию на
setSchedulerHandler(). - Измените уровень детализации журнала с помощью
setMinimumLoggingLevel().
Модель использования
Ниже описано, как использовать CameraXConfig.
- Создайте объект
CameraXConfigс нужными настройками. - Реализуйте интерфейс
CameraXConfig.ProviderвApplicationи верните объектCameraXConfigвgetCameraXConfig(). - Добавьте класс
Applicationв файлAndroidManifest.xml, как описано здесь.
Например, следующий пример кода ограничивает ведение журнала CameraX только сообщениями об ошибках:
Kotlin
class CameraApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setMinimumLoggingLevel(Log.ERROR).build() } }
Сохраните локальную копию объекта CameraXConfig, если вашему приложению нужно знать конфигурацию CameraX после ее настройки.
Ограничитель камеры
При первом вызове ProcessCameraProvider.getInstance() CameraX перечисляет и запрашивает характеристики камер, доступных на устройстве. Поскольку CameraX необходимо взаимодействовать с аппаратными компонентами, этот процесс может занять значительное время для каждой камеры, особенно на устройствах низкого класса. Если ваше приложение использует только определенные камеры на устройстве, например фронтальную камеру по умолчанию, вы можете настроить CameraX так, чтобы игнорировать другие камеры. Это может сократить задержку запуска камер, которые использует ваше приложение.
Если CameraSelector, переданный в CameraXConfig.Builder.setAvailableCamerasLimiter(), отфильтровывает камеру, CameraX ведет себя так, как будто этой камеры не существует. Например, следующий код ограничивает приложение использованием только задней камеры устройства по умолчанию:
Kotlin
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }
Потоки
Многие API платформы, на которых основана библиотека CameraX, требуют блокировки межпроцессного взаимодействия (IPC) с аппаратным обеспечением, ответ от которого иногда может занимать сотни миллисекунд. Поэтому CameraX вызывает эти API только из фоновых потоков, чтобы не блокировать основной поток и сохранить плавность интерфейса. CameraX управляет этими фоновыми потоками внутри, поэтому это поведение кажется прозрачным. Однако некоторые приложения требуют строгого контроля потоков. CameraXConfig позволяет приложению задавать фоновые потоки, которые используются через CameraXConfig.Builder.setCameraExecutor() и CameraXConfig.Builder.setSchedulerHandler().
Camera Executor
Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов из этих API. Для выполнения этих задач CameraX выделяет внутренний объект Executor и управляет им.
Однако если вашему приложению требуется более строгий контроль над цепочками, используйте CameraXConfig.Builder.setCameraExecutor().
Обработчик планировщика
Обработчик планировщика используется для планирования внутренних задач с фиксированными интервалами, например для повторной попытки открыть камеру, когда она недоступна. Этот обработчик не выполняет задания, а только отправляет их исполнителю камеры. Иногда он также используется на устаревших платформах API, которым для обратных вызовов требуется Handler. В этих случаях обратные вызовы по-прежнему отправляются только непосредственно исполнителю камеры. Для выполнения этих задач CameraX выделяет внутренний объект HandlerThread и управляет им, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler().
Журналы
Ведение журнала CameraX позволяет приложениям фильтровать сообщения logcat, поскольку рекомендуется избегать подробных сообщений в рабочем коде. CameraX поддерживает четыре уровня ведения журнала – от самого подробного до самого критического:
Log.DEBUG(по умолчанию)Log.INFOLog.WARNLog.ERROR
Подробное описание этих уровней ведения журнала можно найти в документации по Android Log. Используйте CameraXConfig.Builder.setMinimumLoggingLevel(int), чтобы задать подходящий уровень ведения журнала для приложения.
Выбрать автоматически
CameraX автоматически предоставляет функции, характерные для устройства, на котором запущено приложение. Например, CameraX автоматически определяет наилучшее разрешение, если вы не указали его или если указанное вами разрешение не поддерживается. Все это обрабатывается библиотекой, что избавляет вас от необходимости писать код для конкретного устройства.
Цель CameraX – успешно инициализировать сеанс камеры. Это означает, что CameraX идет на компромисс в отношении разрешения и соотношения сторон в зависимости от возможностей устройства. Это может произойти по следующим причинам:
- Устройство не поддерживает запрошенное разрешение.
- Устройство несовместимо с сервисом. Например, устаревшие устройства могут требовать определенного разрешения экрана.
- На некоторых устройствах определенные форматы доступны только при определенном соотношении сторон.
- Устройство предпочитает ближайшее значение, кратное 16, для кодирования JPEG или видео. Дополнительную информацию вы найдете в статье
SCALER_STREAM_CONFIGURATION_MAP.
Хотя CameraX создает сеанс и управляет им, всегда проверяйте размеры возвращаемых изображений в выходных данных варианта использования в коде и корректируйте их соответствующим образом.
Вращение
По умолчанию поворот камеры соответствует повороту экрана по умолчанию во время создания сценария использования. В этом случае по умолчанию CameraX создает выходные данные, чтобы приложение соответствовало тому, что вы ожидаете увидеть в предварительном просмотре. Вы можете изменить поворот на пользовательское значение для поддержки устройств с несколькими дисплеями, передав текущую ориентацию дисплея при настройке объектов вариантов использования или динамически после их создания.
Приложение может задать целевую ориентацию с помощью настроек конфигурации. Затем оно может обновить настройки поворота с помощью методов из API вариантов использования (например, ImageAnalysis.setTargetRotation()), даже если жизненный цикл находится в состоянии выполнения. Это может быть полезно, если в приложении заблокирован режим портретной съемки и при повороте устройства не происходит реконфигурация, но для съемки или анализа фотографий необходимо знать текущую ориентацию устройства. Например, это может быть необходимо для правильной ориентации лиц при их распознавании или для установки альбомной или портретной ориентации фотографий.
Данные для снятых изображений могут храниться без информации о повороте. Данные Exif содержат информацию о повороте, чтобы приложения галереи могли показывать изображение в правильной ориентации после сохранения.
Чтобы данные предварительного просмотра отображались в правильной ориентации, вы можете использовать выходные метаданные из Preview.PreviewOutput() для создания преобразований.
В приведенном ниже примере кода показано, как задать поворот для события ориентации.
Kotlin
override fun onCreate() { val imageCapture = ImageCapture.Builder().build() val orientationEventListener = object : OrientationEventListener(this as Context) { override fun onOrientationChanged(orientation : Int) { // Monitors orientation values to determine the target rotation value val rotation : Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 in 135..224 -> Surface.ROTATION_180 in 225..314 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageCapture.targetRotation = rotation } } orientationEventListener.enable() }
Java
@Override public void onCreate() { ImageCapture imageCapture = new ImageCapture.Builder().build(); OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) { @Override public void onOrientationChanged(int orientation) { int rotation; // Monitors orientation values to determine the target rotation value if (orientation >= 45 && orientation < 135) { rotation = Surface.ROTATION_270; } else if (orientation >= 135 && orientation < 225) { rotation = Surface.ROTATION_180; } else if (orientation >= 225 && orientation < 315) { rotation = Surface.ROTATION_90; } else { rotation = Surface.ROTATION_0; } imageCapture.setTargetRotation(rotation); } }; orientationEventListener.enable(); }
В зависимости от заданного поворота каждый вариант использования либо поворачивает данные изображения напрямую, либо предоставляет метаданные поворота потребителям неповернутых данных изображения.
- Предварительный просмотр. Вывод метаданных предоставляется для того, чтобы можно было узнать поворот целевого разрешения с помощью
Preview.getTargetRotation(). - ImageAnalysis. Выводятся метаданные, чтобы координаты буфера изображения были известны относительно координат экрана.
- ImageCapture. Метаданные Exif изображения, буфер или и то, и другое изменяются, чтобы отразить настройку поворота. Измененное значение зависит от реализации HAL.
Прямоугольник кадрирования
По умолчанию прямоугольник кадрирования совпадает с прямоугольником буфера. Вы можете настроить его с помощью ViewPort и UseCaseGroup. Группируя варианты использования и настраивая область просмотра, CameraX гарантирует, что прямоугольники кадрирования всех вариантов использования в группе указывают на одну и ту же область датчика камеры.
В следующем фрагменте кода показано, как использовать эти два класса:
Kotlin
val viewPort = ViewPort.Builder(Rational(width, height), display.rotation).build() val useCaseGroup = UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
Java
ViewPort viewPort = new ViewPort.Builder( new Rational(width, height), getDisplay().getRotation()).build(); UseCaseGroup useCaseGroup = new UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build(); cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
ViewPort определяет прямоугольник буфера, видимый конечным пользователям. Затем CameraX вычисляет максимально возможный прямоугольник кадрирования на основе свойств области просмотра и прикрепленных вариантов использования. Обычно, чтобы добиться эффекта WYSIWYG, вы можете настроить область просмотра на основе варианта использования предварительного просмотра. Простой способ получить область просмотра – использовать PreviewView.
В приведенных ниже фрагментах кода показано, как получить объект ViewPort.
Kotlin
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
Java
ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();
В предыдущем примере то, что приложение получает от ImageAnalysis и ImageCapture, соответствует тому, что конечный пользователь видит в PreviewView, если для типа масштабирования PreviewView задано значение по умолчанию FILL_CENTER. После применения прямоугольника кадрирования и поворота к выходному буферу изображение из всех вариантов использования будет одинаковым, хотя, возможно, с разным разрешением. Подробнее о том, как применять информацию о преобразовании, читайте в разделе Преобразование выходных данных.
Выбор камеры
CameraX автоматически выбирает лучшую камеру для требований и сценариев использования вашего приложения. Если вы хотите использовать другое устройство, у вас есть несколько вариантов:
- Запросите фронтальную камеру по умолчанию с помощью
CameraSelector.DEFAULT_FRONT_CAMERA. - Запросите основную камеру по умолчанию с помощью
CameraSelector.DEFAULT_BACK_CAMERA. - Отфильтруйте список доступных устройств по их
CameraCharacteristicsс помощьюCameraSelector.Builder.addCameraFilter().
В примере кода ниже показано, как создать CameraSelector, чтобы повлиять на выбор устройства.
Kotlin
fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? { val cam2Infos = provider.availableCameraInfos.map { Camera2CameraInfo.from(it) }.sortedByDescending { // HARDWARE_LEVEL is Int type, with the order of: // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) } return when { cam2Infos.isNotEmpty() -> { CameraSelector.Builder() .addCameraFilter { it.filter { camInfo -> // cam2Infos[0] is either EXTERNAL or best built-in camera val thisCamId = Camera2CameraInfo.from(camInfo).cameraId thisCamId == cam2Infos[0].cameraId } }.build() } else -> null } } // create a CameraSelector for the USB camera (or highest level internal camera) val selector = selectExternalOrBestCamera(processCameraProvider) processCameraProvider.bindToLifecycle(this, selector, preview, analysis)
Выбор нескольких камер одновременно
Начиная с CameraX 1.3, вы также можете выбрать несколько камер одновременно. Например, вы можете привязать к фронтальной и основной камерам, чтобы делать фотографии или записывать видео с обеих точек зрения одновременно.
При использовании функции параллельной съемки устройство может одновременно задействовать две камеры, направленные в разные стороны, или две задние камеры. В следующем блоке кода показано, как задать две камеры при вызове bindToLifecycle и как получить оба объекта Camera из возвращенного объекта ConcurrentCamera.
Kotlin
// Build ConcurrentCameraConfig val primary = ConcurrentCamera.SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ) val secondary = ConcurrentCamera.SingleCameraConfig( secondaryCameraSelector, useCaseGroup, lifecycleOwner ) val concurrentCamera = cameraProvider.bindToLifecycle( listOf(primary, secondary) ) val primaryCamera = concurrentCamera.cameras[0] val secondaryCamera = concurrentCamera.cameras[1]
Java
// Build ConcurrentCameraConfig SingleCameraConfig primary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); SingleCameraConfig secondary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); ConcurrentCamera concurrentCamera = mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary)); Camera primaryCamera = concurrentCamera.getCameras().get(0); Camera secondaryCamera = concurrentCamera.getCameras().get(1);
Разрешение камеры
Вы можете разрешить CameraX устанавливать разрешение изображения на основе комбинации возможностей устройства, поддерживаемого устройством уровня оборудования, варианта использования и предоставленного соотношения сторон. В некоторых случаях можно задать целевое разрешение или соотношение сторон.
Автоматическое разрешение
CameraX может автоматически определить оптимальные настройки разрешения на основе вариантов использования, указанных в cameraProcessProvider.bindToLifecycle(). По возможности указывайте все варианты использования, которые должны выполняться одновременно в одном сеансе, в одном вызове bindToLifecycle(). CameraX определяет разрешения на основе набора связанных сценариев использования, учитывая поддерживаемый уровень аппаратного обеспечения устройства и его особенности (например, если устройство превышает или не соответствует доступным конфигурациям потока).
Это позволяет запускать приложение на самых разных устройствах, сводя к минимуму количество путей выполнения, зависящих от устройства.
По умолчанию для фотосъемки и анализа изображений используется соотношение сторон 4:3.
Для сценариев использования можно настроить соотношение сторон, чтобы приложение могло указать желаемое соотношение сторон на основе дизайна пользовательского интерфейса. Выходные данные CameraX создаются так, чтобы максимально соответствовать запрошенным соотношениям сторон, поддерживаемым устройством. Если точного совпадения нет, выбирается разрешение, которое соответствует наибольшему количеству условий. Таким образом, приложение диктует, как камера появляется в приложении, а CameraX определяет наилучшие настройки разрешения камеры, чтобы удовлетворить это на разных устройствах.
Например, приложение может:
- Укажите целевое соотношение сторон 4:3 или 16:9 для варианта использования
- Укажите пользовательское разрешение, для которого CameraX попытается найти наиболее близкое совпадение.
- Укажите соотношение сторон для кадрирования (
ImageCapture)
CameraX автоматически выбирает разрешение для внутренних поверхностей Camera2. В таблице ниже приведены доступные разрешения.
| Пример использования | Разрешение внутренних поверхностей | Разрешение выходных данных |
|---|---|---|
| Предпросмотр | Соотношение сторон. Разрешение, которое лучше всего подходит для целевого устройства. | Разрешение внутреннего экрана. Метаданные предоставляются для того, чтобы View мог обрезать, масштабировать и поворачивать изображение для целевого соотношения сторон. |
| Разрешение по умолчанию. Самое высокое разрешение предварительного просмотра или самое высокое разрешение, предпочитаемое устройством, которое соответствует соотношению сторон предварительного просмотра. | ||
| Максимальное разрешение. Размер предварительного просмотра, который лучше всего соответствует разрешению экрана устройства, или 1080p (1920 x 1080) – в зависимости от того, что меньше. | ||
| Анализ изображений | Соотношение сторон. Разрешение, которое лучше всего подходит для целевого устройства. | Разрешение внутреннего экрана. |
| Разрешение по умолчанию. По умолчанию целевое разрешение составляет 640 x 480. Если вы измените и целевое разрешение, и соотношение сторон, то получите наилучшее поддерживаемое разрешение. | ||
Максимальное разрешение. Максимальное разрешение выходного изображения камеры в формате YUV_420_888, полученное из StreamConfigurationMap.getOutputSizes().
По умолчанию целевое разрешение составляет 640 x 480. Если вам нужно более высокое разрешение, используйте setTargetResolution() и setTargetAspectRatio(), чтобы получить наиболее близкое из поддерживаемых разрешений.
|
||
| Съемка фото | Соотношение сторон. Выберите соотношение сторон, которое лучше всего подходит для вашего контента. | Разрешение внутреннего экрана. |
| Разрешение по умолчанию. Максимально доступное разрешение или максимальное разрешение, предпочитаемое устройством, которое соответствует соотношению сторон ImageCapture. | ||
Максимальное разрешение. Максимальное разрешение выходного изображения в формате JPEG. Для этого используйте StreamConfigurationMap.getOutputSizes().
|
Укажите разрешение
Вы можете задать определенные разрешения при создании вариантов использования с помощью метода setTargetResolution(Size resolution), как показано в следующем примере кода:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
Нельзя одновременно задать целевое соотношение сторон и целевое разрешение. В противном случае при создании объекта конфигурации будет выдаваться ошибка IllegalArgumentException.
Выразите разрешение Size в системе координат после поворота поддерживаемых размеров на целевой угол. Например, для устройства с вертикальной ориентацией по умолчанию, запрашивающего вертикальное изображение, можно указать размер 480 x 640, а для того же устройства, повернутого на 90 градусов и запрашивающего горизонтальное изображение, – 640 x 480.
Целевое разрешение определяет минимальное разрешение изображения. Фактическое разрешение изображения – это ближайшее доступное разрешение, которое не меньше целевого. Оно определяется реализацией камеры.
Если же разрешения, равного целевому или превышающего его, нет, выбирается ближайшее доступное разрешение, которое меньше целевого. Разрешения с тем же соотношением сторон, что и у предоставленного Size, имеют более высокий приоритет, чем разрешения с другими соотношениями сторон.
CameraX применяет наиболее подходящее разрешение на основе запросов. Если основная задача – соблюсти соотношение сторон, укажите только setTargetAspectRatio, и CameraX определит подходящее разрешение в зависимости от устройства.
Если основная потребность приложения – указать разрешение, чтобы сделать обработку изображений более эффективной (например, маленькое или среднее изображение в зависимости от возможностей обработки устройства), используйте setTargetResolution(Size resolution).
Если вашему приложению требуется точное разрешение, посмотрите таблицу в разделе createCaptureSession(), чтобы узнать, какие максимальные разрешения поддерживаются каждым уровнем аппаратного обеспечения. Чтобы узнать, какие разрешения поддерживает текущее устройство, ознакомьтесь с разделом StreamConfigurationMap.getOutputSizes(int).
Если приложение работает на устройстве с Android 10 или более поздней версии, вы можете использовать isSessionConfigurationSupported(), чтобы проверить определенный SessionConfiguration.
Управление выводом камеры
Помимо настройки вывода камеры для каждого отдельного случая использования, CameraX также реализует следующие интерфейсы для поддержки операций камеры, общих для всех связанных случаев использования:
CameraControlпозволяет настраивать общие функции камеры.CameraInfoпозволяет запрашивать состояния этих распространенных функций камеры.
Ниже перечислены функции камеры, поддерживаемые CameraControl.
- Масштаб
- Фонарик, включая яркость фонарика на поддерживаемых устройствах.
- Усиление при слабом освещении на поддерживаемых устройствах.
- Фокусировка и экспозамер (нажатие для фокусировки)
- Экспокоррекция
Получение экземпляров CameraControl и CameraInfo
Получите экземпляры CameraControl и CameraInfo с помощью объекта Camera, возвращенного методом ProcessCameraProvider.bindToLifecycle().
Пример кода:
Kotlin
val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo
Java
Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview);
// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl();
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo();
Например, после вызова bindToLifecycle() можно отправить запрос на изменение масштаба и другие операции CameraControl. После остановки или уничтожения действия, используемого для привязки экземпляра камеры, CameraControl больше не может выполнять операции и возвращает ошибку ListenableFuture.
Масштаб
В CameraControl есть два метода для изменения уровня масштабирования:
setZoomRatio()– устанавливает масштаб по коэффициенту масштабирования.Соотношение должно быть в диапазоне от
CameraInfo.getZoomState().getValue().getMinZoomRatio()доCameraInfo.getZoomState().getValue().getMaxZoomRatio(). В противном случае функция возвращает ошибкуListenableFuture.setLinearZoom()задает текущий масштаб с линейным значением от 0 до 1,0.Преимущество линейного масштабирования заключается в том, что оно позволяет масштабировать поле зрения (FOV) при изменении масштаба. Поэтому он идеально подходит для использования с представлением
Slider.
CameraInfo.getZoomState() возвращает LiveData текущего состояния масштабирования. Значение меняется при инициализации камеры или при установке уровня масштабирования с помощью setZoomRatio() или setLinearZoom(). Вызов любого из этих методов задает значения, поддерживающие ZoomState.getZoomRatio() и ZoomState.getLinearZoom().
Это полезно, если вы хотите показывать текст с коэффициентом масштабирования рядом с ползунком.
Просто наблюдайте за ZoomState LiveData, чтобы обновлять оба значения без необходимости конвертации.
Объект ListenableFuture, возвращаемый обоими API, позволяет приложениям получать уведомления о завершении повторяющегося запроса с указанным значением масштаба. Кроме того, если вы зададите новое значение масштаба, пока предыдущая операция ещё выполняется, то ListenableFuture предыдущей операции масштабирования сразу же завершится ошибкой.
Факел
CameraControl.enableTorch(boolean)
включает или выключает фонарик.
CameraInfo.getTorchState() можно использовать для запроса текущего состояния фонарика. Чтобы узнать, доступна ли вспышка, проверьте значение, возвращаемое методом CameraInfo.hasFlashUnit(). В противном случае вызов CameraControl.enableTorch(boolean) приведет к тому, что возвращенный ListenableFuture немедленно завершится с ошибкой, а для состояния фонарика будет установлено значение TorchState.OFF.
Если фонарик включен, он будет работать во время съемки фото и видео независимо от режима вспышки. Режим вспышки, заданный с помощью ImageCapture.setFlashMode(), работает, только когда фонарик отключен.
Фокус и экспозамер
CameraControl.startFocusAndMetering() запускает автофокусировку (AF), автоэкспозицию (AE) и автоматический баланс белого (AWB), устанавливая области замера на основе заданного FocusMeteringAction. Этот метод можно использовать для реализации функции фокусировки по нажатию в приложениях камеры.
MeteringPoint
Сначала создайте MeteringPoint с помощью MeteringPointFactory.createPoint(float x, float y, float
size).
MeteringPoint представляет собой одну точку на Surface камеры. Оно хранится в нормализованной форме, чтобы его можно было преобразовать в координаты датчика для указания областей автофокусировки, автоэкспозиции и автоматического баланса белого.
Размер MeteringPoint может быть от 0 до 1.По умолчанию он равен 0, 15f. При вызове MeteringPointFactory.createPoint(float x, float y, float
size) CameraX создает прямоугольную область с центром в (x, y) для предоставленного size.
В следующем примере кода показано, как создать объект MeteringPoint:
// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_UP) {
val meteringPoint = previewView.meteringPointFactory
.createPoint(motionEvent.x, motionEvent.y)
// Build and start a FocusMeteringAction with meteringPoint.
view.performClick()
}
true
}
// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used
// for preview. Note that if the preview is scaled or cropped in the View,
// it's the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val displayFactory = DisplayOrientedMeteringPointFactory(
surfaceView.display,
camera.cameraInfo,
surfaceView.width.toFloat(),
surfaceView.height.toFloat()
)
// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val surfaceFactory = SurfaceOrientedMeteringPointFactory(
imageWidth.toFloat(),
imageHeight.toFloat(),
imageAnalysis
)
startFocusAndMetering и FocusMeteringAction
Чтобы вызвать startFocusAndMetering(), приложения должны создать FocusMeteringAction, состоящий из одного или нескольких объектов MeteringPoint с необязательными комбинациями режимов тарификации из FLAG_AF, FLAG_AE и FLAG_AWB. Это показано в следующем коде:
// meteringPointFactory is any MeteringPointFactory from the previous example.
val meteringPoint1 = meteringPointFactory.createPoint(x1, y1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
// Optionally add meteringPoint2 for AF/AE.
.addPoint(meteringPoint2, FLAG_AF or FLAG_AE)
// The action is canceled in 3 seconds (if not set, default is 5s).
.setAutoCancelDuration(3, TimeUnit.SECONDS)
.build()
val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering
// result.
result.addListener({
// result.get().isFocusSuccessful returns if the auto focus is successful
// or not.
}, ContextCompat.getMainExecutor(this))
Как показано в коде выше, startFocusAndMetering() принимает FocusMeteringAction, состоящий из одного MeteringPoint для регионов замера AF/AE/AWB и другого MeteringPoint только для AF и AE.
Поскольку не все устройства поддерживают автофокус, автоэкспозицию, автоматический баланс белого и несколько регионов, CameraX выполняет FocusMeteringAction по мере возможности. CameraX использует максимальное количество поддерживаемых объектов MeteringPoint в том порядке, в котором были добавлены точки. Все точки, добавленные после достижения максимального количества, игнорируются. Например, если в FocusMeteringAction передано три объекта MeteringPoint, а платформа поддерживает только два, будут использованы только первые две точки. Последний параметр MeteringPoint игнорируется CameraX.
Экспокоррекция
Экспокоррекция полезна, когда приложениям необходимо точно настроить экспозиционное число (EV) за пределами результата автоматической экспозиции (AE). Значения экспокоррекции объединяются следующим образом, чтобы определить необходимую экспозицию для текущих условий съемки:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
В CameraX есть функция CameraControl.setExposureCompensationIndex(), которая позволяет задать экспокоррекцию в виде индекса.
Положительные значения делают изображение ярче, а отрицательные – темнее. Приложения могут запрашивать поддерживаемый диапазон с помощью ExposureState.getExposureCompensationRange().
Если значение поддерживается, возвращаемый объект ListenableFuture завершается, когда значение успешно включается в запрос на захват. Если указанный индекс выходит за пределы поддерживаемого диапазона, setExposureCompensationIndex() приводит к немедленному завершению возвращаемого объекта ListenableFuture с неудачным результатом.
CameraX сохраняет только последний невыполненный запрос setExposureCompensationIndex(). Если вы вызовете функцию снова до того, как CameraX применит предыдущий запрос, CameraX отменит предыдущий запрос.
В следующем фрагменте кода задается индекс экспокоррекции и регистрируется обратный вызов для момента, когда запрос на изменение экспозиции будет выполнен:
camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
.addListener({
// Get the current exposure compensation index, it might be
// different from the asked value in case this request was
// canceled by a newer setting request.
val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
// Handle the updated exposure compensation index.
}, mainExecutor)
CameraInfo.getExposureState()извлекает текущийExposureState, включая:- Поддержка управления экспокоррекцией.
- Текущий индекс экспокоррекции.
- Диапазон индексов экспокоррекции.
- Шаг экспокоррекции, используемый при расчете значения экспокоррекции.
Например, следующий код инициализирует настройки для экспозиции SeekBar с текущими значениями ExposureState. Поскольку значение прогресса SeekBar начинается с 0, код смещает его на нижнюю границу диапазона экспокоррекции:
val exposureState = camera.cameraInfo.exposureState
val range = exposureState.exposureCompensationRange
binding.seekBar.apply {
isEnabled = exposureState.isExposureCompensationSupported
max = range.upper - range.lower
progress = exposureState.exposureCompensationIndex - range.lower
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
camera.cameraControl.setExposureCompensationIndex(progress + range.lower)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
Дополнительные ресурсы
Чтобы узнать больше о CameraX, ознакомьтесь со следующими дополнительными ресурсами.
Практическая работа
Пример кода
Сообщество разработчиков
Группа обсуждения Android CameraX