Вы настраиваете каждый вариант использования CameraX, чтобы управлять различными аспектами операций варианта использования.
Например, при фотосъемке можно задать целевое соотношение сторон и режим вспышки. Пример кода:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
Помимо параметров конфигурации, в некоторых сценариях использования есть API, позволяющие динамически изменять настройки после создания сценария. Информацию о конфигурации для отдельных сценариев использования можно найти в разделах Реализация предварительного просмотра, Анализ изображений и Фотосъемка.
CameraXConfig
Для простоты CameraX имеет конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако если у вашего приложения есть особые требования или вы хотите настроить конфигурации, используйте интерфейс CameraXConfig.
С помощью CameraXConfig приложение может:
- Оптимизируйте задержку при запуске с помощью
setAvailableCameraLimiter(). - Предоставьте CameraX исполнитель приложения с помощью
setCameraExecutor(). - Замените обработчик планировщика по умолчанию на
setSchedulerHandler(). - Измените уровень детализации журнала с помощью
setMinimumLoggingLevel().
Модель использования
Ниже описано, как использовать CameraXConfig.
- Создайте объект
CameraXConfigс нужными настройками. - Реализуйте интерфейс
CameraXConfig.ProviderвApplicationи верните объектCameraXConfigвgetCameraXConfig(). - Добавьте класс
Applicationв файлAndroidManifest.xml, как описано здесь.
Например, следующий пример кода ограничивает ведение журнала CameraX только сообщениями об ошибках:
Kotlin
class CameraApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setMinimumLoggingLevel(Log.ERROR).build() } }
Сохраните локальную копию объекта CameraXConfig, если вашему приложению нужно знать конфигурацию CameraX после ее настройки.
Ограничитель камеры
При первом вызове ProcessCameraProvider.getInstance() CameraX перечисляет и запрашивает характеристики камер, доступных на устройстве. Поскольку CameraX необходимо взаимодействовать с аппаратными компонентами, этот процесс может занять значительное время для каждой камеры, особенно на устройствах низкого класса. Если ваше приложение использует только определенные камеры на устройстве, например фронтальную камеру по умолчанию, вы можете настроить CameraX так, чтобы игнорировать другие камеры. Это может уменьшить задержку запуска камер, которые использует ваше приложение.
Если CameraSelector, переданный в CameraXConfig.Builder.setAvailableCamerasLimiter(), отфильтровывает камеру, CameraX ведет себя так, как будто этой камеры не существует. Например, следующий код ограничивает приложение использованием только задней камеры устройства по умолчанию:
Kotlin
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }
Потоки
Многие API платформы, на которых основана библиотека CameraX, требуют блокировки межпроцессного взаимодействия (IPC) с аппаратным обеспечением, ответ от которого иногда может занимать сотни миллисекунд. Поэтому CameraX вызывает эти API только из фоновых потоков, чтобы не блокировать основной поток и сохранить плавность интерфейса. CameraX управляет этими фоновыми потоками внутри, поэтому такое поведение кажется прозрачным. Однако некоторые приложения требуют строгого контроля потоков. CameraXConfig позволяет приложению задавать фоновые потоки, которые используются через CameraXConfig.Builder.setCameraExecutor() и CameraXConfig.Builder.setSchedulerHandler().
Camera Executor
Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов из этих API. Для выполнения этих задач CameraX выделяет внутренний объект Executor и управляет им.
Однако если вашему приложению требуется более строгий контроль над цепочками, используйте CameraXConfig.Builder.setCameraExecutor().
Обработчик планировщика
Обработчик планировщика используется для планирования внутренних задач с фиксированными интервалами, например для повторной попытки открыть камеру, когда она недоступна. Этот обработчик не выполняет задания, а только отправляет их исполнителю камеры. Иногда он также используется на устаревших платформах API, которым для обратных вызовов требуется Handler. В этих случаях обратные вызовы по-прежнему отправляются непосредственно исполнителю камеры. Для выполнения этих задач CameraX выделяет внутренний объект HandlerThread и управляет им, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler().
Журналы
Ведение журнала CameraX позволяет приложениям фильтровать сообщения logcat, поскольку рекомендуется избегать подробных сообщений в рабочем коде. CameraX поддерживает четыре уровня ведения журнала – от самого подробного до самого критического:
Log.DEBUG(по умолчанию)Log.INFOLog.WARNLog.ERROR
Подробное описание этих уровней можно найти в документации по журналам Android. Чтобы задать подходящий уровень ведения журнала для приложения, используйте CameraXConfig.Builder.setMinimumLoggingLevel(int).
Выбрать автоматически
CameraX автоматически предоставляет функции, характерные для устройства, на котором запущено ваше приложение. Например, CameraX автоматически определяет наилучшее разрешение, если вы не указали его или если указанное вами разрешение не поддерживается. Все это обрабатывается библиотекой, что избавляет вас от необходимости писать код для конкретного устройства.
Цель CameraX – успешно инициализировать сеанс камеры. Это означает, что CameraX идет на компромисс в отношении разрешения и соотношения сторон в зависимости от возможностей устройства. Это может произойти по следующим причинам:
- Устройство не поддерживает запрошенное разрешение.
- Устройство несовместимо с другими устройствами. Например, устаревшим устройствам для правильной работы требуется определенное разрешение.
- На некоторых устройствах определенные форматы доступны только при определенном соотношении сторон.
- Устройство предпочитает ближайшее значение, кратное 16, для кодирования JPEG или видео. Дополнительную информацию вы найдете в статье
SCALER_STREAM_CONFIGURATION_MAP.
Хотя CameraX создает сеанс и управляет им, всегда проверяйте размеры возвращаемых изображений в выходных данных варианта использования в коде и корректируйте их соответствующим образом.
Вращение
По умолчанию поворот камеры соответствует повороту экрана по умолчанию во время создания варианта использования. В этом случае CameraX создает выходные данные, чтобы приложение соответствовало тому, что вы ожидаете увидеть в предварительном просмотре. Вы можете изменить поворот на пользовательское значение для поддержки устройств с несколькими дисплеями, передав текущую ориентацию дисплея при настройке объектов вариантов использования или динамически после их создания.
В настройках конфигурации приложения можно задать целевую ориентацию. Затем оно может обновить настройки поворота с помощью методов из API вариантов использования (например, ImageAnalysis.setTargetRotation()), даже если жизненный цикл находится в состоянии выполнения. Это может быть полезно, если в приложении заблокирован режим портретной съемки и при повороте устройства не происходит перенастройка, но для фотосъемки или анализа необходимо знать текущую ориентацию устройства. Например, это может быть необходимо для правильной ориентации лиц при их распознавании или для установки альбомной или портретной ориентации фотографий.
Данные для снятых изображений могут храниться без информации о повороте. Данные Exif содержат информацию о повороте, чтобы приложения галереи могли показывать изображение в правильной ориентации после сохранения.
Чтобы данные предварительного просмотра отображались с правильной ориентацией, вы можете использовать метаданные, полученные из Preview.PreviewOutput(), для создания преобразований.
В приведенном ниже примере кода показано, как задать поворот для события ориентации.
Kotlin
override fun onCreate() { val imageCapture = ImageCapture.Builder().build() val orientationEventListener = object : OrientationEventListener(this as Context) { override fun onOrientationChanged(orientation : Int) { // Monitors orientation values to determine the target rotation value val rotation : Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 in 135..224 -> Surface.ROTATION_180 in 225..314 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageCapture.targetRotation = rotation } } orientationEventListener.enable() }
Java
@Override public void onCreate() { ImageCapture imageCapture = new ImageCapture.Builder().build(); OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) { @Override public void onOrientationChanged(int orientation) { int rotation; // Monitors orientation values to determine the target rotation value if (orientation >= 45 && orientation < 135) { rotation = Surface.ROTATION_270; } else if (orientation >= 135 && orientation < 225) { rotation = Surface.ROTATION_180; } else if (orientation >= 225 && orientation < 315) { rotation = Surface.ROTATION_90; } else { rotation = Surface.ROTATION_0; } imageCapture.setTargetRotation(rotation); } }; orientationEventListener.enable(); }
В зависимости от заданного поворота каждый вариант использования либо поворачивает данные изображения напрямую, либо предоставляет метаданные поворота потребителям неповернутых данных изображения.
- Предварительный просмотр. Выводятся метаданные, чтобы можно было узнать поворот целевого разрешения с помощью
Preview.getTargetRotation(). - ImageAnalysis. Выводятся метаданные, чтобы координаты буфера изображения были известны относительно координат экрана.
- ImageCapture. Метаданные Exif изображения, буфер или и то, и другое изменяются, чтобы отразить настройку поворота. Изменяемое значение зависит от реализации HAL.
Прямоугольник кадрирования
По умолчанию прямоугольник кадрирования совпадает с прямоугольником буфера. Вы можете настроить его с помощью ViewPort и UseCaseGroup. Группируя варианты использования и настраивая область просмотра, CameraX гарантирует, что прямоугольники кадрирования всех вариантов использования в группе указывают на одну и ту же область датчика камеры.
В следующем фрагменте кода показано, как использовать эти два класса:
Kotlin
val viewPort = ViewPort.Builder(Rational(width, height), display.rotation).build() val useCaseGroup = UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
Java
ViewPort viewPort = new ViewPort.Builder( new Rational(width, height), getDisplay().getRotation()).build(); UseCaseGroup useCaseGroup = new UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build(); cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
ViewPort определяет буферный прямоугольник, видимый конечным пользователям. Затем CameraX рассчитывает максимально возможный прямоугольник кадрирования на основе свойств области просмотра и прикрепленных вариантов использования. Обычно, чтобы добиться эффекта WYSIWYG, вы можете настроить область просмотра в зависимости от варианта использования предварительного просмотра. Простой способ получить область просмотра – использовать PreviewView.
В приведенных ниже фрагментах кода показано, как получить объект ViewPort.
Kotlin
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
Java
ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();
В предыдущем примере то, что приложение получает от ImageAnalysis и ImageCapture, соответствует тому, что конечный пользователь видит в PreviewView, если для типа масштабирования PreviewView установлено значение по умолчанию – FILL_CENTER. После применения прямоугольника кадрирования и поворота к выходному буферу изображение из всех вариантов использования будет одинаковым, хотя и с разным разрешением. Подробнее о том, как применять информацию о преобразовании…
Выбор камеры
CameraX автоматически выбирает камеру, которая лучше всего подходит для требований и сценариев использования вашего приложения. Если вы хотите использовать другое устройство, у вас есть несколько вариантов:
- Запросите фронтальную камеру по умолчанию с помощью
CameraSelector.DEFAULT_FRONT_CAMERA. - Запросите основную камеру по умолчанию с помощью
CameraSelector.DEFAULT_BACK_CAMERA. - Отфильтруйте список доступных устройств по их
CameraCharacteristicsс помощьюCameraSelector.Builder.addCameraFilter().
В примере кода ниже показано, как создать CameraSelector, чтобы повлиять на выбор устройства.
Kotlin
fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? { val cam2Infos = provider.availableCameraInfos.map { Camera2CameraInfo.from(it) }.sortedByDescending { // HARDWARE_LEVEL is Int type, with the order of: // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) } return when { cam2Infos.isNotEmpty() -> { CameraSelector.Builder() .addCameraFilter { it.filter { camInfo -> // cam2Infos[0] is either EXTERNAL or best built-in camera val thisCamId = Camera2CameraInfo.from(camInfo).cameraId thisCamId == cam2Infos[0].cameraId } }.build() } else -> null } } // create a CameraSelector for the USB camera (or highest level internal camera) val selector = selectExternalOrBestCamera(processCameraProvider) processCameraProvider.bindToLifecycle(this, selector, preview, analysis)
Выбор нескольких камер одновременно
Начиная с CameraX 1.3, вы также можете выбирать несколько камер одновременно. Например, вы можете привязать фронтальную и основную камеры, чтобы одновременно делать снимки или записывать видео с обеих точек зрения.
При использовании функции параллельной съемки устройство может одновременно задействовать две камеры, направленные в разные стороны, или две задние камеры. В следующем блоке кода показано, как настроить две камеры при вызове bindToLifecycle и как получить оба объекта Camera из возвращенного объекта ConcurrentCamera.
Kotlin
// Build ConcurrentCameraConfig val primary = ConcurrentCamera.SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ) val secondary = ConcurrentCamera.SingleCameraConfig( secondaryCameraSelector, useCaseGroup, lifecycleOwner ) val concurrentCamera = cameraProvider.bindToLifecycle( listOf(primary, secondary) ) val primaryCamera = concurrentCamera.cameras[0] val secondaryCamera = concurrentCamera.cameras[1]
Java
// Build ConcurrentCameraConfig SingleCameraConfig primary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); SingleCameraConfig secondary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); ConcurrentCamera concurrentCamera = mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary)); Camera primaryCamera = concurrentCamera.getCameras().get(0); Camera secondaryCamera = concurrentCamera.getCameras().get(1);
Разрешение камеры
Вы можете разрешить CameraX устанавливать разрешение изображения на основе комбинации возможностей устройства, поддерживаемого устройством уровня оборудования, варианта использования и предоставленного соотношения сторон. Вы также можете задать целевое разрешение или соотношение сторон, если это поддерживается.
Автоматическое разрешение
CameraX может автоматически определить оптимальные настройки разрешения на основе вариантов использования, указанных в cameraProcessProvider.bindToLifecycle(). По возможности указывайте все варианты использования, которые должны выполняться одновременно в одном сеансе, в одном вызове bindToLifecycle(). CameraX определяет разрешения на основе набора сценариев использования, связанных с учетом поддерживаемого уровня аппаратного обеспечения устройства и с учетом различий, зависящих от устройства (когда устройство превышает или не соответствует доступным конфигурациям потока).
Это позволяет запускать приложение на самых разных устройствах, сводя к минимуму количество путей выполнения, зависящих от устройства.
По умолчанию для фотосъемки и анализа изображений используется соотношение сторон 4:3.
В сценариях использования можно настраивать соотношение сторон, чтобы приложение могло указывать желаемое соотношение сторон на основе дизайна пользовательского интерфейса. Выходные данные CameraX создаются так, чтобы максимально соответствовать запрошенным соотношениям сторон, поддерживаемым устройством. Если точного совпадения нет, выбирается разрешение, которое соответствует наибольшему количеству условий. Таким образом, приложение определяет, как камера отображается в приложении, а CameraX определяет наилучшие настройки разрешения камеры, чтобы удовлетворить это на разных устройствах.
Например, приложение может:
- Укажите целевое разрешение 4:3 или 16:9 для варианта использования
- Укажите специальное разрешение, для которого CameraX попытается найти наиболее близкое совпадение.
- Укажите соотношение сторон для кадрирования (
ImageCapture)
CameraX автоматически выбирает разрешение внутренней поверхности Camera2. В таблице ниже указаны разрешения.
| Пример использования | Разрешение внутренней поверхности | Разрешение выходных данных |
|---|---|---|
| Предпросмотр | Соотношение сторон. Разрешение, которое лучше всего подходит для целевого объекта. | Разрешение внутреннего экрана. Метаданные предоставляются для того, чтобы View мог обрезать, масштабировать и поворачивать изображение для целевого соотношения сторон. |
| Разрешение по умолчанию. Самое высокое разрешение предварительного просмотра или самое высокое разрешение, предпочитаемое устройством, которое соответствует соотношению сторон предварительного просмотра. | ||
| Максимальное разрешение. Размер предварительного просмотра, который соответствует разрешению экрана устройства, или 1080p (1920 x 1080) – в зависимости от того, что меньше. | ||
| Анализ изображений | Соотношение сторон. Разрешение, которое лучше всего подходит для целевого устройства. | Разрешение внутреннего экрана. |
| Разрешение по умолчанию. По умолчанию целевое разрешение составляет 640 x 480. Если вы измените целевое разрешение и соответствующее соотношение сторон, будет выбрано наиболее подходящее разрешение. | ||
Максимальное разрешение. Максимальное разрешение выходного видеопотока камеры в формате YUV_420_888, которое извлекается из StreamConfigurationMap.getOutputSizes().
По умолчанию целевое разрешение составляет 640 x 480. Если вам нужно более высокое разрешение, используйте setTargetResolution() и setTargetAspectRatio(), чтобы получить наиболее близкое из поддерживаемых разрешений.
|
||
| Съемка фото | Соотношение сторон. Выберите соотношение сторон, которое лучше всего подходит для вашего контента. | Разрешение внутреннего экрана. |
| Разрешение по умолчанию. Максимально доступное разрешение или максимальное разрешение, предпочитаемое устройством, которое соответствует соотношению сторон ImageCapture. | ||
Максимальное разрешение. Максимальное разрешение камеры в формате JPEG. Для этого используйте StreamConfigurationMap.getOutputSizes().
|
Укажите разрешение
Вы можете задать определенные разрешения при создании сценариев использования с помощью метода setTargetResolution(Size resolution), как показано в следующем примере кода:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
Нельзя задать и целевое соотношение сторон, и целевое разрешение для одного и того же варианта использования. В противном случае при создании объекта конфигурации будет выдаваться ошибка IllegalArgumentException.
Выразите разрешение Size в системе координат после поворота поддерживаемых размеров на целевой угол. Например, устройство с вертикальной ориентацией по умолчанию, запрашивающее вертикальное изображение, может указать размер 480 x 640, а то же устройство, повернутое на 90 градусов и запрашивающее горизонтальное изображение, – 640 x 480.
Целевое разрешение определяет минимальное разрешение изображения. Фактическое разрешение изображения – это ближайшее доступное разрешение, которое не меньше целевого, как определено реализацией камеры.
Если же такого разрешения нет, выбирается ближайшее доступное разрешение, которое меньше целевого. Разрешения с тем же соотношением сторон, что и у предоставленного Size, имеют более высокий приоритет, чем разрешения с другими соотношениями сторон.
CameraX применяет наиболее подходящее разрешение на основе запросов. Если основная задача – соблюсти соотношение сторон, укажите только setTargetAspectRatio, и CameraX определит подходящее разрешение в зависимости от устройства.
Если основная потребность приложения – указать разрешение, чтобы сделать обработку изображений более эффективной (например, маленькое или среднее изображение в зависимости от возможностей обработки устройства), используйте setTargetResolution(Size resolution).
Если вашему приложению требуется точное разрешение, посмотрите таблицу в разделе createCaptureSession(), чтобы узнать, какие максимальные разрешения поддерживаются каждым уровнем аппаратного обеспечения. Чтобы узнать, какие разрешения поддерживает ваше устройство, ознакомьтесь с этой статьейStreamConfigurationMap.getOutputSizes(int).
Если приложение работает на устройстве с Android 10 или более поздней версии, вы можете использовать isSessionConfigurationSupported(), чтобы проверить определенный SessionConfiguration.
Управление выводом камеры
Помимо настройки вывода камеры для каждого отдельного случая использования, CameraX также реализует следующие интерфейсы для поддержки операций камеры, общих для всех связанных случаев использования:
CameraControlпозволяет настраивать основные функции камеры.CameraInfoпозволяет запрашивать состояния этих общих функций камеры.
Ниже перечислены функции камеры, поддерживаемые CameraControl.
- Масштаб
- Факел
- Фокусировка и экспозамер (фокусировка по нажатию)
- Экспокоррекция
Получение экземпляров CameraControl и CameraInfo
Получите экземпляры CameraControl и CameraInfo с помощью объекта Camera, возвращенного методом ProcessCameraProvider.bindToLifecycle().
Пример кода:
Kotlin
val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // For performing operations that affect all outputs. val cameraControl = camera.cameraControl // For querying information and states. val cameraInfo = camera.cameraInfo
Java
Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // For performing operations that affect all outputs. CameraControl cameraControl = camera.getCameraControl() // For querying information and states. CameraInfo cameraInfo = camera.getCameraInfo()
Например, вы можете отправить запрос на масштабирование и другие операции 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. 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:
Kotlin
// 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. Пример кода:
Kotlin
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
для запроса на съемку.
Поскольку не все устройства поддерживают автофокус, автоэкспозицию, автоматический баланс белого и несколько областей, CameraX выполняет FocusMeteringAction по мере возможности. CameraX использует максимальное количество поддерживаемых точек замера в том порядке, в котором они были добавлены. Все точки замера, добавленные после достижения максимального количества, игнорируются. Например, если в FocusMeteringAction указано три точки MeteringPoint, а платформа поддерживает только две, то будут использоваться только первые две точки MeteringPoint. Последний параметр MeteringPoint игнорируется CameraX.
Экспокоррекция
Экспокоррекция полезна, когда приложениям необходимо точно настроить экспозиционное число (EV) за пределами выходного результата автоэкспозиции (AE). Значения экспокоррекции объединяются следующим образом, чтобы определить необходимую экспозицию для текущих условий съемки:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
CameraX предоставляет функцию Camera.CameraControl.setExposureCompensationIndex(), которая позволяет задать экспокоррекцию в виде индекса.
Положительные значения делают изображение ярче, а отрицательные – темнее. Приложения могут запрашивать поддерживаемый диапазон с помощью метода CameraInfo.ExposureState.exposureCompensationRange(), описанного в следующем разделе. Если значение поддерживается, возвращаемый объект ListenableFuture завершается, когда значение успешно включено в запросе на захват. Если указанный индекс выходит за пределы поддерживаемого диапазона, setExposureCompensationIndex() приводит к немедленному завершению возвращаемого объекта ListenableFuture с неудачным результатом.
CameraX сохраняет только последний невыполненный запрос setExposureCompensationIndex(). Если вызвать функцию несколько раз до выполнения предыдущего запроса, он будет отменен.
В следующем фрагменте кода задается индекс экспокоррекции и регистрируется обратный вызов, который выполняется после изменения экспозиции.
Kotlin
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:
Kotlin
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
Дополнительные ресурсы
Чтобы узнать больше о CameraX, ознакомьтесь со следующими дополнительными ресурсами.
Практическая работа
Пример кода
Сообщество разработчиков
Группа обсуждения Android CameraX