2021-09-25 17:51:15 +02:00
|
|
|
/**
|
|
|
|
* Loader and Validator for all models used by Human
|
|
|
|
*/
|
|
|
|
|
2021-10-13 16:56:56 +02:00
|
|
|
import { env } from './util/env';
|
2021-09-27 19:58:13 +02:00
|
|
|
import { log } from './util/util';
|
2021-11-13 18:23:32 +01:00
|
|
|
import * as gear from './gear/gear';
|
|
|
|
import * as ssrnetAge from './gear/ssrnet-age';
|
|
|
|
import * as ssrnetGender from './gear/ssrnet-gender';
|
2021-10-13 16:56:56 +02:00
|
|
|
import * as antispoof from './face/antispoof';
|
2021-09-28 18:01:48 +02:00
|
|
|
import * as blazeface from './face/blazeface';
|
2021-10-13 16:56:56 +02:00
|
|
|
import * as blazepose from './body/blazepose';
|
|
|
|
import * as centernet from './object/centernet';
|
|
|
|
import * as efficientpose from './body/efficientpose';
|
|
|
|
import * as emotion from './gear/emotion';
|
2021-09-28 18:01:48 +02:00
|
|
|
import * as facemesh from './face/facemesh';
|
2021-09-27 19:58:13 +02:00
|
|
|
import * as faceres from './face/faceres';
|
2021-10-20 15:10:57 +02:00
|
|
|
import * as handpose from './hand/handpose';
|
2021-09-27 19:58:13 +02:00
|
|
|
import * as handtrack from './hand/handtrack';
|
2021-10-13 16:56:56 +02:00
|
|
|
import * as iris from './face/iris';
|
2021-11-09 20:37:50 +01:00
|
|
|
import * as liveness from './face/liveness';
|
2021-09-27 19:58:13 +02:00
|
|
|
import * as movenet from './body/movenet';
|
2021-06-18 15:16:21 +02:00
|
|
|
import * as nanodet from './object/nanodet';
|
2021-10-13 16:56:56 +02:00
|
|
|
import * as posenet from './body/posenet';
|
2021-06-18 15:16:21 +02:00
|
|
|
import * as segmentation from './segmentation/segmentation';
|
2021-10-13 16:56:56 +02:00
|
|
|
import type { GraphModel } from './tfjs/types';
|
2021-09-23 20:09:41 +02:00
|
|
|
import type { Human } from './human';
|
2021-06-18 15:16:21 +02:00
|
|
|
|
2021-09-23 20:09:41 +02:00
|
|
|
/** Instances of all possible TFJS Graph Models used by Human
|
|
|
|
* - loaded as needed based on configuration
|
|
|
|
* - initialized explictly with `human.load()` method
|
|
|
|
* - initialized implicity on first call to `human.detect()`
|
|
|
|
* - each model can be `null` if not loaded, instance of `GraphModel` if loaded or `Promise` if loading
|
|
|
|
*/
|
|
|
|
export class Models {
|
2021-11-13 18:23:32 +01:00
|
|
|
ssrnetage: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
gear: null | GraphModel | Promise<GraphModel> = null;
|
2021-09-27 19:58:13 +02:00
|
|
|
blazeposedetect: null | GraphModel | Promise<GraphModel> = null;
|
2021-09-23 20:09:41 +02:00
|
|
|
blazepose: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
centernet: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
efficientpose: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
embedding: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
emotion: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
facedetect: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
faceiris: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
facemesh: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
faceres: null | GraphModel | Promise<GraphModel> = null;
|
2021-11-13 18:23:32 +01:00
|
|
|
ssrnetgender: null | GraphModel | Promise<GraphModel> = null;
|
2021-09-23 20:09:41 +02:00
|
|
|
handpose: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
handskeleton: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
handtrack: null | GraphModel | Promise<GraphModel> = null;
|
2021-11-09 20:37:50 +01:00
|
|
|
liveness: null | GraphModel | Promise<GraphModel> = null;
|
2021-09-23 20:09:41 +02:00
|
|
|
movenet: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
nanodet: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
posenet: null | GraphModel | Promise<GraphModel> = null;
|
|
|
|
segmentation: null | GraphModel | Promise<GraphModel> = null;
|
2021-10-13 16:56:56 +02:00
|
|
|
antispoof: null | GraphModel | Promise<GraphModel> = null;
|
2021-09-23 20:09:41 +02:00
|
|
|
}
|
|
|
|
|
2021-09-30 20:28:16 +02:00
|
|
|
export function reset(instance: Human): void {
|
2021-09-17 17:23:00 +02:00
|
|
|
// if (instance.config.debug) log('resetting loaded models');
|
2021-09-23 20:09:41 +02:00
|
|
|
for (const model of Object.keys(instance.models)) instance.models[model] = null;
|
2021-09-17 17:23:00 +02:00
|
|
|
}
|
|
|
|
|
2021-09-19 01:09:02 +02:00
|
|
|
/** Load method preloads all instance.configured models on-demand */
|
2021-09-30 20:28:16 +02:00
|
|
|
export async function load(instance: Human): Promise<void> {
|
2021-09-17 17:23:00 +02:00
|
|
|
if (env.initial) reset(instance);
|
2021-09-23 20:09:41 +02:00
|
|
|
if (instance.config.hand.enabled) { // handpose model is a combo that must be loaded as a whole
|
|
|
|
if (!instance.models.handpose && instance.config.hand.detector?.modelPath?.includes('handdetect')) [instance.models.handpose, instance.models.handskeleton] = await handpose.load(instance.config);
|
|
|
|
if (!instance.models.handskeleton && instance.config.hand.landmarks && instance.config.hand.detector?.modelPath?.includes('handdetect')) [instance.models.handpose, instance.models.handskeleton] = await handpose.load(instance.config);
|
|
|
|
}
|
2021-09-27 19:58:13 +02:00
|
|
|
if (instance.config.body.enabled && !instance.models.blazepose && instance.config.body?.modelPath?.includes('blazepose')) instance.models.blazepose = blazepose.loadPose(instance.config);
|
|
|
|
if (instance.config.body.enabled && !instance.models.blazeposedetect && instance.config.body.detector?.modelPath && instance.config.body?.modelPath?.includes('blazepose')) instance.models.blazeposedetect = blazepose.loadDetect(instance.config);
|
|
|
|
if (instance.config.body.enabled && !instance.models.efficientpose && instance.config.body?.modelPath?.includes('efficientpose')) instance.models.efficientpose = efficientpose.load(instance.config);
|
2021-09-23 20:09:41 +02:00
|
|
|
if (instance.config.body.enabled && !instance.models.movenet && instance.config.body?.modelPath?.includes('movenet')) instance.models.movenet = movenet.load(instance.config);
|
2021-11-09 20:37:50 +01:00
|
|
|
if (instance.config.body.enabled && !instance.models.posenet && instance.config.body?.modelPath?.includes('posenet')) instance.models.posenet = posenet.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && !instance.models.facedetect) instance.models.facedetect = blazeface.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face.antispoof?.enabled && !instance.models.antispoof) instance.models.antispoof = antispoof.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face.liveness?.enabled && !instance.models.liveness) instance.models.liveness = liveness.load(instance.config);
|
2021-09-23 20:09:41 +02:00
|
|
|
if (instance.config.face.enabled && instance.config.face.description?.enabled && !instance.models.faceres) instance.models.faceres = faceres.load(instance.config);
|
2021-11-09 20:37:50 +01:00
|
|
|
if (instance.config.face.enabled && instance.config.face.emotion?.enabled && !instance.models.emotion) instance.models.emotion = emotion.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face.iris?.enabled && !instance.models.faceiris) instance.models.faceiris = iris.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face.mesh?.enabled && !instance.models.facemesh) instance.models.facemesh = facemesh.load(instance.config);
|
2021-11-13 18:23:32 +01:00
|
|
|
if (instance.config.face.enabled && instance.config.face['gear']?.enabled && !instance.models.gear) instance.models.gear = gear.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face['ssrnet']?.enabled && !instance.models.ssrnetage) instance.models.ssrnetage = ssrnetAge.load(instance.config);
|
|
|
|
if (instance.config.face.enabled && instance.config.face['ssrnet']?.enabled && !instance.models.ssrnetgender) instance.models.ssrnetgender = ssrnetGender.load(instance.config);
|
2021-11-09 20:37:50 +01:00
|
|
|
if (instance.config.hand.enabled && !instance.models.handtrack && instance.config.hand.detector?.modelPath?.includes('handtrack')) instance.models.handtrack = handtrack.loadDetect(instance.config);
|
|
|
|
if (instance.config.hand.enabled && instance.config.hand.landmarks && !instance.models.handskeleton && instance.config.hand.detector?.modelPath?.includes('handtrack')) instance.models.handskeleton = handtrack.loadSkeleton(instance.config);
|
|
|
|
if (instance.config.object.enabled && !instance.models.centernet && instance.config.object?.modelPath?.includes('centernet')) instance.models.centernet = centernet.load(instance.config);
|
|
|
|
if (instance.config.object.enabled && !instance.models.nanodet && instance.config.object?.modelPath?.includes('nanodet')) instance.models.nanodet = nanodet.load(instance.config);
|
|
|
|
if (instance.config.segmentation.enabled && !instance.models.segmentation) instance.models.segmentation = segmentation.load(instance.config);
|
2021-09-23 20:09:41 +02:00
|
|
|
|
|
|
|
// models are loaded in parallel asynchronously so lets wait until they are actually loaded
|
|
|
|
for await (const model of Object.keys(instance.models)) {
|
|
|
|
if (instance.models[model] && typeof instance.models[model] !== 'undefined') instance.models[model] = await instance.models[model];
|
2021-06-18 15:16:21 +02:00
|
|
|
}
|
|
|
|
}
|
2021-09-13 00:37:06 +02:00
|
|
|
|
2021-09-30 20:28:16 +02:00
|
|
|
export async function validate(instance: Human): Promise<void> {
|
2021-09-13 00:37:06 +02:00
|
|
|
interface Op { name: string, category: string, op: string }
|
|
|
|
const simpleOps = ['const', 'placeholder', 'noop', 'pad', 'squeeze', 'add', 'sub', 'mul', 'div'];
|
|
|
|
for (const defined of Object.keys(instance.models)) {
|
|
|
|
if (instance.models[defined]) { // check if model is loaded
|
|
|
|
let models: GraphModel[] = [];
|
2021-09-17 20:30:57 +02:00
|
|
|
if (Array.isArray(instance.models[defined])) {
|
|
|
|
models = instance.models[defined]
|
|
|
|
.filter((model) => (model !== null))
|
|
|
|
.map((model) => ((model && model.executor) ? model : model.model));
|
|
|
|
} else {
|
|
|
|
models = [instance.models[defined]];
|
|
|
|
}
|
2021-09-13 00:37:06 +02:00
|
|
|
for (const model of models) {
|
2021-09-17 17:23:00 +02:00
|
|
|
if (!model) {
|
|
|
|
if (instance.config.debug) log('model marked as loaded but not defined:', defined);
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-13 00:37:06 +02:00
|
|
|
const ops: string[] = [];
|
|
|
|
// @ts-ignore // executor is a private method
|
|
|
|
const executor = model?.executor;
|
2021-09-17 17:23:00 +02:00
|
|
|
if (executor && executor.graph.nodes) {
|
2021-09-13 00:37:06 +02:00
|
|
|
for (const kernel of Object.values(executor.graph.nodes)) {
|
|
|
|
const op = (kernel as Op).op.toLowerCase();
|
|
|
|
if (!ops.includes(op)) ops.push(op);
|
|
|
|
}
|
2021-09-17 17:23:00 +02:00
|
|
|
} else {
|
|
|
|
if (!executor && instance.config.debug) log('model signature not determined:', defined);
|
2021-09-13 00:37:06 +02:00
|
|
|
}
|
|
|
|
const missing: string[] = [];
|
|
|
|
for (const op of ops) {
|
|
|
|
if (!simpleOps.includes(op) // exclude simple ops
|
|
|
|
&& !instance.env.kernels.includes(op) // check actual kernel ops
|
|
|
|
&& !instance.env.kernels.includes(op.replace('_', '')) // check variation without _
|
|
|
|
&& !instance.env.kernels.includes(op.replace('native', '')) // check standard variation
|
|
|
|
&& !instance.env.kernels.includes(op.replace('v2', ''))) { // check non-versioned variation
|
|
|
|
missing.push(op);
|
|
|
|
}
|
|
|
|
}
|
2021-09-17 17:23:00 +02:00
|
|
|
// log('model validation ops:', defined, ops);
|
2021-09-13 00:37:06 +02:00
|
|
|
if (missing.length > 0 && instance.config.debug) log('model validation:', defined, missing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|