CameraX系统二:配置选项

配置选项

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

例如,对于图片拍摄用例,您可以设置目标宽高比和闪光灯模式。以下代码显示了一个示例:

KotlinJava

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

除配置选项之外,一些用例会公开 API 以便在创建后动态更改设置。如需了解各个用例的专属配置,请参阅实现预览、分析图片和图片拍摄。

自动选择

CameraX 会根据运行您的应用的设备自动提供专用的功能。例如,如果您未指定分辨率或指定的分辨率不受支持,CameraX 会自动确定要使用的最佳分辨率。所有这些操作均由库进行处理,无需您编写设备专属代码。

CameraX 的目标是成功初始化相机会话。这意味着 CameraX 会根据设备功能降低分辨率和宽高比。降低的原因可能是:

  • 设备不支持请求的分辨率。
  • 设备存在兼容性问题,例如需要特定分辨率才能正常运行的旧设备。
  • 在某些设备上,特定格式仅适用于特定宽高比。
  • 对于 JPEG 或视频编码,设备首选“最近的 mod16”。如需了解详情,请参阅 SCALER_STREAM_CONFIGURATION_MAP。

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

旋转

默认情况下,在用例创建期间,相机的旋转角度会设置为与默认的显示屏旋转角度保持一致。在这种默认情况下,CameraX 会生成输出,让应用能够轻松与您希望在预览中看到的画面保持一致。通过在配置用例对象时传入当前显示屏方向或在创建用例对象之后动态传入显示屏方向,您可以将旋转角度更改为自定义值以支持多显示屏设备。

您的应用可以使用配置设置来设置目标旋转角度。然后,即使生命周期处于运行状态,应用也可以通过使用用例 API 中的方法(例如 ImageAnalysis.setTargetRotation())更新旋转设置。您可以在应用锁定为纵向模式时执行上述操作,这样就无需重新配置旋转角度,但是照片或分析用例需要了解设备当前的旋转角度。例如,用例可能需要了解旋转角度才能以正确的方向进行面部检测,或者将照片设置为横向或纵向。

存储所拍摄图片的数据时可能不会包含旋转信息。Exif 数据包含旋转信息,以便图库应用在保存后以正确的屏幕方向显示图片。

如需以正确的屏幕方向显示预览数据,您可以使用 Preview.PreviewOutput() 的元数据输出创建转换。

以下代码示例展示了如何为屏幕方向事件设置旋转角度:

KotlinJava

@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:提供元数据输出,以便使用 Preview.getTargetRotation() 了解目标分辨率的旋转设置。
  • ImageAnalysis:提供元数据输出,以便了解图片缓冲区坐标相对于显示坐标的位置。
  • ImageCapture:更改图片 Exif 元数据、缓冲区或同时更改两者,从而反映旋转设置。更改的值取决于 HAL 实现。

剪裁矩形

默认情况下,剪裁矩形是完整的缓冲区矩形,您可通过 ViewPort 和 UseCaseGroup 对其进行自定义。通过对用例进行分组并设置视口,CameraX 可保证一个组中的所有用例的剪裁矩形都指向摄像头传感器中的同一个区域。

以下代码段展示了这两个类的使用方法:

KotlinJava

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 对象:

KotlinJava

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

在前面的示例中,应用通过 ImageAnalysis 和 ImageCapture 获取的内容与最终用户在 PreviewView 中看到的内容相同(假定 PreviewView 的缩放类型设为默认值 FILL_CENTER)。将剪裁矩形和旋转角度应用到输出缓冲区后,图片在所有用例中都一样,但分辨率可能会有所不同。如需详细了解如何应用转换信息,请参阅转换输出。

相机分辨率

您可以选择允许 CameraX 根据设备功能、设备支持的硬件级别、用例和所提供的宽高比组合设置图片分辨率。或者,您也可以在支持相应配置的用例中设置特定目标分辨率或特定宽高比。

自动分辨率

CameraX 可以根据 cameraProcessProvider.bindToLifecycle() 中指定的用例自动确定最佳分辨率设置。请尽可能在单个 bindToLifecycle() 调用的单个会话中指定需要同时运行的所有用例。CameraX 会考虑设备支持的硬件级别以及设备专属变化(设备可能会超出或不满足可用的信息流配置),根据绑定的一组用例确定分辨率。这样做的目的是使应用可在各种设备上运行,同时最大限度地减少设备专属代码路径。

图片拍摄和图片分析用例的默认宽高比为 4:3。

用例具有一个可配置的宽高比,让应用可以根据界面设计指定所需的宽高比。CameraX 输出将会生成,以尽可能匹配设备支持的所请求宽高比。如果没有任何支持的完全匹配分辨率,则选择满足最多条件的分辨率。因此,应用决定了相机应如何在应用中显示,CameraX 则会确定最佳相机分辨率设置,以在不同设备上满足这一要求。

