圖片分析

圖片分析用途可為您的應用程式提供方便 CPU 存取的圖片,以便執行圖片處理作業、電腦視覺或機器學習推論。應用程式會導入在每個影格上執行的 analyze() 方法。

如要瞭解如何整合 Google 的 ML Kit 與 CameraX 應用程式,請參閱「ML Kit 分析工具」一文。

運作模式

如果應用程式的分析管道無法達到 CameraX 的畫面更新率要求,您可以透過下列任一種方式將 CameraX 設定為捨棄影格:

  • 非封鎖 (預設):在此模式下,應用程式分析上一張圖片時,執行程式一律會將最新的圖片快取至圖片緩衝區 (類似深度為一的佇列)。如果 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 毫秒),則不論是上述何種操作模式,都能提供順暢的體驗。封鎖模式在某些情況下仍是有用的,例如處理極短暫的系統時基誤差。

使用高延遲和高效能分析工具時,需要較長佇列的封鎖模式才能彌補延遲。但請注意,應用程式仍可以處理所有影格。

搭配使用高延遲和耗時的分析工具時 (分析工具無法處理所有影格),非封鎖模式可能較為適用,因為分析路徑必須捨棄影格,但其他並行的繫結用途仍能看到所有影格。

導入作業

如要在應用程式中使用圖片分析,請按照下列步驟操作:

繫結後,CameraX 會立即將圖片傳送至已註冊的分析工具。完成分析後,請呼叫 ImageAnalysis.clearAnalyzer() 或解除 ImageAnalysis 用途的繫結,以便停止分析。

建構 ImageAnalysis 應用實例

ImageAnalysis 會將分析工具 (圖片使用者) 連結至 CameraX,後者是圖片製作者。應用程式可以使用 ImageAnalysis.Builder 建構 ImageAnalysis 物件。透過 ImageAnalysis.Builder,應用程式可以設定下列項目:

應用程式可以設定解析度或顯示比例,但不能同時設定。確切的輸出解析度取決於應用程式要求的大小 (或顯示比例) 及硬體功能,且可能會與要求的大小或比例不同。如需解析度比對演算法的相關資訊,請參閱 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 提供的下列其中一個值:

  • ImageFormat.RGBA_8888 (如果應用程式要求 OUTPUT_IMAGE_FORMAT_RGBA_8888)。
  • ImageFormat.YUV_420_888 (如果應用程式要求 OUTPUT_IMAGE_FORMAT_YUV_420_888)。

請參閱「建構 ImageAnalysis 用途」瞭解色域設定,以及可以擷取像素位元組的位置。

在分析工具中,應用程式應執行以下操作:

  1. 最好在特定的畫面更新率時間限制內 (例如 30 fps 時不超過 32 毫秒),盡快分析特定影格。如果應用程式無法快速分析影格,請考慮使用其中一種支援的影格捨棄機制
  2. 呼叫 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 ImageAnalysisPreview 用途繫結至 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,請參閱下列其他資源。

程式碼研究室

  • 開始使用 CameraX
  • 程式碼範例

  • CameraX 範例應用程式