ARCore:如何在表面上显示设备保存的图像

ARCore: How to display saved images from device on a surface

我是AR新手。我想知道是否有一种方法可以使用 ARCore for android 将保存在 phone 上的图片显示到表面上。就像我能够将 Sceneform 示例与预编码图像一起使用,但现在我想将图片文件夹中的照片显示到表面上。我阅读了有关纹理的信息,但我也不确定它是如何工作的。 谢谢你的提示。

编辑: 所以我创建了一个 class 用户可以从那里 select 我通过 Uri

保存的图像
public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == this.RESULT_CANCELED) {
        return;
    }
    if (requestCode == GALLERY) {
        if (data != null) {
            contentURI = data.getData();
            try {
                Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), contentURI);
                String path = saveImage(bitmap);
                Toast.makeText(imageSelector.this, "Image Saved!", Toast.LENGTH_SHORT).show();

                //set the image selected to bitmap so that imageView can display it
                imageview.setImageBitmap(bitmap);

                Intent intent = new Intent(this, HelloSceneformActivity.class);
                intent.putExtra("imageUri", contentURI.toString());
                startActivity(intent);

从 ARclass 方面我调用了意图并将其解析如下

//start the intent for the image chosen from the library
    Bundle extras = getIntent().getExtras();
    Uri myUri = Uri.parse(extras.getString("imageUri"));

    // When you build a Renderable, Sceneform loads its resources in the background while returning
    // a CompletableFuture. Call thenAccept(), handle(), or check isDone() before calling get().
    ModelRenderable.builder()
            .setSource(this, myUri)
            .build()
            .thenAccept(renderable -> andyRenderable = renderable)
            .exceptionally(
                    throwable -> {
                        Toast toast =
                                Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                        return null;
                    });

我可以 select 图片,但是程序崩溃了,我得到了这个错误

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.google.ar.sceneform.samples.hellosceneform/com.google.ar.sceneform.samples.hellosceneform.HelloSceneformActivity}: java.lang.IllegalArgumentException: Unable to parse url: 'content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F460/ORIGINAL/NONE/1926270799'

Caused by: java.lang.IllegalArgumentException: Unable to parse url: 'content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F460/ORIGINAL/NONE/1926270799'
        at com.google.ar.sceneform.utilities.LoadHelper.remoteUriToInputStreamCreator(Unknown Source:56)
        at com.google.ar.sceneform.utilities.LoadHelper.fromUri(Unknown Source:40)
        at com.google.ar.sceneform.rendering.Renderable$Builder.build(Renderable.java:343)
        at com.google.ar.sceneform.rendering.ModelRenderable$Builder.build(ModelRenderable.java:44)
        at com.google.ar.sceneform.samples.hellosceneform.HelloSceneformActivity.onCreate(HelloSceneformActivity.java:92)
        at android.app.Activity.performCreate(Activity.java:7174)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)

根据 https://github.com/google-ar/sceneform-android-sdk/issues/79,您目前无法从自定义 URI 加载:

in 1.0 we interpret all file:// uris as being relative to the assets folder which is why we fail to load from external storage. Consequently, in order to load a Texture from a Uri, you'll need to either put in in the assets folder (i.e. bundled into your apk), or host it online where it's available via an http:// scheme.

这意味着目前仅支持 http:// 和 file:// URI,后者只能与资产文件夹相关。 malik-at-work 在 https://github.com/google-ar/sceneform-android-sdk/issues/64:

上的回答证实了这一点

The URI based file loading isn't looking at external storage. Right now it's only looking for application assets using file:// and remote uris starting with http://

编辑:我建议的解决方法是使用 ViewRenderable in combination with a layout with a single ImageView inside. The ViewRenderable can create 2D-Layouts in the Scene, you just have to set your image into the ImageView like you would in a usual Android-App and position / rotate the resulting object to your liking. You can find an example-usage of the ViewRenderable in the solarsystem-Sceneform-example,速度滑块是用它实现的。

编辑 2: 这似乎已在 Sceneform 1.4 中修复,因为第一个提到的问题现已关闭。

您可以将图像作为 Uri 列表传递

public class MainActivity2 extends AppCompatActivity {
Button Gallery, AR;
private static final int PICK_IMAGE = 100;
Uri imageUri;
ImageView imageview;
private ArFragment arFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    Gallery = findViewById(R.id.select);
    AR = findViewById(R.id.ar);
    imageview = findViewById(R.id.imageView);

    AR.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent myIntent = new Intent(MainActivity2.this, MainActivity.class);
            MainActivity2.this.startActivity(myIntent);
        }
    });

    Gallery.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openGallery();
        }
    });


}
private void openGallery() {
    Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
    startActivityForResult(gallery, PICK_IMAGE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK && requestCode == PICK_IMAGE) {
        imageUri = data.getData();
        imageview.setImageURI(imageUri);

        setContentView(R.layout.activity_main);
        arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
        assert arFragment != null;
        arFragment.setOnTapArPlaneListener((hitResult, plane, motionEvent) - > {
            Anchor anchor = hitResult.createAnchor();
            createViewRenderable(hitResult.createAnchor());
        });

    }

}
private void addModeltoScene(Anchor anchor, ModelRenderable modelRenderable) {
    AnchorNode anchorNode = new AnchorNode(anchor);
    TransformableNode transformableNode = new TransformableNode(arFragment.getTransformationSystem());
    transformableNode.setParent(anchorNode);
    transformableNode.setRenderable(modelRenderable);
    arFragment.getArSceneView().getScene().addChild(anchorNode);
    transformableNode.select();
}
private void createViewRenderable(Anchor anchor) {
    ViewRenderable
        .builder()
        .setView(this, R.layout.text)
        .build()
        .thenAccept(viewRenderable - > {
            addtoScene(viewRenderable, anchor);
        });
}
private void addtoScene(ViewRenderable viewRenderable, Anchor anchor) {
    AnchorNode anchorNode = new AnchorNode(anchor);
    anchorNode.setRenderable(viewRenderable);
    arFragment.getArSceneView().getScene().addChild(anchorNode);
    View view = viewRenderable.getView();
    ViewPager viewPager = view.findViewById(R.id.viewPager);
    List < Uri > images = new ArrayList < > ();
    images.add(imageUri);
    Adapter adapter = new Adapter(images);
    viewPager.setAdapter(adapter);


}

class Adapter extends PagerAdapter {
    List < Uri > images;
    Adapter(List < Uri > images) {
        this.images = images;
    }
    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        View view = getLayoutInflater().inflate(R.layout.item, container, false);
        ImageView imageView = view.findViewById(R.id.imageView);
        imageView.setImageURI(imageUri);
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((ImageView) object);
    }

    @Override
    public int getCount() {
        return images.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }
}
}

我写了一篇关于同样的文章。请检查一下。它是关于以 AR 形式显示您图库中的 2D 图像。 https://medium.com/@deveshbhatla952/displaying-2-d-images-from-external-storage-in-ar-form-using-google-sceneform-2baae45f1a42 我试过了,效果很好。