human/test/test-main.js

226 lines
8.5 KiB
JavaScript
Raw Normal View History

2021-04-14 18:53:00 +02:00
const process = require('process');
const canvasJS = require('canvas');
2021-08-31 19:29:29 +02:00
let fetch; // fetch is dynamically imported later
2021-09-13 19:30:46 +02:00
let tensors = 0;
2021-04-14 18:53:00 +02:00
let config;
2021-04-15 15:43:55 +02:00
const log = (status, ...data) => {
2021-04-15 23:13:27 +02:00
if (typeof process.send !== 'undefined') process.send([status, data]); // send to parent process over ipc
// eslint-disable-next-line no-console
else console.log(status, ...data); // write to console if no parent process
2021-04-15 15:43:55 +02:00
};
2021-04-14 18:53:00 +02:00
async function testHTTP() {
if (config.modelBasePath.startsWith('file:')) return true;
return new Promise((resolve) => {
fetch(config.modelBasePath)
.then((res) => {
2021-04-15 15:43:55 +02:00
if (res && res.ok) log('state', 'passed: model server:', config.modelBasePath);
else log('error', 'failed: model server:', config.modelBasePath);
2021-04-14 18:53:00 +02:00
resolve(res && res.ok);
})
.catch((err) => {
2021-04-15 15:43:55 +02:00
log('error', 'failed: model server:', err.message);
2021-04-14 18:53:00 +02:00
resolve(false);
});
});
}
async function getImage(human, input) {
let img;
try {
img = await canvasJS.loadImage(input);
} catch (err) {
2021-04-15 15:43:55 +02:00
log('error', 'failed: load image', input, err.message);
2021-04-14 18:53:00 +02:00
return img;
}
const canvas = canvasJS.createCanvas(img.width, img.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.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], 'int32'); // create rgba image tensor from flat array
const channels = human.tf.split(tensor, 4, 2); // split rgba to channels
const rgb = human.tf.stack([channels[0], channels[1], channels[2]], 2); // stack channels back to rgb
2021-04-15 21:26:31 +02:00
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
2021-04-14 18:53:00 +02:00
return reshape;
});
2021-04-15 15:43:55 +02:00
if (res && res.shape[0] === 1 && res.shape[3] === 3) log('state', 'passed: load image:', input, res.shape);
else log('error', 'failed: load image:', input, res);
2021-04-14 18:53:00 +02:00
return res;
}
function printResults(detect) {
2021-09-13 19:30:46 +02:00
const person = (detect.face && detect.face[0]) ? { score: detect.face[0].score, age: detect.face[0].age, gender: detect.face[0].gender } : {};
2021-04-14 18:53:00 +02:00
const object = (detect.object && detect.object[0]) ? { score: detect.object[0].score, class: detect.object[0].label } : {};
const body = (detect.body && detect.body[0]) ? { score: detect.body[0].score, keypoints: detect.body[0].keypoints.length } : {};
const persons = detect.persons;
if (detect.face) log('data', ' result: face:', detect.face?.length, 'body:', detect.body?.length, 'hand:', detect.hand?.length, 'gesture:', detect.gesture?.length, 'object:', detect.object?.length, 'person:', persons.length, person, object, body);
2021-04-15 15:43:55 +02:00
if (detect.performance) log('data', ' result: performance:', 'load:', detect?.performance.load, 'total:', detect.performance?.total);
2021-04-14 18:53:00 +02:00
}
async function testInstance(human) {
2021-04-15 15:43:55 +02:00
if (human) log('state', 'passed: create human');
else log('error', 'failed: create human');
2021-04-14 18:53:00 +02:00
// if (!human.tf) human.tf = tf;
2021-04-15 15:43:55 +02:00
log('info', 'human version:', human.version);
log('info', 'platform:', human.env.platform, 'agent:', human.env.agent);
2021-04-15 15:43:55 +02:00
log('info', 'tfjs version:', human.tf.version.tfjs);
2021-04-14 18:53:00 +02:00
await human.load();
2021-09-13 19:30:46 +02:00
tensors = human.tf.engine().state.numTensors;
2021-04-15 15:43:55 +02:00
if (config.backend === human.tf.getBackend()) log('state', 'passed: set backend:', config.backend);
else log('error', 'failed: set backend:', config.backend);
2021-09-13 19:30:46 +02:00
log('state', 'tensors', tensors);
2021-04-14 18:53:00 +02:00
if (human.models) {
2021-04-15 15:43:55 +02:00
log('state', 'passed: load models');
2021-04-14 18:53:00 +02:00
const keys = Object.keys(human.models);
const loaded = keys.filter((model) => human.models[model]);
2021-04-15 15:43:55 +02:00
log('state', ' result: defined models:', keys.length, 'loaded models:', loaded.length);
2021-04-14 18:53:00 +02:00
return true;
}
2021-04-15 15:43:55 +02:00
log('error', 'failed: load models');
2021-04-14 18:53:00 +02:00
return false;
}
async function testWarmup(human, title) {
let warmup;
try {
warmup = await human.warmup(config);
} catch (err) {
2021-04-15 15:43:55 +02:00
log('error', 'error warmup');
2021-04-14 18:53:00 +02:00
}
if (warmup) {
2021-04-15 15:43:55 +02:00
log('state', 'passed: warmup:', config.warmup, title);
2021-09-13 19:30:46 +02:00
// const count = human.tf.engine().state.numTensors;
// if (count - tensors > 0) log('warn', 'failed: memory', config.warmup, title, 'tensors:', count - tensors);
2021-04-14 18:53:00 +02:00
printResults(warmup);
return true;
}
2021-04-15 15:43:55 +02:00
log('error', 'failed: warmup:', config.warmup, title);
2021-04-14 18:53:00 +02:00
return false;
}
async function testDetect(human, input, title) {
2021-09-13 19:30:46 +02:00
await human.load(config);
tensors = human.tf.engine().state.numTensors;
2021-04-14 18:53:00 +02:00
const image = input ? await getImage(human, input) : human.tf.randomNormal([1, 1024, 1024, 3]);
if (!image) {
2021-04-15 15:43:55 +02:00
log('error', 'failed: detect: input is null');
2021-04-14 18:53:00 +02:00
return false;
}
let detect;
try {
detect = await human.detect(image, config);
} catch (err) {
2021-04-15 15:43:55 +02:00
log('error', 'error: detect', err);
2021-04-14 18:53:00 +02:00
}
if (image instanceof human.tf.Tensor) human.tf.dispose(image);
if (detect) {
2021-04-15 15:43:55 +02:00
log('state', 'passed: detect:', input || 'random', title);
2021-09-13 19:30:46 +02:00
// const count = human.tf.engine().state.numTensors;
// if (count - tensors > 0) log('warn', 'failed: memory', config.warmup, title, 'tensors:', count - tensors);
2021-04-14 18:53:00 +02:00
printResults(detect);
return true;
}
2021-04-15 15:43:55 +02:00
log('error', 'failed: detect', input || 'random', title);
2021-04-14 18:53:00 +02:00
return false;
}
2021-09-13 19:30:46 +02:00
const evt = { image: 0, detect: 0, warmup: 0 };
async function events(event) {
log('state', 'event:', event);
evt[event]++;
}
2021-04-14 18:53:00 +02:00
async function test(Human, inputConfig) {
config = inputConfig;
2021-08-31 19:29:29 +02:00
fetch = (await import('node-fetch')).default;
2021-04-14 18:53:00 +02:00
const ok = await testHTTP();
if (!ok) {
2021-04-15 15:43:55 +02:00
log('error', 'aborting test');
2021-04-14 18:53:00 +02:00
return;
}
const t0 = process.hrtime.bigint();
let human;
// test event emitter
human = new Human(config);
2021-09-13 19:30:46 +02:00
human.events.addEventListener('warmup', () => events('warmup'));
human.events.addEventListener('image', () => events('image'));
human.events.addEventListener('detect', () => events('detect'));
// test warmup sequences
2021-04-14 18:53:00 +02:00
await testInstance(human);
config.warmup = 'none';
await testWarmup(human, 'default');
config.warmup = 'face';
await testWarmup(human, 'default');
config.warmup = 'body';
await testWarmup(human, 'default');
// test default config
2021-09-13 19:30:46 +02:00
log('info', 'test default');
human = new Human(config);
await testDetect(human, 'samples/ai-body.jpg', 'default');
// test detectors only
log('info', 'test detectors');
config.face = { mesh: { enabled: false }, iris: { enabled: false }, hand: { landmarks: false } };
human = new Human(config);
2021-09-13 19:30:46 +02:00
await testDetect(human, 'samples/ai-body.jpg', 'default');
// test posenet and movenet
2021-04-15 15:43:55 +02:00
log('info', 'test body variants');
2021-09-13 19:30:46 +02:00
config.body = { modelPath: 'posenet.json' };
2021-06-02 19:35:33 +02:00
await testDetect(human, 'samples/ai-body.jpg', 'posenet');
2021-09-13 19:30:46 +02:00
config.body = { modelPath: 'movenet-lightning.json' };
2021-06-02 19:35:33 +02:00
await testDetect(human, 'samples/ai-body.jpg', 'movenet');
// test multiple instances
const first = new Human(config);
const second = new Human(config);
2021-04-14 18:53:00 +02:00
await testDetect(human, null, 'default');
2021-04-15 15:43:55 +02:00
log('info', 'test: first instance');
await testDetect(first, 'samples/ai-upper.jpg', 'default');
2021-04-15 15:43:55 +02:00
log('info', 'test: second instance');
2021-06-02 19:35:33 +02:00
await testDetect(second, 'samples/ai-upper.jpg', 'default');
// test async multiple instances
2021-04-15 15:43:55 +02:00
log('info', 'test: concurrent');
2021-04-14 18:53:00 +02:00
await Promise.all([
2021-06-02 19:35:33 +02:00
testDetect(human, 'samples/ai-face.jpg', 'default'),
testDetect(first, 'samples/ai-face.jpg', 'default'),
2021-06-02 19:35:33 +02:00
testDetect(second, 'samples/ai-face.jpg', 'default'),
testDetect(human, 'samples/ai-body.jpg', 'default'),
testDetect(first, 'samples/ai-body.jpg', 'default'),
2021-06-02 19:35:33 +02:00
testDetect(second, 'samples/ai-body.jpg', 'default'),
testDetect(human, 'samples/ai-upper.jpg', 'default'),
testDetect(first, 'samples/ai-upper.jpg', 'default'),
testDetect(second, 'samples/ai-upper.jpg', 'default'),
2021-04-14 18:53:00 +02:00
]);
// tests end
2021-04-14 18:53:00 +02:00
const t1 = process.hrtime.bigint();
// check tensor leaks
2021-09-13 19:30:46 +02:00
const leak = human.tf.engine().state.numTensors - tensors;
if (leak === 0) log('state', 'passeed: no memory leak');
else log('error', 'failed: memory leak', leak);
// check if all instances reported same
const tensors1 = human.tf.engine().state.numTensors;
const tensors2 = first.tf.engine().state.numTensors;
const tensors3 = second.tf.engine().state.numTensors;
if (tensors1 === tensors2 && tensors1 === tensors3 && tensors2 === tensors3) log('state', 'passeed: equal usage');
else log('error', 'failed: equal usage', tensors1, tensors2, tensors3);
// report end
2021-09-13 19:30:46 +02:00
log('info', 'events:', evt);
2021-05-25 14:58:20 +02:00
log('info', 'test complete:', Math.trunc(Number(t1 - t0) / 1000 / 1000), 'ms');
2021-04-14 18:53:00 +02:00
}
exports.test = test;