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

Raw Depth API 為相機圖片提供深度資料,該圖片的準確度高於完整深度 API,但不一定能涵蓋每個像素。系統也可以進一步處理原始景深圖片和相應的可信度圖片,讓應用程式只使用足以滿足自身用途的深度資料。

裝置相容性

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

與完整深度 API 比較

Raw Depth API 可提供精準度較高的深度預估值,但原始深度圖片不一定會包含相機影像中所有像素的深度估計。相較之下,完整的 Depth API 可提供每個像素的預估深度,但由於深度估計數據的平滑和內插,各像素深度資料可能較不準確。這兩種 API 的深度圖片格式和大小都相同。只有內容不同。

下表說明 Raw Depth API 與完整深度 API 之間的差異,其中使用椅子的圖片和廚房中的桌子。

API 傳回 相機圖片 深度圖片 「可信度」圖片
原始深度 API
  • 原始景深圖片,包含相機影像中部分 (而非全部) 像素的精確深度估計。
  • 可信賴每個原始深度圖片像素的信心圖片。如果相機圖片像素沒有深度估計,信賴水準為零。
完整深度 API
  • 單張「光滑」的深度圖片,內含每個像素的深度預估值。
  • 這個 API 未提供任何可信度圖片。
不適用

可信度圖片

Raw Depth API 傳回的可信度圖片中,較亮的像素可信度較高,其中白色的像素代表完全信賴水準,黑色像素則代表沒有信心。一般來說,相較於其他區域 (例如一塊空白牆壁),相機圖像中相機圖像較有紋理較高的區域 (例如樹木) 會有較高的原始深度。沒有紋理的表面通常產生的信賴水準為零。

如果目標裝置有支援的硬體深度感應器,即使圖像位置與相機相距甚遠,即使在無紋理的表面上,信賴水準可能還是會較高。

運算費用

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.
}

後續步驟