Obrazy rozszerzone ARCore

1. Omówienie

ARCore to platforma do tworzenia aplikacji rzeczywistości rozszerzonej na Androida. Obrazy rozszerzone to funkcja, która umożliwia tworzenie aplikacji AR, które rozpoznają wstępnie zarejestrowane obrazy 2D w świecie rzeczywistym i zakotwiczają treści wirtualne.

Dzięki temu ćwiczeniu w Codelabs dowiesz się, jak zmodyfikować istniejącą przykładową aplikację ARCore, aby dodać obrazy rozszerzone, które poruszają się lub są stałe.

Co utworzysz

W tym ćwiczeniu w Codelabs utworzysz kontynuację istniejącej przykładowej aplikacji ARCore. Po zakończeniu ćwiczeń w Codelabs Twoja aplikacja będzie mogła:

  • Wykryj cel obrazu i dołącz wirtualny labirynt na celowniku
  • Śledź ruchomy cel, dopóki znajduje się w polu widzenia kamery

6bc6605df89de525.gif

Czy tworzysz aplikację ARCore po raz pierwszy?

Tak Nie

Planujesz napisać przykładowy kod w ramach tego ćwiczenia z programowania czy tylko przeczytać te strony?

Pisanie przykładowego kodu Tylko przeczytaj te strony

Czego się nauczysz

  • Jak korzystać z obrazów rozszerzonych w ARCore w Javie
  • Jak ocenić zdolność obrazu do rozpoznania przez ARCore
  • Jak załączyć treści wirtualne do obrazu i śledzić jego ruch

Wymagania wstępne

Do wykonania tego ćwiczenia z programowania potrzebne jest odpowiedni sprzęt i oprogramowanie.

Wymagania sprzętowe

Wymagania dotyczące oprogramowania

  • Plik APK ARCore w wersji 1.9.0 lub nowszej. Ten plik APK jest zwykle automatycznie instalowany na urządzeniu ze Sklepu Play.
  • Maszynę deweloperską z Android Studio (wersja 3.1 lub nowsza).
  • dostęp do internetu, ponieważ biblioteki trzeba pobierać na etapie tworzenia aplikacji.

Skoro masz już wszystko gotowe, możemy zacząć!

2. Konfigurowanie środowiska programistycznego

Pobierz pakiet SDK

Zaczniemy od pobrania najnowszego pakietu ARCore Android SDK z GitHuba. Rozpakuj go do wybranej lokalizacji. Najwcześniejszą wersją pakietu SDK na potrzeby tego ćwiczenia w Codelabs jest 1.18.1. Folder będzie nazywać się arcore-android-sdk-x.xx.x, a dokładną wartością będzie używana przez Ciebie wersja pakietu SDK.

Uruchom Android Studio i kliknij Otwórz istniejący projekt Android Studio.

5fbf2b21609187cc.png

Przejdź do tego rozpakowanego folderu:

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

Kliknij Otwórz.

Poczekaj, aż Android Studio zakończy synchronizację projektu. Jeśli Android Studio nie ma wymaganych komponentów, może wystąpić błąd i wyświetlić komunikat Install missing platform and sync project. Postępuj zgodnie z instrukcjami, aby rozwiązać problem.

Uruchamianie przykładowej aplikacji

Masz już działający projekt aplikacji ARCore, czas więc na przetestowanie aplikacji.

Połącz urządzenie ARCore z maszyną programistyczną i użyj menu Uruchom > Uruchom „app”, aby uruchomić wersję do debugowania na urządzeniu. W oknie z prośbą o wybranie urządzenia, na którym chcesz uruchomić aplikację, wybierz połączone urządzenie i kliknij OK.

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

Ten przykładowy projekt używa targetSdkVersion 28. Jeśli wystąpił błąd kompilacji, na przykład Failed to find Build Tools revision 28.0.3, postępuj zgodnie z instrukcjami w Android Studio, aby pobrać i zainstalować wymaganą wersję Android Build Tools.

Jeśli wszystko będzie w porządku, przykładowa aplikacja uruchomi się na urządzeniu i poprosi o pozwolenie na robienie zdjęć i nagrywanie filmów w ramach obrazu rozszerzonego. Aby przyznać uprawnienia, kliknij ZEZWÓL.

Testowanie za pomocą przykładowego obrazu

Po skonfigurowaniu środowiska programistycznego możesz przetestować aplikację, nadając jej obraz.

