npm install kinect2
var Kinect2 = require('../../lib/kinect2'), express = require('express'), app = express(), server = require('http').createServer(app), io = require('socket.io').listen(server); var kinect = new Kinect2(); // 打開kinect if(kinect.open()) { // 監聽8000端口 server.listen(8000); // 指定請求指向根目錄 app.get('/', function(req, res) { res.sendFile(__dirname + '/public/index.html'); }); // 將骨骼數據發送給瀏覽器端 kinect.on('bodyFrame', function(bodyFrame){ io.sockets.emit('bodyFrame', bodyFrame); }); // 開始讀取骨骼數據 kinect.openBodyReader(); }
二、瀏覽器端
瀏覽器端獲取骨骼數據,並用canvas描繪出來,關鍵代碼以下:javascript
var socket = io.connect('/'); var ctx = canvas.getContext('2d'); socket.on('bodyFrame', function(bodyFrame){ ctx.clearRect(0, 0, canvas.width, canvas.height); var index = 0; // 遍歷全部骨骼數據 bodyFrame.bodies.forEach(function(body){ if(body.tracked) { for(var jointType in body.joints) { var joint = body.joints[jointType]; ctx.fillStyle = colors[index]; // 若是骨骼節點爲脊椎中點 if(jointType == 1) { ctx.fillStyle = colors[2]; } ctx.fillRect(joint.depthX * 512, joint.depthY * 424, 10, 10); } // 識別左右手手勢 updateHandState(body.leftHandState, body.joints[7]); updateHandState(body.rightHandState, body.joints[11]); index++; } }); });
很簡單的幾行代碼,咱們便完成了玩家骨骼捕獲,有必定 javascript基礎的同窗應該很容易能看明白,但不明白的是咱們能獲取哪些數據?如何獲取?骨骼節點名稱分別是什麼?而node-kienct2並無文檔告訴咱們這些。html
kinect.on('bodyFrame', function(bodyFrame){}); //還有哪些數據類型呢?
bodyFrame | 骨骼數據 |
infraredFrame | 紅外數據 |
longExposureInfraredFrame | 相似infraredFrame,貌似精度更高,優化後的數據 |
rawDepthFrame | 未經處理的景深數據 |
depthFrame | 景深數據 |
colorFrame | 彩色圖像 |
multiSourceFrame | 全部數據 |
audio | 音頻數據,未測試 |
body.joints[11] // joints包括哪些呢?
節點類型 | JointType | 節點名稱 |
0 | spineBase | 脊椎基部 |
1 | spineMid | 脊椎中部 |
2 | neck | 頸部 |
3 | head | 頭部 |
4 | shoulderLeft | 左肩 |
5 | elbowLeft | 左肘 |
6 | wristLeft | 左腕 |
7 | handLeft | 左手掌 |
8 | shoulderRight | 右肩 |
9 | elbowRight | 右肘 |
10 | wristRight | 右腕 |
11 | handRight | 右手掌 |
12 | hipLeft | 左屁 |
13 | kneeLeft | 左膝 |
14 | ankleLeft | 左踝 |
15 | footLeft | 左腳 |
16 | hipRight | 右屁 |
17 | kneeRight | 右膝 |
18 | ankleRight | 右踝 |
19 | footRight | 右腳 |
20 | spineShoulder | 頸下脊椎 |
21 | handTipLeft | 左手指(食中無小) |
22 | thumbLeft | 左拇指 |
23 | handTipRight | 右手指 |
24 | thumbRight | 右拇指 |
0 | unknown | 不能識別 |
1 | notTracked | 未能檢測到 |
2 | open | 手掌 |
3 | closed | 握拳 |
4 | lasso | 剪刀手,併合並中食指 |
on | 監聽數據 |
open | 打開Kinect |
close | 關閉 |
openBodyReader | 讀取骨骼數據 |
open**Reader | 相似如上方法,讀取其它類型數據 |
1.一、經過手勢觸發開始遊戲前端 |
1.二、玩家化身四代,左右跑動躲避九尾攻擊java |
1.三、擺出手勢「奧義」,觸發四代大招node |
1.四、用戶掃描二維碼獲取本身現場照片git |
var emitColorFrame = false; io.sockets.on('connection', function (socket){ socket.on('startColorFrame', function(data){ emitColorFrame = true; }); }); kinect.on('multiSourceFrame', function(frame){ // 發送玩家骨骼數據 io.sockets.emit('bodyFrame', frame.body); // 玩家拍照 if(emitColorFrame) { var compression = 1; var origWidth = 1920; var origHeight = 1080; var origLength = 4 * origWidth * origHeight; var compressedWidth = origWidth / compression; var compressedHeight = origHeight / compression; var resizedLength = 4 * compressedWidth * compressedHeight; var resizedBuffer = new Buffer(resizedLength); // ... // 照片數據過大,須要壓縮提升傳輸性能 zlib.deflate(resizedBuffer, function(err, result){ if(!err) { var buffer = result.toString('base64'); io.sockets.emit('colorFrame', buffer); } }); emitColorFrame = false; } }); kinect.openMultiSourceReader({ frameTypes: Kinect2.FrameType.body | Kinect2.FrameType.color });
三、客戶端
客戶端業務邏輯較複雜,咱們提取關鍵步驟進行講解。
3.一、用戶拍照時,因爲處理的數據比較大,爲防止頁面出現卡頓,咱們須要使用web workergithub
(function(){ importScripts('pako.inflate.min.js'); var imageData; function init() { addEventListener('message', function (event) { switch (event.data.message) { case "setImageData": imageData = event.data.imageData; break; case "processImageData": processImageData(event.data.imageBuffer); break; } }); } function processImageData(compressedData) { var imageBuffer = pako.inflate(atob(compressedData)); var pixelArray = imageData.data; var newPixelData = new Uint8Array(imageBuffer); var imageDataSize = imageData.data.length; for (var i = 0; i < imageDataSize; i++) { imageData.data[i] = newPixelData[i]; } for(var x = 0; x < 1920; x++) { for(var y = 0; y < 1080; y++) { var idx = (x + y * 1920) * 4; var r = imageData.data[idx + 0]; var g = imageData.data[idx + 1]; var b = imageData.data[idx + 2]; } } self.postMessage({ "message": "imageReady", "imageData": imageData }); } init(); })();
3.二、接投影儀後,若是渲染面積比較大,會出現白屏,須要關閉瀏覽器硬件加速。web
3.三、現場光線較暗,其它玩家干擾,在追蹤玩家運動軌跡的過程當中,可能會出現抖動的狀況,咱們須要去除干擾數據。(當忽然出現很大位移時,須要將數據移除)express
var tracks = this.tracks; var len = tracks.length; // 數據過濾 if(tracks[len-1] !== window.undefined) { if(Math.abs(n - tracks[len-1]) > 0.2) { return; } } this.tracks.push(n);
3.四、當玩家站立,只是左右少許晃動時,咱們認爲玩家是站立狀態。npm
// 保留5個數據 if(this.tracks.length > 5) { this.tracks.shift(); } else { return; } // 位移總量 var dis = 0; for(var i = 1; i < this.tracks.length; i++) { dis += this.tracks[i] - this.tracks[i-1]; } if(Math.abs(dis) < 0.01) { this.stand(); } else { if(this.tracks[4] > this.tracks[3]) { this.turnRight(); } else { this.turnLeft(); } this.run(); }