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