ui redesign

pull/50/head
Vladimir Mandic 2020-11-19 14:45:59 -05:00
parent e76091ed5b
commit 5a87c48bfe
21 changed files with 287 additions and 222 deletions

View File

@ -21,12 +21,17 @@
], ],
"ignorePatterns": [ "dist", "assets", "media", "models", "node_modules" ], "ignorePatterns": [ "dist", "assets", "media", "models", "node_modules" ],
"rules": { "rules": {
"max-len": [1, 275, 3],
"camelcase": "off", "camelcase": "off",
"guard-for-in": "off", "dot-notation": "off",
"prefer-template":"off",
"import/extensions": "off",
"func-names": "off", "func-names": "off",
"guard-for-in": "off",
"import/extensions": "off",
"import/no-absolute-path": "off",
"import/no-extraneous-dependencies": "off",
"import/prefer-default-export": "off",
"max-len": [1, 275, 3],
"newline-per-chained-call": "off",
"no-async-promise-executor": "off",
"no-await-in-loop": "off", "no-await-in-loop": "off",
"no-bitwise": "off", "no-bitwise": "off",
"no-case-declarations":"off", "no-case-declarations":"off",
@ -35,25 +40,21 @@
"no-mixed-operators": "off", "no-mixed-operators": "off",
"no-param-reassign":"off", "no-param-reassign":"off",
"no-plusplus": "off", "no-plusplus": "off",
"dot-notation": "off", "no-regex-spaces": "off",
"no-restricted-globals": "off", "no-restricted-globals": "off",
"no-restricted-syntax": "off", "no-restricted-syntax": "off",
"no-underscore-dangle": "off",
"no-return-assign": "off", "no-return-assign": "off",
"newline-per-chained-call": "off", "no-underscore-dangle": "off",
"node/no-unpublished-import": "off",
"node/no-unpublished-require": "off",
"node/no-unsupported-features/es-syntax": "off", "node/no-unsupported-features/es-syntax": "off",
"node/shebang": "off", "node/shebang": "off",
"object-curly-newline": "off", "object-curly-newline": "off",
"prefer-destructuring": "off", "prefer-destructuring": "off",
"prefer-template":"off",
"promise/always-return": "off", "promise/always-return": "off",
"promise/catch-or-return": "off", "promise/catch-or-return": "off",
"promise/no-nesting": "off", "promise/no-nesting": "off",
"no-async-promise-executor": "off",
"import/no-absolute-path": "off",
"import/no-extraneous-dependencies": "off",
"node/no-unpublished-import": "off",
"node/no-unpublished-require": "off",
"no-regex-spaces": "off",
"radix": "off" "radix": "off"
} }
} }

BIN
assets/fa-solid-900.woff2 Normal file

Binary file not shown.

View File

