Варианты конфигурации

Вы настраиваете каждый вариант использования CameraX для управления различными аспектами операций варианта использования.

Например, при фотосъемке можно задать целевое соотношение сторон и режим вспышки. Пример кода:

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Помимо параметров конфигурации, некоторые варианты использования предоставляют API для динамического изменения настроек после создания варианта использования. Информацию о конфигурации для отдельных сценариев использования можно найти в разделах Реализация предварительного просмотра, Анализ изображений и Фотосъемка.

CameraXConfig

Для простоты CameraX имеет конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако если у вашего приложения есть особые требования или вы хотите настроить эти конфигурации, используйте интерфейс CameraXConfig.

С помощью CameraXConfig приложение может:

Модель использования

Ниже описано, как использовать CameraXConfig.

  1. Создайте объект CameraXConfig с нужными настройками.
  2. Реализуйте интерфейс CameraXConfig.Provider в Application и верните объект CameraXConfig в getCameraXConfig().
  3. Добавьте класс Application в файл AndroidManifest.xml, как описано здесь.

Например, следующий пример кода ограничивает ведение журнала CameraX только сообщениями об ошибках:

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

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().

Исполнитель камеры

Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов из этих API. Для выполнения этих задач CameraX выделяет внутренний объект Executor и управляет им. Однако если вашему приложению требуется более строгий контроль над цепочками, используйте CameraXConfig.Builder.setCameraExecutor().

Обработчик планировщика

Обработчик планировщика используется для планирования внутренних задач с фиксированными интервалами, например для повторной попытки открыть камеру, когда она недоступна. Этот обработчик не выполняет задания, а только отправляет их исполнителю камеры. Иногда он также используется на устаревших платформах API, которым для обратных вызовов требуется Handler. В этих случаях обратные вызовы по-прежнему отправляются непосредственно исполнителю камеры. Для выполнения этих задач CameraX выделяет внутренний объект HandlerThread и управляет им, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler().

Журналы

Ведение журнала CameraX позволяет приложениям фильтровать сообщения logcat, поскольку рекомендуется избегать подробных сообщений в рабочем коде. CameraX поддерживает четыре уровня ведения журнала – от самого подробного до самого серьезного:

  • Log.DEBUG (по умолчанию)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Подробное описание этих уровней ведения журнала можно найти в документации по Android Log. Чтобы задать подходящий уровень ведения журнала для приложения, используйте CameraXConfig.Builder.setMinimumLoggingLevel(int).

Выбрать автоматически

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

Цель CameraX – успешно инициализировать сеанс камеры. Это означает, что CameraX идет на компромисс в отношении разрешения и соотношения сторон в зависимости от возможностей устройства. Это может произойти по следующим причинам:

  • Устройство не поддерживает запрошенное разрешение.
  • Устройство несовместимо с другими устройствами. Например, устаревшим устройствам для правильной работы требуется определенное разрешение.
  • На некоторых устройствах определенные форматы доступны только при определенном соотношении сторон.
  • Устройство предпочитает ближайшее значение, кратное 16, для кодирования JPEG или видео. Дополнительную информацию вы найдете в статье SCALER_STREAM_CONFIGURATION_MAP.

Хотя CameraX создает сеанс и управляет им, всегда проверяйте размеры возвращаемых изображений в выходных данных варианта использования в коде и корректируйте их соответствующим образом.

Вращение

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

Приложение может задать целевую ориентацию с помощью настроек конфигурации. Затем оно может обновить настройки поворота с помощью методов из API вариантов использования (например, ImageAnalysis.setTargetRotation()), даже если жизненный цикл находится в состоянии выполнения. Это может быть полезно, если в приложении заблокирован режим портретной съемки и при повороте устройства не происходит перенастройка, но для фотосъемки или анализа необходимо знать текущую ориентацию устройства. Например, это может быть необходимо для правильной ориентации лиц при их распознавании или для установки альбомной или портретной ориентации фотографий.

Данные для снятых изображений могут храниться без информации о повороте. Данные Exif содержат информацию о повороте, чтобы приложения галереи могли показывать изображение в правильной ориентации после сохранения.

В приведенном ниже примере кода показано, как задать поворот для события ориентации.

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()
}

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

  • Предварительный просмотр. Выводятся метаданные, чтобы можно было узнать поворот целевого разрешения с помощью Preview.getTargetRotation(). Чтобы показывать данные предварительного просмотра в правильной ориентации, вы можете использовать метаданные, полученные из SurfaceRequest.TransformationInfo, для создания преобразований.
  • ImageAnalysis. Выводятся метаданные, чтобы координаты буфера изображения были известны относительно координат экрана.
  • ImageCapture. Метаданные Exif изображения, буфер или и то, и другое изменяются, чтобы отразить настройку поворота. Измененное значение зависит от реализации HAL.

Прямоугольник кадрирования

По умолчанию прямоугольник кадрирования – это полный прямоугольник буфера. Вы можете настроить его с помощью ViewPort и UseCaseGroup. Группируя варианты использования и настраивая область просмотра, CameraX гарантирует, что прямоугольники кадрирования всех вариантов использования в группе указывают на одну и ту же область датчика камеры.

В следующем фрагменте кода показано, как использовать эти два класса:

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)

ViewPort определяет прямоугольник буфера, видимый конечным пользователям. Затем CameraX вычисляет максимально возможный прямоугольник кадрирования на основе свойств области просмотра и прикрепленных вариантов использования. Обычно, чтобы добиться эффекта WYSIWYG, вы можете настроить область просмотра в зависимости от варианта использования предварительного просмотра. Простой способ получить область просмотра – использовать PreviewView.

В приведенных ниже фрагментах кода показано, как получить объект ViewPort.

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

В предыдущем примере то, что приложение получает от ImageAnalysis и ImageCapture, соответствует тому, что конечный пользователь видит в PreviewView, при условии, что для типа масштаба PreviewView установлено значение по умолчанию FILL_CENTER. После применения прямоугольника кадрирования и поворота к выходному буферу изображение из всех вариантов использования будет одинаковым, хотя, возможно, с разным разрешением. Подробнее о том, как применять информацию о преобразовании…

Выбор камеры

CameraX автоматически выбирает лучшую камеру для требований и сценариев использования вашего приложения. Если вы хотите использовать другое устройство, у вас есть несколько вариантов:

В следующем примере кода показано, как создать CameraSelector, чтобы повлиять на выбор устройства:

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) ?: return
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

Выбор нескольких камер одновременно

Начиная с CameraX 1.3, вы также можете выбирать несколько камер одновременно. Например, вы можете привязать фронтальную и основную камеры, чтобы одновременно делать снимки или записывать видео с обеих.

При использовании функции одновременной съемки устройство может одновременно использовать две камеры с объективами, направленными в разные стороны, или две задние камеры. В следующем блоке кода показано, как задать две камеры при вызове bindToLifecycle и как получить оба объекта Camera из возвращенного объекта ConcurrentCamera.

// 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]

Разрешение камеры

Вы можете разрешить 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), как показано в следующем примере кода:

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(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(). Пример кода:

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

Например, после вызова 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.

Поскольку не все устройства поддерживают AF/AE/AWB и несколько регионов, 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, ознакомьтесь со следующими дополнительными ресурсами.

Практическая работа

  • Начало работы с CameraX
  • Пример кода

  • Примеры приложений CameraX
  • Сообщество разработчиков

    Группа обсуждения Android CameraX