2020-10-19 17:03:48 +02:00
|
|
|
import Human from '../dist/human.esm.js';
|
2020-10-17 13:34:45 +02:00
|
|
|
import draw from './draw.js';
|
2020-10-18 02:59:43 +02:00
|
|
|
import Menu from './menu.js';
|
2020-10-12 16:08:00 +02:00
|
|
|
|
2020-10-19 17:03:48 +02:00
|
|
|
const human = new Human();
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// ui options
|
2020-10-13 15:59:21 +02:00
|
|
|
const ui = {
|
2020-11-03 04:15:37 +01:00
|
|
|
baseColor: 'rgba(173, 216, 230, 0.3)', // 'lightblue' with light alpha channel
|
|
|
|
baseBackground: 'rgba(50, 50, 50, 1)', // 'grey'
|
|
|
|
baseLabel: 'rgba(173, 216, 230, 0.9)', // 'lightblue' with dark alpha channel
|
2020-10-17 13:15:23 +02:00
|
|
|
baseFontProto: 'small-caps {size} "Segoe UI"',
|
2020-10-13 15:59:21 +02:00
|
|
|
baseLineWidth: 16,
|
2020-10-17 13:15:23 +02:00
|
|
|
baseLineHeightProto: 2,
|
2020-10-18 02:59:43 +02:00
|
|
|
columns: 2,
|
2020-10-16 16:12:12 +02:00
|
|
|
busy: false,
|
2020-10-18 02:59:43 +02:00
|
|
|
facing: true,
|
2020-10-17 13:34:45 +02:00
|
|
|
useWorker: false,
|
2020-10-17 12:30:00 +02:00
|
|
|
worker: 'worker.js',
|
2020-10-18 02:59:43 +02:00
|
|
|
samples: ['../assets/sample6.jpg', '../assets/sample1.jpg', '../assets/sample4.jpg', '../assets/sample5.jpg', '../assets/sample3.jpg', '../assets/sample2.jpg'],
|
2020-10-17 13:34:45 +02:00
|
|
|
drawBoxes: true,
|
|
|
|
drawPoints: false,
|
|
|
|
drawPolygons: true,
|
|
|
|
fillPolygons: true,
|
|
|
|
useDepth: true,
|
2020-10-17 16:06:02 +02:00
|
|
|
console: true,
|
2020-10-18 02:59:43 +02:00
|
|
|
maxFrames: 10,
|
2020-10-13 15:59:21 +02:00
|
|
|
};
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// configuration overrides
|
2020-10-12 16:08:00 +02:00
|
|
|
const config = {
|
2020-10-30 15:23:49 +01:00
|
|
|
backend: 'webgl',
|
2020-11-01 19:07:53 +01:00
|
|
|
profile: false,
|
|
|
|
deallocate: false,
|
2020-10-30 15:23:49 +01:00
|
|
|
wasm: { path: '../assets' },
|
2020-10-27 15:06:01 +01:00
|
|
|
filter: {
|
|
|
|
enabled: true,
|
2020-10-27 15:30:28 +01:00
|
|
|
width: 0,
|
2020-10-27 15:06:01 +01:00
|
|
|
height: 0,
|
|
|
|
brightness: 0,
|
|
|
|
contrast: 0,
|
|
|
|
sharpness: 0,
|
|
|
|
blur: 0,
|
|
|
|
saturation: 0,
|
|
|
|
hue: 0,
|
|
|
|
negative: false,
|
|
|
|
sepia: false,
|
|
|
|
vintage: false,
|
|
|
|
kodachrome: false,
|
|
|
|
technicolor: false,
|
|
|
|
polaroid: false,
|
|
|
|
pixelate: 0 },
|
2020-10-18 20:14:05 +02:00
|
|
|
videoOptimized: true,
|
2020-10-12 16:08:00 +02:00
|
|
|
face: {
|
2020-10-15 00:22:38 +02:00
|
|
|
enabled: true,
|
2020-10-14 17:43:33 +02:00
|
|
|
detector: { maxFaces: 10, skipFrames: 10, minConfidence: 0.5, iouThreshold: 0.3, scoreThreshold: 0.7 },
|
2020-10-13 04:03:55 +02:00
|
|
|
mesh: { enabled: true },
|
2020-10-16 17:23:59 +02:00
|
|
|
iris: { enabled: true },
|
2020-10-16 02:29:51 +02:00
|
|
|
age: { enabled: true, skipFrames: 10 },
|
|
|
|
gender: { enabled: true },
|
|
|
|
emotion: { enabled: true, minConfidence: 0.5, useGrayscale: true },
|
2020-10-12 16:08:00 +02:00
|
|
|
},
|
2020-10-16 21:04:51 +02:00
|
|
|
body: { enabled: true, maxDetections: 10, scoreThreshold: 0.7, nmsRadius: 20 },
|
|
|
|
hand: { enabled: true, skipFrames: 10, minConfidence: 0.5, iouThreshold: 0.3, scoreThreshold: 0.7 },
|
2020-10-12 16:08:00 +02:00
|
|
|
};
|
2020-10-17 16:06:02 +02:00
|
|
|
|
|
|
|
// global variables
|
2020-10-18 02:59:43 +02:00
|
|
|
let menu;
|
2020-10-18 18:12:09 +02:00
|
|
|
let menuFX;
|
2020-10-15 15:43:16 +02:00
|
|
|
let worker;
|
|
|
|
let timeStamp;
|
2020-10-16 00:16:05 +02:00
|
|
|
const fps = [];
|
2020-10-12 16:08:00 +02:00
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// helper function: translates json to human readable string
|
2020-10-15 00:22:38 +02:00
|
|
|
function str(...msg) {
|
|
|
|
if (!Array.isArray(msg)) return msg;
|
|
|
|
let line = '';
|
|
|
|
for (const entry of msg) {
|
|
|
|
if (typeof entry === 'object') line += JSON.stringify(entry).replace(/{|}|"|\[|\]/g, '').replace(/,/g, ', ');
|
|
|
|
else line += entry;
|
|
|
|
}
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// helper function: wrapper around console output
|
2020-10-15 21:25:58 +02:00
|
|
|
const log = (...msg) => {
|
|
|
|
// eslint-disable-next-line no-console
|
2020-10-17 16:06:02 +02:00
|
|
|
if (ui.console) console.log(...msg);
|
2020-10-15 21:25:58 +02:00
|
|
|
};
|
2020-10-15 14:16:34 +02:00
|
|
|
|
2020-11-03 04:15:37 +01:00
|
|
|
const status = (msg) => {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
document.getElementById('status').innerText = msg;
|
|
|
|
};
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// draws processed results and starts processing of a next frame
|
2020-10-18 02:59:43 +02:00
|
|
|
function drawResults(input, result, canvas) {
|
2020-11-03 00:54:03 +01:00
|
|
|
// update fps data
|
2020-10-16 00:16:05 +02:00
|
|
|
fps.push(1000 / (performance.now() - timeStamp));
|
2020-10-18 02:59:43 +02:00
|
|
|
if (fps.length > ui.maxFrames) fps.shift();
|
2020-11-03 00:54:03 +01:00
|
|
|
|
|
|
|
// enable for continous performance monitoring
|
|
|
|
// console.log(result.performance);
|
2020-10-16 00:16:05 +02:00
|
|
|
|
|
|
|
// eslint-disable-next-line no-use-before-define
|
2020-11-03 00:54:03 +01:00
|
|
|
requestAnimationFrame(() => runHumanDetect(input, canvas)); // immediate loop before we even draw results
|
2020-10-16 00:16:05 +02:00
|
|
|
|
2020-11-03 00:54:03 +01:00
|
|
|
// draw fps chart
|
|
|
|
menu.updateChart('FPS', fps);
|
2020-10-15 15:43:16 +02:00
|
|
|
// draw image from video
|
|
|
|
const ctx = canvas.getContext('2d');
|
2020-10-27 15:06:01 +01:00
|
|
|
ctx.fillStyle = ui.baseBackground;
|
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
if (result.canvas) ctx.drawImage(result.canvas, 0, 0, result.canvas.width, result.canvas.height, 0, 0, result.canvas.width, result.canvas.height);
|
2020-10-18 18:12:09 +02:00
|
|
|
else ctx.drawImage(input, 0, 0, input.width, input.height, 0, 0, canvas.width, canvas.height);
|
2020-10-15 15:43:16 +02:00
|
|
|
// draw all results
|
2020-10-17 13:34:45 +02:00
|
|
|
draw.face(result.face, canvas, ui, human.facemesh.triangulation);
|
|
|
|
draw.body(result.body, canvas, ui);
|
|
|
|
draw.hand(result.hand, canvas, ui);
|
2020-10-15 15:43:16 +02:00
|
|
|
// update log
|
2020-10-18 02:59:43 +02:00
|
|
|
const engine = human.tf.engine();
|
2020-10-15 15:43:16 +02:00
|
|
|
const memory = `${engine.state.numBytes.toLocaleString()} bytes ${engine.state.numDataBuffers.toLocaleString()} buffers ${engine.state.numTensors.toLocaleString()} tensors`;
|
2020-10-30 15:23:49 +01:00
|
|
|
const gpu = engine.backendInstance ? `GPU: ${(engine.backendInstance.numBytesInGPU ? engine.backendInstance.numBytesInGPU : 0).toLocaleString()} bytes` : '';
|
2020-10-15 21:25:58 +02:00
|
|
|
document.getElementById('log').innerText = `
|
|
|
|
TFJS Version: ${human.tf.version_core} | Backend: ${human.tf.getBackend()} | Memory: ${memory} ${gpu}
|
2020-10-15 15:43:16 +02:00
|
|
|
Performance: ${str(result.performance)} | Object size: ${(str(result)).length.toLocaleString()} bytes
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// setup webcam
|
|
|
|
async function setupCamera() {
|
|
|
|
if (ui.busy) return null;
|
|
|
|
ui.busy = true;
|
|
|
|
const video = document.getElementById('video');
|
|
|
|
const canvas = document.getElementById('canvas');
|
|
|
|
const output = document.getElementById('log');
|
|
|
|
const live = video.srcObject ? ((video.srcObject.getVideoTracks()[0].readyState === 'live') && (video.readyState > 2) && (!video.paused)) : false;
|
2020-10-18 02:59:43 +02:00
|
|
|
let msg = `Setting up camera: live: ${live} facing: ${ui.facing ? 'front' : 'back'}`;
|
2020-11-03 04:15:37 +01:00
|
|
|
status('starting camera');
|
2020-10-17 16:25:27 +02:00
|
|
|
output.innerText += `\n${msg}`;
|
|
|
|
log(msg);
|
2020-10-17 16:06:02 +02:00
|
|
|
// setup webcam. note that navigator.mediaDevices requires that page is accessed via https
|
|
|
|
if (!navigator.mediaDevices) {
|
2020-11-03 04:15:37 +01:00
|
|
|
msg = 'camera access not supported';
|
2020-10-17 16:25:27 +02:00
|
|
|
output.innerText += `\n${msg}`;
|
2020-10-17 16:06:02 +02:00
|
|
|
log(msg);
|
2020-11-03 04:15:37 +01:00
|
|
|
status(msg);
|
2020-10-17 16:06:02 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
let stream;
|
|
|
|
try {
|
|
|
|
stream = await navigator.mediaDevices.getUserMedia({
|
|
|
|
audio: false,
|
2020-10-18 02:59:43 +02:00
|
|
|
video: { facingMode: (ui.facing ? 'user' : 'environment'), width: window.innerWidth, height: window.innerHeight },
|
2020-10-17 16:06:02 +02:00
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
output.innerText += '\nCamera permission denied';
|
2020-11-03 04:15:37 +01:00
|
|
|
status('camera permission denied');
|
2020-10-17 16:06:02 +02:00
|
|
|
log(err);
|
|
|
|
}
|
|
|
|
if (stream) video.srcObject = stream;
|
|
|
|
else return null;
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
video.onloadeddata = async () => {
|
|
|
|
video.width = video.videoWidth;
|
|
|
|
video.height = video.videoHeight;
|
2020-10-27 15:06:01 +01:00
|
|
|
canvas.width = video.width;
|
|
|
|
canvas.height = video.height;
|
2020-10-17 16:06:02 +02:00
|
|
|
if (live) video.play();
|
|
|
|
ui.busy = false;
|
|
|
|
// do once more because onresize events can be delayed or skipped
|
2020-10-17 16:25:27 +02:00
|
|
|
if (video.width > window.innerWidth) await setupCamera();
|
|
|
|
output.innerText += `\nCamera resolution: ${video.width} x ${video.height}`;
|
2020-10-17 16:06:02 +02:00
|
|
|
resolve(video);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrapper for worker.postmessage that creates worker if one does not exist
|
2020-10-16 00:16:05 +02:00
|
|
|
function webWorker(input, image, canvas) {
|
2020-10-15 15:43:16 +02:00
|
|
|
if (!worker) {
|
2020-10-16 00:16:05 +02:00
|
|
|
// create new webworker and add event handler only once
|
2020-10-15 21:25:58 +02:00
|
|
|
log('Creating worker thread');
|
2020-10-17 12:30:00 +02:00
|
|
|
worker = new Worker(ui.worker, { type: 'module' });
|
2020-10-15 15:43:16 +02:00
|
|
|
// after receiving message from webworker, parse&draw results and send new frame for processing
|
2020-10-16 02:20:37 +02:00
|
|
|
worker.addEventListener('message', (msg) => drawResults(input, msg.data, canvas));
|
2020-10-15 15:43:16 +02:00
|
|
|
}
|
2020-10-16 00:16:05 +02:00
|
|
|
// pass image data as arraybuffer to worker by reference to avoid copy
|
|
|
|
worker.postMessage({ image: image.data.buffer, width: canvas.width, height: canvas.height, config }, [image.data.buffer]);
|
2020-10-15 15:43:16 +02:00
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// main processing function when input is webcam, can use direct invocation or web worker
|
2020-10-18 02:59:43 +02:00
|
|
|
function runHumanDetect(input, canvas) {
|
2020-10-15 15:43:16 +02:00
|
|
|
timeStamp = performance.now();
|
2020-10-13 15:59:21 +02:00
|
|
|
// perform detect if live video or not video at all
|
2020-10-16 16:12:12 +02:00
|
|
|
if (input.srcObject) {
|
|
|
|
// if video not ready, just redo
|
|
|
|
const live = (input.srcObject.getVideoTracks()[0].readyState === 'live') && (input.readyState > 2) && (!input.paused);
|
|
|
|
if (!live) {
|
2020-11-03 15:40:04 +01:00
|
|
|
if (!input.paused) {
|
|
|
|
log(`Video not ready: state: ${input.srcObject.getVideoTracks()[0].readyState} stream state: ${input.readyState}`);
|
|
|
|
setTimeout(() => runHumanDetect(input, canvas), 500);
|
|
|
|
}
|
2020-10-16 16:12:12 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
if (ui.useWorker) {
|
2020-10-15 15:43:16 +02:00
|
|
|
// get image data from video as we cannot send html objects to webworker
|
|
|
|
const offscreen = new OffscreenCanvas(canvas.width, canvas.height);
|
|
|
|
const ctx = offscreen.getContext('2d');
|
|
|
|
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);
|
2020-10-16 00:16:05 +02:00
|
|
|
// perform detection in worker
|
|
|
|
webWorker(input, data, canvas);
|
2020-10-15 15:43:16 +02:00
|
|
|
} else {
|
2020-10-18 02:59:43 +02:00
|
|
|
human.detect(input, config).then((result) => {
|
|
|
|
if (result.error) log(result.error);
|
|
|
|
else drawResults(input, result, canvas);
|
2020-11-01 19:07:53 +01:00
|
|
|
if (config.profile) log('Profile data:', human.profile());
|
2020-10-18 02:59:43 +02:00
|
|
|
});
|
2020-10-13 15:59:21 +02:00
|
|
|
}
|
2020-10-12 16:08:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// main processing function when input is image, can use direct invocation or web worker
|
2020-10-16 21:04:51 +02:00
|
|
|
async function processImage(input) {
|
2020-10-18 02:59:43 +02:00
|
|
|
timeStamp = performance.now();
|
2020-10-13 15:59:21 +02:00
|
|
|
return new Promise((resolve) => {
|
2020-11-03 15:34:36 +01:00
|
|
|
const image = new Image();
|
2020-10-16 21:04:51 +02:00
|
|
|
image.onload = async () => {
|
|
|
|
log('Processing image:', image.src);
|
|
|
|
const canvas = document.getElementById('canvas');
|
|
|
|
image.width = image.naturalWidth;
|
|
|
|
image.height = image.naturalHeight;
|
2020-10-27 15:06:01 +01:00
|
|
|
canvas.width = config.filter.width && config.filter.width > 0 ? config.filter.width : image.naturalWidth;
|
|
|
|
canvas.height = config.filter.height && config.filter.height > 0 ? config.filter.height : image.naturalHeight;
|
2020-10-18 02:59:43 +02:00
|
|
|
const result = await human.detect(image, config);
|
|
|
|
drawResults(image, result, canvas);
|
2020-10-16 21:04:51 +02:00
|
|
|
const thumb = document.createElement('canvas');
|
2020-11-03 15:34:36 +01:00
|
|
|
thumb.className = 'thumbnail';
|
2020-10-18 18:12:09 +02:00
|
|
|
thumb.width = window.innerWidth / (ui.columns + 0.1);
|
2020-10-16 21:04:51 +02:00
|
|
|
thumb.height = canvas.height / (window.innerWidth / thumb.width);
|
|
|
|
const ctx = thumb.getContext('2d');
|
|
|
|
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, thumb.width, thumb.height);
|
2020-11-03 15:34:36 +01:00
|
|
|
document.getElementById('samples-container').appendChild(thumb);
|
2020-10-16 21:04:51 +02:00
|
|
|
image.src = '';
|
|
|
|
resolve(true);
|
|
|
|
};
|
|
|
|
image.src = input;
|
2020-10-13 15:59:21 +02:00
|
|
|
});
|
2020-10-12 16:59:55 +02:00
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// just initialize everything and call main function
|
2020-10-17 13:15:23 +02:00
|
|
|
async function detectVideo() {
|
2020-11-01 20:16:47 +01:00
|
|
|
config.videoOptimized = true;
|
2020-11-03 15:34:36 +01:00
|
|
|
document.getElementById('samples-container').style.display = 'none';
|
2020-10-17 13:15:23 +02:00
|
|
|
document.getElementById('canvas').style.display = 'block';
|
|
|
|
const video = document.getElementById('video');
|
|
|
|
const canvas = document.getElementById('canvas');
|
|
|
|
ui.baseFont = ui.baseFontProto.replace(/{size}/, '1.2rem');
|
|
|
|
ui.baseLineHeight = ui.baseLineHeightProto;
|
2020-10-17 16:25:27 +02:00
|
|
|
if ((video.srcObject !== null) && !video.paused) {
|
2020-11-03 04:15:37 +01:00
|
|
|
document.getElementById('play').style.display = 'block';
|
|
|
|
status('paused');
|
2020-10-17 13:15:23 +02:00
|
|
|
video.pause();
|
|
|
|
} else {
|
|
|
|
await setupCamera();
|
2020-11-03 04:15:37 +01:00
|
|
|
document.getElementById('play').style.display = 'none';
|
|
|
|
status('');
|
2020-10-17 13:15:23 +02:00
|
|
|
video.play();
|
|
|
|
}
|
|
|
|
runHumanDetect(video, canvas);
|
|
|
|
}
|
|
|
|
|
2020-10-17 16:06:02 +02:00
|
|
|
// just initialize everything and call main function
|
2020-10-16 21:04:51 +02:00
|
|
|
async function detectSampleImages() {
|
2020-11-03 04:15:37 +01:00
|
|
|
document.getElementById('play').style.display = 'none';
|
2020-11-01 20:16:47 +01:00
|
|
|
config.videoOptimized = false;
|
2020-10-18 14:07:45 +02:00
|
|
|
ui.baseFont = ui.baseFontProto.replace(/{size}/, `${1.2 * ui.columns}rem`);
|
2020-10-17 13:15:23 +02:00
|
|
|
ui.baseLineHeight = ui.baseLineHeightProto * ui.columns;
|
2020-10-16 21:04:51 +02:00
|
|
|
document.getElementById('canvas').style.display = 'none';
|
2020-11-03 15:34:36 +01:00
|
|
|
document.getElementById('samples-container').style.display = 'block';
|
2020-10-16 21:04:51 +02:00
|
|
|
log('Running detection of sample images');
|
2020-11-03 15:34:36 +01:00
|
|
|
status('processing images');
|
|
|
|
document.getElementById('samples-container').innerHTML = '';
|
2020-10-17 13:15:23 +02:00
|
|
|
for (const sample of ui.samples) await processImage(sample);
|
2020-11-03 15:34:36 +01:00
|
|
|
status('');
|
2020-10-17 13:15:23 +02:00
|
|
|
}
|
|
|
|
|
2020-10-18 02:59:43 +02:00
|
|
|
function setupMenu() {
|
2020-10-18 18:12:09 +02:00
|
|
|
menu = new Menu(document.body, '...', { top: '1rem', right: '1rem' });
|
2020-11-03 04:15:37 +01:00
|
|
|
const btn = menu.addButton('Start Video', 'Pause Video', () => detectVideo());
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addButton('Process Images', 'Process Images', () => detectSampleImages());
|
2020-11-03 04:15:37 +01:00
|
|
|
document.getElementById('play').addEventListener('click', () => btn.click());
|
2020-10-18 02:59:43 +02:00
|
|
|
|
2020-10-18 18:12:09 +02:00
|
|
|
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
2020-10-30 15:23:49 +01:00
|
|
|
menu.addList('Backend', ['cpu', 'webgl', 'wasm', 'webgpu'], config.backend, (val) => config.backend = val);
|
2020-11-01 19:07:53 +01:00
|
|
|
menu.addBool('Enable Profiler', config, 'profile');
|
|
|
|
menu.addBool('Memory Deallocator', config, 'deallocate');
|
2020-10-18 18:12:09 +02:00
|
|
|
menu.addBool('Use Web Worker', ui, 'useWorker');
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
|
|
|
menu.addLabel('Enabled Models');
|
|
|
|
menu.addBool('Face Detect', config.face, 'enabled');
|
|
|
|
menu.addBool('Face Mesh', config.face.mesh, 'enabled');
|
|
|
|
menu.addBool('Face Iris', config.face.iris, 'enabled');
|
|
|
|
menu.addBool('Face Age', config.face.age, 'enabled');
|
|
|
|
menu.addBool('Face Gender', config.face.gender, 'enabled');
|
|
|
|
menu.addBool('Face Emotion', config.face.emotion, 'enabled');
|
|
|
|
menu.addBool('Body Pose', config.body, 'enabled');
|
|
|
|
menu.addBool('Hand Pose', config.hand, 'enabled');
|
|
|
|
|
|
|
|
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
|
|
|
menu.addLabel('Model Parameters');
|
|
|
|
menu.addRange('Max Objects', config.face.detector, 'maxFaces', 0, 50, 1, (val) => {
|
2020-10-17 13:15:23 +02:00
|
|
|
config.face.detector.maxFaces = parseInt(val);
|
|
|
|
config.body.maxDetections = parseInt(val);
|
2020-10-18 02:59:43 +02:00
|
|
|
config.hand.maxHands = parseInt(val);
|
2020-10-17 13:15:23 +02:00
|
|
|
});
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addRange('Skip Frames', config.face.detector, 'skipFrames', 0, 50, 1, (val) => {
|
2020-10-17 13:15:23 +02:00
|
|
|
config.face.detector.skipFrames = parseInt(val);
|
|
|
|
config.face.emotion.skipFrames = parseInt(val);
|
|
|
|
config.face.age.skipFrames = parseInt(val);
|
|
|
|
config.hand.skipFrames = parseInt(val);
|
|
|
|
});
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addRange('Min Confidence', config.face.detector, 'minConfidence', 0.0, 1.0, 0.05, (val) => {
|
2020-10-17 13:15:23 +02:00
|
|
|
config.face.detector.minConfidence = parseFloat(val);
|
|
|
|
config.face.emotion.minConfidence = parseFloat(val);
|
|
|
|
config.hand.minConfidence = parseFloat(val);
|
|
|
|
});
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addRange('Score Threshold', config.face.detector, 'scoreThreshold', 0.1, 1.0, 0.05, (val) => {
|
2020-10-17 13:15:23 +02:00
|
|
|
config.face.detector.scoreThreshold = parseFloat(val);
|
|
|
|
config.hand.scoreThreshold = parseFloat(val);
|
|
|
|
config.body.scoreThreshold = parseFloat(val);
|
|
|
|
});
|
2020-10-18 02:59:43 +02:00
|
|
|
menu.addRange('IOU Threshold', config.face.detector, 'iouThreshold', 0.1, 1.0, 0.05, (val) => {
|
2020-10-17 13:15:23 +02:00
|
|
|
config.face.detector.iouThreshold = parseFloat(val);
|
|
|
|
config.hand.iouThreshold = parseFloat(val);
|
|
|
|
});
|
2020-10-18 02:59:43 +02:00
|
|
|
|
|
|
|
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
|
|
|
menu.addChart('FPS', 'FPS');
|
2020-10-18 18:12:09 +02:00
|
|
|
|
|
|
|
menuFX = new Menu(document.body, '...', { top: '1rem', right: '18rem' });
|
|
|
|
menuFX.addLabel('UI Options');
|
|
|
|
menuFX.addBool('Camera Front/Back', ui, 'facing', () => setupCamera());
|
|
|
|
menuFX.addBool('Use 3D Depth', ui, 'useDepth');
|
|
|
|
menuFX.addBool('Draw Boxes', ui, 'drawBoxes');
|
|
|
|
menuFX.addBool('Draw Points', ui, 'drawPoints');
|
|
|
|
menuFX.addBool('Draw Polygons', ui, 'drawPolygons');
|
|
|
|
menuFX.addBool('Fill Polygons', ui, 'fillPolygons');
|
|
|
|
menuFX.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
|
|
|
menuFX.addLabel('Image Filters');
|
|
|
|
menuFX.addBool('Enabled', config.filter, 'enabled');
|
2020-10-27 19:19:24 +01:00
|
|
|
menuFX.addRange('Image width', config.filter, 'width', 100, 3840, 10, (val) => config.filter.width = parseInt(val));
|
|
|
|
menuFX.addRange('Image height', config.filter, 'height', 100, 2160, 10, (val) => config.filter.height = parseInt(val));
|
|
|
|
menuFX.addRange('Brightness', config.filter, 'brightness', -1.0, 1.0, 0.05, (val) => config.filter.brightness = parseFloat(val));
|
|
|
|
menuFX.addRange('Contrast', config.filter, 'contrast', -1.0, 1.0, 0.05, (val) => config.filter.contrast = parseFloat(val));
|
|
|
|
menuFX.addRange('Sharpness', config.filter, 'sharpness', 0, 1.0, 0.05, (val) => config.filter.sharpness = parseFloat(val));
|
|
|
|
menuFX.addRange('Blur', config.filter, 'blur', 0, 20, 1, (val) => config.filter.blur = parseInt(val));
|
|
|
|
menuFX.addRange('Saturation', config.filter, 'saturation', -1.0, 1.0, 0.05, (val) => config.filter.saturation = parseFloat(val));
|
|
|
|
menuFX.addRange('Hue', config.filter, 'hue', 0, 360, 5, (val) => config.filter.hue = parseInt(val));
|
|
|
|
menuFX.addRange('Pixelate', config.filter, 'pixelate', 0, 32, 1, (val) => config.filter.pixelate = parseInt(val));
|
2020-10-18 18:12:09 +02:00
|
|
|
menuFX.addBool('Negative', config.filter, 'negative');
|
|
|
|
menuFX.addBool('Sepia', config.filter, 'sepia');
|
|
|
|
menuFX.addBool('Vintage', config.filter, 'vintage');
|
|
|
|
menuFX.addBool('Kodachrome', config.filter, 'kodachrome');
|
|
|
|
menuFX.addBool('Technicolor', config.filter, 'technicolor');
|
|
|
|
menuFX.addBool('Polaroid', config.filter, 'polaroid');
|
2020-10-16 21:04:51 +02:00
|
|
|
}
|
|
|
|
|
2020-10-12 16:08:00 +02:00
|
|
|
async function main() {
|
2020-11-03 15:34:36 +01:00
|
|
|
log('Human: demo starting ...');
|
2020-10-18 02:59:43 +02:00
|
|
|
setupMenu();
|
2020-11-03 15:34:36 +01:00
|
|
|
document.getElementById('log').innerText = `Human: version: ${human.version} TensorFlow/JS version: ${human.tf.version_core}`;
|
|
|
|
// this is not required, just pre-warms the library
|
2020-11-03 04:15:37 +01:00
|
|
|
status('loading');
|
|
|
|
await human.load();
|
|
|
|
status('initializing');
|
|
|
|
const warmup = new ImageData(50, 50);
|
|
|
|
await human.detect(warmup);
|
|
|
|
status('human: ready');
|
2020-11-03 15:34:36 +01:00
|
|
|
document.getElementById('loader').style.display = 'none';
|
|
|
|
document.getElementById('play').style.display = 'block';
|
2020-10-12 16:08:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
window.onload = main;
|
2020-10-16 16:12:12 +02:00
|
|
|
window.onresize = setupCamera;
|