תמונות משופרות של ARCore

1. סקירה כללית

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

בשיעור הזה תלמדו איך לשנות אפליקציה לדוגמה של ARCore כדי לשלב תמונות מרובות שזזות או מתקנות במקומן.

מה תפַתחו

ב-Codelab הזה, תסתמך על אפליקציה קיימת לדוגמה של ARCore. בסוף ה-Codelab, האפליקציה שלך תוכל:

  • זיהוי יעד תמונה וצירוף מבוך וירטואלי על היעד
  • עוקבים אחרי היעד שנע כל עוד רואים אותו בעיני המצלמה

6bc6605df89de525.gif

זאת הפעם הראשונה שיצרת אפליקציית ARCore?

כן לא

האם אתם מתכננים לכתוב קוד לדוגמה ב-Codelab הזה או רק לקרוא את הדפים האלה?

כתיבת קוד לדוגמה פשוט לקרוא את הדפים האלה

מה תלמדו

  • איך משתמשים בתמונות מרובות ב-ARCore ב-Java
  • איך למדוד את היכולת של תמונה לזהות על ידי ARCore
  • איך לצרף תוכן וירטואלי לתמונה ולעקוב אחרי התנועה שלה

דרישות מוקדמות

כדי להשלים את ה-Codelab הזה צריך חומרה ותוכנה ספציפיות.

דרישות החומרה

דרישות תוכנה

  • ARCore APK 1.9.0 ואילך. בדרך כלל, ה-APK הזה מותקן אוטומטית במכשיר דרך חנות Play
  • מחשב פיתוח עם Android Studio (גרסה 3.1 ואילך)
  • גישה לאינטרנט, כי תצטרכו להוריד ספריות במהלך הפיתוח

עכשיו, אחרי שהכול מוכן, אפשר להתחיל.

2. הגדרת סביבת הפיתוח

הורדת ה-SDK

בתור התחלה, צריך להוריד את הגרסה העדכנית ביותר של ARCore Android SDK מ-GitHub. מחלצים את הקובץ למיקום הרצוי. ל-Codelab הזה, גרסת ה-SDK המוקדמת ביותר היא 1.18.1. התיקייה תקרא arcore-android-sdk-x.xx.x, הערך המדויק יהיה גרסת ה-SDK שבה השתמשת.

מפעילים את Android Studio ולוחצים על פתיחת פרויקט קיים של Android Studio.

5fbf2b21609187cc.png

עוברים אל התיקייה המכווצת הזו:

arcore-android-sdk-x.xx.x/samples/augmented_image_java

לוחצים על פתיחה.

ממתינים שסנכרון הפרויקט יסתיים ב-Android Studio. אם גרסת Android Studio לא כוללת את הרכיבים הדרושים, ייתכן שההעברה תיכשל ותוצג ההודעה Install missing platform and sync project. פועלים לפי ההוראות כדי לפתור את הבעיה.

הרצת האפליקציה לדוגמה

עכשיו, אחרי שיצרתם פרויקט ARCore פעיל, בואו נריץ אותו.

מחברים את מכשיר ARCore למכונת הפיתוח ומשתמשים בתפריט הפעלה > מריצים את 'app' כדי להריץ את גרסת ניפוי הבאגים במכשיר. בתיבת הדו-שיח שבה מופיעה בקשה לבחור מאיזה מכשיר להריץ, בוחרים את המכשיר המחובר ולוחצים על OK (אישור).

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

בפרויקט לדוגמה הזה נעשה שימוש ב-targetSdkVersion 28. אם יש לכם שגיאת build כמו Failed to find Build Tools revision 28.0.3, עליכם לפעול לפי ההוראות שמתוארות ב-Android Studio כדי להוריד ולהתקין את הגרסה הנדרשת של Android Build Tools.

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

בדיקה באמצעות תמונה לדוגמה

הגדרת את סביבת הפיתוח כדי לבדוק את האפליקציה באמצעות תמונה לבדיקה.

חוזרים ב-Android Studio, בחלון Project עוברים אל app > נכסים, ולוחצים לחיצה כפולה על הקובץ default.jpg כדי לפתוח אותו.

9b333680e7b9f247.jpeg

מכוונים את המצלמה של המכשיר לתמונה של כדור הארץ שעל המסך ופועלים לפי ההוראות כדי להתאים את התמונה שסורקים לצלב.

מסגרת תמונה תוצג כשכבת-על מעל התמונה, כך:

999e05ed35964f6e.png

בשלב הבא נבצע שיפורים קלים באפליקציה לדוגמה.

3. הצגת מודל מבוך בתמונה הדו-ממדית

