基本概念

MediaPipe Tasks Vision 是 MediaPipe 提供的一系列开箱即用的计算机视觉任务包,可帮助开发者快速构建高效的视觉应用,广泛应用于如人脸识别、手势识别、图像分割等领域。

主要功能

  • 人脸检测 :能检测图像或视频中人脸的位置。

  • 人脸特征点检测 :可精确定位人脸的关键特征点,方便后续处理。

  • 人脸美颜 :对人脸进行美化处理,如磨皮、美白等。

  • 手势识别 :实时识别手部手势,获取手势结果及手部关键点。

  • 手部特征点检测 :检测手部在图像中的关键特征点,可渲染视觉特效。

  • 人体姿态特征点检测 :结合姿态、人脸、手部特征点检测组件,实现人体特征点的完整检测。

  • 图像分类 :识别图像所属的预定义类别。

  • 图像嵌入 :从图像中提取嵌入。

  • 图像分割 :将图像分割为不同类别。

  • 目标检测 :检测图像或视频中多类目标的位置。

  • 自拍分割 :将自拍图像分割为前景和背景。

特点

  • 易用性强 :提供简洁的 API,无需深入了解底层实现细节,即可快速上手使用。

  • 性能出色 :基于 MediaPipe 强大的框架,能高效处理各种视觉任务,满足实时性要求。

  • 模型丰富 :涵盖多种预训练模型,适应不同场景和需求。

  • 跨平台支持 :可在多种操作系统和设备上运行。

  • 可扩展性好 :支持自定义模型和任务配置,开发者能根据实际需求灵活调整。

安装与使用 

  1. npm i @mediapipe/tasks-vision
  2. import { GestureRecognizer, FilesetResolver, DrawingUtils } from '@mediapipe/tasks-vision'

 实现效果

可见,控制台打印了识别的到的手势类型,以及得分,实际中可以根据情况,对识别到的结果进行对应操作,比如握拳让页面滚动,实现赛博操控。

示例代码

HTML

<template>
  <video id="webcam" width="640" height="480" autoplay></video>
  <canvas id="output_canvas" width="640" height="480"></canvas>
</template>

Script

<script setup lang="ts">
import { GestureRecognizer, FilesetResolver, DrawingUtils } from '@mediapipe/tasks-vision';
import { ref, onMounted, nextTick } from 'vue'
// 手势识别器实例
let gestureRecognizer: any = null;

// 识别器识别的类型(图片/视频)
const runningMode = ref();

// 视频手势信息
const videoGestureInfo = ref<any>({});

// 手势枚举
const enumGesture: any = {
  Closed_Fist: '握紧拳头',
  Open_Palm: '张开手掌',
  Thumb_Up: '竖起大拇指',
  Thumb_Down: '拇指朝下',
  Pointing_Up: '指向上',
  Victory: '胜利',
  None: '未识别',
};

// 创建手势识别器
const createGestureRecognizer = async () => {
  // 加载指定版本的MediaPipe视觉任务WebAssembly模块
  const vision = await FilesetResolver.forVisionTasks('https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.3/wasm');

  // 创建了一个手势识别器实例(这个手势识别器实例使用的是指定版本的MediaPipe视觉任务WebAssembly模块)
  gestureRecognizer = await GestureRecognizer.createFromOptions(vision, {
    // 识别器配置
    baseOptions: {
      // 指向手势识别模型的路径
      modelAssetPath: '/gesture_recognizer.task',
      // 设置为GPU以尝试利用图形处理单元进行加速,提高模型推理的速度
      delegate: 'GPU',
    },
    // 检测手掌的数量
    numHands: 2,
  });
  console.log('手势识别器加载完毕');
  // 识别视频中的手势
  predictWebcam();
};

