使用 ARCore 做為機器學習模型的輸入內容

您可以使用 ARCore 在機器學習管道中擷取的攝影機畫面,打造智慧型擴增實境體驗。ARCore ML Kit 範例示範如何使用 ML KitGoogle Cloud Vision API 識別真實物件。這個範例使用機器學習模型將相機檢視畫面中的物件分類,並為虛擬場景中的物件加上標籤。

ARCore ML Kit 範例是以 Kotlin 編寫。此工具也提供 ARCore SDK GitHub 存放區中的 ml_kotlin 範例應用程式。

使用 ARCore 的 CPU 映像檔

根據預設,ARCore 會擷取至少兩組圖片串流:

  • 用於特徵辨識和圖片處理作業的 CPU 映像檔串流。根據預設,CPU 映像檔的解析度為 VGA (640x480)。如有需要,您可以將 ARCore 設定為使用其他解析度較高的圖片串流。
  • 一個 GPU 紋理串流,包含高解析度紋理,通常解析度為 1080p。這個功能通常會做為面向使用者的相機預覽畫面。這項資訊會儲存在 Session.setCameraTextureName() 指定的 OpenGL 紋理中。
  • SharedCamera.setAppSurfaces() 指定的任何其他串流。

CPU 映像檔大小注意事項

如果使用預設的 VGA 大小 CPU 串流,ARCore 會使用這個串流進行全球理解,因此不會產生額外費用。如要申請不同解析度的直播影片,可能需要耗用大量資源。提醒您,模型的解析度很快可能會增加費用,也就是將圖片的寬度和高度加倍給圖片中的像素量的兩倍。

如果模型在較低解析度的圖片上仍可獲得良好效能,則縮小圖片或許是有利的。

設定其他高解析度 CPU 映像檔串流

機器學習模型的效能取決於做為輸入圖片的解析度。如要調整這些串流的解析度,請使用 Session.setCameraConfig() 變更目前的 CameraConfig,並從 Session.getSupportedCameraConfigs() 中選取有效的設定。

Java

CameraConfigFilter cameraConfigFilter =
    new CameraConfigFilter(session)
        // World-facing cameras only.
        .setFacingDirection(CameraConfig.FacingDirection.BACK);
List<CameraConfig> supportedCameraConfigs =
    session.getSupportedCameraConfigs(cameraConfigFilter);

// Select an acceptable configuration from supportedCameraConfigs.
CameraConfig cameraConfig = selectCameraConfig(supportedCameraConfigs);
session.setCameraConfig(cameraConfig);

Kotlin

val cameraConfigFilter =
  CameraConfigFilter(session)
    // World-facing cameras only.
    .setFacingDirection(CameraConfig.FacingDirection.BACK)
val supportedCameraConfigs = session.getSupportedCameraConfigs(cameraConfigFilter)

// Select an acceptable configuration from supportedCameraConfigs.
val cameraConfig = selectCameraConfig(supportedCameraConfigs)
session.setCameraConfig(cameraConfig)

擷取 CPU 映像檔

使用 Frame.acquireCameraImage() 擷取 CPU 映像檔。您不再需要使用這些圖片時,應立即予以處置。

Java

Image cameraImage = null;
try {
  cameraImage = frame.acquireCameraImage();
  // Process `cameraImage` using your ML inference model.
} catch (NotYetAvailableException e) {
  // NotYetAvailableException is an exception that can be expected when the camera is not ready
  // yet. The image may become available on a next frame.
} catch (RuntimeException e) {
  // A different exception occurred, e.g. DeadlineExceededException, ResourceExhaustedException.
  // Handle this error appropriately.
  handleAcquireCameraImageFailure(e);
} finally {
  if (cameraImage != null) {
    cameraImage.close();
  }
}

Kotlin

// NotYetAvailableException is an exception that can be expected when the camera is not ready yet.
// Map it to `null` instead, but continue to propagate other errors.
fun Frame.tryAcquireCameraImage() =
  try {
    acquireCameraImage()
  } catch (e: NotYetAvailableException) {
    null
  } catch (e: RuntimeException) {
    // A different exception occurred, e.g. DeadlineExceededException, ResourceExhaustedException.
    // Handle this error appropriately.
    handleAcquireCameraImageFailure(e)
  }

// The `use` block ensures the camera image is disposed of after use.
frame.tryAcquireCameraImage()?.use { image ->
  // Process `image` using your ML inference model.
}

處理 CPU 映像檔

如要處理 CPU 映像檔,您可以使用多種機器學習程式庫。

在 AR 場景中顯示結果

一般來說,圖片辨識模型會透過指出所偵測到物件的中心點或邊界多邊形來輸出偵測到的物件。

使用模型輸出的定界框中心點或中心點,您可以將錨點附加至偵測到的物件。使用 Frame.hitTest() 預估虛擬場景中物體的姿勢。

IMAGE_PIXELS 座標轉換為 VIEW 座標:

Java

// Suppose `mlResult` contains an (x, y) of a given point on the CPU image.
float[] cpuCoordinates = new float[] {mlResult.getX(), mlResult.getY()};
float[] viewCoordinates = new float[2];
frame.transformCoordinates2d(
    Coordinates2d.IMAGE_PIXELS, cpuCoordinates, Coordinates2d.VIEW, viewCoordinates);
// `viewCoordinates` now contains coordinates suitable for hit testing.

Kotlin

// Suppose `mlResult` contains an (x, y) of a given point on the CPU image.
val cpuCoordinates = floatArrayOf(mlResult.x, mlResult.y)
val viewCoordinates = FloatArray(2)
frame.transformCoordinates2d(
  Coordinates2d.IMAGE_PIXELS,
  cpuCoordinates,
  Coordinates2d.VIEW,
  viewCoordinates
)
// `viewCoordinates` now contains coordinates suitable for hit testing.

使用這些 VIEW 座標執行命中測試,並根據結果建立錨點:

Java

List<HitResult> hits = frame.hitTest(viewCoordinates[0], viewCoordinates[1]);
HitResult depthPointResult = null;
for (HitResult hit : hits) {
  if (hit.getTrackable() instanceof DepthPoint) {
    depthPointResult = hit;
    break;
  }
}
if (depthPointResult != null) {
  Anchor anchor = depthPointResult.getTrackable().createAnchor(depthPointResult.getHitPose());
  // This anchor will be attached to the scene with stable tracking.
  // It can be used as a position for a virtual object, with a rotation prependicular to the
  // estimated surface normal.
}

Kotlin

val hits = frame.hitTest(viewCoordinates[0], viewCoordinates[1])
val depthPointResult = hits.filter { it.trackable is DepthPoint }.firstOrNull()
if (depthPointResult != null) {
  val anchor = depthPointResult.trackable.createAnchor(depthPointResult.hitPose)
  // This anchor will be attached to the scene with stable tracking.
  // It can be used as a position for a virtual object, with a rotation prependicular to the
  // estimated surface normal.
}

效能注意事項

請按照下列建議,節省處理能力並減少能源消耗:

  • 請勿在每次傳入影格上執行機器學習模型。請考慮改用低影格速率執行物件偵測。
  • 考慮使用線上機器學習推論模型來減少運算複雜度。

後續步驟