Utiliser ARCore en tant qu'entrée pour les modèles de machine learning

Vous pouvez utiliser le flux de caméra enregistré par ARCore dans un pipeline de machine learning pour créer une expérience intelligente de réalité augmentée. L'exemple ML Kit ARCore montre comment identifier des objets réels à l'aide de ML Kit et de l'API Google Cloud Vision. L'exemple utilise un modèle de machine learning pour classer les objets dans le champ de la caméra et y associe une étiquette.

L'exemple ARCore ML Kit est écrit en Kotlin. Elle est également disponible en tant qu'application exemple ml_kotlin dans le dépôt GitHub du SDK ARCore.

Utiliser l'image de processeur d'ARCore

ARCore capture au moins deux ensembles de flux d'images par défaut:

  • Flux d'images du processeur utilisé pour la reconnaissance de caractéristiques et le traitement des images. Par défaut, la résolution de l'image du processeur est VGA (640 x 480 pixels). ARCore peut être configuré pour utiliser un flux d'images de résolution supérieure, si nécessaire.
  • Un flux de texture GPU qui contient une texture haute résolution, généralement à une résolution de 1080p Cette image est généralement utilisée comme aperçu de l'appareil photo face à l'utilisateur. Il est stocké dans la texture OpenGL spécifiée par Session.setCameraTextureName().
  • Tous les flux supplémentaires spécifiés par SharedCamera.setAppSurfaces().

Considérations relatives à la taille des images pour le processeur

L'utilisation du flux de processeur par défaut de la taille VGA par défaut n'engendre aucun coût supplémentaire, car ARCore l'utilise pour permettre aux utilisateurs de comprendre le monde entier. Demander un flux avec une résolution différente peut s'avérer coûteux, car vous devez capturer un flux supplémentaire. Gardez à l'esprit qu'une résolution supérieure peut rapidement devenir coûteuse pour votre modèle: si vous doublez la largeur et la hauteur de l'image, le nombre de pixels dans l'image sera quadruplé.

Il peut s'avérer avantageux de réduire l'échelle de l'image si votre modèle peut toujours être performant sur une image de résolution inférieure.

Configurer un flux d'images de processeur haute résolution supplémentaire

Les performances de votre modèle de ML peuvent dépendre de la résolution de l'image utilisée en entrée. Vous pouvez ajuster la résolution de ces flux en modifiant le CameraConfig actuel à l'aide de Session.setCameraConfig(), en sélectionnant une configuration valide dans 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)

Récupérer l'image de processeur

Récupérez l'image de processeur à l'aide de Frame.acquireCameraImage(). Vous devez supprimer ces images dès qu'elles ne sont plus nécessaires.

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

Traiter l'image de processeur

Différentes bibliothèques de machine learning peuvent être utilisées pour traiter l'image du processeur.

Afficher les résultats dans votre scène de RA

Les modèles de reconnaissance d'image génèrent souvent des objets détectés en indiquant un point central ou un polygone de délimitation représentant l'objet détecté.

En utilisant le point central ou le centre du cadre de délimitation généré par le modèle, il est possible d'associer une ancre à l'objet détecté. Utilisez Frame.hitTest() pour estimer la position d'un objet dans la scène virtuelle.

Convertissez les coordonnées IMAGE_PIXELS en coordonnées 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.

Utilisez ces coordonnées VIEW pour effectuer un test de positionnement et créer une ancre à partir du résultat:

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

Considérations sur les performances

Suivez les recommandations suivantes pour économiser la puissance de traitement et la consommation d'énergie:

  • N'exécutez pas votre modèle de ML sur chaque trame entrante. Envisagez plutôt d'exécuter la détection d'objets à une fréquence d'images faible.
  • Envisagez d'utiliser un modèle d'inférence ML en ligne pour réduire la complexité des calculs.

Étapes suivantes