将 ARCore 用作机器学习模型的输入

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">

您可以在机器学习管道中使用 ARCore 捕获的摄像头画面打造智能的增强现实体验。 ARCore 机器学习套件示例演示了如何使用机器学习套件Google Cloud Vision API 识别真实对象。 此示例使用机器学习模型对摄像头视图中的对象进行分类,并将标签附加到虚拟场景中的对象。

ARCore 机器学习套件示例 用 Kotlin 编写。它还以 ml_kotlin 示例的形式提供 应用(位于 ARCore SDK 中) GitHub 代码库。

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

性能考虑因素

请遵循以下建议以节省处理能力并降低能耗:

  • 不要对每个传入帧运行机器学习模型。请考虑改为以较低的帧速率运行对象检测。
  • 考虑使用在线机器学习推理模型来降低计算复杂性。

后续步骤