配置选项

您可以配置每个 CameraX 用例来控制用例操作的不同方面。

例如,对于图像捕获用例,您可以设置目标纵横比和闪光灯模式。以下代码显示了一个示例

Kotlin

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

Java

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

除了配置选项外,一些用例还公开了 API,可以在创建用例后动态更改设置。有关特定于各个用例的配置信息,请参阅实现预览分析图像图像捕获

CameraXConfig

为简单起见,CameraX 具有默认配置,例如适用于大多数使用场景的内部执行器和处理程序。但是,如果您的应用程序有特殊要求或希望自定义这些配置,CameraXConfig 是为此目的提供的接口。

使用CameraXConfig,应用程序可以执行以下操作

使用模型

以下过程描述了如何使用CameraXConfig

  1. 使用自定义配置创建CameraXConfig对象。
  2. 在您的Application中实现CameraXConfig.Provider接口,并在getCameraXConfig()中返回您的CameraXConfig对象。
  3. 将您的Application类添加到您的AndroidManifest.xml文件中,如此处所述。

例如,以下代码示例将 CameraX 日志记录限制为仅错误消息

Kotlin

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

如果您的应用程序需要在设置后了解 CameraX 配置,请保留CameraXConfig对象的本地副本。

相机限制器

在第一次调用ProcessCameraProvider.getInstance()期间,CameraX 会枚举并查询设备上可用相机的特性。由于 CameraX 需要与硬件组件通信,因此此过程对于每个相机来说可能需要花费不短的时间,尤其是在低端设备上。如果您的应用程序仅使用设备上的特定相机,例如默认的前置摄像头,则可以设置 CameraX 以忽略其他相机,这可以减少应用程序使用的相机的启动延迟。

如果传递给CameraXConfig.Builder.setAvailableCamerasLimiter()CameraSelector过滤掉了一个相机,则 CameraX 会将其视为不存在。例如,以下代码将应用程序限制为仅使用设备的默认后置摄像头

Kotlin

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

线程

CameraX 所构建的许多平台 API 都需要与硬件进行阻塞式进程间通信 (IPC),有时响应时间可能需要数百毫秒。出于这个原因,CameraX 仅从后台线程调用这些 API,以便主线程不会被阻塞,并且 UI 保持流畅。CameraX 在内部管理这些后台线程,以便此行为看起来是透明的。但是,某些应用程序需要严格控制线程。CameraXConfig允许应用程序通过CameraXConfig.Builder.setCameraExecutor()CameraXConfig.Builder.setSchedulerHandler()设置使用的后台线程。

相机执行器

相机执行器用于所有内部 Camera 平台 API 调用,以及来自这些 API 的回调。CameraX 分配并管理一个内部Executor来执行这些任务。但是,如果您的应用程序需要更严格地控制线程,请使用CameraXConfig.Builder.setCameraExecutor()

调度程序处理程序

调度程序处理程序用于以固定间隔安排内部任务,例如在相机不可用时重试打开相机。此处理程序不执行作业,仅将其分派到相机执行器。它有时也用于需要Handler进行回调的旧版 API 平台。在这些情况下,回调仍然仅直接分派到相机执行器。CameraX 分配并管理一个内部HandlerThread来执行这些任务,但您可以使用CameraXConfig.Builder.setSchedulerHandler()覆盖它。

日志记录

CameraX 日志记录允许应用程序过滤 logcat 消息,因为在生产代码中避免冗长的消息是一种良好的实践。CameraX 支持四个日志记录级别,从最详细到最严重

  • Log.DEBUG(默认)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

有关这些日志级别的详细描述,请参阅Android Log 文档。使用CameraXConfig.Builder.setMinimumLoggingLevel(int)为您的应用程序设置适当的日志记录级别。

自动选择

CameraX 自动提供特定于您的应用正在运行的设备的功能。例如,如果您未指定分辨率或您指定的分辨率不受支持,CameraX 会自动确定要使用的最佳分辨率。所有这些都由库处理,无需您编写特定于设备的代码。

CameraX 的目标是成功初始化相机会话。这意味着 CameraX 会根据设备功能对分辨率和纵横比进行折衷。折衷可能发生是因为

  • 设备不支持请求的分辨率。
  • 设备存在兼容性问题,例如需要特定分辨率才能正常运行的旧版设备。
  • 在某些设备上,某些格式仅在某些纵横比下可用。
  • 设备更偏好 JPEG 或视频编码的“最接近 mod16”。有关更多信息,请参阅SCALER_STREAM_CONFIGURATION_MAP

