human/demo/typescript/index.ts

105 lines
5.6 KiB
TypeScript
Raw Normal View History

2021-10-27 14:16:06 +02:00
/**
* Human demo for browsers
2021-10-27 15:45:38 +02:00
* @default Human Library
* @summary <https://github.com/vladmandic/human>
* @author <https://github.com/vladmandic>
* @copyright <https://github.com/vladmandic>
* @license MIT
2021-10-27 14:16:06 +02:00
*/
2022-08-21 19:34:51 +02:00
import * as H from '../../dist/human.esm.js'; // equivalent of @vladmandic/Human
2021-10-27 14:16:06 +02:00
2022-08-21 19:34:51 +02:00
const humanConfig: Partial<H.Config> = { // user configuration for human, used to fine-tune behavior
2021-11-18 16:10:06 +01:00
modelBasePath: '../../models',
2022-06-10 14:47:22 +02:00
filter: { enabled: true, equalization: false, flip: false },
2022-04-11 17:45:24 +02:00
face: { enabled: true, detector: { rotation: false }, mesh: { enabled: true }, attention: { enabled: false }, iris: { enabled: true }, description: { enabled: true }, emotion: { enabled: true } },
2021-11-18 16:10:06 +01:00
body: { enabled: true },
hand: { enabled: true },
2022-04-15 13:54:27 +02:00
object: { enabled: false },
2022-09-30 03:28:13 +02:00
segmentation: { enabled: false },
2021-11-18 16:10:06 +01:00
gesture: { enabled: true },
2021-10-27 14:16:06 +02:00
};
2022-08-21 19:34:51 +02:00
const human = new H.Human(humanConfig); // create instance of human with overrides from user configuration
2021-10-27 14:16:06 +02:00
2022-08-21 19:34:51 +02:00
human.env.perfadd = false; // is performance data showing instant or total values
2021-11-05 18:36:53 +01:00
human.draw.options.font = 'small-caps 18px "Lato"'; // set font used to draw labels when using draw methods
human.draw.options.lineHeight = 20;
2022-07-21 18:53:10 +02:00
// human.draw.options.fillPolygons = true;
2021-11-05 16:28:06 +01:00
const dom = { // grab instances of dom objects so we dont have to look them up later
2021-10-27 15:45:38 +02:00
video: document.getElementById('video') as HTMLVideoElement,
canvas: document.getElementById('canvas') as HTMLCanvasElement,
log: document.getElementById('log') as HTMLPreElement,
fps: document.getElementById('status') as HTMLPreElement,
perf: document.getElementById('performance') as HTMLDivElement,
};
2022-08-04 15:15:13 +02:00
const timestamp = { detect: 0, draw: 0, tensors: 0, start: 0 }; // holds information used to calculate performance and possible memory leaks
const fps = { detectFPS: 0, drawFPS: 0, frames: 0, averageMs: 0 }; // holds calculated fps information for both detect and screen refresh
2021-10-27 15:45:38 +02:00
2021-11-05 16:28:06 +01:00
const log = (...msg) => { // helper method to output messages
2021-10-27 15:45:38 +02:00
dom.log.innerText += msg.join(' ') + '\n';
2022-08-21 19:34:51 +02:00
console.log(...msg); // eslint-disable-line no-console
2021-10-27 15:45:38 +02:00
};
2021-11-05 16:28:06 +01:00
const status = (msg) => dom.fps.innerText = msg; // print status element
2022-08-21 21:23:03 +02:00
const perf = (msg) => dom.perf.innerText = 'tensors:' + (human.tf.memory().numTensors as number).toString() + ' | performance: ' + JSON.stringify(msg).replace(/"|{|}/g, '').replace(/,/g, ' | '); // print performance element
2021-10-27 14:16:06 +02:00
2021-11-05 16:28:06 +01:00
async function detectionLoop() { // main detection loop
2021-10-27 15:45:38 +02:00
if (!dom.video.paused) {
2022-08-04 15:15:13 +02:00
if (timestamp.start === 0) timestamp.start = human.now();
// log('profiling data:', await human.profile(dom.video));
2021-11-05 16:28:06 +01:00
await human.detect(dom.video); // actual detection; were not capturing output in a local variable as it can also be reached via human.result
const tensors = human.tf.memory().numTensors; // check current tensor usage for memory leaks
if (tensors - timestamp.tensors !== 0) log('allocated tensors:', tensors - timestamp.tensors); // printed on start and each time there is a tensor leak
timestamp.tensors = tensors;
2022-08-04 15:15:13 +02:00
fps.detectFPS = Math.round(1000 * 1000 / (human.now() - timestamp.detect)) / 1000;
fps.frames++;
fps.averageMs = Math.round(1000 * (human.now() - timestamp.start) / fps.frames) / 1000;
if (fps.frames % 100 === 0 && !dom.video.paused) log('performance', { ...fps, tensors: timestamp.tensors });
2021-10-27 15:45:38 +02:00
}
2022-08-04 15:15:13 +02:00
timestamp.detect = human.now();
2021-11-05 16:28:06 +01:00
requestAnimationFrame(detectionLoop); // start new frame immediately
2021-10-27 14:16:06 +02:00
}
2021-11-05 16:28:06 +01:00
async function drawLoop() { // main screen refresh loop
2021-10-27 15:45:38 +02:00
if (!dom.video.paused) {
2022-08-21 21:23:03 +02:00
const interpolated = human.next(human.result); // smoothen result using last-known results
if (human.config.filter.flip) human.draw.canvas(interpolated.canvas as HTMLCanvasElement, dom.canvas); // draw processed image to screen canvas
else human.draw.canvas(dom.video, dom.canvas); // draw original video to screen canvas // better than using procesed image as this loop happens faster than processing loop
2021-11-05 16:28:06 +01:00
await human.draw.all(dom.canvas, interpolated); // draw labels, boxes, lines, etc.
perf(interpolated.performance); // write performance data
2021-10-27 14:16:06 +02:00
}
2021-11-05 16:28:06 +01:00
const now = human.now();
2022-08-04 15:15:13 +02:00
fps.drawFPS = Math.round(1000 * 1000 / (now - timestamp.draw)) / 1000;
2021-11-05 16:28:06 +01:00
timestamp.draw = now;
2022-08-04 15:15:13 +02:00
status(dom.video.paused ? 'paused' : `fps: ${fps.detectFPS.toFixed(1).padStart(5, ' ')} detect | ${fps.drawFPS.toFixed(1).padStart(5, ' ')} draw`); // write status
2021-11-05 16:28:06 +01:00
setTimeout(drawLoop, 30); // use to slow down refresh from max refresh rate to target of 30 fps
2021-10-27 14:16:06 +02:00
}
2022-09-30 03:28:13 +02:00
async function webCam() {
await human.webcam.start({ element: dom.video, crop: true }); // use human webcam helper methods and associate webcam stream with a dom element
dom.canvas.width = human.webcam.width;
dom.canvas.height = human.webcam.height;
dom.canvas.onclick = async () => { // pause when clicked on screen and resume on next click
if (human.webcam.paused) await human.webcam.play();
else human.webcam.pause();
};
}
2021-11-05 16:28:06 +01:00
async function main() { // main entry point
2021-11-18 16:10:06 +01:00
log('human version:', human.version, '| tfjs version:', human.tf.version['tfjs-core']);
2021-11-06 15:21:51 +01:00
log('platform:', human.env.platform, '| agent:', human.env.agent);
2021-10-27 14:16:06 +02:00
status('loading...');
2021-11-05 16:28:06 +01:00
await human.load(); // preload all models
log('backend:', human.tf.getBackend(), '| available:', human.env.backends);
2022-07-14 16:02:23 +02:00
log('models stats:', human.getModelStats());
log('models loaded:', Object.values(human.models).filter((model) => model !== null).length);
2021-10-27 14:16:06 +02:00
status('initializing...');
2021-11-05 16:28:06 +01:00
await human.warmup(); // warmup function to initialize backend for future faster detection
await webCam(); // start webcam
await detectionLoop(); // start detection loop
await drawLoop(); // start draw loop
2021-10-27 14:16:06 +02:00
}
window.onload = main;