experimental node-wasm support

pull/280/head
Vladimir Mandic 2021-04-13 21:45:45 -04:00
parent 3618e831cc
commit e231e68d2d
6 changed files with 48 additions and 23 deletions

View File

@ -1,6 +1,6 @@
# @vladmandic/human
Version: **1.5.0**
Version: **1.5.1**
Description: **Human: AI-powered 3D Face Detection & Rotation Tracking, Face Description & Recognition, Body Pose Tracking, 3D Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction, Gesture Recognition**
Author: **Vladimir Mandic <mandic00@live.com>**
@ -9,8 +9,10 @@ Repository: **<git+https://github.com/vladmandic/human.git>**
## Changelog
### **HEAD -> main** 2021/04/13 mandic00@live.com
### **1.5.1** 2021/04/13 mandic00@live.com
- fix for safari imagebitmap
- refactored human.config and human.draw
### **1.4.3** 2021/04/12 mandic00@live.com

10
TODO.md
View File

@ -2,19 +2,15 @@
## Big Ticket Items
- Strong(er) typing
- Automated testing framework
- TypeDoc comments
- Improve automated testing framework
## Explore Models
- EfficientPose
<https://github.com/daniegr/EfficientPose>
<https://github.com/PINTO0309/PINTO_model_zoo/tree/main/084_EfficientPose>
- InsightFace
RetinaFace detetor and ArcFace recognition
RetinaFace detector and ArcFace recognition
<https://github.com/deepinsight/insightface>
## Issues
- box sizing on mobile
- canvas.js for wasm on node

View File

@ -24,7 +24,7 @@
"start": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught --no-deprecation demo/node.js",
"dev": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught server/serve.js",
"build": "rimraf dist/* typedoc/* types/* && node --trace-warnings --unhandled-rejections=strict --trace-uncaught server/build.js",
"lint": "eslint src server demo",
"lint": "eslint src server demo test",
"test": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught --no-deprecation test/test-node.js"
},
"keywords": [

View File

@ -329,13 +329,14 @@ export class Human {
if (this.config.backend && this.config.backend.length > 0) {
// force browser vs node backend
if (this.tf.ENV.flags.IS_BROWSER && this.config.backend === 'tensorflow') this.config.backend = 'webgl';
if (this.tf.ENV.flags.IS_NODE && (this.config.backend === 'webgl' || this.config.backend === 'wasm')) this.config.backend = 'tensorflow';
if (this.tf.ENV.flags.IS_NODE && (this.config.backend === 'webgl' || this.config.backend === 'humangl')) this.config.backend = 'tensorflow';
if (this.config.debug) log('setting backend:', this.config.backend);
if (this.config.backend === 'wasm') {
if (this.config.debug) log('wasm path:', this.config.wasmPath);
this.tf.setWasmPaths(this.config.wasmPath);
if (typeof this.tf?.setWasmPaths !== 'undefined') this.tf.setWasmPaths(this.config.wasmPath);
else throw new Error('Human: WASM backend is not loaded');
const simd = await this.tf.env().getAsync('WASM_HAS_SIMD_SUPPORT');
const mt = await this.tf.env().getAsync('WASM_HAS_MULTITHREAD_SUPPORT');
if (this.config.debug) log(`wasm execution: ${simd ? 'SIMD' : 'no SIMD'} ${mt ? 'multithreaded' : 'singlethreaded'}`);
@ -573,8 +574,16 @@ export class Human {
/** @hidden */
#warmupNode = async () => {
// @ts-ignore
if (typeof tf.node === 'undefined') {
if (this.config.debug) log('Warmup tfjs-node not loaded');
return null;
}
const atob = (str) => Buffer.from(str, 'base64');
const img = this.config.warmup === 'face' ? atob(sample.face) : atob(sample.body);
let img;
if (this.config.warmup === 'face') img = atob(sample.face);
if (this.config.warmup === 'body' || this.config.warmup === 'full') img = atob(sample.body);
if (!img) return null;
// @ts-ignore // tf.node is only defined when compiling for nodejs
const data = tf.node?.decodeJpeg(img);
const expanded = data.expandDims(0);
@ -592,6 +601,7 @@ export class Human {
async warmup(userConfig: Config | Object = {}): Promise<Result | { error }> {
const t0 = now();
if (userConfig) this.config = mergeDeep(this.config, userConfig);
if (!this.config.warmup || this.config.warmup === 'none') return { error: 'null' };
const save = this.config.videoOptimized;
this.config.videoOptimized = false;
let res;

2
src/tfjs/tf-node-wasm.ts Normal file
View File

@ -0,0 +1,2 @@
export * from '@tensorflow/tfjs';
export * from '@tensorflow/tfjs-backend-wasm';

View File

@ -50,36 +50,51 @@ async function testInstance(human) {
log.error('failed: load models');
}
const warmup = await human.warmup();
let warmup;
try {
warmup = await human.warmup();
} catch (err) {
log.error('error warmup');
}
if (warmup) {
log.state('passed: warmup:', config.warmup);
log.data(' result: face:', warmup.face.length, 'body:', warmup.body.length, 'hand:', warmup.hand.length, 'gesture:', warmup.gesture.length, 'object:', warmup.object.length);
log.data(' result: performance:', 'load:', warmup.performance.load, 'total:', warmup.performance.total);
log.data(' result: face:', warmup.face?.length, 'body:', warmup.body?.length, 'hand:', warmup.hand?.length, 'gesture:', warmup.gesture?.length, 'object:', warmup.object?.length);
log.data(' result: performance:', 'load:', warmup.performance?.load, 'total:', warmup.performance?.total);
} else {
log.error('failed: warmup');
}
const random = tf.randomNormal([1, 1024, 1024, 3]);
const detect = await human.detect(random);
let detect;
try {
detect = await human.detect(random);
} catch (err) {
log.error('error: detect', err);
}
tf.dispose(random);
if (detect) {
log.state('passed: detect:', 'random');
log.data(' result: face:', detect.face.length, 'body:', detect.body.length, 'hand:', detect.hand.length, 'gesture:', detect.gesture.length, 'object:', detect.object.length);
log.data(' result: performance:', 'load:', detect.performance.load, 'total:', detect.performance.total);
log.data(' result: face:', detect.face?.length, 'body:', detect.body?.length, 'hand:', detect.hand?.length, 'gesture:', detect.gesture?.length, 'object:', detect.object?.length);
log.data(' result: performance:', 'load:', detect?.performance.load, 'total:', detect.performance?.total);
} else {
log.error('failed: detect');
}
}
async function test() {
log.info('testing instance#1');
config.warmup = 'face';
log.info('testing instance#1 - none');
config.warmup = 'none';
const human1 = new Human(config);
await testInstance(human1);
log.info('testing instance#2');
config.warmup = 'body';
log.info('testing instance#2 - face');
config.warmup = 'face';
const human2 = new Human(config);
await testInstance(human2);
log.info('testing instance#3 - body');
config.warmup = 'body';
const human3 = new Human(config);
await testInstance(human3);
}
test();