javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript手势识别

JavaScript实现手势识别的示例详解

作者:小噔小咚什么东东

这篇文章主要为大家详细介绍了JavaScrip如何利用 TensorFlow.js 中的 HandPose 模型,实现一个基于视频流的手势识别系统,感兴趣的可以了解下

利用 TensorFlow.js 中的 HandPose 模型,实现一个基于视频流的手势识别系统,通过 HTML5 视频流获取摄像头数据,并结合 HandPose 模型来识别用户的手势。最终的目标是能够通过摄像头实时识别并显示手指数量的手势。

项目概述

这个项目的实现包括以下几个步骤:

获取视频流并设置摄像头

首先,需要通过浏览器的 navigator.mediaDevices.getUserMedia API 获取用户的摄像头视频流,并在网页上显示该视频流。具体代码如下:

async function setupCamera() {
  const video = document.getElementById('video');
  const stream = await navigator.mediaDevices.getUserMedia({
    video: true,
  });
  video.srcObject = stream;

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

此函数将视频流显示在 HTML 中的 <video> 元素上,并在视频加载完成后返回该视频元素。

加载 HandPose 模型并显示加载进度

为了进行手势识别,我们需要加载 TensorFlow.js 提供的 HandPose 模型。HandPose 模型是一个用于估计手部关键点位置的深度学习模型,可以帮助我们识别手的姿势。 在加载 HandPose 模型时,我们希望显示加载进度。这是通过 progressCallback 实现的,它会在每次加载进度更新时触发:

async function loadHandPoseModel() {
  const loadingContainer = document.getElementById('loading-container');
  loadingContainer.style.display = 'block'; // 显示加载进度条

  const model = await handpose.load({
    flipHorizontal: false,  // 不要水平翻转模型
    progressCallback: (fraction) => {
      const progress = Math.round(fraction * 100); // 计算加载的百分比
      showLoadingProgress(progress); // 更新进度条
    }
  });

  loadingContainer.style.display = 'none'; // 隐藏加载进度条
  return model;
}

通过此函数,可以实时展示模型加载的进度条,提升用户体验。

识别手势

在加载完 HandPose 模型之后,我们就可以使用该模型来识别手势了。模型会估计手部的 21 个关键点位置,并通过这些关键点来推测手指是否伸展。 我们需要遍历这些关键点,分析每个手指是否伸出。如果手指的末端关键点(例如食指末端)高于相邻的关节点,表示该手指伸展。通过此逻辑,我们可以判断用户伸出的手指数量:

function detectFingers(landmarks) {
  let fingers = 0;

  // 判断每根手指的伸展情况
  const thumb = landmarks[4];  // 拇指
  const index = landmarks[8];  // 食指
  const middle = landmarks[12];  // 中指
  const ring = landmarks[16];  // 无名指
  const pinky = landmarks[20];  // 小拇指

  if (index[1] < thumb[1]) fingers++;  // 食指伸展
  if (middle[1] < index[1]) fingers++;  // 中指伸展
  if (ring[1] < middle[1]) fingers++;  // 无名指伸展
  if (pinky[1] < ring[1]) fingers++;  // 小拇指伸展

  return fingers;
}

通过此函数,能够判断出当前用户伸出的手指数量。

输出手势结果

通过识别出的手指数量,我们可以展示一个对应的数字手势。例如,如果用户伸出一个手指,我们显示“1”;如果伸出两个手指,则显示“2”,依此类推。手势识别结果会实时更新在网页上:

async function detectGesture(video, model) {
  const predictions = await model.estimateHands(video);

  // 如果没有检测到手部,则显示"检测中..."
  if (predictions.length === 0) {
    document.getElementById('result').innerText = "检测中...";
    requestAnimationFrame(() => detectGesture(video, model));
    return;
  }

  const landmarks = predictions[0].landmarks; // 获取手部的关键点
  const fingers = detectFingers(landmarks);

  let resultText = "检测中...";

  // 基于手指的状态判断手势
  switch (fingers) {
    case 1:
      resultText = "一";
      break;
    case 2:
      resultText = "二";
      break;
    case 3:
      resultText = "三";
      break;
    case 4:
      resultText = "四";
      break;
    case 5:
      resultText = "五";
      break;
    default:
      resultText = "无法识别手势";
      break;
  }

  // 更新检测结果显示
  document.getElementById('result').innerText = resultText;

  // 每一帧进行检测
  requestAnimationFrame(() => detectGesture(video, model));
}

每一帧都会重新检测手势,以确保输出实时更新。

整合所有部分

最后,将所有的功能整合在一起,启动视频流并开始手势识别:

async function main() {
  // 等待模型加载
  const model = await loadHandPoseModel();
  
  // 设置视频流并开始播放
  const video = await setupCamera();
  video.play();

  // 开始手势识别
  detectGesture(video, model);
}

main();

通过以上步骤,实现了一个基于 HandPose 模型的手势识别系统。利用浏览器提供的 getUserMedia API 获取视频流,并通过 TensorFlow.js 提供的 HandPose 模型来估计手的关键点,最终实现了对手势的实时识别。

完整代码

// 显示加载进度条
function showLoadingProgress(progress) {
  const progressBar = document.getElementById('progress');
  progressBar.style.width = progress + '%';
}

// 1. 获取视频流并设置摄像头
async function setupCamera() {
  const video = document.getElementById('video');
  const stream = await navigator.mediaDevices.getUserMedia({
    video: true,
  });
  video.srcObject = stream;

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

// 2. 加载 HandPose 模型并显示加载进度
async function loadHandPoseModel() {
  const loadingContainer = document.getElementById('loading-container');
  loadingContainer.style.display = 'block'; // 显示加载进度条

  const model = await handpose.load({
    flipHorizontal: false,  // 不要水平翻转模型
    progressCallback: (fraction) => {
      const progress = Math.round(fraction * 100); // 计算加载的百分比
      showLoadingProgress(progress); // 更新进度条
    }
  });

  loadingContainer.style.display = 'none'; // 隐藏加载进度条
  return model;
}

// 3. 识别手势
async function detectGesture(video, model) {
  const predictions = await model.estimateHands(video);

  // 如果没有检测到手部,则显示"检测中..."
  if (predictions.length === 0) {
    document.getElementById('result').innerText = "检测中...";
    requestAnimationFrame(() => detectGesture(video, model));
    return;
  }

  const landmarks = predictions[0].landmarks; // 获取手部的关键点
  const fingers = detectFingers(landmarks);

  let resultText = "检测中...";

  // 基于手指的状态判断手势
  switch (fingers) {
    case 1:
      resultText = "一";
      break;
    case 2:
      resultText = "二";
      break;
    case 3:
      resultText = "三";
      break;
    case 4:
      resultText = "四";
      break;
    case 5:
      resultText = "五";
      break;
    default:
      resultText = "无法识别手势";
      break;
  }

  // 更新检测结果显示
  document.getElementById('result').innerText = resultText;

  // 每一帧进行检测
  requestAnimationFrame(() => detectGesture(video, model));
}

// 4. 判断手势
function detectFingers(landmarks) {
  let fingers = 0;

  // 判断每个手指是否伸出:如果手指的末端关键点在其他关键点的上方,说明该手指伸出
  // 对于每根手指,检查其关键点位置
  // 手指顺序: [1: thumb, 2: index, 3: middle, 4: ring, 5: pinky]

  // 检查每根手指的伸展情况
  const thumb = landmarks[4];  // 拇指
  const index = landmarks[8];  // 食指
  const middle = landmarks[12];  // 中指
  const ring = landmarks[16];  // 无名指
  const pinky = landmarks[20];  // 小拇指

  if (index[1] < thumb[1]) fingers++;  // 如果食指的末端在拇指的末端前面,说明食指伸展
  if (middle[1] < index[1]) fingers++;  // 如果中指的末端在食指的末端前面,说明中指伸展
  if (ring[1] < middle[1]) fingers++;  // 如果无名指的末端在中指的末端前面,说明无名指伸展
  if (pinky[1] < ring[1]) fingers++;  // 如果小拇指的末端在无名指的末端前面,说明小拇指伸展

  // 在"四"和"五"的判断上,可以加大判断容忍度,避免误判
  if (ring[1] < middle[1] && pinky[1] < ring[1]) {
    // 可能"四"或者"五"的手势识别较弱,可以通过容忍度来改进
    fingers = (fingers === 4) ? 5 : fingers;
  }

  return fingers;
}

async function main() {
  // 等待模型加载
  const model = await loadHandPoseModel();
  
  // 设置视频流并开始播放
  const video = await setupCamera();
  video.play();

  // 开始手势识别
  detectGesture(video, model);
}

main();

html 文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手势识别</title>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/handpose"></script>
  <script src="https://unpkg.com/@tensorflow/tfjs-backend-webgl"></script>
</head>
<body>
  <h1>手势识别</h1>
  <video id="video" width="640" height="480" autoplay></video>
  <div id="result">检测中...</div>

  <div id="loading-container">
    <div id="progress-bar">
      <div id="progress"></div>
    </div>
    <div id="loading-text">正在加载模型...</div>
  </div>

  <script src="app.js"></script>
</body>
</html>

效果展示

到此这篇关于JavaScript实现手势识别的示例详解的文章就介绍到这了,更多相关JavaScript手势识别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文