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

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

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

Котлин

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

Ява

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

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

КамераXConfig

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

С помощью CameraXConfig приложение может выполнять следующие действия:

Модель использования

Следующая процедура описывает, как использовать CameraXConfig :

  1. Создайте объект CameraXConfig с вашими настроенными конфигурациями.
  2. Реализуйте интерфейс CameraXConfig.Provider в своем Application и верните объект CameraXConfig в getCameraXConfig() .
  3. Добавьте класс Application в файл AndroidManifest.xml , как описано здесь .

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

Котлин

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

Сохраните локальную копию объекта CameraXConfig , если вашему приложению необходимо знать конфигурацию CameraX после ее установки.

Ограничитель камеры

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

Если CameraSelector , переданный в CameraXConfig.Builder.setAvailableCamerasLimiter() отфильтровывает камеру, CameraX ведет себя так, как будто эта камера не существует. Например, следующий код ограничивает приложение использованием только задней камеры устройства по умолчанию:

Котлин

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

Темы

Многие из API-интерфейсов платформы, на которых построена CameraX, требуют блокировки межпроцессного взаимодействия (IPC) с помощью оборудования, на ответ которого иногда могут потребоваться сотни миллисекунд. По этой причине CameraX вызывает эти API только из фоновых потоков, чтобы основной поток не блокировался и пользовательский интерфейс оставался гибким. CameraX внутренне управляет этими фоновыми потоками, поэтому такое поведение выглядит прозрачным. Однако некоторые приложения требуют строгого контроля потоков. CameraXConfig позволяет приложению устанавливать фоновые потоки, которые используются через CameraXConfig.Builder.setCameraExecutor() и CameraXConfig.Builder.setSchedulerHandler() .

Исполнитель камеры

Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов из этих API. CameraX выделяет внутреннего Executor и управляет им для выполнения этих задач. Однако если вашему приложению требуется более строгий контроль потоков, используйте CameraXConfig.Builder.setCameraExecutor() .

Обработчик планировщика

Обработчик планировщика используется для планирования внутренних задач через фиксированные интервалы, например повторной попытки открытия камеры, когда она недоступна. Этот обработчик не выполняет задания, а только отправляет их исполнителю камеры. Он также иногда используется на устаревших платформах API, которым требуется Handler для обратных вызовов. В этих случаях обратные вызовы по-прежнему отправляются только непосредственно исполнителю камеры. CameraX выделяет внутренний HandlerThread и управляет им для выполнения этих задач, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler() .

Ведение журнала

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

  • Log.DEBUG (по умолчанию)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Подробное описание этих уровней журнала см. в документации журнала Android . Используйте CameraXConfig.Builder.setMinimumLoggingLevel(int) чтобы установить соответствующий уровень ведения журнала для вашего приложения.

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

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

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

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

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

Вращение

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

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

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

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

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

Котлин

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Ява

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

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

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

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

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

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

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

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

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

Котлин

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Ява

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort определяет прямоугольник буфера, видимый конечным пользователям. Затем CameraX вычисляет максимально возможный прямоугольник обрезки на основе свойств области просмотра и присоединенных вариантов использования. Обычно для достижения эффекта WYSIWYG вы можете настроить область просмотра на основе варианта использования предварительного просмотра. Простой способ получить область просмотра — использовать PreviewView .

В следующих фрагментах кода показано, как получить объект ViewPort :

Котлин

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Ява

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

В предыдущем примере то, что приложение получает от ImageAnalysis и ImageCapture соответствует тому, что конечный пользователь видит в PreviewView , при условии, что для типа масштабирования PreviewView установлено значение по умолчанию FILL_CENTER . После применения прямоугольника обрезки и вращения к выходному буферу изображение во всех случаях использования будет одинаковым, хотя, возможно, с разным разрешением. Дополнительные сведения о том, как применить информацию о преобразовании, см. в разделе «Вывод преобразования» .

Выбор камеры

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

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

Котлин

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

Выбор нескольких камер одновременно

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

При использовании функции «Параллельная камера» устройство может одновременно управлять двумя камерами с разными объективами или двумя задними камерами одновременно. В следующем блоке кода показано, как установить две камеры при вызове bindToLifecycle и как получить оба объекта Camera из возвращенного объекта ConcurrentCamera .

