在Android上将图像与视频流相结合

Hec*_*tor 2 png android arcore sceneform

我正在调查Android上的增强现实.

我在Android应用程序中使用ARCore和Sceneform.

我已经尝试了示例项目,现在想开发自己的应用程序.

我想要实现的一个效果是将图像(例如.jpeg或.png)与来自摄像机设备的实时馈送组合/叠加.

图像将具有透明背景,允许用户同时查看实时馈送和图像

但是我不希望覆盖的图像是固定/静态水印,当用户放大,缩小或平移覆盖的图像时,还必须放大,缩小和平移等.

我不希望被夸大的图像变成3d或任何那种性质.

Sceneform可以实现这种效果吗?或者我是否需要使用其他第三方库和/或工具来实现所需的结果.

UPDATE

用户正在画一张白纸.纸张定向使得使用者舒适地拉伸(左手或右手).用户可以在完成图像时自由移动纸张.

Android设备位于纸张上方,拍摄用户绘制所选图像.

现场摄像机正在投射到大型电视或监视器屏幕.

为了帮助用户,他们选择了静态图像来"跟踪"或"复制".

此图像在Android设备上选择,并与Android应用程序中的实时摄像头流组合.

用户可以放大和缩小他们的绘图,并且组合的实时流和选定的静态图像也将放大和缩小,这将使用户能够通过绘制"自由手"来精确复制所选择的静态图像.

当用户直接看纸张时,他们只能看到他们的图纸.

当用户在电视或监视器上观看他们的演员实况流时,他们会看到他们的绘图和叠加的所选静态图像.用户可以控制静态图像的透明度,以帮助他们准确地复制静态图像.

Cla*_*son 5

我认为您正在寻找的是使用AR来显示图像,以便图像保持在原位,例如在一张纸上,以便作为在纸张上绘制图像副本的指南.

这有两个部分.首先是找到纸张,第二个是将图像放在纸张上,并在手机四处移动时将图像放在纸张上.

只需用纸张检测平面即可完成纸张的定位(有一些对比度,或图案或某些东西与纯白纸张有帮助),然后点击页面中心的位置.这是在HelloSceneform示例中完成的.

如果您想要更准确地定义纸张边界,可以点击纸张的4个角,然后在那里创建锚点.要做到这一点,请注册一个平面轻敲的侦听器onCreate()

   arFragment.setOnTapArPlaneListener(this::onPlaneTapped);
Run Code Online (Sandbox Code Playgroud)

然后在onPlaneTapped中,创建4个anchorNodes.获得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();
    }
}
Run Code Online (Sandbox Code Playgroud)

要初始化绘图,请从位图或drawable创建一个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);
            });
}
Run Code Online (Sandbox Code Playgroud)

保持纹理的模型只是一个扁平的四边形,大小与角之间的最小尺寸.这与使用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);
}
Run Code Online (Sandbox Code Playgroud)

最后一部分是将四边形定位在角落的中心,并创建一个可变形节点,以便可以将图像轻推到位,旋转或缩放为完美尺寸.

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);
}
Run Code Online (Sandbox Code Playgroud)