auto tensor shape and channels handling

pull/233/head
Vladimir Mandic 2021-11-09 19:39:18 -05:00
parent e9ccd722b1
commit 9ffed35d82
12 changed files with 1046 additions and 832 deletions

View File

@ -1006,13 +1006,34 @@ async function process2(input, config3, getTensor = true) {
throw new Error("input type is not recognized"); throw new Error("input type is not recognized");
} }
if (input instanceof Tensor) { if (input instanceof Tensor) {
if (input["isDisposedInternal"]) { let tensor3 = null;
if (input["isDisposedInternal"])
throw new Error("input tensor is disposed"); throw new Error("input tensor is disposed");
} else if (!input.shape || input.shape.length !== 4 || input.shape[0] !== 1 || input.shape[3] !== 3) { if (!input["shape"])
throw new Error("input tensor shape must be [1, height, width, 3] and instead was" + (input["shape"] ? input["shape"].toString() : "unknown")); throw new Error("input tensor has no shape");
} else { if (input.shape.length === 3) {
return { tensor: tfjs_esm_exports.clone(input), canvas: config3.filter.return ? outCanvas : null }; if (input.shape[2] === 3) {
tensor3 = tfjs_esm_exports.expandDims(input, 0);
} else if (input.shape[2] === 4) {
const rgb2 = tfjs_esm_exports.slice3d(input, [0, 0, 0], [-1, -1, 3]);
tensor3 = tfjs_esm_exports.expandDims(rgb2, 0);
tfjs_esm_exports.dispose(rgb2);
}
} else if (input.shape.length === 4) {
if (input.shape[3] === 3) {
tensor3 = tfjs_esm_exports.clone(input);
} else if (input.shape[3] === 4) {
tensor3 = tfjs_esm_exports.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
if (tensor3 == null || tensor3.shape.length !== 4 || tensor3.shape[0] !== 1 || tensor3.shape[3] !== 3)
throw new Error(`could not process input tensor with shape: ${input["shape"]}`);
if (tensor3.dtype === "int32") {
const cast5 = tfjs_esm_exports.cast(tensor3, "float32");
tfjs_esm_exports.dispose(tensor3);
tensor3 = cast5;
}
return { tensor: tensor3, canvas: config3.filter.return ? outCanvas : null };
} else { } else {
if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) { if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) {
if (config3.debug) if (config3.debug)

File diff suppressed because one or more lines are too long

31
dist/human.esm.js vendored
View File

@ -71331,13 +71331,34 @@ async function process2(input2, config3, getTensor2 = true) {
throw new Error("input type is not recognized"); throw new Error("input type is not recognized");
} }
if (input2 instanceof Tensor) { if (input2 instanceof Tensor) {
if (input2["isDisposedInternal"]) { let tensor2 = null;
if (input2["isDisposedInternal"])
throw new Error("input tensor is disposed"); throw new Error("input tensor is disposed");
} else if (!input2.shape || input2.shape.length !== 4 || input2.shape[0] !== 1 || input2.shape[3] !== 3) { if (!input2["shape"])
throw new Error("input tensor shape must be [1, height, width, 3] and instead was" + (input2["shape"] ? input2["shape"].toString() : "unknown")); throw new Error("input tensor has no shape");
} else { if (input2.shape.length === 3) {
return { tensor: clone(input2), canvas: config3.filter.return ? outCanvas : null }; if (input2.shape[2] === 3) {
tensor2 = expandDims(input2, 0);
} else if (input2.shape[2] === 4) {
const rgb2 = slice3d(input2, [0, 0, 0], [-1, -1, 3]);
tensor2 = expandDims(rgb2, 0);
dispose(rgb2);
}
} else if (input2.shape.length === 4) {
if (input2.shape[3] === 3) {
tensor2 = clone(input2);
} else if (input2.shape[3] === 4) {
tensor2 = slice4d(input2, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
if (tensor2 == null || tensor2.shape.length !== 4 || tensor2.shape[0] !== 1 || tensor2.shape[3] !== 3)
throw new Error(`could not process input tensor with shape: ${input2["shape"]}`);
if (tensor2.dtype === "int32") {
const cast7 = cast(tensor2, "float32");
dispose(tensor2);
tensor2 = cast7;
}
return { tensor: tensor2, canvas: config3.filter.return ? outCanvas : null };
} else { } else {
if (typeof input2["readyState"] !== "undefined" && input2["readyState"] <= 2) { if (typeof input2["readyState"] !== "undefined" && input2["readyState"] <= 2) {
if (config3.debug) if (config3.debug)

File diff suppressed because one or more lines are too long

282
dist/human.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1016,13 +1016,34 @@ async function process2(input, config3, getTensor = true) {
throw new Error("input type is not recognized"); throw new Error("input type is not recognized");
} }
if (input instanceof tf2.Tensor) { if (input instanceof tf2.Tensor) {
if (input["isDisposedInternal"]) { let tensor3 = null;
if (input["isDisposedInternal"])
throw new Error("input tensor is disposed"); throw new Error("input tensor is disposed");
} else if (!input.shape || input.shape.length !== 4 || input.shape[0] !== 1 || input.shape[3] !== 3) { if (!input["shape"])
throw new Error("input tensor shape must be [1, height, width, 3] and instead was" + (input["shape"] ? input["shape"].toString() : "unknown")); throw new Error("input tensor has no shape");
} else { if (input.shape.length === 3) {
return { tensor: tf2.clone(input), canvas: config3.filter.return ? outCanvas : null }; if (input.shape[2] === 3) {
tensor3 = tf2.expandDims(input, 0);
} else if (input.shape[2] === 4) {
const rgb2 = tf2.slice3d(input, [0, 0, 0], [-1, -1, 3]);
tensor3 = tf2.expandDims(rgb2, 0);
tf2.dispose(rgb2);
}
} else if (input.shape.length === 4) {
if (input.shape[3] === 3) {
tensor3 = tf2.clone(input);
} else if (input.shape[3] === 4) {
tensor3 = tf2.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
if (tensor3 == null || tensor3.shape.length !== 4 || tensor3.shape[0] !== 1 || tensor3.shape[3] !== 3)
throw new Error(`could not process input tensor with shape: ${input["shape"]}`);
if (tensor3.dtype === "int32") {
const cast5 = tf2.cast(tensor3, "float32");
tf2.dispose(tensor3);
tensor3 = cast5;
}
return { tensor: tensor3, canvas: config3.filter.return ? outCanvas : null };
} else { } else {
if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) { if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) {
if (config3.debug) if (config3.debug)

View File

@ -1017,13 +1017,34 @@ async function process2(input, config3, getTensor = true) {
throw new Error("input type is not recognized"); throw new Error("input type is not recognized");
} }
if (input instanceof tf2.Tensor) { if (input instanceof tf2.Tensor) {
if (input["isDisposedInternal"]) { let tensor3 = null;
if (input["isDisposedInternal"])
throw new Error("input tensor is disposed"); throw new Error("input tensor is disposed");
} else if (!input.shape || input.shape.length !== 4 || input.shape[0] !== 1 || input.shape[3] !== 3) { if (!input["shape"])
throw new Error("input tensor shape must be [1, height, width, 3] and instead was" + (input["shape"] ? input["shape"].toString() : "unknown")); throw new Error("input tensor has no shape");
} else { if (input.shape.length === 3) {
return { tensor: tf2.clone(input), canvas: config3.filter.return ? outCanvas : null }; if (input.shape[2] === 3) {
tensor3 = tf2.expandDims(input, 0);
} else if (input.shape[2] === 4) {
const rgb2 = tf2.slice3d(input, [0, 0, 0], [-1, -1, 3]);
tensor3 = tf2.expandDims(rgb2, 0);
tf2.dispose(rgb2);
}
} else if (input.shape.length === 4) {
if (input.shape[3] === 3) {
tensor3 = tf2.clone(input);
} else if (input.shape[3] === 4) {
tensor3 = tf2.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
if (tensor3 == null || tensor3.shape.length !== 4 || tensor3.shape[0] !== 1 || tensor3.shape[3] !== 3)
throw new Error(`could not process input tensor with shape: ${input["shape"]}`);
if (tensor3.dtype === "int32") {
const cast5 = tf2.cast(tensor3, "float32");
tf2.dispose(tensor3);
tensor3 = cast5;
}
return { tensor: tensor3, canvas: config3.filter.return ? outCanvas : null };
} else { } else {
if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) { if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) {
if (config3.debug) if (config3.debug)

31
dist/human.node.js vendored
View File

@ -1016,13 +1016,34 @@ async function process2(input, config3, getTensor = true) {
throw new Error("input type is not recognized"); throw new Error("input type is not recognized");
} }
if (input instanceof tf2.Tensor) { if (input instanceof tf2.Tensor) {
if (input["isDisposedInternal"]) { let tensor3 = null;
if (input["isDisposedInternal"])
throw new Error("input tensor is disposed"); throw new Error("input tensor is disposed");
} else if (!input.shape || input.shape.length !== 4 || input.shape[0] !== 1 || input.shape[3] !== 3) { if (!input["shape"])
throw new Error("input tensor shape must be [1, height, width, 3] and instead was" + (input["shape"] ? input["shape"].toString() : "unknown")); throw new Error("input tensor has no shape");
} else { if (input.shape.length === 3) {
return { tensor: tf2.clone(input), canvas: config3.filter.return ? outCanvas : null }; if (input.shape[2] === 3) {
tensor3 = tf2.expandDims(input, 0);
} else if (input.shape[2] === 4) {
const rgb2 = tf2.slice3d(input, [0, 0, 0], [-1, -1, 3]);
tensor3 = tf2.expandDims(rgb2, 0);
tf2.dispose(rgb2);
}
} else if (input.shape.length === 4) {
if (input.shape[3] === 3) {
tensor3 = tf2.clone(input);
} else if (input.shape[3] === 4) {
tensor3 = tf2.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
if (tensor3 == null || tensor3.shape.length !== 4 || tensor3.shape[0] !== 1 || tensor3.shape[3] !== 3)
throw new Error(`could not process input tensor with shape: ${input["shape"]}`);
if (tensor3.dtype === "int32") {
const cast5 = tf2.cast(tensor3, "float32");
tf2.dispose(tensor3);
tensor3 = cast5;
}
return { tensor: tensor3, canvas: config3.filter.return ? outCanvas : null };
} else { } else {
if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) { if (typeof input["readyState"] !== "undefined" && input["readyState"] <= 2) {
if (config3.debug) if (config3.debug)

View File

@ -77,15 +77,33 @@ export async function process(input: Input, config: Config, getTensor: boolean =
) { ) {
throw new Error('input type is not recognized'); throw new Error('input type is not recognized');
} }
if (input instanceof tf.Tensor) { if (input instanceof tf.Tensor) { // if input is tensor use as-is without filters but correct shape as needed
// if input is tensor, use as-is let tensor: Tensor | null = null;
if ((input)['isDisposedInternal']) { if ((input as Tensor)['isDisposedInternal']) throw new Error('input tensor is disposed');
throw new Error('input tensor is disposed'); if (!(input as Tensor)['shape']) throw new Error('input tensor has no shape');
} else if (!(input as Tensor).shape || (input as Tensor).shape.length !== 4 || (input as Tensor).shape[0] !== 1 || (input as Tensor).shape[3] !== 3) { if ((input as Tensor).shape.length === 3) { // [height, width, 3 || 4]
throw new Error('input tensor shape must be [1, height, width, 3] and instead was' + (input['shape'] ? input['shape'].toString() : 'unknown')); if ((input as Tensor).shape[2] === 3) { // [height, width, 3] so add batch
} else { tensor = tf.expandDims(input, 0);
return { tensor: tf.clone(input), canvas: (config.filter.return ? outCanvas : null) }; } else if ((input as Tensor).shape[2] === 4) { // [height, width, 4] so strip alpha and add batch
const rgb = tf.slice3d(input, [0, 0, 0], [-1, -1, 3]);
tensor = tf.expandDims(rgb, 0);
tf.dispose(rgb);
}
} else if ((input as Tensor).shape.length === 4) { // [1, width, height, 3 || 4]
if ((input as Tensor).shape[3] === 3) { // [1, width, height, 3] just clone
tensor = tf.clone(input);
} else if ((input as Tensor).shape[3] === 4) { // [1, width, height, 4] so strip alpha
tensor = tf.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
}
} }
// at the end shape must be [1, height, width, 3]
if (tensor == null || tensor.shape.length !== 4 || tensor.shape[0] !== 1 || tensor.shape[3] !== 3) throw new Error(`could not process input tensor with shape: ${input['shape']}`);
if ((tensor as Tensor).dtype === 'int32') {
const cast = tf.cast(tensor, 'float32');
tf.dispose(tensor);
tensor = cast;
}
return { tensor, canvas: (config.filter.return ? outCanvas : null) };
} else { } else {
// check if resizing will be needed // check if resizing will be needed
if (typeof input['readyState'] !== 'undefined' && input['readyState'] <= 2) { if (typeof input['readyState'] !== 'undefined' && input['readyState'] <= 2) {

View File

@ -1,26 +1,26 @@
2021-11-09 18:09:34 INFO:  @vladmandic/human version 2.5.1 2021-11-09 19:33:44 INFO:  @vladmandic/human version 2.5.1
2021-11-09 18:09:34 INFO:  User: vlado Platform: linux Arch: x64 Node: v17.0.1 2021-11-09 19:33:44 INFO:  User: vlado Platform: linux Arch: x64 Node: v17.0.1
2021-11-09 18:09:34 INFO:  Application: {"name":"@vladmandic/human","version":"2.5.1"} 2021-11-09 19:33:44 INFO:  Application: {"name":"@vladmandic/human","version":"2.5.1"}
2021-11-09 18:09:34 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true} 2021-11-09 19:33:44 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true}
2021-11-09 18:09:34 INFO:  Toolchain: {"build":"0.6.3","esbuild":"0.13.12","typescript":"4.4.4","typedoc":"0.22.8","eslint":"8.2.0"} 2021-11-09 19:33:44 INFO:  Toolchain: {"build":"0.6.3","esbuild":"0.13.12","typescript":"4.4.4","typedoc":"0.22.8","eslint":"8.2.0"}
2021-11-09 18:09:34 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]} 2021-11-09 19:33:44 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]}
2021-11-09 18:09:34 STATE: Clean: {"locations":["dist/*","types/*","typedoc/*"]} 2021-11-09 19:33:44 STATE: Clean: {"locations":["dist/*","types/*","typedoc/*"]}
2021-11-09 18:09:34 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":1275} 2021-11-09 19:33:44 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":1275}
2021-11-09 18:09:34 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":57,"inputBytes":526408,"outputBytes":444881} 2021-11-09 19:33:45 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":57,"inputBytes":527426,"outputBytes":445566}
2021-11-09 18:09:34 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":1283} 2021-11-09 19:33:45 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":1283}
2021-11-09 18:09:34 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":57,"inputBytes":526416,"outputBytes":444885} 2021-11-09 19:33:45 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":57,"inputBytes":527434,"outputBytes":445570}
2021-11-09 18:09:34 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":1350} 2021-11-09 19:33:45 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":1350}
2021-11-09 18:09:34 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":57,"inputBytes":526483,"outputBytes":444957} 2021-11-09 19:33:45 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":57,"inputBytes":527501,"outputBytes":445642}
2021-11-09 18:09:34 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1063,"outputBytes":1652} 2021-11-09 19:33:45 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1063,"outputBytes":1652}
2021-11-09 18:09:34 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":2326,"outputBytes":912} 2021-11-09 19:33:45 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":2326,"outputBytes":912}
2021-11-09 18:09:34 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":57,"inputBytes":526045,"outputBytes":446888} 2021-11-09 19:33:45 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":57,"inputBytes":527063,"outputBytes":447664}
2021-11-09 18:09:34 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":2562703,"outputBytes":2497652} 2021-11-09 19:33:45 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":2562703,"outputBytes":2497652}
2021-11-09 18:09:35 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":57,"inputBytes":3022785,"outputBytes":1614549} 2021-11-09 19:33:46 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":57,"inputBytes":3023803,"outputBytes":1614837}
2021-11-09 18:09:35 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":57,"inputBytes":3022785,"outputBytes":2950223} 2021-11-09 19:33:46 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":57,"inputBytes":3023803,"outputBytes":2950885}
2021-11-09 18:09:55 STATE: Typings: {"input":"src/human.ts","output":"types","files":50} 2021-11-09 19:34:06 STATE: Typings: {"input":"src/human.ts","output":"types","files":50}
2021-11-09 18:10:02 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":49,"generated":true} 2021-11-09 19:34:13 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":49,"generated":true}
2021-11-09 18:10:02 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":5801,"outputBytes":3822} 2021-11-09 19:34:13 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":5801,"outputBytes":3822}
2021-11-09 18:10:02 STATE: Compile: {"name":"demo/facerecognition","format":"esm","platform":"browser","input":"demo/facerecognition/index.ts","output":"demo/facerecognition/index.js","files":1,"inputBytes":8949,"outputBytes":6529} 2021-11-09 19:34:13 STATE: Compile: {"name":"demo/facerecognition","format":"esm","platform":"browser","input":"demo/facerecognition/index.ts","output":"demo/facerecognition/index.js","files":1,"inputBytes":8949,"outputBytes":6529}
2021-11-09 18:10:41 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":91,"errors":0,"warnings":0} 2021-11-09 19:34:48 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":91,"errors":0,"warnings":0}
2021-11-09 18:10:42 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"} 2021-11-09 19:34:48 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"}
2021-11-09 18:10:42 INFO:  Done... 2021-11-09 19:34:48 INFO:  Done...

View File

@ -27,7 +27,7 @@ async function testHTTP() {
}); });
} }
async function getImage(human, input) { async function getImage(human, input, options = { channels: 3, expand: true, cast: true }) {
let img; let img;
try { try {
img = await canvasJS.loadImage(input); img = await canvasJS.loadImage(input);
@ -39,18 +39,15 @@ async function getImage(human, input) {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height); ctx.drawImage(img, 0, 0, img.width, img.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const res = human.tf.tidy(() => {
const tensor = human.tf.tensor(Array.from(imageData.data), [canvas.height, canvas.width, 4], 'float32'); // create rgba image tensor from flat array const data = human.tf.tensor(Array.from(imageData.data), [canvas.height, canvas.width, 4], options.cast ? 'float32' : 'int32'); // create rgba image tensor from flat array
const channels = human.tf.split(tensor, 4, 2); // split rgba to channels const channels = options.channels === 3 ? human.tf.slice3d(data, [0, 0, 0], [-1, -1, 3]) : human.tf.clone(data); // optionally strip alpha channel
const rgb = human.tf.stack([channels[0], channels[1], channels[2]], 2); // stack channels back to rgb const tensor = options.expand ? human.tf.expandDims(channels, 0) : human.tf.clone(channels); // optionally add batch num dimension
const reshape = human.tf.reshape(rgb, [1, canvas.height, canvas.width, 3]); // move extra dim from the end of tensor and use it as batch number instead human.tf.dispose([data, channels]);
return reshape; const sum = human.tf.sum(tensor);
}); log('state', 'passed: load image:', input, tensor.shape, { checksum: sum.dataSync()[0] });
const sum = human.tf.sum(res);
if (res && res.shape[0] === 1 && res.shape[3] === 3) log('state', 'passed: load image:', input, res.shape, { checksum: sum.dataSync()[0] });
else log('error', 'failed: load image:', input, res);
human.tf.dispose(sum); human.tf.dispose(sum);
return res; return tensor;
} }
function printResults(detect) { function printResults(detect) {
@ -96,8 +93,6 @@ async function testWarmup(human, title) {
} }
if (warmup) { if (warmup) {
log('state', 'passed: warmup:', config.warmup, title); log('state', 'passed: warmup:', config.warmup, title);
// const count = human.tf.engine().state.numTensors;
// if (count - tensors > 0) log('warn', 'failed: memory', config.warmup, title, 'tensors:', count - tensors);
printResults(warmup); printResults(warmup);
} else { } else {
log('error', 'failed: warmup:', config.warmup, title); log('error', 'failed: warmup:', config.warmup, title);
@ -176,6 +171,41 @@ async function verifyDetails(human) {
} }
} }
async function testTensorShapes(human, input) {
await human.load(config);
const numTensors = human.tf.engine().state.numTensors;
let res;
let tensor;
tensor = await getImage(human, input, { channels: 4, expand: true, cast: true });
res = await human.detect(tensor, config);
verify(res.face.length === 1 && res.face[0].gender === 'female', 'tensor shape:', tensor.shape, 'dtype:', tensor.dtype);
human.tf.dispose(tensor);
tensor = await getImage(human, input, { channels: 4, expand: false, cast: true });
res = await human.detect(tensor, config);
verify(res.face.length === 1 && res.face[0].gender === 'female', 'tensor shape:', tensor.shape, 'dtype:', tensor.dtype);
human.tf.dispose(tensor);
tensor = await getImage(human, input, { channels: 3, expand: true, cast: true });
res = await human.detect(tensor, config);
verify(res.face.length === 1 && res.face[0].gender === 'female', 'tensor shape:', tensor.shape, 'dtype:', tensor.dtype);
human.tf.dispose(tensor);
tensor = await getImage(human, input, { channels: 3, expand: false, cast: true });
res = await human.detect(tensor, config);
verify(res.face.length === 1 && res.face[0].gender === 'female', 'tensor shape:', tensor.shape, 'dtype:', tensor.dtype);
human.tf.dispose(tensor);
tensor = await getImage(human, input, { channels: 4, expand: true, cast: false });
res = await human.detect(tensor, config);
verify(res.face.length === 1 && res.face[0].gender === 'female', 'tensor shape:', tensor.shape, 'dtype:', tensor.dtype);
human.tf.dispose(tensor);
const leak = human.tf.engine().state.numTensors - numTensors;
if (leak !== 0) log('error', 'failed: memory leak', leak);
}
async function verifyCompare(human) { async function verifyCompare(human) {
log('info', 'test: input compare'); log('info', 'test: input compare');
const t1 = await getImage(human, 'samples/in/ai-face.jpg'); const t1 = await getImage(human, 'samples/in/ai-face.jpg');
@ -253,6 +283,7 @@ async function test(Human, inputConfig) {
}); });
await verifyDetails(human); await verifyDetails(human);
await testTensorShapes(human, 'samples/in/ai-body.jpg');
// test default config async // test default config async
log('info', 'test default'); log('info', 'test default');

File diff suppressed because it is too large Load Diff