Menggunakan ARCore sebagai input untuk model Machine Learning

Anda dapat menggunakan feed kamera yang direkam ARCore di pipeline machine learning untuk menciptakan pengalaman augmented reality yang cerdas. Contoh ARCore ML Kit menunjukkan cara menggunakan ML Kit dan Google Cloud Vision API untuk mengidentifikasi objek di dunia nyata. Sampel ini menggunakan model machine learning untuk mengklasifikasikan objek dalam tampilan kamera dan melampirkan label ke objek dalam adegan virtual.

Contoh ARCore ML Kit ditulis dalam Kotlin. Alat ini juga tersedia sebagai aplikasi contoh ml_kotlin di repositori GitHub ARCore SDK.

Menggunakan image CPU ARCore

ARCore menangkap setidaknya dua kumpulan aliran gambar secara default:

  • Aliran gambar CPU yang digunakan untuk pengenalan fitur dan pemrosesan gambar. Secara default, gambar CPU memiliki resolusi VGA (640x480). ARCore dapat dikonfigurasi untuk menggunakan streaming gambar tambahan dengan resolusi lebih tinggi, jika diperlukan.
  • Aliran tekstur GPU, yang berisi tekstur resolusi tinggi, biasanya pada resolusi 1080p. Hal ini biasanya digunakan sebagai pratinjau kamera yang ditampilkan kepada pengguna. Ini disimpan di tekstur OpenGL yang ditentukan oleh Session.setCameraTextureName().
  • Feed tambahan apa pun yang ditentukan oleh SharedCamera.setAppSurfaces().

Pertimbangan ukuran image CPU

Tidak ada biaya tambahan yang dikeluarkan jika streaming CPU berukuran VGA default digunakan karena ARCore menggunakan streaming ini untuk pemahaman dunia. Meminta streaming dengan resolusi yang berbeda mungkin mahal, karena streaming tambahan perlu direkam. Ingatlah bahwa resolusi yang lebih tinggi dapat dengan cepat menjadi mahal untuk model Anda: menggandakan lebar dan tinggi gambar akan menghasilkan empat kali lipat jumlah piksel dalam gambar.

Ini mungkin menguntungkan untuk menurunkan ukuran gambar, jika model Anda tetap dapat bekerja dengan baik pada gambar resolusi lebih rendah.

Mengonfigurasi streaming gambar CPU resolusi tinggi tambahan

Performa model ML Anda mungkin bergantung pada resolusi gambar yang digunakan sebagai input. Resolusi streaming ini dapat disesuaikan dengan mengubah CameraConfig saat ini menggunakan Session.setCameraConfig(), dengan memilih konfigurasi yang valid dari 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)

Mengambil image CPU

Ambil CPU image menggunakan Frame.acquireCameraImage(). Gambar ini harus dibuang segera setelah tidak diperlukan lagi.

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

Memproses image CPU

Untuk memproses image CPU, berbagai library machine learning dapat digunakan.

Menampilkan hasil dalam scene AR

Model pengenalan citra sering menghasilkan objek yang terdeteksi dengan menunjukkan titik tengah atau poligon pembatas yang mewakili objek yang terdeteksi.

Dengan menggunakan titik tengah atau tengah kotak pembatas yang merupakan output dari model, Anda bisa memasang anchor ke objek yang terdeteksi. Gunakan Frame.hitTest() untuk memperkirakan pose objek dalam adegan virtual.

Konversi koordinat IMAGE_PIXELS menjadi koordinat 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.

Gunakan koordinat VIEW ini untuk melakukan hit test dan membuat anchor dari hasil:

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

Pertimbangan performa

Ikuti rekomendasi berikut untuk menghemat daya pemrosesan dan mengonsumsi lebih sedikit energi:

  • Jangan menjalankan model ML Anda di setiap frame yang masuk. Sebaiknya jalankan deteksi objek pada kecepatan frame rendah.
  • Pertimbangkan model inferensi ML online untuk mengurangi kompleksitas komputasi.

Langkah berikutnya