Usa ARCore come input per i modelli di machine learning

Puoi utilizzare il feed videocamera acquisito da ARCore in una pipeline di machine learning per creare un'esperienza di realtà aumentata intelligente. L'esempio di ARCore ML Kit mostra come utilizzare ML Kit e l'API Google Cloud Vision per identificare oggetti reali. L'esempio utilizza un modello di machine learning per classificare gli oggetti nel campo visivo della videocamera e assegna un'etichetta all'oggetto nella scena virtuale.

L'esempio di ARCore ML Kit è scritto in Kotlin. È disponibile anche come campione ml_kotlin nell'SDK ARCore GitHub di ASL.

Utilizza immagine CPU di ARCore

ARCore acquisisce almeno due insiemi di flussi di immagini per impostazione predefinita:

  • Uno stream di immagini CPU utilizzato per il riconoscimento delle caratteristiche e l'elaborazione delle immagini. Per impostazione predefinita, l'immagine CPU ha una risoluzione VGA (640 x 480). ARCore può essere configurato per utilizzare uno stream di immagini a risoluzione più elevata, se necessario.
  • Uno stream di texture GPU, che contiene una texture ad alta risoluzione, solitamente a 1080p. In genere viene utilizzata come anteprima della fotocamera rivolta all'utente. Questo viene memorizzato nella texture OpenGL specificata da Session.setCameraTextureName().
  • Eventuali stream aggiuntivi specificati da SharedCamera.setAppSurfaces().

Considerazioni sulle dimensioni delle immagini CPU

Non sono previsti costi aggiuntivi se viene utilizzato lo stream predefinito con CPU di dimensioni VGA, perché ARCore lo utilizza per la comprensione del mondo. Richiedere uno stream con una risoluzione diversa può essere costoso, in quanto dovrà essere acquisito uno stream aggiuntivo. Tieni presente che una risoluzione più alta può diventare rapidamente costosa per il tuo modello: raddoppiando la larghezza e l'altezza dell'immagine, la quantità di pixel nell'immagine quadruplica.

Potrebbe essere vantaggioso scalare l'immagine se il modello ha un buon rendimento su un'immagine a risoluzione inferiore.

Configura uno stream di immagini CPU ad alta risoluzione aggiuntivo

Le prestazioni del tuo modello ML possono dipendere dalla risoluzione dell'immagine utilizzata come input. La risoluzione di questi stream può essere regolata modificando l'attuale CameraConfig mediante Session.setCameraConfig(), selezionando una configurazione valida da 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)

Recupera l'immagine CPU

Recupera l'immagine CPU utilizzando Frame.acquireCameraImage(). Queste immagini devono essere eliminate appena non sono più necessarie.

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

Elabora l'immagine CPU

Per elaborare l'immagine CPU, possono essere utilizzate varie librerie di machine learning.

Mostra i risultati nella scena AR

I modelli di riconoscimento delle immagini spesso inviano l'output degli oggetti rilevati indicando un punto centrale o un poligono di delimitazione che rappresenta l'oggetto rilevato.

Utilizzando il punto centrale o il centro del riquadro di delimitazione restituito dal modello, è possibile collegare un ancoraggio all'oggetto rilevato. Utilizza Frame.hitTest() per stimare la posa di un oggetto nella scena virtuale.

Converti le coordinate di IMAGE_PIXELS in 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.

Utilizza queste coordinate di VIEW per eseguire un test hit e creare un ancoraggio dal risultato:

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

Considerazioni sulle prestazioni

Segui i consigli riportati di seguito per risparmiare potenza di elaborazione e consumare meno energia:

  • Non eseguire il modello ML su ogni frame in entrata. Valuta la possibilità di eseguire il rilevamento degli oggetti a una frequenza fotogrammi ridotta.
  • Prendi in considerazione un modello di inferenza ML online per ridurre la complessità computazionale.

Passaggi successivi