שימוש ב-ARCore כקלט למודלים של למידת מכונה

אתם יכולים להשתמש בפיד המצלמה ש-ARCore מתעד בצינור עיבוד נתונים של למידת מכונה כדי ליצור חוויה חכמה של מציאות רבודה. הדוגמה של ARCore ML Kit מדגימה איך להשתמש ב-ML Kit וב-Google Cloud Vision API לזיהוי אובייקטים בעולם האמיתי. הדוגמה משתמשת במודל של למידת מכונה כדי לסווג אובייקטים בתצוגת המצלמה, ומצרפת תווית לאובייקט בסצנה הווירטואלית.

הדוגמה של ARCore ML Kit כתובה ב-Kotlin. היא זמינה גם כאפליקציה לדוגמה ml_kotlin מתוך המאגר ARCore SDK ב-GitHub.

שימוש בתמונת המעבד (CPU) של ARCore

כברירת מחדל, ב-ARCore מתועדים שתי קבוצות לפחות של זרמי תמונות:

  • זרם תמונה של המעבד (CPU) המשמש לזיהוי תכונות ולעיבוד תמונה. כברירת מחדל, הרזולוציה של המעבד (CPU) היא VGA (640x480). אם צריך, אפשר להגדיר את ARCore להשתמש בשידור תמונות נוסף ברזולוציה גבוהה יותר.
  • זרם של טקסטורות GPU, שמכיל מרקם ברזולוציה גבוהה, בדרך כלל ברזולוציה של 1080p. מצב זה משמש בדרך כלל כתצוגה מקדימה של המצלמה שפונה למשתמש. מאוחסנים במרקם OpenGL שצוין על ידי Session.setCameraTextureName().
  • כל סטרימינג נוסף שצוין על ידי SharedCamera.setAppSurfaces().

שיקולים לקביעת גודל התמונה במעבד (CPU)

אין עלות נוספת לשימוש בשידור ברירת המחדל של המעבד (CPU) בגודל VGA, כי מערכת ARCore משתמשת בשידור הזה כדי להבין את העולם. בקשה של שידור ברזולוציה אחרת עשויה להיות יקרה, כי יהיה צורך לצלם שידור נוסף. חשוב לזכור שרזולוציה גבוהה יותר עשויה להפוך ליקרה במהירות עבור המודל שלך: הכפלת הרוחב והגובה של התמונה מכפילה את כמות הפיקסלים בתמונה.

כדאי להפחית את רמת הביצועים של התמונה, אם המודל עדיין יכול להניב ביצועים טובים בתמונה ברזולוציה נמוכה יותר.

הגדרת שידור תמונה נוסף של המעבד (CPU) ברזולוציה גבוהה

הביצועים של מודל למידת המכונה עשויים להיות תלויים ברזולוציה של התמונה המשמשת כקלט. אפשר לשנות את הרזולוציה של השידורים האלה על ידי שינוי הרזולוציה של CameraConfig הנוכחית באמצעות Session.setCameraConfig(), בחירת הגדרה חוקית מ-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)

אחזור התמונה של המעבד (CPU)

מאחזרים את התמונה של המעבד (CPU) באמצעות Frame.acquireCameraImage(). אין להשליך את התמונות האלה מיד לאחר שאין בהן צורך.

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

עיבוד התמונה של המעבד (CPU)

כדי לעבד את התמונה של המעבד (CPU), אפשר להשתמש במספר ספריות של למידת מכונה.

הצגת תוצאות בסצנת ה-AR

במקרים רבים, מודלים לזיהוי תמונות מקבלים פלט של אובייקטים על ידי ציון נקודת מרכז או פוליגון תוחם שמייצג את האובייקט שזוהה.

אפשר לחבר עוגן לאובייקט שזוהה באמצעות נקודת המרכז או המרכז של התיבה התוחמת שמתקבלת מהמודל. באמצעות Frame.hitTest() ניתן להעריך את תנוחת העצם בסצנה הווירטואלית.

המירו IMAGE_PIXELS קואורדינטות ל-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.

משתמשים בקואורדינטות VIEW הבאות כדי לבצע בדיקת היט וליצור עוגן מהתוצאה:

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

שיקולי ביצועים

כדי לחסוך בכוח העיבוד ולצרוך פחות אנרגיה, כדאי לפעול לפי ההמלצות הבאות:

  • אין להריץ את מודל ה-ML בכל מסגרת נכנסת. במקום זאת, כדאי להפעיל את זיהוי האובייקטים בקצב פריימים נמוך.
  • חשבו על מודל ההסקה של למידת מכונה (ML) אונליין כדי להפחית את המורכבות החישובית.

השלבים הבאים