Хабрахабр

[Перевод] Как создать приложение дополненной реальности с помощью ARCore

Как создать приложение дополненной реальности с помощью ARCore

Библиотека ARCore от Google позволяет добавлять на 2D-изображение (картинка или видео) полноценные 3D-модели. В этом гайде вы узнаете, как добавить 3D-модели в реальный мир.

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

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

Что такое изображения дополненной реальности?

Согласно документации для разработчиков, изображения дополненной реальности в ARCore позволяют создавать приложения дополненной реальности, которые могут «оживлять» 2D-изображения, например, плакаты или упаковки продуктов.

И эта информация используется для расположения 3D-модели на 2D-изображении. Вы загружаете в ARCore какие-то опорные изображения, а он вам затем сообщает об их обнаружении во время AR-сессии, например во время съёмки видео.

Ограничения использования изображений дополненной реальности

Вот некоторые ограничения, с которыми вы можете столкнуться при использовании изображений дополненной реальности:

  • ARCore может обрабатывать только до 20 опорных изображений одновременно.
  • Физическая плоскость в реальном мире должна быть плоской, а её площадь должна быть больше, чем 15 см х 15 см.
  • ARCore не может отслеживать движущиеся изображения и объекты.

Выбор подходящего опорного изображения

Вот несколько советов для выбора хорошего опорного изображения для ARCore:

  • Изображения дополненной реальности поддерживают форматы PNG, JPEG и JPG.
  • Неважно, цветное будет изображение или чёрно-белое, главное, чтобы оно было высокой контрастности.
  • Разрешение изображения должно быть не менее 300 х 300 пикселей.
  • Использование изображений с высоким разрешением не означает улучшение производительности.
  • Следует избегать изображений с повторяющимися паттернами (например, узорами или горошком).
  • Используйте инструмент arcoreimg, чтобы оценить, насколько подходит ваше изображение для работы. Рекомендуется оценка не менее 75 баллов.

Как использовать инструмент arcoreimg:

  • Загрузите ARCore SDK для Android по этой ссылке.
  • Распакуйте zip-содержимое файла в любое место.
  • В извлеченной папке перейдите по пути tools > arcoreimg > windows (даже если у вас Linux или macOS).
  • Откройте командную строку в этой директории.
  • И введите эту команду:

arcoreimg.exe eval-img --input_image_path=dog.png

Замените dog.png на полный путь к вашему изображению.

Начало работы с приложением дополненной реальности

Теперь, когда вы ознакомились с ARCore и выбрали хорошее изображение с оценкой 75+, пришло время приступить к написанию кода приложения.

Создание фрагмента

Создаём класс с именем CustomArFragment и наследуем его от ArFragment. Мы создадим фрагмент и добавим его в нашу Activity. Вот код для CustomArFragment:

package com.ayusch.augmentedimages; import android.util.Log; import com.google.ar.core.Config;
import com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment; public class CustomArFragment extends ArFragment
}

Делая это, мы убираем с экрана значок руки, который появляется сразу после инициализации фрагмента и говорит пользователю о необходимости перемещения своего смартфона для поиска плоскости. Прежде всего, мы отключаем обнаружение плоскости. Нам это больше не нужно, поскольку мы обнаруживаем не случайные плоскости, а конкретное изображение.

Это гарантирует, что мы будем узнавать об обновлениях изображения всякий раз, когда обновится кадр камеры. Затем мы устанавливаем режим обновления для сессии LATEST_CAMERA_IMAGE.

Настройка базы данных изображений

Теперь мы можем добавлять изображения в нашу базу данных. Добавьте выбранное опорное изображение (которое вы хотите обнаружить в физическом мире) в папку assets (создайте её, если её ещё нет).

В логи мы выведем результат этой операции: Мы создадим эту базу данных, как только будет создан фрагмент.

