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

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

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

Котлин

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 :

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

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

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

Вращение

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

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

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

Для отображения данных предварительного просмотра с правильной ориентацией можно использовать метаданные, полученные с помощью Preview.PreviewOutput() для создания преобразований.

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

Котлин

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() .
  • Анализ изображений : Предоставляются метаданные, позволяющие определить координаты буфера изображения относительно координат дисплея.
  • 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)

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 :

Котлин

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 для влияния на выбор устройства:

Котлин

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, вы также можете одновременно выбирать несколько камер. Например, вы можете привязать переднюю и заднюю камеры для одновременной фото- или видеосъемки с обеих сторон.

При использовании функции Concurrent Camera устройство может одновременно управлять двумя камерами с разными объективами или двумя задними камерами. В следующем блоке кода показано, как установить две камеры при вызове 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]

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. В следующей таблице показаны разрешения:

Вариант использования Разрешение внутренней поверхности Разрешение выходных данных
Предварительный просмотр Соотношение сторон: разрешение, которое наилучшим образом соответствует целевому объекту и условиям съемки. Внутреннее разрешение поверхности. Предоставляются метаданные, позволяющие обрезать, масштабировать и поворачивать изображение в соответствии с целевым соотношением сторон.
Разрешение по умолчанию: максимальное разрешение предварительного просмотра или максимальное разрешение, предпочитаемое устройством, соответствующее соотношению сторон предварительного просмотра.
Максимальное разрешение: Размер предварительного просмотра, который соответствует оптимальному разрешению экрана устройства или разрешению 1080p (1920x1080), в зависимости от того, какое из них меньше.
Анализ изображений Соотношение сторон: разрешение, которое наилучшим образом соответствует целевому объекту и условиям съемки. Разрешение внутренней поверхности.
Разрешение по умолчанию: По умолчанию установлено разрешение 640x480. Изменение как целевого разрешения, так и соответствующего соотношения сторон позволяет получить наилучшее поддерживаемое разрешение.
Максимальное разрешение: максимальное выходное разрешение камеры в формате YUV_420_888, получаемое из StreamConfigurationMap.getOutputSizes() . Целевое разрешение по умолчанию установлено на 640x480, поэтому, если вам нужно разрешение больше 640x480, необходимо использовать setTargetResolution() и setTargetAspectRatio() , чтобы получить ближайшее из поддерживаемых разрешений.
Захват изображения Соотношение сторон: Соотношение сторон, наилучшим образом соответствующее обстановке. Разрешение внутренней поверхности.
Разрешение по умолчанию: максимально доступное разрешение или максимальное разрешение, предпочитаемое устройством, соответствующее соотношению сторон ImageCapture.
Максимальное разрешение: Максимальное выходное разрешение камеры в формате JPEG. Для получения этого значения используйте StreamConfigurationMap.getOutputSizes() .

Укажите разрешение

При построении сценариев использования можно задать конкретное разрешение с помощью метода setTargetResolution(Size resolution) , как показано в следующем примере кода:

Котлин

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Нельзя одновременно задать целевое соотношение сторон и целевое разрешение для одного и того же сценария использования. В противном случае при создании объекта конфигурации возникнет исключение IllegalArgumentException .

Укажите Size разрешения в системе координат после поворота поддерживаемых размеров на заданный угол. Например, устройство с портретной ориентацией и заданным углом поворота может указать разрешение 480x640, а устройство, повернутое на 90 градусов и предназначенное для альбомной ориентации, — 640x480.

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

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

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

Например, вы можете отправлять операции масштабирования и другие операции CameraControl после вызова bindToLifecycle() . После остановки или уничтожения активности, используемой для привязки экземпляра камеры, 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() . Это полезно, если вы хотите отображать текст о соотношении масштабирования рядом с ползунком. Просто отслеживайте LiveData объекта ZoomState , чтобы обновить оба значения без необходимости преобразования.

Объект ListenableFuture возвращаемый обоими API, предоставляет приложениям возможность получать уведомления о завершении повторяющегося запроса с указанным значением масштабирования. Кроме того, если вы установите новое значение масштабирования, пока предыдущая операция еще выполняется, ListenableFuture предыдущей операции масштабирования немедленно завершится с ошибкой.

Факел

CameraControl.enableTorch(boolean) включает или выключает фонарик (также известный как вспышка).

CameraInfo.getTorchState() можно использовать для запроса текущего состояния фонарика. Вы можете проверить значение, возвращаемое методом CameraInfo.hasFlashUnit() , чтобы определить, доступен ли фонарик. Если нет, вызов CameraControl.enableTorch(boolean) приводит к немедленному завершению возвращаемого ListenableFuture с ошибкой и устанавливает состояние фонарика в TorchState.OFF .

Когда фонарик включен, он остается включенным во время фото- и видеосъемки независимо от настройки flashMode. В ImageCapture flashMode работает только тогда, когда фонарик выключен.

Фокусировка и замер экспозиции

CameraControl.startFocusAndMetering() запускает автофокусировку и замер экспозиции, устанавливая области замера AF/AE/AWB на основе заданного FocusMeteringAction. Этот метод часто используется для реализации функции «фокусировка касанием» во многих приложениях для камер.

Пункт учета

Для начала создайте 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) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)

}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please 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 meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

startFocusAndMetering и FocusMeteringAction

Для вызова функции startFocusAndMetering() приложениям необходимо создать объект FocusMeteringAction , состоящий из одной или нескольких MeteringPoints (MeteringPoint) с необязательными комбинациями режимов измерения из FLAG_AF , FLAG_AE , FLAG_AWB . Следующий код демонстрирует это использование:

Котлин

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
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 | 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 и другой точки замера только для AF и AE.

Внутри CameraX преобразует его в MeteringRectangles Camera2 и устанавливает соответствующие параметры CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS в запрос на захват изображения.

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

Компенсация за воздействие

Компенсация экспозиции полезна, когда приложениям необходимо точно настроить значения экспозиции (EV) сверх результата автоматической экспозиции (AE). Значения компенсации экспозиции комбинируются следующим образом для определения необходимой экспозиции в текущих условиях изображения:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

В CameraX предусмотрена функция Camera.CameraControl.setExposureCompensationIndex() позволяющая установить компенсацию экспозиции в виде индексного значения.

Положительные значения индекса делают изображение ярче, а отрицательные — затемняют. Приложения могут запросить поддерживаемый диапазон с помощью CameraInfo.ExposureState.exposureCompensationRange() описанного в следующем разделе. Если значение поддерживается, возвращаемый ListenableFuture завершается, когда значение успешно включено в запросе на захват; если указанный индекс выходит за пределы поддерживаемого диапазона, setExposureCompensationIndex() приводит к немедленному завершению возвращаемого ListenableFuture с результатом «неудачно».

CameraX хранит только последний незавершенный запрос setExposureCompensationIndex() , и многократный вызов функции до выполнения предыдущего запроса приводит к его отмене.

Следующий фрагмент кода устанавливает индекс компенсации рисков и регистрирует функцию обратного вызова, которая срабатывает после выполнения запроса на изменение рисков:

Котлин

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
      
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() извлекает текущее ExposureState включая:

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

Например, следующий код инициализирует настройки SeekBar для экспозиции текущими значениями ExposureState :

Котлин

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

Дополнительные ресурсы

Чтобы узнать больше о CameraX, ознакомьтесь со следующими дополнительными ресурсами.

Кодлаб

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

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

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