Wróć do Android Studio i w oknie Projekt przejdź do sekcji aplikacja > zasoby i kliknij dwukrotnie plik default.jpg, aby go otworzyć.

9b333680e7b9f247.jpeg

Skieruj aparat urządzenia na obraz Ziemi na ekranie i postępuj zgodnie z instrukcjami, aby dopasować do krzyżyka skanowanego obrazu.

Na obraz zostanie nałożona ramka, która wygląda tak:

999e05ed35964f6e.png

Następnie wprowadzimy niewielkie ulepszenia w aplikacji próbnej.

3. Wyświetl model labiryntu na obrazie 2D

Aby zacząć korzystać z obrazów rozszerzonych, możesz wyświetlić na nich model 3D.

Pobierz model 3D

W tym ćwiczeniu w Codelabs użyjemy „Circle Maze – Green”. przez Evol. na licencji CC-BY 3.0. Zapisałam kopię tego modelu 3D w repozytorium GitHub dotyczącym tego ćwiczenia z programowania. Znajdziesz je tutaj.

Aby pobrać model i dołączyć go do Android Studio, wykonaj te czynności.

  1. Przejdź do repozytorium GitHub tego ćwiczenia z programowania, katalogu innej firmy.
  2. Kliknij GreenMaze_obj.zip i kliknij przycisk Pobierz.

Spowoduje to pobranie pliku o nazwie GreenMaze_obj.zip.

  1. W Android Studio utwórz katalog green-maze w sekcji aplikacja > zasoby > modele
  2. Rozpakuj plik GreenMaze_obj.zip i skopiuj zawartość do tej lokalizacji: arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. W Android Studio przejdź do menu aplikacja > zasoby > modele > Green-maze.

W tym folderze powinny się znajdować 2 pliki: GreenMaze.obj i GreenMaze.mtl.

a1f33a2d2d407e03.png

Renderowanie modelu labiryntu

Wykonaj te czynności, aby wyświetlić model 3D GreenMaze.obj na istniejącym zdjęciu 2D.

W obiekcie AugmentedImageRenderer.java dodaj zmienną składową o nazwie mazeRenderer, aby wyrenderować model labiryntu. Ponieważ labirynt powinien zostać dołączony do obrazu, warto umieścić mazeRenderer wewnątrz klasy AugmentedImageRenderer.

AugmentedImageRenderer.java

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

W funkcji createOnGlThread() wczytaj GreenMaze.obj. Dla uproszczenia użyj tej samej tekstury ramki co jej tekstura.

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);

  }

Zastąp definicję funkcji draw() podaną niżej. Spowoduje to dostosowanie rozmiaru labiryntu do rozmiaru wykrytego obrazu i wyświetlenie go na ekranie.

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);
  }

Teraz labirynt powinien wyświetlić się na zdjęciu Ziemi na ekranie default.jpg.

Uwaga: nie masz pełnej kontroli nad tym przykładowym modelem 3D, więc powyższy kod stosuje kilka „magicznych” funkcji liczby. Wymiary modelu labiryntu to 492,65 x 120 x 492,65, ze środkiem w wysokości (251,3, 60, -129,0). Zakres jego wierzchołków Wartości współrzędnych X, Y i Z to odpowiednio [5, 02; 497, 67], [0, 120] i [-375,17; 117, 25]. Dlatego skala modelu labiryntu musi wynosić image_size / 492.65. Element mazeModelLocalOffset został wprowadzony, ponieważ model 3D labiryntu nie jest wyśrodkowany wokół punktu początkowego (0, 0, 0).

Ściana labiryntu jest wciąż za wysoka, aby zmieścić się na zdjęciu. Utwórz funkcję pomocniczą updateModelMatrix(), która może skalować X, Y i Z nierównomiernie, aby przeskalować wysokość labiryntu o 0,1. Zachowaj istniejącą funkcję updateModelMatrix(float[] modelMatrix, float scaleFactor) i dodaj przeciążenie funkcji updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) jako nową funkcję.

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);
}

Uruchom kod. Labirynt powinien teraz dokładnie mieścić się nad zdjęciem.

772cbe2a8baef3ba.png

4. Dodaj Andy’ego do labiryntu

Gdy masz już labirynt, dodaj postać, która będzie się w nim poruszać. Użyj pliku andy.obj dołączonego do pakietu ARCore Android SDK. Zachowaj teksturę ramki obrazu jako teksturę, ponieważ wygląda ona inaczej niż zielony labirynt renderowany na obrazie.

