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

 更新时间:2024年12月24日 11:00:46   作者:小噔小咚什么东东  
这篇文章主要为大家详细介绍了JavaScrip如何利用 TensorFlow.js 中的 HandPose 模型,实现一个基于视频流的手势识别系统,感兴趣的可以了解下

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

项目概述

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

  • 获取视频流并设置摄像头。
  • 加载 HandPose 模型,并显示加载进度。
  • 通过模型识别手势。
  • 基于手指的状态输出对应的手势数字(例如“1”代表一个手指,“2”代表两个手指等)。

获取视频流并设置摄像头

首先,需要通过浏览器的 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手势识别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js函数排序的实例代码

    js函数排序的实例代码

    这篇文章介绍了js函数排序的代码,有需要的朋友可以参考一下
    2013-07-07
  • JavaScript如何让select选择框可输入和可下拉选择

    JavaScript如何让select选择框可输入和可下拉选择

    我们知道一般select下拉框是只能选择的,而有时我们会遇到下拉框中没有要选择的信息项或者下拉选项特别多时,需要允许用户输入想要的内容,这篇文章主要给大家介绍了关于JavaScript如何让select选择框可输入和可下拉选择的相关资料,需要的朋友可以参考下
    2023-10-10
  • js的表单操作 简单计算器

    js的表单操作 简单计算器

    javascript写的简单的加减乘除计算器,里面涉及到一些方法还是很实用的哦,新手不要错过
    2011-12-12
  • JS调用页面表格导出excel示例代码

    JS调用页面表格导出excel示例代码

    这篇文章主要介绍了JS调用页面表格导出excel的具体实现,需要的朋友可以参考下
    2014-03-03
  • 比较详细的javascript DOM 学习笔记

    比较详细的javascript DOM 学习笔记

    看了很多的js dom学习资料,发现这个比较详细,特转载给大家学习一下
    2008-06-06
  • Javascript计算两个marker之间的距离(Google Map V3)

    Javascript计算两个marker之间的距离(Google Map V3)

    做地图开发,最常用到的就是marker一些操作和交互。简单介绍一下,两个marker之间的距离计算,感兴趣的朋友可以参考下哈,希望对你有所帮助
    2013-04-04
  • javascript实现消灭星星小游戏简单版

    javascript实现消灭星星小游戏简单版

    消灭星星是一款经典的益智手游,单位里看到同事天天在手机上玩的游戏,现在也有web版了,出于业余爱好,自己尝试用javascript实现了下,就是略简单了点,文中给出了完整的实例代码,大家可以自行完善!!下面来一起看看吧。
    2016-11-11
  • JavaScript进制转换实现方法解析

    JavaScript进制转换实现方法解析

    这篇文章主要介绍了JavaScript进制转换实现方法,结合实例形式分析了JavaScript进制转换中十进制与其他进制转换、以及随机颜色生成相关操作技巧,需要的朋友可以参考下
    2020-01-01
  • JS获取URL中的参数数据

    JS获取URL中的参数数据

    这篇文章主要介绍了JS获取URL中的参数数据,有需要的朋友可以参考一下
    2013-12-12
  • js实现在页面上弹出蒙板技巧简单实用

    js实现在页面上弹出蒙板技巧简单实用

    蒙板是两个div,其中popWindow样式的div用于遮住整个页面并半透明,感兴趣的朋友可以参考下哈,希望对你学习js蒙版有所帮助
    2013-04-04

最新评论