美文网首页
ARCore 构建增强图像应用程序,识别图片加载模型

ARCore 构建增强图像应用程序,识别图片加载模型

作者: 橙果子 | 来源:发表于2019-08-14 19:28 被阅读0次

    Github源码地址

    在本教程中,您将学习如何通过将锚点设置为特定场景而不是常规平面来将3D模型放置在现实世界中。通过Google的ARCore,您可以增加可以被arcore识别的2D图像,然后将3D模型放在它们上面。

    您提供一些参考图像,并且ARCore跟踪确定这些图像在环境中的物理位置。增强图像已经广泛使用,如书籍,报纸,杂志等。

    您将对ARCore和Sceneform中的一些术语有基本的了解,例如Scene,Anchor,Node,TransformableNode等。

    什么是增强图像?

    ARCore中的增强图像允许您构建可在用户环境中响应2D图像(例如海报或产品包装)的AR应用程序。您提供了一组参考图像,一旦在摄像机视图中检测到这些图像,ARCore跟踪就会告诉您这些图像在AR会话中的物理位置。

    基本上,使用增强图像,您可以将简单的2D图像转换为增强图像,该图像可以被您的应用程序识别,然后用于在其上方放置3D模型。

    当您可能想要使用增强图像?

    以下是使用增强图像之前可能需要考虑的一些限制:

    • 您的使用案例不得涉及同时扫描20多张图像。由于ARCore一次只能跟踪多达20张图像。
    • 现实世界中物理对应物的尺寸必须大于15cm X 15cm且平坦。
    • 您不想跟踪移动的对象。尽管可以在图像停止后开始跟踪,但ARCore无法跟踪运动图像。
    • ARCore使用参考图像中的特征点,可以存储多达1000个图像的特征点信息。

    选择一个好的参考图像

    以下是一些选择良好参考图像以提高arcore可检测性的技巧:

    • 增强图像支持PNG,JPEG和JPG格式。
    • 检测基于高对比度的点,因此无论是使用彩色还是黑/白参考图像,都检测彩色和黑/白图像。
    • 图像的分辨率必须至少为300 X 300像素。
    • 使用高分辨率图像并不意味着提高性能。
    • 必须避免使用具有重复特征的图像,如图案,波尔卡圆点。
    • 使用arcoreimg工具评估参考图像的好坏程度。建议得分至少为75分。

    如何使用 arcoreimg工具:

    从这个链接下载arcore sdk for android:
    在任何您喜欢的地方解压缩zip文件的zip内容。
    导航到解压缩的文件夹,然后转到工具 - > arcoreimg - > windows(linux / macos,无论你使用什么)
    在此位置打开命令提示符。
    现在输入以下命令:

    // 将dog.png替换为图像的完整路径。
    arcoreimg.exe eval-img --input_image_path=dog.png
    

    开始使用增强图像应用程序

    现在你已经熟悉了ARCore和Sceneform,并选择了一个分数为75+的好参考图像,现在是时候开始编写应用程序!

    编写代码

    将创建一个自定义片段以添加到界面中。需要一个自定义代码,将改变默认代码的一些属性。

    创建一个名为“CustomArFragment”的类,并从ArFragment扩展它。以下是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);
    
            return config;
        }
    }
    

    首先,我们将平面发现指令设置为null。通过这样做,我们关闭在初始化片段之后出现的手形图标,该图标指示用户移动他的手机。我们不再需要它,因为我们没有检测随机平面而是特定图像。

    接下来,我们将会话的更新模式设置为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;
        
    
    }
    

    我们很快将 在MainActivity中创建setupAugmentedImagesDb方法。现在创建了CustomArFragment,让我们将它添加到activity_main.xml,这里是您的activity_main.xml的代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        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.google.ar.sceneform.samples.augmentedimage.CustomArFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    
    </RelativeLayout>
    
    

    请注意,我们将此片段的名称设置为CustomArFragment。这是确保添加的片段是我们的自定义片段所必需的。这将确保处理权限处理和会话初始化。

    将图像添加到增强图像数据库

    在这里,我们将设置我们的图像数据库,在现实世界中找到参考图像,然后相应地添加3D模型。

    让我们从设置数据库开始。在MainActivity.java类中创建一个公共函数setupAugmentedImagesDb

    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("default.jpg")) {
                 return BitmapFactory.decodeStream(is);
           } catch (IOException e) {
                  Log.e("ImageLoad", "IO Exception", e);
           }
    
           return null;
    }
    

    我们还有loadAugmentedImage方法,它从assets文件夹加载图像并返回一个位图。

    setupAugmentedImagesDb中,我们首先为此会话初始化数据库,然后将图像添加到此数据库。我们将我们的形象命名为“老虎”。然后我们为此会话配置设置数据库并返回true,表示图像已成功添加。

    default.jpg识别图, frame_lower_left.sfb模型文件

    检测现实世界中的参考图像

    现在我们将开始在现实世界中检测我们的参考图像。为了做到这一点,我们将为场景添加一个监听器,每次创建一个帧时都会调用该监听器,并且将分析该帧以查找我们的参考图像。

    在MainActivity.java 的onCreate方法中添加此行

    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("models/frame_upper_left.sfb"));
                    shouldAddModel = false;
                }
            }
        }
    }
    

    在第一行中,我们从场景中获取帧。甲帧可以被想象为在视频的中间的快照。如果您熟悉视频的工作原理,您可能会知道它们是一系列静止图像,一个接一个地快速翻转,给人以电影的印象。我们正在提取其中一张照片。

    一旦我们有了框架,我们就会分析我们的参考图像。我们使用frame.getUpdatedTrackables提取了ARCore跟踪的所有项目的列表。这是所有检测到的图像的集合。然后我们循环收集并检查框架中是否存在我们的图像“tiger”。
    如果我们找到匹配项,那么我们继续并在检测到的图像上放置3D模型。

    注意:我已添加,shouldAddModel 以确保我们只添加一次模型。

    将3D模型放在参考图像上

    现在我们已经在现实世界中检测到了我们的图像,我们可以开始在其上添加3D模型。我们将从之前的项目中复制placeObject和addNodeToScene方法并在此处添加它们。

    虽然我之前已经逐行解释了这些方法的作用,但这里有一个概述:

    • PlaceObject:此方法用于从提供的Uri构建可渲染。构建可渲染后,将其传递到addNodeToScene方法,其中可渲染附加到节点,并将该节点放置到场景上。
    • AddNodeToScene:此方法从接收到的锚点创建AnchorNode,创建附加了可渲染项的另一个节点,然后将此节点添加到AnchorNode并将AnchorNode添加到场景中。

    这是我们最终的MainActivity.java类:

    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 by Google和Sceneform SDK创建了我们的第一个增强图像应用程序!


    效果图

    相关文章

      网友评论

          本文标题:ARCore 构建增强图像应用程序,识别图片加载模型

          本文链接:https://www.haomeiwen.com/subject/emynjctx.html