Объедините изображение с видео потоком на Android - PullRequest
0 голосов
/ 11 мая 2018

Я исследую дополненную реальность на Android.

Я использую ARCore и Sceneform в приложении для Android.

Я опробовал примеры проектов и теперь хотел бы разработать собственное приложение.

Один эффект, которого я хотел бы достичь, - это объединить / наложить изображение (скажем, .jpeg или .png) с прямой трансляцией с устройств на камере.

Изображение будет иметьпрозрачный фон, который позволяет пользователю одновременно просматривать прямую трансляцию и изображение

Однако я не хочу, чтобы наложенное изображение было фиксированным / статическим водяным знаком, когда пользователь увеличивает или уменьшает наложенное изображение, оно также должноувеличение, уменьшение, панорамирование и т. д.

Я не хочу, чтобы переигранное изображение стало трехмерным или чем-то в этом роде.

Возможен ли этот эффект в Sceneform?или мне нужно будет использовать другие сторонние библиотеки и / или инструменты для достижения желаемых результатов.

ОБНОВЛЕНИЕ

Пользователь рисует на чистом листе белой бумаги.Лист бумаги ориентирован таким образом, чтобы пользователь удобно рисовал (левую или правую руку).Пользователь может свободно перемещать лист бумаги, пока он заканчивает свое изображение.

Устройство Android удерживается над листом бумаги, где снимается изображение, на котором пользователь рисует выбранное изображение.

Прямая трансляция с камерывыполняется на большом экране телевизора или монитора.

Чтобы помочь пользователю, он выбрал статическое изображение для «трассировки» или «копирования».

Это изображение выбрано на устройстве Androidи объединяется с прямой трансляцией камеры в приложении Android.

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

Когда пользователь смотрит прямо на лист бумаги, он видит только свой рисунок.

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

Ответы [ 2 ]

0 голосов
/ 19 мая 2018

Предполагаемое эталонное изображение AR можно масштабировать с помощью объектов AR в качестве точек для определения размера шаблона для пользователя.

Более сложные AR-изображения не будут работать легко, так как AR-изображение накладывается поверх трассировки пользователей, и это будет препятствовать кончику их ручки / карандаша.

Мое решение состоит в том, чтобы закрасить белую бумагу. Это заменит белую бумагу с выбранным изображением или прямой трансляцией. Перемещение бумаги, как вы указали, будет проблемой, если у вас нет средств для отслеживания положения бумаги.

Как вы можете видеть в этом примере, объекты AR находятся впереди, а кеинга - фоном. Поверхность трассировки (бумага) будет в центре.

Ссылка на этот пример находится по ссылке ниже.

RJ

0 голосов
/ 17 мая 2018

Я думаю, что вы ищете, чтобы использовать AR для отображения изображения, чтобы изображение оставалось на месте, например, на листе бумаги, чтобы служить в качестве руководства для рисования копии изображения на бумаге .

Есть 2 части к этому. Во-первых, найдите лист бумаги, а во-вторых, поместите изображение поверх бумаги и держите его там, когда телефон перемещается.

Определить местонахождение листа бумаги можно, просто обнаружив плоскость вместе с бумагой (поможет контраст, или рисунок или что-то в сравнении с простым белым листом бумаги), затем нажмите там, где должен находиться центр страницы. быть. Это сделано в примере HelloSceneform.

Если вы хотите иметь более точные границы бумаги, вы можете нажать на 4 угла бумаги, а затем создать там якоря. Для этого зарегистрируйте прослушиватель с повернутым самолетом в onCreate()

   arFragment.setOnTapArPlaneListener(this::onPlaneTapped);

Затем в onPlaneTapped создайте 4 узла привязки. Получив 4, инициализируйте чертеж, который будет отображаться.

private void onPlaneTapped(HitResult hitResult, Plane plane, MotionEvent event) {
    if (cornerAnchors.size() != 4) {
        AnchorNode corner = createCornerNode(hitResult.createAnchor());
        arFragment.getArSceneView().getScene().addChild(corner);
        cornerAnchors.add(corner);
    }

    if (cornerAnchors.size() == 4 && drawingNode == null) {
        initializeDrawing();
    }
}

