在 Android 應用程式中使用原始深度

Raw Depth API 可為相機影像提供深度資料,其準確率高於「Full Depth API」(完整的深度 API 資料),但不一定能涵蓋每個像素。我們還可進一步處理原始深度圖片及相應的可信度圖片,讓應用程式只使用準確符合其個別用途的深度資料。

裝置相容性

凡是支援 Depth API 的裝置都能使用原始深度。原始 Depth API 和完整 Depth API 一樣,不需要支援的硬體深度感應器,例如飛行時間 (ToF) 感應器。不過,Raw Depth API 和完整的 Depth API 都會使用裝置可能支援的硬體感應器。

原始深度 API 與完整的深度 API

使用 Raw Depth API 可提高深度估算的準確度,但原始深度圖像可能不包含相機影像中所有像素的深度預估。相較之下,完整的深度 API 為每個像素提供預估深度,但每個像素深度資料可能會因為深度估計和內插值而較不準確。這兩種 API 的深度圖片格式和大小皆相同。只有內容不同。

下表以廚房椅子和一張桌子的圖片,說明 Raw Depth API 與完整 Depth API 之間的差異。

API 傳回 相機圖片 厚度圖片 「信心」圖片
原始深度 API
  • 原始的深度圖片,內含非常精確的相機圖像中某些像素 (但並非全部) 的深度預估。
  • 能使每個原始深度圖片像素充滿信心的信心圖片。沒有深度預估的相機圖片像素信賴值為零。
深度 API
  • 一個「順暢」包含每個像素的深度預估。
  • 這個 API 未提供可信度圖片。

圖片可信度

在 Raw Depth API 傳回的信心圖片中,光線較淺的像素有較高的可信度值,白色的像素代表完全信心,黑色的像素則代表沒有信心。一般而言,相機圖像中紋理較高的區域 (例如樹木) 擁有較高的原始深度可信度,遠地高於不含空白牆面等區域。沒有紋理的表面通常會產生 0 的信賴度。

如果目標裝置具備支援的硬體深度感應器,即使在無紋理的表面上,圖片中靠近相機區域的可信度也可能會比較高。

運算費用

Raw Depth API 的運算成本約為完整 Depth API 運算成本的一半。

用途

使用 Raw Depth API 取得深度圖片,可以更詳細地呈現場景中的物件幾何圖形。如要打造 AR 體驗,原始深度資料就相當實用,因為此時必須提高深度準確度和細節,才能處理幾何圖形理解工作。某些用途包括:

  • 3D 重建
  • 評估
  • 形狀偵測

必要條件

請務必瞭解基本 AR 概念 以及如何在繼續操作前設定 ARCore 工作階段

啟用深度

新的 ARCore 工作階段中,檢查使用者的裝置是否支援深度。基於處理電源限制,並非所有與 ARCore 相容的裝置都支援 Depth API。為節省資源,ARCore 預設會停用深度功能。啟用深度模式,讓應用程式使用 Depth API。

Java

Config config = session.getConfig();

// Check whether the user's device supports Depth.
if (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
  // Enable depth mode.
  config.setDepthMode(Config.DepthMode.AUTOMATIC);
}
session.configure(config);

Kotlin

if (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
  session.configure(session.config.apply { depthMode = Config.DepthMode.AUTOMATIC })
}

取得最新的原始深度和信心圖片

呼叫 frame.acquireRawDepthImage16Bits() 即可取得最新的原始深度圖片。透過 Raw Depth API 傳回的所有圖片像素不一定都包含深度資料,且並非每個 ARCore 影格都會包含新的原始深度圖片。如要確認目前影格的原始深度圖片是否為新的圖片,請比對圖片的時間戳記與前一個原始深度圖片的時間戳記。如果時間戳記不同,原始深度圖片就會以新的深度資料為基礎。其他情況下,深度圖片會重新預測過去的深度資料。

呼叫 frame.acquireRawDepthConfidenceImage() 取得信心圖片。你可以透過可信度圖片檢查每個原始深度像素的準確度。可信度圖片會以 Y8 格式傳回。每個像素都是 8 位元無正負號整數。0 表示信賴水準最低,255 表示最多信賴水準。

Java

// Use try-with-resources, so that images are released automatically.
try (
// Depth image is in uint16, at GPU aspect ratio, in native orientation.
Image rawDepth = frame.acquireRawDepthImage16Bits();
    // Confidence image is in uint8, matching the depth image size.
    Image rawDepthConfidence = frame.acquireRawDepthConfidenceImage(); ) {
  // Compare timestamps to determine whether depth is is based on new
  // depth data, or is a reprojection based on device movement.
  boolean thisFrameHasNewDepthData = frame.getTimestamp() == rawDepth.getTimestamp();
  if (thisFrameHasNewDepthData) {
    ByteBuffer depthData = rawDepth.getPlanes()[0].getBuffer();
    ByteBuffer confidenceData = rawDepthConfidence.getPlanes()[0].getBuffer();
    int width = rawDepth.getWidth();
    int height = rawDepth.getHeight();
    someReconstructionPipeline.integrateNewImage(depthData, confidenceData, width, height);
  }
} catch (NotYetAvailableException e) {
  // Depth image is not (yet) available.
}

Kotlin

try {
  // Depth image is in uint16, at GPU aspect ratio, in native orientation.
  frame.acquireRawDepthImage16Bits().use { rawDepth ->
    // Confidence image is in uint8, matching the depth image size.
    frame.acquireRawDepthConfidenceImage().use { rawDepthConfidence ->
      // Compare timestamps to determine whether depth is is based on new
      // depth data, or is a reprojection based on device movement.
      val thisFrameHasNewDepthData = frame.timestamp == rawDepth.timestamp
      if (thisFrameHasNewDepthData) {
        val depthData = rawDepth.planes[0].buffer
        val confidenceData = rawDepthConfidence.planes[0].buffer
        val width = rawDepth.width
        val height = rawDepth.height
        someReconstructionPipeline.integrateNewImage(
          depthData,
          confidenceData,
          width = width,
          height = height
        )
      }
    }
  }
} catch (e: NotYetAvailableException) {
  // Depth image is not (yet) available.
}

後續步驟