【解鎖新技能】手勢識別隔空打飛機

飛機大戰相信你們都玩過,好多同窗甚至本身寫過,無論你是用js仍是用java,是用原生代碼實現仍是使用遊戲引擎實現。無非就是使用鼠標或者手指觸控來在屏幕上拖動飛機移動來打飛機,但我相信你應該尚未玩過使用手勢隔空來控制飛機吧。javascript

那今天咱們就來開發一款飛機大戰遊戲,而後使用手勢隔空操控飛機。css

先上demo: 飛機大戰html

注意:須要開啓電腦攝像頭java

MediaPipe

爲了實現手勢控制,咱們就須要可以經過電腦攝像頭來識別手部的動做。這裏咱們使用到了MediaPip。android

MediaPipe 是一款由 Google Research 開發並開源的多媒體機器學習模型應用框架。在谷歌,一系列重要產品,如 、Google Lens、ARCore、Google Home 以及 ,都已深度整合了 MediaPipe。git

dupdvyvdex

做爲一款跨平臺框架,MediaPipe 不只能夠被部署在服務器端,更能夠在多個移動端 (安卓和蘋果 iOS)和嵌入式平臺(Google Coral 和樹莓派)中做爲設備端機器學習推理 (On-device Machine Learning Inference)框架npm

image-20210317153803384

Media Pip支持使用js實現人臉網格拓撲,人臉檢測,手部檢測,全身檢測和姿式識別等。canvas

image-20210317154028501

手部識別

爲了實現使用手勢控制飛機,那麼咱們須要識別出手部動做。api

hand_tracking_3d_android_gpu

MediaPip能夠識別人手,而且將手部結構按關節進行拓撲,識別結果爲保存在兩個對象中:數組

  • MULTI_HAND_LANDMARKS:保存了每一個手的關節信息
  • MULTI_HANDEDNESS:保存多個手部信息,好比是左手仍是右手

image-20210317155357354

手勢控制飛機原理

原理很簡單,就是使用攝像頭手別手部,MediaPip能夠識別手部21個關節,咱們可使用食指尖這個點來控制飛機移動。即下標爲8的點:INDEX_FINGER_TIP的座標賦值給飛機。其實就至關於使用食指替代了鼠標。

代碼實現

好,原理清楚以後,上代碼:

建立html文件,引入三個js庫:

<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>

index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="demo.css">

</head>

<body>
  <div class="container">
    <!-- 小飛機 -->
    ![](./img/hero.png)
    <!-- video用來開啓攝像頭 -->
    <video class="input_video"></video>
    <!-- 用來繪製識別內容 -->
    <canvas class="output_canvas" width="1280px" height="720px" ></canvas>
    
    <!-- loading 效果,當攝像頭開啓成功後會隱藏 -->
    <div class="loading">
      <div class="spinner"></div>
      <div class="message">
        Loading
      </div>
    </div>
  </div>

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

建立Hands對象:

const hands = new Hands({
  locateFile: (file) => {
    return `https://cdn.jsdelivr.net/npm/@mediapipe/hands@0.1/${file}`;
  }
});

裏面這個file是爲了識別手須要加載的資源文件。

設置默認選項:

hands.setOptions({
  selfieMode: true, //是否自拍,便是否使用前置攝像頭
  maxNumHands: 1,  //最大識別手部數量
  minDetectionConfidence: 0.5,  //識別精度,這個數值越高,則要求圖像高評分才能被識別 默認 0.5
  minTrackingConfidence: 0.5 //跟蹤速度,數值越高,花費時間越長
});

開啓攝像頭,把攝像頭捕捉的畫面傳給hands對象:

const camera = new Camera(videoElement, {
  onFrame: async () => {
    await hands.send({ image: videoElement });
  },
  width: 1280,
  height: 720
});
camera.start();

獲取識別結果:

function onResults(results) {
  // 隱藏loading動畫
  document.body.classList.add('loaded');

  // Draw the overlays.
  canvasCtx.save();
  // 清空畫布
  canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
  // 繪製攝像頭捕捉畫面
  canvasCtx.drawImage(
    results.image, 0, 0, canvasElement.width, canvasElement.height);

    // 識別結果保存在multiHandLandmarks和multiHandedness對象中,若是這兩個對象不爲null,則說明識別成功
  if (results.multiHandLandmarks && results.multiHandedness) {
    
    // 遍歷multiHandLandmarks,得到每一個hand的信息
    for (let index = 0; index < results.multiHandLandmarks.length; index++) {
      const classification = results.multiHandedness[index];
      const isRightHand = classification.label === 'Right';
      // 一個手的關節信息
      const landmarks = results.multiHandLandmarks[index];
      // 下標爲8的關節就是食指,座標值爲寬高的百分比,和畫布寬高相乘就獲得座標
      let x = landmarks[8].x * 1280;
      let y = landmarks[8].y * 720;

      //把手指座標賦值非小飛機
      hero.style.left = x - 50 + 'px';
      hero.style.top = y - 40 + 'px';

      // 繪製手部拓撲圖
      drawConnectors(
        canvasCtx, landmarks, HAND_CONNECTIONS,
        { color: isRightHand ? '#00FF00' : '#FF0000' }),
        drawLandmarks(canvasCtx, landmarks, {
          color: isRightHand ? '#00FF00' : '#FF0000',
          fillColor: isRightHand ? '#FF0000' : '#00FF00',
          radius: (x) => {
            return lerp(x.from.z, -0.15, .1, 10, 1);
          }
        });
    }
  }
  canvasCtx.restore();
}

hands.onResults(onResults);

識別結果保存在multiHandLandmarks和multiHandedness對象中:

image.png

multiHandLandmarks是一個二維數組,每個數組中保存了21個關節座標信息。把食指座標賦值給飛機,就能夠控制飛機移動了。

hand

而後再加一點點細節,咱們的飛機大戰就完成了:

air

好了完成了。

後話

這裏咱們只實現了簡單的跟隨效果。若是再經過計算關節之間的位置關係和運動方向,那麼就能夠識別更復雜的手勢,好比實現手勢翻頁,點擊按鈕,相似華爲手機的手勢識別。

image.png
不過除此以外,咱們還能夠作得更多,加上面部識別和體態識別,你甚至能夠開發體感遊戲。

image.png
後期我也會嘗試開發一些更有意思的應用,歡迎你們持續關注。

完整代碼

關注公衆號H5Talks ,在公衆號後臺回覆:飛機大戰獲取完整代碼。

若是本文對你有幫助,歡迎關注、點贊、評論。

相關文章
相關標籤/搜索