Котлин

// Build ConcurrentCameraConfig
val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

val primaryCamera = concurrentCamera.cameras[0]
val secondaryCamera = concurrentCamera.cameras[1]

Ява

// Build ConcurrentCameraConfig
SingleCameraConfig primary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

SingleCameraConfig secondary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

ConcurrentCamera concurrentCamera =  
    mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary));

Camera primaryCamera = concurrentCamera.getCameras().get(0);
Camera secondaryCamera = concurrentCamera.getCameras().get(1);

Разрешение камеры

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

Автоматическое разрешение

CameraX может автоматически определять наилучшие настройки разрешения на основе вариантов использования, указанных в cameraProcessProvider.bindToLifecycle() . По возможности указывайте все варианты использования, необходимые для одновременного выполнения в одном сеансе, в одном вызове bindToLifecycle() . CameraX определяет разрешения на основе набора вариантов использования, связанных с учетом поддерживаемого аппаратного уровня устройства и учетом различий, специфичных для устройства (когда устройство превышает или не соответствует доступным конфигурациям потока ). Цель состоит в том, чтобы позволить приложению работать на самых разных устройствах, минимизируя при этом пути кода, специфичные для каждого устройства.

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

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

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

  • Укажите целевое разрешение 4:3 или 16:9 для варианта использования.
  • Укажите собственное разрешение, которому CameraX попытается найти наиболее близкое соответствие.
  • Укажите соотношение сторон обрезки для ImageCapture

CameraX автоматически выбирает внутреннее разрешение поверхности Camera2. В следующей таблице показаны разрешения:

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

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

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

Котлин

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

Ява

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

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

Выразите Size разрешения в системе координат после поворота поддерживаемых размеров на целевое вращение. Например, устройство с портретной естественной ориентацией при естественном целевом повороте, запрашивающее портретное изображение, может указать размер 480 x 640, а то же устройство, повернутое на 90 градусов и ориентированное на альбомную ориентацию, может указать 640 x 480.

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

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

CameraX применяет наиболее подходящее разрешение в зависимости от запросов. Если основной задачей является соблюдение соотношения сторон, укажите только setTargetAspectRatio , и CameraX определит конкретное разрешение, подходящее в зависимости от устройства. Если основной задачей приложения является указание разрешения, чтобы сделать обработку изображений более эффективной (например, изображение небольшого или среднего размера в зависимости от возможностей обработки устройства), используйте setTargetResolution(Size resolution) .

Если вашему приложению требуется точное разрешение, см. таблицу в createCaptureSession() чтобы определить, какие максимальные разрешения поддерживаются каждым уровнем оборудования. Чтобы проверить конкретные разрешения, поддерживаемые текущим устройством, см. StreamConfigurationMap.getOutputSizes(int) .

Если ваше приложение работает на Android 10 или более поздней версии, вы можете использовать isSessionConfigurationSupported() для проверки конкретной SessionConfiguration .

Управление выходом камеры

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

  • CameraControl позволяет настраивать общие функции камеры.
  • CameraInfo позволяет запрашивать состояние этих общих функций камеры.

Ниже приведены поддерживаемые функции камеры с помощью CameraControl:

  • Увеличить
  • Факел
  • Фокусировка и замер экспозиции (фокусировка касанием)
  • Компенсация экспозиции

Получите экземпляры CameraControl и CameraInfo.

Получите экземпляры CameraControl и CameraInfo , используя объект Camera , возвращаемый ProcessCameraProvider.bindToLifecycle() . Следующий код показывает пример:

Котлин

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Ява

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

Например, вы можете отправить масштабирование и другие операции CameraControl после вызова bindToLifecycle() . После остановки или уничтожения действия, используемого для привязки экземпляра камеры, CameraControl больше не может выполнять операции и возвращает неудачное ListenableFuture .

Увеличить

