Immagini aumentate ARCore

1. Panoramica

ARCore è una piattaforma per creare app di realtà aumentata su Android. Le immagini aumentate ti consentono di creare app AR in grado di riconoscere immagini 2D preregistrate nel mondo reale e di aggiungere contenuti virtuali su 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, creerai un'app di esempio ARCore preesistente. Al termine del codelab, la tua app sarà in grado di:

  • Rileva un obiettivo immagine e collega un labirinto virtuale al bersaglio
  • Monitora il bersaglio in movimento purché si trovi nel campo visivo della videocamera.

6bc6605df89de525.gif

È la prima volta che crei un'app ARCore?

No

Hai intenzione di scrivere codice campione in questo codelab o vuoi semplicemente leggere queste pagine?

Scrivi 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

  • APK ARCore 1.9.0 o versioni successive. Normalmente questo APK viene installato automaticamente sul dispositivo tramite il Play Store
  • Una macchina di sviluppo con Android Studio (v3.1 o versioni successive)
  • Accesso a Internet, in quanto dovrai scaricare le librerie durante lo sviluppo

Ora che hai tutto pronto, possiamo iniziare.

2. Configurare l'ambiente di sviluppo

Scarica l'SDK

Per iniziare, scarica l'SDK ARCore Android più recente da GitHub. Decomprimilo nella posizione che preferisci. Per questo codelab, la prima versione dell'SDK è la 1.18.1. La cartella verrà denominata arcore-android-sdk-x.xx.x e 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 che Android Studio completi la sincronizzazione del progetto. Se Android Studio non dispone dei componenti necessari, l'operazione potrebbe non riuscire e viene visualizzato 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 dell'app ARCore funzionante, eseguiamolo un test.

Collega il dispositivo ARCore al computer di sviluppo e usa il menu Esegui > Esegui "app" per eseguire la versione di debug sul dispositivo. Nella finestra di dialogo in cui ti viene chiesto di scegliere il dispositivo da cui eseguire l'esecuzione, 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 generazione 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 a posto, l'app di esempio verrà avviata sul dispositivo e ti chiederà l'autorizzazione per consentire all'immagine aumentata di scattare foto e registrare video. Tocca CONSENTI per concedere l'autorizzazione.

Esegui il test con un'immagine di esempio

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

Torna in Android Studio, nella finestra Progetto vai ad app > asset e fai doppio clic sul file default.jpg per aprirlo.

9b333680e7b9f247.jpeg

Inquadra con la fotocamera del dispositivo l'immagine della Terra sullo schermo e segui le istruzioni per inserire l'immagine nel mirino.

Sull'immagine verrà sovrapposta una cornice, in questo modo:

999e05ed35964f6e.png

Successivamente, apporteremo piccoli miglioramenti all'app di esempio.

3. Visualizzazione di un modello di labirinto sull'immagine 2D

Puoi iniziare a giocare con le immagini aumentate visualizzando un modello 3D sopra.

Scarica un modello 3D

Per questo codelab utilizzeremo "Labirinto circolare - Verde". da Evol e concesso in licenza ai sensi delle CC-BY 3.0. Ho archiviato 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 al repository GitHub di questo codelab, directory third_party.
  2. Fai clic su GreenMaze_obj.zip e sul pulsante Scarica.

Verrà scaricato un file denominato GreenMaze_obj.zip.

  1. In Android Studio, crea la directory green-maze in app > asset > di machine learning
  2. Decomprimi GreenMaze_obj.zip e copia il contenuto 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 su app > asset > modelli > labirinto verde.

Questa cartella dovrebbe contenere due file: GreenMaze.obj e GreenMaze.mtl.

a1f33a2d2d407e03.png

Visualizza il modello del labirinto

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

