通过图像分析用例,您的应用可以获得 CPU 可访问的图像,从而执行图像处理、计算机视觉或机器学习推理。应用会实现一个在每帧上运行的 analyze()
方法。
如需了解如何将 Google 的 ML Kit 与您的 CameraX 应用集成,请参阅ML Kit Analyzer。
操作模式
当应用的分析管道无法满足 CameraX 的帧速率要求时,可以按以下方式之一配置 CameraX 以丢弃帧:
非阻塞(默认):在此模式下,执行程序始终将最新图像缓存到一个图像缓冲区(类似于深度为 1 的队列),同时应用分析前一个图像。如果 CameraX 在应用完成处理之前收到新图像,新图像将保存到同一缓冲区,覆盖前一个图像。请注意,在此场景中,
ImageAnalysis.Builder.setImageQueueDepth()
无效,并且缓冲区内容始终被覆盖。您可以通过使用STRATEGY_KEEP_ONLY_LATEST
调用setBackpressureStrategy()
来启用此非阻塞模式。有关执行程序影响的更多信息,请参阅STRATEGY_KEEP_ONLY_LATEST
的参考文档。阻塞:在此模式下,内部执行程序可以将多个图像添加到内部图像队列,并且只有当队列已满时才开始丢弃帧。阻塞发生在整个相机设备范围内:如果相机设备有多个绑定用例,则当 CameraX 处理这些图像时,所有这些用例都将被阻塞。例如,当预览和图像分析都绑定到相机设备时,当 CameraX 处理图像时,预览也会被阻塞。您可以通过将
STRATEGY_BLOCK_PRODUCER
传递给setBackpressureStrategy()
来启用阻塞模式。您还可以使用 ImageAnalysis.Builder.setImageQueueDepth() 配置图像队列深度。
对于低延迟和高性能分析器(分析图像的总时间小于 CameraX 帧的持续时间,例如 60fps 时为 16 毫秒),两种操作模式都能提供流畅的整体体验。阻塞模式在某些情况下仍然很有用,例如处理非常短暂的系统抖动时。
对于高延迟和高性能分析器,具有更长队列的阻塞模式对于补偿延迟是必要的。但请注意,应用仍然可以处理所有帧。
对于高延迟且耗时的分析器(分析器无法处理所有帧),非阻塞模式可能是更合适的选择,因为分析路径必须丢弃帧,但其他并发绑定的用例仍然可以看到所有帧。
实现
要在您的应用中使用图像分析,请按照以下步骤操作:
- 构建一个
ImageAnalysis
用例。 - 创建一个
ImageAnalysis.Analyzer
。 - 将您的分析器设置到您的
ImageAnalysis
。 - 将您的生命周期所有者、相机选择器和
ImageAnalysis
用例绑定到生命周期。
绑定后,CameraX 会立即将图像发送到您注册的分析器。完成分析后,调用 ImageAnalysis.clearAnalyzer()
或解除 ImageAnalysis
用例的绑定以停止分析。
构建 ImageAnalysis 用例
ImageAnalysis
将您的分析器(图像消费者)连接到 CameraX(图像生产者)。应用可以使用 ImageAnalysis.Builder
来构建一个 ImageAnalysis
对象。通过 ImageAnalysis.Builder
,应用可以配置以下内容:
- 图像输出参数
- 格式:CameraX 通过
setOutputImageFormat(int)
支持YUV_420_888
和RGBA_8888
。默认格式为YUV_420_888
。 - 分辨率和纵横比:您可以设置其中一个参数,但请注意不能同时设置这两个值。
- 旋转.
- 目标名称:此参数用于调试目的。
- 格式:CameraX 通过
- 图像流控制
应用可以设置分辨率或纵横比,但不能同时设置两者。确切的输出分辨率取决于应用请求的大小(或纵横比)和硬件功能,并且可能与请求的大小或纵横比不同。有关分辨率匹配算法的信息,请参阅 setTargetResolution()
的文档。
应用可以将输出图像像素配置为 YUV(默认)或 RGBA 颜色空间。当设置 RGBA 输出格式时,CameraX 会在内部将图像从 YUV 转换为 RGBA 颜色空间,并按以下顺序将图像位打包到 ImageProxy 第一个平面(其他两个平面未使用)的 ByteBuffer
中:
ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...
当执行复杂的图像分析,设备无法跟上帧速率时,您可以使用本主题操作模式部分中描述的策略来配置 CameraX 以丢弃帧。
创建分析器
应用可以通过实现 ImageAnalysis.Analyzer
接口并重写 analyze(ImageProxy image)
来创建分析器。在每个分析器中,应用都会收到一个 ImageProxy
,它是 Media.Image 的包装器。图像格式可以通过 ImageProxy.getFormat()
查询。该格式是应用通过 ImageAnalysis.Builder
提供的以下值之一:
- 如果应用请求
OUTPUT_IMAGE_FORMAT_RGBA_8888
,则为ImageFormat.RGBA_8888
。 - 如果应用请求
OUTPUT_IMAGE_FORMAT_YUV_420_888
,则为ImageFormat.YUV_420_888
。
请参阅构建 ImageAnalysis 用例,了解颜色空间配置以及像素字节的检索位置。
在分析器内部,应用应执行以下操作:
- 尽快分析给定帧,最好在给定帧速率时间限制内(例如,对于 30 fps 的情况,小于 32 毫秒)。如果应用无法足够快地分析帧,请考虑支持的帧丢弃机制之一。
- 通过调用
ImageProxy.close()
将ImageProxy
释放给 CameraX。请注意,您不应调用包装的 Media.Image 的关闭函数 (Media.Image.close()
)。
应用可以直接使用 ImageProxy 中包装的 Media.Image
。请不要在包装图像上调用 Media.Image.close()
,因为这会破坏 CameraX 内部的图像共享机制;相反,请使用 ImageProxy.close()
将底层 Media.Image
释放给 CameraX。
为图像分析配置分析器
创建分析器后,使用 ImageAnalysis.setAnalyzer()
注册它以开始分析。完成分析后,使用 ImageAnalysis.clearAnalyzer()
删除已注册的分析器。
图像分析只能配置一个活动分析器。ImageAnalysis.setAnalyzer()
调用会替换已注册的分析器(如果它已存在)。应用可以在绑定用例之前或之后随时设置新的分析器。
将 ImageAnalysis 绑定到生命周期
强烈建议使用 ProcessCameraProvider.bindToLifecycle()
函数将您的 ImageAnalysis
绑定到现有的 AndroidX 生命周期。请注意,bindToLifecycle()
函数会返回选定的 Camera
设备,可用于微调曝光等高级设置。有关控制相机输出的更多信息,请参阅此指南。
以下示例结合了前面步骤的所有内容,将 CameraX ImageAnalysis
和 Preview
用例绑定到 lifeCycle
所有者:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() // enable the following line if RGBA output is needed. // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy -> val rotationDegrees = imageProxy.imageInfo.rotationDegrees // insert your code here. ... // after done, release the ImageProxy object imageProxy.close() }) cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() // enable the following line if RGBA output is needed. //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(new Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() { @Override public void analyze(@NonNull ImageProxy imageProxy) { int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees(); // insert your code here. ... // after done, release the ImageProxy object imageProxy.close(); } }); cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);
其他资源
要了解更多关于 CameraX 的信息,请参阅以下其他资源。
Codelab
代码示例