CameraControl предлагает два метода изменения уровня масштабирования:

  • setZoomRatio() устанавливает масштаб по коэффициенту масштабирования.

    Соотношение должно находиться в диапазоне CameraInfo.getZoomState().getValue().getMinZoomRatio() и CameraInfo.getZoomState().getValue().getMaxZoomRatio() . В противном случае функция возвращает неудачный ListenableFuture .

  • setLinearZoom() устанавливает текущий масштаб со значением линейного масштабирования в диапазоне от 0 до 1,0.

    Преимущество линейного масштабирования заключается в том, что поле зрения (FOV) масштабируется при изменении масштаба. Это делает его идеальным для использования в режиме Slider .

CameraInfo.getZoomState() возвращает LiveData текущего состояния масштабирования. Значение меняется при инициализации камеры или если уровень масштабирования установлен с помощью setZoomRatio() или setLinearZoom() . Вызов любого метода устанавливает значения, поддерживающие ZoomState.getZoomRatio() и ZoomState.getLinearZoom() . Это полезно, если вы хотите отображать текст коэффициента масштабирования рядом со ползунком. Просто наблюдайте за ZoomState LiveData , чтобы обновить оба без необходимости преобразования.

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

Факел

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

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

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

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

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

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

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

Размер MeteringPoint варьируется от 0 до 1, размер по умолчанию — 0,15f. При вызове MeteringPointFactory.createPoint(float x, float y, float size) CameraX создает прямоугольную область с центром в (x, y) для заданного size .

Следующий код демонстрирует, как создать MeteringPoint :

Котлин

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)
…
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

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

startFocusAndMetering и FocusMeteringAction

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

Котлин

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

Как показано в предыдущем коде, startFocusAndMetering() принимает FocusMeteringAction , состоящий из одного MeteringPoint для регионов измерения AF/AE/AWB и другого MeteringPoint только для AF и AE.

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

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

Компенсация экспозиции

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

Exposure = ExposureCompensationIndex * ExposureCompensationStep

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

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

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

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

Котлин

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      …
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() извлекает текущий ExposureState включая:

    • Возможность поддержки управления компенсацией экспозиции.
    • Текущий индекс компенсации экспозиции.
    • Диапазон индекса компенсации экспозиции.
    • Шаг компенсации экспозиции, используемый при расчете значения компенсации экспозиции.

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

Котлин

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

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

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

