implement multi-person gestures

pull/50/head
Vladimir Mandic 2020-11-23 23:36:04 -05:00
parent c688254269
commit f336b7772f
20 changed files with 81 additions and 79 deletions

View File

@ -4,9 +4,11 @@ async function drawGesture(result, canvas, ui) {
ctx.font = ui.baseFont; ctx.font = ui.baseFont;
ctx.fillStyle = ui.baseLabel; ctx.fillStyle = ui.baseLabel;
let i = 1; let i = 1;
for (const [key, val] of Object.entries(result)) { for (const gesture in result) {
if (val.length > 0) { const [where, what] = Object.entries(result[gesture]);
const label = `${key}: ${val.join(', ')}`; if ((what.length > 1) && (what[1].length > 0)) {
const person = where[1] > 0 ? `#${where[1]}` : '';
const label = `${where[0]} ${person}: ${what[1]}`;
ctx.fillStyle = 'black'; ctx.fillStyle = 'black';
ctx.fillText(label, 8, 2 + (i * ui.baseLineHeight)); ctx.fillText(label, 8, 2 + (i * ui.baseLineHeight));
ctx.fillStyle = ui.baseLabel; ctx.fillStyle = ui.baseLabel;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@
] ]
}, },
"demo/draw.js": { "demo/draw.js": {
"bytes": 10436, "bytes": 10568,
"imports": [] "imports": []
}, },
"demo/gl-bench.js": { "demo/gl-bench.js": {
@ -30,7 +30,7 @@
"imports": [] "imports": []
}, },
"dist/human.esm.js": { "dist/human.esm.js": {
"bytes": 1784266, "bytes": 1784464,
"imports": [] "imports": []
} }
}, },
@ -38,17 +38,17 @@
"dist/demo-browser-index.js.map": { "dist/demo-browser-index.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 2678448 "bytes": 2679237
}, },
"dist/demo-browser-index.js": { "dist/demo-browser-index.js": {
"imports": [], "imports": [],
"exports": [], "exports": [],
"inputs": { "inputs": {
"dist/human.esm.js": { "dist/human.esm.js": {
"bytesInOutput": 1777021 "bytesInOutput": 1777219
}, },
"demo/draw.js": { "demo/draw.js": {
"bytesInOutput": 7668 "bytesInOutput": 7773
}, },
"demo/menu.js": { "demo/menu.js": {
"bytesInOutput": 11838 "bytesInOutput": 11838
@ -60,7 +60,7 @@
"bytesInOutput": 19407 "bytesInOutput": 19407
} }
}, },
"bytes": 1830756 "bytes": 1831059
} }
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/human.esm.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12
dist/human.esm.json vendored
View File

@ -228,7 +228,7 @@
] ]
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytes": 2981, "bytes": 3260,
"imports": [] "imports": []
}, },
"src/hand/anchors.js": { "src/hand/anchors.js": {
@ -290,7 +290,7 @@
"imports": [] "imports": []
}, },
"src/human.js": { "src/human.js": {
"bytes": 16057, "bytes": 16046,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js" "path": "dist/tfjs.esm.js"
@ -357,7 +357,7 @@
"dist/human.esm.js.map": { "dist/human.esm.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 2583057 "bytes": 2583632
}, },
"dist/human.esm.js": { "dist/human.esm.js": {
"imports": [], "imports": [],
@ -444,7 +444,7 @@
"bytesInOutput": 1929 "bytesInOutput": 1929
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytesInOutput": 2255 "bytesInOutput": 2459
}, },
"src/imagefx.js": { "src/imagefx.js": {
"bytesInOutput": 13638 "bytesInOutput": 13638
@ -456,7 +456,7 @@
"bytesInOutput": 1529574 "bytesInOutput": 1529574
}, },
"src/human.js": { "src/human.js": {
"bytesInOutput": 10604 "bytesInOutput": 10598
}, },
"src/hand/box.js": { "src/hand/box.js": {
"bytesInOutput": 1880 "bytesInOutput": 1880
@ -471,7 +471,7 @@
"bytesInOutput": 22 "bytesInOutput": 22
} }
}, },
"bytes": 1784266 "bytes": 1784464
} }
} }
} }

4
dist/human.js vendored

File diff suppressed because one or more lines are too long

4
dist/human.js.map vendored

File diff suppressed because one or more lines are too long

12
dist/human.json vendored
View File