אפשר להתחיל לשחק עם תמונות מרובות על ידי הצגת מודל תלת-ממדי מעליו.

הורדת מודל תלת-ממדי

לצורך ה-Codelab הזה נשתמש ב'מבוך מעגלי – ירוק' מטעם Evol, ברישיון CC-BY 3.0. שמרתי עותק של המודל התלת-ממדי הזה במאגר github של ה-Codelab הזה, שנמצא כאן.

כדי להוריד את המודל ולהוסיף אותו ל-Android Studio, צריך לבצע את השלבים הבאים.

  1. עוברים אל מאגר ה-GitHub של ספריית ה-codelab הזו, third_party.
  2. לוחצים על GreenMaze_obj.zip, ולאחר מכן לוחצים על הלחצן הורדה.

תתבצע הורדה של קובץ בשם GreenMaze_obj.zip.

  1. ב-Android Studio, יוצרים ספרייה של green-maze בקטע app > נכסים > מודלים
  2. מחלצים את הקובץ GreenMaze_obj.zip ומעתיקים את התוכן למיקום הזה: arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. ב-Android Studio, עוברים אל אפליקציה > נכסים > מודלים > מבוך ירוק.

בתיקייה הזו אמורים להיות שני קבצים: GreenMaze.obj ו-GreenMaze.mtl.

a1f33a2d2d407e03.png

עיבוד מודל המבוך

כדי להציג את המודל התלת-ממדי GreenMaze.obj מעל התמונה הדו-ממדית הקיימת, פועלים לפי השלבים הבאים.

בפונקציה AugmentedImageRenderer.java, מוסיפים משתנה חבר בשם mazeRenderer כדי לעבד את מודל המבוך. מכיוון שהמבוך צריך לצרף לתמונה, הגיוני להציב את mazeRenderer בתוך המחלקה AugmentedImageRenderer.

AugmentedImageRenderer.java

  // Add a member variable to hold the maze model.
  private final ObjectRenderer mazeRenderer = new ObjectRenderer();

בפונקציה createOnGlThread(), טוענים את GreenMaze.obj. כדי לשמור על פשטות, צריך להשתמש באותו מרקם של מסגרת כמו הטקסטורה שלו.

AugmentedImageRenderer.java

  // Replace the definition of the createOnGlThread() function with the
  // following code, which loads GreenMaze.obj.
  public void createOnGlThread(Context context) throws IOException {

    mazeRenderer.createOnGlThread(
        context, "models/green-maze/GreenMaze.obj", "models/frame_base.png");
    mazeRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);

  }

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

AugmentedImageRenderer.java

  // Adjust size of detected image and render it on-screen
  public void draw(
      float[] viewMatrix,
      float[] projectionMatrix,
      AugmentedImage augmentedImage,
      Anchor centerAnchor,
      float[] colorCorrectionRgba) {
    float[] tintColor =
        convertHexToColor(TINT_COLORS_HEX[augmentedImage.getIndex() % TINT_COLORS_HEX.length]);

    final float mazeEdgeSize = 492.65f; // Magic number of maze size
    final float maxImageEdgeSize = Math.max(augmentedImage.getExtentX(), augmentedImage.getExtentZ()); // Get largest detected image edge size

    Pose anchorPose = centerAnchor.getPose();

    float mazeScaleFactor = maxImageEdgeSize / mazeEdgeSize; // scale to set Maze to image size
    float[] modelMatrix = new float[16];

    // OpenGL Matrix operation is in the order: Scale, rotation and Translation
    // So the manual adjustment is after scale
    // The 251.3f and 129.0f is magic number from the maze obj file
    // You mustWe need to do this adjustment because the maze obj file
    // is not centered around origin. Normally when you
    // work with your own model, you don't have this problem.
    Pose mazeModelLocalOffset = Pose.makeTranslation(
                                -251.3f * mazeScaleFactor,
                                0.0f,
                                129.0f * mazeScaleFactor);
    anchorPose.compose(mazeModelLocalOffset).toMatrix(modelMatrix, 0);
    mazeRenderer.updateModelMatrix(modelMatrix, mazeScaleFactor, mazeScaleFactor/10.0f, mazeScaleFactor); // This line relies on a change in ObjectRenderer.updateModelMatrix later in this codelab.
    mazeRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
  }

עכשיו המבוך אמור להופיע מעל התמונה של כדור הארץ default.jpg.

