שימוש ב-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) המשמש לזיהוי תכונות ולעיבוד תמונה. כברירת מחדל, רזולוציית התמונה של המעבד (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.
}

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

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

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

השלבים הבאים