為 CameraX ImageAnalysis 進行 YUV 到 RGB 的轉換

Android開發者發表於2021-12-24

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 中給我們反饋。

相關引用

歡迎您 點選這裡 向我們提交反饋,或分享您喜歡的內容、發現的問題。您的反饋對我們非常重要,感謝您的支援!

相關文章