Używanie ARCore jako danych wejściowych dla modeli systemów uczących się

Na podstawie obrazu z kamery zarejestrowanej przez ARCore w ramach potoku systemów uczących się możesz tworzyć inteligentne doświadczenia w rzeczywistości rozszerzonej. Przykład ARCore ML Kit pokazuje, jak używać pakietu ML Kit i Google Cloud Vision API do identyfikowania obiektów w świecie rzeczywistym. W przykładzie użyto modelu systemów uczących się do klasyfikowania obiektów w polu widzenia kamery i dodawania do nich etykiety w scenie wirtualnej.

Przykładowy zestaw ARCore ML Kit został napisany w Kotlin. Jest też dostępna jako przykładowa aplikacja ml_kotlin w repozytorium ARCore SDK na GitHubie.

Użyj obrazu procesora ARCore

ARCore domyślnie rejestruje co najmniej 2 zestawy strumieni obrazów:

  • Strumień obrazów procesora używany do rozpoznawania cech i przetwarzania obrazu. Domyślnie obraz procesora ma rozdzielczość VGA (640 x 480). W razie potrzeby można skonfigurować ARCore tak, by używał dodatkowego strumienia obrazów o wyższej rozdzielczości.
  • Strumień tekstur GPU zawierający teksturę w wysokiej rozdzielczości, zwykle w rozdzielczości 1080p. Zwykle służy do wyświetlania podglądu z kamery skierowanej do użytkownika. Wartość ta jest przechowywana w teksturze OpenGL określonej przez Session.setCameraTextureName().
  • Dodatkowe strumienie określone przez SharedCamera.setAppSurfaces().

Uwagi na temat rozmiaru obrazu procesora

Jeśli używany jest strumień procesora o domyślnej wielkości VGA, nie ponosimy dodatkowych kosztów, ponieważ ARCore używa tego strumienia do zrozumienia treści przez cały świat. Żądanie transmisji o innej rozdzielczości może być kosztowne, ponieważ konieczne będzie zarejestrowanie dodatkowego strumienia. Pamiętaj, że wyższa rozdzielczość może szybko stać się kosztowna dla Twojego modelu: podwojenie szerokości i wysokości obrazu czterokrotnie spowoduje czterokrotne zwiększenie liczby pikseli na obrazie.

Przeskalowanie obrazu w dół może być korzystne, jeśli model nadal dobrze sprawdza się na obrazie w niższej rozdzielczości.

Skonfiguruj dodatkowy strumień obrazów z procesora o wysokiej rozdzielczości

Wydajność modelu ML może zależeć od rozdzielczości obrazu używanego jako dane wejściowe. Rozdzielczość tych strumieni można dostosować, zmieniając bieżący element CameraConfig za pomocą Session.setCameraConfig(), wybierając prawidłową konfigurację z 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)

Pobierz obraz procesora

Pobierz obraz procesora za pomocą narzędzia Frame.acquireCameraImage(). Takie zdjęcia należy wyrzucić, gdy tylko nie są już potrzebne.

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

Przetwarzanie obrazu procesora

Do przetwarzania obrazu procesora można używać różnych bibliotek systemów uczących się.

Wyświetlanie wyników w scenie AR

Modele rozpoznawania obrazów często zwracają wykryte obiekty, wskazując punkt środkowy lub wielokąt ograniczający reprezentujący wykryty obiekt.

Korzystając z punktu środkowego lub środka ramki ograniczającej wyjściowej z modelu, możesz dołączyć do wykrytego obiektu kotwicę. Użyj Frame.hitTest(), aby oszacować pozycję obiektu w scenie wirtualnej.

Konwertuj współrzędne IMAGE_PIXELS na współrzędne 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.

Użyj tych współrzędnych VIEW, aby przeprowadzić test z trafieniami i utworzyć kotwicę na podstawie wyniku:

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

Możliwe spowolnienie działania witryny

Postępuj zgodnie z tymi zaleceniami, aby oszczędzać moc obliczeniową i zużywać mniej energii:

  • Nie uruchamiaj modelu ML w każdej przychodzącej ramce. Rozważ uruchomienie wykrywania obiektów z małą liczbą klatek na sekundę.
  • Rozważ zastosowanie modelu wnioskowania dotyczącego systemów uczących się online, aby zmniejszyć złożoność obliczeniową.

Dalsze kroki