Comment construire une application d’images augmentées avec ARCore

Cet article a été publié à l'origine ici.

Dans ce didacticiel, vous apprendrez à placer des modèles 3D dans le monde réel en définissant l’ancre comme une scène spécifique au lieu d’un plan normal. ARCore de Google vous permet d’agrandir les images 2D qui peuvent être reconnues par ARCore pour y placer des modèles 3D.

Vous fournissez des images de référence et le suivi ARCore détermine l'emplacement physique de ces images dans l'environnement. Les images augmentées sont déjà largement utilisées, par exemple dans les livres, les journaux, les magazines, etc.

Mais avant de plonger plus avant dans ce didacticiel, vous devez consulter les articles suivants comme condition préalable à celui-ci:

  • Qu'est-ce que ARCore de Google?
  • Construire une application de réalité augmentée en utilisant ARCore.

Une fois ces deux opérations terminées, vous aurez une compréhension de base de certaines terminologies dans ARCore et Sceneform telles que Scene, Anchor, Node, TransformableNode, etc.

Que sont les images augmentées?

Selon la documentation du développeur, Augmented Images dans ARCore vous permet de créer des applications AR capables de réagir aux images 2D, telles que des affiches ou des emballages de produits, dans l'environnement de l'utilisateur. Vous fournissez un ensemble d'images de référence et ARCore tracking vous indique où ces images sont physiquement situées dans une session AR, une fois qu'elles sont détectées dans la vue Caméra.

Fondamentalement, en utilisant des images augmentées, vous pouvez transformer une image 2D simple en une image augmentée qui peut être reconnue par votre application et être ensuite utilisée pour placer un modèle 3D au-dessus de celle-ci.

Quand vous voudrez peut-être utiliser des images augmentées

Voici quelques restrictions à prendre en compte avant d'utiliser des images augmentées:

  • Votre cas d'utilisation ne doit pas impliquer de numériser plus de 20 images simultanément (car ARCore ne peut suivre que 20 images à la fois).
  • La taille de la contrepartie physique dans le monde réel doit être supérieure à 15 cm x 15 cm et plate.
  • Vous ne voulez pas suivre les objets en mouvement. ARCore ne peut pas suivre les images en mouvement, bien qu'il puisse démarrer le suivi une fois l'image arrêtée.
  • ARCore utilise les points caractéristiques de l’image de référence et peut stocker des informations sur les points caractéristiques de 1 000 images maximum.

Choisir une bonne image de référence

Voici quelques conseils pour choisir une bonne image de référence afin d’améliorer la détectabilité d’ARCore:

  • Les images augmentées prennent en charge les formats PNG, JPEG et JPG.
  • La détection est basée sur des points de contraste élevé. Ainsi, les images couleur et noir / blanc sont détectées, qu'une image de référence couleur ou noir / blanc soit utilisée.
  • La résolution de l’image doit être d’au moins 300 x 300 pixels.
  • L'utilisation d'images haute résolution ne signifie pas une amélioration des performances.
  • Les images présentant des caractéristiques répétitives telles que des motifs et des pois doivent être évitées.
  • Utilisez l'outil arcoreimg pour évaluer la qualité de votre image de référence. Un score d'au moins 75 est recommandé.

Comment utiliser l'outil arcoreimg:

  • Téléchargez le SDK ARCore pour Android à partir de ce lien:
  • Extrayez le contenu du fichier zip où vous voulez.
  • Naviguez jusqu'au dossier extrait et allez dans outils -> arcoreimg -> windows (linux / macos comme vous l'utilisez)
  • Ouvrez l'invite de commande à cet emplacement.
  • Maintenant, entrez cette commande:
arcoreimg.exe eval-img --input_image_path = dog.png

Remplacez dog.png par le chemin complet de votre image.

Débuter avec l'application Images augmentées

Maintenant que vous vous êtes familiarisé avec ARCore et Sceneform et que vous avez sélectionné une bonne image de référence avec un score supérieur à 75, il est temps de commencer à coder l’application !!

Créer un fragment personnalisé

Nous allons créer un fragment personnalisé à ajouter à notre activité. Nous avons besoin d'un fragment personnalisé car nous allons modifier certaines propriétés du fragment par défaut.

Créez une classe nommée «CustomArFragment» et développez-la à partir d'ArFragment. Voici le code pour CustomArFragment:

package com.ayusch.augmentedimages;
importer android.util.Log;
importer com.google.ar.core.Config;
importer com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment;
Classe publique CustomArFragment étend ArFragment {
    @Passer outre
    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);
        retour config;
    }
}

Tout d'abord, nous définissons l'instruction de découverte de plan sur null. En faisant cela, nous désactivons l'icône de la main qui apparaît juste après l'initialisation du fragment, qui indique à l'utilisateur de déplacer son téléphone. Nous n’en avons plus besoin car nous ne détectons pas de plans aléatoires mais une image spécifique.