הערה: מאחר שאין לך שליטה מלאה על המודל התלת-ממדי לדוגמה, הקוד שלמעלה משתמש בכמה 'קסם' . הגודל של מודל המבוך הוא 492.65 x 120 x 492.65, והמרכז הוא (251.3, 60, -129.0). טווח הקודקודים ערכי קואורדינטות X, Y ו-Z הם [5.02, 497.67], [0, 120] ו-[-375.17, 117.25] בהתאמה. לכן, קנה המידה של מודל המבוך צריך להיות image_size / 492.65. הוספנו את המאפיין mazeModelLocalOffset כי המודל התלת-ממדי של המבוך לא מתרכז סביב המקור (0, 0, 0).

הקיר של המבוך עדיין גבוה מדי מכדי להופיע מעל התמונה. יוצרים פונקציית עזר updateModelMatrix() שיכולה לשנות את קנה המידה של X, Y ו-Z באופן לא שווה, כדי לשנות את גובה המבוך ב-0.1. חשוב לשים לב: צריך להשאיר את updateModelMatrix(float[] modelMatrix, float scaleFactor) הקיים ולהוסיף את עומס היתר של הפונקציה updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) כפונקציה חדשה.

common/rendering/ObjectRenderer.java

// Scale X, Y, Z coordinates unevenly
public void updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) {
    float[] scaleMatrix = new float[16];
    Matrix.setIdentityM(scaleMatrix, 0);
    scaleMatrix[0] = scaleFactorX;
    scaleMatrix[5] = scaleFactorY;
    scaleMatrix[10] = scaleFactorZ;
    Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
}

מריצים את הקוד. עכשיו המבוך אמור להתאים בדיוק מעל לתמונה.

772cbe2a8baef3ba.png

4. מוסיפים את אנדי למבוך

עכשיו, אחרי שיצרתם מבוך, תוכלו להוסיף דמות שתסתובב בתוכה. משתמשים בקובץ andy.obj שכלול ב-ARCore Android SDK. יש להשאיר את המרקם של מסגרת התמונה בתור המרקם, מפני שהיא נראית שונה מהמבוך הירוק שמוצג מעל התמונה.

ב-AugmentedImageRenderer.java, צריך להוסיף נכס ObjectRenderer פרטי כדי לעבד את אשר.

AugmentedImageRenderer.java

// Render for Andy
  private final ObjectRenderer andyRenderer = new ObjectRenderer();

בשלב הבא, מאתחלים את andyRenderer בסוף createOnGlThread().

AugmentedImageRenderer.java

public void createOnGlThread(Context context) throws IOException {

    // Initialize andyRenderer
    andyRenderer.createOnGlThread(
        context, "models/andy.obj", "models/andy.png");
    andyRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
  }

בסוף, מרימים את אנדי עומד על המבוך בסוף הפונקציה draw().

AugmentedImageRenderer.java

public void draw(
      float[] viewMatrix,
      float[] projectionMatrix,
      AugmentedImage augmentedImage,
      Anchor centerAnchor,
      float[] colorCorrectionRgba) {


    // Render Andy, standing on top of the maze
    Pose andyModelLocalOffset = Pose.makeTranslation(
        0.0f,
        0.1f,
        0.0f);
    anchorPose.compose(andyModelLocalOffset).toMatrix(modelMatrix, 0);
    andyRenderer.updateModelMatrix(modelMatrix, 0.05f); // 0.05f is a Magic number to scale
    andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);

  }

מריצים את הקוד. אתם אמורים לראות את אנדי עומד על המבוך.

cb1e74569d7ace69.png

קביעת איכות היעד של תמונות

טכנולוגיית ARCore מסתמכת על תכונות חזותיות כדי לזהות תמונות. עקב ההבדלים באיכות, לא ניתן לזהות בקלות את כל התמונות.

arcoreimg הוא כלי שורת הפקודה שמאפשר לקבוע עד כמה תמונה תהיה מזוהה ב-ARCore. הפלט הוא מספר בין 0 ל-100, כאשר 100 הוא הקל ביותר לזיהוי.

הקצר הזה. התשובות שלך יעזרו לנו להשתפר. לדוגמה:

arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$ ./arcoreimg  eval-img --input_image_path=/Users/username/maze.jpg
100

ל-maze.jpg יש ערך של 100, כך שאפשר לזהות אותו בקלות על ידי ARCore.

5. אופציונלי: מעבירים את אנדי במבוך

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

מורידים את הקובץ PhysicsController.java ומוסיפים אותו לפרויקט בספרייה.

arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/

ב-Android Studio, מוסיפים GreenMaze.obj לספרייה נכסי פרויקט כדי שניתן יהיה לטעון אותה בזמן הריצה. העתקת GreenMaze.obj מאפליקציה > נכסים > מודלים > green-maze אל app > נכסים.

