2021-09-20 15:42:34 +02:00
const fs = require ( 'fs' ) ;
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-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 ) ;
} ) ;
} ) ;
}
2021-11-10 01:39:18 +01:00
async function getImage ( human , input , options = { channels : 3 , expand : true , cast : true } ) {
2021-04-14 18:53:00 +02:00
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 ) ;
2021-11-10 01:39:18 +01:00
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 = options . channels === 3 ? human . tf . slice3d ( data , [ 0 , 0 , 0 ] , [ - 1 , - 1 , 3 ] ) : human . tf . clone ( data ) ; // optionally strip alpha channel
const tensor = options . expand ? human . tf . expandDims ( channels , 0 ) : human . tf . clone ( channels ) ; // optionally add batch num dimension
human . tf . dispose ( [ data , channels ] ) ;
const sum = human . tf . sum ( tensor ) ;
log ( 'state' , 'passed: load image:' , input , tensor . shape , { checksum : sum . dataSync ( ) [ 0 ] } ) ;
2021-09-19 20:07:53 +02:00
human . tf . dispose ( sum ) ;
2021-11-10 01:39:18 +01:00
return tensor ;
2021-04-14 18:53:00 +02:00
}
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 } : { } ;
2021-05-24 17:10:13 +02:00
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 ) ;
2021-09-13 00:37:06 +02:00
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-11-15 15:26:38 +01:00
const bindingVer = human . tf . backend ( ) [ 'binding' ] ? human . tf . backend ( ) [ 'binding' ] [ 'TF_Version' ] : null ;
if ( bindingVer ) log ( 'info' , 'tensorflow binding version:' , bindingVer ) ;
2021-04-14 18:53:00 +02:00
await human . load ( ) ;
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-23 20:09:41 +02:00
log ( 'state' , 'tensors' , human . tf . memory ( ) . numTensors ) ;
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-04-14 18:53:00 +02:00
printResults ( warmup ) ;
2021-09-19 20:07:53 +02:00
} else {
log ( 'error' , 'failed: warmup:' , config . warmup , title ) ;
2021-04-14 18:53:00 +02:00
}
2021-09-19 20:07:53 +02:00
return warmup ;
2021-04-14 18:53:00 +02:00
}
2021-09-23 20:09:41 +02:00
async function testDetect ( human , input , title , checkLeak = true ) {
2021-09-13 19:30:46 +02:00
await human . load ( config ) ;
2021-09-23 20:09:41 +02:00
const 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 ) ;
2021-09-19 20:07:53 +02:00
} else {
log ( 'error' , 'failed: detect' , input || 'random' , title ) ;
2021-04-14 18:53:00 +02:00
}
2021-09-23 20:09:41 +02:00
// check tensor leaks
if ( checkLeak ) {
const leak = human . tf . engine ( ) . state . numTensors - tensors ;
if ( leak !== 0 ) log ( 'error' , 'failed: memory leak' , leak ) ;
}
2021-09-19 20:07:53 +02:00
return detect ;
2021-04-14 18:53:00 +02:00
}
2021-11-07 16:03:33 +01:00
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
2021-11-07 16:03:33 +01:00
const verify = ( state , ... messages ) => {
if ( state ) log ( 'state' , 'passed:' , ... messages ) ;
else log ( 'error' , 'failed:' , ... messages ) ;
} ;
async function verifyDetails ( human ) {
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: details verification' ) ;
2021-11-07 16:03:33 +01:00
const res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
verify ( res . face . length === 1 , 'details face length' , res . face . length ) ;
for ( const face of res . face ) {
verify ( face . score > 0.9 && face . boxScore > 0.9 && face . faceScore > 0.9 , 'details face score' , face . score , face . boxScore , face . faceScore ) ;
2021-11-08 13:36:26 +01:00
verify ( face . age > 23 && face . age < 24 && face . gender === 'female' && face . genderScore > 0.9 && face . iris > 70 && face . iris < 80 , 'details face age/gender' , face . age , face . gender , face . genderScore , face . iris ) ;
2021-11-07 16:03:33 +01:00
verify ( face . box . length === 4 && face . boxRaw . length === 4 && face . mesh . length === 478 && face . meshRaw . length === 478 && face . embedding . length === 1024 , 'details face arrays' , face . box . length , face . mesh . length , face . embedding . length ) ;
2021-11-08 13:36:26 +01:00
verify ( face . emotion . length === 3 && face . emotion [ 0 ] . score > 0.45 && face . emotion [ 0 ] . emotion === 'neutral' , 'details face emotion' , face . emotion . length , face . emotion [ 0 ] ) ;
2021-11-09 20:37:50 +01:00
verify ( face . real > 0.8 , 'details face anti-spoofing' , face . real ) ;
verify ( face . live > 0.8 , 'details face liveness' , face . live ) ;
2021-11-07 16:03:33 +01:00
}
verify ( res . body . length === 1 , 'details body length' , res . body . length ) ;
for ( const body of res . body ) {
verify ( body . score > 0.9 && body . box . length === 4 && body . boxRaw . length === 4 && body . keypoints . length === 17 && Object . keys ( body . annotations ) . length === 6 , 'details body' , body . score , body . keypoints . length , Object . keys ( body . annotations ) . length ) ;
}
verify ( res . hand . length === 1 , 'details hand length' , res . hand . length ) ;
for ( const hand of res . hand ) {
verify ( hand . score > 0.5 && hand . boxScore > 0.5 && hand . fingerScore > 0.5 && hand . box . length === 4 && hand . boxRaw . length === 4 && hand . label === 'point' , 'details hand' , hand . boxScore , hand . fingerScore , hand . label ) ;
verify ( hand . keypoints . length === 21 && Object . keys ( hand . landmarks ) . length === 5 && Object . keys ( hand . annotations ) . length === 6 , 'details hand arrays' , hand . keypoints . length , Object . keys ( hand . landmarks ) . length , Object . keys ( hand . annotations ) . length ) ;
}
2021-11-08 13:36:26 +01:00
verify ( res . gesture . length === 6 , 'details gesture length' , res . gesture . length ) ;
2021-11-07 16:03:33 +01:00
verify ( res . gesture [ 0 ] . gesture === 'facing right' , 'details gesture first' , res . gesture [ 0 ] ) ;
verify ( res . object . length === 1 , 'details object length' , res . object . length ) ;
for ( const obj of res . object ) {
verify ( obj . score > 0.7 && obj . label === 'person' && obj . box . length === 4 && obj . boxRaw . length === 4 , 'details object' , obj . score , obj . label ) ;
}
}
2021-11-10 01:39:18 +01:00
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 ) ;
}
2021-11-07 16:03:33 +01:00
async function verifyCompare ( human ) {
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: input compare' ) ;
2021-11-07 16:03:33 +01:00
const t1 = await getImage ( human , 'samples/in/ai-face.jpg' ) ;
const t2 = await getImage ( human , 'samples/in/ai-body.jpg' ) ;
const n1 = await human . compare ( t1 , t1 ) ;
const n2 = await human . compare ( t1 , t2 ) ;
const n3 = await human . compare ( t2 , t1 ) ;
const n4 = await human . compare ( t2 , t2 ) ;
verify ( n1 === 0 && n4 === 0 && Math . round ( n2 ) === Math . round ( n3 ) && n2 > 20 && n2 < 30 , 'image compare' , n1 , n2 ) ;
human . tf . dispose ( [ t1 , t2 ] ) ;
}
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 ( ) ;
2021-09-19 20:07:53 +02:00
let res ;
2021-09-19 01:09:02 +02:00
// test event emitter
2021-09-19 20:07:53 +02:00
const 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' ) ) ;
2021-09-19 20:07:53 +02:00
// test configuration validation
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: configuration validation' ) ;
2021-09-19 20:07:53 +02:00
let invalid = human . validate ( ) ;
if ( invalid . length === 0 ) log ( 'state' , 'passed: configuration default validation' , invalid ) ;
else log ( 'error' , 'failed: configuration default validation' , invalid ) ;
config . invalid = true ;
invalid = human . validate ( config ) ;
if ( invalid . length === 1 ) log ( 'state' , 'passed: configuration invalid validation' , invalid ) ;
else log ( 'error' , 'failed: configuration default validation' , invalid ) ;
delete config . invalid ;
2021-09-20 15:42:34 +02:00
// test model loading
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: model load' ) ;
2021-09-20 15:42:34 +02:00
await human . load ( ) ;
const models = Object . keys ( human . models ) . map ( ( model ) => ( { name : model , loaded : ( human . models [ model ] !== null ) } ) ) ;
const loaded = models . filter ( ( model ) => model . loaded ) ;
2021-11-09 20:37:50 +01:00
if ( models . length === 22 && loaded . length === 12 ) log ( 'state' , 'passed: models loaded' , models . length , loaded . length , models ) ;
2021-09-28 19:48:29 +02:00
else log ( 'error' , 'failed: models loaded' , models . length , loaded . length , models ) ;
// increase defaults
config . face = { detector : { maxDetected : 20 } } ;
2021-09-20 15:42:34 +02:00
2021-09-19 01:09:02 +02:00
// test warmup sequences
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: warmup' ) ;
2021-04-14 18:53:00 +02:00
await testInstance ( human ) ;
2021-09-28 19:48:29 +02:00
config . cacheSensitivity = 0 ;
2021-04-14 18:53:00 +02:00
config . warmup = 'none' ;
2021-09-19 20:07:53 +02:00
res = await testWarmup ( human , 'default' ) ;
if ( res . error !== 'null' ) log ( 'error' , 'failed: warmup none result mismatch' ) ;
else log ( 'state' , 'passed: warmup none result match' ) ;
2021-04-14 18:53:00 +02:00
config . warmup = 'face' ;
2021-09-19 20:07:53 +02:00
res = await testWarmup ( human , 'default' ) ;
2021-11-08 13:36:26 +01:00
if ( ! res || res ? . face ? . length !== 1 || res ? . body ? . length !== 1 || res ? . hand ? . length !== 1 || res ? . gesture ? . length < 7 ) log ( 'error' , 'failed: warmup face result mismatch' , res ? . face ? . length , res ? . body ? . length , res ? . hand ? . length , res ? . gesture ? . length ) ;
2021-09-19 20:07:53 +02:00
else log ( 'state' , 'passed: warmup face result match' ) ;
2021-04-14 18:53:00 +02:00
config . warmup = 'body' ;
2021-09-19 20:07:53 +02:00
res = await testWarmup ( human , 'default' ) ;
2021-11-08 13:36:26 +01:00
if ( ! res || res ? . face ? . length !== 1 || res ? . body ? . length !== 1 || res ? . hand ? . length !== 1 || res ? . gesture ? . length !== 6 ) log ( 'error' , 'failed: warmup body result mismatch' , res ? . face ? . length , res ? . body ? . length , res ? . hand ? . length , res ? . gesture ? . length ) ;
2021-09-19 20:07:53 +02:00
else log ( 'state' , 'passed: warmup body result match' ) ;
2021-11-03 21:32:07 +01:00
log ( 'state' , 'details:' , {
face : { boxScore : res . face [ 0 ] . boxScore , faceScore : res . face [ 0 ] . faceScore , age : res . face [ 0 ] . age , gender : res . face [ 0 ] . gender , genderScore : res . face [ 0 ] . genderScore } ,
emotion : res . face [ 0 ] . emotion ,
body : { score : res . body [ 0 ] . score , keypoints : res . body [ 0 ] . keypoints . length } ,
hand : { boxScore : res . hand [ 0 ] . boxScore , fingerScore : res . hand [ 0 ] . fingerScore , keypoints : res . hand [ 0 ] . keypoints . length } ,
gestures : res . gesture ,
} ) ;
2021-04-14 18:53:00 +02:00
2021-11-07 16:03:33 +01:00
await verifyDetails ( human ) ;
2021-11-10 01:39:18 +01:00
await testTensorShapes ( human , 'samples/in/ai-body.jpg' ) ;
2021-11-07 16:03:33 +01:00
2021-09-20 15:42:34 +02:00
// test default config async
2021-09-13 19:30:46 +02:00
log ( 'info' , 'test default' ) ;
2021-09-19 20:07:53 +02:00
human . reset ( ) ;
2021-09-20 15:42:34 +02:00
config . async = true ;
2021-09-19 20:07:53 +02:00
config . cacheSensitivity = 0 ;
2021-09-25 17:51:15 +02:00
res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
2021-11-03 21:32:07 +01:00
if ( ! res || res ? . face ? . length !== 1 || res ? . face [ 0 ] . gender !== 'female' ) log ( 'error' , 'failed: default result face mismatch' , res ? . face ? . length , res ? . face [ 0 ] . gender , res ? . face [ 0 ] . genderScore ) ;
else log ( 'state' , 'passed: default result face match' , res ? . face ? . length , res ? . face [ 0 ] . gender , res ? . face [ 0 ] . genderScore ) ;
2021-09-19 20:07:53 +02:00
2021-09-20 15:42:34 +02:00
// test default config sync
2021-09-19 20:20:22 +02:00
log ( 'info' , 'test sync' ) ;
human . reset ( ) ;
config . async = false ;
2021-09-20 15:42:34 +02:00
config . cacheSensitivity = 0 ;
2021-09-25 17:51:15 +02:00
res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
2021-11-03 21:32:07 +01:00
if ( ! res || res ? . face ? . length !== 1 || res ? . face [ 0 ] . gender !== 'female' ) log ( 'error' , 'failed: default sync' , res ? . face ? . length , res ? . face [ 0 ] . gender , res ? . face [ 0 ] . genderScore ) ;
else log ( 'state' , 'passed: default sync' , res ? . face ? . length , res ? . face [ 0 ] . gender , res ? . face [ 0 ] . genderScore ) ;
2021-09-19 20:20:22 +02:00
2021-09-20 15:42:34 +02:00
// test image processing
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: image process' ) ;
2021-09-20 15:42:34 +02:00
const img1 = await human . image ( null ) ;
2021-09-25 17:51:15 +02:00
const img2 = await human . image ( await getImage ( human , 'samples/in/ai-face.jpg' ) ) ;
2021-09-20 15:42:34 +02:00
if ( ! img1 || ! img2 || img1 . tensor !== null || img2 . tensor ? . shape ? . length !== 4 ) log ( 'error' , 'failed: image input' , img1 ? . tensor ? . shape , img2 ? . tensor ? . shape ) ;
else log ( 'state' , 'passed: image input' , img1 ? . tensor ? . shape , img2 ? . tensor ? . shape ) ;
// test null input
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: image null' ) ;
2021-09-20 15:42:34 +02:00
res = await human . detect ( null ) ;
if ( ! res || ! res . error ) log ( 'error' , 'failed: invalid input' , res ) ;
2021-11-14 17:22:52 +01:00
else log ( 'state' , 'passed: invalid input' , res . error || res ) ;
2021-09-20 15:42:34 +02:00
// test face similarity
log ( 'info' , 'test face similarity' ) ;
human . reset ( ) ;
config . async = false ;
config . cacheSensitivity = 0 ;
2021-09-25 17:51:15 +02:00
let res1 = await testDetect ( human , 'samples/in/ai-face.jpg' , 'default' ) ;
let res2 = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
let res3 = await testDetect ( human , 'samples/in/ai-upper.jpg' , 'default' ) ;
2021-09-20 15:42:34 +02:00
const desc1 = res1 && res1 . face && res1 . face [ 0 ] && res1 . face [ 0 ] . embedding ? [ ... res1 . face [ 0 ] . embedding ] : null ;
const desc2 = res2 && res2 . face && res2 . face [ 0 ] && res2 . face [ 0 ] . embedding ? [ ... res2 . face [ 0 ] . embedding ] : null ;
const desc3 = res3 && res3 . face && res3 . face [ 0 ] && res3 . face [ 0 ] . embedding ? [ ... res3 . face [ 0 ] . embedding ] : null ;
if ( ! desc1 || ! desc2 || ! desc3 || desc1 . length !== 1024 || desc2 . length !== 1024 || desc3 . length !== 1024 ) log ( 'error' , 'failed: face descriptor' , desc1 ? . length , desc2 ? . length , desc3 ? . length ) ;
else log ( 'state' , 'passed: face descriptor' ) ;
2021-09-30 20:28:16 +02:00
res1 = human . similarity ( desc1 , desc1 ) ;
res2 = human . similarity ( desc1 , desc2 ) ;
res3 = human . similarity ( desc1 , desc3 ) ;
2021-11-03 21:32:07 +01:00
if ( res1 < 1 || res2 < 0.50 || res3 < 0.50 ) log ( 'error' , 'failed: face similarity' , { similarity : [ res1 , res2 , res3 ] , descriptors : [ desc1 ? . length , desc2 ? . length , desc3 ? . length ] } ) ;
2021-09-30 20:28:16 +02:00
else log ( 'state' , 'passed: face similarity' , { similarity : [ res1 , res2 , res3 ] , descriptors : [ desc1 ? . length , desc2 ? . length , desc3 ? . length ] } ) ;
2021-09-20 15:42:34 +02:00
// test face matching
log ( 'info' , 'test face matching' ) ;
2021-09-30 20:28:16 +02:00
const db = JSON . parse ( fs . readFileSync ( 'demo/facematch/faces.json' ) . toString ( ) ) ;
const arr = db . map ( ( rec ) => rec . embedding ) ;
if ( db . length < 20 ) log ( 'error' , 'failed: face database ' , db . length ) ;
2021-09-20 15:42:34 +02:00
else log ( 'state' , 'passed: face database' , db . length ) ;
2021-09-30 20:28:16 +02:00
res1 = human . match ( desc1 , arr ) ;
res2 = human . match ( desc2 , arr ) ;
res3 = human . match ( desc3 , arr ) ;
2021-10-25 19:29:29 +02:00
if ( res1 . index !== 4 || res2 . index !== 4 || res3 . index !== 4 ) log ( 'error' , 'failed: face match' , res1 , res2 , res3 ) ;
2021-09-30 20:28:16 +02:00
else log ( 'state' , 'passed: face match' , { first : { index : res1 . index , similarity : res1 . similarity } } , { second : { index : res2 . index , similarity : res2 . similarity } } , { third : { index : res3 . index , similarity : res3 . similarity } } ) ;
2021-09-20 15:42:34 +02:00
2021-09-19 20:20:22 +02:00
// test object detection
2021-09-19 20:07:53 +02:00
log ( 'info' , 'test object' ) ;
human . reset ( ) ;
config . object = { enabled : true } ;
2021-09-25 17:51:15 +02:00
res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
2021-09-19 20:07:53 +02:00
if ( ! res || res ? . object ? . length !== 1 || res ? . object [ 0 ] ? . label !== 'person' ) log ( 'error' , 'failed: object result mismatch' , res ? . object ? . length ) ;
else log ( 'state' , 'passed: object result match' ) ;
// test sensitive config
log ( 'info' , 'test sensitive' ) ;
human . reset ( ) ;
config . cacheSensitivity = 0 ;
config . face = { detector : { minConfidence : 0.0001 , maxDetected : 1 } } ;
2021-09-28 19:48:29 +02:00
config . body = { minConfidence : 0.0001 } ;
config . hand = { minConfidence : 0.0001 } ;
2021-09-25 17:51:15 +02:00
res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
2021-11-08 13:36:26 +01:00
if ( ! res || res ? . face ? . length !== 1 || res ? . body ? . length !== 1 || res ? . hand ? . length !== 2 || res ? . gesture ? . length !== 8 ) log ( 'error' , 'failed: sensitive result mismatch' , res ? . face ? . length , res ? . body ? . length , res ? . hand ? . length , res ? . gesture ? . length ) ;
2021-09-19 20:07:53 +02:00
else log ( 'state' , 'passed: sensitive result match' ) ;
// test sensitive details face
const face = res && res . face ? res . face [ 0 ] : null ;
2021-09-28 19:48:29 +02:00
if ( ! face || face ? . box ? . length !== 4 || face ? . mesh ? . length !== 478 || face ? . embedding ? . length !== 1024 || face ? . rotation ? . matrix ? . length !== 9 ) {
log ( 'error' , 'failed: sensitive face result mismatch' , res ? . face ? . length , face ? . box ? . length , face ? . mesh ? . length , face ? . embedding ? . length , face ? . rotation ? . matrix ? . length ) ;
2021-09-19 20:07:53 +02:00
} else log ( 'state' , 'passed: sensitive face result match' ) ;
2021-11-02 16:42:15 +01:00
if ( ! face || face ? . emotion ? . length < 3 ) log ( 'error' , 'failed: sensitive face emotion result mismatch' , face ? . emotion . length ) ;
2021-11-03 21:32:07 +01:00
else log ( 'state' , 'passed: sensitive face emotion result' , face ? . emotion . length ) ;
2021-09-19 20:07:53 +02:00
// test sensitive details body
const body = res && res . body ? res . body [ 0 ] : null ;
if ( ! body || body ? . box ? . length !== 4 || body ? . keypoints ? . length !== 17 ) log ( 'error' , 'failed: sensitive body result mismatch' , body ) ;
else log ( 'state' , 'passed: sensitive body result match' ) ;
// test sensitive details hand
const hand = res && res . hand ? res . hand [ 0 ] : null ;
if ( ! hand || hand ? . box ? . length !== 4 || hand ? . keypoints ? . length !== 21 ) log ( 'error' , 'failed: sensitive hand result mismatch' , hand ? . keypoints ? . length ) ;
else log ( 'state' , 'passed: sensitive hand result match' ) ;
2021-09-19 01:09:02 +02:00
// test detectors only
log ( 'info' , 'test detectors' ) ;
2021-09-19 20:07:53 +02:00
human . reset ( ) ;
config . face = { mesh : { enabled : false } , iris : { enabled : false } , description : { enabled : false } , emotion : { enabled : false } } ;
config . hand = { landmarks : false } ;
2021-09-25 17:51:15 +02:00
res = await testDetect ( human , 'samples/in/ai-body.jpg' , 'default' ) ;
2021-09-19 20:07:53 +02:00
if ( ! res || res ? . face ? . length !== 1 || res ? . face [ 0 ] ? . gender || res ? . face [ 0 ] ? . age || res ? . face [ 0 ] ? . embedding ) log ( 'error' , 'failed: detectors result face mismatch' , res ? . face ) ;
else log ( 'state' , 'passed: detector result face match' ) ;
2021-09-28 19:48:29 +02:00
if ( ! res || res ? . hand ? . length !== 1 || res ? . hand [ 0 ] ? . landmarks ? . length > 0 ) log ( 'error' , 'failed: detectors result hand mismatch' , res ? . hand ? . length ) ;
2021-09-19 20:07:53 +02:00
else log ( 'state' , 'passed: detector result hand match' ) ;
2021-09-13 19:30:46 +02:00
2021-09-19 01:09:02 +02:00
// test multiple instances
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: multi-instance' ) ;
2021-09-19 01:09:02 +02:00
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' ) ;
2021-09-25 17:51:15 +02:00
await testDetect ( first , 'samples/in/ai-upper.jpg' , 'default' ) ;
2021-04-15 15:43:55 +02:00
log ( 'info' , 'test: second instance' ) ;
2021-09-25 17:51:15 +02:00
await testDetect ( second , 'samples/in/ai-upper.jpg' , 'default' ) ;
2021-09-19 01:09:02 +02:00
// 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-09-25 17:51:15 +02:00
testDetect ( human , 'samples/in/ai-face.jpg' , 'default' , false ) ,
testDetect ( first , 'samples/in/ai-face.jpg' , 'default' , false ) ,
testDetect ( second , 'samples/in/ai-face.jpg' , 'default' , false ) ,
testDetect ( human , 'samples/in/ai-body.jpg' , 'default' , false ) ,
testDetect ( first , 'samples/in/ai-body.jpg' , 'default' , false ) ,
testDetect ( second , 'samples/in/ai-body.jpg' , 'default' , false ) ,
testDetect ( human , 'samples/in/ai-upper.jpg' , 'default' , false ) ,
testDetect ( first , 'samples/in/ai-upper.jpg' , 'default' , false ) ,
testDetect ( second , 'samples/in/ai-upper.jpg' , 'default' , false ) ,
2021-04-14 18:53:00 +02:00
] ) ;
2021-09-19 01:09:02 +02:00
2021-09-20 15:42:34 +02:00
// test monkey-patch
2021-11-07 16:10:23 +01:00
log ( 'info' , 'test: monkey-patch' ) ;
2021-09-23 01:27:12 +02:00
globalThis . Canvas = canvasJS . Canvas ; // monkey-patch to use external canvas library
globalThis . ImageData = canvasJS . ImageData ; // monkey-patch to use external canvas library
2021-09-25 17:51:15 +02:00
const inputImage = await canvasJS . loadImage ( 'samples/in/ai-face.jpg' ) ; // load image using canvas library
2021-09-20 15:42:34 +02:00
const inputCanvas = new canvasJS . Canvas ( inputImage . width , inputImage . height ) ; // create canvas
const ctx = inputCanvas . getContext ( '2d' ) ;
ctx . drawImage ( inputImage , 0 , 0 ) ; // draw input image onto canvas
res = await human . detect ( inputCanvas ) ;
if ( ! res || res ? . face ? . length !== 1 ) log ( 'error' , 'failed: monkey patch' ) ;
else log ( 'state' , 'passed: monkey patch' ) ;
// test segmentation
res = await human . segmentation ( inputCanvas , inputCanvas ) ;
2021-09-23 01:27:12 +02:00
if ( ! res || ! res . data || ! res . canvas ) log ( 'error' , 'failed: segmentation' ) ;
2021-09-22 22:00:43 +02:00
else log ( 'state' , 'passed: segmentation' , [ res . data . length ] ) ;
2021-09-20 15:42:34 +02:00
human . env . Canvas = undefined ;
2021-09-19 01:09:02 +02:00
// 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 ) ;
2021-11-07 16:03:33 +01:00
// validate cache compare algorithm
await verifyCompare ( human ) ;
2021-11-02 16:42:15 +01:00
// tests end
const t1 = process . hrtime . bigint ( ) ;
2021-09-19 01:09:02 +02:00
// report end
2021-09-13 19:30:46 +02:00
log ( 'info' , 'events:' , evt ) ;
2021-09-23 20:09:41 +02:00
log ( 'info' , 'tensors' , human . tf . memory ( ) . numTensors ) ;
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 ;