additional human.performance counters

pull/356/head
Vladimir Mandic 2021-10-27 09:45:38 -04:00
parent 686b0716de
commit 59058a0b93
8 changed files with 129 additions and 61 deletions

View File

@ -9,8 +9,12 @@
## Changelog ## Changelog
### **HEAD -> main** 2021/10/26 mandic00@live.com ### **2.4.2** 2021/10/27 mandic00@live.com
### **origin/main** 2021/10/27 mandic00@live.com
- switch from es2018 to es2020 for main build
- switch to custom tfjs for demos - switch to custom tfjs for demos
### **release: 2.4.1** 2021/10/25 mandic00@live.com ### **release: 2.4.1** 2021/10/25 mandic00@live.com

View File

@ -16,7 +16,7 @@
<style> <style>
@font-face { font-family: 'Lato'; font-display: swap; font-style: normal; font-weight: 100; src: local('Lato'), url('../../assets/lato-light.woff2') } @font-face { font-family: 'Lato'; font-display: swap; font-style: normal; font-weight: 100; src: local('Lato'), url('../../assets/lato-light.woff2') }
html { font-family: 'Lato', 'Segoe UI'; font-size: 16px; font-variant: small-caps; } html { font-family: 'Lato', 'Segoe UI'; font-size: 16px; font-variant: small-caps; }
body { margin: 0; background: black; color: white; overflow-x: hidden; width: 100vw; height: 100vh; text-align: center; } body { margin: 0; background: black; color: white; overflow-x: hidden; width: 100vw; height: 100vh; }
body::-webkit-scrollbar { display: none; } body::-webkit-scrollbar { display: none; }
</style> </style>
</head> </head>
@ -24,5 +24,7 @@
<canvas id="canvas" style="margin: 0 auto; width: 100%"></canvas> <canvas id="canvas" style="margin: 0 auto; width: 100%"></canvas>
<video id="video" playsinline style="display: none"></video> <video id="video" playsinline style="display: none"></video>
<pre id="status" style="position: absolute; top: 20px; right: 20px; background-color: grey; padding: 8px; box-shadow: 2px 2px black"></pre> <pre id="status" style="position: absolute; top: 20px; right: 20px; background-color: grey; padding: 8px; box-shadow: 2px 2px black"></pre>
<pre id="log" style="padding: 8px"></pre>
<div id="performance" style="position: absolute; bottom: 0; width: 100%; padding: 8px; font-size: 0.8rem;"></div>
</body> </body>
</html> </html>

View File