if ((((MainActivity) getActivity()).setupAugmentedImagesDb(config, session))) { Log.d("SetupAugImgDb", "Success");
} else { Log.e("SetupAugImgDb","Faliure setting up db");
}

Вот как будет выглядеть CustomArFragment:

package com.ayusch.augmentedimages; import android.util.Log; import com.google.ar.core.Config;
import com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment; public class CustomArFragment extends ArFragment { @Override protected Config getSessionConfiguration(Session session) { getPlaneDiscoveryController().setInstructionView(null); Config config = new Config(session); config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE); session.configure(config); getArSceneView().setupSession(session); if ((((MainActivity) getActivity()).setupAugmentedImagesDb(config, session))) { Log.d("SetupAugImgDb", "Success"); } else { Log.e("SetupAugImgDb","Faliure setting up db"); } return config; }
}

Теперь давайте добавим CustomArFragment в наш activity_main.xml: Вскоре мы добавим метод setupAugmentedImagesDb в MainActivity.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/sceneform_fragment" android:name="com.ayusch.augmentedimages.CustomArFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.constraint.ConstraintLayout>

Добавление изображения в базу данных

Сейчас мы настроим нашу базу данных изображений, обнаружим опорное изображение в реальном мире и добавим 3D-модель на изображение.

Создайте публичный метод setupAugmentedImagesDb в классе MainActivity: Давайте начнём с настройки нашей базы данных.

public boolean setupAugmentedImagesDb(Config config, Session session) { AugmentedImageDatabase augmentedImageDatabase; Bitmap bitmap = loadAugmentedImage(); if (bitmap == null) { return false; } augmentedImageDatabase = new AugmentedImageDatabase(session); augmentedImageDatabase.addImage("tiger", bitmap); config.setAugmentedImageDatabase(augmentedImageDatabase); return true;
} private Bitmap loadAugmentedImage() { try (InputStream is = getAssets().open("blanket.jpeg")) { return BitmapFactory.decodeStream(is); } catch (IOException e) { Log.e("ImageLoad", "IO Exception", e); } return null;
}

Мы также создали метод loadAugmentedImage, который загружает изображение из папки ресурсов и возвращает растровое изображение.

Мы назвали наше изображение tiger. В setupAugmentedImagesDb мы сначала инициализируем нашу базу данных для текущей сессии, а затем добавляем изображение в эту базу данных. Затем мы устанавливаем эту базу данных в конфиг и возвращаем true, сообщая о том, что изображение успешно добавлено.

Обнаружение опорных изображений в реальном мире

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

Добавьте эту строку в метод onCreate() в MainActivity:

arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame);

Теперь добавьте метод onUpdateFrame в MainActivity:

@RequiresApi(api = Build.VERSION_CODES.N)
private void onUpdateFrame(FrameTime frameTime) { Frame frame = arFragment.getArSceneView().getArFrame(); Collection<AugmentedImage> augmentedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage augmentedImage : augmentedImages) { if (augmentedImage.getTrackingState() == TrackingState.TRACKING) { if (augmentedImage.getName().equals("tiger") && shouldAddModel) { placeObject(arFragment, augmentedImage.createAnchor(augmentedImage.getCenterPose()), Uri.parse("Mesh_BengalTiger.sfb")); shouldAddModel = false; } } }
}

Кадр можно представить, как обычный скриншот из видео. В первой строке мы получаем сам кадр. Мы просто берём одну из этих картинок. Если вы знакомы с тем, как работает видео, вы знаете, что это просто набор изображений, которые очень быстро сменяют друг друга, создавая впечатление чего-то движущегося.

Мы берём список всех элементов, отслеженных ARCore, используя frame.getUpdatedTrackables. После того, как мы получили кадр, мы анализируем его на предмет наличия на нём нашего опорного изображения. Затем мы перебираем её и проверяем, присутствует ли в кадре наше изображение tiger.

Если совпадение найдено, то мы просто берём и размещаем 3D-модель поверх обнаруженного изображения.

