在 图像分析 用例中,您的应用会获得一个 CPU 可访问的图像,您可以在其上执行图像处理、计算机视觉或机器学习推断。应用实现了一个 analyze()
方法,该方法在每一帧上运行。
要了解如何将 Google 的 ML Kit 集成到您的 CameraX 应用中,请参阅 ML Kit 分析器。
操作模式
当应用程序的分析管道无法满足 CameraX 的帧速率要求时,可以配置 CameraX 以以下方式丢帧
非阻塞(默认):在此模式下,执行器始终将最新图像缓存到图像缓冲区(类似于深度为 1 的队列),同时应用程序分析之前的图像。如果 CameraX 在应用程序完成处理之前收到新图像,则将新图像保存到同一缓冲区,覆盖之前的图像。请注意,
ImageAnalysis.Builder.setImageQueueDepth()
在此场景中无效,缓冲区内容始终会被覆盖。可以通过调用setBackpressureStrategy()
以及STRATEGY_KEEP_ONLY_LATEST
来启用此非阻塞模式。有关执行器影响的更多信息,请参阅STRATEGY_KEEP_ONLY_LATEST
的参考文档。阻塞:在此模式下,内部执行器可以将多个图像添加到内部图像队列,并且仅当队列已满时才开始丢帧。阻塞发生在整个摄像机设备范围内:如果摄像机设备绑定了多个用例,则所有用例在 CameraX 处理这些图像时都将被阻塞。例如,当预览和图像分析都绑定到摄像机设备时,预览也会在 CameraX 处理图像时被阻塞。可以通过将
STRATEGY_BLOCK_PRODUCER
传递给setBackpressureStrategy()
来启用阻塞模式。还可以使用 ImageAnalysis.Builder.setImageQueueDepth() 来配置图像队列深度。
对于总分析图像时间小于 CameraX 帧持续时间(例如,60fps 时为 16ms)的低延迟高性能分析器,两种操作模式都能提供流畅的整体体验。在某些情况下,阻塞模式仍然很有用,例如处理短暂的系统抖动时。
对于高延迟高性能分析器,需要使用更长的队列的阻塞模式来弥补延迟。但是请注意,应用程序仍然可以处理所有帧。
对于高延迟且耗时的分析器(分析器无法处理所有帧),非阻塞模式可能是更合适的选择,因为必须为分析路径丢弃帧,但其他并发绑定的用例仍然可以看到所有帧。
实现
要在应用程序中使用图像分析,请按照以下步骤操作
- 构建一个
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 用例。
在分析器内部,应用程序应执行以下操作
- 尽快分析给定帧,最好在给定的帧速率时间限制内(例如,对于 30fps,小于 32ms)。如果应用程序无法足够快地分析帧,请考虑使用 支持的帧丢弃机制 之一。
- 通过调用
ImageProxy.close()
将ImageProxy
释放回 CameraX。请注意,不应调用包装的 Media.Image 的关闭函数 (Media.Image.close()
)。
应用程序可以直接使用 ImageProxy 内部的包装的 Media.Image
。只需不要对包装的图像调用 Media.Image.close()
,因为这会破坏 CameraX 内部的图像共享机制;而应使用 ImageProxy.close()
将底层 Media.Image
释放回 CameraX。
为 ImageAnalysis 配置您的分析器
创建分析器后,使用 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
代码示例