ARCore als Eingabe für Modelle für maschinelles Lernen verwenden

Mit dem Kamerafeed, den ARCore in einer Pipeline für maschinelles Lernen erfasst, kannst du intelligente Augmented-Reality-Erlebnisse schaffen. Im ARCore ML Kit-Beispiel wird gezeigt, wie mit ML Kit und der Google Cloud Vision API reale Objekte identifiziert werden. Im Beispiel wird ein Modell für maschinelles Lernen verwendet, um Objekte im Sichtfeld der Kamera zu klassifizieren. Außerdem wird dem Objekt in der virtuellen Szene ein Label hinzugefügt.

Das ARCore ML Kit-Beispiel ist in Kotlin geschrieben. Sie ist auch als ml_kotlin-Beispiel-App im GitHub-Repository des ARCore SDK verfügbar.

ARCore-CPU-Image verwenden

ARCore erfasst standardmäßig mindestens zwei Gruppen von Bildstreams:

  • Ein CPU-Bildstream, der für die Featureerkennung und Bildverarbeitung verwendet wird. Standardmäßig hat das CPU-Bild eine VGA-Auflösung (640 x 480). ARCore kann so konfiguriert werden, dass bei Bedarf zusätzliche Bildstreams mit höherer Auflösung verwendet werden.
  • Ein GPU-Texturstream, der eine hochauflösende Textur enthält, die in der Regel eine Auflösung von 1080p hat. Dies wird in der Regel als Kameravorschau für Nutzer verwendet. Sie wird in der von Session.setCameraTextureName() angegebenen OpenGL-Textur gespeichert.
  • Alle zusätzlichen Streams, die von SharedCamera.setAppSurfaces() angegeben werden.

Überlegungen zur CPU-Bildgröße

Es fallen keine zusätzlichen Kosten an, wenn der standardmäßige CPU-Stream in VGA-Größe verwendet wird, da ARCore diesen Stream für alle Nutzer verwendet. Das Anfordern eines Streams mit einer anderen Auflösung kann teuer sein, da ein zusätzlicher Stream erfasst werden muss. Beachten Sie, dass eine höhere Auflösung für Ihr Modell schnell teuer werden kann: Durch die Verdoppelung der Breite und Höhe des Bildes wird die Anzahl der Pixel im Bild vervierfacht.

Es kann von Vorteil sein, das Bild zu verkleinern, wenn Ihr Modell auch mit einem Bild mit niedrigerer Auflösung gut funktioniert.

Zusätzlichen hochauflösenden CPU-Bildstream konfigurieren

Die Leistung Ihres ML-Modells kann von der Auflösung des als Eingabe verwendeten Bilds abhängen. Die Auflösung dieser Streams kann angepasst werden, indem du den aktuellen CameraConfig mit Session.setCameraConfig() änderst und eine gültige Konfiguration aus Session.getSupportedCameraConfigs() auswählst.

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-Image abrufen

Rufen Sie das CPU-Image mit Frame.acquireCameraImage() ab. Sie sollten diese Bilder entsorgen, sobald sie nicht mehr benötigt werden.

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-Bild verarbeiten

Zur Verarbeitung des CPU-Images können verschiedene Bibliotheken für maschinelles Lernen verwendet werden.

Ergebnisse in deiner AR-Szene anzeigen

Bilderkennungsmodelle geben erkannte Objekte häufig aus, indem sie einen Mittelpunkt oder ein Begrenzungspolygon angeben, das das erkannte Objekt darstellt.

Mithilfe des Mittelpunkts oder Mittelpunkts des Begrenzungsrahmens, der vom Modell ausgegeben wird, kann ein Anker an das erkannte Objekt angehängt werden. Verwenden Sie Frame.hitTest(), um die Position eines Objekts in der virtuellen Szene einzuschätzen.

IMAGE_PIXELS-Koordinaten in VIEW-Koordinaten umwandeln:

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.

Verwenden Sie diese VIEW-Koordinaten, um einen Treffertest durchzuführen und aus dem Ergebnis einen Anker zu erstellen:

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

Hinweise zur Leistung

Befolgen Sie die folgenden Empfehlungen, um Prozessorleistung zu sparen und weniger Energie zu verbrauchen:

  • Führen Sie Ihr ML-Modell nicht bei jedem eingehenden Frame aus. Du kannst die Objekterkennung stattdessen mit einer niedrigen Framerate ausführen.
  • Ziehen Sie ein Online-ML-Inferenzmodell in Betracht, um die Rechenkomplexität zu reduzieren.

Nächste Schritte