Immagini aumentate ARCore

1. Panoramica

ARCore è una piattaforma per la creazione di app di realtà aumentata su Android. Augmented Images ti consente di creare app AR in grado di riconoscere immagini 2D preregistrate nel mondo reale e ancorare contenuti virtuali sopra di esse.

Questo codelab ti guida nella modifica di un'app di esempio ARCore esistente per incorporare immagini aumentate in movimento o fisse.

Cosa creerai

In questo codelab, utilizzerai un'app di esempio ARCore preesistente. Al termine del codelab, la tua app sarà in grado di:

  • Rilevare un target immagine e collegare un labirinto virtuale al target
  • Traccia il bersaglio in movimento finché si trova nel campo visivo della videocamera

6bc6605df89de525.gif

È la prima volta che crei un'app ARCore?

No

Hai intenzione di scrivere codice di esempio in questo codelab o vuoi solo leggere queste pagine?

Scrivi un codice di esempio Leggi queste pagine

Obiettivi didattici

  • Come utilizzare le immagini aumentate in ARCore in Java
  • Come valutare la capacità di un'immagine di essere riconosciuta da ARCore
  • Come allegare un contenuto virtuale a un'immagine e monitorarne il movimento

Prerequisiti

Per completare questo codelab, ti serviranno hardware e software specifici.

Requisiti hardware

Requisiti software

  • ARCore APK 1.9.0 o versioni successive. Questo APK viene normalmente installato automaticamente sul dispositivo tramite il Play Store.
  • Una macchina di sviluppo con Android Studio (v3.1 o versioni successive)
  • Accesso a internet, poiché dovrai scaricare le librerie durante lo sviluppo

Ora che hai tutto pronto, iniziamo.

2. Configura l'ambiente di sviluppo

Scarica l'SDK

Inizieremo scaricando l'SDK ARCore per Android più recente da GitHub. Decomprimilo nella posizione che preferisci. Per questo codelab, la versione dell'SDK meno recente è la 1.18.1. La cartella verrà chiamata arcore-android-sdk-x.xx.x, il valore esatto sarà la versione dell'SDK che utilizzi.

Avvia Android Studio e fai clic su Apri un progetto Android Studio esistente.

5fbf2b21609187cc.png

Vai a questa cartella decompressa:

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

Fai clic su Apri.

Attendi il completamento della sincronizzazione del progetto da parte di Android Studio. Se Android Studio non dispone dei componenti richiesti, potrebbe non funzionare e visualizzare il messaggio Install missing platform and sync project. Segui le istruzioni per risolvere il problema.

Esegui l'app di esempio

Ora che hai un progetto di app ARCore funzionante, eseguiamo un test.

Collega il dispositivo ARCore alla macchina di sviluppo e utilizza il menu Run (Esegui) > Run (Esegui) "app" per eseguire la versione di debug sul dispositivo. Nella finestra di dialogo che ti chiede di scegliere da quale dispositivo eseguire il test, scegli il dispositivo connesso e fai clic su Ok.

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

Questo progetto di esempio utilizza targetSdkVersion 28. Se si verifica un errore di build come Failed to find Build Tools revision 28.0.3, segui le istruzioni descritte in Android Studio per scaricare e installare la versione richiesta di Android Build Tools.

Se tutto va a buon fine, l'app di esempio verrà avviata sul dispositivo e ti chiederà l'autorizzazione per consentire a Immagine aumentata di scattare foto e registrare video. Tocca CONSENTI per concedere l'autorizzazione.

Test con un'immagine di esempio

Ora che hai configurato l'ambiente di sviluppo, puoi testare l'app fornendole un'immagine da analizzare.

In Android Studio, nella finestra Progetto, vai a app > assets e fai doppio clic sul file default.jpg per aprirlo.

9b333680e7b9f247.jpeg

