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

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

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

Котлин

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

Ява

ImageCapture imageCapture =
    new 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 платформы 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()
}

Ява

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

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

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

Обрезать прямоугольник

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

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

Котлин

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), в зависимости от того, какое значение меньше.
Анализ изображений Соотношение сторон: разрешение, которое лучше всего подходит для заданных параметров. Разрешение внутренней поверхности.
Разрешение по умолчанию: целевое разрешение по умолчанию — 640 x 480. Изменение целевого разрешения и соответствующего соотношения сторон позволяет выбрать оптимальное поддерживаемое разрешение.
Максимальное разрешение: максимальное выходное разрешение камеры в формате 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 в системе координат после поворота поддерживаемых размеров на целевой угол поворота. Например, устройство с портретной ориентацией в естественном целевом повороте, запрашивающее портретное изображение, может указать разрешение 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

Ява

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 работает только при выключенном фонарике.

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

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

MeteringPoint

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

Codelab

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

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

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