מוסיפים את יחסי התלות הבאים לקובץ build.gradle של האפליקציה.

app/build.gradle

    // jbullet library
    implementation 'cz.advel.jbullet:jbullet:20101010-1'

מגדירים משתנה andyPose כדי לשמור את מיקום התנוחה הנוכחית של אמיר.

AugmentedImageRenderer.java

  // Create a new pose for the Andy
  private Pose andyPose = Pose.IDENTITY;

משנים את AugmentedImageRenderer.java כדי לבצע רינדור של Andy באמצעות המשתנה andyPose החדש.

AugmentedImageRenderer.java

public void draw(
      float[] viewMatrix,
      float[] projectionMatrix,
      AugmentedImage augmentedImage,
      Anchor centerAnchor,
      float[] colorCorrectionRgba) {

    // Use these code to replace previous code for rendering the Andy object
    // 
    // Adjust the Andy's rendering position
    // The Andy's pose is at the maze's vertex's coordinate
    Pose andyPoseInImageSpace = Pose.makeTranslation(
        andyPose.tx() * mazeScaleFactor,
        andyPose.ty() * mazeScaleFactor,
        andyPose.tz() * mazeScaleFactor);

    anchorPose.compose(andyPoseInImageSpace).toMatrix(modelMatrix, 0);
    andyRenderer.updateModelMatrix(modelMatrix, 0.05f);
    andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
  }

צריך להוסיף פונקציית שירות חדשה, updateAndyPose(), כדי לקבל עדכונים לגבי התנוחה של אמיר.

AugmentedImageRenderer.java

  // Receive Andy pose updates
  public void updateAndyPose(Pose pose) {
    andyPose = pose;
  }

ב-AugmentedImageActivity.java, יוצרים אובייקט PhysicsController שמשתמש במנוע הפיזיקה של JBullet כדי לנהל את כל הפונקציות שקשורות לפיזיקה.

AugmentedImageActivity.java

import com.google.ar.core.Pose;

  // Declare the PhysicsController object
  private PhysicsController physicsController;

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

AugmentedImageActivity.java

// Update the case clause for TRACKING to call PhysicsController
// whenever the app recognizes an image
  private void drawAugmentedImages(

    ...

        case TRACKING:
          // Switch to UI Thread to update View
          this.runOnUiThread(
              new Runnable() {
                @Override
                public void run() {
                  fitToScanView.setVisibility(View.GONE);
                }
              });

          // Create a new anchor for newly found images
          if (!augmentedImageMap.containsKey(augmentedImage.getIndex())) {
            Anchor centerPoseAnchor = augmentedImage.createAnchor(augmentedImage.getCenterPose());
            augmentedImageMap.put(
                augmentedImage.getIndex(), Pair.create(augmentedImage, centerPoseAnchor));

            physicsController = new PhysicsController(this);
          } else {
            Pose ballPose = physicsController.getBallPose();
            augmentedImageRenderer.updateAndyPose(ballPose);


            // Use real world gravity, (0, -10, 0), as gravity
            // Convert to Physics world coordinate(maze mesh has to be static)
            // Use the converted coordinate as a force to move the ball
            Pose worldGravityPose = Pose.makeTranslation(0, -10f, 0);
            Pose mazeGravityPose = augmentedImage.getCenterPose().inverse().compose(worldGravityPose);
            float mazeGravity[] = mazeGravityPose.getTranslation();
            physicsController.applyGravityToBall(mazeGravity);

            physicsController.updatePhysics();
          }
          break;

מפעילים את האפליקציה. עומר אמור לנוע עכשיו בצורה מציאותית כשמטים את התמונה.

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

2f0df284705d3704.gif

זהו! תיהנו לנסות לתפוס את אמיר במבוך. רמז: קל יותר למצוא את היציאה כשמחזיקים את תמונת היעד הפוכה.

6. מזל טוב

מזל טוב, הגעת לסוף Codelab זה וכתוצאה מכך:

  • יצירה והרצה של דוגמת AugmentedImage Java של ARCore.
  • הדגימה עודכנה כדי להציג מודל מבוך בתמונה, בקנה מידה מתאים.
  • השתמשה בתנוחה של התמונה כדי לעשות משהו כיפי.

כאן אפשר לעיין בקוד המלא.

נהניתם לעשות את זה ב-Codelab?

כן לא

למדת משהו שימושי במהלך ה-Codelab הזה?

כן לא

האם השלמת את יצירת האפליקציה ב-Codelab הזה?

כן לא

יש לך אפשרות ליצור אפליקציית ARCore ב-6 החודשים הקרובים?

כן אולי לא