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
我试过了,效果很好。
我是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 我试过了,效果很好。