@ -228,7 +228,7 @@
] ]
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytes": 2981, "bytes": 3260,
"imports": [] "imports": []
}, },
"src/hand/anchors.js": { "src/hand/anchors.js": {
@ -290,7 +290,7 @@
"imports": [] "imports": []
}, },
"src/human.js": { "src/human.js": {
"bytes": 16057, "bytes": 16046,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js" "path": "dist/tfjs.esm.js"
@ -357,7 +357,7 @@
"dist/human.js.map": { "dist/human.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 2550003 "bytes": 2550600
}, },
"dist/human.js": { "dist/human.js": {
"imports": [], "imports": [],
@ -442,7 +442,7 @@
"bytesInOutput": 1910 "bytesInOutput": 1910
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytesInOutput": 2235 "bytesInOutput": 2459
}, },
"src/imagefx.js": { "src/imagefx.js": {
"bytesInOutput": 13638 "bytesInOutput": 13638
@ -451,7 +451,7 @@
"bytesInOutput": 4044 "bytesInOutput": 4044
}, },
"src/human.js": { "src/human.js": {
"bytesInOutput": 10668 "bytesInOutput": 10662
}, },
"dist/tfjs.esm.js": { "dist/tfjs.esm.js": {
"bytesInOutput": 1529065 "bytesInOutput": 1529065
@ -469,7 +469,7 @@
"bytesInOutput": 22 "bytesInOutput": 22
} }
}, },
"bytes": 1783568 "bytes": 1783786
} }
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/human.node.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12
dist/human.node.json vendored
View File

@ -228,7 +228,7 @@
] ]
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytes": 2981, "bytes": 3260,
"imports": [] "imports": []
}, },
"src/hand/anchors.js": { "src/hand/anchors.js": {
@ -290,7 +290,7 @@
"imports": [] "imports": []
}, },
"src/human.js": { "src/human.js": {
"bytes": 16057, "bytes": 16046,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js" "path": "dist/tfjs.esm.js"
@ -357,7 +357,7 @@
"dist/human.node-gpu.js.map": { "dist/human.node-gpu.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 686025 "bytes": 686589
}, },
"dist/human.node-gpu.js": { "dist/human.node-gpu.js": {
"imports": [], "imports": [],
@ -445,7 +445,7 @@
"bytesInOutput": 1973 "bytesInOutput": 1973
}, },
"src/gesture/gesture.js": { "src/gesture/gesture.js": {
"bytesInOutput": 2259 "bytesInOutput": 2463
}, },
"src/imagefx.js": { "src/imagefx.js": {
"bytesInOutput": 13620 "bytesInOutput": 13620
@ -454,7 +454,7 @@
"bytesInOutput": 4107 "bytesInOutput": 4107
}, },
"src/human.js": { "src/human.js": {
"bytesInOutput": 10692 "bytesInOutput": 10686
}, },
"src/hand/box.js": { "src/hand/box.js": {
"bytesInOutput": 1917 "bytesInOutput": 1917
@ -469,7 +469,7 @@
"bytesInOutput": 21 "bytesInOutput": 21
} }
}, },
"bytes": 250245 "bytes": 250443
} }
} }
} }

View File

