WEBKT

TensorFlow.js实战:移动端实时人脸检测与高性能贴纸叠加

126 0 0 0

前言

想象一下,你的网页应用能像Snapchat或TikTok一样,实时识别人脸并叠加各种有趣的贴纸或特效,是不是很酷炫?TensorFlow.js让这一切成为了可能。本文将带你一步步实现这个功能,并针对移动设备进行性能优化,确保流畅的用户体验。

技术栈

  • TensorFlow.js: 用于在浏览器中运行机器学习模型的库。
  • Face Detection Model (例如:BlazeFace, MediaPipe FaceMesh): 预训练的人脸检测模型,用于识别视频流中的人脸。
  • HTML5 Canvas: 用于绘制视频帧和叠加贴纸/特效。
  • JavaScript: 用于控制整个流程。

步骤详解

  1. 环境搭建:

    • 引入TensorFlow.js库:

      <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/face-detection@1.0.4/dist/face-detection.min.js"></script>
      
    • 创建HTML元素:

      <video id="video" width="640" height="480" autoplay muted></video>
      <canvas id="canvas" width="640" height="480"></canvas>
      
  2. 获取视频流:

    • 使用getUserMedia API访问摄像头:

      async function setupCamera() {
          const video = document.getElementById('video');
          const stream = await navigator.mediaDevices.getUserMedia({
              'audio': false,
              'video': {facingMode: 'user'},
          });
          video.srcObject = stream;
      
          return new Promise((resolve) => {
              video.onloadedmetadata = () => {
                  resolve(video);
              };
          });
      }
      
  3. 加载人脸检测模型:

    • 选择合适的模型(例如BlazeFace),并加载:

      async function loadFaceDetectionModel() {
          const model = await faceDetection.createDetector(faceDetection.SupportedModels.MediaPipeFaceMesh, {
              runtime: 'tfjs',
              modelType: 'mediapipe_face_mesh',
              maxFaces: 1, // 限制检测的人脸数量,提高性能
              flipHorizontal: true,
          });
          return model;
      }
      
  4. 实时人脸检测:

    • 从视频流中获取帧,并使用模型进行人脸检测:

      async function detectFaces(model, video) {
          const predictions = await model.estimateFaces(video);
          return predictions;
      }
      
  5. 贴纸/特效叠加:

    • 获取人脸关键点(例如眼睛、鼻子、嘴巴等),并根据这些关键点的位置和大小,将贴纸或特效绘制到Canvas上:

      function drawOverlay(predictions, canvas, video) {
          const ctx = canvas.getContext('2d');
          ctx.clearRect(0, 0, canvas.width, canvas.height);
      
          predictions.forEach(prediction => {
              const keypoints = prediction.scaledMesh; // 获取人脸关键点
      
              // 示例:在鼻子位置绘制一个简单的圆形贴纸
              const nose = keypoints[4]; // 鼻子关键点索引
              const x = nose[0];
              const y = nose[1];
              const stickerSize = 50; // 贴纸大小
      
              ctx.beginPath();
              ctx.arc(x, y, stickerSize / 2, 0, 2 * Math.PI);
              ctx.fillStyle = 'red';
              ctx.fill();
          });
      }
      
  6. 性能优化:

    • 模型选择: 选择轻量级的模型,例如BlazeFace,牺牲一定的精度来换取更快的速度。

    • 降低分辨率: 降低视频流的分辨率,减少计算量。

    • 限制检测人脸数量: maxFaces: 1只检测一张人脸,减少计算负担。

    • 帧率控制: 不必每帧都进行检测,可以隔几帧检测一次。例如,每秒检测15帧:

      let lastTime = 0;
      const fps = 15; // 每秒15帧
      function shouldDetect(currentTime) {
          const interval = 1000 / fps;
          if (currentTime - lastTime >= interval) {
              lastTime = currentTime;
              return true;
          }
          return false;
      }
      
    • 硬件加速: 确保TensorFlow.js使用WebGL后端,利用GPU进行加速。

    • 代码优化: 避免在循环中创建对象,尽量复用对象。

  7. 主循环:

    • 将上述步骤整合到主循环中:

      async function main() {
          const video = await setupCamera();
          const model = await loadFaceDetectionModel();
          const canvas = document.getElementById('canvas');
          canvas.width = video.videoWidth;
          canvas.height = video.videoHeight;
      
          function render(currentTime) {
              if (shouldDetect(currentTime)) {
                  detectFaces(model, video).then(predictions => {
                      drawOverlay(predictions, canvas, video);
                  });
              }
      
              requestAnimationFrame(render);
          }
      
          render(0);
      }
      
      main();
      

完整代码示例

<!DOCTYPE html>
<html>
<head>
    <title>TensorFlow.js Face Detection</title>
    <style>
        body {
            font-family: sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        #container {
            position: relative;
        }
        #canvas {
            position: absolute;
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
    <h1>TensorFlow.js 实时人脸检测</h1>
    <div id="container">
        <video id="video" width="640" height="480" autoplay muted></video>
        <canvas id="canvas" width="640" height="480"></canvas>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/face-detection@1.0.4/dist/face-detection.min.js"></script>
    <script>
        async function setupCamera() {
            const video = document.getElementById('video');
            const stream = await navigator.mediaDevices.getUserMedia({
                'audio': false,
                'video': {facingMode: 'user'},
            });
            video.srcObject = stream;

            return new Promise((resolve) => {
                video.onloadedmetadata = () => {
                    resolve(video);
                };
            });
        }

        async function loadFaceDetectionModel() {
            const model = await faceDetection.createDetector(faceDetection.SupportedModels.MediaPipeFaceMesh, {
                runtime: 'tfjs',
                modelType: 'mediapipe_face_mesh',
                maxFaces: 1,
                flipHorizontal: true,
            });
            return model;
        }

        async function detectFaces(model, video) {
            const predictions = await model.estimateFaces(video);
            return predictions;
        }

        function drawOverlay(predictions, canvas, video) {
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            predictions.forEach(prediction => {
                const keypoints = prediction.scaledMesh;

                const nose = keypoints[4];
                const x = nose[0];
                const y = nose[1];
                const stickerSize = 50;

                ctx.beginPath();
                ctx.arc(x, y, stickerSize / 2, 0, 2 * Math.PI);
                ctx.fillStyle = 'red';
                ctx.fill();
            });
        }

        let lastTime = 0;
        const fps = 15;
        function shouldDetect(currentTime) {
            const interval = 1000 / fps;
            if (currentTime - lastTime >= interval) {
                lastTime = currentTime;
                return true;
            }
            return false;
        }

        async function main() {
            const video = await setupCamera();
            const model = await loadFaceDetectionModel();
            const canvas = document.getElementById('canvas');
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;

            function render(currentTime) {
                if (shouldDetect(currentTime)) {
                    detectFaces(model, video).then(predictions => {
                        drawOverlay(predictions, canvas, video);
                    });
                }

                requestAnimationFrame(render);
            }

            render(0);
        }

        main();
    </script>
</body>
</html>

总结

通过以上步骤,你就可以使用TensorFlow.js实现一个简单的实时人脸检测和贴纸叠加功能。记住,性能优化是关键,尤其是在移动设备上。选择合适的模型、控制帧率、利用硬件加速等手段,可以显著提高用户体验。快去尝试一下,打造你自己的酷炫应用吧!

贴纸狂魔 TensorFlow.js人脸检测性能优化

评论点评