Вы настраиваете каждый вариант использования CameraX для управления различными аспектами операций варианта использования.
Например, в случае использования захвата изображения вы можете установить целевое соотношение сторон и режим вспышки. Следующий код показывает один пример:
Котлин
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Ява
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
В дополнение к параметрам конфигурации некоторые варианты использования предоставляют API для динамического изменения настроек после создания варианта использования. Сведения о конфигурации, специфичной для отдельных вариантов использования, см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» и «Захват изображений» .
КамераXConfig
Для простоты CameraX имеет конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако если у вашего приложения есть особые требования или вы предпочитаете настраивать эти конфигурации, CameraXConfig
— это интерфейс для этой цели.
С помощью CameraXConfig
приложение может выполнять следующие действия:
- Оптимизируйте задержку при запуске с помощью
setAvailableCameraLimiter()
. - Предоставьте исполнителя приложения CameraX с помощью
setCameraExecutor()
. - Замените обработчик планировщика по умолчанию на
setSchedulerHandler()
. - Измените уровень ведения журнала с помощью
setMinimumLoggingLevel()
.
Модель использования
Следующая процедура описывает, как использовать CameraXConfig
:
- Создайте объект
CameraXConfig
с вашими индивидуальными конфигурациями. - Реализуйте интерфейс
CameraXConfig.Provider
в своемApplication
и верните объектCameraXConfig
вgetCameraXConfig()
. - Добавьте класс
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.DEFAULT_FRONT_CAMERA
. - Запросите заднюю камеру по умолчанию с помощью
CameraSelector.DEFAULT_BACK_CAMERA
. - Отфильтруйте список доступных устройств по их
CameraCharacteristics
с помощьюCameraSelector.Builder.addCameraFilter()
.
В следующем примере кода показано, как создать 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 точками MeteringPoint на платформе, поддерживающей только 2, используются только первые 2 точки MeteringPoint. Последняя 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, обратитесь к следующим дополнительным ресурсам.
Кодлаб
Пример кода
Сообщество разработчиков
Группа обсуждений Android CameraX
,Вы настраиваете каждый вариант использования CameraX для управления различными аспектами операций варианта использования.
Например, в случае использования захвата изображения вы можете установить целевое соотношение сторон и режим вспышки. Следующий код показывает один пример:
Котлин
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Ява
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
В дополнение к параметрам конфигурации некоторые варианты использования предоставляют API для динамического изменения настроек после создания варианта использования. Сведения о конфигурации, специфичной для отдельных вариантов использования, см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» и «Захват изображений» .
КамераXConfig
Для простоты CameraX имеет конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако если у вашего приложения есть особые требования или вы предпочитаете настраивать эти конфигурации, CameraXConfig
— это интерфейс для этой цели.
С помощью CameraXConfig
приложение может выполнять следующие действия:
- Оптимизируйте задержку при запуске с помощью
setAvailableCameraLimiter()
. - Предоставьте исполнителя приложения CameraX с помощью
setCameraExecutor()
. - Замените обработчик планировщика по умолчанию на
setSchedulerHandler()
. - Измените уровень ведения журнала с помощью
setMinimumLoggingLevel()
.
Модель использования
Следующая процедура описывает, как использовать CameraXConfig
:
- Создайте объект
CameraXConfig
с вашими настроенными конфигурациями. - Реализуйте интерфейс
CameraXConfig.Provider
в своемApplication
и верните объектCameraXConfig
вgetCameraXConfig()
. - Добавьте класс
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 идет на компромисс в отношении разрешения и соотношения сторон в зависимости от возможностей устройства. Компромисс возможен потому, что:
- Устройство не поддерживает запрошенное разрешение.
- У устройства есть проблемы совместимости, например, у устаревших устройств, для правильной работы которых требуются определенные разрешения.
- На некоторых устройствах определенные форматы доступны только с определенным соотношением сторон.
- Устройство предпочитает «ближайший 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()
. - ImageAnaанализ : вывод метаданных обеспечивается таким образом, чтобы координаты буфера изображения были известны относительно координат отображения.
- ImageCapture : метаданные Exif изображения, буфер или и буфер, и метаданные изменяются с учетом настройки поворота. Изменяемое значение зависит от реализации HAL.
Обрезать прямоугольник
По умолчанию прямоугольник обрезки представляет собой прямоугольник полного буфера. Вы можете настроить его с помощью ViewPort
и UseCaseGroup
. Группируя варианты использования и устанавливая просмотр, камера гарантирует, что сельскохозяйственные прямы всех вариантов использования в группе указывают на ту же область в датчике камеры.
Следующий фрагмент кода показывает, как использовать эти два класса:
Котлин
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 вы можете настроить Viewport на основе валяки предварительного просмотра. Простой способ получить ViewPort - это использовать 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.DEFAULT_FRONT_CAMERA
. - Запросите камеру сзади по умолчанию с помощью камеры с помощью
CameraSelector.DEFAULT_BACK_CAMERA
. - Отфильтруйте список доступных устройств по их
CameraCharacteristics
с помощьюCameraSelector.Builder.addCameraFilter()
.
Следующий образец кода иллюстрирует, как создать 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
и как получить оба объекта камеры из возвращаемого объекта 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 установить разрешение изображения на основе комбинации возможностей устройства, поддерживаемого устройства, оборудования , варианта использования и предоставленного соотношения сторон. В качестве альтернативы вы можете установить конкретное целевое разрешение или конкретное соотношение сторон в использовании, которые поддерживают эту конфигурацию.
Автоматическое разрешение
Камера может автоматически определять настройки наилучшего разрешения на основе вариантов использования, указанных в cameraProcessProvider.bindToLifecycle()
. По возможности укажите все варианты использования, необходимые для выполнения одновременно в одном сеансе в одном вызове bindToLifecycle()
. Camerax определяет разрешения на основе набора вариантов использования, связанных с рассмотрением поддерживаемого уровня аппаратного обеспечения устройства и учетом дисперсии для конкретного устройства (где устройство превышает или не соответствует доступным конфигурациям потока ). Цель состоит в том, чтобы позволить приложению работать на широком спектре устройств при минимизации пути кода, специфичных для устройства.
Соотношение сторон по умолчанию для сбора изображений и анализа изображений составляет 4: 3.
Варианты использования имеют настраиваемое соотношение сторон, чтобы приложение указало желаемое соотношение сторон на основе дизайна пользовательского интерфейса. Выходка камера производится в соответствии с запрошенными соотношениями сторон, запрашиваемых так же близко, как и устройство. Если не поддерживается точное разрешение, выбирается то, что выполняет наибольшее количество условий. Таким образом, приложение диктует, как камера появляется в приложении, и камеракс определяет лучшие настройки разрешения камеры, чтобы удовлетворить это на разных устройствах.
Например, приложение может сделать любое из следующего:
- Укажите целевое разрешение 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
разрешения в рамке координат после вращения поддерживаемых размеров путем вращения целевого. Например, устройство с портретной естественной ориентацией в естественном целевом вращении с просьбой портретного изображения может указать 480x640, а то же устройство повернуто на 90 градусов, а ориентация на таргетинг может указать 640x480.
Целевое разрешение пытается установить минимальное связанное с разрешением изображения. Фактическое разрешение изображения является наиболее доступным разрешением в размере, которое не меньше, чем целевое разрешение, как определено реализацией камеры.
Однако, если разрешение не существует, которое равно или больше, чем разрешение цели, выбирается ближайшее доступное разрешение, меньшее, чем целевое разрешение. Резолюции с одинаковым соотношением сторон предоставленного Size
имеют более высокий приоритет, чем разрешения различных соотношений сторон.
Camerax применяет лучшее подходящее разрешение на основе запросов. Если основная потребность заключается в удовлетворении соотношения сторон, укажите только setTargetAspectRatio
, и Camerax определяет конкретное разрешение, подходящее на основе устройства. Если основная потребность в приложении состоит в том, чтобы указать разрешение, чтобы сделать обработку изображений более эффективной (например, небольшое или среднее изображение на основе возможности обработки устройств), используйте setTargetResolution(Size resolution)
.
Если ваше приложение требует точного разрешения, см. Таблицу в createCaptureSession()
чтобы определить, какие максимальные разрешения поддерживаются каждым уровнем аппаратного обеспечения. Чтобы проверить конкретные разрешения, поддерживаемые текущим устройством, см. StreamConfigurationMap.getOutputSizes(int)
.
Если ваше приложение работает на Android 10 или выше, вы можете использовать isSessionConfigurationSupported()
для проверки конкретной SessionConfiguration
.
Управление выводом камеры
В дополнение к тому, чтобы позволить вам настроить вывод камеры с учетом каждого отдельного варианта использования, Camerax также реализует следующие интерфейсы для поддержки операций камеры, общих для всех связанных вариантов использования:
-
CameraControl
позволяет настроить общие функции камеры. -
CameraInfo
позволяет вам запросить состояния этих общих функций камеры.
Это поддерживаемые функции камеры с CamerAcontrol:
- Увеличить
- Факел
- Фокус и измерение (Tap-to-Focus)
- Компенсация экспозиции
Получите экземпляры 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()
Например, вы можете отправить Zoom и другие операции CameraControl
после вызова bindToLifecycle()
. После того, как вы остановите или уничтожили действие, используемое для привязки экземпляра камеры, CameraControl
больше не может выполнять операции и возвращает неудачную ListenableFuture
.
Увеличить
Cameracontrol предлагает два метода изменения уровня масштабирования:
setZoomRatio()
устанавливает увеличение Zoom по отношению к увеличению.Соотношение должно находиться в пределах диапазона
CameraInfo.getZoomState().getValue().getMinZoomRatio()
иCameraInfo.getZoomState().getValue().getMaxZoomRatio()
. В противном случае функция возвращает неудачнуюListenableFuture
.setLinearZoom()
устанавливает текущий зум с линейным значением масштабирования в диапазоне от 0 до 1,0.Преимущество линейного масштабирования состоит в том, что он делает масштаб поля зрения (FOV) с изменениями Zoom. Это делает его идеальным для использования с обзором
Slider
.
CameraInfo.getZoomState()
возвращает живую из текущего состояния Zoom. Значение изменяется при инициатировании камеры или если уровень масштабирования установлен с использованием setZoomRatio()
или setLinearZoom()
. Вызов любой метод устанавливает значения, поддерживающую ZoomState.getZoomRatio()
и ZoomState.getLinearZoom()
. Это полезно, если вы хотите отобразить текст соотношения Zoom вместе со слайдером. Просто наблюдайте за ZoomState
LiveData
, чтобы обновить оба без необходимости сделать конверсию.
ListenableFuture
возвращаемая обоими API, предлагает опцию для приложений, которые будут уведомлены при завершении повторяющегося запроса с указанным значением Zoom. Кроме того, если вы установили новое значение масштабирования, пока предыдущая операция все еще выполняется, предыдущая операция Zoom Operation ListenableFuture
сразу не выполняется.
Факел
CameraControl.enableTorch(boolean)
позволяет или отключает факел (также известный как фонарик).
CameraInfo.getTorchState()
может использоваться для запроса текущего состояния факела. Вы можете проверить значение, возвращаемое CameraInfo.hasFlashUnit()
чтобы определить, доступен ли факел. Если нет, вызов cameracontrol.enabletorch TorchState.OFF
CameraControl.enableTorch(boolean)
приводит к немедленному выполнению возвращаемой ListenableFuture
Когда факел включен, он остается включенным во время захвата фото и видео независимо от настройки FlashMode. flashMode
в ImageCapture
работает только тогда, когда факел отключен.
Фокус и измерение
CameraControl.startFocusAndMetering()
запускает автофокусировку и измерение воздействия, устанавливая области измерения AF/AE/AWB на основе заданного фокусировки. Это часто используется для реализации функции «Tap to Focus» во многих приложениях камеры.
METERIENTPOINT
Для начала создайте 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 создает прямоугольную область, центрированную AT (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)
StartFocusEndetring и фокусировка
Чтобы вызвать 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, и другой точки метки только для AF и AE.
Внутренне Camerax преобразует его в Camera2 MeteringRectangles
и устанавливает соответствующие параметры CONTROL_AF_REGIONS
/ CONTROL_AE_REGIONS
/ CONTROL_AWB_REGIONS
для запроса захвата.
Поскольку не каждое устройство поддерживает AF/AE/AWB и несколько регионов, Camerax выполняет FocusMeteringAction
с наилучшими усилиями. Camerax использует максимальное количество поддерживаемых точек метки, в порядке, что точки были добавлены. Все точки метро, добавленные после максимального количества, игнорируются. Например, если FocusMeteringAction
поставляется с 3 точками метки на платформе, поддерживающей всего 2, используются только первые 2 точки метро. Окончательная MeteringPoint
игнорируется CAMERAX.
Компенсация экспозиции
Компенсация воздействия полезна, когда приложения необходимы для точной настройки значений экспозиции (EV) за пределами выходного результата экспозиции (AE). Значения компенсации воздействия объединяются следующим образом для определения необходимого воздействия для текущих условий изображения:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
Camerax предоставляет функцию Camera.CameraControl.setExposureCompensationIndex()
для установки компенсации экспозиции в качестве значения индекса.
Положительные значения индекса делают изображение ярче, в то время как отрицательные значения смягчают изображение. Приложения могут запросить поддерживаемый диапазон CameraInfo.ExposureState.exposureCompensationRange()
описанный в следующем разделе. Если значение поддерживается, возвращаемая ListenableFuture
завершается, когда значение успешно включено в запросе захвата; Если указанный индекс находится вне поддерживаемого диапазона, setExposureCompensationIndex()
приводит к немедленному выполнению возвращаемой ListenableFuture
с помощью неудачного результата.
Camerax сохраняет только последний обратный запрос setExposureCompensationIndex()
и вызовет функцию несколько раз, прежде чем предыдущий запрос будет выполнен в результате его отмены.
Следующий фрагмент устанавливает индекс компенсации воздействия и регистрирует обратный вызов, когда был выполнен запрос на изменение воздействия:
Котлин
camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex) .addListener({ // Get the current exposure compensation index, it might be // different from the asked value in case this request was // canceled by a newer setting request. val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex … }, mainExecutor)
Camera.CameraInfo.getExposureState()
извлекает текущийExposureState
, включая:- Поддержка контроля компенсации воздействия.
- Индекс компенсации текущего воздействия.
- Диапазон индекса компенсации воздействия.
- Шаг компенсации воздействия, используемый в расчете значения компенсации.
Например, следующий код инициализирует настройки для экспозиции SeekBar
с текущими значениями ExposureState
:
Котлин
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
Дополнительные ресурсы
Чтобы узнать больше о камере, обратитесь к следующим дополнительным ресурсам.
Коделаб
Пример кода
Сообщество разработчиков
Дискуссионная группа Android -камера