Menggunakan ARCore sebagai input untuk model Machine Learning

Anda dapat menggunakan feed kamera yang ditangkap ARCore dalam 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 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 adalah yang ditulis dalam Kotlin. Alat ini juga tersedia sebagai sampel ml_kotlin di ARCore SDK repositori GitHub ASL.

Menggunakan image CPU ARCore

ARCore menangkap setidaknya dua set 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 beresolusi lebih tinggi, jika diperlukan.
  • Streaming tekstur GPU, yang berisi tekstur resolusi tinggi, biasanya pada resolusi 1080p. Ini biasanya digunakan sebagai pratinjau kamera yang ditampilkan kepada pengguna. Class ini disimpan dalam tekstur OpenGL yang ditentukan oleh Session.setCameraTextureName().
  • Streaming tambahan apa pun yang ditentukan oleh SharedCamera.setAppSurfaces().

Pertimbangan ukuran image CPU

Tidak ada biaya tambahan yang dikenakan jika streaming CPU default berukuran VGA digunakan karena ARCore menggunakan streaming ini untuk mendapatkan pemahaman dunia. Meminta streaming dengan resolusi berbeda mungkin mahal, karena streaming tambahan perlu direkam. Perlu diingat bahwa resolusi yang lebih tinggi bisa cepat habis untuk model Anda: menggandakan lebar dan tinggi gambar akan menambah jumlah piksel sebanyak empat kali lipat pada gambar.

Hal ini mungkin akan menguntungkan untuk memperkecil gambar, jika model Anda masih dapat berperforma baik pada gambar beresolusi lebih rendah.

Mengonfigurasi streaming gambar CPU beresolusi 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 image CPU 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 kali menghasilkan objek yang terdeteksi dengan menunjukkan titik tengah atau poligon pembatas yang mewakili objek yang terdeteksi.

Dengan menggunakan titik tengah atau bagian tengah kotak pembatas yang dihasilkan dari model, Anda dapat memasang anchor ke objek yang terdeteksi. Gunakan Frame.hitTest() untuk memperkirakan pose objek di 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 pada setiap frame yang masuk. Sebaiknya jalankan deteksi objek pada kecepatan frame rendah.
  • Pertimbangkan model inferensi ML online untuk mengurangi kompleksitas komputasi.

Langkah berikutnya