From 5e1743695da2ec8afa4068b200c44c13b045e1ba Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 8 Aug 2022 15:09:26 -0400 Subject: [PATCH] add insightface --- CHANGELOG.md | 7 +- TODO.md | 12 + demo/faceid/README.md | 1 + demo/faceid/index.js | 4 +- demo/faceid/index.js.map | 6 +- demo/faceid/index.ts | 11 +- src/body/efficientpose.ts | 21 +- src/face/face.ts | 30 +- src/face/insightface.ts | 55 +++ src/face/match.ts | 4 +- src/face/mobilefacenet.ts | 9 +- src/models.ts | 3 + src/object/nanodet.ts | 104 ++-- test/build.log | 48 +- test/node.js | 5 +- test/test-main.js | 106 ++-- test/test.log | 982 ++++++++++++-------------------------- 17 files changed, 583 insertions(+), 825 deletions(-) create mode 100644 src/face/insightface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dab53ac..9617cb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # @vladmandic/human - Version: **2.9.1** + Version: **2.9.2** Description: **Human: AI-powered 3D Face Detection & Rotation Tracking, Face Description & Recognition, Body Pose Tracking, 3D Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction, Gesture Recognition** Author: **Vladimir Mandic ** @@ -9,7 +9,10 @@ ## Changelog -### **HEAD -> main** 2022/07/29 mandic00@live.com +### **2.9.2** 2022/08/08 mandic00@live.com + + +### **origin/main** 2022/08/04 mandic00@live.com ### **release: 2.9.1** 2022/07/25 mandic00@live.com diff --git a/TODO.md b/TODO.md index 7bcbcb6d..a8baa317 100644 --- a/TODO.md +++ b/TODO.md @@ -49,3 +49,15 @@ Feature is automatically disabled in **NodeJS** without user impact ## Pending Release Changes +- Add **InsightFace** model as alternative for face embedding/descriptor detection + compatible with multiple variations of **InsightFace** models + configurable using `config.face.insightface` config section + see `demo/faceid/index.ts` for usage + models can be downloaded from +- Fix **MobileFaceNet** model as alternative for face embedding/descriptor detection + configurable using `config.face.mobilefacenet` config section +- Fix **EfficientPose** module +- Fix **NanoDet** module +- Fix `human.match` when using mixed descriptor lengths +- Update profiling methods in `human.profile()` +- Update project dependencies diff --git a/demo/faceid/README.md b/demo/faceid/README.md index b3c0a071..a33e66f9 100644 --- a/demo/faceid/README.md +++ b/demo/faceid/README.md @@ -38,4 +38,5 @@ designed to serve as a quick check when used together with other indicators: **FaceID** is compatible with - `faceres.json` (default) perfoms combined age/gender/descriptor analysis - `faceres-deep.json` higher resolution variation of `faceres` +- `insightface` alternative model for face descriptor analysis - `mobilefacenet` alternative model for face descriptor analysis diff --git a/demo/faceid/index.js b/demo/faceid/index.js index 8beacbf6..efe3d385 100644 --- a/demo/faceid/index.js +++ b/demo/faceid/index.js @@ -4,6 +4,6 @@ author: ' */ -import{Human as H}from"../../dist/human.esm.js";var d,R="human",m="person",g=(...t)=>console.log("indexdb",...t);async function b(){return d?!0:new Promise(t=>{let i=indexedDB.open(R,1);i.onerror=s=>g("error:",s),i.onupgradeneeded=s=>{g("create:",s.target),d=s.target.result,d.createObjectStore(m,{keyPath:"id",autoIncrement:!0})},i.onsuccess=s=>{d=s.target.result,g("open:",d),t(!0)}})}async function C(){let t=[];return d||await b(),new Promise(i=>{let s=d.transaction([m],"readwrite").objectStore(m).openCursor(null,"next");s.onerror=o=>g("load error:",o),s.onsuccess=o=>{o.target.result?(t.push(o.target.result.value),o.target.result.continue()):i(t)}})}async function k(){return d||await b(),new Promise(t=>{let i=d.transaction([m],"readwrite").objectStore(m).count();i.onerror=s=>g("count error:",s),i.onsuccess=()=>t(i.result)})}async function x(t){d||await b();let i={name:t.name,descriptor:t.descriptor,image:t.image};d.transaction([m],"readwrite").objectStore(m).put(i),g("save:",i)}async function D(t){d||await b(),d.transaction([m],"readwrite").objectStore(m).delete(t.id),g("delete:",t)}var v={modelBasePath:"../../models",filter:{equalization:!0},face:{enabled:!0,detector:{rotation:!0,return:!0,cropFactor:1.6,mask:!1},description:{enabled:!0},mobilefacenet:{enabled:!1,modelPath:"https://vladmandic.github.io/human-models/models/mobilefacenet.json"},iris:{enabled:!0},emotion:{enabled:!1},antispoof:{enabled:!0},liveness:{enabled:!0}},body:{enabled:!1},hand:{enabled:!1},object:{enabled:!1},gesture:{enabled:!0}},I={order:2,multiplier:25,min:.2,max:.8},c={minConfidence:.6,minSize:224,maxTime:1e4,blinkMin:10,blinkMax:800,threshold:.5,mask:v.face.detector.mask,rotation:v.face.detector.rotation,cropFactor:v.face.detector.cropFactor,...I},n={faceCount:!1,faceConfidence:!1,facingCenter:!1,lookingCenter:!1,blinkDetected:!1,faceSize:!1,antispoofCheck:!1,livenessCheck:!1,elapsedMs:0},M=()=>n.faceCount&&n.faceSize&&n.blinkDetected&&n.facingCenter&&n.lookingCenter&&n.faceConfidence&&n.antispoofCheck&&n.livenessCheck,r={face:null,record:null},l={start:0,end:0,time:0},a=new H(v);a.env.perfadd=!1;a.draw.options.font='small-caps 18px "Lato"';a.draw.options.lineHeight=20;var e={video:document.getElementById("video"),canvas:document.getElementById("canvas"),log:document.getElementById("log"),fps:document.getElementById("fps"),match:document.getElementById("match"),name:document.getElementById("name"),save:document.getElementById("save"),delete:document.getElementById("delete"),retry:document.getElementById("retry"),source:document.getElementById("source"),ok:document.getElementById("ok")},h={detect:0,draw:0},y={detect:0,draw:0},E=0,p=(...t)=>{e.log.innerText+=t.join(" ")+` -`,console.log(...t)},w=t=>e.fps.innerText=t;async function S(){w("starting webcam...");let t={audio:!1,video:{facingMode:"user",resizeMode:"none",width:{ideal:document.body.clientWidth}}},i=await navigator.mediaDevices.getUserMedia(t),s=new Promise(o=>{e.video.onloadeddata=()=>o(!0)});e.video.srcObject=i,e.video.play(),await s,e.canvas.width=e.video.videoWidth,e.canvas.height=e.video.videoHeight,a.env.initial&&p("video:",e.video.videoWidth,e.video.videoHeight,"|",i.getVideoTracks()[0].label),e.canvas.onclick=()=>{e.video.paused?e.video.play():e.video.pause()}}async function T(){if(!e.video.paused){r.face&&r.face.tensor&&a.tf.dispose(r.face.tensor),await a.detect(e.video);let t=a.now();y.detect=1e3/(t-h.detect),h.detect=t,requestAnimationFrame(T)}}async function L(){let t=await a.next(a.result);await a.draw.canvas(e.video,e.canvas),await a.draw.all(e.canvas,t);let i=a.now();if(y.draw=1e3/(i-h.draw),h.draw=i,w(`fps: ${y.detect.toFixed(1).padStart(5," ")} detect | ${y.draw.toFixed(1).padStart(5," ")} draw`),n.faceCount=a.result.face.length===1,n.faceCount){let o=Object.values(a.result.gesture).map(f=>f.gesture);(o.includes("blink left eye")||o.includes("blink right eye"))&&(l.start=a.now()),l.start>0&&!o.includes("blink left eye")&&!o.includes("blink right eye")&&(l.end=a.now()),n.blinkDetected=n.blinkDetected||Math.abs(l.end-l.start)>c.blinkMin&&Math.abs(l.end-l.start)c.minConfidence&&(a.result.face[0].faceScore||0)>c.minConfidence&&(a.result.face[0].genderScore||0)>c.minConfidence,n.antispoofCheck=(a.result.face[0].real||0)>c.minConfidence,n.livenessCheck=(a.result.face[0].live||0)>c.minConfidence,n.faceSize=a.result.face[0].box[2]>=c.minSize&&a.result.face[0].box[3]>=c.minSize}let s=32;for(let[o,f]of Object.entries(n)){let u=document.getElementById(`ok-${o}`);u||(u=document.createElement("div"),u.innerText=o,u.className="ok",u.style.top=`${s}px`,e.ok.appendChild(u)),typeof f=="boolean"?u.style.backgroundColor=f?"lightgreen":"lightcoral":u.innerText=`${o}:${f}`,s+=28}return M()||n.elapsedMs>c.maxTime?(e.video.pause(),a.result.face[0]):(n.elapsedMs=Math.trunc(a.now()-E),new Promise(o=>{setTimeout(async()=>{await L()&&o(a.result.face[0])},30)}))}async function P(){var t,i;if(e.name.value.length>0){let s=(t=e.canvas.getContext("2d"))==null?void 0:t.getImageData(0,0,e.canvas.width,e.canvas.height),o={id:0,name:e.name.value,descriptor:(i=r.face)==null?void 0:i.embedding,image:s};await x(o),p("saved face record:",o.name)}else p("invalid name")}async function z(){r.record&&r.record.id>0&&await D(r.record)}async function j(){var o,f;if((o=e.canvas.getContext("2d"))==null||o.clearRect(0,0,c.minSize,c.minSize),!r.face||!r.face.tensor||!r.face.embedding)return!1;if(console.log("face record:",r.face),a.tf.browser.toPixels(r.face.tensor,e.canvas),await k()===0)return p("face database is empty"),document.body.style.background="black",e.delete.style.display="none",!1;let t=await C(),i=t.map(u=>u.descriptor),s=await a.match(r.face.embedding,i,I);return r.record=t[s.index]||null,r.record&&(p(`best match: ${r.record.name} | id: ${r.record.id} | similarity: ${Math.round(1e3*s.similarity)/10}%`),e.name.value=r.record.name,e.source.style.display="",(f=e.source.getContext("2d"))==null||f.putImageData(r.record.image,0,0)),document.body.style.background=s.similarity>c.threshold?"darkgreen":"maroon",s.similarity>c.threshold}async function B(){var t,i,s,o;return n.faceCount=!1,n.faceConfidence=!1,n.facingCenter=!1,n.blinkDetected=!1,n.faceSize=!1,n.antispoofCheck=!1,n.livenessCheck=!1,n.elapsedMs=0,e.match.style.display="none",e.retry.style.display="none",e.source.style.display="none",document.body.style.background="black",await S(),await T(),E=a.now(),r.face=await L(),e.canvas.width=((i=(t=r.face)==null?void 0:t.tensor)==null?void 0:i.shape[1])||c.minSize,e.canvas.height=((o=(s=r.face)==null?void 0:s.tensor)==null?void 0:o.shape[0])||c.minSize,e.source.width=e.canvas.width,e.source.height=e.canvas.height,e.canvas.style.width="",e.match.style.display="flex",e.save.style.display="flex",e.delete.style.display="flex",e.retry.style.display="block",M()?j():(p("did not find valid face"),!1)}async function q(){p("human version:",a.version,"| tfjs version:",a.tf.version["tfjs-core"]),p("options:",JSON.stringify(c).replace(/{|}|"|\[|\]/g,"").replace(/,/g," ")),w("loading..."),p("known face records:",await k()),await S(),await a.load(),w("initializing..."),e.retry.addEventListener("click",B),e.save.addEventListener("click",P),e.delete.addEventListener("click",z),await a.warmup(),await B()}window.onload=q; +import{Human as H}from"../../dist/human.esm.js";var l,R="human",p="person",g=(...t)=>console.log("indexdb",...t);async function v(){return l?!0:new Promise(t=>{let i=indexedDB.open(R,1);i.onerror=n=>g("error:",n),i.onupgradeneeded=n=>{g("create:",n.target),l=n.target.result,l.createObjectStore(p,{keyPath:"id",autoIncrement:!0})},i.onsuccess=n=>{l=n.target.result,g("open:",l),t(!0)}})}async function x(){let t=[];return l||await v(),new Promise(i=>{let n=l.transaction([p],"readwrite").objectStore(p).openCursor(null,"next");n.onerror=s=>g("load error:",s),n.onsuccess=s=>{s.target.result?(t.push(s.target.result.value),s.target.result.continue()):i(t)}})}async function y(){return l||await v(),new Promise(t=>{let i=l.transaction([p],"readwrite").objectStore(p).count();i.onerror=n=>g("count error:",n),i.onsuccess=()=>t(i.result)})}async function C(t){l||await v();let i={name:t.name,descriptor:t.descriptor,image:t.image};l.transaction([p],"readwrite").objectStore(p).put(i),g("save:",i)}async function D(t){l||await v(),l.transaction([p],"readwrite").objectStore(p).delete(t.id),g("delete:",t)}var b={modelBasePath:"../../models",filter:{equalization:!0},face:{enabled:!0,detector:{rotation:!0,return:!0,cropFactor:1.6,mask:!1},description:{enabled:!0},iris:{enabled:!0},emotion:{enabled:!1},antispoof:{enabled:!0},liveness:{enabled:!0}},body:{enabled:!1},hand:{enabled:!1},object:{enabled:!1},gesture:{enabled:!0}},I={order:2,multiplier:25,min:.2,max:.8},c={minConfidence:.6,minSize:224,maxTime:1e4,blinkMin:10,blinkMax:800,threshold:.5,mask:b.face.detector.mask,rotation:b.face.detector.rotation,cropFactor:b.face.detector.cropFactor,...I},o={faceCount:!1,faceConfidence:!1,facingCenter:!1,lookingCenter:!1,blinkDetected:!1,faceSize:!1,antispoofCheck:!1,livenessCheck:!1,elapsedMs:0},M=()=>o.faceCount&&o.faceSize&&o.blinkDetected&&o.facingCenter&&o.lookingCenter&&o.faceConfidence&&o.antispoofCheck&&o.livenessCheck,r={face:null,record:null},u={start:0,end:0,time:0},a=new H(b);a.env.perfadd=!1;a.draw.options.font='small-caps 18px "Lato"';a.draw.options.lineHeight=20;var e={video:document.getElementById("video"),canvas:document.getElementById("canvas"),log:document.getElementById("log"),fps:document.getElementById("fps"),match:document.getElementById("match"),name:document.getElementById("name"),save:document.getElementById("save"),delete:document.getElementById("delete"),retry:document.getElementById("retry"),source:document.getElementById("source"),ok:document.getElementById("ok")},w={detect:0,draw:0},h={detect:0,draw:0},E=0,m=(...t)=>{e.log.innerText+=t.join(" ")+` +`,console.log(...t)},k=t=>e.fps.innerText=t;async function S(){k("starting webcam...");let t={audio:!1,video:{facingMode:"user",resizeMode:"none",width:{ideal:document.body.clientWidth}}},i=await navigator.mediaDevices.getUserMedia(t),n=new Promise(s=>{e.video.onloadeddata=()=>s(!0)});e.video.srcObject=i,e.video.play(),await n,e.canvas.width=e.video.videoWidth,e.canvas.height=e.video.videoHeight,a.env.initial&&m("video:",e.video.videoWidth,e.video.videoHeight,"|",i.getVideoTracks()[0].label),e.canvas.onclick=()=>{e.video.paused?e.video.play():e.video.pause()}}async function T(){if(!e.video.paused){r.face&&r.face.tensor&&a.tf.dispose(r.face.tensor),await a.detect(e.video);let t=a.now();h.detect=1e3/(t-w.detect),w.detect=t,requestAnimationFrame(T)}}async function L(){let t=await a.next(a.result);await a.draw.canvas(e.video,e.canvas),await a.draw.all(e.canvas,t);let i=a.now();if(h.draw=1e3/(i-w.draw),w.draw=i,k(`fps: ${h.detect.toFixed(1).padStart(5," ")} detect | ${h.draw.toFixed(1).padStart(5," ")} draw`),o.faceCount=a.result.face.length===1,o.faceCount){let s=Object.values(a.result.gesture).map(f=>f.gesture);(s.includes("blink left eye")||s.includes("blink right eye"))&&(u.start=a.now()),u.start>0&&!s.includes("blink left eye")&&!s.includes("blink right eye")&&(u.end=a.now()),o.blinkDetected=o.blinkDetected||Math.abs(u.end-u.start)>c.blinkMin&&Math.abs(u.end-u.start)c.minConfidence&&(a.result.face[0].faceScore||0)>c.minConfidence,o.antispoofCheck=(a.result.face[0].real||0)>c.minConfidence,o.livenessCheck=(a.result.face[0].live||0)>c.minConfidence,o.faceSize=a.result.face[0].box[2]>=c.minSize&&a.result.face[0].box[3]>=c.minSize}let n=32;for(let[s,f]of Object.entries(o)){let d=document.getElementById(`ok-${s}`);d||(d=document.createElement("div"),d.innerText=s,d.className="ok",d.style.top=`${n}px`,e.ok.appendChild(d)),typeof f=="boolean"?d.style.backgroundColor=f?"lightgreen":"lightcoral":d.innerText=`${s}:${f}`,n+=28}return M()||o.elapsedMs>c.maxTime?(e.video.pause(),a.result.face[0]):(o.elapsedMs=Math.trunc(a.now()-E),new Promise(s=>{setTimeout(async()=>{await L()&&s(a.result.face[0])},30)}))}async function z(){var t,i,n,s;if(e.name.value.length>0){let f=(t=e.canvas.getContext("2d"))==null?void 0:t.getImageData(0,0,e.canvas.width,e.canvas.height),d={id:0,name:e.name.value,descriptor:(i=r.face)==null?void 0:i.embedding,image:f};await C(d),m("saved face record:",d.name,"descriptor length:",(s=(n=r.face)==null?void 0:n.embedding)==null?void 0:s.length),m("known face records:",await y())}else m("invalid name")}async function P(){r.record&&r.record.id>0&&await D(r.record)}async function j(){var s,f;if((s=e.canvas.getContext("2d"))==null||s.clearRect(0,0,c.minSize,c.minSize),!r.face||!r.face.tensor||!r.face.embedding)return!1;if(console.log("face record:",r.face),a.tf.browser.toPixels(r.face.tensor,e.canvas),await y()===0)return m("face database is empty"),document.body.style.background="black",e.delete.style.display="none",!1;let t=await x(),i=t.map(d=>d.descriptor).filter(d=>d.length>0),n=await a.match(r.face.embedding,i,I);return r.record=t[n.index]||null,r.record&&(m(`best match: ${r.record.name} | id: ${r.record.id} | similarity: ${Math.round(1e3*n.similarity)/10}%`),e.name.value=r.record.name,e.source.style.display="",(f=e.source.getContext("2d"))==null||f.putImageData(r.record.image,0,0)),document.body.style.background=n.similarity>c.threshold?"darkgreen":"maroon",n.similarity>c.threshold}async function B(){var t,i,n,s;return o.faceCount=!1,o.faceConfidence=!1,o.facingCenter=!1,o.blinkDetected=!1,o.faceSize=!1,o.antispoofCheck=!1,o.livenessCheck=!1,o.elapsedMs=0,e.match.style.display="none",e.retry.style.display="none",e.source.style.display="none",document.body.style.background="black",await S(),await T(),E=a.now(),r.face=await L(),e.canvas.width=((i=(t=r.face)==null?void 0:t.tensor)==null?void 0:i.shape[1])||c.minSize,e.canvas.height=((s=(n=r.face)==null?void 0:n.tensor)==null?void 0:s.shape[0])||c.minSize,e.source.width=e.canvas.width,e.source.height=e.canvas.height,e.canvas.style.width="",e.match.style.display="flex",e.save.style.display="flex",e.delete.style.display="flex",e.retry.style.display="block",M()?j():(m("did not find valid face"),!1)}async function q(){var t,i,n;m("human version:",a.version,"| tfjs version:",a.tf.version["tfjs-core"]),m("face embedding model:",(t=b.face.description)!=null&&t.enabled?"faceres":"",(i=b.face.mobilefacenet)!=null&&i.enabled?"mobilefacenet":"",(n=b.face.insightface)!=null&&n.enabled?"insightface":""),m("options:",JSON.stringify(c).replace(/{|}|"|\[|\]/g,"").replace(/,/g," ")),k("loading..."),m("known face records:",await y()),await S(),await a.load(),k("initializing..."),e.retry.addEventListener("click",B),e.save.addEventListener("click",z),e.delete.addEventListener("click",P),await a.warmup(),await B()}window.onload=q; //# sourceMappingURL=index.js.map diff --git a/demo/faceid/index.js.map b/demo/faceid/index.js.map index a4aeddf4..708d5f43 100644 --- a/demo/faceid/index.js.map +++ b/demo/faceid/index.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["index.ts", "indexdb.ts"], - "sourcesContent": ["/**\n * Human demo for browsers\n * @default Human Library\n * @summary \n * @author \n * @copyright \n * @license MIT\n */\n\nimport { Human, TensorLike, FaceResult } from '../../dist/human.esm.js'; // equivalent of @vladmandic/Human\nimport * as indexDb from './indexdb'; // methods to deal with indexdb\n\nconst humanConfig = { // user configuration for human, used to fine-tune behavior\n modelBasePath: '../../models',\n filter: { equalization: true }, // lets run with histogram equilizer\n face: {\n enabled: true,\n detector: { rotation: true, return: true, cropFactor: 1.6, mask: false }, // return tensor is used to get detected face image\n description: { enabled: true }, // default model for face descriptor extraction is faceres\n mobilefacenet: { enabled: false, modelPath: 'https://vladmandic.github.io/human-models/models/mobilefacenet.json' }, // alternative model\n iris: { enabled: true }, // needed to determine gaze direction\n emotion: { enabled: false }, // not needed\n antispoof: { enabled: true }, // enable optional antispoof module\n liveness: { enabled: true }, // enable optional liveness module\n },\n body: { enabled: false },\n hand: { enabled: false },\n object: { enabled: false },\n gesture: { enabled: true }, // parses face and iris gestures\n};\n\n// const matchOptions = { order: 2, multiplier: 1000, min: 0.0, max: 1.0 }; // for embedding model\nconst matchOptions = { order: 2, multiplier: 25, min: 0.2, max: 0.8 }; // for faceres model\n\nconst options = {\n minConfidence: 0.6, // overal face confidence for box, face, gender, real, live\n minSize: 224, // min input to face descriptor model before degradation\n maxTime: 10000, // max time before giving up\n blinkMin: 10, // minimum duration of a valid blink\n blinkMax: 800, // maximum duration of a valid blink\n threshold: 0.5, // minimum similarity\n mask: humanConfig.face.detector.mask,\n rotation: humanConfig.face.detector.rotation,\n cropFactor: humanConfig.face.detector.cropFactor,\n ...matchOptions,\n};\n\nconst ok = { // must meet all rules\n faceCount: false,\n faceConfidence: false,\n facingCenter: false,\n lookingCenter: false,\n blinkDetected: false,\n faceSize: false,\n antispoofCheck: false,\n livenessCheck: false,\n elapsedMs: 0, // total time while waiting for valid face\n};\nconst allOk = () => ok.faceCount && ok.faceSize && ok.blinkDetected && ok.facingCenter && ok.lookingCenter && ok.faceConfidence && ok.antispoofCheck && ok.livenessCheck;\nconst current: { face: FaceResult | null, record: indexDb.FaceRecord | null } = { face: null, record: null }; // current face record and matched database record\n\nconst blink = { // internal timers for blink start/end/duration\n start: 0,\n end: 0,\n time: 0,\n};\n\n// let db: Array<{ name: string, source: string, embedding: number[] }> = []; // holds loaded face descriptor database\nconst human = new Human(humanConfig); // create instance of human with overrides from user configuration\n\nhuman.env['perfadd'] = false; // is performance data showing instant or total values\nhuman.draw.options.font = 'small-caps 18px \"Lato\"'; // set font used to draw labels when using draw methods\nhuman.draw.options.lineHeight = 20;\n\nconst dom = { // grab instances of dom objects so we dont have to look them up later\n video: document.getElementById('video') as HTMLVideoElement,\n canvas: document.getElementById('canvas') as HTMLCanvasElement,\n log: document.getElementById('log') as HTMLPreElement,\n fps: document.getElementById('fps') as HTMLPreElement,\n match: document.getElementById('match') as HTMLDivElement,\n name: document.getElementById('name') as HTMLInputElement,\n save: document.getElementById('save') as HTMLSpanElement,\n delete: document.getElementById('delete') as HTMLSpanElement,\n retry: document.getElementById('retry') as HTMLDivElement,\n source: document.getElementById('source') as HTMLCanvasElement,\n ok: document.getElementById('ok') as HTMLDivElement,\n};\nconst timestamp = { detect: 0, draw: 0 }; // holds information used to calculate performance and possible memory leaks\nconst fps = { detect: 0, draw: 0 }; // holds calculated fps information for both detect and screen refresh\nlet startTime = 0;\n\nconst log = (...msg) => { // helper method to output messages\n dom.log.innerText += msg.join(' ') + '\\n';\n // eslint-disable-next-line no-console\n console.log(...msg);\n};\nconst printFPS = (msg) => dom.fps.innerText = msg; // print status element\n\nasync function webCam() { // initialize webcam\n printFPS('starting webcam...');\n // @ts-ignore resizeMode is not yet defined in tslib\n const cameraOptions: MediaStreamConstraints = { audio: false, video: { facingMode: 'user', resizeMode: 'none', width: { ideal: document.body.clientWidth } } };\n const stream: MediaStream = await navigator.mediaDevices.getUserMedia(cameraOptions);\n const ready = new Promise((resolve) => { dom.video.onloadeddata = () => resolve(true); });\n dom.video.srcObject = stream;\n dom.video.play();\n await ready;\n dom.canvas.width = dom.video.videoWidth;\n dom.canvas.height = dom.video.videoHeight;\n if (human.env.initial) log('video:', dom.video.videoWidth, dom.video.videoHeight, '|', stream.getVideoTracks()[0].label);\n dom.canvas.onclick = () => { // pause when clicked on screen and resume on next click\n if (dom.video.paused) dom.video.play();\n else dom.video.pause();\n };\n}\n\nasync function detectionLoop() { // main detection loop\n if (!dom.video.paused) {\n if (current.face && current.face.tensor) human.tf.dispose(current.face.tensor); // dispose previous tensor\n await human.detect(dom.video); // actual detection; were not capturing output in a local variable as it can also be reached via human.result\n const now = human.now();\n fps.detect = 1000 / (now - timestamp.detect);\n timestamp.detect = now;\n requestAnimationFrame(detectionLoop); // start new frame immediately\n }\n}\n\nasync function validationLoop(): Promise { // main screen refresh loop\n const interpolated = await human.next(human.result); // smoothen result using last-known results\n await human.draw.canvas(dom.video, dom.canvas); // draw canvas to screen\n await human.draw.all(dom.canvas, interpolated); // draw labels, boxes, lines, etc.\n const now = human.now();\n fps.draw = 1000 / (now - timestamp.draw);\n timestamp.draw = now;\n printFPS(`fps: ${fps.detect.toFixed(1).padStart(5, ' ')} detect | ${fps.draw.toFixed(1).padStart(5, ' ')} draw`); // write status\n ok.faceCount = human.result.face.length === 1; // must be exactly detected face\n if (ok.faceCount) { // skip the rest if no face\n const gestures: string[] = Object.values(human.result.gesture).map((gesture) => gesture.gesture); // flatten all gestures\n if (gestures.includes('blink left eye') || gestures.includes('blink right eye')) blink.start = human.now(); // blink starts when eyes get closed\n if (blink.start > 0 && !gestures.includes('blink left eye') && !gestures.includes('blink right eye')) blink.end = human.now(); // if blink started how long until eyes are back open\n ok.blinkDetected = ok.blinkDetected || (Math.abs(blink.end - blink.start) > options.blinkMin && Math.abs(blink.end - blink.start) < options.blinkMax);\n if (ok.blinkDetected && blink.time === 0) blink.time = Math.trunc(blink.end - blink.start);\n ok.facingCenter = gestures.includes('facing center');\n ok.lookingCenter = gestures.includes('looking center'); // must face camera and look at camera\n ok.faceConfidence = (human.result.face[0].boxScore || 0) > options.minConfidence && (human.result.face[0].faceScore || 0) > options.minConfidence && (human.result.face[0].genderScore || 0) > options.minConfidence;\n ok.antispoofCheck = (human.result.face[0].real || 0) > options.minConfidence;\n ok.livenessCheck = (human.result.face[0].live || 0) > options.minConfidence;\n ok.faceSize = human.result.face[0].box[2] >= options.minSize && human.result.face[0].box[3] >= options.minSize;\n }\n let y = 32;\n for (const [key, val] of Object.entries(ok)) {\n let el = document.getElementById(`ok-${key}`);\n if (!el) {\n el = document.createElement('div');\n el.innerText = key;\n el.className = 'ok';\n el.style.top = `${y}px`;\n dom.ok.appendChild(el);\n }\n if (typeof val === 'boolean') el.style.backgroundColor = val ? 'lightgreen' : 'lightcoral';\n else el.innerText = `${key}:${val}`;\n y += 28;\n }\n if (allOk()) { // all criteria met\n dom.video.pause();\n return human.result.face[0];\n }\n if (ok.elapsedMs > options.maxTime) { // give up\n dom.video.pause();\n return human.result.face[0];\n } else { // run again\n ok.elapsedMs = Math.trunc(human.now() - startTime);\n return new Promise((resolve) => {\n setTimeout(async () => {\n const res = await validationLoop(); // run validation loop until conditions are met\n if (res) resolve(human.result.face[0]); // recursive promise resolve\n }, 30); // use to slow down refresh from max refresh rate to target of 30 fps\n });\n }\n}\n\nasync function saveRecords() {\n if (dom.name.value.length > 0) {\n const image = dom.canvas.getContext('2d')?.getImageData(0, 0, dom.canvas.width, dom.canvas.height) as ImageData;\n const rec = { id: 0, name: dom.name.value, descriptor: current.face?.embedding as number[], image };\n await indexDb.save(rec);\n log('saved face record:', rec.name);\n } else {\n log('invalid name');\n }\n}\n\nasync function deleteRecord() {\n if (current.record && current.record.id > 0) {\n await indexDb.remove(current.record);\n }\n}\n\nasync function detectFace() {\n dom.canvas.getContext('2d')?.clearRect(0, 0, options.minSize, options.minSize);\n if (!current.face || !current.face.tensor || !current.face.embedding) return false;\n // eslint-disable-next-line no-console\n console.log('face record:', current.face);\n human.tf.browser.toPixels(current.face.tensor as unknown as TensorLike, dom.canvas);\n if (await indexDb.count() === 0) {\n log('face database is empty');\n document.body.style.background = 'black';\n dom.delete.style.display = 'none';\n return false;\n }\n const db = await indexDb.load();\n const descriptors = db.map((rec) => rec.descriptor);\n const res = await human.match(current.face.embedding, descriptors, matchOptions);\n current.record = db[res.index] || null;\n if (current.record) {\n log(`best match: ${current.record.name} | id: ${current.record.id} | similarity: ${Math.round(1000 * res.similarity) / 10}%`);\n dom.name.value = current.record.name;\n dom.source.style.display = '';\n dom.source.getContext('2d')?.putImageData(current.record.image, 0, 0);\n }\n document.body.style.background = res.similarity > options.threshold ? 'darkgreen' : 'maroon';\n return res.similarity > options.threshold;\n}\n\nasync function main() { // main entry point\n ok.faceCount = false;\n ok.faceConfidence = false;\n ok.facingCenter = false;\n ok.blinkDetected = false;\n ok.faceSize = false;\n ok.antispoofCheck = false;\n ok.livenessCheck = false;\n ok.elapsedMs = 0;\n dom.match.style.display = 'none';\n dom.retry.style.display = 'none';\n dom.source.style.display = 'none';\n document.body.style.background = 'black';\n await webCam();\n await detectionLoop(); // start detection loop\n startTime = human.now();\n current.face = await validationLoop(); // start validation loop\n dom.canvas.width = current.face?.tensor?.shape[1] || options.minSize;\n dom.canvas.height = current.face?.tensor?.shape[0] || options.minSize;\n dom.source.width = dom.canvas.width;\n dom.source.height = dom.canvas.height;\n dom.canvas.style.width = '';\n dom.match.style.display = 'flex';\n dom.save.style.display = 'flex';\n dom.delete.style.display = 'flex';\n dom.retry.style.display = 'block';\n if (!allOk()) { // is all criteria met?\n log('did not find valid face');\n return false;\n } else {\n return detectFace();\n }\n}\n\nasync function init() {\n log('human version:', human.version, '| tfjs version:', human.tf.version['tfjs-core']);\n log('options:', JSON.stringify(options).replace(/{|}|\"|\\[|\\]/g, '').replace(/,/g, ' '));\n printFPS('loading...');\n log('known face records:', await indexDb.count());\n await webCam(); // start webcam\n await human.load(); // preload all models\n printFPS('initializing...');\n dom.retry.addEventListener('click', main);\n dom.save.addEventListener('click', saveRecords);\n dom.delete.addEventListener('click', deleteRecord);\n await human.warmup(); // warmup function to initialize backend for future faster detection\n await main();\n}\n\nwindow.onload = init;\n", "let db: IDBDatabase; // instance of indexdb\n\nconst database = 'human';\nconst table = 'person';\n\nexport type FaceRecord = { id: number, name: string, descriptor: number[], image: ImageData };\n\n// eslint-disable-next-line no-console\nconst log = (...msg) => console.log('indexdb', ...msg);\n\nexport async function open() {\n if (db) return true;\n return new Promise((resolve) => {\n const request: IDBOpenDBRequest = indexedDB.open(database, 1);\n request.onerror = (evt) => log('error:', evt);\n request.onupgradeneeded = (evt: IDBVersionChangeEvent) => { // create if doesnt exist\n log('create:', evt.target);\n db = (evt.target as IDBOpenDBRequest).result;\n db.createObjectStore(table, { keyPath: 'id', autoIncrement: true });\n };\n request.onsuccess = (evt) => { // open\n db = (evt.target as IDBOpenDBRequest).result as IDBDatabase;\n log('open:', db);\n resolve(true);\n };\n });\n}\n\nexport async function load(): Promise {\n const faceDB: Array = [];\n if (!db) await open(); // open or create if not already done\n return new Promise((resolve) => {\n const cursor: IDBRequest = db.transaction([table], 'readwrite').objectStore(table).openCursor(null, 'next');\n cursor.onerror = (evt) => log('load error:', evt);\n cursor.onsuccess = (evt) => {\n if ((evt.target as IDBRequest).result) {\n faceDB.push((evt.target as IDBRequest).result.value);\n (evt.target as IDBRequest).result.continue();\n } else {\n resolve(faceDB);\n }\n };\n });\n}\n\nexport async function count(): Promise {\n if (!db) await open(); // open or create if not already done\n return new Promise((resolve) => {\n const store: IDBRequest = db.transaction([table], 'readwrite').objectStore(table).count();\n store.onerror = (evt) => log('count error:', evt);\n store.onsuccess = () => resolve(store.result);\n });\n}\n\nexport async function save(faceRecord: FaceRecord) {\n if (!db) await open(); // open or create if not already done\n const newRecord = { name: faceRecord.name, descriptor: faceRecord.descriptor, image: faceRecord.image }; // omit id as its autoincrement\n db.transaction([table], 'readwrite').objectStore(table).put(newRecord);\n log('save:', newRecord);\n}\n\nexport async function remove(faceRecord: FaceRecord) {\n if (!db) await open(); // open or create if not already done\n db.transaction([table], 'readwrite').objectStore(table).delete(faceRecord.id); // delete based on id\n log('delete:', faceRecord);\n}\n"], - "mappings": ";;;;;;AASA,OAAS,SAAAA,MAAqC,0BCT9C,IAAIC,EAEEC,EAAW,QACXC,EAAQ,SAKRC,EAAM,IAAIC,IAAQ,QAAQ,IAAI,UAAW,GAAGA,CAAG,EAErD,eAAsBC,GAAO,CAC3B,OAAIL,EAAW,GACR,IAAI,QAASM,GAAY,CAC9B,IAAMC,EAA4B,UAAU,KAAKN,EAAU,CAAC,EAC5DM,EAAQ,QAAWC,GAAQL,EAAI,SAAUK,CAAG,EAC5CD,EAAQ,gBAAmBC,GAA+B,CACxDL,EAAI,UAAWK,EAAI,MAAM,EACzBR,EAAMQ,EAAI,OAA4B,OACtCR,EAAG,kBAAkBE,EAAO,CAAE,QAAS,KAAM,cAAe,EAAK,CAAC,CACpE,EACAK,EAAQ,UAAaC,GAAQ,CAC3BR,EAAMQ,EAAI,OAA4B,OACtCL,EAAI,QAASH,CAAE,EACfM,EAAQ,EAAI,CACd,CACF,CAAC,CACH,CAEA,eAAsBG,GAA8B,CAClD,IAAMC,EAA4B,CAAC,EACnC,OAAKV,GAAI,MAAMK,EAAK,EACb,IAAI,QAASC,GAAY,CAC9B,IAAMK,EAAqBX,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,WAAW,KAAM,MAAM,EAC1GS,EAAO,QAAWH,GAAQL,EAAI,cAAeK,CAAG,EAChDG,EAAO,UAAaH,GAAQ,CACrBA,EAAI,OAAsB,QAC7BE,EAAO,KAAMF,EAAI,OAAsB,OAAO,KAAK,EAClDA,EAAI,OAAsB,OAAO,SAAS,GAE3CF,EAAQI,CAAM,CAElB,CACF,CAAC,CACH,CAEA,eAAsBE,GAAyB,CAC7C,OAAKZ,GAAI,MAAMK,EAAK,EACb,IAAI,QAASC,GAAY,CAC9B,IAAMO,EAAoBb,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,MAAM,EACxFW,EAAM,QAAWL,GAAQL,EAAI,eAAgBK,CAAG,EAChDK,EAAM,UAAY,IAAMP,EAAQO,EAAM,MAAM,CAC9C,CAAC,CACH,CAEA,eAAsBC,EAAKC,EAAwB,CAC5Cf,GAAI,MAAMK,EAAK,EACpB,IAAMW,EAAY,CAAE,KAAMD,EAAW,KAAM,WAAYA,EAAW,WAAY,MAAOA,EAAW,KAAM,EACtGf,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,IAAIc,CAAS,EACrEb,EAAI,QAASa,CAAS,CACxB,CAEA,eAAsBC,EAAOF,EAAwB,CAC9Cf,GAAI,MAAMK,EAAK,EACpBL,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,OAAOa,EAAW,EAAE,EAC5EZ,EAAI,UAAWY,CAAU,CAC3B,CDrDA,IAAMG,EAAc,CAClB,cAAe,eACf,OAAQ,CAAE,aAAc,EAAK,EAC7B,KAAM,CACJ,QAAS,GACT,SAAU,CAAE,SAAU,GAAM,OAAQ,GAAM,WAAY,IAAK,KAAM,EAAM,EACvE,YAAa,CAAE,QAAS,EAAK,EAC7B,cAAe,CAAE,QAAS,GAAO,UAAW,qEAAsE,EAClH,KAAM,CAAE,QAAS,EAAK,EACtB,QAAS,CAAE,QAAS,EAAM,EAC1B,UAAW,CAAE,QAAS,EAAK,EAC3B,SAAU,CAAE,QAAS,EAAK,CAC5B,EACA,KAAM,CAAE,QAAS,EAAM,EACvB,KAAM,CAAE,QAAS,EAAM,EACvB,OAAQ,CAAE,QAAS,EAAM,EACzB,QAAS,CAAE,QAAS,EAAK,CAC3B,EAGMC,EAAe,CAAE,MAAO,EAAG,WAAY,GAAI,IAAK,GAAK,IAAK,EAAI,EAE9DC,EAAU,CACd,cAAe,GACf,QAAS,IACT,QAAS,IACT,SAAU,GACV,SAAU,IACV,UAAW,GACX,KAAMF,EAAY,KAAK,SAAS,KAChC,SAAUA,EAAY,KAAK,SAAS,SACpC,WAAYA,EAAY,KAAK,SAAS,WACtC,GAAGC,CACL,EAEME,EAAK,CACT,UAAW,GACX,eAAgB,GAChB,aAAc,GACd,cAAe,GACf,cAAe,GACf,SAAU,GACV,eAAgB,GAChB,cAAe,GACf,UAAW,CACb,EACMC,EAAQ,IAAMD,EAAG,WAAaA,EAAG,UAAYA,EAAG,eAAiBA,EAAG,cAAgBA,EAAG,eAAiBA,EAAG,gBAAkBA,EAAG,gBAAkBA,EAAG,cACrJE,EAA0E,CAAE,KAAM,KAAM,OAAQ,IAAK,EAErGC,EAAQ,CACZ,MAAO,EACP,IAAK,EACL,KAAM,CACR,EAGMC,EAAQ,IAAIC,EAAMR,CAAW,EAEnCO,EAAM,IAAI,QAAa,GACvBA,EAAM,KAAK,QAAQ,KAAO,yBAC1BA,EAAM,KAAK,QAAQ,WAAa,GAEhC,IAAME,EAAM,CACV,MAAO,SAAS,eAAe,OAAO,EACtC,OAAQ,SAAS,eAAe,QAAQ,EACxC,IAAK,SAAS,eAAe,KAAK,EAClC,IAAK,SAAS,eAAe,KAAK,EAClC,MAAO,SAAS,eAAe,OAAO,EACtC,KAAM,SAAS,eAAe,MAAM,EACpC,KAAM,SAAS,eAAe,MAAM,EACpC,OAAQ,SAAS,eAAe,QAAQ,EACxC,MAAO,SAAS,eAAe,OAAO,EACtC,OAAQ,SAAS,eAAe,QAAQ,EACxC,GAAI,SAAS,eAAe,IAAI,CAClC,EACMC,EAAY,CAAE,OAAQ,EAAG,KAAM,CAAE,EACjCC,EAAM,CAAE,OAAQ,EAAG,KAAM,CAAE,EAC7BC,EAAY,EAEVC,EAAM,IAAIC,IAAQ,CACtBL,EAAI,IAAI,WAAaK,EAAI,KAAK,GAAG,EAAI;AAAA,EAErC,QAAQ,IAAI,GAAGA,CAAG,CACpB,EACMC,EAAYD,GAAQL,EAAI,IAAI,UAAYK,EAE9C,eAAeE,GAAS,CACtBD,EAAS,oBAAoB,EAE7B,IAAME,EAAwC,CAAE,MAAO,GAAO,MAAO,CAAE,WAAY,OAAQ,WAAY,OAAQ,MAAO,CAAE,MAAO,SAAS,KAAK,WAAY,CAAE,CAAE,EACvJC,EAAsB,MAAM,UAAU,aAAa,aAAaD,CAAa,EAC7EE,EAAQ,IAAI,QAASC,GAAY,CAAEX,EAAI,MAAM,aAAe,IAAMW,EAAQ,EAAI,CAAG,CAAC,EACxFX,EAAI,MAAM,UAAYS,EACtBT,EAAI,MAAM,KAAK,EACf,MAAMU,EACNV,EAAI,OAAO,MAAQA,EAAI,MAAM,WAC7BA,EAAI,OAAO,OAASA,EAAI,MAAM,YAC1BF,EAAM,IAAI,SAASM,EAAI,SAAUJ,EAAI,MAAM,WAAYA,EAAI,MAAM,YAAa,IAAKS,EAAO,eAAe,EAAE,GAAG,KAAK,EACvHT,EAAI,OAAO,QAAU,IAAM,CACrBA,EAAI,MAAM,OAAQA,EAAI,MAAM,KAAK,EAChCA,EAAI,MAAM,MAAM,CACvB,CACF,CAEA,eAAeY,GAAgB,CAC7B,GAAI,CAACZ,EAAI,MAAM,OAAQ,CACjBJ,EAAQ,MAAQA,EAAQ,KAAK,QAAQE,EAAM,GAAG,QAAQF,EAAQ,KAAK,MAAM,EAC7E,MAAME,EAAM,OAAOE,EAAI,KAAK,EAC5B,IAAMa,EAAMf,EAAM,IAAI,EACtBI,EAAI,OAAS,KAAQW,EAAMZ,EAAU,QACrCA,EAAU,OAASY,EACnB,sBAAsBD,CAAa,CACrC,CACF,CAEA,eAAeE,GAAsC,CACnD,IAAMC,EAAe,MAAMjB,EAAM,KAAKA,EAAM,MAAM,EAClD,MAAMA,EAAM,KAAK,OAAOE,EAAI,MAAOA,EAAI,MAAM,EAC7C,MAAMF,EAAM,KAAK,IAAIE,EAAI,OAAQe,CAAY,EAC7C,IAAMF,EAAMf,EAAM,IAAI,EAKtB,GAJAI,EAAI,KAAO,KAAQW,EAAMZ,EAAU,MACnCA,EAAU,KAAOY,EACjBP,EAAS,QAAQJ,EAAI,OAAO,QAAQ,CAAC,EAAE,SAAS,EAAG,GAAG,cAAcA,EAAI,KAAK,QAAQ,CAAC,EAAE,SAAS,EAAG,GAAG,QAAQ,EAC/GR,EAAG,UAAYI,EAAM,OAAO,KAAK,SAAW,EACxCJ,EAAG,UAAW,CAChB,IAAMsB,EAAqB,OAAO,OAAOlB,EAAM,OAAO,OAAO,EAAE,IAAKmB,GAAYA,EAAQ,OAAO,GAC3FD,EAAS,SAAS,gBAAgB,GAAKA,EAAS,SAAS,iBAAiB,KAAGnB,EAAM,MAAQC,EAAM,IAAI,GACrGD,EAAM,MAAQ,GAAK,CAACmB,EAAS,SAAS,gBAAgB,GAAK,CAACA,EAAS,SAAS,iBAAiB,IAAGnB,EAAM,IAAMC,EAAM,IAAI,GAC5HJ,EAAG,cAAgBA,EAAG,eAAkB,KAAK,IAAIG,EAAM,IAAMA,EAAM,KAAK,EAAIJ,EAAQ,UAAY,KAAK,IAAII,EAAM,IAAMA,EAAM,KAAK,EAAIJ,EAAQ,SACxIC,EAAG,eAAiBG,EAAM,OAAS,IAAGA,EAAM,KAAO,KAAK,MAAMA,EAAM,IAAMA,EAAM,KAAK,GACzFH,EAAG,aAAesB,EAAS,SAAS,eAAe,EACnDtB,EAAG,cAAgBsB,EAAS,SAAS,gBAAgB,EACrDtB,EAAG,gBAAkBI,EAAM,OAAO,KAAK,GAAG,UAAY,GAAKL,EAAQ,gBAAkBK,EAAM,OAAO,KAAK,GAAG,WAAa,GAAKL,EAAQ,gBAAkBK,EAAM,OAAO,KAAK,GAAG,aAAe,GAAKL,EAAQ,cACvMC,EAAG,gBAAkBI,EAAM,OAAO,KAAK,GAAG,MAAQ,GAAKL,EAAQ,cAC/DC,EAAG,eAAiBI,EAAM,OAAO,KAAK,GAAG,MAAQ,GAAKL,EAAQ,cAC9DC,EAAG,SAAWI,EAAM,OAAO,KAAK,GAAG,IAAI,IAAML,EAAQ,SAAWK,EAAM,OAAO,KAAK,GAAG,IAAI,IAAML,EAAQ,OACzG,CACA,IAAIyB,EAAI,GACR,OAAW,CAACC,EAAKC,CAAG,IAAK,OAAO,QAAQ1B,CAAE,EAAG,CAC3C,IAAI2B,EAAK,SAAS,eAAe,MAAMF,GAAK,EACvCE,IACHA,EAAK,SAAS,cAAc,KAAK,EACjCA,EAAG,UAAYF,EACfE,EAAG,UAAY,KACfA,EAAG,MAAM,IAAM,GAAGH,MAClBlB,EAAI,GAAG,YAAYqB,CAAE,GAEnB,OAAOD,GAAQ,UAAWC,EAAG,MAAM,gBAAkBD,EAAM,aAAe,aACzEC,EAAG,UAAY,GAAGF,KAAOC,IAC9BF,GAAK,EACP,CAKA,OAJIvB,EAAM,GAIND,EAAG,UAAYD,EAAQ,SACzBO,EAAI,MAAM,MAAM,EACTF,EAAM,OAAO,KAAK,KAEzBJ,EAAG,UAAY,KAAK,MAAMI,EAAM,IAAI,EAAIK,CAAS,EAC1C,IAAI,QAASQ,GAAY,CAC9B,WAAW,SAAY,CACT,MAAMG,EAAe,GACxBH,EAAQb,EAAM,OAAO,KAAK,EAAE,CACvC,EAAG,EAAE,CACP,CAAC,EAEL,CAEA,eAAewB,GAAc,CArL7B,IAAAC,EAAAC,EAsLE,GAAIxB,EAAI,KAAK,MAAM,OAAS,EAAG,CAC7B,IAAMyB,GAAQF,EAAAvB,EAAI,OAAO,WAAW,IAAI,IAA1B,YAAAuB,EAA6B,aAAa,EAAG,EAAGvB,EAAI,OAAO,MAAOA,EAAI,OAAO,QACrF0B,EAAM,CAAE,GAAI,EAAG,KAAM1B,EAAI,KAAK,MAAO,YAAYwB,EAAA5B,EAAQ,OAAR,YAAA4B,EAAc,UAAuB,MAAAC,CAAM,EAClG,MAAcE,EAAKD,CAAG,EACtBtB,EAAI,qBAAsBsB,EAAI,IAAI,CACpC,MACEtB,EAAI,cAAc,CAEtB,CAEA,eAAewB,GAAe,CACxBhC,EAAQ,QAAUA,EAAQ,OAAO,GAAK,GACxC,MAAciC,EAAOjC,EAAQ,MAAM,CAEvC,CAEA,eAAekC,GAAa,CAtM5B,IAAAP,EAAAC,EAwME,IADAD,EAAAvB,EAAI,OAAO,WAAW,IAAI,IAA1B,MAAAuB,EAA6B,UAAU,EAAG,EAAG9B,EAAQ,QAASA,EAAQ,SAClE,CAACG,EAAQ,MAAQ,CAACA,EAAQ,KAAK,QAAU,CAACA,EAAQ,KAAK,UAAW,MAAO,GAI7E,GAFA,QAAQ,IAAI,eAAgBA,EAAQ,IAAI,EACxCE,EAAM,GAAG,QAAQ,SAASF,EAAQ,KAAK,OAAiCI,EAAI,MAAM,EAC9E,MAAc+B,EAAM,IAAM,EAC5B,OAAA3B,EAAI,wBAAwB,EAC5B,SAAS,KAAK,MAAM,WAAa,QACjCJ,EAAI,OAAO,MAAM,QAAU,OACpB,GAET,IAAMgC,EAAK,MAAcC,EAAK,EACxBC,EAAcF,EAAG,IAAKN,GAAQA,EAAI,UAAU,EAC5CS,EAAM,MAAMrC,EAAM,MAAMF,EAAQ,KAAK,UAAWsC,EAAa1C,CAAY,EAC/E,OAAAI,EAAQ,OAASoC,EAAGG,EAAI,QAAU,KAC9BvC,EAAQ,SACVQ,EAAI,eAAeR,EAAQ,OAAO,cAAcA,EAAQ,OAAO,oBAAoB,KAAK,MAAM,IAAOuC,EAAI,UAAU,EAAI,KAAK,EAC5HnC,EAAI,KAAK,MAAQJ,EAAQ,OAAO,KAChCI,EAAI,OAAO,MAAM,QAAU,IAC3BwB,EAAAxB,EAAI,OAAO,WAAW,IAAI,IAA1B,MAAAwB,EAA6B,aAAa5B,EAAQ,OAAO,MAAO,EAAG,IAErE,SAAS,KAAK,MAAM,WAAauC,EAAI,WAAa1C,EAAQ,UAAY,YAAc,SAC7E0C,EAAI,WAAa1C,EAAQ,SAClC,CAEA,eAAe2C,GAAO,CAhOtB,IAAAb,EAAAC,EAAAa,EAAAC,EA0PE,OAzBA5C,EAAG,UAAY,GACfA,EAAG,eAAiB,GACpBA,EAAG,aAAe,GAClBA,EAAG,cAAgB,GACnBA,EAAG,SAAW,GACdA,EAAG,eAAiB,GACpBA,EAAG,cAAgB,GACnBA,EAAG,UAAY,EACfM,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,OAAO,MAAM,QAAU,OAC3B,SAAS,KAAK,MAAM,WAAa,QACjC,MAAMO,EAAO,EACb,MAAMK,EAAc,EACpBT,EAAYL,EAAM,IAAI,EACtBF,EAAQ,KAAO,MAAMkB,EAAe,EACpCd,EAAI,OAAO,QAAQwB,GAAAD,EAAA3B,EAAQ,OAAR,YAAA2B,EAAc,SAAd,YAAAC,EAAsB,MAAM,KAAM/B,EAAQ,QAC7DO,EAAI,OAAO,SAASsC,GAAAD,EAAAzC,EAAQ,OAAR,YAAAyC,EAAc,SAAd,YAAAC,EAAsB,MAAM,KAAM7C,EAAQ,QAC9DO,EAAI,OAAO,MAAQA,EAAI,OAAO,MAC9BA,EAAI,OAAO,OAASA,EAAI,OAAO,OAC/BA,EAAI,OAAO,MAAM,MAAQ,GACzBA,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,KAAK,MAAM,QAAU,OACzBA,EAAI,OAAO,MAAM,QAAU,OAC3BA,EAAI,MAAM,MAAM,QAAU,QACrBL,EAAM,EAIFmC,EAAW,GAHlB1B,EAAI,yBAAyB,EACtB,GAIX,CAEA,eAAemC,GAAO,CACpBnC,EAAI,iBAAkBN,EAAM,QAAS,kBAAmBA,EAAM,GAAG,QAAQ,YAAY,EACrFM,EAAI,WAAY,KAAK,UAAUX,CAAO,EAAE,QAAQ,eAAgB,EAAE,EAAE,QAAQ,KAAM,GAAG,CAAC,EACtFa,EAAS,YAAY,EACrBF,EAAI,sBAAuB,MAAc2B,EAAM,CAAC,EAChD,MAAMxB,EAAO,EACb,MAAMT,EAAM,KAAK,EACjBQ,EAAS,iBAAiB,EAC1BN,EAAI,MAAM,iBAAiB,QAASoC,CAAI,EACxCpC,EAAI,KAAK,iBAAiB,QAASsB,CAAW,EAC9CtB,EAAI,OAAO,iBAAiB,QAAS4B,CAAY,EACjD,MAAM9B,EAAM,OAAO,EACnB,MAAMsC,EAAK,CACb,CAEA,OAAO,OAASG", - "names": ["Human", "db", "database", "table", "log", "msg", "open", "resolve", "request", "evt", "load", "faceDB", "cursor", "count", "store", "save", "faceRecord", "newRecord", "remove", "humanConfig", "matchOptions", "options", "ok", "allOk", "current", "blink", "human", "Human", "dom", "timestamp", "fps", "startTime", "log", "msg", "printFPS", "webCam", "cameraOptions", "stream", "ready", "resolve", "detectionLoop", "now", "validationLoop", "interpolated", "gestures", "gesture", "y", "key", "val", "el", "saveRecords", "_a", "_b", "image", "rec", "save", "deleteRecord", "remove", "detectFace", "count", "db", "load", "descriptors", "res", "main", "_c", "_d", "init"] + "sourcesContent": ["/**\n * Human demo for browsers\n * @default Human Library\n * @summary \n * @author \n * @copyright \n * @license MIT\n */\n\nimport { Human, TensorLike, FaceResult } from '../../dist/human.esm.js'; // equivalent of @vladmandic/Human\nimport * as indexDb from './indexdb'; // methods to deal with indexdb\n\nconst humanConfig = { // user configuration for human, used to fine-tune behavior\n modelBasePath: '../../models',\n filter: { equalization: true }, // lets run with histogram equilizer\n face: {\n enabled: true,\n detector: { rotation: true, return: true, cropFactor: 1.6, mask: false }, // return tensor is used to get detected face image\n description: { enabled: true }, // default model for face descriptor extraction is faceres\n // mobilefacenet: { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/mobilefacenet.json' }, // alternative model\n // insightface: { enabled: true, modelPath: 'https://vladmandic.github.io/insightface/models/insightface-mobilenet-swish.json' }, // alternative model\n iris: { enabled: true }, // needed to determine gaze direction\n emotion: { enabled: false }, // not needed\n antispoof: { enabled: true }, // enable optional antispoof module\n liveness: { enabled: true }, // enable optional liveness module\n },\n body: { enabled: false },\n hand: { enabled: false },\n object: { enabled: false },\n gesture: { enabled: true }, // parses face and iris gestures\n};\n\n// const matchOptions = { order: 2, multiplier: 1000, min: 0.0, max: 1.0 }; // for embedding model\nconst matchOptions = { order: 2, multiplier: 25, min: 0.2, max: 0.8 }; // for faceres model\n\nconst options = {\n minConfidence: 0.6, // overal face confidence for box, face, gender, real, live\n minSize: 224, // min input to face descriptor model before degradation\n maxTime: 10000, // max time before giving up\n blinkMin: 10, // minimum duration of a valid blink\n blinkMax: 800, // maximum duration of a valid blink\n threshold: 0.5, // minimum similarity\n mask: humanConfig.face.detector.mask,\n rotation: humanConfig.face.detector.rotation,\n cropFactor: humanConfig.face.detector.cropFactor,\n ...matchOptions,\n};\n\nconst ok = { // must meet all rules\n faceCount: false,\n faceConfidence: false,\n facingCenter: false,\n lookingCenter: false,\n blinkDetected: false,\n faceSize: false,\n antispoofCheck: false,\n livenessCheck: false,\n elapsedMs: 0, // total time while waiting for valid face\n};\nconst allOk = () => ok.faceCount && ok.faceSize && ok.blinkDetected && ok.facingCenter && ok.lookingCenter && ok.faceConfidence && ok.antispoofCheck && ok.livenessCheck;\nconst current: { face: FaceResult | null, record: indexDb.FaceRecord | null } = { face: null, record: null }; // current face record and matched database record\n\nconst blink = { // internal timers for blink start/end/duration\n start: 0,\n end: 0,\n time: 0,\n};\n\n// let db: Array<{ name: string, source: string, embedding: number[] }> = []; // holds loaded face descriptor database\nconst human = new Human(humanConfig); // create instance of human with overrides from user configuration\n\nhuman.env['perfadd'] = false; // is performance data showing instant or total values\nhuman.draw.options.font = 'small-caps 18px \"Lato\"'; // set font used to draw labels when using draw methods\nhuman.draw.options.lineHeight = 20;\n\nconst dom = { // grab instances of dom objects so we dont have to look them up later\n video: document.getElementById('video') as HTMLVideoElement,\n canvas: document.getElementById('canvas') as HTMLCanvasElement,\n log: document.getElementById('log') as HTMLPreElement,\n fps: document.getElementById('fps') as HTMLPreElement,\n match: document.getElementById('match') as HTMLDivElement,\n name: document.getElementById('name') as HTMLInputElement,\n save: document.getElementById('save') as HTMLSpanElement,\n delete: document.getElementById('delete') as HTMLSpanElement,\n retry: document.getElementById('retry') as HTMLDivElement,\n source: document.getElementById('source') as HTMLCanvasElement,\n ok: document.getElementById('ok') as HTMLDivElement,\n};\nconst timestamp = { detect: 0, draw: 0 }; // holds information used to calculate performance and possible memory leaks\nconst fps = { detect: 0, draw: 0 }; // holds calculated fps information for both detect and screen refresh\nlet startTime = 0;\n\nconst log = (...msg) => { // helper method to output messages\n dom.log.innerText += msg.join(' ') + '\\n';\n // eslint-disable-next-line no-console\n console.log(...msg);\n};\nconst printFPS = (msg) => dom.fps.innerText = msg; // print status element\n\nasync function webCam() { // initialize webcam\n printFPS('starting webcam...');\n // @ts-ignore resizeMode is not yet defined in tslib\n const cameraOptions: MediaStreamConstraints = { audio: false, video: { facingMode: 'user', resizeMode: 'none', width: { ideal: document.body.clientWidth } } };\n const stream: MediaStream = await navigator.mediaDevices.getUserMedia(cameraOptions);\n const ready = new Promise((resolve) => { dom.video.onloadeddata = () => resolve(true); });\n dom.video.srcObject = stream;\n dom.video.play();\n await ready;\n dom.canvas.width = dom.video.videoWidth;\n dom.canvas.height = dom.video.videoHeight;\n if (human.env.initial) log('video:', dom.video.videoWidth, dom.video.videoHeight, '|', stream.getVideoTracks()[0].label);\n dom.canvas.onclick = () => { // pause when clicked on screen and resume on next click\n if (dom.video.paused) dom.video.play();\n else dom.video.pause();\n };\n}\n\nasync function detectionLoop() { // main detection loop\n if (!dom.video.paused) {\n if (current.face && current.face.tensor) human.tf.dispose(current.face.tensor); // dispose previous tensor\n await human.detect(dom.video); // actual detection; were not capturing output in a local variable as it can also be reached via human.result\n const now = human.now();\n fps.detect = 1000 / (now - timestamp.detect);\n timestamp.detect = now;\n requestAnimationFrame(detectionLoop); // start new frame immediately\n }\n}\n\nasync function validationLoop(): Promise { // main screen refresh loop\n const interpolated = await human.next(human.result); // smoothen result using last-known results\n await human.draw.canvas(dom.video, dom.canvas); // draw canvas to screen\n await human.draw.all(dom.canvas, interpolated); // draw labels, boxes, lines, etc.\n const now = human.now();\n fps.draw = 1000 / (now - timestamp.draw);\n timestamp.draw = now;\n printFPS(`fps: ${fps.detect.toFixed(1).padStart(5, ' ')} detect | ${fps.draw.toFixed(1).padStart(5, ' ')} draw`); // write status\n ok.faceCount = human.result.face.length === 1; // must be exactly detected face\n if (ok.faceCount) { // skip the rest if no face\n const gestures: string[] = Object.values(human.result.gesture).map((gesture) => gesture.gesture); // flatten all gestures\n if (gestures.includes('blink left eye') || gestures.includes('blink right eye')) blink.start = human.now(); // blink starts when eyes get closed\n if (blink.start > 0 && !gestures.includes('blink left eye') && !gestures.includes('blink right eye')) blink.end = human.now(); // if blink started how long until eyes are back open\n ok.blinkDetected = ok.blinkDetected || (Math.abs(blink.end - blink.start) > options.blinkMin && Math.abs(blink.end - blink.start) < options.blinkMax);\n if (ok.blinkDetected && blink.time === 0) blink.time = Math.trunc(blink.end - blink.start);\n ok.facingCenter = gestures.includes('facing center');\n ok.lookingCenter = gestures.includes('looking center'); // must face camera and look at camera\n ok.faceConfidence = (human.result.face[0].boxScore || 0) > options.minConfidence && (human.result.face[0].faceScore || 0) > options.minConfidence;\n ok.antispoofCheck = (human.result.face[0].real || 0) > options.minConfidence;\n ok.livenessCheck = (human.result.face[0].live || 0) > options.minConfidence;\n ok.faceSize = human.result.face[0].box[2] >= options.minSize && human.result.face[0].box[3] >= options.minSize;\n }\n let y = 32;\n for (const [key, val] of Object.entries(ok)) {\n let el = document.getElementById(`ok-${key}`);\n if (!el) {\n el = document.createElement('div');\n el.innerText = key;\n el.className = 'ok';\n el.style.top = `${y}px`;\n dom.ok.appendChild(el);\n }\n if (typeof val === 'boolean') el.style.backgroundColor = val ? 'lightgreen' : 'lightcoral';\n else el.innerText = `${key}:${val}`;\n y += 28;\n }\n if (allOk()) { // all criteria met\n dom.video.pause();\n return human.result.face[0];\n }\n if (ok.elapsedMs > options.maxTime) { // give up\n dom.video.pause();\n return human.result.face[0];\n } else { // run again\n ok.elapsedMs = Math.trunc(human.now() - startTime);\n return new Promise((resolve) => {\n setTimeout(async () => {\n const res = await validationLoop(); // run validation loop until conditions are met\n if (res) resolve(human.result.face[0]); // recursive promise resolve\n }, 30); // use to slow down refresh from max refresh rate to target of 30 fps\n });\n }\n}\n\nasync function saveRecords() {\n if (dom.name.value.length > 0) {\n const image = dom.canvas.getContext('2d')?.getImageData(0, 0, dom.canvas.width, dom.canvas.height) as ImageData;\n const rec = { id: 0, name: dom.name.value, descriptor: current.face?.embedding as number[], image };\n await indexDb.save(rec);\n log('saved face record:', rec.name, 'descriptor length:', current.face?.embedding?.length);\n log('known face records:', await indexDb.count());\n } else {\n log('invalid name');\n }\n}\n\nasync function deleteRecord() {\n if (current.record && current.record.id > 0) {\n await indexDb.remove(current.record);\n }\n}\n\nasync function detectFace() {\n dom.canvas.getContext('2d')?.clearRect(0, 0, options.minSize, options.minSize);\n if (!current.face || !current.face.tensor || !current.face.embedding) return false;\n // eslint-disable-next-line no-console\n console.log('face record:', current.face);\n human.tf.browser.toPixels(current.face.tensor as unknown as TensorLike, dom.canvas);\n if (await indexDb.count() === 0) {\n log('face database is empty');\n document.body.style.background = 'black';\n dom.delete.style.display = 'none';\n return false;\n }\n const db = await indexDb.load();\n const descriptors = db.map((rec) => rec.descriptor).filter((desc) => desc.length > 0);\n const res = await human.match(current.face.embedding, descriptors, matchOptions);\n current.record = db[res.index] || null;\n if (current.record) {\n log(`best match: ${current.record.name} | id: ${current.record.id} | similarity: ${Math.round(1000 * res.similarity) / 10}%`);\n dom.name.value = current.record.name;\n dom.source.style.display = '';\n dom.source.getContext('2d')?.putImageData(current.record.image, 0, 0);\n }\n document.body.style.background = res.similarity > options.threshold ? 'darkgreen' : 'maroon';\n return res.similarity > options.threshold;\n}\n\nasync function main() { // main entry point\n ok.faceCount = false;\n ok.faceConfidence = false;\n ok.facingCenter = false;\n ok.blinkDetected = false;\n ok.faceSize = false;\n ok.antispoofCheck = false;\n ok.livenessCheck = false;\n ok.elapsedMs = 0;\n dom.match.style.display = 'none';\n dom.retry.style.display = 'none';\n dom.source.style.display = 'none';\n document.body.style.background = 'black';\n await webCam();\n await detectionLoop(); // start detection loop\n startTime = human.now();\n current.face = await validationLoop(); // start validation loop\n dom.canvas.width = current.face?.tensor?.shape[1] || options.minSize;\n dom.canvas.height = current.face?.tensor?.shape[0] || options.minSize;\n dom.source.width = dom.canvas.width;\n dom.source.height = dom.canvas.height;\n dom.canvas.style.width = '';\n dom.match.style.display = 'flex';\n dom.save.style.display = 'flex';\n dom.delete.style.display = 'flex';\n dom.retry.style.display = 'block';\n if (!allOk()) { // is all criteria met?\n log('did not find valid face');\n return false;\n } else {\n return detectFace();\n }\n}\n\nasync function init() {\n log('human version:', human.version, '| tfjs version:', human.tf.version['tfjs-core']);\n log('face embedding model:', humanConfig.face['description']?.enabled ? 'faceres' : '', humanConfig.face['mobilefacenet']?.enabled ? 'mobilefacenet' : '', humanConfig.face['insightface']?.enabled ? 'insightface' : '');\n log('options:', JSON.stringify(options).replace(/{|}|\"|\\[|\\]/g, '').replace(/,/g, ' '));\n printFPS('loading...');\n log('known face records:', await indexDb.count());\n await webCam(); // start webcam\n await human.load(); // preload all models\n printFPS('initializing...');\n dom.retry.addEventListener('click', main);\n dom.save.addEventListener('click', saveRecords);\n dom.delete.addEventListener('click', deleteRecord);\n await human.warmup(); // warmup function to initialize backend for future faster detection\n await main();\n}\n\nwindow.onload = init;\n", "let db: IDBDatabase; // instance of indexdb\n\nconst database = 'human';\nconst table = 'person';\n\nexport type FaceRecord = { id: number, name: string, descriptor: number[], image: ImageData };\n\n// eslint-disable-next-line no-console\nconst log = (...msg) => console.log('indexdb', ...msg);\n\nexport async function open() {\n if (db) return true;\n return new Promise((resolve) => {\n const request: IDBOpenDBRequest = indexedDB.open(database, 1);\n request.onerror = (evt) => log('error:', evt);\n request.onupgradeneeded = (evt: IDBVersionChangeEvent) => { // create if doesnt exist\n log('create:', evt.target);\n db = (evt.target as IDBOpenDBRequest).result;\n db.createObjectStore(table, { keyPath: 'id', autoIncrement: true });\n };\n request.onsuccess = (evt) => { // open\n db = (evt.target as IDBOpenDBRequest).result as IDBDatabase;\n log('open:', db);\n resolve(true);\n };\n });\n}\n\nexport async function load(): Promise {\n const faceDB: Array = [];\n if (!db) await open(); // open or create if not already done\n return new Promise((resolve) => {\n const cursor: IDBRequest = db.transaction([table], 'readwrite').objectStore(table).openCursor(null, 'next');\n cursor.onerror = (evt) => log('load error:', evt);\n cursor.onsuccess = (evt) => {\n if ((evt.target as IDBRequest).result) {\n faceDB.push((evt.target as IDBRequest).result.value);\n (evt.target as IDBRequest).result.continue();\n } else {\n resolve(faceDB);\n }\n };\n });\n}\n\nexport async function count(): Promise {\n if (!db) await open(); // open or create if not already done\n return new Promise((resolve) => {\n const store: IDBRequest = db.transaction([table], 'readwrite').objectStore(table).count();\n store.onerror = (evt) => log('count error:', evt);\n store.onsuccess = () => resolve(store.result);\n });\n}\n\nexport async function save(faceRecord: FaceRecord) {\n if (!db) await open(); // open or create if not already done\n const newRecord = { name: faceRecord.name, descriptor: faceRecord.descriptor, image: faceRecord.image }; // omit id as its autoincrement\n db.transaction([table], 'readwrite').objectStore(table).put(newRecord);\n log('save:', newRecord);\n}\n\nexport async function remove(faceRecord: FaceRecord) {\n if (!db) await open(); // open or create if not already done\n db.transaction([table], 'readwrite').objectStore(table).delete(faceRecord.id); // delete based on id\n log('delete:', faceRecord);\n}\n"], + "mappings": ";;;;;;AASA,OAAS,SAAAA,MAAqC,0BCT9C,IAAIC,EAEEC,EAAW,QACXC,EAAQ,SAKRC,EAAM,IAAIC,IAAQ,QAAQ,IAAI,UAAW,GAAGA,CAAG,EAErD,eAAsBC,GAAO,CAC3B,OAAIL,EAAW,GACR,IAAI,QAASM,GAAY,CAC9B,IAAMC,EAA4B,UAAU,KAAKN,EAAU,CAAC,EAC5DM,EAAQ,QAAWC,GAAQL,EAAI,SAAUK,CAAG,EAC5CD,EAAQ,gBAAmBC,GAA+B,CACxDL,EAAI,UAAWK,EAAI,MAAM,EACzBR,EAAMQ,EAAI,OAA4B,OACtCR,EAAG,kBAAkBE,EAAO,CAAE,QAAS,KAAM,cAAe,EAAK,CAAC,CACpE,EACAK,EAAQ,UAAaC,GAAQ,CAC3BR,EAAMQ,EAAI,OAA4B,OACtCL,EAAI,QAASH,CAAE,EACfM,EAAQ,EAAI,CACd,CACF,CAAC,CACH,CAEA,eAAsBG,GAA8B,CAClD,IAAMC,EAA4B,CAAC,EACnC,OAAKV,GAAI,MAAMK,EAAK,EACb,IAAI,QAASC,GAAY,CAC9B,IAAMK,EAAqBX,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,WAAW,KAAM,MAAM,EAC1GS,EAAO,QAAWH,GAAQL,EAAI,cAAeK,CAAG,EAChDG,EAAO,UAAaH,GAAQ,CACrBA,EAAI,OAAsB,QAC7BE,EAAO,KAAMF,EAAI,OAAsB,OAAO,KAAK,EAClDA,EAAI,OAAsB,OAAO,SAAS,GAE3CF,EAAQI,CAAM,CAElB,CACF,CAAC,CACH,CAEA,eAAsBE,GAAyB,CAC7C,OAAKZ,GAAI,MAAMK,EAAK,EACb,IAAI,QAASC,GAAY,CAC9B,IAAMO,EAAoBb,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,MAAM,EACxFW,EAAM,QAAWL,GAAQL,EAAI,eAAgBK,CAAG,EAChDK,EAAM,UAAY,IAAMP,EAAQO,EAAM,MAAM,CAC9C,CAAC,CACH,CAEA,eAAsBC,EAAKC,EAAwB,CAC5Cf,GAAI,MAAMK,EAAK,EACpB,IAAMW,EAAY,CAAE,KAAMD,EAAW,KAAM,WAAYA,EAAW,WAAY,MAAOA,EAAW,KAAM,EACtGf,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,IAAIc,CAAS,EACrEb,EAAI,QAASa,CAAS,CACxB,CAEA,eAAsBC,EAAOF,EAAwB,CAC9Cf,GAAI,MAAMK,EAAK,EACpBL,EAAG,YAAY,CAACE,CAAK,EAAG,WAAW,EAAE,YAAYA,CAAK,EAAE,OAAOa,EAAW,EAAE,EAC5EZ,EAAI,UAAWY,CAAU,CAC3B,CDrDA,IAAMG,EAAc,CAClB,cAAe,eACf,OAAQ,CAAE,aAAc,EAAK,EAC7B,KAAM,CACJ,QAAS,GACT,SAAU,CAAE,SAAU,GAAM,OAAQ,GAAM,WAAY,IAAK,KAAM,EAAM,EACvE,YAAa,CAAE,QAAS,EAAK,EAG7B,KAAM,CAAE,QAAS,EAAK,EACtB,QAAS,CAAE,QAAS,EAAM,EAC1B,UAAW,CAAE,QAAS,EAAK,EAC3B,SAAU,CAAE,QAAS,EAAK,CAC5B,EACA,KAAM,CAAE,QAAS,EAAM,EACvB,KAAM,CAAE,QAAS,EAAM,EACvB,OAAQ,CAAE,QAAS,EAAM,EACzB,QAAS,CAAE,QAAS,EAAK,CAC3B,EAGMC,EAAe,CAAE,MAAO,EAAG,WAAY,GAAI,IAAK,GAAK,IAAK,EAAI,EAE9DC,EAAU,CACd,cAAe,GACf,QAAS,IACT,QAAS,IACT,SAAU,GACV,SAAU,IACV,UAAW,GACX,KAAMF,EAAY,KAAK,SAAS,KAChC,SAAUA,EAAY,KAAK,SAAS,SACpC,WAAYA,EAAY,KAAK,SAAS,WACtC,GAAGC,CACL,EAEME,EAAK,CACT,UAAW,GACX,eAAgB,GAChB,aAAc,GACd,cAAe,GACf,cAAe,GACf,SAAU,GACV,eAAgB,GAChB,cAAe,GACf,UAAW,CACb,EACMC,EAAQ,IAAMD,EAAG,WAAaA,EAAG,UAAYA,EAAG,eAAiBA,EAAG,cAAgBA,EAAG,eAAiBA,EAAG,gBAAkBA,EAAG,gBAAkBA,EAAG,cACrJE,EAA0E,CAAE,KAAM,KAAM,OAAQ,IAAK,EAErGC,EAAQ,CACZ,MAAO,EACP,IAAK,EACL,KAAM,CACR,EAGMC,EAAQ,IAAIC,EAAMR,CAAW,EAEnCO,EAAM,IAAI,QAAa,GACvBA,EAAM,KAAK,QAAQ,KAAO,yBAC1BA,EAAM,KAAK,QAAQ,WAAa,GAEhC,IAAME,EAAM,CACV,MAAO,SAAS,eAAe,OAAO,EACtC,OAAQ,SAAS,eAAe,QAAQ,EACxC,IAAK,SAAS,eAAe,KAAK,EAClC,IAAK,SAAS,eAAe,KAAK,EAClC,MAAO,SAAS,eAAe,OAAO,EACtC,KAAM,SAAS,eAAe,MAAM,EACpC,KAAM,SAAS,eAAe,MAAM,EACpC,OAAQ,SAAS,eAAe,QAAQ,EACxC,MAAO,SAAS,eAAe,OAAO,EACtC,OAAQ,SAAS,eAAe,QAAQ,EACxC,GAAI,SAAS,eAAe,IAAI,CAClC,EACMC,EAAY,CAAE,OAAQ,EAAG,KAAM,CAAE,EACjCC,EAAM,CAAE,OAAQ,EAAG,KAAM,CAAE,EAC7BC,EAAY,EAEVC,EAAM,IAAIC,IAAQ,CACtBL,EAAI,IAAI,WAAaK,EAAI,KAAK,GAAG,EAAI;AAAA,EAErC,QAAQ,IAAI,GAAGA,CAAG,CACpB,EACMC,EAAYD,GAAQL,EAAI,IAAI,UAAYK,EAE9C,eAAeE,GAAS,CACtBD,EAAS,oBAAoB,EAE7B,IAAME,EAAwC,CAAE,MAAO,GAAO,MAAO,CAAE,WAAY,OAAQ,WAAY,OAAQ,MAAO,CAAE,MAAO,SAAS,KAAK,WAAY,CAAE,CAAE,EACvJC,EAAsB,MAAM,UAAU,aAAa,aAAaD,CAAa,EAC7EE,EAAQ,IAAI,QAASC,GAAY,CAAEX,EAAI,MAAM,aAAe,IAAMW,EAAQ,EAAI,CAAG,CAAC,EACxFX,EAAI,MAAM,UAAYS,EACtBT,EAAI,MAAM,KAAK,EACf,MAAMU,EACNV,EAAI,OAAO,MAAQA,EAAI,MAAM,WAC7BA,EAAI,OAAO,OAASA,EAAI,MAAM,YAC1BF,EAAM,IAAI,SAASM,EAAI,SAAUJ,EAAI,MAAM,WAAYA,EAAI,MAAM,YAAa,IAAKS,EAAO,eAAe,EAAE,GAAG,KAAK,EACvHT,EAAI,OAAO,QAAU,IAAM,CACrBA,EAAI,MAAM,OAAQA,EAAI,MAAM,KAAK,EAChCA,EAAI,MAAM,MAAM,CACvB,CACF,CAEA,eAAeY,GAAgB,CAC7B,GAAI,CAACZ,EAAI,MAAM,OAAQ,CACjBJ,EAAQ,MAAQA,EAAQ,KAAK,QAAQE,EAAM,GAAG,QAAQF,EAAQ,KAAK,MAAM,EAC7E,MAAME,EAAM,OAAOE,EAAI,KAAK,EAC5B,IAAMa,EAAMf,EAAM,IAAI,EACtBI,EAAI,OAAS,KAAQW,EAAMZ,EAAU,QACrCA,EAAU,OAASY,EACnB,sBAAsBD,CAAa,CACrC,CACF,CAEA,eAAeE,GAAsC,CACnD,IAAMC,EAAe,MAAMjB,EAAM,KAAKA,EAAM,MAAM,EAClD,MAAMA,EAAM,KAAK,OAAOE,EAAI,MAAOA,EAAI,MAAM,EAC7C,MAAMF,EAAM,KAAK,IAAIE,EAAI,OAAQe,CAAY,EAC7C,IAAMF,EAAMf,EAAM,IAAI,EAKtB,GAJAI,EAAI,KAAO,KAAQW,EAAMZ,EAAU,MACnCA,EAAU,KAAOY,EACjBP,EAAS,QAAQJ,EAAI,OAAO,QAAQ,CAAC,EAAE,SAAS,EAAG,GAAG,cAAcA,EAAI,KAAK,QAAQ,CAAC,EAAE,SAAS,EAAG,GAAG,QAAQ,EAC/GR,EAAG,UAAYI,EAAM,OAAO,KAAK,SAAW,EACxCJ,EAAG,UAAW,CAChB,IAAMsB,EAAqB,OAAO,OAAOlB,EAAM,OAAO,OAAO,EAAE,IAAKmB,GAAYA,EAAQ,OAAO,GAC3FD,EAAS,SAAS,gBAAgB,GAAKA,EAAS,SAAS,iBAAiB,KAAGnB,EAAM,MAAQC,EAAM,IAAI,GACrGD,EAAM,MAAQ,GAAK,CAACmB,EAAS,SAAS,gBAAgB,GAAK,CAACA,EAAS,SAAS,iBAAiB,IAAGnB,EAAM,IAAMC,EAAM,IAAI,GAC5HJ,EAAG,cAAgBA,EAAG,eAAkB,KAAK,IAAIG,EAAM,IAAMA,EAAM,KAAK,EAAIJ,EAAQ,UAAY,KAAK,IAAII,EAAM,IAAMA,EAAM,KAAK,EAAIJ,EAAQ,SACxIC,EAAG,eAAiBG,EAAM,OAAS,IAAGA,EAAM,KAAO,KAAK,MAAMA,EAAM,IAAMA,EAAM,KAAK,GACzFH,EAAG,aAAesB,EAAS,SAAS,eAAe,EACnDtB,EAAG,cAAgBsB,EAAS,SAAS,gBAAgB,EACrDtB,EAAG,gBAAkBI,EAAM,OAAO,KAAK,GAAG,UAAY,GAAKL,EAAQ,gBAAkBK,EAAM,OAAO,KAAK,GAAG,WAAa,GAAKL,EAAQ,cACpIC,EAAG,gBAAkBI,EAAM,OAAO,KAAK,GAAG,MAAQ,GAAKL,EAAQ,cAC/DC,EAAG,eAAiBI,EAAM,OAAO,KAAK,GAAG,MAAQ,GAAKL,EAAQ,cAC9DC,EAAG,SAAWI,EAAM,OAAO,KAAK,GAAG,IAAI,IAAML,EAAQ,SAAWK,EAAM,OAAO,KAAK,GAAG,IAAI,IAAML,EAAQ,OACzG,CACA,IAAIyB,EAAI,GACR,OAAW,CAACC,EAAKC,CAAG,IAAK,OAAO,QAAQ1B,CAAE,EAAG,CAC3C,IAAI2B,EAAK,SAAS,eAAe,MAAMF,GAAK,EACvCE,IACHA,EAAK,SAAS,cAAc,KAAK,EACjCA,EAAG,UAAYF,EACfE,EAAG,UAAY,KACfA,EAAG,MAAM,IAAM,GAAGH,MAClBlB,EAAI,GAAG,YAAYqB,CAAE,GAEnB,OAAOD,GAAQ,UAAWC,EAAG,MAAM,gBAAkBD,EAAM,aAAe,aACzEC,EAAG,UAAY,GAAGF,KAAOC,IAC9BF,GAAK,EACP,CAKA,OAJIvB,EAAM,GAIND,EAAG,UAAYD,EAAQ,SACzBO,EAAI,MAAM,MAAM,EACTF,EAAM,OAAO,KAAK,KAEzBJ,EAAG,UAAY,KAAK,MAAMI,EAAM,IAAI,EAAIK,CAAS,EAC1C,IAAI,QAASQ,GAAY,CAC9B,WAAW,SAAY,CACT,MAAMG,EAAe,GACxBH,EAAQb,EAAM,OAAO,KAAK,EAAE,CACvC,EAAG,EAAE,CACP,CAAC,EAEL,CAEA,eAAewB,GAAc,CAtL7B,IAAAC,EAAAC,EAAAC,EAAAC,EAuLE,GAAI1B,EAAI,KAAK,MAAM,OAAS,EAAG,CAC7B,IAAM2B,GAAQJ,EAAAvB,EAAI,OAAO,WAAW,IAAI,IAA1B,YAAAuB,EAA6B,aAAa,EAAG,EAAGvB,EAAI,OAAO,MAAOA,EAAI,OAAO,QACrF4B,EAAM,CAAE,GAAI,EAAG,KAAM5B,EAAI,KAAK,MAAO,YAAYwB,EAAA5B,EAAQ,OAAR,YAAA4B,EAAc,UAAuB,MAAAG,CAAM,EAClG,MAAcE,EAAKD,CAAG,EACtBxB,EAAI,qBAAsBwB,EAAI,KAAM,sBAAsBF,GAAAD,EAAA7B,EAAQ,OAAR,YAAA6B,EAAc,YAAd,YAAAC,EAAyB,MAAM,EACzFtB,EAAI,sBAAuB,MAAc0B,EAAM,CAAC,CAClD,MACE1B,EAAI,cAAc,CAEtB,CAEA,eAAe2B,GAAe,CACxBnC,EAAQ,QAAUA,EAAQ,OAAO,GAAK,GACxC,MAAcoC,EAAOpC,EAAQ,MAAM,CAEvC,CAEA,eAAeqC,GAAa,CAxM5B,IAAAV,EAAAC,EA0ME,IADAD,EAAAvB,EAAI,OAAO,WAAW,IAAI,IAA1B,MAAAuB,EAA6B,UAAU,EAAG,EAAG9B,EAAQ,QAASA,EAAQ,SAClE,CAACG,EAAQ,MAAQ,CAACA,EAAQ,KAAK,QAAU,CAACA,EAAQ,KAAK,UAAW,MAAO,GAI7E,GAFA,QAAQ,IAAI,eAAgBA,EAAQ,IAAI,EACxCE,EAAM,GAAG,QAAQ,SAASF,EAAQ,KAAK,OAAiCI,EAAI,MAAM,EAC9E,MAAc8B,EAAM,IAAM,EAC5B,OAAA1B,EAAI,wBAAwB,EAC5B,SAAS,KAAK,MAAM,WAAa,QACjCJ,EAAI,OAAO,MAAM,QAAU,OACpB,GAET,IAAMkC,EAAK,MAAcC,EAAK,EACxBC,EAAcF,EAAG,IAAKN,GAAQA,EAAI,UAAU,EAAE,OAAQS,GAASA,EAAK,OAAS,CAAC,EAC9EC,EAAM,MAAMxC,EAAM,MAAMF,EAAQ,KAAK,UAAWwC,EAAa5C,CAAY,EAC/E,OAAAI,EAAQ,OAASsC,EAAGI,EAAI,QAAU,KAC9B1C,EAAQ,SACVQ,EAAI,eAAeR,EAAQ,OAAO,cAAcA,EAAQ,OAAO,oBAAoB,KAAK,MAAM,IAAO0C,EAAI,UAAU,EAAI,KAAK,EAC5HtC,EAAI,KAAK,MAAQJ,EAAQ,OAAO,KAChCI,EAAI,OAAO,MAAM,QAAU,IAC3BwB,EAAAxB,EAAI,OAAO,WAAW,IAAI,IAA1B,MAAAwB,EAA6B,aAAa5B,EAAQ,OAAO,MAAO,EAAG,IAErE,SAAS,KAAK,MAAM,WAAa0C,EAAI,WAAa7C,EAAQ,UAAY,YAAc,SAC7E6C,EAAI,WAAa7C,EAAQ,SAClC,CAEA,eAAe8C,GAAO,CAlOtB,IAAAhB,EAAAC,EAAAC,EAAAC,EA4PE,OAzBAhC,EAAG,UAAY,GACfA,EAAG,eAAiB,GACpBA,EAAG,aAAe,GAClBA,EAAG,cAAgB,GACnBA,EAAG,SAAW,GACdA,EAAG,eAAiB,GACpBA,EAAG,cAAgB,GACnBA,EAAG,UAAY,EACfM,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,OAAO,MAAM,QAAU,OAC3B,SAAS,KAAK,MAAM,WAAa,QACjC,MAAMO,EAAO,EACb,MAAMK,EAAc,EACpBT,EAAYL,EAAM,IAAI,EACtBF,EAAQ,KAAO,MAAMkB,EAAe,EACpCd,EAAI,OAAO,QAAQwB,GAAAD,EAAA3B,EAAQ,OAAR,YAAA2B,EAAc,SAAd,YAAAC,EAAsB,MAAM,KAAM/B,EAAQ,QAC7DO,EAAI,OAAO,SAAS0B,GAAAD,EAAA7B,EAAQ,OAAR,YAAA6B,EAAc,SAAd,YAAAC,EAAsB,MAAM,KAAMjC,EAAQ,QAC9DO,EAAI,OAAO,MAAQA,EAAI,OAAO,MAC9BA,EAAI,OAAO,OAASA,EAAI,OAAO,OAC/BA,EAAI,OAAO,MAAM,MAAQ,GACzBA,EAAI,MAAM,MAAM,QAAU,OAC1BA,EAAI,KAAK,MAAM,QAAU,OACzBA,EAAI,OAAO,MAAM,QAAU,OAC3BA,EAAI,MAAM,MAAM,QAAU,QACrBL,EAAM,EAIFsC,EAAW,GAHlB7B,EAAI,yBAAyB,EACtB,GAIX,CAEA,eAAeoC,GAAO,CApQtB,IAAAjB,EAAAC,EAAAC,EAqQErB,EAAI,iBAAkBN,EAAM,QAAS,kBAAmBA,EAAM,GAAG,QAAQ,YAAY,EACrFM,EAAI,yBAAyBmB,EAAAhC,EAAY,KAAK,cAAjB,MAAAgC,EAAiC,QAAU,UAAY,IAAIC,EAAAjC,EAAY,KAAK,gBAAjB,MAAAiC,EAAmC,QAAU,gBAAkB,IAAIC,EAAAlC,EAAY,KAAK,cAAjB,MAAAkC,EAAiC,QAAU,cAAgB,EAAE,EACxNrB,EAAI,WAAY,KAAK,UAAUX,CAAO,EAAE,QAAQ,eAAgB,EAAE,EAAE,QAAQ,KAAM,GAAG,CAAC,EACtFa,EAAS,YAAY,EACrBF,EAAI,sBAAuB,MAAc0B,EAAM,CAAC,EAChD,MAAMvB,EAAO,EACb,MAAMT,EAAM,KAAK,EACjBQ,EAAS,iBAAiB,EAC1BN,EAAI,MAAM,iBAAiB,QAASuC,CAAI,EACxCvC,EAAI,KAAK,iBAAiB,QAASsB,CAAW,EAC9CtB,EAAI,OAAO,iBAAiB,QAAS+B,CAAY,EACjD,MAAMjC,EAAM,OAAO,EACnB,MAAMyC,EAAK,CACb,CAEA,OAAO,OAASC", + "names": ["Human", "db", "database", "table", "log", "msg", "open", "resolve", "request", "evt", "load", "faceDB", "cursor", "count", "store", "save", "faceRecord", "newRecord", "remove", "humanConfig", "matchOptions", "options", "ok", "allOk", "current", "blink", "human", "Human", "dom", "timestamp", "fps", "startTime", "log", "msg", "printFPS", "webCam", "cameraOptions", "stream", "ready", "resolve", "detectionLoop", "now", "validationLoop", "interpolated", "gestures", "gesture", "y", "key", "val", "el", "saveRecords", "_a", "_b", "_c", "_d", "image", "rec", "save", "count", "deleteRecord", "remove", "detectFace", "db", "load", "descriptors", "desc", "res", "main", "init"] } diff --git a/demo/faceid/index.ts b/demo/faceid/index.ts index 0445d29a..feb31a1b 100644 --- a/demo/faceid/index.ts +++ b/demo/faceid/index.ts @@ -17,7 +17,8 @@ const humanConfig = { // user configuration for human, used to fine-tune behavio enabled: true, detector: { rotation: true, return: true, cropFactor: 1.6, mask: false }, // return tensor is used to get detected face image description: { enabled: true }, // default model for face descriptor extraction is faceres - mobilefacenet: { enabled: false, modelPath: 'https://vladmandic.github.io/human-models/models/mobilefacenet.json' }, // alternative model + // mobilefacenet: { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/mobilefacenet.json' }, // alternative model + // insightface: { enabled: true, modelPath: 'https://vladmandic.github.io/insightface/models/insightface-mobilenet-swish.json' }, // alternative model iris: { enabled: true }, // needed to determine gaze direction emotion: { enabled: false }, // not needed antispoof: { enabled: true }, // enable optional antispoof module @@ -142,7 +143,7 @@ async function validationLoop(): Promise { // main screen refresh lo if (ok.blinkDetected && blink.time === 0) blink.time = Math.trunc(blink.end - blink.start); ok.facingCenter = gestures.includes('facing center'); ok.lookingCenter = gestures.includes('looking center'); // must face camera and look at camera - ok.faceConfidence = (human.result.face[0].boxScore || 0) > options.minConfidence && (human.result.face[0].faceScore || 0) > options.minConfidence && (human.result.face[0].genderScore || 0) > options.minConfidence; + ok.faceConfidence = (human.result.face[0].boxScore || 0) > options.minConfidence && (human.result.face[0].faceScore || 0) > options.minConfidence; ok.antispoofCheck = (human.result.face[0].real || 0) > options.minConfidence; ok.livenessCheck = (human.result.face[0].live || 0) > options.minConfidence; ok.faceSize = human.result.face[0].box[2] >= options.minSize && human.result.face[0].box[3] >= options.minSize; @@ -184,7 +185,8 @@ async function saveRecords() { const image = dom.canvas.getContext('2d')?.getImageData(0, 0, dom.canvas.width, dom.canvas.height) as ImageData; const rec = { id: 0, name: dom.name.value, descriptor: current.face?.embedding as number[], image }; await indexDb.save(rec); - log('saved face record:', rec.name); + log('saved face record:', rec.name, 'descriptor length:', current.face?.embedding?.length); + log('known face records:', await indexDb.count()); } else { log('invalid name'); } @@ -209,7 +211,7 @@ async function detectFace() { return false; } const db = await indexDb.load(); - const descriptors = db.map((rec) => rec.descriptor); + const descriptors = db.map((rec) => rec.descriptor).filter((desc) => desc.length > 0); const res = await human.match(current.face.embedding, descriptors, matchOptions); current.record = db[res.index] || null; if (current.record) { @@ -258,6 +260,7 @@ async function main() { // main entry point async function init() { log('human version:', human.version, '| tfjs version:', human.tf.version['tfjs-core']); + log('face embedding model:', humanConfig.face['description']?.enabled ? 'faceres' : '', humanConfig.face['mobilefacenet']?.enabled ? 'mobilefacenet' : '', humanConfig.face['insightface']?.enabled ? 'insightface' : ''); log('options:', JSON.stringify(options).replace(/{|}|"|\[|\]/g, '').replace(/,/g, ' ')); printFPS('loading...'); log('known face records:', await indexDb.count()); diff --git a/src/body/efficientpose.ts b/src/body/efficientpose.ts index 0c3c7c8b..478b2fe8 100644 --- a/src/body/efficientpose.ts +++ b/src/body/efficientpose.ts @@ -32,22 +32,23 @@ export async function load(config: Config): Promise { } // performs argmax and max functions on a 2d tensor -async function max2d(inputs, minScore) { +async function max2d(inputs, minScore): Promise<[number, number, number]> { const [width, height] = inputs.shape; const reshaped = tf.reshape(inputs, [height * width]); // combine all data const max = tf.max(reshaped, 0); - const newScore = (await max.data())[0]; // get highest score - tf.dispose([reshaped, max]); + const newScore: number = (await max.data())[0]; // get highest score if (newScore > minScore) { // skip coordinate calculation is score is too low const coordinates = tf.argMax(reshaped, 0); const mod = tf.mod(coordinates, width); const x = (await mod.data())[0]; - const div = tf.div(coordinates, tf.scalar(width, 'int32')); - const y = (await div.data())[0]; - tf.dispose([mod, div]); + const div = tf.div(coordinates, width); + const y: number = (await div.data())[0]; + tf.dispose([reshaped, max, coordinates, mod, div]); return [x, y, newScore]; + } else { + tf.dispose([reshaped, max]); + return [0, 0, newScore]; } - return [0, 0, newScore]; } export async function predict(image: Tensor, config: Config): Promise { @@ -66,7 +67,6 @@ export async function predict(image: Tensor, config: Config): Promise | null; let emotionRes: { score: number, emotion: Emotion }[] | Promise<{ score: number, emotion: Emotion }[]>; let mobilefacenetRes: number[] | Promise | null; + let insightfaceRes: number[] | Promise | null; let antispoofRes: number | Promise | null; let livenessRes: number | Promise | null; let descRes: DescRes | Promise | null; @@ -126,7 +128,7 @@ export const detectFace = async (instance: Human /* instance of human */, input: } instance.analyze('End SSRNet:'); - // run gear, inherits face from blazeface + // run mobilefacenet alternative, inherits face from blazeface instance.analyze('Start MobileFaceNet:'); if (instance.config.async) { mobilefacenetRes = instance.config.face['mobilefacenet']?.enabled ? mobilefacenet.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null; @@ -138,21 +140,33 @@ export const detectFace = async (instance: Human /* instance of human */, input: } instance.analyze('End MobileFaceNet:'); - // run emotion, inherits face from blazeface + // run insightface alternative, inherits face from blazeface + instance.analyze('Start InsightFace:'); + if (instance.config.async) { + insightfaceRes = instance.config.face['insightface']?.enabled ? insightface.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null; + } else { + instance.state = 'run:mobilefacenet'; + timeStamp = now(); + insightfaceRes = instance.config.face['insightface']?.enabled ? await insightface.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null; + instance.performance.mobilefacenet = Math.trunc(now() - timeStamp); + } + instance.analyze('End InsightFace:'); + + // run faceres, inherits face from blazeface instance.analyze('Start Description:'); if (instance.config.async) { - descRes = instance.config.face.description?.enabled ? faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null; + descRes = faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length); } else { instance.state = 'run:description'; timeStamp = now(); - descRes = instance.config.face.description?.enabled ? await faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null; + descRes = await faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length); instance.performance.description = env.perfadd ? (instance.performance.description || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp); } instance.analyze('End Description:'); // if async wait for results if (instance.config.async) { - [ageRes, genderRes, emotionRes, mobilefacenetRes, descRes, gearRes, antispoofRes, livenessRes] = await Promise.all([ageRes, genderRes, emotionRes, mobilefacenetRes, descRes, gearRes, antispoofRes, livenessRes]); + [ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes] = await Promise.all([ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes]); } instance.analyze('Finish Face:'); @@ -173,10 +187,14 @@ export const detectFace = async (instance: Human /* instance of human */, input: race: (gearRes as gear.GearType).race, }; } - if (instance.config.face['mobilefacenet']?.enabled && mobilefacenetRes) { // override descriptor if embedding model is used + if (instance.config.face['mobilefacenet']?.enabled && mobilefacenetRes) { // override descriptor if mobilefacenet model is used (descRes as DescRes).descriptor = mobilefacenetRes as number[]; } + if (instance.config.face['insightface']?.enabled && insightfaceRes) { // override descriptor if insightface model is used + (descRes as DescRes).descriptor = insightfaceRes as number[]; + } + // calculate iris distance // iris: array[ center, left, top, right, bottom] if (!instance.config.face.iris?.enabled) { diff --git a/src/face/insightface.ts b/src/face/insightface.ts new file mode 100644 index 00000000..98fa80fc --- /dev/null +++ b/src/face/insightface.ts @@ -0,0 +1,55 @@ +/** + * InsightFace model implementation + * + * Based on: [**DeepInsight InsightFace**](https://github.com/deepinsight/insightface) + * + * Alternative face embedding detection + */ + +import { log, now } from '../util/util'; +import * as tf from '../../dist/tfjs.esm.js'; +import { loadModel } from '../tfjs/load'; +import type { Tensor, GraphModel } from '../tfjs/types'; +import type { Config } from '../config'; +import { env } from '../util/env'; + +let model: GraphModel | null; +const last: Array = []; +let lastCount = 0; +let lastTime = 0; +let skipped = Number.MAX_SAFE_INTEGER; + +export async function load(config: Config): Promise { + if (env.initial) model = null; + if (!model) model = await loadModel(config.face['insightface'].modelPath); + else if (config.debug) log('cached model:', model['modelUrl']); + return model; +} + +export async function predict(input: Tensor, config: Config, idx, count): Promise { + if (!model) return []; + const skipFrame = skipped < (config.face['insightface']?.skipFrames || 0); + const skipTime = (config.face['insightface']?.skipTime || 0) > (now() - lastTime); + if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx]) { + skipped++; + return last[idx]; + } + return new Promise(async (resolve) => { + let data: Array = []; + if (config.face['insightface']?.enabled && model?.inputs[0].shape) { + const t: Record = {}; + t.crop = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // just resize to fit the embedding model + // do a tight crop of image and resize it to fit the model + // const box = [[0.05, 0.15, 0.85, 0.85]]; // empyrical values for top, left, bottom, right + // t.crop = tf.image.cropAndResize(input, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]); + t.data = model?.execute(t.crop) as Tensor; + const output = await t.data.data(); + data = Array.from(output); // convert typed array to simple array + Object.keys(t).forEach((tensor) => tf.dispose(t[tensor])); + } + last[idx] = data; + lastCount = count; + lastTime = now(); + resolve(data); + }); +} diff --git a/src/face/match.ts b/src/face/match.ts index 161e2bdd..42a22f06 100644 --- a/src/face/match.ts +++ b/src/face/match.ts @@ -54,13 +54,13 @@ export function similarity(descriptor1: Descriptor, descriptor2: Descriptor, opt * - `similarity` calculated normalized `similarity` of given descriptor to the best match */ export function match(descriptor: Descriptor, descriptors: Array, options: MatchOptions = { order: 2, multiplier: 25, threshold: 0, min: 0.2, max: 0.8 }) { - if (!Array.isArray(descriptor) || !Array.isArray(descriptors) || descriptor.length < 64 || descriptors.length === 0 || descriptor.length !== descriptors[0].length) { // validate input + if (!Array.isArray(descriptor) || !Array.isArray(descriptors) || descriptor.length < 64 || descriptors.length === 0) { // validate input return { index: -1, distance: Number.POSITIVE_INFINITY, similarity: 0 }; } let lowestDistance = Number.MAX_SAFE_INTEGER; let index = -1; for (let i = 0; i < descriptors.length; i++) { - const res = distance(descriptor, descriptors[i], options); + const res = descriptors[i].length === descriptor.length ? distance(descriptor, descriptors[i], options) : Number.MAX_SAFE_INTEGER; if (res < lowestDistance) { lowestDistance = res; index = i; diff --git a/src/face/mobilefacenet.ts b/src/face/mobilefacenet.ts index d85923e8..94186a8e 100644 --- a/src/face/mobilefacenet.ts +++ b/src/face/mobilefacenet.ts @@ -1,5 +1,5 @@ /** - * EfficientPose model implementation + * MobileFaceNet model implementation * * Based on: [**BecauseofAI MobileFace**](https://github.com/becauseofAI/MobileFace) * @@ -46,15 +46,15 @@ const contrast = merge.sub(mean).mul(factor).add(mean); export async function predict(input: Tensor, config: Config, idx, count): Promise { if (!model) return []; - const skipFrame = skipped < (config.face['embedding']?.skipFrames || 0); - const skipTime = (config.face['embedding']?.skipTime || 0) > (now() - lastTime); + const skipFrame = skipped < (config.face['mobilefacenet']?.skipFrames || 0); + const skipTime = (config.face['mobilefacenet']?.skipTime || 0) > (now() - lastTime); if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx]) { skipped++; return last[idx]; } return new Promise(async (resolve) => { let data: Array = []; - if (config.face['embedding']?.enabled && model?.inputs[0].shape) { + if (config.face['mobilefacenet']?.enabled && model?.inputs[0].shape) { const t: Record = {}; t.crop = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // just resize to fit the embedding model // do a tight crop of image and resize it to fit the model @@ -75,6 +75,7 @@ export async function predict(input: Tensor, config: Config, idx, count): Promis */ const output = await t.data.data(); data = Array.from(output); // convert typed array to simple array + Object.keys(t).forEach((tensor) => tf.dispose(t[tensor])); } last[idx] = data; lastCount = count; diff --git a/src/models.ts b/src/models.ts index ca81b07e..ae7f2c75 100644 --- a/src/models.ts +++ b/src/models.ts @@ -14,6 +14,7 @@ import * as centernet from './object/centernet'; import * as efficientpose from './body/efficientpose'; import * as emotion from './gear/emotion'; import * as mobilefacenet from './face/mobilefacenet'; +import * as insightface from './face/insightface'; import * as facemesh from './face/facemesh'; import * as faceres from './face/faceres'; import * as handpose from './hand/handpose'; @@ -42,6 +43,7 @@ export class Models { centernet: null | GraphModel | Promise = null; efficientpose: null | GraphModel | Promise = null; mobilefacenet: null | GraphModel | Promise = null; + insightface: null | GraphModel | Promise = null; emotion: null | GraphModel | Promise = null; facedetect: null | GraphModel | Promise = null; faceiris: null | GraphModel | Promise = null; @@ -131,6 +133,7 @@ export async function load(instance: Human): Promise { if (instance.config.face.enabled && instance.config.face['ssrnet']?.enabled && !instance.models.ssrnetgender) instance.models.ssrnetgender = ssrnetGender.load(instance.config); // @ts-ignore optional model if (instance.config.face.enabled && instance.config.face['mobilefacenet']?.enabled && !instance.models.mobilefacenet) instance.models.mobilefacenet = mobilefacenet.load(instance.config); + if (instance.config.face.enabled && instance.config.face['insightface']?.enabled && !instance.models.insightface) instance.models.insightface = insightface.load(instance.config); if (instance.config.hand.enabled && !instance.models.handtrack && instance.config.hand.detector?.modelPath?.includes('handtrack')) instance.models.handtrack = handtrack.loadDetect(instance.config); if (instance.config.hand.enabled && instance.config.hand.landmarks && !instance.models.handskeleton && instance.config.hand.detector?.modelPath?.includes('handtrack')) instance.models.handskeleton = handtrack.loadSkeleton(instance.config); if (instance.config.object.enabled && !instance.models.centernet && instance.config.object?.modelPath?.includes('centernet')) instance.models.centernet = centernet.load(instance.config); diff --git a/src/object/nanodet.ts b/src/object/nanodet.ts index 85f8f7c9..8952ca2d 100644 --- a/src/object/nanodet.ts +++ b/src/object/nanodet.ts @@ -36,56 +36,54 @@ async function process(res: Tensor[], outputShape: [number, number], config: Con let results: Array = []; for (const strideSize of [1, 2, 4]) { // try each stride size as it detects large/medium/small objects // find scores, boxes, classes - tf.tidy(async () => { // wrap in tidy to automatically deallocate temp tensors - const baseSize = strideSize * 13; // 13x13=169, 26x26=676, 52x52=2704 - // find boxes and scores output depending on stride - const scoresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) === labels.length))); - const featuresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) < labels.length))); - const boxesMax = featuresT.reshape([-1, 4, featuresT.shape[1] / 4]); // reshape [output] to [4, output / 4] where number is number of different features inside each stride - const boxIdx = await boxesMax.argMax(2).array(); // what we need is indexes of features with highest scores, not values itself - const scores = await scoresT.array(); // optionally use exponential scores or just as-is - for (let i = 0; i < scoresT.shape[0]; i++) { // total strides (x * y matrix) - for (let j = 0; j < scoresT.shape[1]; j++) { // one score for each class - const score = scores[i][j]; // get score for current position - if (score > (config.object.minConfidence || 0) && j !== 61) { - const cx = (0.5 + Math.trunc(i % baseSize)) / baseSize; // center.x normalized to range 0..1 - const cy = (0.5 + Math.trunc(i / baseSize)) / baseSize; // center.y normalized to range 0..1 - const boxOffset = boxIdx[i].map((a: number) => a * (baseSize / strideSize / inputSize)); // just grab indexes of features with highest scores - const [x, y] = [ - cx - (scaleBox / strideSize * boxOffset[0]), - cy - (scaleBox / strideSize * boxOffset[1]), - ]; - const [w, h] = [ - cx + (scaleBox / strideSize * boxOffset[2]) - x, - cy + (scaleBox / strideSize * boxOffset[3]) - y, - ]; - let boxRaw: Box = [x, y, w, h]; // results normalized to range 0..1 - boxRaw = boxRaw.map((a) => Math.max(0, Math.min(a, 1))) as Box; // fix out-of-bounds coords - const box = [ // results normalized to input image pixels - boxRaw[0] * outputShape[0], - boxRaw[1] * outputShape[1], - boxRaw[2] * outputShape[0], - boxRaw[3] * outputShape[1], - ]; - const result = { - id: id++, - // strideSize, - score: Math.round(100 * score) / 100, - class: j + 1, - label: labels[j].label as ObjectType, - // center: [Math.trunc(outputShape[0] * cx), Math.trunc(outputShape[1] * cy)], - // centerRaw: [cx, cy], - box: box.map((a) => Math.trunc(a)) as Box, - boxRaw, - }; - results.push(result); - } + const baseSize = strideSize * 13; // 13x13=169, 26x26=676, 52x52=2704 + // find boxes and scores output depending on stride + const scoresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) === labels.length))); + const scores = await scoresT.array(); // optionally use exponential scores or just as-is + const featuresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) < labels.length))); + const boxesMaxT = featuresT.reshape([-1, 4, featuresT.shape[1] / 4]); // reshape [output] to [4, output / 4] where number is number of different features inside each stride + const boxIdxT = boxesMaxT.argMax(2); // what we need is indexes of features with highest scores, not values itself + const boxIdx = await boxIdxT.array(); // what we need is indexes of features with highest scores, not values itself + for (let i = 0; i < scoresT.shape[0]; i++) { // total strides (x * y matrix) + for (let j = 0; j < scoresT.shape[1]; j++) { // one score for each class + const score = scores[i][j]; // get score for current position + if (score > (config.object.minConfidence || 0) && j !== 61) { + const cx = (0.5 + Math.trunc(i % baseSize)) / baseSize; // center.x normalized to range 0..1 + const cy = (0.5 + Math.trunc(i / baseSize)) / baseSize; // center.y normalized to range 0..1 + const boxOffset = boxIdx[i].map((a: number) => a * (baseSize / strideSize / inputSize)); // just grab indexes of features with highest scores + const [x, y] = [ + cx - (scaleBox / strideSize * boxOffset[0]), + cy - (scaleBox / strideSize * boxOffset[1]), + ]; + const [w, h] = [ + cx + (scaleBox / strideSize * boxOffset[2]) - x, + cy + (scaleBox / strideSize * boxOffset[3]) - y, + ]; + let boxRaw: Box = [x, y, w, h]; // results normalized to range 0..1 + boxRaw = boxRaw.map((a) => Math.max(0, Math.min(a, 1))) as Box; // fix out-of-bounds coords + const box = [ // results normalized to input image pixels + boxRaw[0] * outputShape[0], + boxRaw[1] * outputShape[1], + boxRaw[2] * outputShape[0], + boxRaw[3] * outputShape[1], + ]; + const result = { + id: id++, + // strideSize, + score: Math.round(100 * score) / 100, + class: j + 1, + label: labels[j].label as ObjectType, + // center: [Math.trunc(outputShape[0] * cx), Math.trunc(outputShape[1] * cy)], + // centerRaw: [cx, cy], + box: box.map((a) => Math.trunc(a)) as Box, + boxRaw, + }; + results.push(result); } } - }); + } + tf.dispose([scoresT, featuresT, boxesMaxT, boxIdxT]); } - // deallocate tensors - res.forEach((t) => tf.dispose(t)); // normally nms is run on raw results, but since boxes need to be calculated this way we skip calulcation of // unnecessary boxes and run nms only on good candidates (basically it just does IOU analysis as scores are already filtered) @@ -117,19 +115,17 @@ export async function predict(image: Tensor, config: Config): Promise { const outputSize = [image.shape[2] || 0, image.shape[1] || 0]; - const resize = tf.image.resizeBilinear(image, [inputSize, inputSize], false); - const norm = tf.div(resize, constants.tf255); - const transpose = norm.transpose([0, 3, 1, 2]); - tf.dispose(norm); - tf.dispose(resize); + const resizeT = tf.image.resizeBilinear(image, [inputSize, inputSize], false); + const normT = tf.div(resizeT, constants.tf255); + const transposeT = tf.transpose(normT, [0, 3, 1, 2]); let objectT; - if (config.object.enabled) objectT = model.execute(transpose); + if (config.object.enabled) objectT = model.execute(transposeT); lastTime = now(); - tf.dispose(transpose); const obj = await process(objectT as Tensor[], outputSize as [number, number], config); last = obj; + tf.dispose([resizeT, normT, transposeT, ...objectT]); resolve(obj); }); } diff --git a/test/build.log b/test/build.log index efeb664e..35e72036 100644 --- a/test/build.log +++ b/test/build.log @@ -1,24 +1,24 @@ -2022-08-04 09:11:15 INFO:  Application: {"name":"@vladmandic/human","version":"2.9.1"} -2022-08-04 09:11:15 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true} -2022-08-04 09:11:15 INFO:  Toolchain: {"build":"0.7.8","esbuild":"0.14.53","typescript":"4.7.4","typedoc":"0.23.10","eslint":"8.21.0"} -2022-08-04 09:11:15 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]} -2022-08-04 09:11:15 STATE: Clean: {"locations":["dist/*","types/lib/*","typedoc/*"]} -2022-08-04 09:11:15 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":608} -2022-08-04 09:11:15 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":74,"inputBytes":649269,"outputBytes":304361} -2022-08-04 09:11:15 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":612} -2022-08-04 09:11:15 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":74,"inputBytes":649273,"outputBytes":304365} -2022-08-04 09:11:15 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":664} -2022-08-04 09:11:15 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":74,"inputBytes":649325,"outputBytes":304415} -2022-08-04 09:11:15 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1069,"outputBytes":358} -2022-08-04 09:11:15 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":1032,"outputBytes":583} -2022-08-04 09:11:15 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":74,"inputBytes":649244,"outputBytes":303238} -2022-08-04 09:11:15 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":11,"inputBytes":1271,"outputBytes":2787569} -2022-08-04 09:11:15 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":74,"inputBytes":3436230,"outputBytes":1666096} -2022-08-04 09:11:16 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":74,"inputBytes":3436230,"outputBytes":3066923} -2022-08-04 09:11:22 STATE: Typings: {"input":"src/human.ts","output":"types/lib","files":30} -2022-08-04 09:11:25 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":76,"generated":true} -2022-08-04 09:11:25 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":6787,"outputBytes":3141} -2022-08-04 09:11:25 STATE: Compile: {"name":"demo/faceid","format":"esm","platform":"browser","input":"demo/faceid/index.ts","output":"demo/faceid/index.js","files":2,"inputBytes":15174,"outputBytes":7604} -2022-08-04 09:11:35 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":107,"errors":0,"warnings":0} -2022-08-04 09:11:35 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"} -2022-08-04 09:11:35 INFO:  Done... +2022-08-08 15:08:28 INFO:  Application: {"name":"@vladmandic/human","version":"2.9.2"} +2022-08-08 15:08:28 INFO:  Environment: {"profile":"production","config":".build.json","package":"package.json","tsconfig":true,"eslintrc":true,"git":true} +2022-08-08 15:08:28 INFO:  Toolchain: {"build":"0.7.8","esbuild":"0.14.54","typescript":"4.7.4","typedoc":"0.23.10","eslint":"8.21.0"} +2022-08-08 15:08:28 INFO:  Build: {"profile":"production","steps":["clean","compile","typings","typedoc","lint","changelog"]} +2022-08-08 15:08:28 STATE: Clean: {"locations":["dist/*","types/lib/*","typedoc/*"]} +2022-08-08 15:08:28 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":608} +2022-08-08 15:08:28 STATE: Compile: {"name":"human/nodejs/cpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node.js","files":75,"inputBytes":652676,"outputBytes":305745} +2022-08-08 15:08:28 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":612} +2022-08-08 15:08:28 STATE: Compile: {"name":"human/nodejs/gpu","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-gpu.js","files":75,"inputBytes":652680,"outputBytes":305749} +2022-08-08 15:08:28 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":664} +2022-08-08 15:08:28 STATE: Compile: {"name":"human/nodejs/wasm","format":"cjs","platform":"node","input":"src/human.ts","output":"dist/human.node-wasm.js","files":75,"inputBytes":652732,"outputBytes":305799} +2022-08-08 15:08:28 STATE: Compile: {"name":"tfjs/browser/version","format":"esm","platform":"browser","input":"tfjs/tf-version.ts","output":"dist/tfjs.version.js","files":1,"inputBytes":1069,"outputBytes":358} +2022-08-08 15:08:28 STATE: Compile: {"name":"tfjs/browser/esm/nobundle","format":"esm","platform":"browser","input":"tfjs/tf-browser.ts","output":"dist/tfjs.esm.js","files":2,"inputBytes":1032,"outputBytes":583} +2022-08-08 15:08:28 STATE: Compile: {"name":"human/browser/esm/nobundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm-nobundle.js","files":75,"inputBytes":652651,"outputBytes":304598} +2022-08-08 15:08:28 STATE: Compile: {"name":"tfjs/browser/esm/custom","format":"esm","platform":"browser","input":"tfjs/tf-custom.ts","output":"dist/tfjs.esm.js","files":11,"inputBytes":1271,"outputBytes":2787569} +2022-08-08 15:08:29 STATE: Compile: {"name":"human/browser/iife/bundle","format":"iife","platform":"browser","input":"src/human.ts","output":"dist/human.js","files":75,"inputBytes":3439637,"outputBytes":1667460} +2022-08-08 15:08:29 STATE: Compile: {"name":"human/browser/esm/bundle","format":"esm","platform":"browser","input":"src/human.ts","output":"dist/human.esm.js","files":75,"inputBytes":3439637,"outputBytes":3069462} +2022-08-08 15:08:36 STATE: Typings: {"input":"src/human.ts","output":"types/lib","files":30} +2022-08-08 15:08:38 STATE: TypeDoc: {"input":"src/human.ts","output":"typedoc","objects":76,"generated":true} +2022-08-08 15:08:38 STATE: Compile: {"name":"demo/typescript","format":"esm","platform":"browser","input":"demo/typescript/index.ts","output":"demo/typescript/index.js","files":1,"inputBytes":6787,"outputBytes":3141} +2022-08-08 15:08:38 STATE: Compile: {"name":"demo/faceid","format":"esm","platform":"browser","input":"demo/faceid/index.ts","output":"demo/faceid/index.js","files":2,"inputBytes":15629,"outputBytes":7798} +2022-08-08 15:08:47 STATE: Lint: {"locations":["*.json","src/**/*.ts","test/**/*.js","demo/**/*.js"],"files":108,"errors":0,"warnings":0} +2022-08-08 15:08:47 STATE: ChangeLog: {"repository":"https://github.com/vladmandic/human","branch":"main","output":"CHANGELOG.md"} +2022-08-08 15:08:47 INFO:  Done... diff --git a/test/node.js b/test/node.js index 85d0dc15..1f013005 100644 --- a/test/node.js +++ b/test/node.js @@ -5,11 +5,12 @@ const { fork } = require('child_process'); const log = require('@vladmandic/pilogger'); let logFile = 'test.log'; +log.configure({ inspect: { breakLength: 500 } }); const tests = [ 'test-node.js', - 'test-node-gpu.js', - 'test-node-wasm.js', + // 'test-node-gpu.js', + // 'test-node-wasm.js', ]; const demos = [ diff --git a/test/test-main.js b/test/test-main.js index 1af9322b..3110adb7 100644 --- a/test/test-main.js +++ b/test/test-main.js @@ -254,7 +254,7 @@ async function test(Human, inputConfig) { await human.load(); const models = Object.keys(human.models).map((model) => ({ name: model, loaded: (human.models[model] !== null), url: human.models[model] ? human.models[model].modelUrl : null })); const loaded = models.filter((model) => model.loaded); - if (models.length === 22 && loaded.length === 12) log('state', 'passed: models loaded', models.length, loaded.length, models); + if (models.length === 23 && loaded.length === 12) log('state', 'passed: models loaded', models.length, loaded.length, models); else log('error', 'failed: models loaded', models.length, loaded.length, models); // increase defaults @@ -292,7 +292,7 @@ async function test(Human, inputConfig) { human.reset(); config.async = true; config.cacheSensitivity = 0; - res = await testDetect(human, 'samples/in/ai-body.jpg', 'default'); + res = await testDetect(human, 'samples/in/ai-body.jpg', 'async'); 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); @@ -301,7 +301,7 @@ async function test(Human, inputConfig) { human.reset(); config.async = false; config.cacheSensitivity = 0; - res = await testDetect(human, 'samples/in/ai-body.jpg', 'default'); + res = await testDetect(human, 'samples/in/ai-body.jpg', 'sync'); 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); @@ -319,21 +319,25 @@ async function test(Human, inputConfig) { else log('state', 'passed: invalid input', res.error || res); // test face attention - /* log('info', 'test face attention'); + config.face.mesh = { enabled: true }; + res = await testDetect(human, 'samples/in/ai-face.jpg', 'face mesh'); + if (!res || !res.face[0] || res.face[0].mesh.length !== 478 || Object.keys(res.face[0].annotations).length !== 36) log('error', 'failed: face mesh', { mesh: res.face[0]?.mesh?.length, annotations: Object.keys(res.face[0].annotations).length }); + else log('state', 'passed: face attention'); human.models.facemesh = null; // unload model config.face.attention = { enabled: true }; - res = await testDetect(human, 'samples/in/ai-face.jpg', 'default'); - */ + res = await testDetect(human, 'samples/in/ai-face.jpg', 'face attention'); + if (!res || !res.face[0] || res.face[0].mesh.length !== 478 || Object.keys(res.face[0].annotations).length !== 36) log('error', 'failed: face attention', { mesh: res.face[0]?.mesh?.length, annotations: Object.keys(res.face[0].annotations).length }); + else log('state', 'passed: face attention'); // test face similarity log('info', 'test face similarity'); human.reset(); config.async = false; config.cacheSensitivity = 0; - 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'); + let res1 = await testDetect(human, 'samples/in/ai-face.jpg', 'face similarity'); + let res2 = await testDetect(human, 'samples/in/ai-body.jpg', 'face similarity'); + let res3 = await testDetect(human, 'samples/in/ai-upper.jpg', 'face similarity'); 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; @@ -345,6 +349,20 @@ async function test(Human, inputConfig) { if (res1 < 1 || res2 < 0.40 || res3 < 0.40 || res2 > 0.75 || res3 > 0.75) log('error', 'failed: face similarity', { similarity: [res1, res2, res3], descriptors: [desc1?.length, desc2?.length, desc3?.length] }); else log('state', 'passed: face similarity', { similarity: [res1, res2, res3], descriptors: [desc1?.length, desc2?.length, desc3?.length] }); + // test alternative face embeddings + log('info', 'test face similarity alternative'); + human.reset(); + config.async = false; + config.cacheSensitivity = 0; + config.face['mobilefacenet'] = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/mobilefacenet.json' }; + res = await testDetect(human, 'samples/in/ai-face.jpg', 'face embeddings'); + if (!res || !res.face || !res.face[0] || res.face[0].embedding.length !== 192) log('error', 'failed: mobilefacenet', { embedding: res.face[0]?.embedding?.length }); + else log('state', 'passed: mobilefacenet'); + config.face['insightface'] = { enabled: true, modelPath: 'https://vladmandic.github.io/insightface/models/insightface-mobilenet-swish.json' }; + res = await testDetect(human, 'samples/in/ai-face.jpg', 'face embeddings'); + if (!res || !res.face || !res.face[0] || res.face[0].embedding.length !== 512) log('error', 'failed: insightface', { embedding: res.face[0]?.embedding?.length }); + else log('state', 'passed: insightface'); + // test face matching log('info', 'test face matching'); const db = JSON.parse(fs.readFileSync('demo/facematch/faces.json').toString()); @@ -360,10 +378,16 @@ async function test(Human, inputConfig) { // test object detection log('info', 'test object'); human.reset(); - config.object = { enabled: true }; - res = await testDetect(human, 'samples/in/ai-body.jpg', 'default'); - 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'); + config.object = { enabled: true, modelPath: 'mb3-centernet.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'object'); + if (!res || res?.object?.length < 1 || res?.object[0]?.label !== 'person') log('error', 'failed: centernet', res?.object); + else log('state', 'passed: centernet'); + + config.object = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/nanodet.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'object'); + if (!res || res?.object?.length < 1 || res?.object[0]?.label !== 'person') log('error', 'failed: nanodet', res?.object); + else log('state', 'passed: nanodet'); + config.object.enabled = false; // test sensitive config log('info', 'test sensitive'); @@ -372,7 +396,7 @@ async function test(Human, inputConfig) { config.face = { detector: { minConfidence: 0.0001, maxDetected: 1 } }; config.body = { minConfidence: 0.0001 }; config.hand = { minConfidence: 0.0001 }; - res = await testDetect(human, 'samples/in/ai-body.jpg', 'default'); + res = await testDetect(human, 'samples/in/ai-body.jpg', 'sensitive'); 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); else log('state', 'passed: sensitive result match'); @@ -394,13 +418,39 @@ async function test(Human, inputConfig) { 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'); + // test body alternatives + log('info', 'test body'); + human.reset(); + config.async = false; + config.cacheSensitivity = 0; + + config.body = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/blazepose-heavy.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'blazepose'); + if (!res || !res.body || !res.body[0] || res.body[0].score < 0.9 || res.body[0].keypoints?.length !== 39) log('error', 'failed: blazepose', { body: res.body[0] }); + else log('state', 'passed: blazepose'); + + config.body = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/efficientpose.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'efficientpose'); + if (!res || !res.body || !res.body[0] || res.body[0].score < 0.7 || res.body[0].keypoints?.length !== 13) log('error', 'failed: efficientpose', { body: res.body[0] }); + else log('state', 'passed: efficientpose'); + + config.body = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/posenet.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'posenet'); + if (!res || !res.body || !res.body[0] || res.body[0].score < 0.9 || res.body[0].keypoints?.length !== 16) log('error', 'failed: posenet', { body: res.body[0] }); + else log('state', 'passed: posenet'); + + config.body = { enabled: true, modelPath: 'https://vladmandic.github.io/human-models/models/movenet-lightning.json' }; + res = await testDetect(human, 'samples/in/ai-body.jpg', 'movenet'); + if (!res || !res.body || !res.body[0] || res.body[0].score < 0.9 || res.body[0].keypoints?.length !== 17) log('error', 'failed: movenet', { body: res.body[0] }); + else log('state', 'passed: movenet'); + // test detectors only log('info', 'test detectors'); human.reset(); config.face = { mesh: { enabled: false }, iris: { enabled: false }, description: { enabled: false }, emotion: { enabled: false } }; config.hand = { landmarks: false }; - res = await testDetect(human, 'samples/in/ai-body.jpg', 'default'); - 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); + res = await testDetect(human, 'samples/in/ai-body.jpg', 'detectors'); + if (!res || res?.face?.length !== 1 || res?.face[0]?.gender !== 'unknown' || res?.face[0]?.age || res?.face[0]?.embedding?.length > 0) log('error', 'failed: detectors result face mismatch', res?.face); else log('state', 'passed: detector result face match'); if (!res || res?.hand?.length !== 1 || res?.hand[0]?.landmarks?.length > 0) log('error', 'failed: detectors result hand mismatch', res?.hand?.length); else log('state', 'passed: detector result hand match'); @@ -409,24 +459,24 @@ async function test(Human, inputConfig) { log('info', 'test: multi-instance'); const first = new Human(config); const second = new Human(config); - await testDetect(human, null, 'default'); + await testDetect(human, null, 'multi instance'); log('info', 'test: first instance'); - await testDetect(first, 'samples/in/ai-upper.jpg', 'default'); + await testDetect(first, 'samples/in/ai-upper.jpg', 'multi instance'); log('info', 'test: second instance'); - await testDetect(second, 'samples/in/ai-upper.jpg', 'default'); + await testDetect(second, 'samples/in/ai-upper.jpg', 'multi instance'); // test async multiple instances log('info', 'test: concurrent'); await Promise.all([ - 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), + testDetect(human, 'samples/in/ai-face.jpg', 'concurrent', false), + testDetect(first, 'samples/in/ai-face.jpg', 'concurrent', false), + testDetect(second, 'samples/in/ai-face.jpg', 'concurrent', false), + testDetect(human, 'samples/in/ai-body.jpg', 'concurrent', false), + testDetect(first, 'samples/in/ai-body.jpg', 'concurrent', false), + testDetect(second, 'samples/in/ai-body.jpg', 'concurrent', false), + testDetect(human, 'samples/in/ai-upper.jpg', 'concurrent', false), + testDetect(first, 'samples/in/ai-upper.jpg', 'concurrent', false), + testDetect(second, 'samples/in/ai-upper.jpg', 'concurrent', false), ]); // test monkey-patch diff --git a/test/test.log b/test/test.log index c8617008..0c94ed72 100644 --- a/test/test.log +++ b/test/test.log @@ -1,684 +1,298 @@ -2022-08-04 09:13:03 INFO:  @vladmandic/human version 2.9.1 -2022-08-04 09:13:03 INFO:  User: vlado Platform: linux Arch: x64 Node: v16.15.0 -2022-08-04 09:13:03 INFO:  tests: ["test-node.js","test-node-gpu.js","test-node-wasm.js"] -2022-08-04 09:13:03 INFO:  demos: ["../demo/nodejs/node.js","../demo/nodejs/node-canvas.js","../demo/nodejs/node-env.js","../demo/nodejs/node-event.js","../demo/nodejs/node-multiprocess.js"] -2022-08-04 09:13:03 INFO:  -2022-08-04 09:13:03 INFO:  test-node.js start -2022-08-04 09:13:03 INFO:  test-node.js test: configuration validation -2022-08-04 09:13:03 STATE: test-node.js passed: configuration default validation [] -2022-08-04 09:13:03 STATE: test-node.js passed: configuration invalid validation [{"reason":"unknown property","where":"config.invalid = true"}] -2022-08-04 09:13:03 INFO:  test-node.js test: model load -2022-08-04 09:13:04 STATE: test-node.js passed: models loaded 22 12 [{"name":"ssrnetage","loaded":false,"url":null},{"name":"gear","loaded":false,"url":null},{"name":"blazeposedetect","loaded":false,"url":null},{"name":"blazepose","loaded":false,"url":null},{"name":"centernet","loaded":true,"url":"file://models/mb3-centernet.json"},{"name":"efficientpose","loaded":false,"url":null},{"name":"mobilefacenet","loaded":false,"url":null},{"name":"emotion","loaded":true,"url":"file://models/emotion.json"},{"name":"facedetect","loaded":true,"url":"file://models/blazeface.json"},{"name":"faceiris","loaded":true,"url":"file://models/iris.json"},{"name":"facemesh","loaded":true,"url":"file://models/facemesh.json"},{"name":"faceres","loaded":true,"url":"file://models/faceres.json"},{"name":"ssrnetgender","loaded":false,"url":null},{"name":"handpose","loaded":false,"url":null},{"name":"handskeleton","loaded":true,"url":"file://models/handlandmark-full.json"},{"name":"handtrack","loaded":true,"url":"file://models/handtrack.json"},{"name":"liveness","loaded":true,"url":"file://models/liveness.json"},{"name":"movenet","loaded":true,"url":"file://models/movenet-lightning.json"},{"name":"nanodet","loaded":false,"url":null},{"name":"posenet","loaded":false,"url":null},{"name":"segmentation","loaded":true,"url":"file://models/selfie.json"},{"name":"antispoof","loaded":true,"url":"file://models/antispoof.json"}] -2022-08-04 09:13:04 INFO:  test-node.js test: warmup -2022-08-04 09:13:04 STATE: test-node.js passed: create human -2022-08-04 09:13:04 INFO:  test-node.js human version: 2.9.1 -2022-08-04 09:13:04 INFO:  test-node.js platform: linux x64 agent: NodeJS v16.15.0 -2022-08-04 09:13:04 INFO:  test-node.js tfjs version: 3.19.0 -2022-08-04 09:13:04 INFO:  test-node.js tensorflow binding version: 2.7.3-dev20220521 -2022-08-04 09:13:04 STATE: test-node.js passed: set backend: tensorflow -2022-08-04 09:13:04 STATE: test-node.js tensors 1921 -2022-08-04 09:13:04 STATE: test-node.js passed: load models -2022-08-04 09:13:04 STATE: test-node.js result: defined models: 22 loaded models: 12 -2022-08-04 09:13:04 STATE: test-node.js passed: warmup: none default -2022-08-04 09:13:04 DATA:  test-node.js result: face: 0 body: 0 hand: 0 gesture: 0 object: 0 person: 0 {} {} {} -2022-08-04 09:13:04 DATA:  test-node.js result: performance: load: null total: null -2022-08-04 09:13:04 STATE: test-node.js passed: warmup none result match -2022-08-04 09:13:04 STATE: test-node.js event: image -2022-08-04 09:13:04 STATE: test-node.js event: detect -2022-08-04 09:13:04 STATE: test-node.js event: warmup -2022-08-04 09:13:04 STATE: test-node.js passed: warmup: face default -2022-08-04 09:13:04 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.42,"keypoints":4} -2022-08-04 09:13:04 DATA:  test-node.js result: performance: load: null total: 354 -2022-08-04 09:13:04 STATE: test-node.js passed: warmup face result match -2022-08-04 09:13:04 STATE: test-node.js event: image -2022-08-04 09:13:04 STATE: test-node.js event: detect -2022-08-04 09:13:04 STATE: test-node.js event: warmup -2022-08-04 09:13:04 STATE: test-node.js passed: warmup: body default -2022-08-04 09:13:04 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:04 DATA:  test-node.js result: performance: load: null total: 270 -2022-08-04 09:13:04 STATE: test-node.js passed: warmup body result match -2022-08-04 09:13:04 STATE: test-node.js details: {"face":{"boxScore":0.92,"faceScore":1,"age":23.7,"gender":"female","genderScore":0.97},"emotion":[{"score":0.63,"emotion":"angry"},{"score":0.22,"emotion":"fear"}],"body":{"score":0.92,"keypoints":17},"hand":{"boxScore":0.52,"fingerScore":0.73,"keypoints":21},"gestures":[{"face":0,"gesture":"facing right"},{"face":0,"gesture":"mouth 10% open"},{"hand":0,"gesture":"pinky forward"},{"hand":0,"gesture":"palm up"},{"hand":0,"gesture":"open palm"},{"iris":0,"gesture":"looking left"},{"iris":0,"gesture":"looking up"}]} -2022-08-04 09:13:04 INFO:  test-node.js test: details verification -2022-08-04 09:13:04 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:04 STATE: test-node.js event: image -2022-08-04 09:13:05 STATE: test-node.js event: detect -2022-08-04 09:13:05 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:05 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:05 DATA:  test-node.js result: performance: load: null total: 248 -2022-08-04 09:13:05 STATE: test-node.js passed: details face length 1 -2022-08-04 09:13:05 STATE: test-node.js passed: details face score 1 0.93 1 -2022-08-04 09:13:05 STATE: test-node.js passed: details face age/gender 23.7 female 0.97 85.47 -2022-08-04 09:13:05 STATE: test-node.js passed: details face arrays 4 478 1024 -2022-08-04 09:13:05 STATE: test-node.js passed: details face emotion 2 {"score":0.59,"emotion":"angry"} -2022-08-04 09:13:05 STATE: test-node.js passed: details face anti-spoofing 0.79 -2022-08-04 09:13:05 STATE: test-node.js passed: details face liveness 0.83 -2022-08-04 09:13:05 STATE: test-node.js passed: details body length 1 -2022-08-04 09:13:05 STATE: test-node.js passed: details body 0.92 17 6 -2022-08-04 09:13:05 STATE: test-node.js passed: details hand length 1 -2022-08-04 09:13:05 STATE: test-node.js passed: details hand 0.51 0.73 point -2022-08-04 09:13:05 STATE: test-node.js passed: details hand arrays 21 5 7 -2022-08-04 09:13:05 STATE: test-node.js passed: details gesture length 7 -2022-08-04 09:13:05 STATE: test-node.js passed: details gesture first {"face":0,"gesture":"facing right"} -2022-08-04 09:13:05 STATE: test-node.js passed: details object length 1 -2022-08-04 09:13:05 STATE: test-node.js passed: details object 0.72 person -2022-08-04 09:13:05 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996928} -2022-08-04 09:13:05 STATE: test-node.js event: image -2022-08-04 09:13:05 STATE: test-node.js event: detect -2022-08-04 09:13:05 STATE: test-node.js passed: tensor shape: [1,1200,1200,4] dtype: float32 -2022-08-04 09:13:05 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1200,1200,4] {"checksum":1371996928} -2022-08-04 09:13:05 STATE: test-node.js event: image -2022-08-04 09:13:06 STATE: test-node.js event: detect -2022-08-04 09:13:06 STATE: test-node.js passed: tensor shape: [1200,1200,4] dtype: float32 -2022-08-04 09:13:06 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:06 STATE: test-node.js event: image -2022-08-04 09:13:06 STATE: test-node.js event: detect -2022-08-04 09:13:06 STATE: test-node.js passed: tensor shape: [1,1200,1200,3] dtype: float32 -2022-08-04 09:13:06 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:06 STATE: test-node.js event: image -2022-08-04 09:13:06 STATE: test-node.js event: detect -2022-08-04 09:13:06 STATE: test-node.js passed: tensor shape: [1200,1200,3] dtype: float32 -2022-08-04 09:13:07 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996871} -2022-08-04 09:13:07 STATE: test-node.js event: image -2022-08-04 09:13:07 STATE: test-node.js event: detect -2022-08-04 09:13:07 STATE: test-node.js passed: tensor shape: [1,1200,1200,4] dtype: int32 -2022-08-04 09:13:07 INFO:  test-node.js test default -2022-08-04 09:13:07 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:07 STATE: test-node.js event: image -2022-08-04 09:13:07 STATE: test-node.js event: detect -2022-08-04 09:13:07 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:07 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:07 DATA:  test-node.js result: performance: load: null total: 212 -2022-08-04 09:13:07 STATE: test-node.js passed: default result face match 1 female 0.97 -2022-08-04 09:13:07 INFO:  test-node.js test sync -2022-08-04 09:13:08 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:08 STATE: test-node.js event: image -2022-08-04 09:13:08 STATE: test-node.js event: detect -2022-08-04 09:13:08 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:08 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:08 DATA:  test-node.js result: performance: load: null total: 207 -2022-08-04 09:13:08 STATE: test-node.js passed: default sync 1 female 0.97 -2022-08-04 09:13:08 INFO:  test-node.js test: image process -2022-08-04 09:13:08 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:08 STATE: test-node.js passed: image input null [1,256,256,3] -2022-08-04 09:13:08 INFO:  test-node.js test: image null -2022-08-04 09:13:08 STATE: test-node.js passed: invalid input could not convert input to tensor -2022-08-04 09:13:08 INFO:  test-node.js test face similarity -2022-08-04 09:13:08 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:08 STATE: test-node.js event: image -2022-08-04 09:13:08 STATE: test-node.js event: detect -2022-08-04 09:13:08 STATE: test-node.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:08 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} -2022-08-04 09:13:08 DATA:  test-node.js result: performance: load: null total: 210 -2022-08-04 09:13:08 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:08 STATE: test-node.js event: image -2022-08-04 09:13:08 STATE: test-node.js event: detect -2022-08-04 09:13:08 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:08 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:08 DATA:  test-node.js result: performance: load: null total: 219 -2022-08-04 09:13:09 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:09 STATE: test-node.js event: image -2022-08-04 09:13:09 STATE: test-node.js event: detect -2022-08-04 09:13:09 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:09 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 4 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":7} -2022-08-04 09:13:09 DATA:  test-node.js result: performance: load: null total: 199 -2022-08-04 09:13:09 STATE: test-node.js passed: face descriptor -2022-08-04 09:13:09 STATE: test-node.js passed: face similarity {"similarity":[1,0.44727452329649126,0.5567935850640406],"descriptors":[1024,1024,1024]} -2022-08-04 09:13:09 INFO:  test-node.js test face matching -2022-08-04 09:13:09 STATE: test-node.js passed: face database 40 -2022-08-04 09:13:09 STATE: test-node.js passed: face match {"first":{"index":4,"similarity":0.7827852615252829}} {"second":{"index":4,"similarity":0.5002052633015844}} {"third":{"index":4,"similarity":0.5401587887998899}} -2022-08-04 09:13:09 INFO:  test-node.js test object -2022-08-04 09:13:09 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:09 STATE: test-node.js event: image -2022-08-04 09:13:09 STATE: test-node.js event: detect -2022-08-04 09:13:09 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:09 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:09 DATA:  test-node.js result: performance: load: null total: 207 -2022-08-04 09:13:09 STATE: test-node.js passed: object result match -2022-08-04 09:13:09 INFO:  test-node.js test sensitive -2022-08-04 09:13:09 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:09 STATE: test-node.js event: image -2022-08-04 09:13:10 STATE: test-node.js event: detect -2022-08-04 09:13:10 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:10 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:10 DATA:  test-node.js result: performance: load: null total: 229 -2022-08-04 09:13:10 STATE: test-node.js passed: sensitive result match -2022-08-04 09:13:10 STATE: test-node.js passed: sensitive face result match -2022-08-04 09:13:10 STATE: test-node.js passed: sensitive face emotion result [{"score":0.59,"emotion":"angry"},{"score":0.29,"emotion":"fear"}] -2022-08-04 09:13:10 STATE: test-node.js passed: sensitive body result match -2022-08-04 09:13:10 STATE: test-node.js passed: sensitive hand result match -2022-08-04 09:13:10 INFO:  test-node.js test detectors -2022-08-04 09:13:10 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:10 STATE: test-node.js event: image -2022-08-04 09:13:10 STATE: test-node.js event: detect -2022-08-04 09:13:10 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:10 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:10 DATA:  test-node.js result: performance: load: null total: 141 -2022-08-04 09:13:10 STATE: test-node.js passed: detector result face match -2022-08-04 09:13:10 STATE: test-node.js passed: detector result hand match -2022-08-04 09:13:10 INFO:  test-node.js test: multi-instance -2022-08-04 09:13:10 STATE: test-node.js event: image -2022-08-04 09:13:10 STATE: test-node.js event: detect -2022-08-04 09:13:10 STATE: test-node.js passed: detect: random default -2022-08-04 09:13:10 DATA:  test-node.js result: face: 0 body: 1 hand: 0 gesture: 0 object: 0 person: 0 {} {} {"score":0.07,"keypoints":15} -2022-08-04 09:13:10 DATA:  test-node.js result: performance: load: null total: 140 -2022-08-04 09:13:10 INFO:  test-node.js test: first instance -2022-08-04 09:13:10 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:10 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:10 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:10 DATA:  test-node.js result: performance: load: null total: 146 -2022-08-04 09:13:10 INFO:  test-node.js test: second instance -2022-08-04 09:13:10 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:11 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:11 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:11 DATA:  test-node.js result: performance: load: null total: 135 -2022-08-04 09:13:11 INFO:  test-node.js test: concurrent -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:11 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:12 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} -2022-08-04 09:13:12 STATE: test-node.js event: image -2022-08-04 09:13:12 STATE: test-node.js event: image -2022-08-04 09:13:12 STATE: test-node.js event: image -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1440 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1440 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1581 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1581 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1581 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1581 -2022-08-04 09:13:13 STATE: test-node.js event: detect -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1278 -2022-08-04 09:13:13 STATE: test-node.js event: detect -2022-08-04 09:13:13 STATE: test-node.js event: detect -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1278 -2022-08-04 09:13:13 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:13 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:13 DATA:  test-node.js result: performance: load: null total: 1278 -2022-08-04 09:13:13 INFO:  test-node.js test: monkey-patch -2022-08-04 09:13:13 STATE: test-node.js event: image -2022-08-04 09:13:13 STATE: test-node.js event: detect -2022-08-04 09:13:13 STATE: test-node.js passed: monkey patch -2022-08-04 09:13:13 STATE: test-node.js passed: segmentation [65536] -2022-08-04 09:13:13 STATE: test-node.js passeed: equal usage -2022-08-04 09:13:13 INFO:  test-node.js test: input compare -2022-08-04 09:13:13 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:13 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} -2022-08-04 09:13:13 STATE: test-node.js passed: image compare 0 23.275441687091504 -2022-08-04 09:13:13 INFO:  test-node.js events: {"image":21,"detect":21,"warmup":2} -2022-08-04 09:13:13 INFO:  test-node.js tensors 1927 -2022-08-04 09:13:13 INFO:  test-node.js test complete: 9854 ms -2022-08-04 09:13:13 INFO:  -2022-08-04 09:13:13 INFO:  test-node-gpu.js start -2022-08-04 09:13:14 INFO:  test-node-gpu.js test: configuration validation -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: configuration default validation [] -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: configuration invalid validation [{"reason":"unknown property","where":"config.invalid = true"}] -2022-08-04 09:13:14 INFO:  test-node-gpu.js test: model load -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: models loaded 22 12 [{"name":"ssrnetage","loaded":false,"url":null},{"name":"gear","loaded":false,"url":null},{"name":"blazeposedetect","loaded":false,"url":null},{"name":"blazepose","loaded":false,"url":null},{"name":"centernet","loaded":true,"url":"file://models/mb3-centernet.json"},{"name":"efficientpose","loaded":false,"url":null},{"name":"mobilefacenet","loaded":false,"url":null},{"name":"emotion","loaded":true,"url":"file://models/emotion.json"},{"name":"facedetect","loaded":true,"url":"file://models/blazeface.json"},{"name":"faceiris","loaded":true,"url":"file://models/iris.json"},{"name":"facemesh","loaded":true,"url":"file://models/facemesh.json"},{"name":"faceres","loaded":true,"url":"file://models/faceres.json"},{"name":"ssrnetgender","loaded":false,"url":null},{"name":"handpose","loaded":false,"url":null},{"name":"handskeleton","loaded":true,"url":"file://models/handlandmark-full.json"},{"name":"handtrack","loaded":true,"url":"file://models/handtrack.json"},{"name":"liveness","loaded":true,"url":"file://models/liveness.json"},{"name":"movenet","loaded":true,"url":"file://models/movenet-lightning.json"},{"name":"nanodet","loaded":false,"url":null},{"name":"posenet","loaded":false,"url":null},{"name":"segmentation","loaded":true,"url":"file://models/selfie.json"},{"name":"antispoof","loaded":true,"url":"file://models/antispoof.json"}] -2022-08-04 09:13:14 INFO:  test-node-gpu.js test: warmup -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: create human -2022-08-04 09:13:14 INFO:  test-node-gpu.js human version: 2.9.1 -2022-08-04 09:13:14 INFO:  test-node-gpu.js platform: linux x64 agent: NodeJS v16.15.0 -2022-08-04 09:13:14 INFO:  test-node-gpu.js tfjs version: 3.19.0 -2022-08-04 09:13:14 INFO:  test-node-gpu.js tensorflow binding version: 2.7.3-dev20220521 -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: set backend: tensorflow -2022-08-04 09:13:14 STATE: test-node-gpu.js tensors 1921 -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: load models -2022-08-04 09:13:14 STATE: test-node-gpu.js result: defined models: 22 loaded models: 12 -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: warmup: none default -2022-08-04 09:13:14 DATA:  test-node-gpu.js result: face: 0 body: 0 hand: 0 gesture: 0 object: 0 person: 0 {} {} {} -2022-08-04 09:13:14 DATA:  test-node-gpu.js result: performance: load: null total: null -2022-08-04 09:13:14 STATE: test-node-gpu.js passed: warmup none result match -2022-08-04 09:13:15 STATE: test-node-gpu.js event: image -2022-08-04 09:13:19 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:19 STATE: test-node-gpu.js event: warmup -2022-08-04 09:13:19 STATE: test-node-gpu.js passed: warmup: face default -2022-08-04 09:13:19 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.42,"keypoints":4} -2022-08-04 09:13:19 DATA:  test-node-gpu.js result: performance: load: null total: 3847 -2022-08-04 09:13:19 STATE: test-node-gpu.js passed: warmup face result match -2022-08-04 09:13:19 STATE: test-node-gpu.js event: image -2022-08-04 09:13:19 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:19 STATE: test-node-gpu.js event: warmup -2022-08-04 09:13:19 STATE: test-node-gpu.js passed: warmup: body default -2022-08-04 09:13:19 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:19 DATA:  test-node-gpu.js result: performance: load: null total: 240 -2022-08-04 09:13:19 STATE: test-node-gpu.js passed: warmup body result match -2022-08-04 09:13:19 STATE: test-node-gpu.js details: {"face":{"boxScore":0.92,"faceScore":1,"age":23.7,"gender":"female","genderScore":0.97},"emotion":[{"score":0.63,"emotion":"angry"},{"score":0.22,"emotion":"fear"}],"body":{"score":0.92,"keypoints":17},"hand":{"boxScore":0.52,"fingerScore":0.73,"keypoints":21},"gestures":[{"face":0,"gesture":"facing right"},{"face":0,"gesture":"mouth 10% open"},{"hand":0,"gesture":"pinky forward"},{"hand":0,"gesture":"palm up"},{"hand":0,"gesture":"open palm"},{"iris":0,"gesture":"looking left"},{"iris":0,"gesture":"looking up"}]} -2022-08-04 09:13:19 INFO:  test-node-gpu.js test: details verification -2022-08-04 09:13:19 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:19 STATE: test-node-gpu.js event: image -2022-08-04 09:13:20 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:20 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:20 DATA:  test-node-gpu.js result: performance: load: null total: 224 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face length 1 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face score 1 0.93 1 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face age/gender 23.7 female 0.97 85.47 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face arrays 4 478 1024 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face emotion 2 {"score":0.59,"emotion":"angry"} -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face anti-spoofing 0.79 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details face liveness 0.83 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details body length 1 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details body 0.92 17 6 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details hand length 1 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details hand 0.51 0.73 point -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details hand arrays 21 5 7 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details gesture length 7 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details gesture first {"face":0,"gesture":"facing right"} -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details object length 1 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: details object 0.72 person -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996928} -2022-08-04 09:13:20 STATE: test-node-gpu.js event: image -2022-08-04 09:13:20 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: tensor shape: [1,1200,1200,4] dtype: float32 -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1200,1200,4] {"checksum":1371996928} -2022-08-04 09:13:20 STATE: test-node-gpu.js event: image -2022-08-04 09:13:20 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:20 STATE: test-node-gpu.js passed: tensor shape: [1200,1200,4] dtype: float32 -2022-08-04 09:13:21 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:21 STATE: test-node-gpu.js event: image -2022-08-04 09:13:21 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:21 STATE: test-node-gpu.js passed: tensor shape: [1,1200,1200,3] dtype: float32 -2022-08-04 09:13:21 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:21 STATE: test-node-gpu.js event: image -2022-08-04 09:13:21 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:21 STATE: test-node-gpu.js passed: tensor shape: [1200,1200,3] dtype: float32 -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996871} -2022-08-04 09:13:22 STATE: test-node-gpu.js event: image -2022-08-04 09:13:22 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: tensor shape: [1,1200,1200,4] dtype: int32 -2022-08-04 09:13:22 INFO:  test-node-gpu.js test default -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:22 STATE: test-node-gpu.js event: image -2022-08-04 09:13:22 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:22 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:22 DATA:  test-node-gpu.js result: performance: load: null total: 238 -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: default result face match 1 female 0.97 -2022-08-04 09:13:22 INFO:  test-node-gpu.js test sync -2022-08-04 09:13:22 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:22 STATE: test-node-gpu.js event: image -2022-08-04 09:13:23 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: performance: load: null total: 188 -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: default sync 1 female 0.97 -2022-08-04 09:13:23 INFO:  test-node-gpu.js test: image process -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: image input null [1,256,256,3] -2022-08-04 09:13:23 INFO:  test-node-gpu.js test: image null -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: invalid input could not convert input to tensor -2022-08-04 09:13:23 INFO:  test-node-gpu.js test face similarity -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:23 STATE: test-node-gpu.js event: image -2022-08-04 09:13:23 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: performance: load: null total: 164 -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:23 STATE: test-node-gpu.js event: image -2022-08-04 09:13:23 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: performance: load: null total: 196 -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:23 STATE: test-node-gpu.js event: image -2022-08-04 09:13:23 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 4 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":7} -2022-08-04 09:13:23 DATA:  test-node-gpu.js result: performance: load: null total: 171 -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: face descriptor -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: face similarity {"similarity":[1,0.447238756461232,0.556914029877052],"descriptors":[1024,1024,1024]} -2022-08-04 09:13:23 INFO:  test-node-gpu.js test face matching -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: face database 40 -2022-08-04 09:13:23 STATE: test-node-gpu.js passed: face match {"first":{"index":4,"similarity":0.7828184453007331}} {"second":{"index":4,"similarity":0.5001334216773398}} {"third":{"index":4,"similarity":0.5403054967489764}} -2022-08-04 09:13:23 INFO:  test-node-gpu.js test object -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:24 STATE: test-node-gpu.js event: image -2022-08-04 09:13:24 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:24 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:24 DATA:  test-node-gpu.js result: performance: load: null total: 194 -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: object result match -2022-08-04 09:13:24 INFO:  test-node-gpu.js test sensitive -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:24 STATE: test-node-gpu.js event: image -2022-08-04 09:13:24 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:24 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:24 DATA:  test-node-gpu.js result: performance: load: null total: 196 -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: sensitive result match -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: sensitive face result match -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: sensitive face emotion result [{"score":0.59,"emotion":"angry"},{"score":0.29,"emotion":"fear"}] -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: sensitive body result match -2022-08-04 09:13:24 STATE: test-node-gpu.js passed: sensitive hand result match -2022-08-04 09:13:24 INFO:  test-node-gpu.js test detectors -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:25 STATE: test-node-gpu.js event: image -2022-08-04 09:13:25 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: performance: load: null total: 116 -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detector result face match -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detector result hand match -2022-08-04 09:13:25 INFO:  test-node-gpu.js test: multi-instance -2022-08-04 09:13:25 STATE: test-node-gpu.js event: image -2022-08-04 09:13:25 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detect: random default -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: face: 0 body: 1 hand: 0 gesture: 0 object: 0 person: 0 {} {} {"score":0.07,"keypoints":15} -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: performance: load: null total: 110 -2022-08-04 09:13:25 INFO:  test-node-gpu.js test: first instance -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: performance: load: null total: 108 -2022-08-04 09:13:25 INFO:  test-node-gpu.js test: second instance -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:25 DATA:  test-node-gpu.js result: performance: load: null total: 105 -2022-08-04 09:13:25 INFO:  test-node-gpu.js test: concurrent -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:25 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:26 STATE: test-node-gpu.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289056} -2022-08-04 09:13:26 STATE: test-node-gpu.js event: image -2022-08-04 09:13:26 STATE: test-node-gpu.js event: image -2022-08-04 09:13:26 STATE: test-node-gpu.js event: image -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1079 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1079 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1151 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1151 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1151 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 1151 -2022-08-04 09:13:27 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 831 -2022-08-04 09:13:27 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:27 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 831 -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:27 DATA:  test-node-gpu.js result: performance: load: null total: 831 -2022-08-04 09:13:27 INFO:  test-node-gpu.js test: monkey-patch -2022-08-04 09:13:27 STATE: test-node-gpu.js event: image -2022-08-04 09:13:27 STATE: test-node-gpu.js event: detect -2022-08-04 09:13:27 STATE: test-node-gpu.js passed: monkey patch -2022-08-04 09:13:28 STATE: test-node-gpu.js passed: segmentation [65536] -2022-08-04 09:13:28 STATE: test-node-gpu.js passeed: equal usage -2022-08-04 09:13:28 INFO:  test-node-gpu.js test: input compare -2022-08-04 09:13:28 STATE: test-node-gpu.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} -2022-08-04 09:13:28 STATE: test-node-gpu.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796928} -2022-08-04 09:13:28 STATE: test-node-gpu.js passed: image compare 0 23.275441687091504 -2022-08-04 09:13:28 INFO:  test-node-gpu.js events: {"image":21,"detect":21,"warmup":2} -2022-08-04 09:13:28 INFO:  test-node-gpu.js tensors 1927 -2022-08-04 09:13:28 INFO:  test-node-gpu.js test complete: 13503 ms -2022-08-04 09:13:29 INFO:  -2022-08-04 09:13:29 INFO:  test-node-wasm.js start -2022-08-04 09:13:29 STATE: test-node-wasm.js passed: model server: https://vladmandic.github.io/human/models/ -2022-08-04 09:13:29 INFO:  test-node-wasm.js test: configuration validation -2022-08-04 09:13:29 STATE: test-node-wasm.js passed: configuration default validation [] -2022-08-04 09:13:29 STATE: test-node-wasm.js passed: configuration invalid validation [{"reason":"unknown property","where":"config.invalid = true"}] -2022-08-04 09:13:29 INFO:  test-node-wasm.js test: model load -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: models loaded 22 12 [{"name":"ssrnetage","loaded":false,"url":null},{"name":"gear","loaded":false,"url":null},{"name":"blazeposedetect","loaded":false,"url":null},{"name":"blazepose","loaded":false,"url":null},{"name":"centernet","loaded":true,"url":"https://vladmandic.github.io/human/models/mb3-centernet.json"},{"name":"efficientpose","loaded":false,"url":null},{"name":"mobilefacenet","loaded":false,"url":null},{"name":"emotion","loaded":true,"url":"https://vladmandic.github.io/human/models/emotion.json"},{"name":"facedetect","loaded":true,"url":"https://vladmandic.github.io/human/models/blazeface.json"},{"name":"faceiris","loaded":true,"url":"https://vladmandic.github.io/human/models/iris.json"},{"name":"facemesh","loaded":true,"url":"https://vladmandic.github.io/human/models/facemesh.json"},{"name":"faceres","loaded":true,"url":"https://vladmandic.github.io/human/models/faceres.json"},{"name":"ssrnetgender","loaded":false,"url":null},{"name":"handpose","loaded":false,"url":null},{"name":"handskeleton","loaded":true,"url":"https://vladmandic.github.io/human/models/handlandmark-full.json"},{"name":"handtrack","loaded":true,"url":"https://vladmandic.github.io/human/models/handtrack.json"},{"name":"liveness","loaded":true,"url":"https://vladmandic.github.io/human/models/liveness.json"},{"name":"movenet","loaded":true,"url":"https://vladmandic.github.io/human/models/movenet-lightning.json"},{"name":"nanodet","loaded":false,"url":null},{"name":"posenet","loaded":false,"url":null},{"name":"segmentation","loaded":true,"url":"https://vladmandic.github.io/human/models/selfie.json"},{"name":"antispoof","loaded":true,"url":"https://vladmandic.github.io/human/models/antispoof.json"}] -2022-08-04 09:13:31 INFO:  test-node-wasm.js test: warmup -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: create human -2022-08-04 09:13:31 INFO:  test-node-wasm.js human version: 2.9.1 -2022-08-04 09:13:31 INFO:  test-node-wasm.js platform: linux x64 agent: NodeJS v16.15.0 -2022-08-04 09:13:31 INFO:  test-node-wasm.js tfjs version: 3.19.0 -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: set backend: wasm -2022-08-04 09:13:31 STATE: test-node-wasm.js tensors 1921 -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: load models -2022-08-04 09:13:31 STATE: test-node-wasm.js result: defined models: 22 loaded models: 12 -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: warmup: none default -2022-08-04 09:13:31 DATA:  test-node-wasm.js result: face: 0 body: 0 hand: 0 gesture: 0 object: 0 person: 0 {} {} {} -2022-08-04 09:13:31 DATA:  test-node-wasm.js result: performance: load: null total: null -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: warmup none result match -2022-08-04 09:13:31 STATE: test-node-wasm.js event: image -2022-08-04 09:13:31 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:31 STATE: test-node-wasm.js event: warmup -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: warmup: face default -2022-08-04 09:13:31 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} -2022-08-04 09:13:31 DATA:  test-node-wasm.js result: performance: load: null total: 595 -2022-08-04 09:13:31 STATE: test-node-wasm.js passed: warmup face result match -2022-08-04 09:13:32 STATE: test-node-wasm.js event: image -2022-08-04 09:13:32 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:32 STATE: test-node-wasm.js event: warmup -2022-08-04 09:13:32 STATE: test-node-wasm.js passed: warmup: body default -2022-08-04 09:13:32 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:32 DATA:  test-node-wasm.js result: performance: load: null total: 421 -2022-08-04 09:13:32 STATE: test-node-wasm.js passed: warmup body result match -2022-08-04 09:13:32 STATE: test-node-wasm.js details: {"face":{"boxScore":0.93,"faceScore":1,"age":23.7,"gender":"female","genderScore":0.97},"emotion":[{"score":0.59,"emotion":"angry"},{"score":0.29,"emotion":"fear"}],"body":{"score":0.92,"keypoints":17},"hand":{"boxScore":0.51,"fingerScore":0.73,"keypoints":21},"gestures":[{"face":0,"gesture":"facing right"},{"face":0,"gesture":"mouth 21% open"},{"hand":0,"gesture":"pinky forward"},{"hand":0,"gesture":"palm up"},{"hand":0,"gesture":"open palm"},{"iris":0,"gesture":"looking left"},{"iris":0,"gesture":"looking up"}]} -2022-08-04 09:13:32 INFO:  test-node-wasm.js test: details verification -2022-08-04 09:13:32 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:32 STATE: test-node-wasm.js event: image -2022-08-04 09:13:33 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:33 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:33 DATA:  test-node-wasm.js result: performance: load: null total: 399 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face length 1 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face score 1 0.93 1 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face age/gender 23.7 female 0.97 85.47 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face arrays 4 478 1024 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face emotion 2 {"score":0.59,"emotion":"angry"} -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face anti-spoofing 0.79 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details face liveness 0.83 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details body length 1 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details body 0.92 17 6 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details hand length 1 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details hand 0.51 0.73 point -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details hand arrays 21 5 7 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details gesture length 7 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details gesture first {"face":0,"gesture":"facing right"} -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details object length 1 -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: details object 0.72 person -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1413675264} -2022-08-04 09:13:33 STATE: test-node-wasm.js event: image -2022-08-04 09:13:33 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:33 STATE: test-node-wasm.js passed: tensor shape: [1,1200,1200,4] dtype: float32 -2022-08-04 09:13:34 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1200,1200,4] {"checksum":1413675264} -2022-08-04 09:13:34 STATE: test-node-wasm.js event: image -2022-08-04 09:13:34 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:34 STATE: test-node-wasm.js passed: tensor shape: [1200,1200,4] dtype: float32 -2022-08-04 09:13:34 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:34 STATE: test-node-wasm.js event: image -2022-08-04 09:13:35 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:35 STATE: test-node-wasm.js passed: tensor shape: [1,1200,1200,3] dtype: float32 -2022-08-04 09:13:35 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:35 STATE: test-node-wasm.js event: image -2022-08-04 09:13:36 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:36 STATE: test-node-wasm.js passed: tensor shape: [1200,1200,3] dtype: float32 -2022-08-04 09:13:36 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996871} -2022-08-04 09:13:36 STATE: test-node-wasm.js event: image -2022-08-04 09:13:36 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:36 STATE: test-node-wasm.js passed: tensor shape: [1,1200,1200,4] dtype: int32 -2022-08-04 09:13:36 INFO:  test-node-wasm.js test default -2022-08-04 09:13:37 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:37 STATE: test-node-wasm.js event: image -2022-08-04 09:13:37 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:37 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:37 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 8 object: 1 person: 1 {"score":1,"age":29.6,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:37 DATA:  test-node-wasm.js result: performance: load: null total: 471 -2022-08-04 09:13:37 STATE: test-node-wasm.js passed: default result face match 1 female 0.97 -2022-08-04 09:13:37 INFO:  test-node-wasm.js test sync -2022-08-04 09:13:37 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:37 STATE: test-node-wasm.js event: image -2022-08-04 09:13:38 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:38 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 8 object: 1 person: 1 {"score":1,"age":29.6,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:38 DATA:  test-node-wasm.js result: performance: load: null total: 394 -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: default sync 1 female 0.97 -2022-08-04 09:13:38 INFO:  test-node-wasm.js test: image process -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: image input null [1,256,256,3] -2022-08-04 09:13:38 INFO:  test-node-wasm.js test: image null -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: invalid input could not convert input to tensor -2022-08-04 09:13:38 INFO:  test-node-wasm.js test face similarity -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:38 STATE: test-node-wasm.js event: image -2022-08-04 09:13:38 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:38 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} -2022-08-04 09:13:38 DATA:  test-node-wasm.js result: performance: load: null total: 372 -2022-08-04 09:13:38 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:38 STATE: test-node-wasm.js event: image -2022-08-04 09:13:39 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:39 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 8 object: 1 person: 1 {"score":1,"age":29.6,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:39 DATA:  test-node-wasm.js result: performance: load: null total: 472 -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:39 STATE: test-node-wasm.js event: image -2022-08-04 09:13:39 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:39 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 4 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":7} -2022-08-04 09:13:39 DATA:  test-node-wasm.js result: performance: load: null total: 386 -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: face descriptor -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: face similarity {"similarity":[1,0.5266119940661309,0.4858842904087851],"descriptors":[1024,1024,1024]} -2022-08-04 09:13:39 INFO:  test-node-wasm.js test face matching -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: face database 40 -2022-08-04 09:13:39 STATE: test-node-wasm.js passed: face match {"first":{"index":4,"similarity":0.7827852754786533}} {"second":{"index":4,"similarity":0.5660821189104794}} {"third":{"index":4,"similarity":0.45074189882665594}} -2022-08-04 09:13:39 INFO:  test-node-wasm.js test object -2022-08-04 09:13:40 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:40 STATE: test-node-wasm.js event: image -2022-08-04 09:13:40 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:40 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:40 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 8 object: 1 person: 1 {"score":1,"age":29.6,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:40 DATA:  test-node-wasm.js result: performance: load: null total: 431 -2022-08-04 09:13:40 STATE: test-node-wasm.js passed: object result match -2022-08-04 09:13:40 INFO:  test-node-wasm.js test sensitive -2022-08-04 09:13:40 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:40 STATE: test-node-wasm.js event: image -2022-08-04 09:13:41 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:41 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 2 gesture: 10 object: 1 person: 1 {"score":1,"age":29.6,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:41 DATA:  test-node-wasm.js result: performance: load: null total: 442 -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: sensitive result match -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: sensitive face result match -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: sensitive face emotion result [{"score":0.46,"emotion":"neutral"},{"score":0.24,"emotion":"fear"},{"score":0.17,"emotion":"sad"}] -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: sensitive body result match -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: sensitive hand result match -2022-08-04 09:13:41 INFO:  test-node-wasm.js test detectors -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:41 STATE: test-node-wasm.js event: image -2022-08-04 09:13:41 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:41 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:41 DATA:  test-node-wasm.js result: performance: load: null total: 269 -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: detector result face match -2022-08-04 09:13:41 STATE: test-node-wasm.js passed: detector result hand match -2022-08-04 09:13:41 INFO:  test-node-wasm.js test: multi-instance -2022-08-04 09:13:41 STATE: test-node-wasm.js event: image -2022-08-04 09:13:42 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: detect: random default -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: face: 0 body: 1 hand: 0 gesture: 0 object: 0 person: 0 {} {} {"score":0.07,"keypoints":15} -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: performance: load: null total: 239 -2022-08-04 09:13:42 INFO:  test-node-wasm.js test: first instance -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: performance: load: null total: 280 -2022-08-04 09:13:42 INFO:  test-node-wasm.js test: second instance -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:42 DATA:  test-node-wasm.js result: performance: load: null total: 255 -2022-08-04 09:13:42 INFO:  test-node-wasm.js test: concurrent -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:42 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:43 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:43 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:43 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:43 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:43 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:44 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:44 STATE: test-node-wasm.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151155104} -2022-08-04 09:13:44 STATE: test-node-wasm.js event: image -2022-08-04 09:13:44 STATE: test-node-wasm.js event: image -2022-08-04 09:13:44 STATE: test-node-wasm.js event: image -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2455 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2455 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2721 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2721 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2721 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2721 -2022-08-04 09:13:46 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-upper.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 1 person: 1 {"score":0.96} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":16} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2334 -2022-08-04 09:13:46 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:46 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-face.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 2 object: 1 person: 1 {"score":0.91} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2334 -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: detect: samples/in/ai-body.jpg default -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 1 person: 1 {"score":0.93} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} -2022-08-04 09:13:46 DATA:  test-node-wasm.js result: performance: load: null total: 2334 -2022-08-04 09:13:46 INFO:  test-node-wasm.js test: monkey-patch -2022-08-04 09:13:46 STATE: test-node-wasm.js event: image -2022-08-04 09:13:46 STATE: test-node-wasm.js event: detect -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: monkey patch -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: segmentation [65536] -2022-08-04 09:13:46 STATE: test-node-wasm.js passeed: equal usage -2022-08-04 09:13:46 INFO:  test-node-wasm.js test: input compare -2022-08-04 09:13:46 STATE: test-node-wasm.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34697856} -2022-08-04 09:13:47 STATE: test-node-wasm.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1038921856} -2022-08-04 09:13:47 STATE: test-node-wasm.js passed: image compare 0 23.280073018790848 -2022-08-04 09:13:47 INFO:  test-node-wasm.js events: {"image":21,"detect":21,"warmup":2} -2022-08-04 09:13:47 INFO:  test-node-wasm.js tensors 1929 -2022-08-04 09:13:47 INFO:  test-node-wasm.js test complete: 17201 ms -2022-08-04 09:13:47 INFO:  all tests complete -2022-08-04 09:13:47 INFO:  failed: {"count":0,"messages":[]} -2022-08-04 09:13:47 INFO:  status: {"test":"test-node.js","passed":101,"failed":0} -2022-08-04 09:13:47 INFO:  status: {"test":"test-node-gpu.js","passed":101,"failed":0} -2022-08-04 09:13:47 INFO:  status: {"test":"test-node-wasm.js","passed":102,"failed":0} +2022-08-08 15:09:00 INFO:  @vladmandic/human version 2.9.2 +2022-08-08 15:09:00 INFO:  User: vlado Platform: linux Arch: x64 Node: v16.15.0 +2022-08-08 15:09:00 INFO:  tests: ["test-node.js"] +2022-08-08 15:09:00 INFO:  demos: ["../demo/nodejs/node.js","../demo/nodejs/node-canvas.js","../demo/nodejs/node-env.js","../demo/nodejs/node-event.js","../demo/nodejs/node-multiprocess.js"] +2022-08-08 15:09:00 INFO:  +2022-08-08 15:09:00 INFO:  test-node.js start +2022-08-08 15:09:00 INFO:  test-node.js test: configuration validation +2022-08-08 15:09:00 STATE: test-node.js passed: configuration default validation [] +2022-08-08 15:09:00 STATE: test-node.js passed: configuration invalid validation [{"reason":"unknown property","where":"config.invalid = true"}] +2022-08-08 15:09:00 INFO:  test-node.js test: model load +2022-08-08 15:09:00 STATE: test-node.js passed: models loaded 23 12 [{"name":"ssrnetage","loaded":false,"url":null},{"name":"gear","loaded":false,"url":null},{"name":"blazeposedetect","loaded":false,"url":null},{"name":"blazepose","loaded":false,"url":null},{"name":"centernet","loaded":true,"url":"file://models/mb3-centernet.json"},{"name":"efficientpose","loaded":false,"url":null},{"name":"mobilefacenet","loaded":false,"url":null},{"name":"insightface","loaded":false,"url":null},{"name":"emotion","loaded":true,"url":"file://models/emotion.json"},{"name":"facedetect","loaded":true,"url":"file://models/blazeface.json"},{"name":"faceiris","loaded":true,"url":"file://models/iris.json"},{"name":"facemesh","loaded":true,"url":"file://models/facemesh.json"},{"name":"faceres","loaded":true,"url":"file://models/faceres.json"},{"name":"ssrnetgender","loaded":false,"url":null},{"name":"handpose","loaded":false,"url":null},{"name":"handskeleton","loaded":true,"url":"file://models/handlandmark-full.json"},{"name":"handtrack","loaded":true,"url":"file://models/handtrack.json"},{"name":"liveness","loaded":true,"url":"file://models/liveness.json"},{"name":"movenet","loaded":true,"url":"file://models/movenet-lightning.json"},{"name":"nanodet","loaded":false,"url":null},{"name":"posenet","loaded":false,"url":null},{"name":"segmentation","loaded":true,"url":"file://models/selfie.json"},{"name":"antispoof","loaded":true,"url":"file://models/antispoof.json"}] +2022-08-08 15:09:00 INFO:  test-node.js test: warmup +2022-08-08 15:09:00 STATE: test-node.js passed: create human +2022-08-08 15:09:00 INFO:  test-node.js human version: 2.9.2 +2022-08-08 15:09:00 INFO:  test-node.js platform: linux x64 agent: NodeJS v16.15.0 +2022-08-08 15:09:00 INFO:  test-node.js tfjs version: 3.19.0 +2022-08-08 15:09:00 INFO:  test-node.js tensorflow binding version: 2.7.3-dev20220521 +2022-08-08 15:09:00 STATE: test-node.js passed: set backend: tensorflow +2022-08-08 15:09:00 STATE: test-node.js tensors 1921 +2022-08-08 15:09:00 STATE: test-node.js passed: load models +2022-08-08 15:09:00 STATE: test-node.js result: defined models: 23 loaded models: 12 +2022-08-08 15:09:00 STATE: test-node.js passed: warmup: none default +2022-08-08 15:09:00 DATA:  test-node.js result: face: 0 body: 0 hand: 0 gesture: 0 object: 0 person: 0 {} {} {} +2022-08-08 15:09:00 DATA:  test-node.js result: performance: load: null total: null +2022-08-08 15:09:00 STATE: test-node.js passed: warmup none result match +2022-08-08 15:09:00 STATE: test-node.js event: image +2022-08-08 15:09:00 STATE: test-node.js event: detect +2022-08-08 15:09:00 STATE: test-node.js event: warmup +2022-08-08 15:09:00 STATE: test-node.js passed: warmup: face default +2022-08-08 15:09:00 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.42,"keypoints":4} +2022-08-08 15:09:00 DATA:  test-node.js result: performance: load: null total: 361 +2022-08-08 15:09:00 STATE: test-node.js passed: warmup face result match +2022-08-08 15:09:00 STATE: test-node.js event: image +2022-08-08 15:09:01 STATE: test-node.js event: detect +2022-08-08 15:09:01 STATE: test-node.js event: warmup +2022-08-08 15:09:01 STATE: test-node.js passed: warmup: body default +2022-08-08 15:09:01 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:01 DATA:  test-node.js result: performance: load: null total: 270 +2022-08-08 15:09:01 STATE: test-node.js passed: warmup body result match +2022-08-08 15:09:01 STATE: test-node.js details: {"face":{"boxScore":0.92,"faceScore":1,"age":23.7,"gender":"female","genderScore":0.97},"emotion":[{"score":0.63,"emotion":"angry"},{"score":0.22,"emotion":"fear"}],"body":{"score":0.92,"keypoints":17},"hand":{"boxScore":0.52,"fingerScore":0.73,"keypoints":21},"gestures":[{"face":0,"gesture":"facing right"},{"face":0,"gesture":"mouth 10% open"},{"hand":0,"gesture":"pinky forward"},{"hand":0,"gesture":"palm up"},{"hand":0,"gesture":"open palm"},{"iris":0,"gesture":"looking left"},{"iris":0,"gesture":"looking up"}]} +2022-08-08 15:09:01 INFO:  test-node.js test: details verification +2022-08-08 15:09:01 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:01 STATE: test-node.js event: image +2022-08-08 15:09:01 STATE: test-node.js event: detect +2022-08-08 15:09:01 STATE: test-node.js passed: detect: samples/in/ai-body.jpg default +2022-08-08 15:09:01 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:01 DATA:  test-node.js result: performance: load: null total: 257 +2022-08-08 15:09:01 STATE: test-node.js passed: details face length 1 +2022-08-08 15:09:01 STATE: test-node.js passed: details face score 1 0.93 1 +2022-08-08 15:09:01 STATE: test-node.js passed: details face age/gender 23.7 female 0.97 85.47 +2022-08-08 15:09:01 STATE: test-node.js passed: details face arrays 4 478 1024 +2022-08-08 15:09:01 STATE: test-node.js passed: details face emotion 2 {"score":0.59,"emotion":"angry"} +2022-08-08 15:09:01 STATE: test-node.js passed: details face anti-spoofing 0.79 +2022-08-08 15:09:01 STATE: test-node.js passed: details face liveness 0.83 +2022-08-08 15:09:01 STATE: test-node.js passed: details body length 1 +2022-08-08 15:09:01 STATE: test-node.js passed: details body 0.92 17 6 +2022-08-08 15:09:01 STATE: test-node.js passed: details hand length 1 +2022-08-08 15:09:01 STATE: test-node.js passed: details hand 0.51 0.73 point +2022-08-08 15:09:01 STATE: test-node.js passed: details hand arrays 21 5 7 +2022-08-08 15:09:01 STATE: test-node.js passed: details gesture length 7 +2022-08-08 15:09:01 STATE: test-node.js passed: details gesture first {"face":0,"gesture":"facing right"} +2022-08-08 15:09:01 STATE: test-node.js passed: details object length 1 +2022-08-08 15:09:01 STATE: test-node.js passed: details object 0.72 person +2022-08-08 15:09:01 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996928} +2022-08-08 15:09:01 STATE: test-node.js event: image +2022-08-08 15:09:02 STATE: test-node.js event: detect +2022-08-08 15:09:02 STATE: test-node.js passed: tensor shape: [1,1200,1200,4] dtype: float32 +2022-08-08 15:09:02 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1200,1200,4] {"checksum":1371996928} +2022-08-08 15:09:02 STATE: test-node.js event: image +2022-08-08 15:09:02 STATE: test-node.js event: detect +2022-08-08 15:09:02 STATE: test-node.js passed: tensor shape: [1200,1200,4] dtype: float32 +2022-08-08 15:09:02 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:02 STATE: test-node.js event: image +2022-08-08 15:09:03 STATE: test-node.js event: detect +2022-08-08 15:09:03 STATE: test-node.js passed: tensor shape: [1,1200,1200,3] dtype: float32 +2022-08-08 15:09:03 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:03 STATE: test-node.js event: image +2022-08-08 15:09:03 STATE: test-node.js event: detect +2022-08-08 15:09:03 STATE: test-node.js passed: tensor shape: [1200,1200,3] dtype: float32 +2022-08-08 15:09:03 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,4] {"checksum":1371996871} +2022-08-08 15:09:03 STATE: test-node.js event: image +2022-08-08 15:09:03 STATE: test-node.js event: detect +2022-08-08 15:09:03 STATE: test-node.js passed: tensor shape: [1,1200,1200,4] dtype: int32 +2022-08-08 15:09:03 INFO:  test-node.js test default +2022-08-08 15:09:04 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:04 STATE: test-node.js event: image +2022-08-08 15:09:04 STATE: test-node.js event: detect +2022-08-08 15:09:04 STATE: test-node.js passed: detect: samples/in/ai-body.jpg async +2022-08-08 15:09:04 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:04 DATA:  test-node.js result: performance: load: null total: 248 +2022-08-08 15:09:04 STATE: test-node.js passed: default result face match 1 female 0.97 +2022-08-08 15:09:04 INFO:  test-node.js test sync +2022-08-08 15:09:04 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:04 STATE: test-node.js event: image +2022-08-08 15:09:04 STATE: test-node.js event: detect +2022-08-08 15:09:04 STATE: test-node.js passed: detect: samples/in/ai-body.jpg sync +2022-08-08 15:09:04 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:04 DATA:  test-node.js result: performance: load: null total: 219 +2022-08-08 15:09:04 STATE: test-node.js passed: default sync 1 female 0.97 +2022-08-08 15:09:04 INFO:  test-node.js test: image process +2022-08-08 15:09:04 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:04 STATE: test-node.js passed: image input null [1,256,256,3] +2022-08-08 15:09:04 INFO:  test-node.js test: image null +2022-08-08 15:09:04 STATE: test-node.js passed: invalid input could not convert input to tensor +2022-08-08 15:09:04 INFO:  test-node.js test face attention +2022-08-08 15:09:04 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:04 STATE: test-node.js event: image +2022-08-08 15:09:04 STATE: test-node.js event: detect +2022-08-08 15:09:04 STATE: test-node.js passed: detect: samples/in/ai-face.jpg face mesh +2022-08-08 15:09:04 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} +2022-08-08 15:09:04 DATA:  test-node.js result: performance: load: null total: 213 +2022-08-08 15:09:04 STATE: test-node.js passed: face attention +2022-08-08 15:09:04 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:04 STATE: test-node.js event: image +2022-08-08 15:09:05 STATE: test-node.js event: detect +2022-08-08 15:09:05 STATE: test-node.js passed: detect: samples/in/ai-face.jpg face attention +2022-08-08 15:09:05 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} +2022-08-08 15:09:05 DATA:  test-node.js result: performance: load: null total: 231 +2022-08-08 15:09:05 STATE: test-node.js passed: face attention +2022-08-08 15:09:05 INFO:  test-node.js test face similarity +2022-08-08 15:09:05 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:05 STATE: test-node.js event: image +2022-08-08 15:09:05 STATE: test-node.js event: detect +2022-08-08 15:09:05 STATE: test-node.js passed: detect: samples/in/ai-face.jpg face similarity +2022-08-08 15:09:05 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} +2022-08-08 15:09:05 DATA:  test-node.js result: performance: load: null total: 213 +2022-08-08 15:09:05 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:05 STATE: test-node.js event: image +2022-08-08 15:09:05 STATE: test-node.js event: detect +2022-08-08 15:09:05 STATE: test-node.js passed: detect: samples/in/ai-body.jpg face similarity +2022-08-08 15:09:05 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:05 DATA:  test-node.js result: performance: load: null total: 230 +2022-08-08 15:09:05 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:05 STATE: test-node.js event: image +2022-08-08 15:09:06 STATE: test-node.js event: detect +2022-08-08 15:09:06 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg face similarity +2022-08-08 15:09:06 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 5 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.71,"class":"person"} {"score":0.75,"keypoints":7} +2022-08-08 15:09:06 DATA:  test-node.js result: performance: load: null total: 206 +2022-08-08 15:09:06 STATE: test-node.js passed: face descriptor +2022-08-08 15:09:06 STATE: test-node.js passed: face similarity {"similarity":[1,0.44727452329649126,0.5567935850640406],"descriptors":[1024,1024,1024]} +2022-08-08 15:09:06 INFO:  test-node.js test face similarity alternative +2022-08-08 15:09:06 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:06 STATE: test-node.js event: image +2022-08-08 15:09:07 STATE: test-node.js event: detect +2022-08-08 15:09:07 STATE: test-node.js passed: detect: samples/in/ai-face.jpg face embeddings +2022-08-08 15:09:07 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} +2022-08-08 15:09:07 DATA:  test-node.js result: performance: load: null total: 247 +2022-08-08 15:09:07 STATE: test-node.js passed: mobilefacenet +2022-08-08 15:09:07 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:07 STATE: test-node.js event: image +2022-08-08 15:09:08 STATE: test-node.js event: detect +2022-08-08 15:09:08 STATE: test-node.js passed: detect: samples/in/ai-face.jpg face embeddings +2022-08-08 15:09:08 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 6 object: 1 person: 1 {"score":1,"age":23.5,"gender":"female"} {"score":0.82,"class":"person"} {"score":0.47,"keypoints":3} +2022-08-08 15:09:08 DATA:  test-node.js result: performance: load: null total: 263 +2022-08-08 15:09:08 STATE: test-node.js passed: insightface +2022-08-08 15:09:08 INFO:  test-node.js test face matching +2022-08-08 15:09:08 STATE: test-node.js passed: face database 40 +2022-08-08 15:09:08 STATE: test-node.js passed: face match {"first":{"index":4,"similarity":0.7827852615252829}} {"second":{"index":4,"similarity":0.5002052633015844}} {"third":{"index":4,"similarity":0.5401587887998899}} +2022-08-08 15:09:08 INFO:  test-node.js test object +2022-08-08 15:09:08 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:08 STATE: test-node.js event: image +2022-08-08 15:09:08 STATE: test-node.js event: detect +2022-08-08 15:09:08 STATE: test-node.js passed: detect: samples/in/ai-body.jpg object +2022-08-08 15:09:08 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 1 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.72,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:08 DATA:  test-node.js result: performance: load: null total: 268 +2022-08-08 15:09:08 STATE: test-node.js passed: centernet +2022-08-08 15:09:09 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:09 STATE: test-node.js event: image +2022-08-08 15:09:10 STATE: test-node.js event: detect +2022-08-08 15:09:10 STATE: test-node.js passed: detect: samples/in/ai-body.jpg object +2022-08-08 15:09:10 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 7 object: 3 person: 1 {"score":1,"age":23.7,"gender":"female"} {"score":0.86,"class":"person"} {"score":0.92,"keypoints":17} +2022-08-08 15:09:10 DATA:  test-node.js result: performance: load: null total: 263 +2022-08-08 15:09:10 STATE: test-node.js passed: nanodet +2022-08-08 15:09:10 INFO:  test-node.js test sensitive +2022-08-08 15:09:10 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:10 STATE: test-node.js event: image +2022-08-08 15:09:10 STATE: test-node.js event: detect +2022-08-08 15:09:10 STATE: test-node.js passed: detect: samples/in/ai-body.jpg sensitive +2022-08-08 15:09:10 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 0 person: 1 {"score":1,"age":23.7,"gender":"female"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:10 DATA:  test-node.js result: performance: load: null total: 205 +2022-08-08 15:09:10 STATE: test-node.js passed: sensitive result match +2022-08-08 15:09:10 STATE: test-node.js passed: sensitive face result match +2022-08-08 15:09:10 STATE: test-node.js passed: sensitive face emotion result [{"score":0.59,"emotion":"angry"},{"score":0.29,"emotion":"fear"}] +2022-08-08 15:09:10 STATE: test-node.js passed: sensitive body result match +2022-08-08 15:09:10 STATE: test-node.js passed: sensitive hand result match +2022-08-08 15:09:10 INFO:  test-node.js test body +2022-08-08 15:09:12 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:12 STATE: test-node.js event: image +2022-08-08 15:09:12 STATE: test-node.js event: detect +2022-08-08 15:09:12 STATE: test-node.js passed: detect: samples/in/ai-body.jpg blazepose +2022-08-08 15:09:12 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 0 person: 1 {"score":1,"age":23.7,"gender":"female"} {} {"score":0.99,"keypoints":39} +2022-08-08 15:09:12 DATA:  test-node.js result: performance: load: null total: 270 +2022-08-08 15:09:12 STATE: test-node.js passed: blazepose +2022-08-08 15:09:14 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:14 STATE: test-node.js event: image +2022-08-08 15:09:15 STATE: test-node.js event: detect +2022-08-08 15:09:15 STATE: test-node.js passed: detect: samples/in/ai-body.jpg efficientpose +2022-08-08 15:09:15 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 0 person: 1 {"score":1,"age":23.7,"gender":"female"} {} {"score":0.75,"keypoints":13} +2022-08-08 15:09:15 DATA:  test-node.js result: performance: load: null total: 281 +2022-08-08 15:09:15 STATE: test-node.js passed: efficientpose +2022-08-08 15:09:16 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:16 STATE: test-node.js event: image +2022-08-08 15:09:16 STATE: test-node.js event: detect +2022-08-08 15:09:16 STATE: test-node.js passed: detect: samples/in/ai-body.jpg posenet +2022-08-08 15:09:16 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 0 person: 1 {"score":1,"age":23.7,"gender":"female"} {} {"score":0.96,"keypoints":16} +2022-08-08 15:09:16 DATA:  test-node.js result: performance: load: null total: 209 +2022-08-08 15:09:16 STATE: test-node.js passed: posenet +2022-08-08 15:09:16 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:16 STATE: test-node.js event: image +2022-08-08 15:09:16 STATE: test-node.js event: detect +2022-08-08 15:09:16 STATE: test-node.js passed: detect: samples/in/ai-body.jpg movenet +2022-08-08 15:09:16 DATA:  test-node.js result: face: 1 body: 1 hand: 2 gesture: 9 object: 0 person: 1 {"score":1,"age":23.7,"gender":"female"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:16 DATA:  test-node.js result: performance: load: null total: 206 +2022-08-08 15:09:16 STATE: test-node.js passed: movenet +2022-08-08 15:09:16 INFO:  test-node.js test detectors +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:17 STATE: test-node.js event: image +2022-08-08 15:09:17 STATE: test-node.js event: detect +2022-08-08 15:09:17 STATE: test-node.js passed: detect: samples/in/ai-body.jpg detectors +2022-08-08 15:09:17 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.93,"gender":"unknown"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:17 DATA:  test-node.js result: performance: load: null total: 94 +2022-08-08 15:09:17 STATE: test-node.js passed: detector result face match +2022-08-08 15:09:17 STATE: test-node.js passed: detector result hand match +2022-08-08 15:09:17 INFO:  test-node.js test: multi-instance +2022-08-08 15:09:17 STATE: test-node.js event: image +2022-08-08 15:09:17 STATE: test-node.js event: detect +2022-08-08 15:09:17 STATE: test-node.js passed: detect: random multi instance +2022-08-08 15:09:17 DATA:  test-node.js result: face: 0 body: 1 hand: 0 gesture: 0 object: 0 person: 0 {} {} {"score":0,"keypoints":0} +2022-08-08 15:09:17 DATA:  test-node.js result: performance: load: null total: 86 +2022-08-08 15:09:17 INFO:  test-node.js test: first instance +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:17 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg multi instance +2022-08-08 15:09:17 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 0 person: 1 {"score":0.96,"gender":"unknown"} {} {"score":0.75,"keypoints":7} +2022-08-08 15:09:17 DATA:  test-node.js result: performance: load: null total: 88 +2022-08-08 15:09:17 INFO:  test-node.js test: second instance +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:17 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg multi instance +2022-08-08 15:09:17 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 0 person: 1 {"score":0.96,"gender":"unknown"} {} {"score":0.75,"keypoints":7} +2022-08-08 15:09:17 DATA:  test-node.js result: performance: load: null total: 85 +2022-08-08 15:09:17 INFO:  test-node.js test: concurrent +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:17 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:18 STATE: test-node.js passed: load image: samples/in/ai-upper.jpg [1,720,688,3] {"checksum":151289024} +2022-08-08 15:09:18 STATE: test-node.js event: image +2022-08-08 15:09:18 STATE: test-node.js event: image +2022-08-08 15:09:18 STATE: test-node.js event: image +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 0 person: 1 {"score":0.96,"gender":"unknown"} {} {"score":0.75,"keypoints":7} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 0 person: 1 {"score":0.96,"gender":"unknown"} {} {"score":0.75,"keypoints":7} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-face.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.91,"gender":"unknown"} {} {"score":0.47,"keypoints":3} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-face.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.91,"gender":"unknown"} {} {"score":0.47,"keypoints":3} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-body.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.93,"gender":"unknown"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-body.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.93,"gender":"unknown"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 936 +2022-08-08 15:09:19 STATE: test-node.js event: detect +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-upper.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 0 gesture: 0 object: 0 person: 1 {"score":0.96,"gender":"unknown"} {} {"score":0.75,"keypoints":7} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 742 +2022-08-08 15:09:19 STATE: test-node.js event: detect +2022-08-08 15:09:19 STATE: test-node.js event: detect +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-face.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.91,"gender":"unknown"} {} {"score":0.47,"keypoints":3} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 742 +2022-08-08 15:09:19 STATE: test-node.js passed: detect: samples/in/ai-body.jpg concurrent +2022-08-08 15:09:19 DATA:  test-node.js result: face: 1 body: 1 hand: 1 gesture: 0 object: 0 person: 1 {"score":0.93,"gender":"unknown"} {} {"score":0.92,"keypoints":17} +2022-08-08 15:09:19 DATA:  test-node.js result: performance: load: null total: 742 +2022-08-08 15:09:19 INFO:  test-node.js test: monkey-patch +2022-08-08 15:09:19 STATE: test-node.js event: image +2022-08-08 15:09:19 STATE: test-node.js event: detect +2022-08-08 15:09:19 STATE: test-node.js passed: monkey patch +2022-08-08 15:09:19 STATE: test-node.js passed: segmentation [65536] +2022-08-08 15:09:19 STATE: test-node.js passeed: equal usage +2022-08-08 15:09:19 INFO:  test-node.js test: input compare +2022-08-08 15:09:19 STATE: test-node.js passed: load image: samples/in/ai-face.jpg [1,256,256,3] {"checksum":34696120} +2022-08-08 15:09:19 STATE: test-node.js passed: load image: samples/in/ai-body.jpg [1,1200,1200,3] {"checksum":1004796864} +2022-08-08 15:09:19 STATE: test-node.js passed: image compare 0 23.275441687091504 +2022-08-08 15:09:19 INFO:  test-node.js events: {"image":30,"detect":30,"warmup":2} +2022-08-08 15:09:19 INFO:  test-node.js tensors 4105 +2022-08-08 15:09:19 INFO:  test-node.js test complete: 19131 ms +2022-08-08 15:09:19 INFO:  all tests complete +2022-08-08 15:09:19 INFO:  failed: {"count":0,"messages":[]} +2022-08-08 15:09:19 INFO:  status: {"test":"test-node.js","passed":128,"failed":0}