@ -8,70 +8,95 @@
import Human from "../../dist/human.custom.esm.js"; import Human from "../../dist/human.custom.esm.js";
var config = { var config = {
modelBasePath: "../../models", modelBasePath: "../../models",
backend: "humangl" backend: "humangl",
async: true
}; };
var human = new Human(config); var human = new Human(config);
var result; var result;
var video = document.getElementById("video"); var dom = {
var canvas = document.getElementById("canvas"); video: document.getElementById("video"),
var fps = { detect: 0, draw: 0, element: document.getElementById("status") }; canvas: document.getElementById("canvas"),
var log = (...msg) => console.log(...msg); log: document.getElementById("log"),
fps: document.getElementById("status"),
perf: document.getElementById("performance")
};
var fps = { detect: 0, draw: 0 };
var log = (...msg) => {
dom.log.innerText += msg.join(" ") + "\n";
console.log(...msg);
};
var status = (msg) => { var status = (msg) => {
if (fps.element) dom.fps.innerText = msg;
fps.element.innerText = msg; };
var perf = (msg) => {
dom.perf.innerText = "performance: " + JSON.stringify(msg).replace(/"|{|}/g, "").replace(/,/g, " | ");
}; };
async function webCam() { async function webCam() {
status("starting webcam..."); status("starting webcam...");
const options = { audio: false, video: { facingMode: "user", resizeMode: "none", width: { ideal: document.body.clientWidth } } }; const options = { audio: false, video: { facingMode: "user", resizeMode: "none", width: { ideal: document.body.clientWidth } } };
const stream = await navigator.mediaDevices.getUserMedia(options); const stream = await navigator.mediaDevices.getUserMedia(options);
const ready = new Promise((resolve) => { const ready = new Promise((resolve) => {
video.onloadeddata = () => resolve(true); dom.video.onloadeddata = () => resolve(true);
}); });
video.srcObject = stream; dom.video.srcObject = stream;
video.play(); dom.video.play();
await ready; await ready;
canvas.width = video.videoWidth; dom.canvas.width = dom.video.videoWidth;
canvas.height = video.videoHeight; dom.canvas.height = dom.video.videoHeight;
const track = stream.getVideoTracks()[0]; const track = stream.getVideoTracks()[0];
const capabilities = track.getCapabilities(); const capabilities = track.getCapabilities();
const settings = track.getSettings(); const settings = track.getSettings();
const constraints = track.getConstraints(); const constraints = track.getConstraints();
log("video:", video.videoWidth, video.videoHeight, { stream, track, settings, constraints, capabilities }); log("video:", dom.video.videoWidth, dom.video.videoHeight, track.label, { stream, track, settings, constraints, capabilities });
canvas.onclick = () => { dom.canvas.onclick = () => {
if (video.paused) if (dom.video.paused)
video.play(); dom.video.play();
else else
video.pause(); dom.video.pause();
}; };
} }
async function detectionLoop() { async function detectionLoop() {
const t0 = human.now(); const t0 = human.now();
if (!video.paused) if (!dom.video.paused) {
result = await human.detect(video); result = await human.detect(dom.video);
}
const t1 = human.now(); const t1 = human.now();
fps.detect = 1e3 / (t1 - t0); fps.detect = 1e3 / (t1 - t0);
requestAnimationFrame(detectionLoop); requestAnimationFrame(detectionLoop);
} }
async function drawLoop() { async function drawLoop() {
const t0 = human.now(); const t0 = human.now();
if (!video.paused) { if (!dom.video.paused) {
const interpolated = await human.next(result); const interpolated = await human.next(result);
await human.draw.canvas(video, canvas); await human.draw.canvas(dom.video, dom.canvas);
await human.draw.all(canvas, interpolated); await human.draw.all(dom.canvas, interpolated);
perf(interpolated.performance);
} }
const t1 = human.now(); const t1 = human.now();
fps.draw = 1e3 / (t1 - t0); fps.draw = 1e3 / (t1 - t0);
status(video.paused ? "paused" : `fps: ${fps.detect.toFixed(1).padStart(5, " ")} detect / ${fps.draw.toFixed(1).padStart(5, " ")} draw`); status(dom.video.paused ? "paused" : `fps: ${fps.detect.toFixed(1).padStart(5, " ")} detect / ${fps.draw.toFixed(1).padStart(5, " ")} draw`);
requestAnimationFrame(drawLoop); requestAnimationFrame(drawLoop);
} }
async function main() { async function main() {
log("human version:", human.version, "tfjs:", human.tf.version_core);
log("platform:", human.env.platform, "agent:", human.env.agent);
human.env.perfadd = true;
status("loading..."); status("loading...");
await human.load(); await human.load();
status("initializing..."); status("initializing...");
log("backend:", human.tf.getBackend(), "available:", human.env.backends);
await human.warmup(); await human.warmup();
await webCam(); await webCam();
await detectionLoop(); await detectionLoop();
await drawLoop(); await drawLoop();
} }
window.onload = main; window.onload = main;
/**
* Human demo for browsers
* @default Human Library
* @summary <https://github.com/vladmandic/human>
* @author <https://github.com/vladmandic>
* @copyright <https://github.com/vladmandic>
* @license MIT
*/
//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map

View File

@ -1,6 +1,10 @@
/** /**
* Human demo for browsers * Human demo for browsers
* @description Simple Human demo for browsers using WebCam * @default Human Library
* @summary <https://github.com/vladmandic/human>
* @author <https://github.com/vladmandic>
* @copyright <https://github.com/vladmandic>
* @license MIT
*/ */
import Human from '../../dist/human.custom.esm.js'; // equivalent of @vladmandic/human import Human from '../../dist/human.custom.esm.js'; // equivalent of @vladmandic/human
@ -8,43 +12,60 @@ import Human from '../../dist/human.custom.esm.js'; // equivalent of @vladmandic
const config = { const config = {
modelBasePath: '../../models', modelBasePath: '../../models',
backend: 'humangl', backend: 'humangl',
async: true,
}; };
const human = new Human(config); const human = new Human(config);
let result; let result;
const video = document.getElementById('video') as HTMLVideoElement; const dom = {
const canvas = document.getElementById('canvas') as HTMLCanvasElement; video: document.getElementById('video') as HTMLVideoElement,
const fps = { detect: 0, draw: 0, element: document.getElementById('status') }; canvas: document.getElementById('canvas') as HTMLCanvasElement,
log: document.getElementById('log') as HTMLPreElement,
fps: document.getElementById('status') as HTMLPreElement,
perf: document.getElementById('performance') as HTMLDivElement,
};
// eslint-disable-next-line no-console const fps = { detect: 0, draw: 0 };
const log = (...msg) => console.log(...msg);
const status = (msg) => { if (fps.element) fps.element.innerText = msg; }; const log = (...msg) => {
dom.log.innerText += msg.join(' ') + '\n';
// eslint-disable-next-line no-console
console.log(...msg);
};
const status = (msg) => {
dom.fps.innerText = msg;
};
const perf = (msg) => {
dom.perf.innerText = 'performance: ' + JSON.stringify(msg).replace(/"|{|}/g, '').replace(/,/g, ' | ');
};
async function webCam() { async function webCam() {
status('starting webcam...'); status('starting webcam...');
const options = { audio: false, video: { facingMode: 'user', resizeMode: 'none', width: { ideal: document.body.clientWidth } } }; const options = { audio: false, video: { facingMode: 'user', resizeMode: 'none', width: { ideal: document.body.clientWidth } } };
const stream: MediaStream = await navigator.mediaDevices.getUserMedia(options); const stream: MediaStream = await navigator.mediaDevices.getUserMedia(options);
const ready = new Promise((resolve) => { video.onloadeddata = () => resolve(true); }); const ready = new Promise((resolve) => { dom.video.onloadeddata = () => resolve(true); });
video.srcObject = stream; dom.video.srcObject = stream;
video.play(); dom.video.play();
await ready; await ready;
canvas.width = video.videoWidth; dom.canvas.width = dom.video.videoWidth;
canvas.height = video.videoHeight; dom.canvas.height = dom.video.videoHeight;
const track: MediaStreamTrack = stream.getVideoTracks()[0]; const track: MediaStreamTrack = stream.getVideoTracks()[0];
const capabilities: MediaTrackCapabilities = track.getCapabilities(); const capabilities: MediaTrackCapabilities = track.getCapabilities();
const settings: MediaTrackSettings = track.getSettings(); const settings: MediaTrackSettings = track.getSettings();
const constraints: MediaTrackConstraints = track.getConstraints(); const constraints: MediaTrackConstraints = track.getConstraints();
log('video:', video.videoWidth, video.videoHeight, { stream, track, settings, constraints, capabilities }); log('video:', dom.video.videoWidth, dom.video.videoHeight, track.label, { stream, track, settings, constraints, capabilities });
canvas.onclick = () => { dom.canvas.onclick = () => {
if (video.paused) video.play(); if (dom.video.paused) dom.video.play();
else video.pause(); else dom.video.pause();
}; };
} }
async function detectionLoop() { async function detectionLoop() {
const t0 = human.now(); const t0 = human.now();
if (!video.paused) result = await human.detect(video); if (!dom.video.paused) {
result = await human.detect(dom.video);
}
const t1 = human.now(); const t1 = human.now();
fps.detect = 1000 / (t1 - t0); fps.detect = 1000 / (t1 - t0);
requestAnimationFrame(detectionLoop); requestAnimationFrame(detectionLoop);
@ -52,21 +73,26 @@ async function detectionLoop() {
async function drawLoop() { async function drawLoop() {
const t0 = human.now(); const t0 = human.now();
if (!video.paused) { if (!dom.video.paused) {
const interpolated = await human.next(result); const interpolated = await human.next(result);
await human.draw.canvas(video, canvas); await human.draw.canvas(dom.video, dom.canvas);
await human.draw.all(canvas, interpolated); await human.draw.all(dom.canvas, interpolated);
perf(interpolated.performance);
} }
const t1 = human.now(); const t1 = human.now();
fps.draw = 1000 / (t1 - t0); fps.draw = 1000 / (t1 - t0);
status(video.paused ? 'paused' : `fps: ${fps.detect.toFixed(1).padStart(5, ' ')} detect / ${fps.draw.toFixed(1).padStart(5, ' ')} draw`); status(dom.video.paused ? 'paused' : `fps: ${fps.detect.toFixed(1).padStart(5, ' ')} detect / ${fps.draw.toFixed(1).padStart(5, ' ')} draw`);
requestAnimationFrame(drawLoop); requestAnimationFrame(drawLoop);
} }
async function main() { async function main() {
log('human version:', human.version, 'tfjs:', human.tf.version_core);
log('platform:', human.env.platform, 'agent:', human.env.agent);
human.env.perfadd = true;
status('loading...'); status('loading...');
await human.load(); await human.load();
status('initializing...'); status('initializing...');
log('backend:', human.tf.getBackend(), 'available:', human.env.backends);
await human.warmup(); await human.warmup();
await webCam(); await webCam();
await detectionLoop(); await detectionLoop();

View File

@ -92,7 +92,7 @@ export const detectFace = async (parent /* instance of human */, input: Tensor):
parent.state = 'run:description'; parent.state = 'run:description';
timeStamp = now(); timeStamp = now();
descRes = parent.config.face.description.enabled ? await faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; descRes = parent.config.face.description.enabled ? await faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null;
parent.performance.embedding = env.perfadd ? (parent.performance.embedding || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp); parent.performance.description = env.perfadd ? (parent.performance.description || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
} }
parent.analyze('End Description:'); parent.analyze('End Description:');

View File

@ -1,6 +1,10 @@
/** /**
* Human main module * Human main module
* @default Human Library
* @summary <https://github.com/vladmandic/human>
* @author <https://github.com/vladmandic> * @author <https://github.com/vladmandic>
* @copyright <https://github.com/vladmandic>
* @license MIT
*/ */
// module imports // module imports
@ -150,7 +154,7 @@ export class Human {
this.#numTensors = 0; this.#numTensors = 0;
this.#analyzeMemoryLeaks = false; this.#analyzeMemoryLeaks = false;
this.#checkSanity = false; this.#checkSanity = false;
this.performance = { backend: 0, load: 0, image: 0, frames: 0, cached: 0, changed: 0, total: 0, draw: 0 }; this.performance = {};
this.events = (typeof EventTarget !== 'undefined') ? new EventTarget() : undefined; this.events = (typeof EventTarget !== 'undefined') ? new EventTarget() : undefined;
// object that contains all initialized models // object that contains all initialized models
this.models = new models.Models(); this.models = new models.Models();
@ -310,7 +314,7 @@ export class Human {
} }
const current = Math.trunc(now() - timeStamp); const current = Math.trunc(now() - timeStamp);
if (current > (this.performance.load as number || 0)) this.performance.load = this.env.perfadd ? (this.performance.load || 0) + current : current; if (current > (this.performance.loadModels as number || 0)) this.performance.loadModels = this.env.perfadd ? (this.performance.loadModels || 0) + current : current;
} }
// emit event // emit event
@ -335,8 +339,12 @@ export class Human {
* @param userConfig?: {@link Config} * @param userConfig?: {@link Config}
* @returns result: {@link Result} * @returns result: {@link Result}
*/ */
async warmup(userConfig?: Partial<Config>): Promise<Result | { error }> { async warmup(userConfig?: Partial<Config>) {
return warmups.warmup(this, userConfig) as Promise<Result | { error }>; const t0 = now();
const res = await warmups.warmup(this, userConfig);
const t1 = now();
this.performance.warmup = Math.trunc(t1 - t0);
return res;
} }
/** Main detection method /** Main detection method
@ -379,7 +387,7 @@ export class Human {
this.state = 'image'; this.state = 'image';
const img = image.process(input, this.config) as { canvas: HTMLCanvasElement | OffscreenCanvas, tensor: Tensor }; const img = image.process(input, this.config) as { canvas: HTMLCanvasElement | OffscreenCanvas, tensor: Tensor };
this.process = img; this.process = img;
this.performance.image = this.env.perfadd ? (this.performance.image || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp); this.performance.inputProcess = this.env.perfadd ? (this.performance.inputProcess || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
this.analyze('Get Image:'); this.analyze('Get Image:');
if (!img.tensor) { if (!img.tensor) {
@ -391,11 +399,11 @@ export class Human {
timeStamp = now(); timeStamp = now();
this.config.skipAllowed = await image.skip(this.config, img.tensor); this.config.skipAllowed = await image.skip(this.config, img.tensor);
if (!this.performance.frames) this.performance.frames = 0; if (!this.performance.totalFrames) this.performance.totalFrames = 0;
if (!this.performance.cached) this.performance.cached = 0; if (!this.performance.cachedFrames) this.performance.cachedFrames = 0;
(this.performance.frames as number)++; (this.performance.totalFrames as number)++;
if (this.config.skipAllowed) this.performance.cached++; if (this.config.skipAllowed) this.performance.cachedFrames++;
this.performance.changed = this.env.perfadd ? (this.performance.changed || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp); this.performance.inputCheck = this.env.perfadd ? (this.performance.inputCheck || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
this.analyze('Check Changed:'); this.analyze('Check Changed:');
// prepare where to store model results // prepare where to store model results
@ -454,7 +462,7 @@ export class Human {
} }
this.analyze('End Hand:'); this.analyze('End Hand:');
// run nanodet // run object detection
this.analyze('Start Object:'); this.analyze('Start Object:');
this.state = 'detect:object'; this.state = 'detect:object';
if (this.config.async) { if (this.config.async) {
@ -483,7 +491,7 @@ export class Human {
else if (this.performance.gesture) delete this.performance.gesture; else if (this.performance.gesture) delete this.performance.gesture;
} }
this.performance.total = Math.trunc(now() - timeStart); this.performance.total = this.env.perfadd ? (this.performance.total || 0) + Math.trunc(now() - timeStart) : Math.trunc(now() - timeStart);
const shape = this.process?.tensor?.shape || []; const shape = this.process?.tensor?.shape || [];
this.result = { this.result = {
face: faceRes as FaceResult[], face: faceRes as FaceResult[],

View File

@ -99,7 +99,7 @@ export async function check(instance, force = false) {
// wait for ready // wait for ready
tf.enableProdMode(); tf.enableProdMode();
await tf.ready(); await tf.ready();
instance.performance.backend = Math.trunc(now() - timeStamp); instance.performance.initBackend = Math.trunc(now() - timeStamp);
instance.config.backend = tf.getBackend(); instance.config.backend = tf.getBackend();
env.updateBackend(); // update env on backend init env.updateBackend(); // update env on backend init

View File

@ -9,8 +9,10 @@ import * as moveNetCoords from '../body/movenetcoords';
import * as blazePoseCoords from '../body/blazeposecoords'; import * as blazePoseCoords from '../body/blazeposecoords';
import * as efficientPoseCoords from '../body/efficientposecoords'; import * as efficientPoseCoords from '../body/efficientposecoords';
import { now } from './util'; import { now } from './util';
import { env } from './env';
const bufferedResult: Result = { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0 }; const bufferedResult: Result = { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0 };
let interpolateTime = 0;
export function calc(newResult: Result, config: Config): Result { export function calc(newResult: Result, config: Config): Result {
const t0 = now(); const t0 = now();
@ -163,7 +165,8 @@ export function calc(newResult: Result, config: Config): Result {
// append interpolation performance data // append interpolation performance data
const t1 = now(); const t1 = now();
if (newResult.performance) bufferedResult.performance = { ...newResult.performance, interpolate: Math.round(t1 - t0) }; interpolateTime = env.perfadd ? interpolateTime + Math.round(t1 - t0) : Math.round(t1 - t0);
if (newResult.performance) bufferedResult.performance = { ...newResult.performance, interpolate: interpolateTime };
return bufferedResult; return bufferedResult;
} }