mirror of https://github.com/vladmandic/human
add curve draw output
parent
988f7a7cbd
commit
b9dddcdd0a
|
@ -71,7 +71,7 @@ export default {
|
|||
// 'blazeface-back' is blazeface model optimized for smaller and/or distanct faces
|
||||
// 'faceboxes' is alternative model to 'blazeface'
|
||||
inputSize: 256, // fixed value: 128 for front and 256 for 'back'
|
||||
rotation: false, // use best-guess rotated face image or just box with rotation as-is
|
||||
rotation: true, // use best-guess rotated face image or just box with rotation as-is
|
||||
// false means higher performance, but incorrect mesh mapping if face angle is above 20 degrees
|
||||
maxFaces: 10, // maximum number of faces detected in the input
|
||||
// should be set to the minimum number for performance
|
||||
|
|
|
@ -2,7 +2,7 @@ import Human from '../dist/human.esm.js'; // equivalent of @vladmandic/human
|
|||
import Menu from './menu.js';
|
||||
import GLBench from './gl-bench.js';
|
||||
|
||||
const userConfig = { }; // add any user configuration overrides
|
||||
const userConfig = { backend: 'wasm' }; // add any user configuration overrides
|
||||
|
||||
/*
|
||||
const userConfig = {
|
||||
|
@ -40,11 +40,11 @@ const ui = {
|
|||
drawFPS: [], // internal, holds fps values for draw performance
|
||||
buffered: false, // experimental, should output be buffered between frames
|
||||
drawWarmup: false, // debug only, should warmup image processing be displayed on startup
|
||||
drawThread: null, // perform draw operations in a separate thread
|
||||
detectThread: null, // perform detect operations in a separate thread
|
||||
drawThread: null, // internl, perform draw operations in a separate thread
|
||||
detectThread: null, // internl, perform detect operations in a separate thread
|
||||
framesDraw: 0, // internal, statistics on frames drawn
|
||||
framesDetect: 0, // internal, statistics on frames detected
|
||||
bench: false, // show gl fps benchmark window
|
||||
bench: true, // show gl fps benchmark window
|
||||
lastFrame: 0, // time of last frame processing
|
||||
};
|
||||
|
||||
|
@ -110,7 +110,11 @@ async function drawResults(input) {
|
|||
await menu.process.updateChart('FPS', ui.detectFPS);
|
||||
|
||||
// get updated canvas
|
||||
if (ui.buffered || !result.canvas) result.canvas = await human.image(input).canvas;
|
||||
if (ui.buffered || !result.canvas) {
|
||||
const image = await human.image(input);
|
||||
result.canvas = image.canvas;
|
||||
human.tf.dispose(image.tensor);
|
||||
}
|
||||
|
||||
// draw image from video
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
@ -421,10 +425,10 @@ function setupMenu() {
|
|||
menu.display.addHTML('<hr style="border-style: inset; border-color: dimgray">');
|
||||
menu.display.addBool('use 3D depth', human.draw.options, 'useDepth');
|
||||
menu.display.addBool('print labels', human.draw.options, 'drawLabels');
|
||||
menu.display.addBool('draw points', human.draw.options, 'drawPoints');
|
||||
menu.display.addBool('draw boxes', human.draw.options, 'drawBoxes');
|
||||
menu.display.addBool('draw polygons', human.draw.options, 'drawPolygons');
|
||||
menu.display.addBool('Fill Polygons', human.draw.options, 'fillPolygons');
|
||||
menu.display.addBool('draw points', human.draw.options, 'drawPoints');
|
||||
menu.display.addBool('fill polygons', human.draw.options, 'fillPolygons');
|
||||
|
||||
menu.image = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[1] });
|
||||
menu.image.addBool('enabled', human.config.filter, 'enabled', (val) => human.config.filter.enabled = val);
|
||||
|
@ -449,8 +453,8 @@ function setupMenu() {
|
|||
menu.process = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[2] });
|
||||
menu.process.addList('backend', ['cpu', 'webgl', 'wasm', 'humangl'], human.config.backend, (val) => human.config.backend = val);
|
||||
menu.process.addBool('async operations', human.config, 'async', (val) => human.config.async = val);
|
||||
menu.process.addBool('enable profiler', human.config, 'profile', (val) => human.config.profile = val);
|
||||
menu.process.addBool('memory shield', human.config, 'deallocate', (val) => human.config.deallocate = val);
|
||||
// menu.process.addBool('enable profiler', human.config, 'profile', (val) => human.config.profile = val);
|
||||
// menu.process.addBool('memory shield', human.config, 'deallocate', (val) => human.config.deallocate = val);
|
||||
menu.process.addBool('use web worker', ui, 'useWorker');
|
||||
menu.process.addHTML('<hr style="border-style: inset; border-color: dimgray">');
|
||||
menu.process.addLabel('model parameters');
|
||||
|
@ -526,7 +530,6 @@ async function drawWarmup(res) {
|
|||
|
||||
async function main() {
|
||||
log('Demo starting ...');
|
||||
log('Browser:', navigator?.userAgent);
|
||||
setupMenu();
|
||||
document.getElementById('log').innerText = `Human: version ${human.version}`;
|
||||
if (ui.modelsPreload && !ui.useWorker) {
|
||||
|
|
|
@ -20,7 +20,7 @@ const UISVG = `
|
|||
<svg viewBox="0 0 55 60">
|
||||
<text x="27" y="56" class="gl-fps">00 FPS</text>
|
||||
<text x="30" y="8" class="gl-mem"></text>
|
||||
<rect x="0" y="14" rx="4" ry="4" width="55" height="32"></rect>
|
||||
<rect x="0" y="14" rx="4" ry="4" width="65" height="32"></rect>
|
||||
<polyline class="gl-chart"></polyline>
|
||||
</svg>
|
||||
<svg viewBox="0 0 14 60" class="gl-cpu-svg">
|
||||
|
|
|
@ -18,7 +18,7 @@ let theme = {
|
|||
function createCSS() {
|
||||
if (CSScreated) return;
|
||||
const css = `
|
||||
:root { --rounded: 0.2rem; }
|
||||
:root { --rounded: 0.1rem; }
|
||||
.menu { position: absolute; top: 0rem; right: 0; width: max-content; padding: 0 0.2rem 0 0.2rem; line-height: 1.8rem; z-index: 10;
|
||||
box-shadow: 0 0 8px dimgrey; background: ${theme.background}; border-radius: var(--rounded); border-color: black; border-style: solid; border-width: thin; }
|
||||
|
||||
|
|
26
demo/node.js
26
demo/node.js
|
@ -40,8 +40,6 @@ async function init() {
|
|||
// pre-load models
|
||||
log.info('Human:', human.version);
|
||||
log.info('Active Configuration', human.config);
|
||||
log.info('TFJS Version:', human.tf.version_core, 'Backend:', tf.getBackend());
|
||||
log.info('TFJS Flags:', human.tf.env().features);
|
||||
await human.load();
|
||||
const loaded = Object.keys(human.models).filter((a) => human.models[a]);
|
||||
log.info('Loaded:', loaded);
|
||||
|
@ -63,27 +61,31 @@ async function detect(input) {
|
|||
// dispose image tensor as we no longer need it
|
||||
image.dispose();
|
||||
// print data to console
|
||||
log.data(result);
|
||||
log.data('Face: ', result.face);
|
||||
log.data('Body:', result.body);
|
||||
log.data('Hand:', result.hand);
|
||||
log.data('Gesture:', result.gesture);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
// test with embedded face image
|
||||
// test with embedded full body image
|
||||
let result;
|
||||
|
||||
log.state('Processing embedded warmup image: face');
|
||||
myConfig.warmup = 'face';
|
||||
const resultFace = await human.warmup(myConfig);
|
||||
log.data('Face: ', resultFace.face);
|
||||
result = await human.warmup(myConfig);
|
||||
log.data('Face: ', result.face);
|
||||
|
||||
// test with embedded full body image
|
||||
log.state('Processing embedded warmup image: full');
|
||||
myConfig.warmup = 'full';
|
||||
const resultFull = await human.warmup(myConfig);
|
||||
log.data('Body:', resultFull.body);
|
||||
log.data('Hand:', resultFull.hand);
|
||||
log.data('Gesture:', resultFull.gesture);
|
||||
result = await human.warmup(myConfig);
|
||||
log.data('Body:', result.body);
|
||||
log.data('Hand:', result.hand);
|
||||
log.data('Gesture:', result.gesture);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
log.info('NodeJS:', process.version);
|
||||
log.header();
|
||||
log.info('Current folder:', process.env.PWD);
|
||||
await init();
|
||||
if (process.argv.length !== 3) {
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
"seedrandom": "^3.0.5",
|
||||
"simple-git": "^2.36.0",
|
||||
"tslib": "^2.1.0",
|
||||
"typescript": "^4.3.0-dev.20210305"
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught --no-deprecation src/node.js",
|
||||
|
|
265
src/draw.ts
265
src/draw.ts
|
@ -9,13 +9,14 @@ export const options = {
|
|||
lineHeight: 20,
|
||||
lineWidth: 6,
|
||||
pointSize: 2,
|
||||
roundRect: 8,
|
||||
roundRect: 28,
|
||||
drawPoints: false,
|
||||
drawLabels: true,
|
||||
drawBoxes: true,
|
||||
drawPolygons: true,
|
||||
fillPolygons: false,
|
||||
useDepth: true,
|
||||
useCurves: true,
|
||||
bufferedOutput: false,
|
||||
};
|
||||
|
||||
|
@ -27,9 +28,13 @@ function point(ctx, x, y) {
|
|||
}
|
||||
|
||||
function rect(ctx, x, y, width, height) {
|
||||
if (options.roundRect && options.roundRect > 0) {
|
||||
ctx.beginPath();
|
||||
if (options.useCurves) {
|
||||
const cx = (x + x + width) / 2;
|
||||
const cy = (y + y + height) / 2;
|
||||
ctx.ellipse(cx, cy, width / 2, height / 2, 0, 0, 2 * Math.PI);
|
||||
} else {
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + options.roundRect, y);
|
||||
ctx.lineTo(x + width - options.roundRect, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + options.roundRect);
|
||||
|
@ -40,122 +45,40 @@ function rect(ctx, x, y, width, height) {
|
|||
ctx.lineTo(x, y + options.roundRect);
|
||||
ctx.quadraticCurveTo(x, y, x + options.roundRect, y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
} else {
|
||||
rect(ctx, x, y, width, height);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
function lines(ctx, points) {
|
||||
function lines(ctx, points: number[] = []) {
|
||||
if (points === undefined || points.length === 0) return;
|
||||
ctx.beginPath();
|
||||
const path = new Path2D();
|
||||
path.moveTo(points[0][0], points[0][1]);
|
||||
for (const pt of points) {
|
||||
path.lineTo(pt[0], parseInt(pt[1]));
|
||||
}
|
||||
ctx.stroke(path);
|
||||
ctx.moveTo(points[0][0], points[0][1]);
|
||||
for (const pt of points) ctx.lineTo(pt[0], parseInt(pt[1]));
|
||||
ctx.stroke();
|
||||
if (options.fillPolygons) {
|
||||
ctx.closePath();
|
||||
ctx.fill(path);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
function curve(ctx, points = []) {
|
||||
if (points.length < 2) return;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
ctx.beginPath();
|
||||
function curves(ctx, points: number[] = []) {
|
||||
if (points === undefined || points.length === 0) return;
|
||||
if (!options.useCurves || points.length <= 2) {
|
||||
lines(ctx, points);
|
||||
return;
|
||||
}
|
||||
ctx.moveTo(points[0][0], points[0][1]);
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
const xMid = (points[i][0] + points[i + 1][0]) / 2;
|
||||
const yMid = (points[i][1] + points[i + 1][1]) / 2;
|
||||
const cpX1 = (xMid + points[i][0]) / 2;
|
||||
const cpX2 = (xMid + points[i + 1][1]) / 2;
|
||||
ctx.quadraticCurveTo(cpX1, points[i][1], xMid, yMid);
|
||||
ctx.quadraticCurveTo(cpX2, points[i + 1][1], points[i + 1][0], points[i + 1][0]);
|
||||
for (let i = 0; i < points.length - 2; i++) {
|
||||
const xc = (points[i][0] + points[i + 1][0]) / 2;
|
||||
const yc = (points[i][1] + points[i + 1][1]) / 2;
|
||||
ctx.quadraticCurveTo(points[i][0], points[i][1], xc, yc);
|
||||
}
|
||||
ctx.strokeStyle = options.color;
|
||||
ctx.quadraticCurveTo(points[points.length - 2][0], points[points.length - 2][1], points[points.length - 1][0], points[points.length - 1][1]);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
function bezier(ctx, points) {
|
||||
const tension = 0; // tension at 0 will be straight line
|
||||
const factor = 1; // factor is normally 1, but changing the value can control the smoothness too
|
||||
if (points.length < 2) return;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
ctx.strokeStyle = options.color;
|
||||
ctx.fillStyle = options.color;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0][0], points[0][1]);
|
||||
let dx1 = 0;
|
||||
let dy1 = 0;
|
||||
let preP = points[0];
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
const curP = points[i];
|
||||
const nexP = points[i + 1];
|
||||
const m = nexP ? (nexP[1] - preP[1]) / (nexP[0] - preP[0]) : 0;
|
||||
const dx2 = nexP ? (nexP[0] - curP[0]) * -factor : 0;
|
||||
const dy2 = nexP ? dx2 * m * tension : 0;
|
||||
ctx.bezierCurveTo(preP[0] - dx1, preP[1] - dy1, curP[0] + dx2, curP[1] + dy2, curP[0], curP[1]);
|
||||
dx1 = dx2;
|
||||
dy1 = dy2;
|
||||
preP = curP;
|
||||
if (options.fillPolygons) {
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
function spline(ctx, points) {
|
||||
const tension = 0.8;
|
||||
if (points.length < 2) return;
|
||||
const va = (arr, i, j) => [arr[2 * j] - arr[2 * i], arr[2 * j + 1] - arr[2 * i + 1]];
|
||||
const distance = (arr, i, j) => Math.sqrt(((arr[2 * i] - arr[2 * j]) ** 2) + ((arr[2 * i + 1] - arr[2 * j + 1]) ** 2));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
||||
const ctlpts = (x1, y1, x2, y2, x3, y3) => {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const v = va(arguments, 0, 2);
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const d01 = distance(arguments, 0, 1);
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const d12 = distance(arguments, 1, 2);
|
||||
const d012 = d01 + d12;
|
||||
return [
|
||||
x2 - v[0] * tension * d01 / d012, y2 - v[1] * tension * d01 / d012,
|
||||
x2 + v[0] * tension * d12 / d012, y2 + v[1] * tension * d12 / d012,
|
||||
];
|
||||
};
|
||||
const pts: any[] = [];
|
||||
for (const pt of points) {
|
||||
pts.push(pt[0]);
|
||||
pts.push(pt[1]);
|
||||
}
|
||||
let cps = [];
|
||||
for (let i = 0; i < pts.length - 2; i += 1) {
|
||||
// @ts-ignore
|
||||
cps = cps.concat(ctlpts(pts[2 * i + 0], pts[2 * i + 1], pts[2 * i + 2], pts[2 * i + 3], pts[2 * i + 4], pts[2 * i + 5]));
|
||||
}
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
ctx.strokeStyle = options.color;
|
||||
if (points.length === 2) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pts[0], pts[1]);
|
||||
ctx.lineTo(pts[2], pts[3]);
|
||||
} else {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pts[0], pts[1]);
|
||||
// first segment is a quadratic
|
||||
ctx.quadraticCurveTo(cps[0], cps[1], pts[2], pts[3]);
|
||||
// for all middle points, connect with bezier
|
||||
let i;
|
||||
for (i = 2; i < ((pts.length / 2) - 1); i += 1) {
|
||||
ctx.bezierCurveTo(cps[(2 * (i - 1) - 1) * 2], cps[(2 * (i - 1) - 1) * 2 + 1], cps[(2 * (i - 1)) * 2], cps[(2 * (i - 1)) * 2 + 1], pts[i * 2], pts[i * 2 + 1]);
|
||||
}
|
||||
// last segment is a quadratic
|
||||
ctx.quadraticCurveTo(cps[(2 * (i - 1) - 1) * 2], cps[(2 * (i - 1) - 1) * 2 + 1], pts[i * 2], pts[i * 2 + 1]);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
export async function gesture(inCanvas, result) {
|
||||
|
@ -193,7 +116,9 @@ export async function face(inCanvas, result) {
|
|||
ctx.font = options.font;
|
||||
ctx.strokeStyle = options.color;
|
||||
ctx.fillStyle = options.color;
|
||||
if (options.drawBoxes) rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3]);
|
||||
if (options.drawBoxes) {
|
||||
rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3]);
|
||||
}
|
||||
// silly hack since fillText does not suport new line
|
||||
const labels:string[] = [];
|
||||
labels.push(`face confidence: ${Math.trunc(100 * f.confidence)}%`);
|
||||
|
@ -299,82 +224,70 @@ export async function body(inCanvas, result) {
|
|||
}
|
||||
}
|
||||
if (options.drawPolygons) {
|
||||
const path = new Path2D();
|
||||
let root;
|
||||
let part;
|
||||
const points: any[] = [];
|
||||
// torso
|
||||
root = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (root && root.score > config.body.scoreThreshold) {
|
||||
const points: any[] = [];
|
||||
points.push([root.position.x, root.position.y, 'leftShoulder']);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
lines(ctx, points);
|
||||
}
|
||||
points.length = 0;
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
if (points.length === 5) lines(ctx, points); // only draw if we have complete torso
|
||||
// leg left
|
||||
root = result[i].keypoints.find((a) => a.part === 'leftHip');
|
||||
if (root && root.score > config.body.scoreThreshold) {
|
||||
const points: any[] = [];
|
||||
points.push([root.position.x, root.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftKnee');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftAnkle');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftHeel');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftFoot');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
lines(ctx, points);
|
||||
}
|
||||
points.length = 0;
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftKnee');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftAnkle');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftHeel');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftFoot');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
curves(ctx, points);
|
||||
// leg right
|
||||
root = result[i].keypoints.find((a) => a.part === 'rightHip');
|
||||
if (root && root.score > config.body.scoreThreshold) {
|
||||
const points: any[] = [];
|
||||
points.push([root.position.x, root.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightKnee');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightAnkle');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightHeel');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightFoot');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
lines(ctx, points);
|
||||
}
|
||||
points.length = 0;
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightHip');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightKnee');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightAnkle');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightHeel');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightFoot');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
curves(ctx, points);
|
||||
// arm left
|
||||
root = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (root && root.score > config.body.scoreThreshold) {
|
||||
const points: any[] = [];
|
||||
points.push([root.position.x, root.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftElbow');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftWrist');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftPalm');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
lines(ctx, points);
|
||||
}
|
||||
points.length = 0;
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftElbow');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftWrist');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'leftPalm');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
curves(ctx, points);
|
||||
// arm right
|
||||
root = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
||||
if (root && root.score > config.body.scoreThreshold) {
|
||||
const points: any[] = [];
|
||||
points.push([root.position.x, root.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightElbow');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightWrist');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightPalm');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
lines(ctx, points);
|
||||
}
|
||||
points.length = 0;
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightElbow');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightWrist');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
part = result[i].keypoints.find((a) => a.part === 'rightPalm');
|
||||
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]);
|
||||
curves(ctx, points);
|
||||
// draw all
|
||||
ctx.stroke(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/human.ts
16
src/human.ts
|
@ -1,4 +1,5 @@
|
|||
import { log } from './log';
|
||||
import * as sysinfo from './sysinfo';
|
||||
import * as tf from '../dist/tfjs.esm.js';
|
||||
import * as backend from './tfjs/backend';
|
||||
import * as facemesh from './blazeface/facemesh';
|
||||
|
@ -61,6 +62,7 @@ class Human {
|
|||
emotion: any;
|
||||
body: any;
|
||||
hand: any;
|
||||
sysinfo: any;
|
||||
|
||||
constructor(userConfig = {}) {
|
||||
this.tf = tf;
|
||||
|
@ -95,6 +97,8 @@ class Human {
|
|||
this.emotion = emotion;
|
||||
this.body = this.config.body.modelType.startsWith('posenet') ? posenet : blazepose;
|
||||
this.hand = handpose;
|
||||
// include platform info
|
||||
this.sysinfo = sysinfo.info();
|
||||
}
|
||||
|
||||
profile() {
|
||||
|
@ -139,7 +143,11 @@ class Human {
|
|||
if (userConfig) this.config = mergeDeep(this.config, userConfig);
|
||||
|
||||
if (this.firstRun) {
|
||||
if (this.config.debug) log(`version: ${this.version} TensorFlow/JS version: ${this.tf.version_core}`);
|
||||
if (this.config.debug) log(`version: ${this.version}`);
|
||||
if (this.config.debug) log(`tfjs version: ${this.tf.version_core}`);
|
||||
if (this.config.debug) log('platform:', this.sysinfo.platform);
|
||||
if (this.config.debug) log('agent:', this.sysinfo.agent);
|
||||
|
||||
await this.checkBackend(true);
|
||||
if (this.tf.ENV.flags.IS_BROWSER) {
|
||||
if (this.config.debug) log('configuration:', this.config);
|
||||
|
@ -206,9 +214,11 @@ class Human {
|
|||
if (this.config.debug) log('setting backend:', this.config.backend);
|
||||
|
||||
if (this.config.backend === 'wasm') {
|
||||
if (this.config.debug) log('settings wasm path:', this.config.wasmPath);
|
||||
if (this.config.debug) log('wasm path:', this.config.wasmPath);
|
||||
this.tf.setWasmPaths(this.config.wasmPath);
|
||||
const simd = await this.tf.env().getAsync('WASM_HAS_SIMD_SUPPORT');
|
||||
const mt = await this.tf.env().getAsync('WASM_HAS_MULTITHREAD_SUPPORT');
|
||||
if (this.config.debug) log(`wasm execution: ${simd ? 'SIMD' : 'no SIMD'} ${mt ? 'multithreaded' : 'singlethreaded'}`);
|
||||
if (!simd) log('warning: wasm simd support is not enabled');
|
||||
}
|
||||
|
||||
|
@ -538,7 +548,7 @@ class Human {
|
|||
else res = await this.warmupNode();
|
||||
this.config.videoOptimized = video;
|
||||
const t1 = now();
|
||||
if (this.config.debug) log('Warmup', this.config.warmup, Math.round(t1 - t0), 'ms', res);
|
||||
if (this.config.debug) log('Warmup', this.config.warmup, Math.round(t1 - t0), 'ms');
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
2
wiki
2
wiki
|
@ -1 +1 @@
|
|||
Subproject commit ce4f3e12bd49ee404186c55ab977b4b1612d17f4
|
||||
Subproject commit 562a698daecaecf8580120fc4e1c9b6ac66ba537
|
Loading…
Reference in New Issue