为 CameraX ImageAnalysis 进行 YUV 到 RGB 的转换

为 CameraX ImageAnalysis 进行 YUV 到 RGB 的转换

CameraX 是一个旨在帮助开发者简化相机应用开发工作的 Jetpack 支持库。它支持多种诸如 ImageCapture、Preview 和 ImageAnalysis 这种可以和 ML KitTensorFlow Lite 无缝结合的使用场景。这为文本识别、图像标记等应用的开发提供了可能,甚至还可以支持使用开发者自己训练的 TensorFlow Lite 模型进行物体的识别和检测。然而,在 CameraX 和这些库之间进行图像格式转换的工作还是比较费时费力的。本文我们会介绍最近为 CameraX ImageAnalysis 带来的新功能,支持从 YUV 到 RGB 的转换,我们会介绍一些背景知识,为什么会引入该功能,并会以少量的示例代码来介绍如何使用它。

背景

CameraX 使用 YUV420_888 来生成图像,该格式有 8 位的 Luma(Y)、Chroma(U, V) 和 Paddings(P) 三个通道。YUV 是一种通用且灵活的格式,它支持不同的设备上的 OEM 变体,这就覆盖了很多 ImageAnalysis 的使用场景。然而很多应用依然依赖 RGB 格式。在我们的开发者社区,YUV 到 RGB 的转换是呼声最高的功能之一,因为 RGB 格式流行且易于使用,且有时需要在 TensorFlow Lite 模型中使用。让我们先来看看 YUV 和 RGB 格式。

YUV_420_888 格式

YUV 格式也可以被称为 "YCbCr",它包括平面 (planar,如 I420)、半平面 (semi-planar,如 NV21/NV12) 和打包 (packed,如 UYVY) 格式。YUV_420_888 是一种通用的 YCbCr 格式,它能够表示任何 4:2:0 色度二次采样的平面或半平面缓冲区 (但不完全交错),每个颜色样本有 8 位。且能够保证 Y 平面不会与 U/V 平面交错 (且像素步长始终为 1),以及 U/V 平面总是具有相同的行步长和像素步长。


RGBA_8888 格式

RGBA_8888 是一种标准的具有红、绿、蓝和 alpha 通道的 RGB 格式,每个通道有 8 位。主要的转换对象是 RGB 颜色空间,RGB 因为色差变化较少,相对来说比较简单。

API 实现

我们评估了三种将 YUV 转换为 RGB 的方法:

  1. 使用 Java/Kotlin
  2. 使用 Renderscript 渲染脚本
  3. 原生方案 (使用 C/C++ 和 NDK)

使用 Java/Kotlin 来实现对图片的处理需要长时间的计算,并面临着垃圾回收带来的压力。而 Renderscript 是面向计算密集型任务 (比如从 YUV 转换为 RGB 格式) 的一个候选方案,然而从 Android 12 开始,这种方法已经被 废弃 了。

考虑到之后的扩展性和兼容性,我们决定使用原生方案 (libyuv + NDK)。Libyuv 是一个开源项目,它包含了对 YUV 的缩放、转换和旋转功能。综合所有因素,宏观上来看,CameraX 颜色转换的 pipeline 如下图:

为了向后兼容,我们依然使用 ImageProxy 作为输出。ImageProxy 是 media.image 的一个封装类,它是 Android framework 中提供的一个图片缓冲。Java/Kotlin 层可以从 Surface 中通过 dequeueInputImage() 获得一个输入的 Image,然后使用 ImageReaderImageWriter 将 Image 数据写入其中,从而得到一个转换后的 Image。由于 ImageWriter 是在 API 23 中添加的,我们使用 ANativeWindow 以及其缓冲区来产生 RGBA 格式的输出图像,以支持更多的 API 级别。

对于输入数据,我们在 CameraX 内部支持 YUV_420_888 格式的不同变体 (I420,NV12,NV21 等)。对于输出数据,我们现在支持 RGBA 格式,但将来会扩展到更多其他的 RGB 格式。

由于我们使用 libyuv 作为新的依赖库,我们的库大小增加了大约 50 KB

API 使用

CameraX 1.1.0-alpha08 版本开始,应用可以通过在 ImageAnalysis 配置中使用 setOutputImageFormat 来选择 YUV_420_888 或者 RGBA_8888 的图片输出格式。

一旦选择了 RGBA_8888,输出的图片格式将会是 PixelFormat.RGBA_8888,它只有一个带有填充的图像平面 (逐个 R,G,B,A 的像素)。原则上 Android framework 支持的图像缓冲区格式是 PixelFormat 和 ImageFormat 的子集。

相比之下,如果选择了 YUV_420_888,输出的图片格式将是 ImageFormat.YUV_420_888,它有 3 个独立的图像平面 (Y,U,V)。

性能

我们做了一些性能测试,并与在不同的 Android 版本和设备上使用 Renderscript 的结果进行了比较。总体上来说,在不同分辨率和 Android 系统版本上,使用 libyuv 的 pipeline 要优于使用 Renderscript 的实现。




总结

我们在 CameraX ImageAnalysis pipeline 中支持了 YUV 到 RGB 的转换。用户现在可以简单地为一个 ImageAnalysis 用例选择一个输出格式 (YUV_420_888 或 RGBA_8888),并用于其他库之中。而这仅仅是一个开始,我们还计划在 CameraX ImageAnalysis pipeline 中增加更多的图像处理功能,并将其扩展到其他的用例中 (例如 ImageCapture 或 Preview 等)。如果您有任何功能上的需求,请联系我们。

YUV 到 RGB 转换的示例代码可以在 GitHub 中查看。若需了解更多关于 CameraX 的消息,请参考 官方文档。若要了解关于 CameraX 的最新进展,您可以加入 CameraX 讨论区。另外,您的反馈对我们来说十分具有价值,欢迎随时在 CameraX 讨论区留言或在官方的 Issue Tracker 中给我们反馈。

相关引用

欢迎您 点击这里 向我们提交反馈,或分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!

版权声明

禁止一切形式的转载-禁止商用-禁止衍生 申请授权

脉脉不得语
脉脉不得语
Zhengzhou Website
Android Developer | https://androiddevtools.cn and https://androidweekly.io Funder | GDG Zhengzhou Funder & Ex Organizer | http://Toast.show(∞) Podcast Host

你已经成功订阅到 Android 开发技术周报
太棒了!接下来,完成检验以获得全部访问权限 Android 开发技术周报
欢迎回来!你已经成功登录了。
Unable to sign you in. Please try again.
成功!您的帐户已完全激活,您现在可以访问所有内容。
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.
🍗