@ -1,19 +1,19 @@
exports.body = (res) => { exports.body = (res) => {
if (!res) return []; if (!res) return [];
const gestures = []; const gestures = [];
for (const pose of res) { for (const i in res) {
// raising hands // raising hands
const leftWrist = pose.keypoints.find((a) => (a.part === 'leftWrist')); const leftWrist = res[i].keypoints.find((a) => (a.part === 'leftWrist'));
const rightWrist = pose.keypoints.find((a) => (a.part === 'rightWrist')); const rightWrist = res[i].keypoints.find((a) => (a.part === 'rightWrist'));
const nose = pose.keypoints.find((a) => (a.part === 'nose')); const nose = res[i].keypoints.find((a) => (a.part === 'nose'));
if (nose && leftWrist && rightWrist && (leftWrist.position.y < nose.position.y) && (rightWrist.position.y < nose.position.y)) gestures.push('i give up'); if (nose && leftWrist && rightWrist && (leftWrist.position.y < nose.position.y) && (rightWrist.position.y < nose.position.y)) gestures.push({ body: i, gesture: 'i give up' });
else if (nose && leftWrist && (leftWrist.position.y < nose.position.y)) gestures.push('raise left hand'); else if (nose && leftWrist && (leftWrist.position.y < nose.position.y)) gestures.push({ body: i, gesture: 'raise left hand' });
else if (nose && rightWrist && (rightWrist.position.y < nose.position.y)) gestures.push('raise right hand'); else if (nose && rightWrist && (rightWrist.position.y < nose.position.y)) gestures.push({ body: i, gesture: 'raise right hand' });
// leaning // leaning
const leftShoulder = pose.keypoints.find((a) => (a.part === 'leftShoulder')); const leftShoulder = res[i].keypoints.find((a) => (a.part === 'leftShoulder'));
const rightShoulder = pose.keypoints.find((a) => (a.part === 'rightShoulder')); const rightShoulder = res[i].keypoints.find((a) => (a.part === 'rightShoulder'));
if (leftShoulder && rightShoulder) gestures.push(`leaning ${(leftShoulder.position.y > rightShoulder.position.y) ? 'left' : 'right'}`); if (leftShoulder && rightShoulder) gestures.push({ body: i, gesture: `leaning ${(leftShoulder.position.y > rightShoulder.position.y) ? 'left' : 'right'}` });
} }
return gestures; return gestures;
}; };
@ -21,19 +21,19 @@ exports.body = (res) => {
exports.face = (res) => { exports.face = (res) => {
if (!res) return []; if (!res) return [];
const gestures = []; const gestures = [];
for (const face of res) { for (const i in res) {
if (face.mesh && face.mesh.length > 0) { if (res[i].mesh && res[i].mesh.length > 0) {
const eyeFacing = face.mesh[35][2] - face.mesh[263][2]; const eyeFacing = res[i].mesh[35][2] - res[i].mesh[263][2];
if (Math.abs(eyeFacing) < 10) gestures.push('facing camera'); if (Math.abs(eyeFacing) < 10) gestures.push({ face: i, gesture: 'facing camera' });
else gestures.push(`facing ${eyeFacing < 0 ? 'right' : 'left'}`); else gestures.push({ face: i, gesture: `facing ${eyeFacing < 0 ? 'right' : 'left'}` });
const openLeft = Math.abs(face.mesh[374][1] - face.mesh[386][1]) / Math.abs(face.mesh[443][1] - face.mesh[450][1]); // center of eye inner lid y coord div center of wider eye border y coord const openLeft = Math.abs(res[i].mesh[374][1] - res[i].mesh[386][1]) / Math.abs(res[i].mesh[443][1] - res[i].mesh[450][1]); // center of eye inner lid y coord div center of wider eye border y coord
if (openLeft < 0.2) gestures.push('blink left eye'); if (openLeft < 0.2) gestures.push({ face: i, gesture: 'blink left eye' });
const openRight = Math.abs(face.mesh[145][1] - face.mesh[159][1]) / Math.abs(face.mesh[223][1] - face.mesh[230][1]); // center of eye inner lid y coord div center of wider eye border y coord const openRight = Math.abs(res[i].mesh[145][1] - res[i].mesh[159][1]) / Math.abs(res[i].mesh[223][1] - res[i].mesh[230][1]); // center of eye inner lid y coord div center of wider eye border y coord
if (openRight < 0.2) gestures.push('blink right eye'); if (openRight < 0.2) gestures.push({ face: i, gesture: 'blink right eye' });
const mouthOpen = Math.min(100, 500 * Math.abs(face.mesh[13][1] - face.mesh[14][1]) / Math.abs(face.mesh[10][1] - face.mesh[152][1])); const mouthOpen = Math.min(100, 500 * Math.abs(res[i].mesh[13][1] - res[i].mesh[14][1]) / Math.abs(res[i].mesh[10][1] - res[i].mesh[152][1]));
if (mouthOpen > 10) gestures.push(`mouth ${Math.trunc(mouthOpen)}% open`); if (mouthOpen > 10) gestures.push({ face: i, gesture: `mouth ${Math.trunc(mouthOpen)}% open` });
const chinDepth = face.mesh[152][2]; const chinDepth = res[i].mesh[152][2];
if (Math.abs(chinDepth) > 10) gestures.push(`head ${chinDepth < 0 ? 'up' : 'down'}`); if (Math.abs(chinDepth) > 10) gestures.push({ face: i, gesture: `head ${chinDepth < 0 ? 'up' : 'down'}` });
} }
} }
return gestures; return gestures;
@ -42,15 +42,15 @@ exports.face = (res) => {
exports.hand = (res) => { exports.hand = (res) => {
if (!res) return []; if (!res) return [];
const gestures = []; const gestures = [];
for (const hand of res) { for (const i in res) {
const fingers = []; const fingers = [];
for (const [finger, pos] of Object.entries(hand['annotations'])) { for (const [finger, pos] of Object.entries(res[i]['annotations'])) {
if (finger !== 'palmBase') fingers.push({ name: finger.toLowerCase(), position: pos[0] }); // get tip of each finger if (finger !== 'palmBase') fingers.push({ name: finger.toLowerCase(), position: pos[0] }); // get tip of each finger
} }
if (fingers && fingers.length > 0) { if (fingers && fingers.length > 0) {
const closest = fingers.reduce((best, a) => (best.position[2] < a.position[2] ? best : a)); const closest = fingers.reduce((best, a) => (best.position[2] < a.position[2] ? best : a));
const highest = fingers.reduce((best, a) => (best.position[1] < a.position[1] ? best : a)); const highest = fingers.reduce((best, a) => (best.position[1] < a.position[1] ? best : a));
gestures.push(`${closest.name} forward ${highest.name} up`); gestures.push({ hand: i, gesture: `${closest.name} forward ${highest.name} up` });
} }
} }
return gestures; return gestures;

View File

@ -408,7 +408,7 @@ class Human {
let gestureRes = []; let gestureRes = [];
if (this.config.gesture.enabled) { if (this.config.gesture.enabled) {
timeStamp = now(); timeStamp = now();
gestureRes = { face: gesture.face(faceRes), body: gesture.body(poseRes), hand: gesture.hand(handRes) }; gestureRes = [...gesture.face(faceRes), ...gesture.body(poseRes), ...gesture.hand(handRes)];
if (!this.config.async) this.perf.gesture = Math.trunc(now() - timeStamp); if (!this.config.async) this.perf.gesture = Math.trunc(now() - timeStamp);
else if (this.perf.gesture) delete this.perf.gesture; else if (this.perf.gesture) delete this.perf.gesture;
} }

2
wiki

@ -1 +1 @@
Subproject commit 333457d935d89f2559342b31907d04d091520604 Subproject commit bcac4981f7df29e367259caf6b3b73e5ecde6519