Флаг shouldAddModel используется для того, чтобы мы добавляли 3D-модель только один раз. Примечание.

Размещение 3D-модели над опорным изображением

Добавим методы placeObject и addNodeToScene: Теперь, когда мы нашли наше опорное изображение в реальном мире, мы можем добавлять 3D-модель поверх него.

  • placeObject: этот метод используется для построения отрендеренного объекта по заданному Uri. Как только рендеринг завершён, объект передаётся в метод addNodeToScene, где объект прикрепляется к узлу, и этот узел помещается на сцену.
  • addNodeToScene: этот метод создаёт узел из полученного якоря, создаёт другой узел, к которому присоединяется визуализируемый объект, затем добавляет этот узел в якорный узел и помещает его на сцену.

Вот так теперь выглядит MainActivity:

package com.ayusch.augmentedimages; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast; import com.google.ar.core.Anchor;
import com.google.ar.core.AugmentedImage;
import com.google.ar.core.AugmentedImageDatabase;
import com.google.ar.core.Config;
import com.google.ar.core.Frame;
import com.google.ar.core.Session;
import com.google.ar.core.TrackingState;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.FrameTime;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.ux.ArFragment;
import com.google.ar.sceneform.ux.TransformableNode; import java.io.IOException;
import java.io.InputStream;
import java.util.Collection; public class MainActivity extends AppCompatActivity { ArFragment arFragment; boolean shouldAddModel = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); arFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.sceneform_fragment); arFragment.getPlaneDiscoveryController().hide(); arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame); } @RequiresApi(api = Build.VERSION_CODES.N) private void placeObject(ArFragment arFragment, Anchor anchor, Uri uri) { ModelRenderable.builder() .setSource(arFragment.getContext(), uri) .build() .thenAccept(modelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable)) .exceptionally(throwable -> { Toast.makeText(arFragment.getContext(), "Error:" + throwable.getMessage(), Toast.LENGTH_LONG).show(); return null; } ); } @RequiresApi(api = Build.VERSION_CODES.N) private void onUpdateFrame(FrameTime frameTime) { Frame frame = arFragment.getArSceneView().getArFrame(); Collection<AugmentedImage> augmentedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage augmentedImage : augmentedImages) { if (augmentedImage.getTrackingState() == TrackingState.TRACKING) { if (augmentedImage.getName().equals("tiger") && shouldAddModel) { placeObject(arFragment, augmentedImage.createAnchor(augmentedImage.getCenterPose()), Uri.parse("Mesh_BengalTiger.sfb")); shouldAddModel = false; } } } } public boolean setupAugmentedImagesDb(Config config, Session session) { AugmentedImageDatabase augmentedImageDatabase; Bitmap bitmap = loadAugmentedImage(); if (bitmap == null) { return false; } augmentedImageDatabase = new AugmentedImageDatabase(session); augmentedImageDatabase.addImage("tiger", bitmap); config.setAugmentedImageDatabase(augmentedImageDatabase); return true; } private Bitmap loadAugmentedImage() { try (InputStream is = getAssets().open("blanket.jpeg")) { return BitmapFactory.decodeStream(is); } catch (IOException e) { Log.e("ImageLoad", "IO Exception", e); } return null; } private void addNodeToScene(ArFragment arFragment, Anchor anchor, Renderable renderable) { AnchorNode anchorNode = new AnchorNode(anchor); TransformableNode node = new TransformableNode(arFragment.getTransformationSystem()); node.setRenderable(renderable); node.setParent(anchorNode); arFragment.getArSceneView().getScene().addChild(anchorNode); node.select(); }
}

Вы должны увидеть экран, как показано ниже. Теперь запустите ваше приложение. И как только ARCore обнаружит опорное изображение в реальном мире, добавит на него вашу 3D-модель. Подвигайте телефон немного над опорным объектом.

Результат

Читайте также: Создание вашего первого ARCore-приложения

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть