[ Web Proxy ]
URL:
Viewing: https://developers.google.com/ar/develop/java/machine-learning [Back]  [Original]

Use ARCore as input for Machine Learning models  |  Google for Developers Skip to main content
Send feedback

Use ARCore as input for Machine Learning models Stay organized with collections Save and categorize content based on your preferences.

outlined_flag
Your browser does not support the video tag.

You can use the camera feed that ARCore captures in a machine learning pipeline to create an intelligent augmented reality experience. The ARCore ML Kit sample demonstrates how to use ML Kit and the Google Cloud Vision API to identify real-world objects. The sample uses a machine learning model to classify objects in the camera's view and attaches a label to the object in the virtual scene.

The ARCore ML Kit sample is written in Kotlin. It is also available as the ml_kotlin sample app in the ARCore SDK GitHub repository.

Use ARCore's CPU image

ARCore captures at least two sets of image streams by default:

CPU image size considerations

No additional cost is incurred if the default VGA-sized CPU stream is used because ARCore uses this stream for world comprehension. Requesting a stream with a different resolution may be expensive, as an additional stream will need to be captured. Keep in mind that a higher resolution may quickly become expensive for your model: doubling the width and height of the image quadruples the amount of pixels in the image.

It may be advantageous to downscale the image, if your model can still perform well on a lower resolution image.

Configure an additional high resolution CPU image stream

The performance of your ML model may depend on the resolution of the image used as input. The resolution of these streams can be adjusted by changing the current CameraConfig using Session.setCameraConfig(), selecting a valid configuration from 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)

Retrieve the CPU image

Retrieve the CPU image using Frame.acquireCameraImage(). These images should be disposed of as soon as they're no longer needed.

Note: Frame.acquireCameraImage() can throw NotYetAvailableException for several frames after session start, and for a few frames at a time while the session is running. Ensure that your application can handle this case.

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

Process the CPU image

To process the CPU image, various machine learning libraries can be used.

Display results in your AR scene

Image recognition models often output detected objects by indicating a center point or a bounding polygon representing the detected object.

Using the center point or center of the bounding box that is output from the model, it's possible to attach an anchor to the detected object. Use Frame.hitTest() to estimate the pose of an object in the virtual scene.

Important: Frame.hitTest() expects coordinates in the VIEW coordinate system. Since your model uses the CPU stream, your model will give results in the IMAGE_PIXELS coordinate system. Use Frame.transformCoordinates2d() to convert between the systems.

Convert IMAGE_PIXELS coordinates to VIEW coordinates:

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.

Use these VIEW coordinates to conduct a hit test and create an anchor from the result:

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

Performance considerations

Follow the following recommendations to save processing power and consume less energy:

Next steps

Send feedback

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2024-10-31 UTC.

Need to tell us more? [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2024-10-31 UTC."],[],["ARCore's camera feed can be used in machine learning to enhance AR experiences. The CPU image stream, primarily for image processing, can be retrieved via `Frame.acquireCameraImage()`. Image resolution can be configured via `Session.setCameraConfig()`. ML Kit or Firebase can process these images; `InputImage.fromMediaImage` is used for CPU image conversion. Model output coordinates, in `IMAGE_PIXELS`, are converted to `VIEW` coordinates for hit testing via `Frame.transformCoordinates2d()`, enabling anchor placement with `Frame.hitTest()`. Performance can be improved by avoiding frame-by-frame processing.\n"]]

Web Proxy Viewer  |  New URL  |  Original Page