Ensuite, nous définissons le mode de mise à jour de la session sur LATEST_CAMERA_IMAGE. Cela garantit que votre écouteur de mise à jour est appelé chaque fois que le cadre de la caméra est mis à jour. Il configure le comportement de la méthode update.

Configuration de la base de données des images augmentées

Ajoutez votre image de référence choisie (que vous souhaitez détecter dans le monde physique) dans le dossier des actifs. Si votre dossier d’actifs n’existe pas, créez-en un. Nous allons maintenant ajouter des images augmentées à notre base de données, qui seront ensuite détectées dans le monde réel.

Nous allons configurer cette base de données dès que le fragment (scène) sera créé. Ensuite, nous vérifions le succès et l'échec de cet appel et définissons le journal en conséquence. Ajoutez le code suivant à votre fragment personnalisé:

if ((((MainActivity) getActivity ()). setupAugmentedImagesDb (config, session))) {
    Log.d ("SetupAugImgDb", "Success");
} autre {
    Log.e ("SetupAugImgDb", "Configuration de la base de données");
}

Voici à quoi ressemblerait le CustomArFragment:

package com.ayusch.augmentedimages;
importer android.util.Log;
importer com.google.ar.core.Config;
importer com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment;
Classe publique CustomArFragment étend ArFragment {
    @Passer outre
    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");
        } autre {
            Log.e ("SetupAugImgDb", "Configuration de la base de données");
        }
        retour config;
    }
}

Nous allons bientôt créer la méthode setupAugmentedImagesDb dans MainActivity. Maintenant que le CustomArFragment a été créé, ajoutons-le à notre activity_main.xml, voici le code de votre activity_main.xml:


    

Notez que nous avons attribué le nom de ce fragment à notre CustomArFragment. Cela est nécessaire pour garantir que le fragment ajouté est notre fragment personnalisé. Cela garantira que la gestion des autorisations et les initialisations de session sont prises en charge.

Ajout d'une image à la base de données d'images augmentées

Ici, nous allons configurer notre base de données d'images. Recherchez l'image de référence dans le monde réel, puis ajoutez un modèle 3D en conséquence.

Commençons par configurer notre base de données. Créez une fonction publique setupAugmentedImagesDb dans la classe MainActivity.java:

public boolean setupAugmentedImagesDb (Configuration config, Session session) {
    AugmentedImageDatabase augmentedImageDatabase;
    Bitmap bitmap = loadAugmentedImage ();
    if (bitmap == null) {
        retourne faux;
    }
    augmentedImageDatabase = new AugmentedImageDatabase (session);
    augmentedImageDatabase.addImage ("tiger", bitmap);
    config.setAugmentedImageDatabase (augmentedImageDatabase);
    retourne vrai;
}
bitmap privé loadAugmentedImage () {
try (InputStream est = getAssets (). open ("blanket.jpeg")) {
        return BitmapFactory.decodeStream (est);
    } catch (IOException e) {
        Log.e ("ImageLoad", "IO Exception", e);
    }
    return null;
}

Nous avons également une méthode loadAugmentedImage qui charge l'image à partir du dossier assets et renvoie un bitmap.

Dans setupAugmentedImagesDb, nous initialisons d'abord notre base de données pour cette session, puis nous ajoutons une image à cette base de données. Nous nommerons notre image "tigre". Ensuite, nous définissons la base de données pour cette configuration de session et renvoyons true, indiquant que l'image est ajoutée avec succès.

Détecter les images de référence dans le monde réel

Nous allons maintenant commencer à détecter nos images de référence dans le monde réel. Pour ce faire, nous allons ajouter un auditeur à notre scène, qui sera appelé chaque fois qu'une image est créée, et cette image sera analysée pour trouver notre image de référence.

Ajoutez cette ligne dans la méthode onCreate de MainActivity.java:

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

Ajoutez maintenant la méthode onUpdateFrame à MainActivity:

@RequiresApi (api = Build.VERSION_CODES.N)
void privé onUpdateFrame (FrameTime frameTime) {
    Frame frame = arFragment.getArSceneView (). GetArFrame ();
    Collection  augmentedImages = frame.getUpdatedTrackables (AugmentedImage.class);
    pour (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;
            }
        }
    }
}

En première ligne, nous obtenons le cadre de la scène. Une image peut être imaginée comme un instantané au milieu d'une vidéo. Si vous connaissez le fonctionnement d'une vidéo, vous savez peut-être qu'il s'agit d'une série d'images fixes renversées très rapidement, ce qui donne l'impression d'un film. Nous extrayons une de ces images.

Une fois que nous avons le cadre, nous analysons notre image de référence. Nous extrayons une liste de tous les éléments suivis par ARCore à l'aide de frame.getUpdatedTrackables. Ceci est une collection de toutes les images détectées. Nous parcourons ensuite la collection et vérifions si notre image «tigre» est présente dans le cadre.

Si nous trouvons une correspondance, nous allons de l'avant et plaçons un modèle 3D sur l'image détectée.

