From 7fa09937b4115053d370ec408dceb27a5f387a85 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 12 Sep 2021 12:42:17 -0400 Subject: [PATCH] added human.env diagnostic class --- .github/ISSUE_TEMPLATE/issue.md | 17 +++-- CHANGELOG.md | 2 + README.md | 7 ++ demo/index.js | 6 +- src/config.ts | 6 +- src/env.ts | 128 ++++++++++++++++++++++++++++++++ src/human.ts | 30 +++++--- wiki | 2 +- 8 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 src/env.ts diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md index 7ba86e17..60dc14d5 100644 --- a/.github/ISSUE_TEMPLATE/issue.md +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -13,16 +13,23 @@ assignees: vladmandic **Expected Behavior** -**Environment +**Environment** -- Module version? +- Human library version? - Built-in demo or custom code? - Type of module used (e.g. `js`, `esm`, `esm-nobundle`)? -- Browser or NodeJS and version (e.g. NodeJS 14.15 or Chrome 89)? -- OS and Hardware platform (e.g. Windows 10, Ubuntu Linux on x64, Android 10)? -- Packager (if any) (e.g, webpack, rollup, parcel, esbuild, etc.)? +- TensorFlow/JS version (if not using bundled module)? +- Browser or NodeJS and version (e.g. *NodeJS 14.15* or *Chrome 89*)? +- OS and Hardware platform (e.g. *Windows 10*, *Ubuntu Linux on x64*, *Android 10*)? +- Packager (if any) (e.g, *webpack*, *rollup*, *parcel*, *esbuild*, etc.)? +- Framework (if any) (e.g. *React*, *NextJS*, etc.)? + +**Diagnostics** + +- Check out any applicable [diagnostic steps](https://github.com/vladmandic/human/wiki/Diag) **Additional** - For installation or startup issues include your `package.json` - For usage issues, it is recommended to post your code as [gist](https://gist.github.com/) +- For general questions, create a [discussion topic](https://github.com/vladmandic/human/discussions) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81cc9458..917d0021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ### **HEAD -> main** 2021/09/12 mandic00@live.com +- release candidate +- parametrize face config - mark all config items as optional - redefine config and result interfaces - fix usge of string enums diff --git a/README.md b/README.md index d1da264a..b2cdd165 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Check out [**Live Demo**](https://vladmandic.github.io/human/demo/index.html) ap - [**Performance Notes**](https://github.com/vladmandic/human/wiki/Performance) - [**Performance Profiling**](https://github.com/vladmandic/human/wiki/Profiling) - [**Platform Support**](https://github.com/vladmandic/human/wiki/Platforms) +- [**Diagnostic and Performance trace information**](https://github.com/vladmandic/human/wiki/Diag) - [**List of Models & Credits**](https://github.com/vladmandic/human/wiki/Models) - [**Security & Privacy Policy**](https://github.com/vladmandic/human/blob/main/SECURITY.md) - [**License & Usage Restrictions**](https://github.com/vladmandic/human/blob/main/LICENSE) @@ -309,6 +310,12 @@ For more info, see [**Configuration Details**](https://github.com/vladmandic/hum


+## Diagnostics + +- [How to get diagnostic information or performance trace information](https://github.com/vladmandic/human/wiki/Diag) + +


+ `Human` library is written in `TypeScript` [4.3](https://www.typescriptlang.org/docs/handbook/intro.html) Conforming to `JavaScript` [ECMAScript version 2020](https://www.ecma-international.org/ecma-262/11.0/index.html) standard Build target is `JavaScript` [EMCAScript version 2018](https://262.ecma-international.org/9.0/) diff --git a/demo/index.js b/demo/index.js index 8e38ce8d..79c3bb56 100644 --- a/demo/index.js +++ b/demo/index.js @@ -953,8 +953,7 @@ async function main() { // create instance of human human = new Human(userConfig); - log('human version:', Human.version); - log('tfjs version:', human.tf.version.tfjs); + log('human version:', human.version); userConfig = { ...human.config, ...userConfig }; if (typeof tf !== 'undefined') { // eslint-disable-next-line no-undef @@ -962,6 +961,7 @@ async function main() { // eslint-disable-next-line no-undef human.tf = tf; // use externally loaded version of tfjs } + log('tfjs version:', human.tf.version.tfjs); // setup main menu await setupMenu(); @@ -1013,6 +1013,8 @@ async function main() { log('overriding images list:', JSON.parse(params.get('images'))); await detectSampleImages(); } + + if (human.config.debug) log('environment:', human.env); } window.onload = main; diff --git a/src/config.ts b/src/config.ts index b3afb3fd..41fb7c67 100644 --- a/src/config.ts +++ b/src/config.ts @@ -246,9 +246,11 @@ export interface Config { * */ const config: Config = { - backend: 'humangl', // select tfjs backend to use, leave empty to use default backend + backend: '', // select tfjs backend to use, leave empty to use default backend // can be 'webgl', 'wasm', 'cpu', or 'humangl' which is a custom version of webgl - modelBasePath: '../models/', // base path for all models + // default set to `humangl` for browsers and `tensorflow` for nodejs + modelBasePath: '', // base path for all models + // default set to `../models/` for browsers and `file://models/` for nodejs wasmPath: '', // path for wasm binaries, only used for backend: wasm // default set to download from jsdeliv during Human class instantiation debug: true, // print additional status messages to console diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 00000000..d7b1ee78 --- /dev/null +++ b/src/env.ts @@ -0,0 +1,128 @@ +import * as tf from '../dist/tfjs.esm.js'; + +export interface Env { + browser: undefined | boolean, + node: undefined | boolean, + worker: undefined | boolean, + platform: undefined | string, + agent: undefined | string, + backends: string[], + tfjs: { + version: undefined | string, + external: undefined | boolean, + }, + wasm: { + supported: undefined | boolean, + simd: undefined | boolean, + multithread: undefined | boolean, + }, + webgl: { + supported: undefined | boolean, + version: undefined | string, + renderer: undefined | string, + }, + webgpu: { + supported: undefined | boolean, + adapter: undefined | string, + }, + kernels: string[], +} + +export const env: Env = { + browser: undefined, + node: undefined, + worker: undefined, + platform: undefined, + agent: undefined, + backends: [], + tfjs: { + version: undefined, + external: undefined, + }, + wasm: { + supported: undefined, + simd: undefined, + multithread: undefined, + }, + webgl: { + supported: undefined, + version: undefined, + renderer: undefined, + }, + webgpu: { + supported: undefined, + adapter: undefined, + }, + kernels: [], +}; + +export function cpuinfo() { + const cpu = { model: '', flags: [] }; + if (env.node && env.platform?.startsWith('linux')) { + // eslint-disable-next-line global-require + const fs = require('fs'); + try { + const data = fs.readFileSync('/proc/cpuinfo').toString(); + for (const line of data.split('\n')) { + if (line.startsWith('model name')) { + cpu.model = line.match(/:(.*)/g)[0].replace(':', '').trim(); + } + if (line.startsWith('flags')) { + cpu.flags = line.match(/:(.*)/g)[0].replace(':', '').trim().split(' ').sort(); + } + } + } catch { /**/ } + } + if (!env['cpu']) Object.defineProperty(env, 'cpu', { value: cpu }); + else env['cpu'] = cpu; +} + +export async function get() { + env.browser = typeof navigator !== 'undefined'; + env.node = typeof process !== 'undefined'; + // @ts-ignore WorkerGlobalScope evaluated in browser only + env.worker = env.browser ? (typeof WorkerGlobalScope !== 'undefined') : undefined; + env.tfjs.version = tf.version_core; + + // get platform and agent + if (typeof navigator !== 'undefined') { + const raw = navigator.userAgent.match(/\(([^()]+)\)/g); + if (raw && raw[0]) { + const platformMatch = raw[0].match(/\(([^()]+)\)/g); + env.platform = (platformMatch && platformMatch[0]) ? platformMatch[0].replace(/\(|\)/g, '') : ''; + env.agent = navigator.userAgent.replace(raw[0], ''); + if (env.platform[1]) env.agent = env.agent.replace(raw[1], ''); + env.agent = env.agent.replace(/ /g, ' '); + } + } else if (typeof process !== 'undefined') { + env.platform = `${process.platform} ${process.arch}`; + env.agent = `NodeJS ${process.version}`; + } + + // analyze backends + env.backends = Object.keys(tf.engine().registryFactory); + env.wasm.supported = env.backends.includes('wasm'); + if (env.wasm.supported) { + env.wasm.simd = await tf.env().getAsync('WASM_HAS_SIMD_SUPPORT'); + env.wasm.multithread = await tf.env().getAsync('WASM_HAS_MULTITHREAD_SUPPORT'); + } + + env.webgl.supported = typeof tf.backend().gpgpu !== 'undefined'; + if (env.webgl.supported) { + // @ts-ignore getGPGPUContext only exists on WebGL backend + const gl = await tf.backend().getGPGPUContext().gl; + if (gl) { + env.webgl.version = gl.getParameter(gl.VERSION); + env.webgl.renderer = gl.getParameter(gl.RENDERER); + } + } + + env.webgpu.supported = env.browser && typeof navigator['gpu'] !== 'undefined'; + if (env.webgpu.supported) env.webgpu.adapter = (await navigator['gpu'].requestAdapter())?.name; + + // enumerate kernels + env.kernels = tf.getKernelsForBackend(tf.getBackend()).map((kernel) => kernel.kernelName); + + // get cpu info + // cpuinfo(); +} diff --git a/src/human.ts b/src/human.ts index 25c8bcbd..a3cfb441 100644 --- a/src/human.ts +++ b/src/human.ts @@ -5,7 +5,6 @@ import { log, now, mergeDeep } from './helpers'; import { Config, defaults } from './config'; import { Result, FaceResult, HandResult, BodyResult, ObjectResult, GestureResult } from './result'; -import * as sysinfo from './sysinfo'; import * as tf from '../dist/tfjs.esm.js'; import * as backend from './tfjs/backend'; import * as models from './models'; @@ -26,6 +25,7 @@ import * as draw from './draw/draw'; import * as persons from './persons'; import * as interpolate from './interpolate'; import * as sample from './sample'; +import * as env from './env'; import * as app from '../package.json'; import { Tensor, GraphModel } from './tfjs/types'; @@ -33,6 +33,7 @@ import { Tensor, GraphModel } from './tfjs/types'; export * from './config'; export * from './result'; export type { DrawOptions } from './draw/draw'; +export { env } from './env'; /** Defines all possible input types for **Human** detection * @typedef Input Type @@ -93,6 +94,10 @@ export class Human { * - Can be embedded or externally provided */ tf: TensorFlow; + /** + * Object containing environment information used for diagnostics + */ + env: env.Env; /** Draw helper classes that can draw detected objects on canvas using specified draw * - options: {@link DrawOptions} global settings for all draw operations, can be overriden for each draw method * - face: draw detected faces @@ -141,8 +146,6 @@ export class Human { faceTriangulation: typeof facemesh.triangulation; /** Refernce UV map of 468 values, used for 3D mapping of the face mesh */ faceUVMap: typeof facemesh.uvmap; - /** Platform and agent information detected by Human */ - sysinfo: { platform: string, agent: string }; /** Performance object that contains values for all recently performed operations */ performance: Record; // perf members are dynamically defined as needed #numTensors: number; @@ -159,9 +162,13 @@ export class Human { * @param userConfig: {@link Config} */ constructor(userConfig?: Partial) { + env.get(); + this.env = env.env; + defaults.wasmPath = `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${tf.version_core}/dist/`; + defaults.modelBasePath = this.env.browser ? '../models/' : 'file://models/'; + defaults.backend = this.env.browser ? 'humangl' : 'tensorflow'; this.version = app.version; // expose version property on instance of class Object.defineProperty(this, 'version', { value: app.version }); // expose version property directly on class itself - defaults.wasmPath = `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${tf.version_core}/dist/`; this.config = mergeDeep(defaults, userConfig || {}); this.tf = tf; this.draw = draw; @@ -199,7 +206,6 @@ export class Human { this.faceTriangulation = facemesh.triangulation; this.faceUVMap = facemesh.uvmap; // include platform info - this.sysinfo = sysinfo.info(); this.#lastInputSum = 1; this.#emit('create'); } @@ -287,10 +293,9 @@ export class Human { if (this.#firstRun) { // print version info on first run and check for correct backend setup if (this.config.debug) log(`version: ${this.version}`); if (this.config.debug) log(`tfjs version: ${this.tf.version_core}`); - if (this.config.debug) log('platform:', this.sysinfo.platform); - if (this.config.debug) log('agent:', this.sysinfo.agent); + if (this.config.debug) log('environment:', env.env); - await this.#checkBackend(true); + await this.#checkBackend(); if (this.tf.ENV.flags.IS_BROWSER) { if (this.config.debug) log('configuration:', this.config); if (this.config.debug) log('tf flags:', this.tf.ENV.flags); @@ -316,8 +321,8 @@ export class Human { // check if backend needs initialization if it changed /** @hidden */ - #checkBackend = async (force = false) => { - if (this.config.backend && (this.config.backend.length > 0) && force || (this.tf.getBackend() !== this.config.backend)) { + #checkBackend = async () => { + if (this.#firstRun || (this.config.backend && (this.config.backend.length > 0) || (this.tf.getBackend() !== this.config.backend))) { const timeStamp = now(); this.state = 'backend'; /* force backend reload @@ -343,7 +348,7 @@ export class Human { this.config.backend = 'humangl'; } if (this.tf.ENV.flags.IS_NODE && (this.config.backend === 'webgl' || this.config.backend === 'humangl')) { - log('override: backend set to webgl while running in nodejs'); + log(`override: backend set to ${this.config.backend} while running in nodejs`); this.config.backend = 'tensorflow'; } @@ -410,6 +415,9 @@ export class Human { this.tf.enableProdMode(); await this.tf.ready(); this.performance.backend = Math.trunc(now() - timeStamp); + + env.get(); // update env on backend init + this.env = env.env; } } diff --git a/wiki b/wiki index ec49beb9..ee12bda3 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit ec49beb9f19c0abde3e62b24ba2c7749ef54c9aa +Subproject commit ee12bda3113d3d893c898a9827f9c174d4058fb8