mirror of https://github.com/vladmandic/human
added wasm and webgpu backends
parent
5ae0cce05a
commit
254deb9c59
|
@ -9,7 +9,8 @@
|
|||
- [**Change Log**](./CHANGELOG.md)
|
||||
- [**Live Demo**](https://vladmandic.github.io/human/demo/index.html)
|
||||
|
||||
Compatible with Browser, WebWorker and NodeJS execution!
|
||||
Compatible with *Browser*, *WebWorker* and *NodeJS* execution
|
||||
Compatible with *CPU*, *WebGL*, *WASM* and *WebGPU* backends
|
||||
(and maybe with React-Native as it doesn't use any DOM objects)
|
||||
|
||||
*This is a pre-release project, see [issues](https://github.com/vladmandic/human/issues) for list of known limitations and planned enhancements*
|
||||
|
@ -159,10 +160,12 @@ If your application resides in a different folder, modify `modelPath` property i
|
|||
|
||||
Demos are included in `/demo`:
|
||||
|
||||
Browser:
|
||||
**Browser**:
|
||||
- `index.html`, `browser.js`, `worker.js`: Full demo using Browser with ESM module, includes selectable backends and webworkers
|
||||
|
||||
NodeJS:
|
||||
*If you want to test `wasm` or `webgpu` backends, enable loading in `index.html`*
|
||||
|
||||
**NodeJS**:
|
||||
- `node.js`: Demo using NodeJS with CommonJS module
|
||||
This is a very simple demo as althought `Human` library is compatible with NodeJS execution
|
||||
and is able to load images and models from local filesystem,
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -29,7 +29,8 @@ const ui = {
|
|||
|
||||
// configuration overrides
|
||||
const config = {
|
||||
backend: 'webgl', // if you want to use 'wasm' backend, enable script load of tf and tf-backend-wasm in index.html
|
||||
backend: 'webgl',
|
||||
wasm: { path: '../assets' },
|
||||
filter: {
|
||||
enabled: true,
|
||||
width: 0,
|
||||
|
@ -108,7 +109,7 @@ function drawResults(input, result, canvas) {
|
|||
// update log
|
||||
const engine = human.tf.engine();
|
||||
const memory = `${engine.state.numBytes.toLocaleString()} bytes ${engine.state.numDataBuffers.toLocaleString()} buffers ${engine.state.numTensors.toLocaleString()} tensors`;
|
||||
const gpu = engine.backendInstance ? `GPU: ${engine.backendInstance.numBytesInGPU.toLocaleString()} bytes` : '';
|
||||
const gpu = engine.backendInstance ? `GPU: ${(engine.backendInstance.numBytesInGPU ? engine.backendInstance.numBytesInGPU : 0).toLocaleString()} bytes` : '';
|
||||
document.getElementById('log').innerText = `
|
||||
TFJS Version: ${human.tf.version_core} | Backend: ${human.tf.getBackend()} | Memory: ${memory} ${gpu}
|
||||
Performance: ${str(result.performance)} | Object size: ${(str(result)).length.toLocaleString()} bytes
|
||||
|
@ -267,6 +268,7 @@ function setupMenu() {
|
|||
menu.addButton('Process Images', 'Process Images', () => detectSampleImages());
|
||||
|
||||
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
||||
menu.addList('Backend', ['cpu', 'webgl', 'wasm', 'webgpu'], config.backend, (val) => config.backend = val);
|
||||
menu.addBool('Use Web Worker', ui, 'useWorker');
|
||||
menu.addHTML('<hr style="min-width: 200px; border-style: inset; border-color: dimgray">');
|
||||
menu.addLabel('Enabled Models');
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
<meta name="msapplication-tooltip" content="Human: AI-powered 3D Human Detection">
|
||||
<link rel="manifest" href="../dist/human.esm.json">
|
||||
<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon">
|
||||
<!-- <script src="../assets/tf.min.js"></script> -->
|
||||
<!-- <script src="../assets/tf-backend-wasm.min.js"></script> -->
|
||||
<!-- <script src="../assets/tf.es2017.js"></script> -->
|
||||
<!-- <script src="../assets/tf-backend-wasm.es2017.js"></script> -->
|
||||
<!-- <script src="../assets/tf-backend-webgpu.js"></script> -->
|
||||
<script src="./browser.js" type="module"></script>
|
||||
</head>
|
||||
<body style="margin: 0; background: black; color: white; font-family: 'Segoe UI'; font-size: 16px; font-variant: small-caps; overflow-x: hidden">
|
||||
|
|
24
demo/menu.js
24
demo/menu.js
|
@ -8,9 +8,13 @@ const css = `
|
|||
.menu-container-fadein { max-height: 100vh; overflow: hidden; transition: max-height, 0.5s ease; }
|
||||
.menu-item { display: flex; white-space: nowrap; background: darkslategray; padding: 0.2rem; width: max-content; }
|
||||
.menu-title { text-align: right; cursor: pointer; }
|
||||
.menu-hr { margin: 0.2rem; border: 1px solid rgba(0, 0, 0, 0.5) }
|
||||
.menu-hr { margin: 0.2rem; border: 1px solid rgba(0, 0, 0, 0.5); }
|
||||
.menu-label { padding: 0; }
|
||||
|
||||
.menu-list { margin-right: 0.8rem; }
|
||||
select:focus { outline: none; }
|
||||
.menu-list-item { background: black; color: white; border: none; padding: 0.2rem; font-family: inherit; font-variant: inherit; border-radius: 1rem; }
|
||||
|
||||
.menu-chart-title { align-items: center; }
|
||||
.menu-chart-canvas { background: transparent; height: 40px; width: 180px; margin: 0.2rem 0.2rem 0.2rem 1rem; }
|
||||
|
||||
|
@ -130,6 +134,24 @@ class Menu {
|
|||
});
|
||||
}
|
||||
|
||||
async addList(title, items, selected, callback) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'menu-item';
|
||||
let options = '';
|
||||
for (const item of items) {
|
||||
const def = item === selected ? 'selected' : '';
|
||||
options += `<option value="${item}" ${def}>${item}</option>`;
|
||||
}
|
||||
el.innerHTML = `<div class="menu-list"><select name="${this.ID}" class="menu-list-item">${options}</select><label for="${this.ID}"></label></div>${title}`;
|
||||
el.style.fontFamily = document.body.style.fontFamily;
|
||||
el.style.fontSize = document.body.style.fontSize;
|
||||
el.style.fontVariant = document.body.style.fontVariant;
|
||||
this.container.appendChild(el);
|
||||
el.addEventListener('change', (evt) => {
|
||||
if (callback) callback(items[evt.target.selectedIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
async addRange(title, object, variable, min, max, step, callback) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'menu-item';
|
||||
|
|
|
@ -5777,7 +5777,7 @@ var require_config = __commonJS((exports) => {
|
|||
var require_package = __commonJS((exports, module) => {
|
||||
module.exports = {
|
||||
name: "@vladmandic/human",
|
||||
version: "0.4.10",
|
||||
version: "0.5.1",
|
||||
description: "human: 3D Face Detection, Iris Tracking and Age & Gender Prediction",
|
||||
sideEffects: false,
|
||||
main: "dist/human.node.js",
|
||||
|
@ -5799,10 +5799,9 @@ var require_package = __commonJS((exports, module) => {
|
|||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
devDependencies: {
|
||||
seedrandom: "^3.0.5",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"@vladmandic/pilogger": "^0.2.6",
|
||||
"@vladmandic/pilogger": "^0.2.7",
|
||||
dayjs: "^1.9.4",
|
||||
esbuild: "^0.7.22",
|
||||
eslint: "^7.12.1",
|
||||
|
@ -5812,6 +5811,7 @@ var require_package = __commonJS((exports, module) => {
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
rimraf: "^3.0.2",
|
||||
seedrandom: "^3.0.5",
|
||||
"simple-git": "^2.21.0"
|
||||
},
|
||||
scripts: {
|
||||
|
@ -5958,17 +5958,33 @@ class Human {
|
|||
this.models.emotion = await emotion.load(this.config);
|
||||
}
|
||||
}
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log("Human library backend not registred:", backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
tfImage(input) {
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0)
|
||||
targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0)
|
||||
targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0)
|
||||
targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0)
|
||||
|
@ -6015,7 +6031,19 @@ class Human {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
if (this.config.backend === "webgl" || canvas instanceof ImageData)
|
||||
pixels = tf.browser.fromPixels(canvas);
|
||||
else {
|
||||
const tempCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -6039,12 +6067,7 @@ class Human {
|
|||
return new Promise(async (resolve) => {
|
||||
const timeStart = now();
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
if (first) {
|
||||
this.log("Human library starting");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,7 +5,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"package.json": {
|
||||
"bytes": 2871,
|
||||
"bytes": 2870,
|
||||
"imports": []
|
||||
},
|
||||
"src/emotion/emotion.js": {
|
||||
|
@ -116,7 +116,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 11908,
|
||||
"bytes": 13144,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/facemesh/facemesh.js"
|
||||
|
@ -260,7 +260,7 @@
|
|||
"dist/human.esm-nobundle.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 248630
|
||||
"bytes": 250346
|
||||
},
|
||||
"dist/human.esm-nobundle.js": {
|
||||
"imports": [],
|
||||
|
@ -353,16 +353,16 @@
|
|||
"bytesInOutput": 2230
|
||||
},
|
||||
"package.json": {
|
||||
"bytesInOutput": 3013
|
||||
"bytesInOutput": 3012
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 10775
|
||||
"bytesInOutput": 11781
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 0
|
||||
}
|
||||
},
|
||||
"bytes": 155239
|
||||
"bytes": 156244
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72826,7 +72826,7 @@ var require_config = __commonJS((exports) => {
|
|||
var require_package = __commonJS((exports, module) => {
|
||||
module.exports = {
|
||||
name: "@vladmandic/human",
|
||||
version: "0.4.10",
|
||||
version: "0.5.1",
|
||||
description: "human: 3D Face Detection, Iris Tracking and Age & Gender Prediction",
|
||||
sideEffects: false,
|
||||
main: "dist/human.node.js",
|
||||
|
@ -72848,10 +72848,9 @@ var require_package = __commonJS((exports, module) => {
|
|||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
devDependencies: {
|
||||
seedrandom: "^3.0.5",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"@vladmandic/pilogger": "^0.2.6",
|
||||
"@vladmandic/pilogger": "^0.2.7",
|
||||
dayjs: "^1.9.4",
|
||||
esbuild: "^0.7.22",
|
||||
eslint: "^7.12.1",
|
||||
|
@ -72861,6 +72860,7 @@ var require_package = __commonJS((exports, module) => {
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
rimraf: "^3.0.2",
|
||||
seedrandom: "^3.0.5",
|
||||
"simple-git": "^2.21.0"
|
||||
},
|
||||
scripts: {
|
||||
|
@ -73007,17 +73007,33 @@ class Human {
|
|||
this.models.emotion = await emotion.load(this.config);
|
||||
}
|
||||
}
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log("Human library backend not registred:", backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
tfImage(input) {
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0)
|
||||
targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0)
|
||||
targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0)
|
||||
targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0)
|
||||
|
@ -73064,7 +73080,19 @@ class Human {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
if (this.config.backend === "webgl" || canvas instanceof ImageData)
|
||||
pixels = tf.browser.fromPixels(canvas);
|
||||
else {
|
||||
const tempCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -73088,12 +73116,7 @@ class Human {
|
|||
return new Promise(async (resolve) => {
|
||||
const timeStart = now();
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
if (first) {
|
||||
this.log("Human library starting");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -149,7 +149,7 @@
|
|||
]
|
||||
},
|
||||
"package.json": {
|
||||
"bytes": 2871,
|
||||
"bytes": 2870,
|
||||
"imports": []
|
||||
},
|
||||
"src/emotion/emotion.js": {
|
||||
|
@ -291,7 +291,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 11908,
|
||||
"bytes": 13144,
|
||||
"imports": [
|
||||
{
|
||||
"path": "node_modules/@tensorflow/tfjs/dist/tf.node.js"
|
||||
|
@ -468,7 +468,7 @@
|
|||
"dist/human.esm.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 5121936
|
||||
"bytes": 5123652
|
||||
},
|
||||
"dist/human.esm.js": {
|
||||
"imports": [],
|
||||
|
@ -618,16 +618,16 @@
|
|||
"bytesInOutput": 2230
|
||||
},
|
||||
"package.json": {
|
||||
"bytesInOutput": 3013
|
||||
"bytesInOutput": 3012
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 10765
|
||||
"bytesInOutput": 11771
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 0
|
||||
}
|
||||
},
|
||||
"bytes": 2924203
|
||||
"bytes": 2925208
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72827,7 +72827,7 @@ var Human = (() => {
|
|||
var require_package = __commonJS((exports, module) => {
|
||||
module.exports = {
|
||||
name: "@vladmandic/human",
|
||||
version: "0.4.10",
|
||||
version: "0.5.1",
|
||||
description: "human: 3D Face Detection, Iris Tracking and Age & Gender Prediction",
|
||||
sideEffects: false,
|
||||
main: "dist/human.node.js",
|
||||
|
@ -72849,10 +72849,9 @@ var Human = (() => {
|
|||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
devDependencies: {
|
||||
seedrandom: "^3.0.5",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"@vladmandic/pilogger": "^0.2.6",
|
||||
"@vladmandic/pilogger": "^0.2.7",
|
||||
dayjs: "^1.9.4",
|
||||
esbuild: "^0.7.22",
|
||||
eslint: "^7.12.1",
|
||||
|
@ -72862,6 +72861,7 @@ var Human = (() => {
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
rimraf: "^3.0.2",
|
||||
seedrandom: "^3.0.5",
|
||||
"simple-git": "^2.21.0"
|
||||
},
|
||||
scripts: {
|
||||
|
@ -73012,17 +73012,33 @@ var Human = (() => {
|
|||
this.models.emotion = await emotion.load(this.config);
|
||||
}
|
||||
}
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log("Human library backend not registred:", backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
tfImage(input) {
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0)
|
||||
targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0)
|
||||
targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0)
|
||||
targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0)
|
||||
|
@ -73069,7 +73085,19 @@ var Human = (() => {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
if (this.config.backend === "webgl" || canvas instanceof ImageData)
|
||||
pixels = tf.browser.fromPixels(canvas);
|
||||
else {
|
||||
const tempCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -73093,12 +73121,7 @@ var Human = (() => {
|
|||
return new Promise(async (resolve) => {
|
||||
const timeStart = now();
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
if (first) {
|
||||
this.log("Human library starting");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -149,7 +149,7 @@
|
|||
]
|
||||
},
|
||||
"package.json": {
|
||||
"bytes": 2871,
|
||||
"bytes": 2870,
|
||||
"imports": []
|
||||
},
|
||||
"src/emotion/emotion.js": {
|
||||
|
@ -291,7 +291,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 11908,
|
||||
"bytes": 13144,
|
||||
"imports": [
|
||||
{
|
||||
"path": "node_modules/@tensorflow/tfjs/dist/tf.node.js"
|
||||
|
@ -468,7 +468,7 @@
|
|||
"dist/human.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 5125792
|
||||
"bytes": 5127510
|
||||
},
|
||||
"dist/human.js": {
|
||||
"imports": [],
|
||||
|
@ -618,13 +618,13 @@
|
|||
"bytesInOutput": 2424
|
||||
},
|
||||
"package.json": {
|
||||
"bytesInOutput": 3145
|
||||
"bytesInOutput": 3144
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 11994
|
||||
"bytesInOutput": 13092
|
||||
}
|
||||
},
|
||||
"bytes": 3070080
|
||||
"bytes": 3071177
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5777,7 +5777,7 @@ var require_config = __commonJS((exports2) => {
|
|||
var require_package = __commonJS((exports2, module2) => {
|
||||
module2.exports = {
|
||||
name: "@vladmandic/human",
|
||||
version: "0.4.10",
|
||||
version: "0.5.1",
|
||||
description: "human: 3D Face Detection, Iris Tracking and Age & Gender Prediction",
|
||||
sideEffects: false,
|
||||
main: "dist/human.node.js",
|
||||
|
@ -5799,10 +5799,9 @@ var require_package = __commonJS((exports2, module2) => {
|
|||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
devDependencies: {
|
||||
seedrandom: "^3.0.5",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"@vladmandic/pilogger": "^0.2.6",
|
||||
"@vladmandic/pilogger": "^0.2.7",
|
||||
dayjs: "^1.9.4",
|
||||
esbuild: "^0.7.22",
|
||||
eslint: "^7.12.1",
|
||||
|
@ -5812,6 +5811,7 @@ var require_package = __commonJS((exports2, module2) => {
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
rimraf: "^3.0.2",
|
||||
seedrandom: "^3.0.5",
|
||||
"simple-git": "^2.21.0"
|
||||
},
|
||||
scripts: {
|
||||
|
@ -5961,17 +5961,33 @@ class Human {
|
|||
this.models.emotion = await emotion.load(this.config);
|
||||
}
|
||||
}
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log("Human library backend not registred:", backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
tfImage(input) {
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0)
|
||||
targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0)
|
||||
targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0)
|
||||
targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0)
|
||||
|
@ -6018,7 +6034,19 @@ class Human {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
if (this.config.backend === "webgl" || canvas instanceof ImageData)
|
||||
pixels = tf.browser.fromPixels(canvas);
|
||||
else {
|
||||
const tempCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -6042,12 +6070,7 @@ class Human {
|
|||
return new Promise(async (resolve) => {
|
||||
const timeStart = now();
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
if (first) {
|
||||
this.log("Human library starting");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -73868,7 +73868,7 @@ var require_config = __commonJS((exports2) => {
|
|||
var require_package = __commonJS((exports2, module2) => {
|
||||
module2.exports = {
|
||||
name: "@vladmandic/human",
|
||||
version: "0.4.10",
|
||||
version: "0.5.1",
|
||||
description: "human: 3D Face Detection, Iris Tracking and Age & Gender Prediction",
|
||||
sideEffects: false,
|
||||
main: "dist/human.node.js",
|
||||
|
@ -73890,10 +73890,9 @@ var require_package = __commonJS((exports2, module2) => {
|
|||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
devDependencies: {
|
||||
seedrandom: "^3.0.5",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"@vladmandic/pilogger": "^0.2.6",
|
||||
"@vladmandic/pilogger": "^0.2.7",
|
||||
dayjs: "^1.9.4",
|
||||
esbuild: "^0.7.22",
|
||||
eslint: "^7.12.1",
|
||||
|
@ -73903,6 +73902,7 @@ var require_package = __commonJS((exports2, module2) => {
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
rimraf: "^3.0.2",
|
||||
seedrandom: "^3.0.5",
|
||||
"simple-git": "^2.21.0"
|
||||
},
|
||||
scripts: {
|
||||
|
@ -74052,17 +74052,33 @@ class Human {
|
|||
this.models.emotion = await emotion.load(this.config);
|
||||
}
|
||||
}
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log("Human library backend not registred:", backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
tfImage(input) {
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || input.shape && input.shape[1] > 0;
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || input.shape && input.shape[2] > 0;
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0)
|
||||
targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0)
|
||||
targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0)
|
||||
targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0)
|
||||
|
@ -74109,7 +74125,19 @@ class Human {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
if (this.config.backend === "webgl" || canvas instanceof ImageData)
|
||||
pixels = tf.browser.fromPixels(canvas);
|
||||
else {
|
||||
const tempCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -74133,12 +74161,7 @@ class Human {
|
|||
return new Promise(async (resolve) => {
|
||||
const timeStart = now();
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = "backend";
|
||||
this.log("Human library setting backend:", this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
if (first) {
|
||||
this.log("Human library starting");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,7 +5,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"package.json": {
|
||||
"bytes": 2871,
|
||||
"bytes": 2870,
|
||||
"imports": []
|
||||
},
|
||||
"src/emotion/emotion.js": {
|
||||
|
@ -116,7 +116,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 11908,
|
||||
"bytes": 13144,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/facemesh/facemesh.js"
|
||||
|
@ -260,7 +260,7 @@
|
|||
"dist/human.node-nobundle.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 260882
|
||||
"bytes": 263858
|
||||
},
|
||||
"dist/human.node-nobundle.js": {
|
||||
"imports": [],
|
||||
|
@ -353,16 +353,16 @@
|
|||
"bytesInOutput": 2232
|
||||
},
|
||||
"package.json": {
|
||||
"bytesInOutput": 3016
|
||||
"bytesInOutput": 3015
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 47
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 10775
|
||||
"bytesInOutput": 11781
|
||||
}
|
||||
},
|
||||
"bytes": 155393
|
||||
"bytes": 156398
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
48
src/human.js
48
src/human.js
|
@ -126,17 +126,33 @@ class Human {
|
|||
}
|
||||
}
|
||||
|
||||
async resetBackend(backendName) {
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = 'backend';
|
||||
if (backendName in tf.engine().registry) {
|
||||
this.log('Human library setting backend:', this.config.backend);
|
||||
this.config.backend = backendName;
|
||||
const backendFactory = tf.findBackendFactory(backendName);
|
||||
tf.removeBackend(backendName);
|
||||
tf.registerBackend(backendName, backendFactory);
|
||||
await tf.setBackend(backendName);
|
||||
await tf.ready();
|
||||
} else {
|
||||
this.log('Human library backend not registred:', backendName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tfImage(input) {
|
||||
// let imageData;
|
||||
let filtered;
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || (input.shape && (input.shape[1] > 0));
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || (input.shape && (input.shape[2] > 0));
|
||||
let targetWidth = originalWidth;
|
||||
let targetHeight = originalHeight;
|
||||
if (this.fx && this.config.filter.enabled && !(input instanceof tf.Tensor)) {
|
||||
const originalWidth = input.naturalWidth || input.videoWidth || input.width || (input.shape && (input.shape[1] > 0));
|
||||
const originalHeight = input.naturalHeight || input.videoHeight || input.height || (input.shape && (input.shape[2] > 0));
|
||||
|
||||
let targetWidth = originalWidth;
|
||||
if (this.config.filter.width > 0) targetWidth = this.config.filter.width;
|
||||
else if (this.config.filter.height > 0) targetWidth = originalWidth * (this.config.filter.height / originalHeight);
|
||||
let targetHeight = originalHeight;
|
||||
if (this.config.filter.height > 0) targetHeight = this.config.filter.height;
|
||||
else if (this.config.filter.width > 0) targetHeight = originalHeight * (this.config.filter.width / originalWidth);
|
||||
const offscreenCanvas = (typeof OffscreenCanvas !== 'undefined') ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement('canvas');
|
||||
|
@ -166,7 +182,20 @@ class Human {
|
|||
if (input instanceof tf.Tensor) {
|
||||
tensor = tf.clone(input);
|
||||
} else {
|
||||
const pixels = tf.browser.fromPixels(filtered || input);
|
||||
const canvas = filtered || input;
|
||||
let pixels;
|
||||
// tf kernel-optimized method to get imagedata, also if input is imagedata, just use it
|
||||
if ((this.config.backend === 'webgl') || (canvas instanceof ImageData)) pixels = tf.browser.fromPixels(canvas);
|
||||
// cpu and wasm kernel does not implement efficient fromPixels method nor we can use canvas as-is, so we do a silly one more canvas
|
||||
else {
|
||||
const tempCanvas = (typeof OffscreenCanvas !== 'undefined') ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement('canvas');
|
||||
tempCanvas.width = targetWidth;
|
||||
tempCanvas.height = targetHeight;
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
const data = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||
pixels = tf.browser.fromPixels(data);
|
||||
}
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
|
@ -197,12 +226,7 @@ class Human {
|
|||
|
||||
// configure backend
|
||||
timeStamp = now();
|
||||
if (tf.getBackend() !== this.config.backend) {
|
||||
this.state = 'backend';
|
||||
this.log('Human library setting backend:', this.config.backend);
|
||||
await tf.setBackend(this.config.backend);
|
||||
await tf.ready();
|
||||
}
|
||||
await this.resetBackend(this.config.backend);
|
||||
perf.backend = Math.trunc(now() - timeStamp);
|
||||
|
||||
// check number of loaded models
|
||||
|
|
Loading…
Reference in New Issue