// 识别视频中的手势
const predictWebcam = async () => {
  // 判断是否可以使用摄像头
  if (!hasGetUserMedia()) return alert('此设备不允许使用摄像头!');
  // 判断手势识别器是否加载完成
  if (!gestureRecognizer) return alert('手势识别器未加载完成');

  if (runningMode.value !== 'VIDEO') {
    // 设置识别器识别的类型为视频
    runningMode.value = 'VIDEO';
    await gestureRecognizer.setOptions({ runningMode: runningMode.value });
  }

  await gestureRecognizer.setOptions({ numHands: 2 });

  nextTick(() => {
    // 获取video元素
    const video = document.getElementById('webcam') as HTMLVideoElement;
    // 获取视频手势节点绘制的canvas元素
    const canvasElement = document.getElementById('output_canvas') as HTMLCanvasElement;
    // 设置canvas的宽度和高度为video的宽度和高度
    canvasElement.width = video.clientWidth;
    canvasElement.height = video.clientHeight;
    // 获取canvas的上下文
    const canvasCtx = canvasElement.getContext('2d') as CanvasRenderingContext2D;

    // 设置上次识别视频手势的时间
    let lastVideoTime = -1;

    // 识别视频中的手势
    const predictWebcam = () => {
      // 获取当前视频的时间
      let nowInMs = Date.now();
      let results: any = {};

      // 如果视频的时间发生变化,则识别视频中的手势
      if (video.currentTime !== lastVideoTime) {
        // 替换上次识别视频手势的时间
        lastVideoTime = video.currentTime;
        results = gestureRecognizer.recognizeForVideo(video, nowInMs);
      }

      // 保存当前的canvas状态
      canvasCtx.save();
      // 清除canvas的内容
      canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);

      // 创建drawingUtils实例,用于可视化MediaPipeVision任务的结果
      const drawingUtils = new DrawingUtils(canvasCtx);
      // 判断是否识别到手势
      if (results.landmarks) {
        // 循环绘制手势的节点
        for (const landmarks of results.landmarks) {
          // 绘制手势连接线
          drawingUtils.drawConnectors(landmarks, GestureRecognizer.HAND_CONNECTIONS, {
            // 连接线的颜色
            color: '#00FF00',
            // 连接线的宽度
            lineWidth: 3,
          });
          // 绘制手势关节点
          drawingUtils.drawLandmarks(landmarks, {
            // 关节点的颜色
            color: '#FF0000',
            // 关节点的半径
            radius: 2.5,
          });
        }
      }
      // 恢复canvas的状态
      canvasCtx.restore();

      // 判断是否识别到手势数据
      if (results?.gestures?.length > 0) {
        videoGestureInfo.value.categoryName = enumGesture[results.gestures[0][0].categoryName];
        videoGestureInfo.value.categoryScore = parseFloat(results.gestures[0][0].score * 100).toFixed(2);
        videoGestureInfo.value.handedness = results.handednesses[0][0].displayName;
        console.log('识别到的手势类别', videoGestureInfo.value.categoryName);
        console.log('识别到的手势得分', videoGestureInfo.value.categoryScore);
      } else {
        videoGestureInfo.value.categoryName = '';
        videoGestureInfo.value.categoryScore = '';
        videoGestureInfo.value.handedness = '';
      }

      // 递归调用,继续识别视频中的手势
      requestAnimationFrame(predictWebcam);
    };

    // 打开摄像头
    navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
      // 视频流添加到video元素中
      video.srcObject = stream;
      // 绑定视频加载完成事件,开始识别视频中的手势
      video.addEventListener('loadeddata', predictWebcam);
    });
  });
};

// 判断是否可以使用摄像头
function hasGetUserMedia() {
  return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}

onMounted(() => {
  // 加载手势识别器
  createGestureRecognizer();
});

</script>

其他

  1. 当然@mediapipe/tasks-vision的功能远不止于手势识别,像人脸识别,对象检测,面部实现也都支持,详情见@mediapipe/tasks-vision的npm地址
  2. 目前加载指定版本的MediaPipe视觉任务WebAssembly模块可能会出现加载失败,这与网络有关,属于正常。
  3. gesture_recognizer.task模型可以从官方地址获取,这里主包是下载到了本地方便使用。
Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