尽管 CameraX 创建和管理会话,但始终检查代码中用例输出上的返回图像大小并进行相应调整。

旋转

默认情况下,相机旋转设置为在创建用例期间与默认显示器的旋转匹配。在此默认情况下,CameraX 生成输出以使应用程序与您期望在预览中看到的输出相匹配。您可以将旋转更改为自定义值以支持多显示器设备,方法是在配置用例对象时或动态地在创建用例对象后传入当前显示方向。

您的应用可以使用配置设置设置目标旋转。然后,它可以通过使用用例 API 中的方法(例如ImageAnalysis.setTargetRotation())更新旋转设置,即使生命周期处于运行状态。当应用程序锁定为纵向模式时(因此在旋转时不会发生任何重新配置),但照片或分析用例需要了解设备的当前旋转时,您可能会使用此方法。例如,可能需要旋转感知,以便面部检测时面部方向正确,或者照片设置为横向或纵向。

捕获图像的数据可能会存储在没有旋转信息的情况下。Exif 数据包含旋转信息,以便图库应用程序在保存后以正确的方向显示图像。

要以正确的方向显示预览数据,您可以使用来自Preview.PreviewOutput()的元数据输出创建变换。

以下代码示例显示了如何在方向事件上设置旋转

Kotlin

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

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

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

Java

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

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

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

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

根据设置的旋转,每个用例要么直接旋转图像数据,要么向非旋转图像数据的使用者提供旋转元数据。

  • 预览:提供元数据输出,以便使用Preview.getTargetRotation()了解目标分辨率的旋转。
  • ImageAnalysis:提供元数据输出,以便图像缓冲区坐标相对于显示坐标已知。
  • ImageCapture:图像 Exif 元数据、缓冲区或缓冲区和元数据都将被更改以记录旋转设置。更改的值取决于 HAL 实现。

裁剪矩形

默认情况下,裁剪矩形是完整的缓冲区矩形。您可以使用ViewPortUseCaseGroup自定义它。通过对用例进行分组并设置视口,CameraX 保证组中所有用例的裁剪矩形都指向相机传感器中的同一区域。

以下代码片段显示了如何使用这两个类

Kotlin

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

Java

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

ViewPort定义了最终用户可见的缓冲区矩形。然后 CameraX 根据视口和附加用例的属性计算出最大的可能裁剪矩形。通常,为了实现所见即所得的效果,您可以根据预览用例配置视口。获取视口的一种简单方法是使用PreviewView

以下代码片段显示了如何获取ViewPort对象

Kotlin

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

Java

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

在前面的示例中,应用程序从ImageAnalysisImageCapture获取的内容与最终用户在PreviewView中看到的内容匹配,假设PreviewView的缩放类型设置为默认值FILL_CENTER。在将裁剪矩形和旋转应用于输出缓冲区后,所有用例中的图像都相同,但分辨率可能不同。有关如何应用转换信息的更多信息,请参阅转换输出

相机选择

CameraX 会自动为您的应用程序的要求和用例选择最佳的相机设备。如果您希望使用与为您选择的设备不同的设备,则有一些选项

以下代码示例演示了如何创建 CameraSelector 来影响设备选择

Kotlin

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

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

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

同时选择多个摄像头

从 CameraX 1.3 开始,您还可以同时选择多个摄像头。例如,您可以绑定到前后摄像头,以同时从两个角度拍摄照片或录制视频。

使用并发摄像头功能时,设备可以同时操作两个具有不同朝向镜头的摄像头,或同时操作两个后置摄像头。以下代码块展示了如何在调用 bindToLifecycle 时设置两个摄像头,以及如何从返回的 ConcurrentCamera 对象中获取这两个 Camera 对象。

Kotlin

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

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

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

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

Java

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

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

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

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

摄像头分辨率

您可以选择让 CameraX 根据设备功能、设备支持的 硬件级别、用例和提供的纵横比的组合来设置图像分辨率。或者,您可以在支持该配置的用例中设置特定的目标分辨率或特定的纵横比。

自动分辨率

CameraX 可以根据 cameraProcessProvider.bindToLifecycle() 中指定的用例自动确定最佳分辨率设置。在可能的情况下,在一个 bindToLifecycle() 调用中,指定所有需要并发运行的用例。CameraX 通过考虑设备支持的硬件级别并考虑设备特定的差异(设备超出或不满足 可用的流配置)来根据绑定的用例集确定分辨率。目的是让应用程序在各种设备上运行,同时最大限度地减少设备特定的代码路径。

图像捕捉和图像分析用例的默认纵横比为 4:3。