例如,应用可以执行以下任一操作:

  • 为用例指定 4:3 或 16:9 的目标分辨率
  • 指定自定义分辨率,CameraX 将尝试查找与其最接近的匹配项
  • 为 ImageCapture 指定剪裁宽高比

CameraX 会自动选择内部 Camera2 界面分辨率。下表显示了这些分辨率:

用例 内部界面分辨率 输出数据分辨率
预览 宽高比:使目标与设置最相符的分辨率。 内部界面分辨率。提供元数据以允许 View 针对目标宽高比进行剪裁、缩放和旋转。
默认分辨率:最高预览分辨率,或与上述宽高比匹配的最高设备首选分辨率。
最大分辨率:预览大小,指的是与设备的屏幕分辨率或 1080p (1920x1080)(以较低者为准)匹配的最佳尺寸。
图片分析 宽高比:使目标与设置最相符的分辨率。 内部界面分辨率。
默认分辨率:默认目标分辨率设置为 640x480。同时调整目标分辨率和相应的宽高比可调出支持的最佳分辨率。
最大分辨率:从 StreamConfigurationMap.getOutputSizes() 中检索到的 YUV_420_888 格式的相机设备最大输出分辨率。目标分辨率默认设置为 640x480;因此,如果您希望分辨率大于 640x480,必须使用 setTargetResolution() 和 setTargetAspectRatio() 从支持的分辨率中选择最接近的一个。
图片拍摄 宽高比:最适合设置的宽高比。 内部界面分辨率。
默认分辨率:最高可用分辨率,或与上述宽高比匹配的最高设备首选分辨率。
最大分辨率:JPEG 格式的相机设备最大输出分辨率。请使用 StreamConfigurationMap.getOutputSizes() 检索此分辨率。

指定分辨率

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

KotlinJava

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

您无法针对同一个用例设置目标宽高比和目标分辨率。这样做会在构建配置对象时抛出 IllegalArgumentException

按照目标旋转角度旋转支持的大小后,请在坐标系中表示分辨率 Size。例如,自然屏幕方向为纵向并采用自然目标旋转角度的设备如果请求纵向图片,可指定 480x640;而同一设备旋转 90 度并以横向屏幕方向为目标后,可指定 640x480。

目标分辨率会尝试制定图片分辨率的下限。实际的图片分辨率是最接近的可用分辨率,其大小不小于相机实现确定的目标分辨率。但是,如果不存在等于或大于目标分辨率的分辨率,就会从小于目标分辨率的可用分辨率中选择最接近的一个。与提供的 Size 具有相同宽高比的分辨率,其优先级高于具有不同宽高比的分辨率。

CameraX 会根据请求应用最合适的分辨率。如果主要需求是满足宽高比要求,则仅指定 setTargetAspectRatio,CameraX 会根据设备确定合适的特定分辨率。如果应用的主要需求是指定分辨率以提高图片处理效率(例如根据设备处理能力处理较小或中等大小的图片),请使用 setTargetResolution(Size resolution)

注意:如果使用 setTargetResolution(),可能会得到宽高比与其他用例不匹配的缓冲区。如果宽高比必须匹配,请检查两个用例返回的缓冲区尺寸,然后剪裁或缩放其中一个以与另一个匹配。

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

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

控制对焦

CameraControl API 提供“点按对焦”功能。首先获取一个 CameraControl 对象,如以下代码所示:

KotlinJava

Camera camera = processCameraProvider.bindToLifecycle(...);
CameraControl cameraControl = camera.getCameraControl();

使用 MeteringPointFactory、MeteringPoint、MeteringMode 和 FocusMeteringAction 运行“点按对焦”功能:

KotlinJava

MeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(width, height);
MeteringPoint point = factory.createPoint(x, y);
FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
        .addPoint(point2, FocusMeteringAction.FLAG_AE) // could have many
        // auto calling cancelFocusAndMetering in 5 seconds
        .setAutoCancelDuration(5, TimeUnit.SECONDS)
        .build();

ListenableFuture future = cameraControl.startFocusAndMetering(action)
future.addListener( () -> {
    try {
        FocusMeteringResult result = future.get();
        // process the result
    } catch (Exception e) {
    }
} , executor);

其他资源

要详细了解 CameraX,请参阅下面列出的其他资源。

Codelab

  • CameraX 使用入门

代码示例

  • 官方 CameraX 示例应用

开发者社区

Android CameraX 论坛

你可能感兴趣的:(CameraX,android,前端,java)