Mit dem Kamerafeed, den ARCore in einer Pipeline für maschinelles Lernen aufzeichnet, können Sie ein intelligentes Augmented-Reality-Erlebnis schaffen. Im ARCore ML Kit-Beispiel wird gezeigt, wie Sie mit ML Kit und der Google Cloud Vision API reale Objekte identifizieren können. Im Beispiel wird ein Modell für maschinelles Lernen verwendet, um Objekte in der Kameraansicht zu klassifizieren und dem Objekt in der virtuellen Szene ein Label hinzuzufügen.
Das ARCore ML Kit-Beispiel ist die in Kotlin geschrieben wurden. Sie ist auch als ml_kotlin-Beispiel verfügbar. App im ARCore SDK GitHub-Repository
CPU-Image von ARCore verwenden
ARCore erfasst standardmäßig mindestens zwei Bildstreams:
- Ein CPU-Bildstream, der für die Featureerkennung und Bildverarbeitung verwendet wird. Standardmäßig hat das CPU-Image eine Auflösung von VGA (640 x 480). ARCore kann bei Bedarf so konfiguriert werden, dass ein zusätzlicher Bildstream mit höherer Auflösung verwendet wird.
- Ein GPU-Texturstream, der eine hochauflösende Textur enthält, normalerweise eine Auflösung von 1080p. Sie wird in der Regel als eine für den Nutzer sichtbare Kameravorschau verwendet.
Sie wird in der OpenGL-Textur gespeichert, die durch
Session.setCameraTextureName()
angegeben wird. - Alle zusätzlichen Streams, die durch
SharedCamera.setAppSurfaces()
angegeben werden.
Überlegungen zur CPU-Image-Größe
Wenn der standardmäßige CPU-Stream in VGA-Größe verwendet wird, fallen keine zusätzlichen Kosten an, da ARCore diesen Stream zum besseren Verständnis nutzt. Das Anfordern eines Streams mit einer anderen Auflösung kann teuer sein, da ein zusätzlicher Stream aufgenommen werden muss. Beachten Sie, dass eine höhere Auflösung für Ihr Modell schnell teuer werden kann: Die Verdoppelung von Breite und Höhe des Bildes vervierfacht die Anzahl der Pixel im Bild.
Es kann vorteilhaft sein, das Bild zu verkleinern, sofern Ihr Modell auch bei Bildern mit geringerer Auflösung gut funktioniert.
Zusätzlichen CPU-Bildstream mit hoher Auflösung 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. Ändern Sie dazu den aktuellen CameraConfig
mit Session.setCameraConfig()
und wählen Sie eine gültige Konfiguration aus Session.getSupportedCameraConfigs()
aus.
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-Image verarbeiten
Zur Verarbeitung des CPU-Images können verschiedene Bibliotheken für maschinelles Lernen verwendet werden.
- ML Kit: ML Kit bietet eine On-Device-API zur Objekterkennung und -verfolgung.
In die API ist ein grober Klassifikator integriert und Sie können auch benutzerdefinierte Klassifizierungsmodelle verwenden, um einen kleineren Bereich von Objekten abzudecken.
Verwenden Sie
InputImage.fromMediaImage
, um Ihr CPU-Image in einInputImage
zu konvertieren. - Firebase Machine Learning: Firebase bietet APIs für maschinelles Lernen, die entweder in der Cloud oder auf dem Gerät ausgeführt werden können. Weitere Informationen finden Sie in der Firebase-Dokumentation zum sicheren Labeling von Bildern mit Cloud Vision mithilfe von Firebase Auth und Firebase-Funktionen unter Android.
Ergebnisse in AR-Szenen anzeigen
Bilderkennungsmodelle geben häufig erkannte Objekte aus, indem sie einen Mittelpunkt oder ein Begrenzungspolygon für das erkannte Objekt angeben.
Durch Verwenden des Mittelpunkts oder der Mitte des Begrenzungsrahmens, der vom Modell ausgegeben wird, ist es möglich, einen Anker an das erkannte Objekt anzuhängen. Mit Frame.hitTest()
können Sie die Position eines Objekts in der virtuellen Szene schä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 einen Anker aus dem Ergebnis 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
Folgen Sie den folgenden Empfehlungen, um Rechenleistung zu sparen und Energie zu sparen:
- Führen Sie Ihr ML-Modell nicht für jeden eingehenden Frame aus. Erwägen Sie stattdessen, die Objekterkennung mit einer niedrigen Framerate auszuführen.
- Erwägen Sie ein Online-ML-Inferenzmodell, um die Rechenkomplexität zu reduzieren.