Punta la fotocamera del dispositivo sull'immagine della Terra sullo schermo e segui le istruzioni per centrare l'immagine che stai scansionando nel mirino.

Sull'immagine verrà sovrapposto un riquadro, come questo:

999e05ed35964f6e.png

Successivamente, apporteremo piccoli miglioramenti all'app di esempio.

3. Visualizzare un modello di labirinto nell'immagine 2D

Puoi iniziare a utilizzare le immagini aumentate visualizzando un modello 3D sopra l'immagine.

Scaricare un modello 3D

Per questo codelab utilizzeremo "Circle Maze - Green" di Evol, con licenza CC-BY 3.0. Ho memorizzato una copia di questo modello 3D nel repository GitHub di questo codelab, che puoi trovare qui.

Segui questi passaggi per scaricare il modello e includerlo in Android Studio.

  1. Vai alla directory third_party del repository GitHub di questo codelab.
  2. Fai clic su GreenMaze_obj.zip e poi sul pulsante Scarica.

Viene scaricato un file denominato GreenMaze_obj.zip.

  1. In Android Studio, crea la directory green-maze in app > assets > models
  2. Decomprimi GreenMaze_obj.zip e copia i contenuti in questa posizione: arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. In Android Studio, vai a app > assets > models > green-maze.

In questa cartella devono essere presenti due file: GreenMaze.obj e GreenMaze.mtl.

a1f33a2d2d407e03.png

Visualizzare il modello del labirinto

Segui questi passaggi per visualizzare il modello 3D GreenMaze.obj sopra l'immagine 2D esistente.

In AugmentedImageRenderer.java, aggiungi una variabile membro chiamata mazeRenderer per eseguire il rendering del modello del labirinto. Poiché il labirinto deve essere collegato all'immagine, è opportuno inserire mazeRenderer all'interno della classe AugmentedImageRenderer.

AugmentedImageRenderer.java

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

Nella funzione createOnGlThread(), carica GreenMaze.obj. Per semplicità, utilizza la stessa trama del frame.

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

  }

Sostituisci la definizione della funzione draw() con la seguente. In questo modo, le dimensioni del labirinto vengono adattate a quelle dell'immagine rilevata e il labirinto viene visualizzato sullo schermo.

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

Ora il labirinto dovrebbe essere visualizzato sopra l'immagine default.jpg della Terra.

Nota:poiché non hai il controllo completo di questo modello 3D di esempio, il codice riportato sopra utilizza alcuni numeri "magici". La dimensione del modello del labirinto è 492,65 x 120 x 492,65, con il centro in (251,3, 60, -129,0). L'intervallo dei valori delle coordinate X, Y e Z dei suoi vertici è rispettivamente [5,02, 497,67], [0, 120] e [-375,17, 117,25]. Pertanto, la scala del modello del labirinto deve essere image_size / 492.65. Il mazeModelLocalOffset viene introdotto perché il modello 3D del labirinto non è centrato sull'origine (0, 0, 0).

Il muro del labirinto è ancora un po' troppo alto per essere inserito sopra l'immagine. Crea una funzione helper updateModelMatrix() che possa scalare X, Y e Z in modo non uniforme per scalare l'altezza del labirinto di 0, 1. Tieni presente che devi conservare updateModelMatrix(float[] modelMatrix, float scaleFactor) esistente e aggiungere l'overload della funzione updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) come nuova funzione.

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

Esegui il codice. Ora il labirinto dovrebbe adattarsi perfettamente sopra l'immagine.

772cbe2a8baef3ba.png

4. Aggiungi Andy al labirinto

Ora che hai un labirinto, aggiungi un personaggio che si muova al suo interno. Utilizza il file andy.obj incluso nell'SDK ARCore per Android. Mantieni la trama della cornice dell'immagine così com'è, perché è diversa dal labirinto verde visualizzato sopra l'immagine.

In AugmentedImageRenderer.java, aggiungi un ObjectRenderer privato per visualizzare Andy.