Чтобы инициализировать чертеж, создайте текстуру Sceneform из растрового изображения или отрисовки. Это может быть от ресурса или файла URL. Вы хотите, чтобы текстура показывала все изображение и масштабировалась, когда размер модели, удерживающей его, изменяется.

private void initializeDrawing() {
    Texture.Sampler sampler = Texture.Sampler.builder()
            .setWrapMode(Texture.Sampler.WrapMode.CLAMP_TO_EDGE)
            .setMagFilter(Texture.Sampler.MagFilter.NEAREST)
            .setMinFilter(Texture.Sampler.MinFilter.LINEAR_MIPMAP_LINEAR)
            .build();
    Texture.builder()
            .setSource(this, R.drawable.logo_google_developers)
            .setSampler(sampler)
            .build()
            .thenAccept(texture -> {
                MaterialFactory.makeTransparentWithTexture(this, texture)
                        .thenAccept(this::buildDrawingRenderable);
            });
}

Модель для удержания текстуры - это просто квадратный квадратик, размер которого соответствует минимальному размеру между углами. Это та же логика, что и при разметке четырехугольника с использованием OpenGL.

private void buildDrawingRenderable(Material material) {

    Integer[] indices = {
            0, 1, 3, 3, 1, 2
    };

    //Calculate the center of the corners.
    float min_x = Float.MAX_VALUE;
    float max_x = Float.MIN_VALUE;
    float min_z = Float.MAX_VALUE;
    float max_z = Float.MIN_VALUE;
    for (AnchorNode node : cornerAnchors) {
        float x = node.getWorldPosition().x;
        float z = node.getWorldPosition().z;
        min_x = Float.min(min_x, x);
        max_x = Float.max(max_x, x);
        min_z = Float.min(min_z, z);
        max_z = Float.max(max_z, z);
    }

    float width = Math.abs(max_x - min_x);
    float height = Math.abs(max_z - min_z);
    float extent = Math.min(width / 2, height / 2);

    Vertex[] vertices = {
            Vertex.builder()
                    .setPosition(new Vector3(-extent, 0, extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(0, 1)) // top left
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(extent, 0, extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(1, 1)) // top right
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(extent, 0, -extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(1, 0)) // bottom right
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(-extent, 0, -extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(0, 0)) // bottom left
                    .build()
    };

    RenderableDefinition.Submesh[] submeshes = {
            RenderableDefinition.Submesh.builder().
                    setMaterial(material)
                    .setTriangleIndices(Arrays.asList(indices))
                    .build()
    };

    RenderableDefinition def = RenderableDefinition.builder()
            .setSubmeshes(Arrays.asList(submeshes))

            .setVertices(Arrays.asList(vertices)).build();

    ModelRenderable.builder().setSource(def)
            .setRegistryId("drawing").build()
            .thenAccept(this::positionDrawing);
}

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

private void positionDrawing(ModelRenderable drawingRenderable) {


    //Calculate the center of the corners.
    float min_x = Float.MAX_VALUE;
    float max_x = Float.MIN_VALUE;
    float min_z = Float.MAX_VALUE;
    float max_z = Float.MIN_VALUE;
    for (AnchorNode node : cornerAnchors) {
        float x = node.getWorldPosition().x;
        float z = node.getWorldPosition().z;
        min_x = Float.min(min_x, x);
        max_x = Float.max(max_x, x);
        min_z = Float.min(min_z, z);
        max_z = Float.max(max_z, z);
    }

    Vector3 center = new Vector3((min_x + max_x) / 2f,
            cornerAnchors.get(0).getWorldPosition().y, (min_z + max_z) / 2f);

    Anchor centerAnchor = null;
    Vector3 screenPt = arFragment.getArSceneView().getScene().getCamera().worldToScreenPoint(center);
    List<HitResult> hits = arFragment.getArSceneView().getArFrame().hitTest(screenPt.x, screenPt.y);
    for (HitResult hit : hits) {
        if (hit.getTrackable() instanceof Plane) {
            centerAnchor = hit.createAnchor();
            break;
        }
    }

    AnchorNode centerNode = new AnchorNode(centerAnchor);
    centerNode.setParent(arFragment.getArSceneView().getScene());

    drawingNode = new TransformableNode(arFragment.getTransformationSystem());
    drawingNode.setParent(centerNode);
    drawingNode.setRenderable(drawingRenderable);
}
...