Remarque: j'ai ajouté, shouldAddModel pour que le modèle ne soit ajouté qu'une seule fois.

Placement d'un modèle 3D sur une image de référence

Maintenant que nous avons détecté notre image dans le monde réel, nous pouvons commencer à y ajouter des modèles 3D. Nous allons copier les méthodes placeObject et addNodeToScene de notre projet précédent et les ajouter ici.

Bien que j'aie déjà expliqué ce que ces méthodes font ligne par ligne, voici un aperçu:

  • PlaceObject: Cette méthode est utilisée pour construire un rendu à partir de l'URI fourni. Une fois que le rendu est construit, il est passé à la méthode addNodeToScene, dans laquelle le rendu est attaché à un nœud et ce nœud est placé sur la scène.
  • AddNodeToScene: cette méthode crée un AnchorNode à partir de l'ancre reçue, crée un autre nœud sur lequel le rendu est attaché, puis ajoute ce nœud au AnchorNode et ajoute AnchorNode à la scène.

Voici notre dernière classe MainActivity.java:

package com.ayusch.augmentedimages;
importer android.graphics.Bitmap;
importer android.graphics.BitmapFactory;
importer android.net.Uri;
importer android.os.Build;
importer android.support.annotation.RequiresApi;
importer android.support.v7.app.AppCompatActivity;
importer android.os.Bundle;
importer android.util.Log;
importer android.widget.Toast;
importer com.google.ar.core.Anchor;
importer com.google.ar.core.AugmentedImage;
importer com.google.ar.core.AugmentedImageDatabase;
importer com.google.ar.core.Config;
importer com.google.ar.core.Frame;
importer com.google.ar.core.Session;
importer 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;
importer java.io.IOException;
importer java.io.InputStream;
import java.util.Collection;
Classe publique MainActivity étend AppCompatActivity {
    ArFragment ArFragment;
    boolean shouldAddModel = true;
    @Passer outre
    Void protégé 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)
    placeObject (espace d'archive, ancre d'ancrage, Uri uri) {
        ModelRenderable.builder ()
                .setSource (arFragment.getContext (), uri)
                .construire()
                .thenAccept (modelRenderable -> addNodeToScene (arFragment, anchor, modelRenderable))
                .exceptionnellement (jetable -> {
                            Toast.makeText (arFragment.getContext (), "Erreur:" + throwable.getMessage (), Toast.LENGTH_LONG) .show ();
                            return null;
                        }
                )
    }
    @RequiresApi (api = Build.VERSION_CODES.N)
    void privé onUpdateFrame (FrameTime frameTime) {
        Frame frame = arFragment.getArSceneView (). GetArFrame ();
        Collection  augmentedImages = frame.getUpdatedTrackables (AugmentedImage.class);
        pour (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 (Configuration config, Session session) {
        AugmentedImageDatabase augmentedImageDatabase;
        Bitmap bitmap = loadAugmentedImage ();
        if (bitmap == null) {
            retourne faux;
        }
        augmentedImageDatabase = new AugmentedImageDatabase (session);
        augmentedImageDatabase.addImage ("tiger", bitmap);
        config.setAugmentedImageDatabase (augmentedImageDatabase);
        retourne vrai;
    }
    bitmap privé loadAugmentedImage () {
        try (InputStream est = getAssets (). open ("blanket.jpeg")) {
            return BitmapFactory.decodeStream (est);
        } catch (IOException e) {
            Log.e ("ImageLoad", "IO Exception", e);
        }
        return null;
    }
    private void addNodeToScene (ArFragment arFragment, Ancre d'ancrage, Rendu restituable) {
        AnchorNode anchorNode = new AnchorNode (ancre);
        TransformableNode node = new TransformableNode (arFragment.getTransformationSystem ());
        node.setRenderable (rendu);
        node.setParent (anchorNode);
        arFragment.getArSceneView (). getScene (). addChild (anchorNode);
        node.select ();
    }
}

Maintenant, lancez votre application. Vous devriez voir un écran comme indiqué ci-dessous. Déplacez-vous un peu sur votre téléphone par-dessus l'objet de référence. ARCore détectera les points caractéristiques et dès qu’il détectera l’image de référence dans le monde réel, il y ajoutera votre modèle 3D.

[ID de la légende = "attachment_1000" align = "aligncenter" width = "1280"]

J'ai utilisé ma couverture comme référence

Avec cela, nous avons créé notre toute première application Augmented Images à l'aide d'ARCore de Google et de Sceneform SDK!

Si vous souhaitez rester au courant des derniers articles, inscrivez-vous à la lettre d'information hebdomadaire en entrant votre adresse email dans le formulaire en haut à droite de cette page.

Vous aimez ce que vous lisez? N'oubliez pas de partager ce message sur Facebook, Whatsapp et LinkedIn.

Vous pouvez me suivre sur LinkedIn, Quora, Twitter et Instagram, où je réponds aux questions relatives au développement mobile, notamment Android et Flutter.