W pliku AugmentedImageRenderer.java dodaj prywatny obiekt ObjectRenderer, aby renderować Andy'ego.

AugmentedImageRenderer.java

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

Następnie zainicjuj andyRenderer pod koniec 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);
  }

Na koniec wyrenderuj Andy’ego stojącego na labiryncie na końcu funkcji 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);

  }

Uruchom kod. Zobaczysz Andy’ego, który stoi na szczycie labiryntu.

cb1e74569d7ace69.png

Określanie docelowej jakości obrazu

ARCore rozpoznaje obrazy za pomocą funkcji wizualnych. Ze względu na różnice w jakości nie wszystkie obrazy są łatwo rozpoznawalne.

arcoreimg to narzędzie wiersza poleceń, które pozwala określić, jak rozpoznawalny będzie obraz dla ARCore. Podaje liczbę od 0 do 100, przy czym 100 to najłatwiejsza do rozpoznania wartość.

, Oto przykład:

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

maze.jpg ma wartość 100, więc jest łatwo rozpoznawalny przez ARCore.

5. Opcjonalnie: niech Andy porusza się w labiryncie

Na koniec możesz dodać kod, który sprawi, że andy poruszają się po labiryncie. Na przykład do przeprowadzenia symulacji fizyki możesz użyć mechanizmu open source Physics, jBullet. Nic nie szkodzi, jeśli pominiesz tę część.

Pobierz PhysicsController.java i dodaj go do swojego projektu w katalogu

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

W Android Studio dodaj plik GreenMaze.obj do katalogu zasobów projektu, aby można go było załadować w czasie działania. Skopiuj GreenMaze.obj z aplikacji > zasoby > modele > green-maze do app > zasobów.

Dodaj te zależności do pliku build.gradle aplikacji.

app/build.gradle

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

Zdefiniuj zmienną andyPose, aby zapisać pozycję Andy’ego w bieżącej pozycji.

AugmentedImageRenderer.java

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

Zmodyfikuj AugmentedImageRenderer.java, aby wyrenderować Andy za pomocą nowej zmiennej 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);
  }

Dodaj nową funkcję narzędziową (updateAndyPose()), aby otrzymywać informacje o pozycji Andy’ego.

AugmentedImageRenderer.java

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

W AugmentedImageActivity.java utwórz obiekt PhysicsController, który używa silnika fizycznego JBullet do zarządzania wszystkimi funkcjami związanymi z fizyką.

AugmentedImageActivity.java

import com.google.ar.core.Pose;

  // Declare the PhysicsController object
  private PhysicsController physicsController;

W silniku fizyki używamy sztywnej piłki, aby przedstawić Andy’ego i zaktualizować jego pozycję, korzystając z jej pozycji. Wywołaj PhysicsController, aby zaktualizować zasady fizyczne za każdym razem, gdy aplikacja rozpozna obraz. Aby poruszać piłką tak jak w świecie rzeczywistym, użyj rzeczywistej grawitacji, aby poruszać piłką w labiryncie.

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;

Uruchom aplikację. Andy powinien realistycznie się poruszać, gdy przechylisz obraz.

W przykładzie poniżej użyto innego telefonu do wyświetlenia obrazu. Możesz użyć dowolnego wygodnego rozwiązania, np. tabletu, okładki książki drukowanej lub wydrukowanego papieru przyklejonego na płaskim obiekcie.

2f0df284705d3704.gif

Znakomicie. Dobrej zabawy! Spróbuj uwolnić Andy'ego przez labirynt. Wskazówka: wyjście łatwiej znaleźć, gdy przytrzymasz obraz docelowy do góry nogami.

6. Gratulacje

Gratulacje. To już koniec tego ćwiczenia z programowania. Dlatego:

  • Utworzyliśmy i uruchomiliśmy przykładową aplikację ARCore w języku Java AugmentedImage.
  • Zaktualizowano przykład, aby wyświetlić na obrazie model labiryntu w odpowiedniej skali.
  • Wykorzystanie wizerunku w celu zrobienia czegoś zabawnego.

Pełny kod możesz pobrać tutaj.

Czy ćwiczenie z programowania było przyjemne?

Tak Nie

Czy udało Ci się dowiedzieć czegoś przydatnego podczas tego ćwiczenia z programowania?

Tak Nie

Czy udało Ci się ukończyć tworzenie aplikacji w ramach tego ćwiczenia z programowania?

Tak Nie

Czy w ciągu najbliższych 6 miesięcy planujesz stworzyć aplikację ARCore?

Tak Być może Nie
.