Скрыть PlaneRenderer при съемке фотографий в Sceneform

Я добавил функцию на основе учебника Codelabs от Google (https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..index#15), который позволяет пользователям сфотографировать объекты AR, которые были добавлены в сцену. Код работает нормально, однако я хочу скрыть PlaneRenderer (белые точки, которые появляются, когда ARCore обнаруживает поверхность) на фотографии, сделанной пользователями.

В onClickListener для кнопки «Захват фото» я попытался установить PlaneRenderer на невидимость перед вызовом takePhoto (). Это скрывало PlaneRenderer на экране, но не на сделанной фотографии.

Это мой onClickListener:

capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
                for (TransformableNode vNode : videoNodeList){
                    if (vNode.isSelected()){
                        vNode.getTransformationSystem().selectNode(null);
                    }
                }
                takePhoto();
            }
        });

videoNodeList содержит список transformableNodes и используется для отслеживания объектов, добавленных пользователями (поскольку пользователи могут добавлять более 1 объекта в сцену). Поскольку объекты являются трансформируемыми узлами, пользователи могут нажимать на них для изменения размера / поворота, при этом под выбранным объектом отображается небольшой кружок. Итак, добавлен цикл for, чтобы отменить выбор всех трансформируемых узлов при съемке фотографий, чтобы маленький кружок также не появлялся на фотографии.

Метод takePhoto () взят из учебника CodeLabs и представлен следующим образом:

private void takePhoto() {

        final String filename = generateFilename();
        ArSceneView view = arFragment.getArSceneView();

        // Create a bitmap the size of the scene view.
        final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
                Bitmap.Config.ARGB_8888);

        // Create a handler thread to offload the processing of the image.
        final HandlerThread handlerThread = new HandlerThread("PixelCopier");
        handlerThread.start();
        // Make the request to copy.
        PixelCopy.request(view, bitmap, (copyResult) -> {
            if (copyResult == PixelCopy.SUCCESS) {
                try {
                    File file = saveBitmapToDisk(bitmap, filename);
                    MediaScannerConnection.scanFile(this,
                            new String[] { file.toString() }, null,
                            new MediaScannerConnection.OnScanCompletedListener() {
                                public void onScanCompleted(String path, Uri uri) {
                                    Log.i("ExternalStorage", "Scanned " + path + ":");
                                    Log.i("ExternalStorage", "-> uri=" + uri);
                                }
                            });
                } catch (IOException e) {
                    Toast toast = Toast.makeText(ChromaKeyVideoActivity.this, e.toString(),
                            Toast.LENGTH_LONG);
                    toast.show();
                    return;
                }
                Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
                        "Photo saved", Snackbar.LENGTH_LONG);
                snackbar.setAction("Open in Photos", v -> {
                    File photoFile = new File(filename);

                    Uri photoURI = FileProvider.getUriForFile(ChromaKeyVideoActivity.this,
                            ChromaKeyVideoActivity.this.getPackageName() + ".provider",
                            photoFile);
                    Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
                    intent.setDataAndType(photoURI, "image/*");
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(intent);

                });
                snackbar.show();
            } else {
                Toast toast = Toast.makeText(ChromaKeyVideoActivity.this,
                        "Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
                toast.show();
            }
            handlerThread.quitSafely();
        }, new Handler(handlerThread.getLooper()));

    }

Чтобы дать вам более четкое изображение, PlaneRenderer скрывается на экране устройства при нажатии кнопки «Сделать фото». Вот что видно сразу после того, как пользователь нажмет кнопку «Сделать фото»: PlaneRenderer скрыт на экране

Однако PlaneRenderer по-прежнему отображается на сделанной фотографии. Это полученное изображение:  фото снято

это не то, что я искал, так как я хочу скрыть PlaneRenderer на фотографии (т.е. на сделанной фотографии не должно быть белых точек)

Пользователи этого приложения добавляют объекты, выбирая объект в меню и нажимая на PlaneRenderer, поэтому полностью отключить PlaneRenderer невозможно. Кроме того, у меня есть еще одна функция записи видео в приложении, которой удалось успешно скрыть PlaneRenderer в записи, просто установив PlaneRenderer на невидимость, поэтому я не уверен, почему она не работает при захвате фотографий.

Любая помощь будет оценена! :)


person Cedric    schedule 10.03.2020    source источник


Ответы (1)


Наконец понял это спустя бесчисленное количество часов. Поделиться своим решением (может быть, не лучшим решением) на случай, если кто-то столкнется с этой же проблемой в будущем.

Я обнаружил, что из-за используемого handlerThread метод takePhoto () всегда выполняется до того, как PlaneRenderer становится невидимым при каждом нажатии кнопки. Итак, я добавил небольшую задержку, чтобы убедиться, что произойдет обратное, т.е. delay takePhoto () на короткое время, так что метод всегда будет выполняться после того, как planeRenderer станет невидимым.

Вот фрагмент кода:

capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
                for (TransformableNode vNode : videoNodeList){
                    if (vNode.isSelected()){
                        vNode.getTransformationSystem().selectNode(null);
                    }
                }

                v.postDelayed(new Runnable() {
                    public void run() {
                        takePhoto();
                    }
                }, 80);
            }
        });

Этот метод у меня сработал, но я уверен, что есть лучшие решения для решения этой проблемы. Надеюсь, это поможет кому-то с той же проблемой, и не стесняйтесь вносить свой вклад, если вы знаете лучшее решение.

person Cedric    schedule 11.03.2020
comment
при тестировании я обнаружил, что postDelayed () не нужен. Протестировано на медленном Galaxy S8 и быстром Galaxy S20 - person Someone Somewhere; 31.12.2020