ARCore 擴增圖片

1. 總覽

ARCore 是一個平台,可讓您在 Android 平台上建構擴增實境應用程式。擴增圖片可讓你建立 AR 應用程式,藉此辨識現實世界中預先註冊的 2D 圖片,並將虛擬內容錨定在這些圖片上方。

本程式碼研究室將引導您修改現有的 ARCore 範例應用程式,以納入要移動或固定的擴增圖片。

建構項目

在這個程式碼研究室中,您將使用現有的 ARCore 範例應用程式進行建構。完成程式碼研究室後,您的應用程式將能:

  • 偵測圖片目標,並在目標上連接虛擬迷宮
  • 只要移動到攝影機視角的移動目標,即可進行追蹤

6bc6605df89de525.gif

這是你第一次製作 ARCore 應用程式嗎?

您打算在本程式碼研究室中編寫程式碼範例,或單純想閱讀這些網頁嗎?

撰寫程式碼範例 純粹閱讀這些網頁

課程內容

  • 如何在 Java 中使用 ARCore 中的擴增圖片
  • 如何測量 ARCore 辨識圖片的能力
  • 如何在圖片中附加虛擬內容並追蹤其動作

必要條件

您需要使用特定的硬體和軟體,才能完成這個程式碼研究室。

硬體需求

軟體需求

  • ARCore APK 1.9.0 以上版本。這個 APK 通常會透過 Play 商店自動安裝在裝置上
  • 搭載 Android Studio (3.1 以上版本) 的開發機器
  • 網際網路存取權,因為在開發期間需下載程式庫

一切準備就緒後,開始吧!

2. 設定開發環境

下載 SDK

請先從 GitHub 下載最新的 ARCore Android SDK。將其解壓縮到您想要的位置。在本程式碼研究室中,最早的 SDK 版本為 1.18.1。這個資料夾稱為 arcore-android-sdk-x.xx.x,確切值會是您使用的 SDK 版本。

啟動 Android Studio,然後按一下「Open an existing Android Studio project」

5fbf2b21609187cc.png

前往這個解壓縮資料夾:

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

按一下「開啟」

等待 Android Studio 完成專案同步處理作業。如果 Android Studio 沒有必要元件,可能會失敗,並顯示 Install missing platform and sync project 訊息。按照操作說明修正問題。

執行範例應用程式

建立可運作的 ARCore 應用程式專案後,讓我們開始執行測試。

將 ARCore 裝置連線至開發機器,然後依序使用選單 [Run] >執行「app」,在裝置上執行偵錯版本。在提示中選擇要用來執行的裝置,請選擇已連結的裝置,然後按一下「OK」

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

這個範例專案使用 targetSdkVersion 28。如果您有 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. 在 2D 圖片上顯示迷宮模型

您可以在「擴增圖片」上方顯示 3D 模型,開始體驗擴增圖片功能。

下載 3D 模型

在本程式碼研究室中,我們會使用「Circle Maze - Green」,並依據 CC-BY 3.0 授權取得授權。我已將這個 3D 模型的副本儲存在本程式碼研究室的 GitHub 存放區中,可以在這裡取得。

請按照下列步驟下載模型並加進 Android Studio。

  1. 前往本程式碼研究室的 GitHub 存放區 (third_party 目錄)
  2. 按一下「GreenMaze_obj.zip」GreenMaze_obj.zip,然後點選「下載」GreenMaze_obj.zip按鈕。

這會下載名為 GreenMaze_obj.zip 的檔案。

  1. 在 Android Studio 中,於 app > 下方建立 green-maze 目錄資源 >模型
  2. 解壓縮 GreenMaze_obj.zip,並將內容複製到這個位置:arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. 在 Android Studio 中,前往「app」(應用程式) > 資源 >模型 >綠迷宮

這個資料夾中應有兩個檔案:GreenMaze.objGreenMaze.mtl

a1f33a2d2d407e03.png

轉譯迷宮模型

如要在現有 2D 圖像上方顯示 GreenMaze.obj 3D 模型,請按照下列步驟操作。

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 圖片上方。

注意:由於您並不完全控制這個 3D 模型範例,因此上述程式碼會使用一些「魔法」數字。迷宮模型的尺寸為 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,是因為迷宮的 3D 模型未以原點 (0, 0, 0) 為中心。

迷宮的牆壁仍然太高,無法印在相片上方。建立能不均勻縮放 X、Y、Z 的輔助函式 updateModelMatrix(),將迷宮的高度調整為 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 加入迷宮

現在您已擁有迷宮,可以新增角色並在其中移動。使用 ARCore Android SDK 內含的 andy.obj 檔案。請保留圖片框架紋理做為紋理,因為它看起來與圖片上方算繪的綠色迷宮不同。

AugmentedImageRenderer.java 中,新增私人 ObjectRenderer 以轉譯 Andy。

AugmentedImageRenderer.java

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

接下來,在 createOnGlThread() 的結尾初始化 andyRenderer

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() 函式結尾的迷宮上方算繪 Andy。

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 是最容易辨識的數字。

,直接在 Google Cloud 控制台實際操作。範例如下:

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 >資產 >模型 >綠迷應用程式 >assets

在應用程式的 build.gradle 檔案中新增下列依附元件。

app/build.gradle

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

定義變數 andyPose 以儲存 Andy 目前姿勢的位置。

AugmentedImageRenderer.java

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

修改 AugmentedImageRenderer.java,使用新的 andyPose 變數轉譯 Andy。

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(),以接收 Andy 姿勢更新。

AugmentedImageRenderer.java

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

AugmentedImageActivity.java 中,建立使用 JBullet 物理引擎的 PhysicsController 物件,藉此管理所有物理相關函式。

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. 恭喜

恭喜,您已經完成本程式碼研究室,因此獲得以下成果:

  • 建構並執行 ARCore AugmentedImage Java 範例。
  • 更新範例,以適當比例在圖片上顯示迷宮模型。
  • 運用圖片姿勢做一些有趣的事。

如要參考完整程式碼,請在這裡下載。

你在執行這個程式碼研究室時有樂趣嗎?

在本程式碼研究室中,您是否學到任何實用內容?

您是否已完成本程式碼研究室中的應用程式製作程序?

您是否打算在未來 6 個月內推出 ARCore 應用程式?

不確定