关于png:在Android上结合视频流的图像

 2021-04-26 

Combine image with video stream on Android

我正在研究Android上的增强现实。

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

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

我想实现的一种效果是将图像(例如.jpeg或.png)与来自摄像机内置设备的实时供稿合并/叠加。

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

但是我不希望叠加图像成为固定/静态水印,当用户放大,缩小或平移时,叠加图像也必须放大,缩小和平移等。

我不希望过度播放的图像成为3d或任何具有这种性质的图像。

Sceneform可以实现这种效果吗?还是我需要使用其他第三方库和/或工具才能获得所需的结果。

更新

用户正在一张白纸上绘图。调整纸张的方向,使用户可以舒适地绘画(左手或右手)。用户在完成图像处理时可以自由移动纸张。

Android设备位于纸片上方,可以拍摄用户绘制所选图像的画面。

实时摄像机的提要正在投射到大电视或监视器屏幕上。

为帮助用户,他们已将静态图像选择为" trace "或" Copy "。

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

用户可以放大和缩小其图形,并且组合的实时流和选定的静态图像也将进行放大和缩小,这将使用户可以通过绘制" Free Hand"来精确复制选定的静态图像。 "。

当用户直接看纸时,他们只能看到自己的图纸。

当用户在电视或监视器上观看他们的绘画的实况直播流时,他们会看到他们的绘画和所选的静态图像叠加在一起。用户可以控制静态图像的透明度,以帮助他们精确复制静态图像。


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

有2个部分。首先是找到纸,其次是将图像放在纸上,并在手机四处移动时将其保持在该位置。

仅通过检测纸张的平面即可找到纸张(具有一定的对比度,图案或其他东西,而与普通的白色纸张比较有帮助),然后轻按页面中心的位置是。这是在HelloSceneform示例中完成的。

如果要使纸张装订得更准确,可以点击纸张的四个角,然后在其中创建锚点。为此,在onCreate()

中注册一个平面监听的侦听器

1
   arFragment.setOnTapArPlaneListener(this::onPlaneTapped);

然后在onPlaneTapped中,创建4个anchorNode。一旦有了4,就初始化要显示的图形。

1
2
3
4
5
6
7
8
9
10
11
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 Texture。这可以来自资源或文件URL。您希望纹理显示整个图像,并在调整包含它的模型的大小时进行缩放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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布置四边形的逻辑相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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);
}

最后一部分是将四边形放置在角的中心,并创建一个Transformable节点,以便可以将图像微移到适当的位置,旋转或缩放为最佳大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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);
}

可以使用ARobjects缩放所需的AR参考图像,以此为用户调整模板大小。

更复杂的AR图像将不容易工作,因为AR图像会覆盖在用户跟踪的顶部,这会阻塞他们的笔/铅笔的笔尖。

我的解决方案是将白皮书抠像。这会将白皮书替换为所选的图像或实时供稿。除非您有跟踪纸张位置的方法,否则按照您指定的方向移动纸张将是一个问题。

如本例所示,AR对象在前面,而抠像是背景。跟踪表面(纸张)将在中心。

此示例的引用在下面的链接上。

RJ

  • YouTube-AR追踪环境