AugmentedImageRenderer.java

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

Successivamente, inizializza andyRenderer alla fine di 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);
  }

Infine, esegui il rendering di Andy in cima al labirinto alla fine della funzione 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);

  }

Esegui il codice. Dovresti vedere Andy in cima al labirinto.

cb1e74569d7ace69.png

Determinare la qualità dell'immagine target

ARCore si basa su caratteristiche visive per riconoscere le immagini. A causa delle differenze di qualità, non tutte le immagini possono essere riconosciute facilmente.

arcoreimg è uno strumento a riga di comando che ti consente di determinare il livello di riconoscibilità di un'immagine per ARCore. Restituisce un numero compreso tra 0 e 100, dove 100 è il più facile da riconoscere.

Ecco un esempio:

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

maze.jpg ha un valore di 100, quindi viene facilmente riconosciuto da ARCore.

5. (Facoltativo) Fai muovere Andy nel labirinto

Infine, puoi aggiungere del codice per far muovere Andy nel labirinto. Ad esempio, utilizza il motore fisico open source jBullet per gestire la simulazione fisica. Puoi saltare questa parte.

Scarica PhysicsController.java e aggiungilo al tuo progetto nella directory

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

In Android Studio, aggiungi GreenMaze.obj alla directory project assets, in modo che possa essere caricato in fase di runtime. Copia GreenMaze.obj da app > assets > models > green-maze a app > assets.

Aggiungi le seguenti dipendenze al file build.gradle dell'app.

app/build.gradle

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

Definisci una variabile andyPose per memorizzare la posizione della posa attuale di Andy.

AugmentedImageRenderer.java

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

Modifica AugmentedImageRenderer.java per eseguire il rendering di Andy utilizzando la nuova variabile 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);
  }

Aggiungi una nuova funzione di utilità, updateAndyPose(), per ricevere gli aggiornamenti della posa di Andy.

AugmentedImageRenderer.java

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

In AugmentedImageActivity.java, crea un oggetto PhysicsController che utilizza il motore fisico JBullet per gestire tutte le funzioni correlate alla fisica.

AugmentedImageActivity.java

import com.google.ar.core.Pose;

  // Declare the PhysicsController object
  private PhysicsController physicsController;

Nel motore fisico, utilizziamo una palla rigida per rappresentare Andy e aggiorniamo la sua posa utilizzando la posa della palla. Chiama PhysicsController per aggiornare la fisica ogni volta che l'app riconosce un'immagine. Per muovere la palla come nel mondo reale, applica la gravità per spostarla nel labirinto.

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;

Esegui l'app. Ora Andy dovrebbe muoversi in modo realistico quando inclini l'immagine.

L'esempio riportato di seguito utilizza un altro smartphone per visualizzare l'immagine. Puoi utilizzare qualsiasi cosa ti sia comoda, ad esempio un tablet, la copertina di un libro cartaceo o semplicemente un foglio stampato attaccato a un oggetto piatto.

2f0df284705d3704.gif

È tutto. Divertiti a cercare di far uscire Andy dal labirinto. Suggerimento: è più facile trovare l'uscita quando tieni l'immagine di destinazione capovolta.

6. Complimenti

Congratulazioni, hai raggiunto la fine di questo codelab e quindi:

  • Hai creato ed eseguito un esempio di AugmentedImage Java di ARCore.
  • È stato aggiornato l'esempio per mostrare un modello di labirinto sull'immagine, alla scala corretta.
  • Ha utilizzato la posa dell'immagine per fare qualcosa di divertente.

Se vuoi fare riferimento al codice completo, puoi scaricarlo qui.

Ti è piaciuto questo codelab?

No

Hai imparato qualcosa di utile durante questo codelab?

No

Hai completato la creazione dell'app in questo codelab?

No

Hai intenzione di creare un'app ARCore nei prossimi 6 mesi?

Forse No