如何将画布恰好堆叠在网络摄像头流的顶部?

问题描述

代码如下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>
        <Meta charset="UTF-8" />
        <Meta name="viewport" content="width=device-width,initial-scale=1.0" />
        <title>Document</title>
        <script>
            async function get_facemesh() {
                var canvas = document.getElementById("facemesh");

                var draw = canvas.getContext("2d");

                const stream = document.getElementById("movie");

                const model = await facemesh.load((maxFaces = 1));

                while (1) {
                    const faces = await model.estimateFaces(stream);

                    if (faces.length != 0) {
                        canvas.width = canvas.width;

                        var mesh = faces[0].scaledMesh;
                        var widthAcross = mesh[454][0] - mesh[234][0];
                        var heightVertical = mesh[152][1] - mesh[10][1];

                        console.log(heightVertical);

                        draw.fillStyle = "red";

                        for (var i = 0; i < mesh.length; i++) {
                            draw.fillRect(mesh[i][0],mesh[i][1],2,2);
                        }
                    } else {
                        console.log(`No faces have been detected`);
                    }

                    await tf.nextFrame();
                }
            }
        </script>
    </head>
    <body>
        <div class="webcam">
            <video width="600" height="450" autoplay id="movie"></video>
            <canvas width="600" height="450" id="facemesh"> </canvas>
        </div>
        <script>
            video = document.getElementById("movie");
            if (navigator.mediaDevices.getUserMedia) {
                navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
                    video.srcObject = stream;
                });
            }

            main();

            function main() {
                if (video.readyState == 4) {
                    console.log("video is ready for processing..");
                    get_facemesh();
                } else {
                    console.log("nope,not loaded yet..");
                    setTimeout(main,1000 / 30);
                }
            }
        </script>
    </body>
    <style>
        body {
            background-color: limegreen;
            margin: 0;
            padding: 0;
        }
        video,canvas {
            -webkit-transform: scaleX(-1);
            transform: scaleX(-1);
            position: fixed;
            margin: 0 auto;
        }

        .webcam {
            width: 600px;
            height: 450px;
            background-color: limegreen;
            margin: auto;
        }
    </style>
</html>

这是那些喜欢这些的人的codepen.io链接https://codepen.io/mdsiam8/pen/gOrPayp

我尝试使用位置固定的技巧,但是即使它们正好位于彼此之上,也没有用。感觉画布有些伸张,或者可能稍微向左变形。我尝试摆弄这个方法,但找不到解决方法,因此,如果有任何人可以提供帮助,我将不胜感激。顺便说一句,我知道在网络摄像头顶部偏移的小面网是不正常的,因为我有一个文件并使用了位置固定的技巧,而且这是完美的,所以我不确定为什么它不是这种情况也是这样。

解决方法

<video>的{​​{1}}和width属性设置“元素可视内容的尺寸” ref,这意味着,仅显示方式。
您的脸部检测器正在按视频的固有尺寸处理视频数据,即由媒体本身而不是由元素定义的那些,在显示时会拉伸或缩小视觉内容它。

另一方面,画布的heightwidth属性设置其固有尺寸

因此,两种媒体的固有尺寸实际上都不同,并且视频中找到的坐标与画布上的坐标不匹配。

要解决此问题,您只需将画布的heightwidth分别设置为heightvideo.videoWidth,以使其与视频内容的固有尺寸匹配,然后通过CSS 调整其大小,使其与video.videoHeight元素的显示尺寸匹配。

但是请注意,MediaStreams可以在播放时更改其尺寸,因此您可能希望收听<video>的{​​{1}}事件。

Link to updated codepen since StackSnippets don't allow gUM.

最值得注意的变化:

resize
<video>
const canvas = document.getElementById("facemesh");
const video = document.getElementById("movie");
if (navigator.mediaDevices.getUserMedia) {
  navigator.mediaDevices.getUserMedia({ video: true })
    .then( stream => {
    video.srcObject = stream;
    video.addEventListener( "resize",(evt) => {
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
    } );
    // don't poll,use events
    video.addEventListener( "loadedmetadata",(evt) => {
      get_facemesh();
    } );
  } );
}