@ -1,7 +1,7 @@
import Human from '../dist/human.esm.js'; import Human from '../dist/human.esm.js';
import draw from './draw.js'; import draw from './draw.js';
import Menu from './menu.js'; import Menu from './menu.js';
import GLBench from '../assets/gl-bench.js'; import GLBench from './gl-bench.js';
const userConfig = {}; // add any user configuration overrides const userConfig = {}; // add any user configuration overrides
@ -11,10 +11,9 @@ const human = new Human(userConfig);
const ui = { const ui = {
baseColor: 'rgba(173, 216, 230, 0.3)', // 'lightblue' with light alpha channel baseColor: 'rgba(173, 216, 230, 0.3)', // 'lightblue' with light alpha channel
baseBackground: 'rgba(50, 50, 50, 1)', // 'grey' baseBackground: 'rgba(50, 50, 50, 1)', // 'grey'
baseLabel: 'rgba(173, 216, 230, 0.9)', // 'lightblue' with dark alpha channel baseLabel: 'rgba(173, 216, 230, 1)', // 'lightblue' with dark alpha channel
baseFontProto: 'small-caps {size} "Segoe UI"', baseFontProto: 'small-caps {size} "Segoe UI"',
baseLineWidth: 12, baseLineWidth: 12,
baseLineHeightProto: 2,
crop: true, crop: true,
columns: 2, columns: 2,
busy: false, busy: false,
@ -38,7 +37,6 @@ const ui = {
detectFPS: [], detectFPS: [],
drawFPS: [], drawFPS: [],
buffered: false, buffered: false,
bufferedFPSTarget: 0,
drawThread: null, drawThread: null,
detectThread: null, detectThread: null,
framesDraw: 0, framesDraw: 0,
@ -47,11 +45,9 @@ const ui = {
}; };
// global variables // global variables
let menu; const menu = {};
let menuFX;
let worker; let worker;
let bench; let bench;
let sample;
let lastDetectedResult = {}; let lastDetectedResult = {};
// helper function: translates json to human readable string // helper function: translates json to human readable string
@ -78,14 +74,16 @@ const status = (msg) => {
document.getElementById('status').innerText = msg; document.getElementById('status').innerText = msg;
}; };
async function calcSimmilariry(faces) { let original;
if (!faces || !faces[0] || (faces[0].embedding?.length !== 192)) return; async function calcSimmilariry(result) {
const current = faces[0].embedding; document.getElementById('compare-container').style.display = human.config.face.embedding.enabled ? 'block' : 'none';
const original = (sample && sample.face && sample.face[0] && sample.face[0].embedding) ? sample.face[0].embedding : null; if ((result?.face?.length > 0) && (result?.face[0].embedding?.length !== 192)) return;
if (original && original.length === 192) { if (!original) {
const simmilarity = human.simmilarity(current, original); original = result;
document.getElementById('simmilarity').innerText = `simmilarity: ${Math.trunc(1000 * simmilarity) / 10}%`; document.getElementById('compare-canvas').getContext('2d').drawImage(original.canvas, 0, 0, 200, 200);
} }
const simmilarity = human.simmilarity(original.face[0].embedding, result.face[0].embedding);
document.getElementById('simmilarity').innerText = `simmilarity: ${Math.trunc(1000 * simmilarity) / 10}%`;
} }
// draws processed results and starts processing of a next frame // draws processed results and starts processing of a next frame
@ -103,7 +101,7 @@ async function drawResults(input) {
// console.log(result.performance); // console.log(result.performance);
// draw fps chart // draw fps chart
await menu.updateChart('FPS', ui.detectFPS); await menu.process.updateChart('FPS', ui.detectFPS);
// get updated canvas // get updated canvas
if (ui.buffered || !result.canvas) result.canvas = await human.image(input, userConfig); if (ui.buffered || !result.canvas) result.canvas = await human.image(input, userConfig);
@ -125,7 +123,7 @@ async function drawResults(input) {
await draw.body(result.body, canvas, ui); await draw.body(result.body, canvas, ui);
await draw.hand(result.hand, canvas, ui); await draw.hand(result.hand, canvas, ui);
await draw.gesture(result.gesture, canvas, ui); await draw.gesture(result.gesture, canvas, ui);
await calcSimmilariry(result.face); await calcSimmilariry(result);
// update log // update log
const engine = human.tf.engine(); const engine = human.tf.engine();
@ -145,14 +143,11 @@ async function drawResults(input) {
ui.framesDraw++; ui.framesDraw++;
ui.lastFrame = performance.now(); ui.lastFrame = performance.now();
// if buffered, immediate loop but limit frame rate although it's going to run slower as JS is singlethreaded // if buffered, immediate loop but limit frame rate although it's going to run slower as JS is singlethreaded
if ((ui.bufferedFPSTarget === 0) && ui.buffered) { if (ui.buffered) {
ui.drawThread = requestAnimationFrame(() => drawResults(input, canvas)); ui.drawThread = requestAnimationFrame(() => drawResults(input, canvas));
} else if ((ui.bufferedFPSTarget === 0) && ui.buffered && !ui.drawThread) {
log('starting buffered refresh');
if (ui.bufferedFPSTarget > 0) ui.drawThread = setInterval(() => drawResults(input, canvas), 1000 / ui.bufferedFPSTarget);
} else if (!ui.buffered && ui.drawThread) { } else if (!ui.buffered && ui.drawThread) {
log('stopping buffered refresh'); log('stopping buffered refresh');
clearTimeout(ui.drawThread); cancelAnimationFrame(ui.drawThread);
ui.drawThread = null; ui.drawThread = null;
} }
} }
@ -181,7 +176,7 @@ async function setupCamera() {
video: { facingMode: ui.facing ? 'user' : 'environment', resizeMode: ui.crop ? 'crop-and-scale' : 'none' }, video: { facingMode: ui.facing ? 'user' : 'environment', resizeMode: ui.crop ? 'crop-and-scale' : 'none' },
}; };
if (window.innerWidth > window.innerHeight) constraints.video.width = { ideal: window.innerWidth }; if (window.innerWidth > window.innerHeight) constraints.video.width = { ideal: window.innerWidth };
else constraints.video.height = { ideal: window.innerHeight }; else constraints.video.height = { ideal: (window.innerHeight - document.getElementById('menubar').offsetHeight) };
try { try {
stream = await navigator.mediaDevices.getUserMedia(constraints); stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (err) { } catch (err) {
@ -209,8 +204,9 @@ async function setupCamera() {
ui.menuWidth.input.setAttribute('value', video.width); ui.menuWidth.input.setAttribute('value', video.width);
ui.menuHeight.input.setAttribute('value', video.height); ui.menuHeight.input.setAttribute('value', video.height);
// silly font resizing for paint-on-canvas since viewport can be zoomed // silly font resizing for paint-on-canvas since viewport can be zoomed
const size = 14 + (6 * canvas.width / window.innerWidth); const size = Math.trunc(window.devicePixelRatio * (8 + (4 * canvas.width / window.innerWidth)));
ui.baseFont = ui.baseFontProto.replace(/{size}/, `${size}px`); ui.baseFont = ui.baseFontProto.replace(/{size}/, `${size}px`);
ui.baseLineHeight = size + 4;
if (live) video.play(); if (live) video.play();
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
if (live && !ui.detectThread) runHumanDetect(video, canvas); if (live && !ui.detectThread) runHumanDetect(video, canvas);
@ -223,6 +219,20 @@ async function setupCamera() {
}); });
} }
function initPerfMonitor() {
if (!bench) {
const gl = null;
// cosnt gl = human.tf.engine().backend.gpgpu.gl;
// if (!gl) log('bench cannot get tensorflow webgl context');
bench = new GLBench(gl, {
trackGPU: false, // this is really slow
chartHz: 20,
chartLen: 20,
});
bench.begin();
}
}
// wrapper for worker.postmessage that creates worker if one does not exist // wrapper for worker.postmessage that creates worker if one does not exist
function webWorker(input, image, canvas, timestamp) { function webWorker(input, image, canvas, timestamp) {
if (!worker) { if (!worker) {
@ -233,8 +243,11 @@ function webWorker(input, image, canvas, timestamp) {
worker.addEventListener('message', (msg) => { worker.addEventListener('message', (msg) => {
if (msg.data.result.performance && msg.data.result.performance.total) ui.detectFPS.push(1000 / msg.data.result.performance.total); if (msg.data.result.performance && msg.data.result.performance.total) ui.detectFPS.push(1000 / msg.data.result.performance.total);
if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift(); if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift();
if (ui.bench) bench.end(); if (ui.bench) {
if (ui.bench) bench.nextFrame(timestamp); if (!bench) initPerfMonitor();
bench.nextFrame(timestamp);
}
if (document.getElementById('gl-bench')) document.getElementById('gl-bench').style.display = ui.bench ? 'block' : 'none';
lastDetectedResult = msg.data.result; lastDetectedResult = msg.data.result;
ui.framesDetect++; ui.framesDetect++;
if (!ui.drawThread) drawResults(input); if (!ui.drawThread) drawResults(input);
@ -243,7 +256,6 @@ function webWorker(input, image, canvas, timestamp) {
}); });
} }
// pass image data as arraybuffer to worker by reference to avoid copy // pass image data as arraybuffer to worker by reference to avoid copy
if (ui.bench) bench.begin();
worker.postMessage({ image: image.data.buffer, width: canvas.width, height: canvas.height, userConfig }, [image.data.buffer]); worker.postMessage({ image: image.data.buffer, width: canvas.width, height: canvas.height, userConfig }, [image.data.buffer]);
} }
@ -253,7 +265,7 @@ function runHumanDetect(input, canvas, timestamp) {
const live = input.srcObject && (input.srcObject.getVideoTracks()[0].readyState === 'live') && (input.readyState > 2) && (!input.paused); const live = input.srcObject && (input.srcObject.getVideoTracks()[0].readyState === 'live') && (input.readyState > 2) && (!input.paused);
if (!live && input.srcObject) { if (!live && input.srcObject) {
// stop ui refresh // stop ui refresh
if (ui.drawThread) clearTimeout(ui.drawThread); if (ui.drawThread) cancelAnimationFrame(ui.drawThread);
if (ui.detectThread) cancelAnimationFrame(ui.detectThread); if (ui.detectThread) cancelAnimationFrame(ui.detectThread);
ui.drawThread = null; ui.drawThread = null;
ui.detectThread = null; ui.detectThread = null;
@ -272,19 +284,20 @@ function runHumanDetect(input, canvas, timestamp) {
const offscreen = (typeof OffscreenCanvas !== 'undefined') ? new OffscreenCanvas(canvas.width, canvas.height) : document.createElement('canvas'); const offscreen = (typeof OffscreenCanvas !== 'undefined') ? new OffscreenCanvas(canvas.width, canvas.height) : document.createElement('canvas');
offscreen.width = canvas.width; offscreen.width = canvas.width;
offscreen.height = canvas.height; offscreen.height = canvas.height;
const ctx = offscreen.getContext('2d'); const ctx = offscreen.getContext('2d');
ctx.drawImage(input, 0, 0, input.width, input.height, 0, 0, canvas.width, canvas.height); ctx.drawImage(input, 0, 0, input.width, input.height, 0, 0, canvas.width, canvas.height);
const data = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
// perform detection in worker // perform detection in worker
webWorker(input, data, canvas, userConfig, timestamp); webWorker(input, data, canvas, userConfig, timestamp);
} else { } else {
if (ui.bench) bench.begin();
human.detect(input, userConfig).then((result) => { human.detect(input, userConfig).then((result) => {
if (result.performance && result.performance.total) ui.detectFPS.push(1000 / result.performance.total); if (result.performance && result.performance.total) ui.detectFPS.push(1000 / result.performance.total);
if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift(); if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift();
if (ui.bench) bench.end(); if (ui.bench) {
if (ui.bench) bench.nextFrame(timestamp); if (!bench) initPerfMonitor();
bench.nextFrame(timestamp);
}
if (document.getElementById('gl-bench')) document.getElementById('gl-bench').style.display = ui.bench ? 'block' : 'none';
if (result.error) log(result.error); if (result.error) log(result.error);
else { else {
lastDetectedResult = result; lastDetectedResult = result;
@ -331,15 +344,19 @@ async function detectVideo() {
document.getElementById('canvas').style.display = 'block'; document.getElementById('canvas').style.display = 'block';
const video = document.getElementById('video'); const video = document.getElementById('video');
const canvas = document.getElementById('canvas'); const canvas = document.getElementById('canvas');
ui.baseLineHeight = ui.baseLineHeightProto;
if ((video.srcObject !== null) && !video.paused) { if ((video.srcObject !== null) && !video.paused) {
document.getElementById('play').style.display = 'block'; document.getElementById('play').style.display = 'block';
document.getElementById('btnStart').className = 'button button-start';
document.getElementById('btnStart').innerHTML = 'start<br>video';
status('paused'); status('paused');
video.pause(); video.pause();
} else { } else {
await setupCamera(); await setupCamera();
document.getElementById('play').style.display = 'none'; document.getElementById('play').style.display = 'none';
for (const m of Object.values(menu)) m.hide();
status(''); status('');
document.getElementById('btnStart').className = 'button button-stop';
document.getElementById('btnStart').innerHTML = 'pause<br>video';
video.play(); video.play();
} }
if (!ui.detectThread) runHumanDetect(video, canvas); if (!ui.detectThread) runHumanDetect(video, canvas);
@ -349,9 +366,9 @@ async function detectVideo() {
async function detectSampleImages() { async function detectSampleImages() {
document.getElementById('play').style.display = 'none'; document.getElementById('play').style.display = 'none';
userConfig.videoOptimized = false; userConfig.videoOptimized = false;
const size = 12 + Math.trunc(12 * ui.columns * window.innerWidth / document.body.clientWidth); const size = Math.trunc(window.devicePixelRatio * (8 + (4 * ui.columns)));
ui.baseFont = ui.baseFontProto.replace(/{size}/, `${size}px`); ui.baseFont = ui.baseFontProto.replace(/{size}/, `${size}px`);
ui.baseLineHeight = ui.baseLineHeightProto * ui.columns; ui.baseLineHeight = size + 2;
document.getElementById('canvas').style.display = 'none'; document.getElementById('canvas').style.display = 'none';
document.getElementById('samples-container').style.display = 'block'; document.getElementById('samples-container').style.display = 'block';
log('Running detection of sample images'); log('Running detection of sample images');
@ -362,121 +379,116 @@ async function detectSampleImages() {
} }
function setupMenu() { function setupMenu() {
document.getElementById('compare-container').style.display = human.config.face.embedding.enabled ? 'block' : 'none'; let x = [];
menu = new Menu(document.body, '', { top: '1rem', right: '1rem' }); if (window.innerWidth > 800) {
const btn = menu.addButton('start video', 'pause video', () => detectVideo()); // initial position of menu items, later it's calculated based on mouse coordinates
menu.addButton('process images', 'process images', () => detectSampleImages()); x = [`${document.getElementById('btnDisplay').offsetLeft - 50}px`, `${document.getElementById('btnImage').offsetLeft - 50}px`, `${document.getElementById('btnProcess').offsetLeft - 50}px`, `${document.getElementById('btnModel').offsetLeft - 50}px`];
document.getElementById('play').addEventListener('click', () => btn.click()); } else {
// absolute minimum spacing for menus
x = ['0rem', '11rem', '21.1rem', '33rem'];
}
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">'); menu.display = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[0] });
menu.addList('backend', ['cpu', 'webgl', 'wasm'], human.config.backend, (val) => human.config.backend = val); menu.display.addBool('perf monitor', ui, 'bench', (val) => ui.bench = val);
menu.addBool('async operations', human.config, 'async', (val) => human.config.async = val); menu.display.addBool('buffered output', ui, 'buffered', (val) => ui.buffered = val);
// menu.addBool('enable profiler', human.config, 'profile', (val) => human.config.profile = val); menu.display.addBool('crop & scale', ui, 'crop', () => setupCamera());
// menu.addBool('memory shield', human.config, 'deallocate', (val) => human.config.deallocate = val); menu.display.addBool('camera facing', ui, 'facing', () => setupCamera());
menu.addBool('use web worker', ui, 'useWorker'); menu.display.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">'); menu.display.addBool('use 3D depth', ui, 'useDepth');
menu.addLabel('enabled models'); menu.display.addBool('draw boxes', ui, 'drawBoxes');
menu.addBool('face detect', human.config.face, 'enabled'); menu.display.addBool('draw polygons', ui, 'drawPolygons');
menu.addBool('face mesh', human.config.face.mesh, 'enabled'); menu.display.addBool('Fill Polygons', ui, 'fillPolygons');
menu.addBool('face iris', human.config.face.iris, 'enabled'); menu.display.addBool('draw points', ui, 'drawPoints');
menu.addBool('face age', human.config.face.age, 'enabled');
menu.addBool('face gender', human.config.face.gender, 'enabled');
menu.addBool('face emotion', human.config.face.emotion, 'enabled');
// menu.addBool('face compare', human.config.face.embedding, 'enabled', (val) => {
// human.config.face.embedding.enabled = val;
// document.getElementById('compare-container').style.display = human.config.face.embedding.enabled ? 'block' : 'none';
// });
menu.addBool('body pose', human.config.body, 'enabled');
menu.addBool('hand pose', human.config.hand, 'enabled');
menu.addBool('gesture analysis', human.config.gesture, 'enabled');
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">'); menu.image = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[1] });
menu.addLabel('model parameters'); menu.image.addBool('enabled', human.config.filter, 'enabled');
menu.addRange('max objects', human.config.face.detector, 'maxFaces', 1, 50, 1, (val) => { ui.menuWidth = menu.image.addRange('image width', human.config.filter, 'width', 0, 3840, 10, (val) => human.config.filter.width = parseInt(val));
ui.menuHeight = menu.image.addRange('image height', human.config.filter, 'height', 0, 2160, 10, (val) => human.config.filter.height = parseInt(val));
menu.image.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.image.addRange('brightness', human.config.filter, 'brightness', -1.0, 1.0, 0.05, (val) => human.config.filter.brightness = parseFloat(val));
menu.image.addRange('contrast', human.config.filter, 'contrast', -1.0, 1.0, 0.05, (val) => human.config.filter.contrast = parseFloat(val));
menu.image.addRange('sharpness', human.config.filter, 'sharpness', 0, 1.0, 0.05, (val) => human.config.filter.sharpness = parseFloat(val));
menu.image.addRange('blur', human.config.filter, 'blur', 0, 20, 1, (val) => human.config.filter.blur = parseInt(val));
menu.image.addRange('saturation', human.config.filter, 'saturation', -1.0, 1.0, 0.05, (val) => human.config.filter.saturation = parseFloat(val));
menu.image.addRange('hue', human.config.filter, 'hue', 0, 360, 5, (val) => human.config.filter.hue = parseInt(val));
menu.image.addRange('pixelate', human.config.filter, 'pixelate', 0, 32, 1, (val) => human.config.filter.pixelate = parseInt(val));
menu.image.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.image.addBool('negative', human.config.filter, 'negative');
menu.image.addBool('sepia', human.config.filter, 'sepia');
menu.image.addBool('vintage', human.config.filter, 'vintage');
menu.image.addBool('kodachrome', human.config.filter, 'kodachrome');
menu.image.addBool('technicolor', human.config.filter, 'technicolor');
menu.image.addBool('polaroid', human.config.filter, 'polaroid');
menu.process = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[2] });
menu.process.addList('backend', ['cpu', 'webgl', 'wasm'], 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('use web worker', ui, 'useWorker');
menu.process.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.process.addLabel('model parameters');
menu.process.addRange('max objects', human.config.face.detector, 'maxFaces', 1, 50, 1, (val) => {
human.config.face.detector.maxFaces = parseInt(val); human.config.face.detector.maxFaces = parseInt(val);
human.config.body.maxDetections = parseInt(val); human.config.body.maxDetections = parseInt(val);
human.config.hand.maxHands = parseInt(val); human.config.hand.maxHands = parseInt(val);
}); });
menu.addRange('skip frames', human.config.face.detector, 'skipFrames', 0, 50, 1, (val) => { menu.process.addRange('skip frames', human.config.face.detector, 'skipFrames', 0, 50, 1, (val) => {
human.config.face.detector.skipFrames = parseInt(val); human.config.face.detector.skipFrames = parseInt(val);
human.config.face.emotion.skipFrames = parseInt(val); human.config.face.emotion.skipFrames = parseInt(val);
human.config.face.age.skipFrames = parseInt(val); human.config.face.age.skipFrames = parseInt(val);
human.config.hand.skipFrames = parseInt(val); human.config.hand.skipFrames = parseInt(val);
}); });
menu.addRange('min confidence', human.config.face.detector, 'minConfidence', 0.0, 1.0, 0.05, (val) => { menu.process.addRange('min confidence', human.config.face.detector, 'minConfidence', 0.0, 1.0, 0.05, (val) => {
human.config.face.detector.minConfidence = parseFloat(val); human.config.face.detector.minConfidence = parseFloat(val);
human.config.face.gender.minConfidence = parseFloat(val); human.config.face.gender.minConfidence = parseFloat(val);
human.config.face.emotion.minConfidence = parseFloat(val); human.config.face.emotion.minConfidence = parseFloat(val);
human.config.hand.minConfidence = parseFloat(val); human.config.hand.minConfidence = parseFloat(val);
}); });
menu.addRange('score threshold', human.config.face.detector, 'scoreThreshold', 0.1, 1.0, 0.05, (val) => { menu.process.addRange('score threshold', human.config.face.detector, 'scoreThreshold', 0.1, 1.0, 0.05, (val) => {
human.config.face.detector.scoreThreshold = parseFloat(val); human.config.face.detector.scoreThreshold = parseFloat(val);
human.config.hand.scoreThreshold = parseFloat(val); human.config.hand.scoreThreshold = parseFloat(val);
human.config.body.scoreThreshold = parseFloat(val); human.config.body.scoreThreshold = parseFloat(val);
}); });
menu.addRange('overlap', human.config.face.detector, 'iouThreshold', 0.1, 1.0, 0.05, (val) => { menu.process.addRange('overlap', human.config.face.detector, 'iouThreshold', 0.1, 1.0, 0.05, (val) => {
human.config.face.detector.iouThreshold = parseFloat(val); human.config.face.detector.iouThreshold = parseFloat(val);
human.config.hand.iouThreshold = parseFloat(val); human.config.hand.iouThreshold = parseFloat(val);
}); });
menu.process.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.process.addButton('process sample images', 'process images', () => detectSampleImages());
menu.process.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menu.process.addChart('FPS', 'FPS');
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">'); menu.models = new Menu(document.body, '', { top: `${document.getElementById('menubar').offsetHeight}px`, left: x[3] });
menu.addChart('FPS', 'FPS'); menu.models.addBool('face detect', human.config.face, 'enabled');
menu.models.addBool('face mesh', human.config.face.mesh, 'enabled');
menuFX = new Menu(document.body, '', { top: '1rem', right: '18rem' }); menu.models.addBool('face iris', human.config.face.iris, 'enabled');
menuFX.addLabel('ui options'); menu.models.addBool('face age', human.config.face.age, 'enabled');
menuFX.addBool('buffered output', ui, 'buffered', (val) => ui.buffered = val); menu.models.addBool('face gender', human.config.face.gender, 'enabled');
menuFX.addBool('crop & scale', ui, 'crop', () => setupCamera()); menu.models.addBool('face emotion', human.config.face.emotion, 'enabled');
menuFX.addBool('camera front/back', ui, 'facing', () => setupCamera()); menu.models.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menuFX.addBool('use 3D depth', ui, 'useDepth'); menu.models.addBool('body pose', human.config.body, 'enabled');
menuFX.addBool('draw boxes', ui, 'drawBoxes'); menu.models.addBool('hand pose', human.config.hand, 'enabled');
menuFX.addBool('draw polygons', ui, 'drawPolygons'); menu.models.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menuFX.addBool('Fill Polygons', ui, 'fillPolygons'); menu.models.addBool('gestures', human.config.gesture, 'enabled');
menuFX.addBool('draw points', ui, 'drawPoints'); menu.models.addHTML('<hr style="border-style: inset; border-color: dimgray">');
menuFX.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">'); menu.models.addBool('face compare', human.config.face.embedding, 'enabled', (val) => {
menuFX.addLabel('image processing'); original = null;
menuFX.addBool('enabled', human.config.filter, 'enabled'); human.config.face.embedding.enabled = val;
ui.menuWidth = menuFX.addRange('image width', human.config.filter, 'width', 0, 3840, 10, (val) => human.config.filter.width = parseInt(val));
ui.menuHeight = menuFX.addRange('image height', human.config.filter, 'height', 0, 2160, 10, (val) => human.config.filter.height = parseInt(val));
menuFX.addRange('brightness', human.config.filter, 'brightness', -1.0, 1.0, 0.05, (val) => human.config.filter.brightness = parseFloat(val));
menuFX.addRange('contrast', human.config.filter, 'contrast', -1.0, 1.0, 0.05, (val) => human.config.filter.contrast = parseFloat(val));
menuFX.addRange('sharpness', human.config.filter, 'sharpness', 0, 1.0, 0.05, (val) => human.config.filter.sharpness = parseFloat(val));
menuFX.addRange('blur', human.config.filter, 'blur', 0, 20, 1, (val) => human.config.filter.blur = parseInt(val));
menuFX.addRange('saturation', human.config.filter, 'saturation', -1.0, 1.0, 0.05, (val) => human.config.filter.saturation = parseFloat(val));
menuFX.addRange('hue', human.config.filter, 'hue', 0, 360, 5, (val) => human.config.filter.hue = parseInt(val));
menuFX.addRange('pixelate', human.config.filter, 'pixelate', 0, 32, 1, (val) => human.config.filter.pixelate = parseInt(val));
menuFX.addBool('negative', human.config.filter, 'negative');
menuFX.addBool('sepia', human.config.filter, 'sepia');
menuFX.addBool('vintage', human.config.filter, 'vintage');
menuFX.addBool('kodachrome', human.config.filter, 'kodachrome');
menuFX.addBool('technicolor', human.config.filter, 'technicolor');
menuFX.addBool('polaroid', human.config.filter, 'polaroid');
}
async function setupMonitor() {
let gl = human.tf.engine().backend.gpgpu;
if (!gl) gl = document.getElementById('bench-canvas').getContext('webgl2');
if (!bench) {
bench = new GLBench(gl, {
trackGPU: true,
chartHz: 20,
chartLen: 20,
}); });
}
/* document.getElementById('btnDisplay').addEventListener('click', (evt) => menu.display.toggle(evt));
function update(now) { document.getElementById('btnImage').addEventListener('click', (evt) => menu.image.toggle(evt));
bench.nextFrame(now); document.getElementById('btnProcess').addEventListener('click', (evt) => menu.process.toggle(evt));
requestAnimationFrame(update); document.getElementById('btnModel').addEventListener('click', (evt) => menu.models.toggle(evt));
} document.getElementById('btnStart').addEventListener('click', () => detectVideo());
requestAnimationFrame(update); document.getElementById('play').addEventListener('click', () => detectVideo());
*/
// class MathBackendWebGL extends tf.KernelBackend property gpgpu is gl context
} }
async function main() { async function main() {
log('demo starting ...'); log('demo starting ...');
setupMenu(); setupMenu();
setupMonitor(); document.getElementById('log').innerText = `Human: version ${human.version}`;
document.getElementById('log').innerText = `Human: version ${human.version} TensorFlow/JS: version ${human.tf.version_core}`;
// human.tf.ENV.set('WEBGL_FORCE_F16_TEXTURES', true); // human.tf.ENV.set('WEBGL_FORCE_F16_TEXTURES', true);
// this is not required, just pre-loads all models // this is not required, just pre-loads all models
if (ui.modelsPreload && !ui.useWorker) { if (ui.modelsPreload && !ui.useWorker) {
@ -486,7 +498,7 @@ async function main() {
// this is not required, just pre-warms all models for faster initial inference // this is not required, just pre-warms all models for faster initial inference
if (ui.modelsWarmup && !ui.useWorker) { if (ui.modelsWarmup && !ui.useWorker) {
status('initializing'); status('initializing');
sample = await human.warmup(userConfig, document.getElementById('sample-image')); await human.warmup(userConfig);
} }
status('human: ready'); status('human: ready');
document.getElementById('loader').style.display = 'none'; document.getElementById('loader').style.display = 'none';

View File

@ -7,7 +7,10 @@ async function drawGesture(result, canvas, ui) {
for (const [key, val] of Object.entries(result)) { for (const [key, val] of Object.entries(result)) {
if (val.length > 0) { if (val.length > 0) {
const label = `${key}: ${val.join(', ')}`; const label = `${key}: ${val.join(', ')}`;
ctx.fillText(label, 6, i * (ui.baseLineHeight + 24)); ctx.fillStyle = 'black';
ctx.fillText(label, 8, 2 + (i * ui.baseLineHeight));
ctx.fillStyle = ui.baseLabel;
ctx.fillText(label, 6, 0 + (i * ui.baseLineHeight));
i += 1; i += 1;
} }
} }
@ -29,6 +32,7 @@ async function drawFace(result, canvas, ui, triangulation) {
const labels = []; const labels = [];
// labels.push(`${Math.trunc(100 * face.confidence)}% face`); // labels.push(`${Math.trunc(100 * face.confidence)}% face`);
if (face.genderConfidence) labels.push(`${Math.trunc(100 * face.genderConfidence)}% ${face.gender || ''}`); if (face.genderConfidence) labels.push(`${Math.trunc(100 * face.genderConfidence)}% ${face.gender || ''}`);
// if (face.genderConfidence) labels.push(face.gender);
if (face.age) labels.push(`age: ${face.age || ''}`); if (face.age) labels.push(`age: ${face.age || ''}`);
if (face.iris) labels.push(`iris: ${face.iris}`); if (face.iris) labels.push(`iris: ${face.iris}`);
if (face.emotion && face.emotion.length > 0) { if (face.emotion && face.emotion.length > 0) {
@ -36,7 +40,13 @@ async function drawFace(result, canvas, ui, triangulation) {
labels.push(emotion.join(' ')); labels.push(emotion.join(' '));
} }
ctx.fillStyle = ui.baseLabel; ctx.fillStyle = ui.baseLabel;
for (const i in labels) ctx.fillText(labels[i], face.box[0] + 8, face.box[1] + 24 + ((i + 1) * ui.baseLineHeight)); for (let i = 0; i < labels.length; i++) {
ctx.fillStyle = 'black';
ctx.fillText(labels[i], face.box[0] + face.box[2] + 9, ((i + 1) * ui.baseLineHeight) + face.box[1] + 9);
ctx.fillStyle = ui.baseLabel;
ctx.fillText(labels[i], face.box[0] + face.box[2] + 8, ((i + 1) * ui.baseLineHeight) + face.box[1] + 8);
}
ctx.fillStyle = ui.baseColor;
ctx.stroke(); ctx.stroke();
ctx.lineWidth = 1; ctx.lineWidth = 1;
if (face.mesh) { if (face.mesh) {
@ -186,8 +196,10 @@ async function drawHand(result, canvas, ui) {
ctx.strokeStyle = ui.baseColor; ctx.strokeStyle = ui.baseColor;
ctx.fillStyle = ui.baseColor; ctx.fillStyle = ui.baseColor;
ctx.rect(hand.box[0], hand.box[1], hand.box[2], hand.box[3]); ctx.rect(hand.box[0], hand.box[1], hand.box[2], hand.box[3]);
ctx.fillStyle = 'black';
ctx.fillText('hand', hand.box[0] + 3, 1 + hand.box[1] + ui.baseLineHeight, hand.box[2]);
ctx.fillStyle = ui.baseLabel; ctx.fillStyle = ui.baseLabel;
ctx.fillText('hand', hand.box[0] + 2, hand.box[1] + 22, hand.box[2]); ctx.fillText('hand', hand.box[0] + 2, 0 + hand.box[1] + ui.baseLineHeight, hand.box[2]);
ctx.stroke(); ctx.stroke();
} }
if (ui.drawPoints) { if (ui.drawPoints) {
@ -222,11 +234,10 @@ async function drawHand(result, canvas, ui) {
} }
} }
const draw = { // eslint-disable-next-line import/prefer-default-export
export default {
face: drawFace, face: drawFace,
body: drawBody, body: drawBody,
hand: drawHand, hand: drawHand,
gesture: drawGesture, gesture: drawGesture,
}; };
export default draw;

View File

@ -1,9 +1,11 @@
// modified based on: https://github.com/munrocket/gl-bench /* eslint-disable max-len */
// based on: https://github.com/munrocket/gl-bench
const UICSS = ` const UICSS = `
#gl-bench { position: absolute; right: 1rem; bottom: 1rem; z-index:1000; -webkit-user-select: none; -moz-user-select: none; user-select: none; } #gl-bench { position: absolute; right: 1rem; bottom: 1rem; z-index:1000; -webkit-user-select: none; -moz-user-select: none; user-select: none; }
#gl-bench div { position: relative; display: block; margin: 4px; padding: 0 7px 0 10px; background: darkslategray; border-radius: 0.2rem; cursor: pointer; opacity: 0.9; } #gl-bench div { position: relative; display: block; margin: 4px; padding: 0 7px 0 10px; background: darkslategray; border-radius: 0.2rem; cursor: pointer; opacity: 0.9; }
#gl-bench svg { height: 60px; margin: 0 4px 0px 4px; } #gl-bench svg { height: 60px; margin: 0 0px 0px 4px; }
#gl-bench text { font-size: 16px; font-family: 'Lato', 'Segoe UI'; dominant-baseline: middle; text-anchor: middle; } #gl-bench text { font-size: 16px; font-family: 'Lato', 'Segoe UI'; dominant-baseline: middle; text-anchor: middle; }
#gl-bench .gl-mem { font-size: 12px; fill: white; } #gl-bench .gl-mem { font-size: 12px; fill: white; }
#gl-bench .gl-fps { font-size: 13px; fill: white; } #gl-bench .gl-fps { font-size: 13px; fill: white; }
@ -17,7 +19,7 @@ const UISVG = `
<div class="gl-box"> <div class="gl-box">
<svg viewBox="0 0 55 60"> <svg viewBox="0 0 55 60">
<text x="27" y="56" class="gl-fps">00 FPS</text> <text x="27" y="56" class="gl-fps">00 FPS</text>
<text x="28" y="8" class="gl-mem"></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="55" height="32"></rect>
<polyline class="gl-chart"></polyline> <polyline class="gl-chart"></polyline>
</svg> </svg>
@ -87,21 +89,41 @@ class GLBench {
}); });
}, 0)); }, 0));
const addProfiler = (fn, self, target) => function () { const addProfiler = (fn, self, target) => {
const t = self.now(); const t = self.now();
// eslint-disable-next-line prefer-rest-params // eslint-disable-next-line prefer-rest-params
fn.apply(target, arguments); fn.apply(target, arguments);
if (self.trackGPU) self.finished.push(glFinish(t, self.activeAccums.slice(0))); if (self.trackGPU) self.finished.push(glFinish(t, self.activeAccums.slice(0)));
}; };
['drawArrays', 'drawElements', 'drawArraysInstanced', 'drawBuffers', 'drawElementsInstanced', 'drawRangeElements'].forEach((fn) => { if (gl[fn]) gl[fn] = addProfiler(gl[fn], this, gl); }); /* ['drawArrays', 'drawElements', 'drawArraysInstanced', 'drawBuffers', 'drawElementsInstanced', 'drawRangeElements'].forEach((fn) => {
if (gl[fn]) {
gl[fn] = addProfiler(gl[fn], this, gl);
}
});
*/
const fn = 'drawElements';
if (gl[fn]) {
gl[fn] = addProfiler(gl[fn], this, gl);
} else {
// eslint-disable-next-line no-console
console.log('bench: cannot attach to webgl function');
}
gl.getExtension = ((fn, self) => function () { /*
gl.getExtension = ((fn, self) => {
// eslint-disable-next-line prefer-rest-params // eslint-disable-next-line prefer-rest-params
const ext = fn.apply(gl, arguments); const ext = fn.apply(gl, arguments);
if (ext) ['drawElementsInstancedANGLE', 'drawBuffersWEBGL'].forEach((fn2) => { if (ext[fn2]) ext[fn2] = addProfiler(ext[fn2], self, ext); }); if (ext) {
['drawElementsInstancedANGLE', 'drawBuffersWEBGL'].forEach((fn2) => {
if (ext[fn2]) {
ext[fn2] = addProfiler(ext[fn2], self, ext);
}
});
}
return ext; return ext;
})(gl.getExtension, this); })(gl.getExtension, this);
*/
} }
// init ui and ui loggers // init ui and ui loggers
@ -127,7 +149,7 @@ class GLBench {
nodes['gl-gpu'][i].style.strokeDasharray = (gpu * 0.27).toFixed(0) + ' 100'; nodes['gl-gpu'][i].style.strokeDasharray = (gpu * 0.27).toFixed(0) + ' 100';
// eslint-disable-next-line no-nested-ternary // eslint-disable-next-line no-nested-ternary
nodes['gl-mem'][i].innerHTML = names[i] ? names[i] : (mem ? 'mem: ' + mem.toFixed(0) + 'mb' : ''); nodes['gl-mem'][i].innerHTML = names[i] ? names[i] : (mem ? 'mem: ' + mem.toFixed(0) + 'mb' : '');
nodes['gl-fps'][i].innerHTML = fps.toFixed(0) + ' FPS'; nodes['gl-fps'][i].innerHTML = 'FPS: ' + fps.toFixed(1);
logger(names[i], cpu, gpu, mem, fps, totalTime, frameId); logger(names[i], cpu, gpu, mem, fps, totalTime, frameId);
}; };
})(this.paramLogger, this.dom, this.names); })(this.paramLogger, this.dom, this.names);

View File

@ -19,22 +19,26 @@
<!-- <script src="./browser.js" type="module"></script> --> <!-- <script src="./browser.js" type="module"></script> -->
<style> <style>
@font-face { font-family: 'Lato'; font-display: swap; font-style: normal; font-weight: 400; src: local('Lato'), url('../assets/lato.ttf') format('truetype'); } @font-face { font-family: 'Lato'; font-display: swap; font-style: normal; font-weight: 400; src: local('Lato'), url('../assets/lato.ttf') format('truetype'); }
@font-face { font-family: 'FA'; font-display: swap; font-style: normal; font-weight: 900; src: local('FA'), url('../assets/fa-solid-900.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; scrollbar-width: none; } body { margin: 0; background: black; color: white; overflow-x: hidden; scrollbar-width: none; }
body::-webkit-scrollbar { display: none; } body::-webkit-scrollbar { display: none; }
.play { position: absolute; width: 300px; height: 300px; z-index: 9; top: 30%; left: 50%; margin-left: -150px; display: none; } hr { width: 100%; }
.play-background { fill:darkslategray; cursor:pointer; opacity: 0.6; } .play { position: absolute; width: 250px; height: 250px; z-index: 9; top: 55%; left: 50%; margin-left: -125px; display: none; }
.play-foreground { fill:white; cursor:pointer; opacity: 0.8; } .btn-background { fill:grey; cursor: pointer; opacity: 0.6; }
.play-foreground:hover { opacity: 1; } .btn-background:hover { opacity: 1; }
.btn-foreground { fill:white; cursor: pointer; opacity: 0.8; }
.btn-foreground:hover { opacity: 1; }
.status { position: absolute; width: 100vw; bottom: 15%; text-align: center; font-size: 4rem; font-weight: 100; text-shadow: 2px 2px darkslategrey; } .status { position: absolute; width: 100vw; bottom: 15%; text-align: center; font-size: 4rem; font-weight: 100; text-shadow: 2px 2px darkslategrey; }
.thumbnail { margin: 8px; box-shadow: 0 0 4px 4px dimgrey; } .thumbnail { margin: 8px; box-shadow: 0 0 4px 4px dimgrey; }
.thumbnail:hover { box-shadow: 0 0 8px 8px dimgrey; filter: grayscale(1); } .thumbnail:hover { box-shadow: 0 0 8px 8px dimgrey; filter: grayscale(1); }
.log { position: fixed; bottom: 0; margin: 0.4rem; font-size: 0.9rem; } .log { position: absolute; bottom: 0; margin: 0.4rem; font-size: 0.9rem; }
.menubar { width: 100vw; background: darkslategray; display: flex; justify-content: space-evenly; text-align: center; padding: 8px; cursor: pointer; }
.samples-container { display: flex; flex-wrap: wrap; } .samples-container { display: flex; flex-wrap: wrap; }
.video { display: none; } .video { display: none; }
.canvas { margin: 0 auto; } .canvas { margin: 0 auto; }
.bench { position: absolute; right: 0; bottom: 0; } .bench { position: absolute; right: 0; bottom: 0; }
.compare-image { width: 10vw; position: absolute; top: 150px; left: 30px; box-shadow: 0 0 2px 2px black; background: black; } .compare-image { width: 200px; position: absolute; top: 150px; left: 30px; box-shadow: 0 0 2px 2px black; background: black; }
.loader { width: 300px; height: 300px; border: 3px solid transparent; border-radius: 50%; border-top: 4px solid #f15e41; animation: spin 4s linear infinite; position: absolute; top: 30%; left: 50%; margin-left: -150px; z-index: 15; } .loader { width: 300px; height: 300px; border: 3px solid transparent; border-radius: 50%; border-top: 4px solid #f15e41; animation: spin 4s linear infinite; position: absolute; top: 30%; left: 50%; margin-left: -150px; z-index: 15; }
.loader::before, .loader::after { content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px; right: 6px; border-radius: 50%; border: 4px solid transparent; } .loader::before, .loader::after { content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px; right: 6px; border-radius: 50%; border: 4px solid transparent; }
.loader::before { border-top-color: #bad375; animation: 3s spin linear infinite; } .loader::before { border-top-color: #bad375; animation: 3s spin linear infinite; }
@ -51,13 +55,22 @@
from { transform: rotate(0deg); } from { transform: rotate(0deg); }
from { transform: rotate(360deg); } from { transform: rotate(360deg); }
} }
.button { text-shadow: 2px 2px black; display: flex; }
.button:hover { text-shadow: -2px -2px black; color: lightblue; }
.button::before { display: inline-block; font-style: normal; font-variant: normal; text-rendering: auto; -webkit-font-smoothing: antialiased; font-family: "FA"; font-weight: 900; font-size: 2.4rem; margin-right: 0.4rem; }
.button-display::before { content: "\f8c4"; }
.button-image::before { content: "\f3f2"; }
.button-process::before { content: "\f3f0"; }
.button-model::before { content: "\f2c2"; }
.button-start::before { content: "\f144"; }
.button-stop::before { content: "\f28b"; }
</style> </style>
</head> </head>
<body> <body>
<div id="play" class="play"> <div id="play" class="play">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm115.7 272l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z" class="play-background"/> <path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm115.7 272l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z" class="btn-background"/>
<path d="M371.7 280l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z" class="play-foreground"/> <path d="M371.7 280l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z" class="btn-foreground"/>
</svg> </svg>
</div> </div>
<div id="background"> <div id="background">
@ -67,16 +80,22 @@
</div> </div>
<div id="loader" class="loader"></div> <div id="loader" class="loader"></div>
<div id="status" class="status"></div> <div id="status" class="status"></div>
<div id="menubar" class="menubar">
<span class="button button-display" id="btnDisplay">Display<br>Options</span>
<span class="button button-image" id="btnImage">Image<br>Processing</span>
<span class="button button-process" id="btnProcess">Model<br>Processing</span>
<span class="button button-model" id="btnModel">Model<br>Selection</span>
<span class="button button-start" id="btnStart">Start<br>Video</span>
</div>
<div id="media"> <div id="media">
<canvas id="canvas" class="canvas"></canvas> <canvas id="canvas" class="canvas"></canvas>
<video id="video" playsinline class="video"></video> <video id="video" playsinline class="video"></video>
</div> </div>
<div id="compare-container" style="display: none" class="compare-image"> <div id="compare-container" style="display: none" class="compare-image">
<img id="sample-image" style="width: 100%" src="../assets/sample-me.jpg"></img> <canvas id="compare-canvas" width="200px" height="200px"></canvas>
<div id="simmilarity"></div> <div id="simmilarity"></div>
</div> </div>
<div id="samples-container" class="samples-container"></div> <div id="samples-container" class="samples-container"></div>
<canvas id="bench-canvas" class="bench"></canvas>
<div id="log" class="log"></div> <div id="log" class="log"></div>
</body> </body>
</html> </html>

View File

@ -19,15 +19,15 @@ function createCSS() {
if (CSScreated) return; if (CSScreated) return;
const css = ` const css = `
:root { --rounded: 0.2rem; } :root { --rounded: 0.2rem; }
.menu { position: absolute; top: 0rem; right: 0; width: fit-content; padding: 0 0.8rem 0 0.8rem; line-height: 1.8rem; z-index: 10; .menu { position: absolute; top: 0rem; right: 0; width: fit-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; } box-shadow: 0 0 8px dimgrey; background: ${theme.background}; border-radius: var(--rounded); border-color: black; border-style: solid; border-width: thin; }
.menu:hover { box-shadow: 0 0 8px ${theme.hover}; } .menu:hover { box-shadow: 0 0 8px ${theme.hover}; }
.menu-container { display: block; max-height: 100vh; } .menu-container { display: block; max-height: 100vh; }
.menu-container-fadeout { max-height: 0; overflow: hidden; transition: max-height, 0.5s ease; } .menu-container-fadeout { max-height: 0; overflow: hidden; transition: max-height, 0.5s ease; }
.menu-container-fadein { max-height: 100vh; overflow: hidden; transition: max-height, 0.5s ease; } .menu-container-fadein { max-height: 100vh; overflow: hidden; transition: max-height, 0.5s ease; }
.menu-item { display: flex; white-space: nowrap; padding: 0.2rem; width: max-content; cursor: default; } .menu-item { display: flex; white-space: nowrap; padding: 0.2rem; cursor: default; width: 100%; }
.menu-title { text-align: right; cursor: pointer; } .menu-title { cursor: pointer; }
.menu-hr { margin: 0.2rem; border: 1px solid rgba(0, 0, 0, 0.5) } .menu-hr { margin: 0.2rem; border: 1px solid rgba(0, 0, 0, 0.5) }
.menu-label { padding: 0; font-weight: 800; } .menu-label { padding: 0; font-weight: 800; }
@ -39,12 +39,12 @@ function createCSS() {
.menu-chart-title { padding: 0; font-size: 0.8rem; font-weight: 800; align-items: center} .menu-chart-title { padding: 0; font-size: 0.8rem; font-weight: 800; align-items: center}
.menu-chart-canvas { background: transparent; margin: 0.2rem 0 0.2rem 0.6rem; } .menu-chart-canvas { background: transparent; margin: 0.2rem 0 0.2rem 0.6rem; }
.menu-button { border: 0; background: ${theme.buttonBackground}; width: 100%; padding: 8px; margin: 8px 0 8px 0; cursor: pointer; box-shadow: 4px 4px 4px 0 dimgrey; .menu-button { border: 0; background: ${theme.buttonBackground}; width: -webkit-fill-available; padding: 8px; margin: 8px; cursor: pointer; box-shadow: 4px 4px 4px 0 dimgrey;
border-radius: var(--rounded); justify-content: center; font-family: inherit; font-variant: inherit; font-size: 1rem; font-weight: 800; } border-radius: var(--rounded); justify-content: center; font-family: inherit; font-variant: inherit; font-size: 1rem; font-weight: 800; }
.menu-button:hover { background: ${theme.buttonHover}; box-shadow: 4px 4px 4px 0 black; } .menu-button:hover { background: ${theme.buttonHover}; box-shadow: 4px 4px 4px 0 black; }
.menu-button:focus { outline: none; } .menu-button:focus { outline: none; }
.menu-checkbox { width: 2.8rem; height: 1rem; background: ${theme.itemBackground}; margin: 0.5rem 0.8rem 0 0; position: relative; border-radius: var(--rounded); } .menu-checkbox { width: 2.8rem; height: 1rem; background: ${theme.itemBackground}; margin: 0.5rem 0.5rem 0 0; position: relative; border-radius: var(--rounded); }
.menu-checkbox:after { content: 'OFF'; color: ${theme.checkboxOff}; position: absolute; right: 0.2rem; top: -0.4rem; font-weight: 800; font-size: 0.5rem; } .menu-checkbox:after { content: 'OFF'; color: ${theme.checkboxOff}; position: absolute; right: 0.2rem; top: -0.4rem; font-weight: 800; font-size: 0.5rem; }
.menu-checkbox:before { content: 'ON'; color: ${theme.checkboxOn}; position: absolute; left: 0.3rem; top: -0.4rem; font-weight: 800; font-size: 0.5rem; } .menu-checkbox:before { content: 'ON'; color: ${theme.checkboxOn}; position: absolute; left: 0.3rem; top: -0.4rem; font-weight: 800; font-size: 0.5rem; }
.menu-checkbox-label { width: 1.3rem; height: 0.8rem; cursor: pointer; position: absolute; top: 0.1rem; left: 0.1rem; z-index: 1; background: ${theme.checkboxOff}; .menu-checkbox-label { width: 1.3rem; height: 0.8rem; cursor: pointer; position: absolute; top: 0.1rem; left: 0.1rem; z-index: 1; background: ${theme.checkboxOff};
@ -53,14 +53,14 @@ function createCSS() {
input[type=checkbox] { visibility: hidden; } input[type=checkbox] { visibility: hidden; }
input[type=checkbox]:checked + label { left: 1.4rem; background: ${theme.checkboxOn}; } input[type=checkbox]:checked + label { left: 1.4rem; background: ${theme.checkboxOn}; }
.menu-range { margin: 0 0.8rem 0 0; width: 5rem; background: transparent; color: ${theme.rangeBackground}; } .menu-range { margin: 0.2rem 0.5rem 0 0; width: 3.5rem; background: transparent; color: ${theme.rangeBackground}; }
.menu-range:before { color: ${theme.rangeLabel}; margin: 0 0.4rem 0 0; font-weight: 800; font-size: 0.6rem; position: relative; top: 0.3rem; content: attr(value); } .menu-range:before { color: ${theme.rangeLabel}; margin: 0 0.4rem 0 0; font-weight: 800; font-size: 0.6rem; position: relative; top: 0.3rem; content: attr(value); }
input[type=range] { -webkit-appearance: none; } input[type=range] { -webkit-appearance: none; }
input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 1rem; cursor: pointer; background: ${theme.itemBackground}; border-radius: var(--rounded); border: 1px; } input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 1rem; cursor: pointer; background: ${theme.itemBackground}; border-radius: var(--rounded); border: 1px; }
input[type=range]::-moz-range-track { width: 100%; height: 1rem; cursor: pointer; background: ${theme.itemBackground}; border-radius: var(--rounded); border: 1px; } input[type=range]::-moz-range-track { width: 100%; height: 1rem; cursor: pointer; background: ${theme.itemBackground}; border-radius: var(--rounded); border: 1px; }
input[type=range]::-webkit-slider-thumb { border: 1px solid #000000; margin-top: 0.05rem; height: 0.9rem; width: 1.5rem; border-radius: var(--rounded); background: ${theme.rangeBackground}; cursor: pointer; -webkit-appearance: none; } input[type=range]::-webkit-slider-thumb { border: 1px solid #000000; margin-top: 0.05rem; height: 0.9rem; width: 1rem; border-radius: var(--rounded); background: ${theme.rangeBackground}; cursor: pointer; -webkit-appearance: none; }
input[type=range]::-moz-range-thumb { border: 1px solid #000000; margin-top: 0.05rem; height: 0.9rem; width: 1.5rem; border-radius: var(--rounded); background: ${theme.rangeBackground}; cursor: pointer; -webkit-appearance: none; } input[type=range]::-moz-range-thumb { border: 1px solid #000000; margin-top: 0.05rem; height: 0.9rem; width: 1rem; border-radius: var(--rounded); background: ${theme.rangeBackground}; cursor: pointer; -webkit-appearance: none; }
.svg-background { fill:darkslategrey; cursor:pointer; opacity: 0.6; } .svg-background { fill:darkslategrey; cursor:pointer; opacity: 0.6; }
.svg-foreground { fill:white; cursor:pointer; opacity: 0.8; } .svg-foreground { fill:white; cursor:pointer; opacity: 0.8; }
@ -106,7 +106,7 @@ class Menu {
<path d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zm-51.37 182.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" class="svg-background"/> <path d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zm-51.37 182.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" class="svg-background"/>
<path d="M348.63 214.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" class="svg-foreground"/> <path d="M348.63 214.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" class="svg-foreground"/>
</svg>`; </svg>`;
elTitle.innerHTML = `${title}${svg}`; if (title) elTitle.innerHTML = `${title}${svg}`;
this.menu.appendChild(elTitle); this.menu.appendChild(elTitle);
elTitle.addEventListener('click', () => { elTitle.addEventListener('click', () => {
this.container.classList.toggle('menu-container-fadeout'); this.container.classList.toggle('menu-container-fadeout');
@ -152,9 +152,9 @@ class Menu {
this.container.classList.toggle('menu-container-fadein'); this.container.classList.toggle('menu-container-fadein');
if (this.container.classList.contains('menu-container-fadein') && evt) { if (this.container.classList.contains('menu-container-fadein') && evt) {
const x = evt.x || (evt.touches && evt.touches[0] ? evt.touches[0].pageX : null); const x = evt.x || (evt.touches && evt.touches[0] ? evt.touches[0].pageX : null);
const y = evt.y || (evt.touches && evt.touches[0] ? evt.touches[0].pageY : null); // const y = evt.y || (evt.touches && evt.touches[0] ? evt.touches[0].pageY : null);
if (x) this.menu.style.left = `${x - 105}px`; if (x) this.menu.style.left = `${x - (this.menu.offsetWidth / 2)}px`;
if (y) this.menu.style.top = '5.5rem'; // `${evt.y + 55}px`; // if (y) this.menu.style.top = '5.5rem'; // `${evt.y + 55}px`;
if (this.menu.offsetLeft < 0) this.menu.style.left = 0; if (this.menu.offsetLeft < 0) this.menu.style.left = 0;
if ((this.menu.offsetLeft + this.menu.offsetWidth) > window.innerWidth) { if ((this.menu.offsetLeft + this.menu.offsetWidth) > window.innerWidth) {
this.menu.style.left = null; this.menu.style.left = null;
@ -279,7 +279,7 @@ class Menu {
else this.addValue(title, val); else this.addValue(title, val);
} }
addChart(title, id, width = 200, height = 40, color) { addChart(title, id, width = 150, height = 40, color) {
if (color) theme.chartColor = color; if (color) theme.chartColor = color;
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'menu-item menu-chart-title'; el.className = 'menu-item menu-chart-title';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,7 @@
{ {
"inputs": { "inputs": {
"assets/gl-bench.js": {
"bytes": 10410,
"imports": []
},
"demo/browser.js": { "demo/browser.js": {
"bytes": 23089, "bytes": 24755,
"imports": [ "imports": [
{ {
"path": "dist/human.esm.js" "path": "dist/human.esm.js"
@ -17,16 +13,20 @@
"path": "demo/menu.js" "path": "demo/menu.js"
}, },
{ {
"path": "assets/gl-bench.js" "path": "demo/gl-bench.js"
} }
] ]
}, },
"demo/draw.js": { "demo/draw.js": {
"bytes": 9814, "bytes": 10436,
"imports": []
},
"demo/gl-bench.js": {
"bytes": 10782,
"imports": [] "imports": []
}, },
"demo/menu.js": { "demo/menu.js": {
"bytes": 13814, "bytes": 13842,
"imports": [] "imports": []
}, },
"dist/human.esm.js": { "dist/human.esm.js": {
@ -38,29 +38,29 @@
"dist/demo-browser-index.js.map": { "dist/demo-browser-index.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 2671871 "bytes": 2675872
}, },
"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": 1776267 "bytesInOutput": 1776263
}, },
"demo/draw.js": { "demo/draw.js": {
"bytesInOutput": 7284 "bytesInOutput": 7668
}, },
"demo/menu.js": { "demo/menu.js": {
"bytesInOutput": 11921 "bytesInOutput": 11838
}, },
"assets/gl-bench.js": { "demo/gl-bench.js": {
"bytesInOutput": 7731 "bytesInOutput": 7436
}, },
"demo/browser.js": { "demo/browser.js": {
"bytesInOutput": 16997 "bytesInOutput": 19015
} }
}, },
"bytes": 1827465 "bytes": 1829485
} }
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
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

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
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

View File

@ -116,8 +116,8 @@ class Human {
if (userConfig) this.config = mergeDeep(this.config, userConfig); if (userConfig) this.config = mergeDeep(this.config, userConfig);
if (this.firstRun) { if (this.firstRun) {
this.checkBackend(true);
this.log(`version: ${this.version} TensorFlow/JS version: ${tf.version_core}`); this.log(`version: ${this.version} TensorFlow/JS version: ${tf.version_core}`);
this.checkBackend(true);
this.log('configuration:', this.config); this.log('configuration:', this.config);
this.log('flags:', tf.ENV.flags); this.log('flags:', tf.ENV.flags);
this.firstRun = false; this.firstRun = false;