Кодлаб

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

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

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

    ,

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

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

    Котлин

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

    Ява

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

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

    КамераXConfig

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

    С помощью CameraXConfig приложение может выполнять следующие действия:

    Модель использования

    Следующая процедура описывает, как использовать CameraXConfig :

    1. Создайте объект CameraXConfig с вашими настроенными конфигурациями.
    2. Реализуйте интерфейс CameraXConfig.Provider в своем Application и верните объект CameraXConfig в getCameraXConfig() .
    3. Добавьте класс Application в файл AndroidManifest.xml , как описано здесь .

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

    Котлин

    class CameraApplication : Application(), CameraXConfig.Provider {
       override fun getCameraXConfig(): CameraXConfig {
           return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
               .setMinimumLoggingLevel(Log.ERROR).build()
       }
    }
    

    Сохраните локальную копию объекта CameraXConfig , если вашему приложению необходимо знать конфигурацию CameraX после ее установки.

    Ограничитель камеры

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

    Если CameraSelector , переданный в CameraXConfig.Builder.setAvailableCamerasLimiter() отфильтровывает камеру, CameraX ведет себя так, как будто эта камера не существует. Например, следующий код ограничивает приложение использованием только задней камеры устройства по умолчанию:

    Котлин

    class MainApplication : Application(), CameraXConfig.Provider {
       override fun getCameraXConfig(): CameraXConfig {
           return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                  .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
                  .build()
       }
    }
    

    Темы

    Многие из API-интерфейсов платформы, на которых построена CameraX, требуют блокировки межпроцессного взаимодействия (IPC) с помощью оборудования, на ответ которого иногда могут потребоваться сотни миллисекунд. По этой причине CameraX вызывает эти API только из фоновых потоков, чтобы основной поток не блокировался и пользовательский интерфейс оставался гибким. CameraX внутренне управляет этими фоновыми потоками, поэтому такое поведение выглядит прозрачным. Однако некоторые приложения требуют строгого контроля потоков. CameraXConfig позволяет приложению устанавливать фоновые потоки, которые используются через CameraXConfig.Builder.setCameraExecutor() и CameraXConfig.Builder.setSchedulerHandler() .

    Исполнитель камеры

    Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов из этих API. CameraX выделяет внутреннего Executor и управляет им для выполнения этих задач. Однако если вашему приложению требуется более строгий контроль потоков, используйте CameraXConfig.Builder.setCameraExecutor() .

    Обработчик планировщика

    Обработчик планировщика используется для планирования внутренних задач через фиксированные интервалы, например повторной попытки открытия камеры, когда она недоступна. Этот обработчик не выполняет задания, а только отправляет их исполнителю камеры. Он также иногда используется на устаревших платформах API, которым требуется Handler для обратных вызовов. В этих случаях обратные вызовы по-прежнему отправляются только непосредственно исполнителю камеры. CameraX выделяет внутренний HandlerThread и управляет им для выполнения этих задач, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler() .

    Ведение журнала

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

    • Log.DEBUG (по умолчанию)
    • Log.INFO
    • Log.WARN
    • Log.ERROR

    Подробное описание этих уровней журнала см. в документации журнала Android . Используйте CameraXConfig.Builder.setMinimumLoggingLevel(int) чтобы установить соответствующий уровень ведения журнала для вашего приложения.

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

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

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

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

    Котлин

    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(boolean) приводит TorchState.OFF немедленному выполнению возвращаемой 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
    }
    

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

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

    Кодлаб

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

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

    Дискуссионная группа Android -камера

    ,

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

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

    Котлин

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

    Ява

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

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

    CAMERAXCONFIG

    Для простоты у Camerax есть конфигурации по умолчанию, такие как внутренние исполнители и обработчики, которые подходят для большинства сценариев использования. Однако, если ваше приложение имеет особые требования или предпочитает настраивать эти конфигурации, CameraXConfig является интерфейсом для этой цели.

    С помощью CameraXConfig приложение может сделать следующее:

    Модель использования

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

    1. Создайте объект CameraXConfig с вашими настраиваемыми конфигурациями.
    2. Реализуйте интерфейс CameraXConfig.Provider в вашем Application и верните свой объект CameraXConfig в getCameraXConfig() .
    3. Добавьте свой класс Application в свой файл AndroidManifest.xml , как описано здесь .

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

    Котлин

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

    Если 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 платформы, на которых строится камера, требуют блокировки межпроцессной связи (IPC) с оборудованием, которое иногда может занять сотни миллисекунд, чтобы ответить. По этой причине Camerax называет только эти API из фоновых потоков, так что основной поток не заблокирован, а пользовательский интерфейс остается плавным. Камера внутренне управляет этими фоновыми потоками так, чтобы это поведение казалось прозрачным. Тем не менее, некоторые приложения требуют строгого управления потоками. CameraXConfig позволяет приложению устанавливать фоновые потоки, которые используются через CameraXConfig.Builder.setCameraExecutor() и CameraXConfig.Builder.setSchedulerHandler() .

    Исполнитель камеры

    Исполнитель камеры используется для всех внутренних вызовов API платформы камеры, а также для обратных вызовов от этих API. Камеракс выделяет и управляет внутренним Executor для выполнения этих задач. Однако, если ваше приложение требует более строгого управления потоками, используйте CameraXConfig.Builder.setCameraExecutor() .

    Планировщик

    Обработчик планировщика используется для планирования внутренних задач с фиксированными промежутками, таких как повторное отверстие камеры, когда она недоступна. Этот обработчик не выполняет задания и только отправляет их исполнителю камеры. Это также иногда используется на платформах API Legacy, которые требуют Handler для обратных вызовов. В этих случаях обратные вызовы по -прежнему отправляются только непосредственно исполнителю камеры. Камеракс выделяет и управляет внутренним HandlerThread для выполнения этих задач, но вы можете переопределить его с помощью CameraXConfig.Builder.setSchedulerHandler() .

    Ведение журнала

    Регистрация камеры позволяет приложениям фильтровать сообщения logcat, так как это может быть хорошей практикой, чтобы избежать словесных сообщений в вашем производственном коде. Камера поддерживает четыре уровня регистрации, от наиболее многослойной до наиболее тяжелой:

    • Log.DEBUG (по умолчанию)
    • Log.INFO
    • Log.WARN
    • Log.ERROR

    Обратитесь к документации журнала Android для подробного описания этих уровней журнала. Используйте CameraXConfig.Builder.setMinimumLoggingLevel(int) чтобы установить соответствующий уровень ведения журнала для вашего приложения.

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

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

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

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

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

    Вращение

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

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

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

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

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

    Котлин

    override fun onCreate() {
        val imageCapture = ImageCapture.Builder().build()
    
        val orientationEventListener = object : OrientationEventListener(this as Context) {
            override fun onOrientationChanged(orientation : Int) {
                // Monitors orientation values to determine the target rotation value
                val rotation : Int = when (orientation) {
                    in 45..134 -> Surface.ROTATION_270
                    in 135..224 -> Surface.ROTATION_180
                    in 225..314 -> Surface.ROTATION_90
                    else -> Surface.ROTATION_0
                }
    
                imageCapture.targetRotation = rotation
            }
        }
        orientationEventListener.enable()
    }
    

    Ява

    @Override
    public void onCreate() {
        ImageCapture imageCapture = new ImageCapture.Builder().build();
    
        OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
           @Override
           public void onOrientationChanged(int orientation) {
               int rotation;
    
               // Monitors orientation values to determine the target rotation value
               if (orientation >= 45 && orientation < 135) {
                   rotation = Surface.ROTATION_270;
               } else if (orientation >= 135 && orientation < 225) {
                   rotation = Surface.ROTATION_180;
               } else if (orientation >= 225 && orientation < 315) {
                   rotation = Surface.ROTATION_90;
               } else {
                   rotation = Surface.ROTATION_0;
               }
    
               imageCapture.setTargetRotation(rotation);
           }
        };
    
        orientationEventListener.enable();
    }
    

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

    • Предварительный просмотр : вывод метаданных предоставляется таким образом, чтобы вращение целевого разрешения было известно с использованием Preview.getTargetRotation() .
    • Imageanalysis : вывод метаданных обеспечивается таким образом, чтобы координаты буфера изображения известны относительно координат отображения.
    • ImageCapture : метаданные Exif 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 , чтобы повлиять на выбор устройства:

    Котлин

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

    If your app requires an exact resolution, see the table within createCaptureSession() to determine what maximum resolutions are supported by each hardware level. To check for the specific resolutions supported by the current device, see StreamConfigurationMap.getOutputSizes(int) .

    If your app is running on Android 10 or higher, you can use isSessionConfigurationSupported() to verify a specific SessionConfiguration .

    Control camera output

    In addition to letting you configure the camera output as-needed for each individual use case, CameraX also implements the following interfaces to support camera operations common to all bound use cases:

    • CameraControl lets you configure common camera features.
    • CameraInfo lets you query the states of those common camera features.

    These are the supported camera features with CameraControl:

    • Увеличить
    • Факел
    • Focus and Metering (tap-to-focus)
    • Exposure Compensation

    Get instances of CameraControl and CameraInfo

    Retrieve instances of CameraControl and CameraInfo using the Camera object returned by ProcessCameraProvider.bindToLifecycle() . The following code shows an example:

    Котлин

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

    For example, you can submit zoom and other CameraControl operations after calling bindToLifecycle() . After you stop or destroy the activity used to bind the camera instance, CameraControl can no longer execute operations and returns a failed ListenableFuture .

    Увеличить

    CameraControl offers two methods for changing the zoom level:

    • setZoomRatio() sets the zoom by the zoom ratio.

      The ratio must be within the range of CameraInfo.getZoomState().getValue().getMinZoomRatio() and CameraInfo.getZoomState().getValue().getMaxZoomRatio() . Otherwise the function returns a failed ListenableFuture .

    • setLinearZoom() sets the current zoom with a linear zoom value ranging from 0 to 1.0.

      The advantage of linear zoom is that it makes the field of view (FOV) scale with changes in zoom. This makes it ideal for use with a Slider view.

    CameraInfo.getZoomState() returns a LiveData of the current zoom state. The value changes when the camera is initialized or if the zoom level is set using setZoomRatio() or setLinearZoom() . Calling either method sets the values backing ZoomState.getZoomRatio() and ZoomState.getLinearZoom() . This is helpful if you want to display zoom ratio text alongside a slider. Simply observe the ZoomState LiveData to update both without needing to do a conversion.

    The ListenableFuture returned by both APIs offers the option for applications to be notified when a repeating request with the specified zoom value is completed. In addition, if you set a new zoom value while the previous operation is still executing, the previous zoom operation's ListenableFuture fails immediately.

    Факел

    CameraControl.enableTorch(boolean) enables or disables the torch (also known as the flashlight).

    CameraInfo.getTorchState() can be used to query the current torch state. You can check the value returned by CameraInfo.hasFlashUnit() to determine whether a torch is available. If not, calling CameraControl.enableTorch(boolean) causes the returned ListenableFuture to complete immediately with a failed result and sets the torch state to TorchState.OFF .

    When the torch is enabled, it remains on during photo and video capture regardless of the flashMode setting. The flashMode in ImageCapture works only when the torch is disabled.

    Focus and Metering

    CameraControl.startFocusAndMetering() triggers autofocus and exposure metering by setting AF/AE/AWB metering regions based on the given FocusMeteringAction. This is often used to implement the “tap to focus” feature in many camera applications.

    MeteringPoint

    To begin, create a MeteringPoint using MeteringPointFactory.createPoint(float x, float y, float size) . A MeteringPoint represents a single point on the camera Surface . It's stored in a normalized form so that it can be easily converted to sensor coordinates for specifying AF/AE/AWB regions.

    The size of the MeteringPoint ranges from 0 to 1, with a default size of 0.15f. When calling MeteringPointFactory.createPoint(float x, float y, float size) , CameraX creates a rectangle region centered at (x, y) for the provided size .

    The following code demonstrates how to create a 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 and FocusMeteringAction

    To invoke startFocusAndMetering() , applications must build a FocusMeteringAction , which consists of one or more MeteringPoints with optional metering mode combinations from FLAG_AF , FLAG_AE , FLAG_AWB . The follow code demonstrates this usage:

    Котлин

    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)
    

    As shown in the preceding code, startFocusAndMetering() takes a FocusMeteringAction consisting of one MeteringPoint for AF/AE/AWB metering regions and another MeteringPoint for AF and AE only.

    Internally, CameraX converts it into Camera2 MeteringRectangles and sets the corresponding CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS parameters to the capture request.

    Since not every device supports AF/AE/AWB and multiple regions, CameraX executes the FocusMeteringAction with best effort. CameraX uses the maximum number of MeteringPoints supported, in the order that points were added. All MeteringPoints added after the maximum count are ignored. For example, if a FocusMeteringAction is supplied with 3 MeteringPoints on a platform supporting just 2, only the first 2 MeteringPoints are used. The final MeteringPoint is ignored by CameraX.

    Exposure Compensation

    Exposure compensation is useful when applications need to fine-tune exposure values (EV) beyond the auto exposure (AE) output result. Exposure compensation values are combined in the following way to determine the necessary exposure for current image conditions:

    Exposure = ExposureCompensationIndex * ExposureCompensationStep

    CameraX provides the Camera.CameraControl.setExposureCompensationIndex() function for setting the exposure compensation as an index value.

    Positive index values make the image brighter, while negative values dim the image. Applications can query the supported range by CameraInfo.ExposureState.exposureCompensationRange() described in the next section. If the value is supported, the returned ListenableFuture completes when the value is successfully enabled in the capture request; if the specified index is out of the supported range, setExposureCompensationIndex() causes the returned ListenableFuture to complete immediately with a failed result.

    CameraX keeps only the latest outstanding setExposureCompensationIndex() request, and calling the function multiple times before the previous request gets executed results in its cancellation.

    The following snippet sets an exposure compensation index and registers a callback for when the exposure change request has been executed:

    Котлин

    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() retrieves the current ExposureState including:

      • The supportability of exposure compensation control.
      • The current exposure compensation index.
      • The exposure compensation index range.
      • The exposure compensation step used in exposure compensation value calculation.

    For example, the following code initializes the settings for an exposure SeekBar with current ExposureState values:

    Котлин

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

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

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

    Кодлаб

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

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

    Android CameraX Discussion Group