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

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

ARCore היא פלטפורמה לבניית אפליקציות של מציאות רבודה ב-Android. Augmented Images מאפשרת לכם ליצור אפליקציות AR שיכולות לזהות תמונות דו-ממדיות שרשומות מראש בעולם האמיתי, ולהוסיף תוכן וירטואלי מעליהן.

ה-Codelab הזה מנחה אתכם לשנות אפליקציית דוגמה קיימת של 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 ולוחצים על Open an existing Android Studio project (פתיחת פרויקט קיים של 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 למחשב פיתוח ומשתמשים בתפריט הפעלה > הפעלת האפליקציה כדי להפעיל את גרסת ניפוי הבאגים במכשיר. בתיבת הדו-שיח שמופיעה ומבקשת לבחור את המכשיר שממנו רוצים להפעיל את האפליקציה, בוחרים את המכשיר המחובר ולוחצים על אישור.

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

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

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

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

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

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

9b333680e7b9f247.jpeg

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

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

999e05ed35964f6e.png

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

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

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

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

ב-codelab הזה נשתמש ב-"Circle Maze - Green" של 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 > assets > models
  2. פותחים את ה-ZIP GreenMaze_obj.zip ומעתיקים את התוכן למיקום הזה: arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. ב-Android Studio, עוברים אל app > assets > models > green-maze (אפליקציה > נכסים > מודלים > מבוך ירוק).

צריכים להיות שני קבצים בתיקייה הזו: 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 לספרייה project assets כדי שיהיה אפשר לטעון אותו בזמן הריצה. מעתיקים את GreenMaze.obj מ-app > assets > models > green-maze אל app > assets.

מוסיפים את יחסי התלות הבאים לקובץ 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 כך שהדמות של אנדי תוצג באמצעות המשתנה החדש 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 הזה?

כן לא

האם סיימת ליצור את האפליקציה בסדנת הקוד הזו?

כן לא

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

כן אולי לא