In AugmentedImageRenderer.java, aggiungi una variabile membro denominata mazeRenderer per eseguire il rendering del modello del labirinto. Poiché il labirinto deve essere attaccato all'immagine, ha senso inserire mazeRenderer nella 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 texture 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 all'immagine rilevata e la viene visualizzata 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 su questo modello 3D di esempio, il codice riportato sopra utilizza alcune espressioni "magiche" numeri. Le dimensioni del modello del labirinto sono 492,65 x 120 x 492,65, con il centro a (251,3, 60, -129,0). L'intervallo dei vertici I valori delle coordinate X, Y e Z sono rispettivamente [5.02, 497.67], [0, 120] e [-375.17, 117.25]. Di conseguenza, la scala del modello di labirinto deve essere image_size / 492.65. mazeModelLocalOffset viene introdotto perché il modello 3D del labirinto non è centrato sull'origine (0, 0, 0).

La parete del labirinto è ancora un po' troppo alta per essere posizionata sopra l'immagine. Crea una funzione helper updateModelMatrix() in grado di scalare X, Y, Z in modo non uniforme per scalare l'altezza del labirinto di 0,1. Tieni presente che devi mantenere l'elemento updateModelMatrix(float[] modelMatrix, float scaleFactor) esistente e aggiungere la funzione sovraccarico 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. Il labirinto dovrebbe ora adattarsi perfettamente all'immagine.

772cbe2a8baef3ba.png

4. Aggiungi Andy al labirinto

Ora che hai creato un labirinto, aggiungi un personaggio per spostarti al suo interno. Utilizza il file andy.obj incluso nell'SDK ARCore Android. Mantieni la texture della cornice immagine come texture, perché ha un aspetto diverso dal labirinto verde visualizzato sopra l'immagine.

In AugmentedImageRenderer.java, aggiungi un elemento ObjectRenderer privato per eseguire il rendering di Andrea.

AugmentedImageRenderer.java

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

Quindi, 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, mostra Andrea 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à delle immagini target

ARCore si basa su funzionalità visive per riconoscere le immagini. A causa delle differenze di qualità, non tutte le immagini sono facilmente riconoscibili.

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

di Google. 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 pari a 100, pertanto è facilmente riconoscibile da ARCore.

5. (Facoltativo) Fai muovere Andy nel labirinto

Infine, puoi aggiungere del codice per far spostare l'Andy nel labirinto. Ad esempio, utilizza il motore di fisica open source jBullet per gestire la simulazione della fisica. Se salti questa parte va benissimo.

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 degli asset del progetto in modo che possa essere caricato durante l'esecuzione. Copia GreenMaze.obj dall'app > asset > models > green-maze ad app > asset.

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 aggiornamenti sulla posa di Andy.

AugmentedImageRenderer.java

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

In AugmentedImageActivity.java, crea un oggetto PhysicsController che utilizzi 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 di fisica, in realtà utilizziamo una palla rigida per rappresentare Andy e aggiorniamo la posa di Andy utilizzando la posa della palla. Chiama PhysicsController per aggiornare le leggi fisiche ogni volta che l'app riconosce un'immagine. Per spostare la palla come se fosse nel mondo reale, applica la gravità reale 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. Andy ora dovrebbe spostarsi realisticamente quando inclini l'immagine.

Nell'esempio riportato di seguito viene utilizzato un altro smartphone per visualizzare l'immagine. Puoi utilizzare tutto ciò che ti è più comodo, come un tablet, la copertina di un fotolibro o solo una carta stampata attaccata a un oggetto piatto.

2f0df284705d3704.gif

È tutto. Divertiti a cercare di portare Andrea nel labirinto. Suggerimento: è più facile trovare l'uscita se tieni l'immagine target capovolta.

6. Complimenti

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

  • Ha creato e eseguito un campione AugmentedImage Java ARCore.
  • È stato aggiornato l'esempio per visualizzare il modello del labirinto sull'immagine, con la scala corretta.
  • Ha utilizzato la posa dell'immagine per fare qualcosa di divertente.

Per consultare il codice completo, scaricalo qui.

Ti sei divertito a seguire questo codelab?

No

Hai imparato qualcosa di utile in 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