Дополненные изображения ARCore

1. Обзор

ARCore — это платформа для создания приложений дополненной реальности на Android. Augmented Images позволяет создавать AR-приложения, способные распознавать предварительно зарегистрированные 2D-изображения реального мира и накладывать на них виртуальный контент.

В этом практическом занятии вы узнаете, как модифицировать существующее демонстрационное приложение ARCore, чтобы добавить в него дополненные изображения, которые могут двигаться или оставаться неподвижными.

Что вы построите

В этом практическом занятии вы будете развивать существующее демонстрационное приложение ARCore. К концу занятия ваше приложение сможет:

  • Обнаружить целевой объект на изображении и прикрепить к нему виртуальный лабиринт.
  • Отслеживайте движущуюся цель до тех пор, пока она находится в поле зрения камеры.

6bc6605df89de525.gif

Это ваш первый опыт создания приложения на ARCore?

Да Нет

Вы планируете писать примеры кода в рамках этого практического занятия или просто хотите почитать эти страницы?

Напишите пример кода Просто прочтите эти страницы

Что вы узнаете

  • Как использовать дополненные изображения в ARCore на Java
  • Как оценить способность ARCore распознавать изображение?
  • Как прикрепить виртуальный контент к изображению и отслеживать его перемещение.

Предварительные требования

Для выполнения этого практического задания вам потребуется специальное оборудование и программное обеспечение.

Требования к оборудованию

Требования к программному обеспечению

  • ARCore APK 1.9.0 или более поздней версии. Этот APK-файл обычно устанавливается на устройство автоматически через Play Store.
  • Компьютер разработчика с установленной программой Android Studio (версия 3.1 или более поздняя)
  • Доступ к интернету необходим, так как в процессе разработки потребуется загружать библиотеки.

Теперь, когда у вас всё готово, давайте начнём!

2. Настройка среды разработки.

Загрузите SDK

Начнём с загрузки последней версии ARCore Android SDK с GitHub. Распакуйте её в удобное для вас место. Для этого практического занятия используется самая ранняя версия 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 к машине разработчика и используйте меню «Запуск» > «Запустить приложение» , чтобы запустить отладочную версию на устройстве. В диалоговом окне, предлагающем выбрать устройство для запуска, выберите подключенное устройство и нажмите «ОК» .

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

В этом примере проекта используется targetSdkVersion 28 Если у вас возникла ошибка сборки, например, Failed to find Build Tools revision 28.0.3 , следуйте инструкциям, описанным в Android Studio, чтобы загрузить и установить необходимую версию Android Build Tools.

Если все пройдет успешно, демонстрационное приложение запустится на устройстве и запросит разрешение на создание фотографий и видео с помощью дополненной реальности. Нажмите «РАЗРЕШИТЬ» , чтобы предоставить разрешение.

Протестируйте с помощью тестового изображения.

Теперь, когда вы настроили среду разработки, вы можете протестировать приложение, предоставив ему изображение для просмотра.

В Android Studio, в окне Project , перейдите в раздел app > assets и дважды щелкните файл default.jpg , чтобы открыть его.

9b333680e7b9f247.jpeg

Наведите камеру устройства на изображение Земли на экране и следуйте инструкциям, чтобы совместить сканируемое изображение с перекрестием прицела.

Поверх изображения будет наложена рамка, вот так:

999e05ed35964f6e.png

Далее мы внесем небольшие улучшения в демонстрационное приложение.

3. Отобразите модель лабиринта на двухмерном изображении.

Вы можете начать экспериментировать с дополненной реальностью, отобразив поверх неё 3D-модель.

Скачать 3D-модель

Для этого практического занятия мы будем использовать модель " Circle Maze - Green " от Evol, распространяемую по лицензии CC-BY 3.0 . Я сохранил копию этой 3D-модели в репозитории GitHub этого практического занятия, который вы можете найти здесь .

Выполните следующие шаги, чтобы загрузить модель и добавить её в Android Studio.

  1. Перейдите в репозиторий GitHub этого учебного пособия, в каталог third_party .
  2. Щелкните файл GreenMaze_obj.zip и нажмите кнопку «Скачать» .

В результате загрузки создается файл под названием GreenMaze_obj.zip .

  1. В Android Studio создайте директорию green-maze в папке app > assets > models.
  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 > assets > models > green-maze .

В этой папке должны находиться два файла: GreenMaze.obj и GreenMaze.mtl .

a1f33a2d2d407e03.png

Визуализируйте модель лабиринта.

Выполните следующие шаги, чтобы отобразить 3D-модель GreenMaze.obj поверх существующего 2D-изображения.

В 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).

Стена лабиринта все еще немного высоковата, чтобы поместиться поверх изображения. Создайте вспомогательную функцию 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();

Далее, в конце метода 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() отобразите Энди, стоящего на вершине лабиринта.

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 в каталог 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. Поздравляем!

Поздравляем, вы дошли до конца этого практического задания и, следовательно, выполнили следующее:

  • Создал и запустил Java-пример дополненной реальности с использованием ARCore.
  • В примере обновлено отображение модели лабиринта на изображении в правильном масштабе.
  • Использовала позу на фотографии, чтобы сделать что-нибудь забавное.

Если вы хотите ознакомиться с полным кодом, вы можете скачать его здесь .

Вам понравилось выполнять это задание в формате CodeLab?

Да Нет

Вы узнали что-нибудь полезное во время выполнения этого практического задания?

Да Нет

Вы завершили разработку приложения в рамках этого практического занятия?

Да Нет

Планируете ли вы разработать приложение на ARCore в ближайшие 6 месяцев?

Да Может быть Нет