add getModelStats method

pull/356/head
Vladimir Mandic 2022-07-02 03:39:40 -04:00
parent 772964ff49
commit ffdd43faf9
8 changed files with 77 additions and 38 deletions

View File

@ -9,7 +9,7 @@
## Changelog
### **HEAD -> main** 2022/06/10 mandic00@live.com
### **HEAD -> main** 2022/06/21 mandic00@live.com
### **release: 2.8.1** 2022/06/08 mandic00@live.com

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@
import { Human, Config } from '../../dist/human.esm.js'; // equivalent of @vladmandic/Human
const humanConfig: Partial<Config> = { // user configuration for human, used to fine-tune behavior
// backend: 'webgpu' as const,
// backend: 'wasm' as const,
// wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.18.0/dist/',
// cacheSensitivity: 0,
async: true,

View File

@ -2,6 +2,7 @@
import { log } from '../util/util';
import * as tf from '../../dist/tfjs.esm.js';
import { loadModel } from '../tfjs/load';
import type { GraphModel, Tensor } from '../tfjs/types';
import type { Config } from '../config';
@ -56,7 +57,7 @@ export class FaceBoxes {
}
export async function load(config) {
const model = await tf.loadGraphModel(config.face.detector.modelPath);
const model = await loadModel(config.face.detector?.modelPath);
if (config.debug) log(`load model: ${config.face.detector.modelPath.match(/\/(.*)\./)[1]}`);
const faceboxes = new FaceBoxes(model, config);
if (config.face.mesh.enabled && config.debug) log(`load model: ${config.face.mesh.modelPath.match(/\/(.*)\./)[1]}`);

View File

@ -329,6 +329,9 @@ export class Human {
return interpolate.calc(result, this.config) as Result;
}
/** get model loading/loaded stats */
getModelStats = () => models.getModelStats();
/** Warmup method pre-initializes all configured models for faster inference
* - can take significant time on startup
* - only used for `webgl` and `humangl` backends

View File

@ -24,6 +24,7 @@ import * as movenet from './body/movenet';
import * as nanodet from './object/nanodet';
import * as posenet from './body/posenet';
import * as segmentation from './segmentation/segmentation';
import { modelStats } from './tfjs/load';
import type { GraphModel } from './tfjs/types';
import type { Human } from './human';
@ -58,6 +59,16 @@ export class Models {
antispoof: null | GraphModel | Promise<GraphModel> = null;
}
export const getModelStats = () => {
let sizeManifest = 0;
let sizeWeights = 0;
for (const m of Object.values(modelStats)) {
sizeManifest += m.manifest;
sizeWeights += m.weights;
}
return { sizeManifest, sizeWeights, numModels: Object.values(modelStats).length };
};
export function reset(instance: Human): void {
// if (instance.config.debug) log('resetting loaded models');
for (const model of Object.keys(instance.models)) instance.models[model as keyof Models] = null;
@ -67,8 +78,12 @@ export function reset(instance: Human): void {
export async function load(instance: Human): Promise<void> {
if (env.initial) reset(instance);
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);
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);
}
}
if (instance.config.body.enabled && !instance.models.blazepose && instance.config.body?.modelPath?.includes('blazepose')) instance.models.blazepose = blazepose.loadPose(instance.config);
// @ts-ignore optional model
@ -99,7 +114,9 @@ export async function load(instance: Human): Promise<void> {
// 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 as keyof Models] && typeof instance.models[model as keyof Models] !== 'undefined') instance.models[model as keyof Models] = await instance.models[model as keyof Models];
if (instance.models[model as keyof Models] && typeof instance.models[model as keyof Models] !== 'undefined') {
instance.models[model as keyof Models] = await instance.models[model as keyof Models];
}
}
}

View File

@ -10,6 +10,15 @@ const options = {
modelBasePath: '',
};
type ModelStats = {
name: string,
cached: boolean,
manifest: number,
weights: number,
}
export const modelStats: Record<string, ModelStats> = {};
async function httpHandler(url, init?): Promise<Response | null> {
if (options.debug) log('load model fetch:', url, init);
return fetch(url, init);
@ -25,26 +34,35 @@ export async function loadModel(modelPath: string | undefined): Promise<GraphMod
let modelUrl = join(options.modelBasePath, modelPath || '');
if (!modelUrl.toLowerCase().endsWith('.json')) modelUrl += '.json';
const modelPathSegments = modelUrl.split('/');
const cachedModelName = 'indexeddb://' + modelPathSegments[modelPathSegments.length - 1].replace('.json', ''); // generate short model name for cache
const shortModelName = modelPathSegments[modelPathSegments.length - 1].replace('.json', '');
const cachedModelName = 'indexeddb://' + shortModelName; // generate short model name for cache
modelStats[shortModelName] = {
name: shortModelName,
manifest: 0,
weights: 0,
cached: false,
};
const cachedModels = await tf.io.listModels(); // list all models already in cache
const modelCached = options.cacheModels && Object.keys(cachedModels).includes(cachedModelName); // is model found in cache
modelStats[shortModelName].cached = options.cacheModels && Object.keys(cachedModels).includes(cachedModelName); // is model found in cache
const tfLoadOptions = typeof fetch === 'undefined' ? {} : { fetchFunc: (url, init?) => httpHandler(url, init) };
const model: GraphModel = new tf.GraphModel(modelCached ? cachedModelName : modelUrl, tfLoadOptions) as unknown as GraphModel; // create model prototype and decide if load from cache or from original modelurl
const model: GraphModel = new tf.GraphModel(modelStats[shortModelName].cached ? cachedModelName : modelUrl, tfLoadOptions) as unknown as GraphModel; // create model prototype and decide if load from cache or from original modelurl
let loaded = false;
try {
// @ts-ignore private function
model.findIOHandler(); // decide how to actually load a model
// @ts-ignore private property
if (options.debug) log('model load handler:', model.handler);
if (options.debug) log('model load handler:', model['handler']);
// @ts-ignore private property
const artifacts = await model.handler.load(); // load manifest
modelStats[shortModelName].manifest = artifacts?.weightData?.byteLength || 0;
model.loadSync(artifacts); // load weights
if (options.verbose) log('load model:', model['modelUrl']);
// @ts-ignore private property
modelStats[shortModelName].weights = model?.artifacts?.weightData?.byteLength || 0;
if (options.verbose) log('load model:', model['modelUrl'], { bytes: modelStats[shortModelName].weights });
loaded = true;
} catch (err) {
log('error loading model:', modelUrl, err);
}
if (loaded && options.cacheModels && !modelCached) { // save model to cache
if (loaded && options.cacheModels && !modelStats[shortModelName].cached) { // save model to cache
try {
const saveResult = await model.save(cachedModelName);
log('model saved:', cachedModelName, saveResult);

View File

@ -1,24 +1,24 @@
2022-06-21 13:20:54 INFO:  Application: {"name":"@vladmandic/human","version":"2.8.1"}
2022-06-21 13:20:54 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true}
2022-06-21 13:20:54 INFO:  Toolchain: {"build":"0.7.3","esbuild":"0.14.47","typescript":"4.7.4","typedoc":"0.22.17","eslint":"8.18.0"}
2022-06-21 13:20:54 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]}
2022-06-21 13:20:54 STATE: Clean: {"locations":["dist/*","types/lib/*","typedoc/*"]}
2022-06-21 13:20:54 STATE: Compile: {"name":"tfjs/nodejs/cpu","format":"cjs","platform":"node","input":"tfjs/tf-node.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":102,"outputBytes":608}
2022-06-21 13:20:54 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":73,"inputBytes":642839,"outputBytes":300711}
2022-06-21 13:20:54 STATE: Compile: {"name":"tfjs/nodejs/gpu","format":"cjs","platform":"node","input":"tfjs/tf-node-gpu.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":110,"outputBytes":612}
2022-06-21 13:20:54 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":73,"inputBytes":642843,"outputBytes":300715}
2022-06-21 13:20:54 STATE: Compile: {"name":"tfjs/nodejs/wasm","format":"cjs","platform":"node","input":"tfjs/tf-node-wasm.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":149,"outputBytes":664}
2022-06-21 13:20:54 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":73,"inputBytes":642895,"outputBytes":300765}
2022-06-21 13:20:54 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1069,"outputBytes":371}
2022-06-21 13:20:54 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":1045,"outputBytes":596}
2022-06-21 13:20:54 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":73,"inputBytes":642827,"outputBytes":299608}
2022-06-21 13:20:55 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":110,"outputBytes":1353541}
2022-06-21 13:20:55 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":73,"inputBytes":1995772,"outputBytes":1652210}
2022-06-21 13:20:55 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":73,"inputBytes":1995772,"outputBytes":2139073}
2022-06-21 13:21:07 STATE: Typings: {"input":"src/human.ts","output":"types/lib","files":116}
2022-06-21 13:21:13 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":73,"generated":true}
2022-06-21 13:21:13 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":6326,"outputBytes":3070}
2022-06-21 13:21:13 STATE: Compile: {"name":"demo/faceid","format":"esm","platform":"browser","input":"demo/faceid/index.ts","output":"demo/faceid/index.js","files":2,"inputBytes":15174,"outputBytes":7833}
2022-06-21 13:22:08 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":106,"errors":0,"warnings":0}
2022-06-21 13:22:08 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"}
2022-06-21 13:22:08 INFO:  Done...
2022-07-02 03:34:20 INFO:  Application: {"name":"@vladmandic/human","version":"2.8.1"}
2022-07-02 03:34:20 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true}
2022-07-02 03:34:20 INFO:  Toolchain: {"build":"0.7.3","esbuild":"0.14.47","typescript":"4.7.4","typedoc":"0.22.17","eslint":"8.18.0"}
2022-07-02 03:34:20 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]}
2022-07-02 03:34:20 STATE: Clean: {"locations":["dist/*","types/lib/*","typedoc/*"]}
2022-07-02 03:34:20 STATE: Compile: {"name":"tfjs/nodejs/cpu","format":"cjs","platform":"node","input":"tfjs/tf-node.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":102,"outputBytes":608}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":73,"inputBytes":643891,"outputBytes":301229}
2022-07-02 03:34:21 STATE: Compile: {"name":"tfjs/nodejs/gpu","format":"cjs","platform":"node","input":"tfjs/tf-node-gpu.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":110,"outputBytes":612}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":73,"inputBytes":643895,"outputBytes":301233}
2022-07-02 03:34:21 STATE: Compile: {"name":"tfjs/nodejs/wasm","format":"cjs","platform":"node","input":"tfjs/tf-node-wasm.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":149,"outputBytes":664}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":73,"inputBytes":643947,"outputBytes":301283}
2022-07-02 03:34:21 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1069,"outputBytes":371}
2022-07-02 03:34:21 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":1045,"outputBytes":596}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":73,"inputBytes":643879,"outputBytes":300126}
2022-07-02 03:34:21 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":1,"inputBytes":110,"outputBytes":1353541}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":73,"inputBytes":1996824,"outputBytes":1652733}
2022-07-02 03:34:21 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":73,"inputBytes":1996824,"outputBytes":2140101}
2022-07-02 03:34:36 STATE: Typings: {"input":"src/human.ts","output":"types/lib","files":116}
2022-07-02 03:34:44 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":73,"generated":true}
2022-07-02 03:34:44 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":6324,"outputBytes":3070}
2022-07-02 03:34:44 STATE: Compile: {"name":"demo/faceid","format":"esm","platform":"browser","input":"demo/faceid/index.ts","output":"demo/faceid/index.js","files":2,"inputBytes":15174,"outputBytes":7833}
2022-07-02 03:35:04 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":106,"errors":0,"warnings":0}
2022-07-02 03:35:05 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"}
2022-07-02 03:35:05 INFO:  Done...