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

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

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

Котлин

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

Ява

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

В дополнение к параметрам конфигурации некоторые варианты использования предоставляют API для динамического изменения настроек после создания варианта использования. Сведения о конфигурации, специфичной для отдельных вариантов использования, см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» и «Захват изображений» .

КамераXConfig

Для простоты 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 . Используйте CameraXConfig.Builder.setMinimumLoggingLevel(int) чтобы установить соответствующий уровень ведения журнала для вашего приложения.

Автоматический выбор

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

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

  • Устройство не поддерживает запрошенное разрешение.
  • У устройства есть проблемы совместимости, например, у устаревших устройств, для правильной работы которых требуются определенные разрешения.
  • На некоторых устройствах определенные форматы доступны только с определенным соотношением сторон.
  • Устройство отдает предпочтение «ближайшему модулю 16» для кодирования 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()
}

Ява

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

Ява

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

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

Ява

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

Ява

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() . Следующий код показывает пример:

Котлин

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

Ява

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

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

Факел

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

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

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

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

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

МетерингПойнт

Для начала создайте MeteringPoint , используя MeteringPointFactory.createPoint(float x, float y, float size) . MeteringPoint представляет одну точку на Surface камеры. Они сохраняются в нормализованной форме, поэтому их можно легко преобразовать в координаты датчика для указания областей AF/AE/AWB.

Размер 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 с дополнительными комбинациями режимов измерения из 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 и другого MeteringPoint только для AF и AE.

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

Поскольку не каждое устройство поддерживает AF/AE/AWB и несколько регионов, CameraX максимально эффективно выполняет действие FocusMeteringAction . CameraX использует максимальное поддерживаемое количество точек MeteringPoints в том порядке, в котором они были добавлены. Все точки MeteringPoints, добавленные после максимального количества, игнорируются. Например, если FocusMeteringAction поставляется с 3 MeteringPoints на платформе, поддерживающей только 2, используются только первые 2 MeteringPoints. Последняя 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