用例具有可配置的纵横比,允许应用程序根据 UI 设计指定所需的纵横比。CameraX 输出的生成尽可能地匹配请求的纵横比(在设备支持的范围内)。如果没有支持完全匹配的分辨率,则选择最符合条件的分辨率。因此,应用程序决定摄像头在应用程序中的显示方式,而 CameraX 确定最佳的摄像头分辨率设置以在不同设备上满足该需求。

例如,应用程序可以执行以下任何操作

  • 为用例指定 4:3 或 16:9 的目标分辨率
  • 指定自定义分辨率,CameraX 会尝试找到最接近的匹配项
  • ImageCapture 指定裁剪纵横比

CameraX 自动选择内部 Camera2 表面分辨率。下表显示了分辨率

用例 内部表面分辨率 输出数据分辨率
预览 纵横比:最适合目标设置的分辨率。 内部表面分辨率。提供元数据以允许 View 针对目标纵横比进行裁剪、缩放和旋转。
默认分辨率:最高预览分辨率,或与预览纵横比匹配的最高设备首选分辨率。
最大分辨率:预览大小,指的是与设备屏幕分辨率最匹配的大小,或 1080p(1920x1080),取较小者。
图像分析 纵横比:最适合目标设置的分辨率。 内部表面分辨率。
默认分辨率:默认目标分辨率设置为 640x480。调整目标分辨率和相应的纵横比可获得最佳支持的分辨率。
最大分辨率:摄像头设备的 YUV_420_888 格式的最大输出分辨率,从 StreamConfigurationMap.getOutputSizes() 中检索。目标分辨率默认设置为 640x480,因此,如果您想要大于 640x480 的分辨率,则必须使用 setTargetResolution()setTargetAspectRatio() 从支持的分辨率中获取最接近的分辨率。
图像捕获 纵横比:最适合设置的纵横比。 内部表面分辨率。
默认分辨率:可用的最高分辨率,或与 ImageCapture 纵横比匹配的最高设备首选分辨率。
最大分辨率:摄像头设备的 JPEG 格式的最大输出分辨率。使用 StreamConfigurationMap.getOutputSizes() 检索此值。

指定分辨率

您可以在使用 setTargetResolution(Size resolution) 方法构建用例时设置特定分辨率,如下面的代码示例所示

Kotlin

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

Java

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

您不能在同一个用例上同时设置目标纵横比和目标分辨率。这样做会在构建配置对象时抛出 IllegalArgumentException

在旋转目标旋转的支持大小后,在坐标系中表达分辨率 Size。例如,一个自然方向为纵向且自然目标旋转为纵向的设备,请求纵向图像可以指定 480x640,而同一个设备旋转 90 度并以横向为目标方向可以指定 640x480。

目标分辨率尝试为图像分辨率建立最小边界。实际图像分辨率是大小上最接近且不小于目标分辨率的可用分辨率,由摄像头实现确定。

但是,如果不存在等于或大于目标分辨率的分辨率,则选择小于目标分辨率的最接近的可用分辨率。与提供的 Size 具有相同纵横比的分辨率优先于不同纵横比的分辨率。

CameraX 根据请求应用最合适的分辨率。如果主要需求是满足纵横比,则仅指定 setTargetAspectRatio,CameraX 会根据设备确定合适的确切分辨率。如果应用程序的主要需求是指定分辨率以使图像处理更有效(例如,基于设备处理能力的小型或中型图像),请使用 setTargetResolution(Size resolution)

如果您的应用程序需要确切的分辨率,请参阅 createCaptureSession() 中的表格,以确定每个硬件级别支持哪些最大分辨率。要检查当前设备支持的特定分辨率,请参阅 StreamConfigurationMap.getOutputSizes(int)

如果您的应用程序在 Android 10 或更高版本上运行,则可以使用 isSessionConfigurationSupported() 来验证特定的 SessionConfiguration

控制摄像头输出

除了允许您根据需要为每个单独的用例配置摄像头输出之外,CameraX 还实现了以下接口以支持所有绑定用例共有的摄像头操作

  • CameraControl 允许您配置常见的摄像头功能。
  • CameraInfo 允许您查询这些常见摄像头功能的状态。

以下是 CameraControl 支持的摄像头功能

  • 缩放
  • 闪光灯
  • 对焦和测光(点触对焦)
  • 曝光补偿

获取 CameraControl 和 CameraInfo 的实例

使用 ProcessCameraProvider.bindToLifecycle() 返回的 Camera 对象检索 CameraControlCameraInfo 的实例。以下代码显示了一个示例

Kotlin

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

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

Java

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

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

例如,您可以在调用 bindToLifecycle() 后提交缩放和其他 CameraControl 操作。在停止或销毁用于绑定摄像头实例的活动后,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 即可更新两者,而无需进行转换。

两个 API 返回的 ListenableFuture 为应用程序提供了在完成具有指定缩放值的重复请求时收到通知的选项。此外,如果您在先前的操作仍在执行时设置新的缩放值,则先前的缩放操作的 ListenableFuture 将立即失败。

闪光灯

CameraControl.enableTorch(boolean) 启用或禁用闪光灯(也称为手电筒)。

可以使用 CameraInfo.getTorchState() 查询当前手电筒状态。您可以检查 CameraInfo.hasFlashUnit() 返回的值来确定手电筒是否可用。如果不可用,调用 CameraControl.enableTorch(boolean) 会导致返回的 ListenableFuture 立即完成并失败,并将手电筒状态设置为 TorchState.OFF

启用手电筒后,无论闪光灯模式设置如何,它都会在拍照和录像期间保持开启状态。 ImageCapture 中的 flashMode 仅在禁用手电筒时有效。

对焦和测光

CameraControl.startFocusAndMetering() 通过根据给定的 FocusMeteringAction 设置 AF/AE/AWB 测光区域来触发自动对焦和曝光测光。这通常用于在许多相机应用程序中实现“点触对焦”功能。

测光点

首先,使用 MeteringPointFactory.createPoint(float x, float y, float size) 创建一个 MeteringPointMeteringPoint 代表相机 Surface 上的一个点。它以规范化的形式存储,以便可以轻松地转换为传感器坐标,用于指定 AF/AE/AWB 区域。

MeteringPoint 的大小范围为 0 到 1,默认大小为 0.15f。调用 MeteringPointFactory.createPoint(float x, float y, float size) 时,CameraX 会创建一个以 (x, y) 为中心、大小为 size 的矩形区域。

以下代码演示了如何创建一个 MeteringPoint

Kotlin

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

}

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

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

startFocusAndMetering 和 FocusMeteringAction

要调用 startFocusAndMetering(),应用程序必须构建一个 FocusMeteringAction,它包含一个或多个 MeteringPoints,以及来自 FLAG_AFFLAG_AEFLAG_AWB 的可选测光模式组合。以下代码演示了此用法

Kotlin

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

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

如前面的代码所示, startFocusAndMetering() 获取一个 FocusMeteringAction,该动作包含一个用于 AF/AE/AWB 测光区域的 MeteringPoint 和另一个仅用于 AF 和 AE 的 MeteringPoint。

在内部,CameraX 将其转换为 Camera2 MeteringRectangles,并将相应的 CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS 参数设置为捕获请求。

由于并非所有设备都支持 AF/AE/AWB 和多个区域,因此 CameraX 会尽力执行 FocusMeteringAction。CameraX 使用支持的最大 MeteringPoints 数量,按照添加点的顺序。超过最大数量后添加的所有 MeteringPoints 都会被忽略。例如,如果在仅支持 2 个点的平台上为 FocusMeteringAction 提供了 3 个 MeteringPoints,则仅使用前 2 个 MeteringPoints。CameraX 会忽略最后一个 MeteringPoint

曝光补偿

当应用程序需要微调超出自动曝光 (AE) 输出结果的曝光值 (EV) 时,曝光补偿非常有用。曝光补偿值以以下方式组合以确定当前图像条件所需的曝光

曝光 = 曝光补偿指数 * 曝光补偿步长

CameraX 提供了 Camera.CameraControl.setExposureCompensationIndex() 函数,用于将曝光补偿设置为索引值。

正索引值使图像更亮,而负索引值使图像变暗。应用程序可以通过下一节中描述的 CameraInfo.ExposureState.exposureCompensationRange() 查询支持的范围。如果支持该值,则在捕获请求中成功启用该值时,返回的 ListenableFuture 将完成;如果指定的索引超出支持范围,则 setExposureCompensationIndex() 会导致返回的 ListenableFuture 立即完成并失败。

CameraX 只保留最新的未完成 setExposureCompensationIndex() 请求,并且在之前的请求执行之前多次调用该函数会导致其取消。

以下代码片段设置曝光补偿指数,并在曝光更改请求执行时注册回调

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      
   }, mainExecutor)

例如,以下代码使用当前 ExposureState 值初始化曝光 SeekBar 的设置

Kotlin

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

其他资源

要了解有关 CameraX 的更多信息,请参阅以下其他资源。

Codelab

  • CameraX 入门
  • 代码示例

  • CameraX 示例应用程序
  • 开发者社区

    Android CameraX 讨论组