diff --git a/.eslintrc.json b/.eslintrc.json
index d680eeee..7815c0a2 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -158,6 +158,7 @@
"dist",
"demo/helpers/*.js",
"demo/typescript/*.js",
- "demo/faceid/*.js"
+ "demo/faceid/*.js",
+ "typedoc"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ef16290..525191a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,10 @@
## Changelog
-### **HEAD -> main** 2022/08/24 mandic00@live.com
+### **HEAD -> main** 2022/08/30 mandic00@live.com
+
+
+### **origin/main** 2022/08/28 mandic00@live.com
- expand type safety
- full eslint rule rewrite
diff --git a/README.md b/README.md
index e0b0362d..aa903286 100644
--- a/README.md
+++ b/README.md
@@ -350,7 +350,7 @@ For more info, see [**Configuration Details**](https://github.com/vladmandic/hum
-`Human` library is written in `TypeScript` [4.7](https://www.typescriptlang.org/docs/handbook/intro.html)
+`Human` library is written in `TypeScript` [4.8](https://www.typescriptlang.org/docs/handbook/intro.html)
Conforming to latest `JavaScript` [ECMAScript version 2022](https://262.ecma-international.org/) standard
Build target is `JavaScript` [EMCAScript version 2018](https://262.ecma-international.org/11.0/)
diff --git a/demo/faceid/index.js b/demo/faceid/index.js
index a147c3ca..35441db1 100644
--- a/demo/faceid/index.js
+++ b/demo/faceid/index.js
@@ -4,328 +4,6 @@
author: '
*/
-
-// demo/faceid/index.ts
-import * as H from "../../dist/human.esm.js";
-
-// demo/faceid/indexdb.ts
-var db;
-var database = "human";
-var table = "person";
-var log = (...msg) => console.log("indexdb", ...msg);
-async function open() {
- if (db)
- return true;
- return new Promise((resolve) => {
- const request = indexedDB.open(database, 1);
- request.onerror = (evt) => log("error:", evt);
- request.onupgradeneeded = (evt) => {
- log("create:", evt.target);
- db = evt.target.result;
- db.createObjectStore(table, { keyPath: "id", autoIncrement: true });
- };
- request.onsuccess = (evt) => {
- db = evt.target.result;
- log("open:", db);
- resolve(true);
- };
- });
-}
-async function load() {
- const faceDB = [];
- if (!db)
- await open();
- return new Promise((resolve) => {
- const cursor = db.transaction([table], "readwrite").objectStore(table).openCursor(null, "next");
- cursor.onerror = (evt) => log("load error:", evt);
- cursor.onsuccess = (evt) => {
- if (evt.target.result) {
- faceDB.push(evt.target.result.value);
- evt.target.result.continue();
- } else {
- resolve(faceDB);
- }
- };
- });
-}
-async function count() {
- if (!db)
- await open();
- return new Promise((resolve) => {
- const store = db.transaction([table], "readwrite").objectStore(table).count();
- store.onerror = (evt) => log("count error:", evt);
- store.onsuccess = () => resolve(store.result);
- });
-}
-async function save(faceRecord) {
- if (!db)
- await open();
- const newRecord = { name: faceRecord.name, descriptor: faceRecord.descriptor, image: faceRecord.image };
- db.transaction([table], "readwrite").objectStore(table).put(newRecord);
- log("save:", newRecord);
-}
-async function remove(faceRecord) {
- if (!db)
- await open();
- db.transaction([table], "readwrite").objectStore(table).delete(faceRecord.id);
- log("delete:", faceRecord);
-}
-
-// demo/faceid/index.ts
-var humanConfig = {
- modelBasePath: "../../models",
- filter: { equalization: true },
- face: {
- enabled: true,
- detector: { rotation: true, return: true, cropFactor: 1.6, mask: false },
- description: { enabled: true },
- iris: { enabled: true },
- emotion: { enabled: false },
- antispoof: { enabled: true },
- liveness: { enabled: true }
- },
- body: { enabled: false },
- hand: { enabled: false },
- object: { enabled: false },
- gesture: { enabled: true }
-};
-var matchOptions = { order: 2, multiplier: 25, min: 0.2, max: 0.8 };
-var options = {
- minConfidence: 0.6,
- minSize: 224,
- maxTime: 1e4,
- blinkMin: 10,
- blinkMax: 800,
- threshold: 0.5,
- mask: humanConfig.face.detector.mask,
- rotation: humanConfig.face.detector.rotation,
- cropFactor: humanConfig.face.detector.cropFactor,
- ...matchOptions
-};
-var ok = {
- faceCount: false,
- faceConfidence: false,
- facingCenter: false,
- lookingCenter: false,
- blinkDetected: false,
- faceSize: false,
- antispoofCheck: false,
- livenessCheck: false,
- elapsedMs: 0
-};
-var allOk = () => ok.faceCount && ok.faceSize && ok.blinkDetected && ok.facingCenter && ok.lookingCenter && ok.faceConfidence && ok.antispoofCheck && ok.livenessCheck;
-var current = { face: null, record: null };
-var blink = {
- start: 0,
- end: 0,
- time: 0
-};
-var human = new H.Human(humanConfig);
-human.env.perfadd = false;
-human.draw.options.font = 'small-caps 18px "Lato"';
-human.draw.options.lineHeight = 20;
-var dom = {
- 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")
-};
-var timestamp = { detect: 0, draw: 0 };
-var fps = { detect: 0, draw: 0 };
-var startTime = 0;
-var log2 = (...msg) => {
- dom.log.innerText += msg.join(" ") + "\n";
- console.log(...msg);
-};
-var printFPS = (msg) => dom.fps.innerText = msg;
-async function webCam() {
- printFPS("starting webcam...");
- const cameraOptions = { audio: false, video: { facingMode: "user", resizeMode: "none", width: { ideal: document.body.clientWidth } } };
- const stream = await navigator.mediaDevices.getUserMedia(cameraOptions);
- const ready = new Promise((resolve) => {
- dom.video.onloadeddata = () => resolve(true);
- });
- dom.video.srcObject = stream;
- void dom.video.play();
- await ready;
- dom.canvas.width = dom.video.videoWidth;
- dom.canvas.height = dom.video.videoHeight;
- if (human.env.initial)
- log2("video:", dom.video.videoWidth, dom.video.videoHeight, "|", stream.getVideoTracks()[0].label);
- dom.canvas.onclick = () => {
- if (dom.video.paused)
- void dom.video.play();
- else
- dom.video.pause();
- };
-}
-async function detectionLoop() {
- var _a;
- if (!dom.video.paused) {
- if ((_a = current.face) == null ? void 0 : _a.tensor)
- human.tf.dispose(current.face.tensor);
- await human.detect(dom.video);
- const now = human.now();
- fps.detect = 1e3 / (now - timestamp.detect);
- timestamp.detect = now;
- requestAnimationFrame(detectionLoop);
- }
-}
-async function validationLoop() {
- const interpolated = human.next(human.result);
- human.draw.canvas(dom.video, dom.canvas);
- await human.draw.all(dom.canvas, interpolated);
- const now = human.now();
- fps.draw = 1e3 / (now - timestamp.draw);
- timestamp.draw = now;
- printFPS(`fps: ${fps.detect.toFixed(1).padStart(5, " ")} detect | ${fps.draw.toFixed(1).padStart(5, " ")} draw`);
- ok.faceCount = human.result.face.length === 1;
- if (ok.faceCount) {
- const gestures = Object.values(human.result.gesture).map((gesture) => gesture.gesture);
- if (gestures.includes("blink left eye") || gestures.includes("blink right eye"))
- blink.start = human.now();
- if (blink.start > 0 && !gestures.includes("blink left eye") && !gestures.includes("blink right eye"))
- blink.end = human.now();
- ok.blinkDetected = ok.blinkDetected || Math.abs(blink.end - blink.start) > options.blinkMin && Math.abs(blink.end - blink.start) < options.blinkMax;
- 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");
- 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;
- }
- let y = 32;
- for (const [key, val] of Object.entries(ok)) {
- let el = document.getElementById(`ok-${key}`);
- if (!el) {
- el = document.createElement("div");
- el.innerText = key;
- el.className = "ok";
- el.style.top = `${y}px`;
- dom.ok.appendChild(el);
- }
- if (typeof val === "boolean")
- el.style.backgroundColor = val ? "lightgreen" : "lightcoral";
- else
- el.innerText = `${key}:${val}`;
- y += 28;
- }
- if (allOk()) {
- dom.video.pause();
- return human.result.face[0];
- }
- if (ok.elapsedMs > options.maxTime) {
- dom.video.pause();
- return human.result.face[0];
- }
- ok.elapsedMs = Math.trunc(human.now() - startTime);
- return new Promise((resolve) => {
- setTimeout(async () => {
- await validationLoop();
- resolve(human.result.face[0]);
- }, 30);
- });
-}
-async function saveRecords() {
- var _a, _b, _c, _d;
- if (dom.name.value.length > 0) {
- const image = (_a = dom.canvas.getContext("2d")) == null ? void 0 : _a.getImageData(0, 0, dom.canvas.width, dom.canvas.height);
- const rec = { id: 0, name: dom.name.value, descriptor: (_b = current.face) == null ? void 0 : _b.embedding, image };
- await save(rec);
- log2("saved face record:", rec.name, "descriptor length:", (_d = (_c = current.face) == null ? void 0 : _c.embedding) == null ? void 0 : _d.length);
- log2("known face records:", await count());
- } else {
- log2("invalid name");
- }
-}
-async function deleteRecord() {
- if (current.record && current.record.id > 0) {
- await remove(current.record);
- }
-}
-async function detectFace() {
- var _a, _b, _c, _d;
- (_a = dom.canvas.getContext("2d")) == null ? void 0 : _a.clearRect(0, 0, options.minSize, options.minSize);
- if (!((_b = current == null ? void 0 : current.face) == null ? void 0 : _b.tensor) || !((_c = current == null ? void 0 : current.face) == null ? void 0 : _c.embedding))
- return false;
- console.log("face record:", current.face);
- human.tf.browser.toPixels(current.face.tensor, dom.canvas);
- if (await count() === 0) {
- log2("face database is empty");
- document.body.style.background = "black";
- dom.delete.style.display = "none";
- return false;
- }
- const db2 = await load();
- const descriptors = db2.map((rec) => rec.descriptor).filter((desc) => desc.length > 0);
- const res = human.match(current.face.embedding, descriptors, matchOptions);
- current.record = db2[res.index] || null;
- if (current.record) {
- log2(`best match: ${current.record.name} | id: ${current.record.id} | similarity: ${Math.round(1e3 * res.similarity) / 10}%`);
- dom.name.value = current.record.name;
- dom.source.style.display = "";
- (_d = dom.source.getContext("2d")) == null ? void 0 : _d.putImageData(current.record.image, 0, 0);
- }
- document.body.style.background = res.similarity > options.threshold ? "darkgreen" : "maroon";
- return res.similarity > options.threshold;
-}
-async function main() {
- var _a, _b;
- ok.faceCount = false;
- ok.faceConfidence = false;
- ok.facingCenter = false;
- ok.blinkDetected = false;
- ok.faceSize = false;
- ok.antispoofCheck = false;
- ok.livenessCheck = false;
- ok.elapsedMs = 0;
- dom.match.style.display = "none";
- dom.retry.style.display = "none";
- dom.source.style.display = "none";
- document.body.style.background = "black";
- await webCam();
- await detectionLoop();
- startTime = human.now();
- current.face = await validationLoop();
- dom.canvas.width = ((_a = current.face.tensor) == null ? void 0 : _a.shape[1]) || options.minSize;
- dom.canvas.height = ((_b = current.face.tensor) == null ? void 0 : _b.shape[0]) || options.minSize;
- dom.source.width = dom.canvas.width;
- dom.source.height = dom.canvas.height;
- dom.canvas.style.width = "";
- dom.match.style.display = "flex";
- dom.save.style.display = "flex";
- dom.delete.style.display = "flex";
- dom.retry.style.display = "block";
- if (!allOk()) {
- log2("did not find valid face");
- return false;
- }
- return detectFace();
-}
-async function init() {
- var _a, _b;
- log2("human version:", human.version, "| tfjs version:", human.tf.version["tfjs-core"]);
- log2("face embedding model:", humanConfig.face.description.enabled ? "faceres" : "", ((_a = humanConfig.face["mobilefacenet"]) == null ? void 0 : _a.enabled) ? "mobilefacenet" : "", ((_b = humanConfig.face["insightface"]) == null ? void 0 : _b.enabled) ? "insightface" : "");
- log2("options:", JSON.stringify(options).replace(/{|}|"|\[|\]/g, "").replace(/,/g, " "));
- printFPS("loading...");
- log2("known face records:", await count());
- await webCam();
- await human.load();
- printFPS("initializing...");
- dom.retry.addEventListener("click", main);
- dom.save.addEventListener("click", saveRecords);
- dom.delete.addEventListener("click", deleteRecord);
- await human.warmup();
- await main();
-}
-window.onload = init;
+import*as I from"../../dist/human.esm.js";var l,z="human",g="person",p=(...t)=>console.log("indexdb",...t);async function v(){return l?!0:new Promise(t=>{let n=indexedDB.open(z,1);n.onerror=r=>p("error:",r),n.onupgradeneeded=r=>{p("create:",r.target),l=r.target.result,l.createObjectStore(g,{keyPath:"id",autoIncrement:!0})},n.onsuccess=r=>{l=r.target.result,p("open:",l),t(!0)}})}async function D(){let t=[];return l||await v(),new Promise(n=>{let r=l.transaction([g],"readwrite").objectStore(g).openCursor(null,"next");r.onerror=i=>p("load error:",i),r.onsuccess=i=>{i.target.result?(t.push(i.target.result.value),i.target.result.continue()):n(t)}})}async function y(){return l||await v(),new Promise(t=>{let n=l.transaction([g],"readwrite").objectStore(g).count();n.onerror=r=>p("count error:",r),n.onsuccess=()=>t(n.result)})}async function M(t){l||await v();let n={name:t.name,descriptor:t.descriptor,image:t.image};l.transaction([g],"readwrite").objectStore(g).put(n),p("save:",n)}async function B(t){l||await v(),l.transaction([g],"readwrite").objectStore(g).delete(t.id),p("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}},S={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,...S},o={faceCount:!1,faceConfidence:!1,facingCenter:!1,lookingCenter:!1,blinkDetected:!1,faceSize:!1,antispoofCheck:!1,livenessCheck:!1,elapsedMs:0},H=()=>o.faceCount&&o.faceSize&&o.blinkDetected&&o.facingCenter&&o.lookingCenter&&o.faceConfidence&&o.antispoofCheck&&o.livenessCheck,s={face:null,record:null},f={start:0,end:0,time:0},a=new I.Human(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},T=0,u=(...t)=>{e.log.innerText+=t.join(" ")+`
+`,console.log(...t)},k=t=>e.fps.innerText=t;async function L(){k("starting webcam...");let t={audio:!1,video:{facingMode:"user",resizeMode:"none",width:{ideal:document.body.clientWidth}}},n=await navigator.mediaDevices.getUserMedia(t),r=new Promise(i=>{e.video.onloadeddata=()=>i(!0)});e.video.srcObject=n,e.video.play(),await r,e.canvas.width=e.video.videoWidth,e.canvas.height=e.video.videoHeight,a.env.initial&&u("video:",e.video.videoWidth,e.video.videoHeight,"|",n.getVideoTracks()[0].label),e.canvas.onclick=()=>{e.video.paused?e.video.play():e.video.pause()}}async function R(){var t;if(!e.video.paused){(t=s.face)!=null&&t.tensor&&a.tf.dispose(s.face.tensor),await a.detect(e.video);let n=a.now();h.detect=1e3/(n-w.detect),w.detect=n,requestAnimationFrame(R)}}async function F(){let t=a.next(a.result);a.draw.canvas(e.video,e.canvas),await a.draw.all(e.canvas,t);let n=a.now();if(h.draw=1e3/(n-w.draw),w.draw=n,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 i=Object.values(a.result.gesture).map(m=>m.gesture);(i.includes("blink left eye")||i.includes("blink right eye"))&&(f.start=a.now()),f.start>0&&!i.includes("blink left eye")&&!i.includes("blink right eye")&&(f.end=a.now()),o.blinkDetected=o.blinkDetected||Math.abs(f.end-f.start)>c.blinkMin&&Math.abs(f.end-f.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 r=32;for(let[i,m]of Object.entries(o)){let d=document.getElementById(`ok-${i}`);d||(d=document.createElement("div"),d.innerText=i,d.className="ok",d.style.top=`${r}px`,e.ok.appendChild(d)),typeof m=="boolean"?d.style.backgroundColor=m?"lightgreen":"lightcoral":d.innerText=`${i}:${m}`,r+=28}return H()||o.elapsedMs>c.maxTime?(e.video.pause(),a.result.face[0]):(o.elapsedMs=Math.trunc(a.now()-T),new Promise(i=>{setTimeout(async()=>{await F(),i(a.result.face[0])},30)}))}async function j(){var t,n,r,i;if(e.name.value.length>0){let m=(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:(n=s.face)==null?void 0:n.embedding,image:m};await M(d),u("saved face record:",d.name,"descriptor length:",(i=(r=s.face)==null?void 0:r.embedding)==null?void 0:i.length),u("known face records:",await y())}else u("invalid name")}async function q(){s.record&&s.record.id>0&&await B(s.record)}async function O(){var i,m,d,C;if((i=e.canvas.getContext("2d"))==null||i.clearRect(0,0,c.minSize,c.minSize),!((m=s==null?void 0:s.face)!=null&&m.tensor)||!((d=s==null?void 0:s.face)!=null&&d.embedding))return!1;if(console.log("face record:",s.face),a.tf.browser.toPixels(s.face.tensor,e.canvas),await y()===0)return u("face database is empty"),document.body.style.background="black",e.delete.style.display="none",!1;let t=await D(),n=t.map(x=>x.descriptor).filter(x=>x.length>0),r=a.match(s.face.embedding,n,S);return s.record=t[r.index]||null,s.record&&(u(`best match: ${s.record.name} | id: ${s.record.id} | similarity: ${Math.round(1e3*r.similarity)/10}%`),e.name.value=s.record.name,e.source.style.display="",(C=e.source.getContext("2d"))==null||C.putImageData(s.record.image,0,0)),document.body.style.background=r.similarity>c.threshold?"darkgreen":"maroon",r.similarity>c.threshold}async function E(){var t,n;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 L(),await R(),T=a.now(),s.face=await F(),e.canvas.width=((t=s.face.tensor)==null?void 0:t.shape[1])||c.minSize,e.canvas.height=((n=s.face.tensor)==null?void 0:n.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",H()?O():(u("did not find valid face"),!1)}async function $(){var t,n;u("human version:",a.version,"| tfjs version:",a.tf.version["tfjs-core"]),u("face embedding model:",b.face.description.enabled?"faceres":"",(t=b.face.mobilefacenet)!=null&&t.enabled?"mobilefacenet":"",(n=b.face.insightface)!=null&&n.enabled?"insightface":""),u("options:",JSON.stringify(c).replace(/{|}|"|\[|\]/g,"").replace(/,/g," ")),k("loading..."),u("known face records:",await y()),await L(),await a.load(),k("initializing..."),e.retry.addEventListener("click",E),e.save.addEventListener("click",j),e.delete.addEventListener("click",q),await a.warmup(),await E()}window.onload=$;
//# sourceMappingURL=index.js.map
diff --git a/demo/faceid/index.js.map b/demo/faceid/index.js.map
index 54e89e4a..d9e1c020 100644
--- a/demo/faceid/index.js.map
+++ b/demo/faceid/index.js.map
@@ -2,6 +2,6 @@
"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 * as H 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: H.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 H.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 console.log(...msg); // eslint-disable-line no-console\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 void 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) void 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?.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 = human.next(human.result); // smoothen result using last-known results\n 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: H.GestureResult) => 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 }\n // run again\n ok.elapsedMs = Math.trunc(human.now() - startTime);\n return new Promise((resolve) => {\n setTimeout(async () => {\n await validationLoop(); // run validation loop until conditions are met\n 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\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?.tensor || !current?.face?.embedding) return false;\n console.log('face record:', current.face); // eslint-disable-line no-console\n human.tf.browser.toPixels(current.face.tensor as unknown as H.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 = 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 }\n return detectFace();\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 interface FaceRecord { id: number, name: string, descriptor: number[], image: ImageData }\n\nconst log = (...msg) => console.log('indexdb', ...msg); // eslint-disable-line no-console\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;\n log('open:', db);\n resolve(true);\n };\n });\n}\n\nexport async function load(): Promise {\n const faceDB: FaceRecord[] = [];\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": "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",
- "names": ["log", "db"]
+ "mappings": "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",
+ "names": ["H", "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", "dom", "timestamp", "fps", "startTime", "log", "msg", "printFPS", "webCam", "cameraOptions", "stream", "ready", "resolve", "detectionLoop", "_a", "now", "validationLoop", "interpolated", "gestures", "gesture", "y", "key", "val", "el", "saveRecords", "_b", "_c", "_d", "image", "rec", "save", "count", "deleteRecord", "remove", "detectFace", "db", "load", "descriptors", "desc", "res", "main", "init"]
}
diff --git a/demo/typescript/index.js b/demo/typescript/index.js
index 01fd8c38..1fd213b1 100644
--- a/demo/typescript/index.js
+++ b/demo/typescript/index.js
@@ -4,109 +4,6 @@
author: '
*/
-
-// demo/typescript/index.ts
-import * as H from "../../dist/human.esm.js";
-var humanConfig = {
- async: false,
- modelBasePath: "../../models",
- filter: { enabled: true, equalization: false, flip: false },
- face: { enabled: true, detector: { rotation: false }, mesh: { enabled: true }, attention: { enabled: false }, iris: { enabled: true }, description: { enabled: true }, emotion: { enabled: true } },
- body: { enabled: true },
- hand: { enabled: true },
- object: { enabled: false },
- gesture: { enabled: true }
-};
-var human = new H.Human(humanConfig);
-human.env.perfadd = false;
-human.draw.options.font = 'small-caps 18px "Lato"';
-human.draw.options.lineHeight = 20;
-var dom = {
- video: document.getElementById("video"),
- canvas: document.getElementById("canvas"),
- log: document.getElementById("log"),
- fps: document.getElementById("status"),
- perf: document.getElementById("performance")
-};
-var timestamp = { detect: 0, draw: 0, tensors: 0, start: 0 };
-var fps = { detectFPS: 0, drawFPS: 0, frames: 0, averageMs: 0 };
-var log = (...msg) => {
- dom.log.innerText += msg.join(" ") + "\n";
- console.log(...msg);
-};
-var status = (msg) => dom.fps.innerText = msg;
-var perf = (msg) => dom.perf.innerText = "tensors:" + human.tf.memory().numTensors.toString() + " | performance: " + JSON.stringify(msg).replace(/"|{|}/g, "").replace(/,/g, " | ");
-async function webCam() {
- status("starting webcam...");
- const options = { audio: false, video: { facingMode: "user", resizeMode: "none", width: { ideal: document.body.clientWidth }, height: { ideal: document.body.clientHeight } } };
- const stream = await navigator.mediaDevices.getUserMedia(options);
- const ready = new Promise((resolve) => {
- dom.video.onloadeddata = () => resolve(true);
- });
- dom.video.srcObject = stream;
- void dom.video.play();
- await ready;
- dom.canvas.width = dom.video.videoWidth;
- dom.canvas.height = dom.video.videoHeight;
- const track = stream.getVideoTracks()[0];
- const capabilities = track.getCapabilities ? track.getCapabilities() : "";
- const settings = track.getSettings ? track.getSettings() : "";
- const constraints = track.getConstraints ? track.getConstraints() : "";
- log("video:", dom.video.videoWidth, dom.video.videoHeight, track.label, { stream, track, settings, constraints, capabilities });
- dom.canvas.onclick = () => {
- if (dom.video.paused)
- void dom.video.play();
- else
- dom.video.pause();
- };
-}
-async function detectionLoop() {
- if (!dom.video.paused) {
- if (timestamp.start === 0)
- timestamp.start = human.now();
- await human.detect(dom.video);
- const tensors = human.tf.memory().numTensors;
- if (tensors - timestamp.tensors !== 0)
- log("allocated tensors:", tensors - timestamp.tensors);
- timestamp.tensors = tensors;
- fps.detectFPS = Math.round(1e3 * 1e3 / (human.now() - timestamp.detect)) / 1e3;
- fps.frames++;
- fps.averageMs = Math.round(1e3 * (human.now() - timestamp.start) / fps.frames) / 1e3;
- if (fps.frames % 100 === 0 && !dom.video.paused)
- log("performance", { ...fps, tensors: timestamp.tensors });
- }
- timestamp.detect = human.now();
- requestAnimationFrame(detectionLoop);
-}
-async function drawLoop() {
- if (!dom.video.paused) {
- const interpolated = human.next(human.result);
- if (human.config.filter.flip)
- human.draw.canvas(interpolated.canvas, dom.canvas);
- else
- human.draw.canvas(dom.video, dom.canvas);
- await human.draw.all(dom.canvas, interpolated);
- perf(interpolated.performance);
- }
- const now = human.now();
- fps.drawFPS = Math.round(1e3 * 1e3 / (now - timestamp.draw)) / 1e3;
- timestamp.draw = now;
- status(dom.video.paused ? "paused" : `fps: ${fps.detectFPS.toFixed(1).padStart(5, " ")} detect | ${fps.drawFPS.toFixed(1).padStart(5, " ")} draw`);
- setTimeout(drawLoop, 30);
-}
-async function main() {
- log("human version:", human.version, "| tfjs version:", human.tf.version["tfjs-core"]);
- log("platform:", human.env.platform, "| agent:", human.env.agent);
- status("loading...");
- await human.load();
- log("backend:", human.tf.getBackend(), "| available:", human.env.backends);
- log("models stats:", human.getModelStats());
- log("models loaded:", Object.values(human.models).filter((model) => model !== null).length);
- status("initializing...");
- await human.warmup();
- await webCam();
- await detectionLoop();
- await drawLoop();
-}
-window.onload = main;
+import*as c from"../../dist/human.esm.js";var w={async:!1,modelBasePath:"../../models",filter:{enabled:!0,equalization:!1,flip:!1},face:{enabled:!0,detector:{rotation:!1},mesh:{enabled:!0},attention:{enabled:!1},iris:{enabled:!0},description:{enabled:!0},emotion:{enabled:!0}},body:{enabled:!0},hand:{enabled:!0},object:{enabled:!1},gesture:{enabled:!0}},e=new c.Human(w);e.env.perfadd=!1;e.draw.options.font='small-caps 18px "Lato"';e.draw.options.lineHeight=20;var t={video:document.getElementById("video"),canvas:document.getElementById("canvas"),log:document.getElementById("log"),fps:document.getElementById("status"),perf:document.getElementById("performance")},n={detect:0,draw:0,tensors:0,start:0},o={detectFPS:0,drawFPS:0,frames:0,averageMs:0},i=(...a)=>{t.log.innerText+=a.join(" ")+`
+`,console.log(...a)},r=a=>t.fps.innerText=a,b=a=>t.perf.innerText="tensors:"+e.tf.memory().numTensors.toString()+" | performance: "+JSON.stringify(a).replace(/"|{|}/g,"").replace(/,/g," | ");async function h(){r("starting webcam...");let a={audio:!1,video:{facingMode:"user",resizeMode:"none",width:{ideal:document.body.clientWidth},height:{ideal:document.body.clientHeight}}},d=await navigator.mediaDevices.getUserMedia(a),f=new Promise(p=>{t.video.onloadeddata=()=>p(!0)});t.video.srcObject=d,t.video.play(),await f,t.canvas.width=t.video.videoWidth,t.canvas.height=t.video.videoHeight;let s=d.getVideoTracks()[0],v=s.getCapabilities?s.getCapabilities():"",g=s.getSettings?s.getSettings():"",u=s.getConstraints?s.getConstraints():"";i("video:",t.video.videoWidth,t.video.videoHeight,s.label,{stream:d,track:s,settings:g,constraints:u,capabilities:v}),t.canvas.onclick=()=>{t.video.paused?t.video.play():t.video.pause()}}async function l(){if(!t.video.paused){n.start===0&&(n.start=e.now()),await e.detect(t.video);let a=e.tf.memory().numTensors;a-n.tensors!==0&&i("allocated tensors:",a-n.tensors),n.tensors=a,o.detectFPS=Math.round(1e3*1e3/(e.now()-n.detect))/1e3,o.frames++,o.averageMs=Math.round(1e3*(e.now()-n.start)/o.frames)/1e3,o.frames%100===0&&!t.video.paused&&i("performance",{...o,tensors:n.tensors})}n.detect=e.now(),requestAnimationFrame(l)}async function m(){if(!t.video.paused){let d=e.next(e.result);e.config.filter.flip?e.draw.canvas(d.canvas,t.canvas):e.draw.canvas(t.video,t.canvas),await e.draw.all(t.canvas,d),b(d.performance)}let a=e.now();o.drawFPS=Math.round(1e3*1e3/(a-n.draw))/1e3,n.draw=a,r(t.video.paused?"paused":`fps: ${o.detectFPS.toFixed(1).padStart(5," ")} detect | ${o.drawFPS.toFixed(1).padStart(5," ")} draw`),setTimeout(m,30)}async function M(){i("human version:",e.version,"| tfjs version:",e.tf.version["tfjs-core"]),i("platform:",e.env.platform,"| agent:",e.env.agent),r("loading..."),await e.load(),i("backend:",e.tf.getBackend(),"| available:",e.env.backends),i("models stats:",e.getModelStats()),i("models loaded:",Object.values(e.models).filter(a=>a!==null).length),r("initializing..."),await e.warmup(),await h(),await l(),await m()}window.onload=M;
//# sourceMappingURL=index.js.map
diff --git a/demo/typescript/index.js.map b/demo/typescript/index.js.map
index 8de29dc1..ca92fb46 100644
--- a/demo/typescript/index.js.map
+++ b/demo/typescript/index.js.map
@@ -2,6 +2,6 @@
"version": 3,
"sources": ["index.ts"],
"sourcesContent": ["/**\n * Human demo for browsers\n * @default Human Library\n * @summary \n * @author \n * @copyright \n * @license MIT\n */\n\nimport * as H from '../../dist/human.esm.js'; // equivalent of @vladmandic/Human\n\nconst humanConfig: Partial = { // user configuration for human, used to fine-tune behavior\n // backend: 'wasm' as const,\n // wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.20.0/dist/',\n // cacheSensitivity: 0,\n async: false,\n modelBasePath: '../../models',\n filter: { enabled: true, equalization: false, flip: false },\n face: { enabled: true, detector: { rotation: false }, mesh: { enabled: true }, attention: { enabled: false }, iris: { enabled: true }, description: { enabled: true }, emotion: { enabled: true } },\n body: { enabled: true },\n hand: { enabled: true },\n object: { enabled: false },\n gesture: { enabled: true },\n};\n\nconst human = new H.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// human.draw.options.fillPolygons = true;\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('status') as HTMLPreElement,\n perf: document.getElementById('performance') as HTMLDivElement,\n};\nconst timestamp = { detect: 0, draw: 0, tensors: 0, start: 0 }; // holds information used to calculate performance and possible memory leaks\nconst fps = { detectFPS: 0, drawFPS: 0, frames: 0, averageMs: 0 }; // holds calculated fps information for both detect and screen refresh\n\nconst log = (...msg) => { // helper method to output messages\n dom.log.innerText += msg.join(' ') + '\\n';\n console.log(...msg); // eslint-disable-line no-console\n};\nconst status = (msg) => dom.fps.innerText = msg; // print status element\nconst perf = (msg) => dom.perf.innerText = 'tensors:' + (human.tf.memory().numTensors as number).toString() + ' | performance: ' + JSON.stringify(msg).replace(/\"|{|}/g, '').replace(/,/g, ' | '); // print performance element\n\nasync function webCam() { // initialize webcam\n status('starting webcam...');\n // @ts-ignore resizeMode is not yet defined in tslib\n const options: MediaStreamConstraints = { audio: false, video: { facingMode: 'user', resizeMode: 'none', width: { ideal: document.body.clientWidth }, height: { ideal: document.body.clientHeight } } };\n const stream: MediaStream = await navigator.mediaDevices.getUserMedia(options);\n const ready = new Promise((resolve) => { dom.video.onloadeddata = () => resolve(true); });\n dom.video.srcObject = stream;\n void dom.video.play();\n await ready;\n dom.canvas.width = dom.video.videoWidth;\n dom.canvas.height = dom.video.videoHeight;\n const track: MediaStreamTrack = stream.getVideoTracks()[0];\n const capabilities: MediaTrackCapabilities | string = track.getCapabilities ? track.getCapabilities() : '';\n const settings: MediaTrackSettings | string = track.getSettings ? track.getSettings() : '';\n const constraints: MediaTrackConstraints | string = track.getConstraints ? track.getConstraints() : '';\n log('video:', dom.video.videoWidth, dom.video.videoHeight, track.label, { stream, track, settings, constraints, capabilities });\n dom.canvas.onclick = () => { // pause when clicked on screen and resume on next click\n if (dom.video.paused) void dom.video.play();\n else dom.video.pause();\n };\n}\n\nasync function detectionLoop() { // main detection loop\n if (!dom.video.paused) {\n if (timestamp.start === 0) timestamp.start = human.now();\n // log('profiling data:', await human.profile(dom.video));\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 tensors = human.tf.memory().numTensors; // check current tensor usage for memory leaks\n if (tensors - timestamp.tensors !== 0) log('allocated tensors:', tensors - timestamp.tensors); // printed on start and each time there is a tensor leak\n timestamp.tensors = tensors;\n fps.detectFPS = Math.round(1000 * 1000 / (human.now() - timestamp.detect)) / 1000;\n fps.frames++;\n fps.averageMs = Math.round(1000 * (human.now() - timestamp.start) / fps.frames) / 1000;\n if (fps.frames % 100 === 0 && !dom.video.paused) log('performance', { ...fps, tensors: timestamp.tensors });\n }\n timestamp.detect = human.now();\n requestAnimationFrame(detectionLoop); // start new frame immediately\n}\n\nasync function drawLoop() { // main screen refresh loop\n if (!dom.video.paused) {\n const interpolated = human.next(human.result); // smoothen result using last-known results\n if (human.config.filter.flip) human.draw.canvas(interpolated.canvas as HTMLCanvasElement, dom.canvas); // draw processed image to screen canvas\n else human.draw.canvas(dom.video, dom.canvas); // draw original video to screen canvas // better than using procesed image as this loop happens faster than processing loop\n await human.draw.all(dom.canvas, interpolated); // draw labels, boxes, lines, etc.\n perf(interpolated.performance); // write performance data\n }\n const now = human.now();\n fps.drawFPS = Math.round(1000 * 1000 / (now - timestamp.draw)) / 1000;\n timestamp.draw = now;\n status(dom.video.paused ? 'paused' : `fps: ${fps.detectFPS.toFixed(1).padStart(5, ' ')} detect | ${fps.drawFPS.toFixed(1).padStart(5, ' ')} draw`); // write status\n setTimeout(drawLoop, 30); // use to slow down refresh from max refresh rate to target of 30 fps\n}\n\nasync function main() { // main entry point\n log('human version:', human.version, '| tfjs version:', human.tf.version['tfjs-core']);\n log('platform:', human.env.platform, '| agent:', human.env.agent);\n status('loading...');\n await human.load(); // preload all models\n log('backend:', human.tf.getBackend(), '| available:', human.env.backends);\n log('models stats:', human.getModelStats());\n log('models loaded:', Object.values(human.models).filter((model) => model !== null).length);\n status('initializing...');\n await human.warmup(); // warmup function to initialize backend for future faster detection\n await webCam(); // start webcam\n await detectionLoop(); // start detection loop\n await drawLoop(); // start draw loop\n}\n\nwindow.onload = main;\n"],
- "mappings": "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",
- "names": []
+ "mappings": "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",
+ "names": ["H", "humanConfig", "human", "dom", "timestamp", "fps", "log", "msg", "status", "perf", "webCam", "options", "stream", "ready", "resolve", "track", "capabilities", "settings", "constraints", "detectionLoop", "tensors", "drawLoop", "interpolated", "now", "main", "model"]
}
diff --git a/dist/human.d.ts b/dist/human.d.ts
index 0c1d2909..24904b17 100644
--- a/dist/human.d.ts
+++ b/dist/human.d.ts
@@ -212,6 +212,10 @@ export declare interface Config {
* default: 0.7
*/
cacheSensitivity: number;
+ /** Software Kernels
+ * Registers software kernel ops running on CPU when accelerated version of kernel is not found in the current backend
+ */
+ softwareKernels: boolean;
/** Perform immediate garbage collection on deallocated tensors instead of caching them */
deallocate: boolean;
/** Internal Variable */
diff --git a/dist/human.esm-nobundle.d.ts b/dist/human.esm-nobundle.d.ts
index 0c1d2909..24904b17 100644
--- a/dist/human.esm-nobundle.d.ts
+++ b/dist/human.esm-nobundle.d.ts
@@ -212,6 +212,10 @@ export declare interface Config {
* default: 0.7
*/
cacheSensitivity: number;
+ /** Software Kernels
+ * Registers software kernel ops running on CPU when accelerated version of kernel is not found in the current backend
+ */
+ softwareKernels: boolean;
/** Perform immediate garbage collection on deallocated tensors instead of caching them */
deallocate: boolean;
/** Internal Variable */
diff --git a/dist/human.esm-nobundle.js b/dist/human.esm-nobundle.js
index 317f08b8..db90c82d 100644
--- a/dist/human.esm-nobundle.js
+++ b/dist/human.esm-nobundle.js
@@ -4,265 +4,7 @@
author: '
*/
-var __defProp = Object.defineProperty;
-var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
-var __getOwnPropNames = Object.getOwnPropertyNames;
-var __hasOwnProp = Object.prototype.hasOwnProperty;
-var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
-var __export = (target, all2) => {
- for (var name in all2)
- __defProp(target, name, { get: all2[name], enumerable: true });
-};
-var __copyProps = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames(from))
- if (!__hasOwnProp.call(to, key) && key !== except)
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
- }
- return to;
-};
-var __reExport = (target, mod3, secondTarget) => (__copyProps(target, mod3, "default"), secondTarget && __copyProps(secondTarget, mod3, "default"));
-var __publicField = (obj, key, value) => {
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
- return value;
-};
-var __accessCheck = (obj, member, msg) => {
- if (!member.has(obj))
- throw TypeError("Cannot " + msg);
-};
-var __privateGet = (obj, member, getter) => {
- __accessCheck(obj, member, "read from private field");
- return getter ? getter.call(obj) : member.get(obj);
-};
-var __privateAdd = (obj, member, value) => {
- if (member.has(obj))
- throw TypeError("Cannot add the same private member more than once");
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
-};
-var __privateSet = (obj, member, value, setter) => {
- __accessCheck(obj, member, "write to private field");
- setter ? setter.call(obj, value) : member.set(obj, value);
- return value;
-};
-
-// src/util/util.ts
-function log(...msg) {
- const dt = new Date();
- const ts = `${dt.getHours().toString().padStart(2, "0")}:${dt.getMinutes().toString().padStart(2, "0")}:${dt.getSeconds().toString().padStart(2, "0")}.${dt.getMilliseconds().toString().padStart(3, "0")}`;
- if (msg)
- console.log(ts, "Human:", ...msg);
-}
-function join(folder, file) {
- const separator = folder.endsWith("/") ? "" : "/";
- const skipJoin = file.startsWith(".") || file.startsWith("/") || file.startsWith("http:") || file.startsWith("https:") || file.startsWith("file:");
- const path = skipJoin ? `${file}` : `${folder}${separator}${file}`;
- if (!path.toLocaleLowerCase().includes(".json"))
- throw new Error(`modelpath error: expecting json file: ${path}`);
- return path;
-}
-var now = () => {
- if (typeof performance !== "undefined")
- return performance.now();
- return parseInt((Number(process.hrtime.bigint()) / 1e3 / 1e3).toString());
-};
-function validate(defaults, config3, parent = "config", msgs = []) {
- for (const key of Object.keys(config3)) {
- if (typeof config3[key] === "object") {
- validate(defaults[key], config3[key], key, msgs);
- } else {
- const defined = defaults && typeof defaults[key] !== "undefined";
- if (!defined)
- msgs.push({ reason: "unknown property", where: `${parent}.${key} = ${config3[key]}` });
- const same = defaults && typeof defaults[key] === typeof config3[key];
- if (defined && !same)
- msgs.push({ reason: "property type mismatch", where: `${parent}.${key} = ${config3[key]}`, expected: typeof defaults[key] });
- }
- }
- if (config3.debug && parent === "config" && msgs.length > 0)
- log("invalid configuration", msgs);
- return msgs;
-}
-function mergeDeep(...objects) {
- const isObject = (obj) => obj && typeof obj === "object";
- return objects.reduce((prev, obj) => {
- Object.keys(obj || {}).forEach((key) => {
- const pVal = prev[key];
- const oVal = obj[key];
- if (Array.isArray(pVal) && Array.isArray(oVal))
- prev[key] = pVal.concat(...oVal);
- else if (isObject(pVal) && isObject(oVal))
- prev[key] = mergeDeep(pVal, oVal);
- else
- prev[key] = oVal;
- });
- return prev;
- }, {});
-}
-
-// src/config.ts
-var config = {
- backend: "",
- modelBasePath: "",
- cacheModels: true,
- validateModels: true,
- wasmPath: "",
- wasmPlatformFetch: false,
- debug: false,
- async: true,
- warmup: "full",
- cacheSensitivity: 0.7,
- skipAllowed: false,
- deallocate: false,
- softwareKernels: false,
- filter: {
- enabled: true,
- equalization: false,
- width: 0,
- height: 0,
- flip: false,
- return: true,
- brightness: 0,
- contrast: 0,
- sharpness: 0,
- blur: 0,
- saturation: 0,
- hue: 0,
- negative: false,
- sepia: false,
- vintage: false,
- kodachrome: false,
- technicolor: false,
- polaroid: false,
- pixelate: 0
- },
- gesture: {
- enabled: true
- },
- face: {
- enabled: true,
- detector: {
- modelPath: "blazeface.json",
- rotation: true,
- maxDetected: 1,
- skipFrames: 99,
- skipTime: 2500,
- minConfidence: 0.2,
- iouThreshold: 0.1,
- mask: false,
- return: false
- },
- mesh: {
- enabled: true,
- modelPath: "facemesh.json",
- keepInvalid: false
- },
- attention: {
- enabled: false,
- modelPath: "facemesh-attention.json"
- },
- iris: {
- enabled: true,
- modelPath: "iris.json"
- },
- emotion: {
- enabled: true,
- minConfidence: 0.1,
- skipFrames: 99,
- skipTime: 1500,
- modelPath: "emotion.json"
- },
- description: {
- enabled: true,
- modelPath: "faceres.json",
- skipFrames: 99,
- skipTime: 3e3,
- minConfidence: 0.1
- },
- antispoof: {
- enabled: false,
- skipFrames: 99,
- skipTime: 4e3,
- modelPath: "antispoof.json"
- },
- liveness: {
- enabled: false,
- skipFrames: 99,
- skipTime: 4e3,
- modelPath: "liveness.json"
- }
- },
- body: {
- enabled: true,
- modelPath: "movenet-lightning.json",
- maxDetected: -1,
- minConfidence: 0.3,
- skipFrames: 1,
- skipTime: 200
- },
- hand: {
- enabled: true,
- rotation: true,
- skipFrames: 99,
- skipTime: 1e3,
- minConfidence: 0.5,
- iouThreshold: 0.2,
- maxDetected: -1,
- landmarks: true,
- detector: {
- modelPath: "handtrack.json"
- },
- skeleton: {
- modelPath: "handlandmark-full.json"
- }
- },
- object: {
- enabled: false,
- modelPath: "mb3-centernet.json",
- minConfidence: 0.2,
- iouThreshold: 0.4,
- maxDetected: 10,
- skipFrames: 99,
- skipTime: 2e3
- },
- segmentation: {
- enabled: false,
- modelPath: "selfie.json",
- blur: 8
- }
-};
-
-// dist/tfjs.esm.js
-var tfjs_esm_exports = {};
-__export(tfjs_esm_exports, {
- GraphModel: () => GraphModel,
- Tensor: () => Tensor,
- version: () => version8
-});
-__reExport(tfjs_esm_exports, dist_star);
-__reExport(tfjs_esm_exports, dist_star2);
-import * as dist_star from "@tensorflow/tfjs/dist/index.js";
-import * as dist_star2 from "@tensorflow/tfjs-backend-webgl/dist/index.js";
-import { Tensor } from "@tensorflow/tfjs/dist/index.js";
-import { GraphModel } from "@tensorflow/tfjs-converter/dist/index";
-var version = "3.20.0";
-var version2 = "3.20.0";
-var version3 = "3.20.0";
-var version4 = "3.20.0";
-var version5 = "3.20.0";
-var version6 = "3.20.0";
-var version7 = "3.20.0";
-var version8 = {
- tfjs: version,
- "tfjs-core": version2,
- "tfjs-data": version3,
- "tfjs-layers": version4,
- "tfjs-converter": version5,
- "tfjs-backend-webgl": version6,
- "tfjs-backend-wasm": version7
-};
-
-// src/image/imagefxshaders.ts
-var vertexIdentity = `
+var Q2=Object.defineProperty;var Co=Object.getOwnPropertyDescriptor;var Io=Object.getOwnPropertyNames;var jo=Object.prototype.hasOwnProperty;var Oo=(e,t,o)=>t in e?Q2(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o;var q0=(e,t)=>{for(var o in t)Q2(e,o,{get:t[o],enumerable:!0})},W1=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Io(t))!jo.call(e,r)&&r!==o&&Q2(e,r,{get:()=>t[r],enumerable:!(n=Co(t,r))||n.enumerable});return e},Z=(e,t,o)=>(W1(e,t,"default"),o&&W1(o,t,"default"));var w=(e,t,o)=>(Oo(e,typeof t!="symbol"?t+"":t,o),o),F1=(e,t,o)=>{if(!t.has(e))throw TypeError("Cannot "+o)};var Ve=(e,t,o)=>(F1(e,t,"read from private field"),o?o.call(e):t.get(e)),Ze=(e,t,o)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,o)},Xe=(e,t,o,n)=>(F1(e,t,"write to private field"),n?n.call(e,o):t.set(e,o),o);function b(...e){let t=new Date,o=`${t.getHours().toString().padStart(2,"0")}:${t.getMinutes().toString().padStart(2,"0")}:${t.getSeconds().toString().padStart(2,"0")}.${t.getMilliseconds().toString().padStart(3,"0")}`;e&&console.log(o,"Human:",...e)}function G1(e,t){let o=e.endsWith("/")?"":"/",r=t.startsWith(".")||t.startsWith("/")||t.startsWith("http:")||t.startsWith("https:")||t.startsWith("file:")?`${t}`:`${e}${o}${t}`;if(!r.toLocaleLowerCase().includes(".json"))throw new Error(`modelpath error: expecting json file: ${r}`);return r}var v=()=>typeof performance!="undefined"?performance.now():parseInt((Number(process.hrtime.bigint())/1e3/1e3).toString());function _2(e,t,o="config",n=[]){for(let r of Object.keys(t))if(typeof t[r]=="object")_2(e[r],t[r],r,n);else{let s=e&&typeof e[r]!="undefined";s||n.push({reason:"unknown property",where:`${o}.${r} = ${t[r]}`});let a=e&&typeof e[r]==typeof t[r];s&&!a&&n.push({reason:"property type mismatch",where:`${o}.${r} = ${t[r]}`,expected:typeof e[r]})}return t.debug&&o==="config"&&n.length>0&&b("invalid configuration",n),n}function U(...e){let t=o=>o&&typeof o=="object";return e.reduce((o,n)=>(Object.keys(n||{}).forEach(r=>{let s=o[r],a=n[r];Array.isArray(s)&&Array.isArray(a)?o[r]=s.concat(...a):t(s)&&t(a)?o[r]=U(s,a):o[r]=a}),o),{})}var Ae={backend:"",modelBasePath:"",cacheModels:!0,validateModels:!0,wasmPath:"",wasmPlatformFetch:!1,debug:!1,async:!0,warmup:"full",cacheSensitivity:.7,skipAllowed:!1,deallocate:!1,softwareKernels:!1,filter:{enabled:!0,equalization:!1,width:0,height:0,flip:!1,return:!0,brightness:0,contrast:0,sharpness:0,blur:0,saturation:0,hue:0,negative:!1,sepia:!1,vintage:!1,kodachrome:!1,technicolor:!1,polaroid:!1,pixelate:0},gesture:{enabled:!0},face:{enabled:!0,detector:{modelPath:"blazeface.json",rotation:!0,maxDetected:1,skipFrames:99,skipTime:2500,minConfidence:.2,iouThreshold:.1,mask:!1,return:!1},mesh:{enabled:!0,modelPath:"facemesh.json",keepInvalid:!1},attention:{enabled:!1,modelPath:"facemesh-attention.json"},iris:{enabled:!0,modelPath:"iris.json"},emotion:{enabled:!0,minConfidence:.1,skipFrames:99,skipTime:1500,modelPath:"emotion.json"},description:{enabled:!0,modelPath:"faceres.json",skipFrames:99,skipTime:3e3,minConfidence:.1},antispoof:{enabled:!1,skipFrames:99,skipTime:4e3,modelPath:"antispoof.json"},liveness:{enabled:!1,skipFrames:99,skipTime:4e3,modelPath:"liveness.json"}},body:{enabled:!0,modelPath:"movenet-lightning.json",maxDetected:-1,minConfidence:.3,skipFrames:1,skipTime:200},hand:{enabled:!0,rotation:!0,skipFrames:99,skipTime:1e3,minConfidence:.5,iouThreshold:.2,maxDetected:-1,landmarks:!0,detector:{modelPath:"handtrack.json"},skeleton:{modelPath:"handlandmark-full.json"}},object:{enabled:!1,modelPath:"mb3-centernet.json",minConfidence:.2,iouThreshold:.4,maxDetected:10,skipFrames:99,skipTime:2e3},segmentation:{enabled:!1,modelPath:"selfie.json",blur:8}};var A={};q0(A,{GraphModel:()=>$2,Tensor:()=>be,version:()=>qe});Z(A,e7);Z(A,t7);import*as e7 from"@tensorflow/tfjs/dist/index.js";import*as t7 from"@tensorflow/tfjs-backend-webgl/dist/index.js";import{Tensor as be}from"@tensorflow/tfjs/dist/index.js";import{GraphModel as $2}from"@tensorflow/tfjs-converter/dist/index";var No="3.20.0",Lo="3.20.0",Wo="3.20.0",Fo="3.20.0",Go="3.20.0",Bo="3.20.0",Ho="3.20.0",qe={tfjs:No,"tfjs-core":Lo,"tfjs-data":Wo,"tfjs-layers":Fo,"tfjs-converter":Go,"tfjs-backend-webgl":Bo,"tfjs-backend-wasm":Ho};var B1=`
precision highp float;
attribute vec2 pos;
attribute vec2 uv;
@@ -272,8 +14,7 @@ var vertexIdentity = `
vUv = uv;
gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);
}
-`;
-var colorMatrixWithAlpha = `
+`;var H1=`
precision highp float;
varying vec2 vUv;
uniform sampler2D texture;
@@ -285,8 +26,7 @@ var colorMatrixWithAlpha = `
gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];
gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];
}
-`;
-var colorMatrixWithoutAlpha = `
+`,D1=`
precision highp float;
varying vec2 vUv;
uniform sampler2D texture;
@@ -298,8 +38,7 @@ var colorMatrixWithoutAlpha = `
gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];
gl_FragColor.a = c.a;
}
-`;
-var pixelate = `
+`,V1=`
precision highp float;
varying vec2 vUv;
uniform vec2 size;
@@ -312,8 +51,7 @@ var pixelate = `
vec2 coord = pixelate(vUv, size);
gl_FragColor += texture2D(texture, coord);
}
-`;
-var blur = `
+`,Z1=`
precision highp float;
varying vec2 vUv;
uniform sampler2D texture;
@@ -336,8 +74,7 @@ var blur = `
gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;
gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;
}
-`;
-var convolution = `
+`,X1=`
precision highp float;
varying vec2 vUv;
uniform sampler2D texture;
@@ -359,13096 +96,19 @@ var convolution = `
c31 * m[6] + c32 * m[7] + c33 * m[8];
gl_FragColor.a = c22.a;
}
-`;
-
-// src/image/imagefx.ts
-var collect = (source, prefix, collection) => {
- const r = new RegExp("\\b" + prefix + " \\w+ (\\w+)", "ig");
- source.replace(r, (match3, name) => {
- collection[name] = 0;
- return match3;
- });
-};
-var GLProgram = class {
- constructor(gl, vertexSource, fragmentSource) {
- __publicField(this, "uniform", {});
- __publicField(this, "attribute", {});
- __publicField(this, "gl");
- __publicField(this, "id");
- __publicField(this, "compile", (source, type) => {
- const shader = this.gl.createShader(type);
- if (!shader) {
- log("filter: could not create shader");
- return null;
- }
- this.gl.shaderSource(shader, source);
- this.gl.compileShader(shader);
- if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
- log(`filter: gl compile failed: ${this.gl.getShaderInfoLog(shader) || "unknown"}`);
- return null;
- }
- return shader;
- });
- this.gl = gl;
- const vertexShader = this.compile(vertexSource, this.gl.VERTEX_SHADER);
- const fragmentShader = this.compile(fragmentSource, this.gl.FRAGMENT_SHADER);
- this.id = this.gl.createProgram();
- if (!vertexShader || !fragmentShader)
- return;
- if (!this.id) {
- log("filter: could not create webgl program");
- return;
- }
- this.gl.attachShader(this.id, vertexShader);
- this.gl.attachShader(this.id, fragmentShader);
- this.gl.linkProgram(this.id);
- if (!this.gl.getProgramParameter(this.id, this.gl.LINK_STATUS)) {
- log(`filter: gl link failed: ${this.gl.getProgramInfoLog(this.id) || "unknown"}`);
- return;
- }
- this.gl.useProgram(this.id);
- collect(vertexSource, "attribute", this.attribute);
- for (const a in this.attribute)
- this.attribute[a] = this.gl.getAttribLocation(this.id, a);
- collect(vertexSource, "uniform", this.uniform);
- collect(fragmentSource, "uniform", this.uniform);
- for (const u in this.uniform)
- this.uniform[u] = this.gl.getUniformLocation(this.id, u);
- }
-};
-function GLImageFilter() {
- let drawCount = 0;
- let sourceTexture = null;
- let lastInChain = false;
- let currentFramebufferIndex = -1;
- let tempFramebuffers = [null, null];
- let filterChain = [];
- let vertexBuffer = null;
- let currentProgram = null;
- const fxcanvas = canvas(100, 100);
- const shaderProgramCache = {};
- const DRAW = { INTERMEDIATE: 1 };
- const gl = fxcanvas.getContext("webgl");
- if (!gl) {
- log("filter: cannot get webgl context");
- return;
- }
- this.gl = gl;
- function resize(width, height) {
- if (width === fxcanvas.width && height === fxcanvas.height)
- return;
- fxcanvas.width = width;
- fxcanvas.height = height;
- if (!vertexBuffer) {
- const vertices = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0]);
- vertexBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
- }
- gl.viewport(0, 0, fxcanvas.width, fxcanvas.height);
- tempFramebuffers = [null, null];
- }
- function createFramebufferTexture(width, height) {
- const fbo = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
- const renderbuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
- const texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- return { fbo, texture };
- }
- function getTempFramebuffer(index2) {
- tempFramebuffers[index2] = tempFramebuffers[index2] || createFramebufferTexture(fxcanvas.width, fxcanvas.height);
- return tempFramebuffers[index2];
- }
- function draw(flags = 0) {
- if (!currentProgram)
- return;
- let source = null;
- let target = null;
- let flipY = false;
- if (drawCount === 0)
- source = sourceTexture;
- else
- source = getTempFramebuffer(currentFramebufferIndex).texture || null;
- drawCount++;
- if (lastInChain && !(flags & DRAW.INTERMEDIATE)) {
- target = null;
- flipY = drawCount % 2 === 0;
- } else {
- currentFramebufferIndex = (currentFramebufferIndex + 1) % 2;
- target = getTempFramebuffer(currentFramebufferIndex).fbo || null;
- }
- gl.bindTexture(gl.TEXTURE_2D, source);
- gl.bindFramebuffer(gl.FRAMEBUFFER, target);
- gl.uniform1f(currentProgram.uniform["flipY"], flipY ? -1 : 1);
- gl.drawArrays(gl.TRIANGLES, 0, 6);
- }
- function compileShader(fragmentSource) {
- if (shaderProgramCache[fragmentSource]) {
- currentProgram = shaderProgramCache[fragmentSource];
- gl.useProgram((currentProgram ? currentProgram.id : null) || null);
- return currentProgram;
- }
- currentProgram = new GLProgram(gl, vertexIdentity, fragmentSource);
- if (!currentProgram) {
- log("filter: could not get webgl program");
- return null;
- }
- const floatSize = Float32Array.BYTES_PER_ELEMENT;
- const vertSize = 4 * floatSize;
- gl.enableVertexAttribArray(currentProgram.attribute["pos"]);
- gl.vertexAttribPointer(currentProgram.attribute["pos"], 2, gl.FLOAT, false, vertSize, 0 * floatSize);
- gl.enableVertexAttribArray(currentProgram.attribute["uv"]);
- gl.vertexAttribPointer(currentProgram.attribute["uv"], 2, gl.FLOAT, false, vertSize, 2 * floatSize);
- shaderProgramCache[fragmentSource] = currentProgram;
- return currentProgram;
- }
- const filter = {
- colorMatrix: (matrix) => {
- const m = new Float32Array(matrix);
- m[4] /= 255;
- m[9] /= 255;
- m[14] /= 255;
- m[19] /= 255;
- const shader = m[18] === 1 && m[3] === 0 && m[8] === 0 && m[13] === 0 && m[15] === 0 && m[16] === 0 && m[17] === 0 && m[19] === 0 ? colorMatrixWithoutAlpha : colorMatrixWithAlpha;
- const program = compileShader(shader);
- if (!program)
- return;
- gl.uniform1fv(program.uniform["m"], m);
- draw();
- },
- brightness: (brightness) => {
- const b = (brightness || 0) + 1;
- filter.colorMatrix([
- b,
- 0,
- 0,
- 0,
- 0,
- 0,
- b,
- 0,
- 0,
- 0,
- 0,
- 0,
- b,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- saturation: (amount) => {
- const x = (amount || 0) * 2 / 3 + 1;
- const y = (x - 1) * -0.5;
- filter.colorMatrix([
- x,
- y,
- y,
- 0,
- 0,
- y,
- x,
- y,
- 0,
- 0,
- y,
- y,
- x,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- desaturate: () => {
- filter.saturation(-1);
- },
- contrast: (amount) => {
- const v = (amount || 0) + 1;
- const o = -128 * (v - 1);
- filter.colorMatrix([
- v,
- 0,
- 0,
- 0,
- o,
- 0,
- v,
- 0,
- 0,
- o,
- 0,
- 0,
- v,
- 0,
- o,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- negative: () => {
- filter.contrast(-2);
- },
- hue: (rotation) => {
- rotation = (rotation || 0) / 180 * Math.PI;
- const cos = Math.cos(rotation);
- const sin = Math.sin(rotation);
- const lumR = 0.213;
- const lumG = 0.715;
- const lumB = 0.072;
- filter.colorMatrix([
- lumR + cos * (1 - lumR) + sin * -lumR,
- lumG + cos * -lumG + sin * -lumG,
- lumB + cos * -lumB + sin * (1 - lumB),
- 0,
- 0,
- lumR + cos * -lumR + sin * 0.143,
- lumG + cos * (1 - lumG) + sin * 0.14,
- lumB + cos * -lumB + sin * -0.283,
- 0,
- 0,
- lumR + cos * -lumR + sin * -(1 - lumR),
- lumG + cos * -lumG + sin * lumG,
- lumB + cos * (1 - lumB) + sin * lumB,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- desaturateLuminance: () => {
- filter.colorMatrix([
- 0.2764723,
- 0.929708,
- 0.0938197,
- 0,
- -37.1,
- 0.2764723,
- 0.929708,
- 0.0938197,
- 0,
- -37.1,
- 0.2764723,
- 0.929708,
- 0.0938197,
- 0,
- -37.1,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- sepia: () => {
- filter.colorMatrix([
- 0.393,
- 0.7689999,
- 0.18899999,
- 0,
- 0,
- 0.349,
- 0.6859999,
- 0.16799999,
- 0,
- 0,
- 0.272,
- 0.5339999,
- 0.13099999,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- brownie: () => {
- filter.colorMatrix([
- 0.5997023498159715,
- 0.34553243048391263,
- -0.2708298674538042,
- 0,
- 47.43192855600873,
- -0.037703249837783157,
- 0.8609577587992641,
- 0.15059552388459913,
- 0,
- -36.96841498319127,
- 0.24113635128153335,
- -0.07441037908422492,
- 0.44972182064877153,
- 0,
- -7.562075277591283,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- vintagePinhole: () => {
- filter.colorMatrix([
- 0.6279345635605994,
- 0.3202183420819367,
- -0.03965408211312453,
- 0,
- 9.651285835294123,
- 0.02578397704808868,
- 0.6441188644374771,
- 0.03259127616149294,
- 0,
- 7.462829176470591,
- 0.0466055556782719,
- -0.0851232987247891,
- 0.5241648018700465,
- 0,
- 5.159190588235296,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- kodachrome: () => {
- filter.colorMatrix([
- 1.1285582396593525,
- -0.3967382283601348,
- -0.03992559172921793,
- 0,
- 63.72958762196502,
- -0.16404339962244616,
- 1.0835251566291304,
- -0.05498805115633132,
- 0,
- 24.732407896706203,
- -0.16786010706155763,
- -0.5603416277695248,
- 1.6014850761964943,
- 0,
- 35.62982807460946,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- technicolor: () => {
- filter.colorMatrix([
- 1.9125277891456083,
- -0.8545344976951645,
- -0.09155508482755585,
- 0,
- 11.793603434377337,
- -0.3087833385928097,
- 1.7658908555458428,
- -0.10601743074722245,
- 0,
- -70.35205161461398,
- -0.231103377548616,
- -0.7501899197440212,
- 1.847597816108189,
- 0,
- 30.950940869491138,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- polaroid: () => {
- filter.colorMatrix([
- 1.438,
- -0.062,
- -0.062,
- 0,
- 0,
- -0.122,
- 1.378,
- -0.122,
- 0,
- 0,
- -0.016,
- -0.016,
- 1.483,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- shiftToBGR: () => {
- filter.colorMatrix([
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ]);
- },
- convolution: (matrix) => {
- const m = new Float32Array(matrix);
- const pixelSizeX = 1 / fxcanvas.width;
- const pixelSizeY = 1 / fxcanvas.height;
- const program = compileShader(convolution);
- if (!program)
- return;
- gl.uniform1fv(program.uniform["m"], m);
- gl.uniform2f(program.uniform["px"], pixelSizeX, pixelSizeY);
- draw();
- },
- detectEdges: () => {
- filter.convolution.call(this, [
- 0,
- 1,
- 0,
- 1,
- -4,
- 1,
- 0,
- 1,
- 0
- ]);
- },
- sobelX: () => {
- filter.convolution.call(this, [
- -1,
- 0,
- 1,
- -2,
- 0,
- 2,
- -1,
- 0,
- 1
- ]);
- },
- sobelY: () => {
- filter.convolution.call(this, [
- -1,
- -2,
- -1,
- 0,
- 0,
- 0,
- 1,
- 2,
- 1
- ]);
- },
- sharpen: (amount) => {
- const a = amount || 1;
- filter.convolution.call(this, [
- 0,
- -1 * a,
- 0,
- -1 * a,
- 1 + 4 * a,
- -1 * a,
- 0,
- -1 * a,
- 0
- ]);
- },
- emboss: (size2) => {
- const s = size2 || 1;
- filter.convolution.call(this, [
- -2 * s,
- -1 * s,
- 0,
- -1 * s,
- 1,
- 1 * s,
- 0,
- 1 * s,
- 2 * s
- ]);
- },
- blur: (size2) => {
- const blurSizeX = size2 / 7 / fxcanvas.width;
- const blurSizeY = size2 / 7 / fxcanvas.height;
- const program = compileShader(blur);
- if (!program)
- return;
- gl.uniform2f(program.uniform["px"], 0, blurSizeY);
- draw(DRAW.INTERMEDIATE);
- gl.uniform2f(program.uniform["px"], blurSizeX, 0);
- draw();
- },
- pixelate: (size2) => {
- const blurSizeX = size2 / fxcanvas.width;
- const blurSizeY = size2 / fxcanvas.height;
- const program = compileShader(pixelate);
- if (!program)
- return;
- gl.uniform2f(program.uniform["size"], blurSizeX, blurSizeY);
- draw();
- }
- };
- this.add = function(name) {
- const args = Array.prototype.slice.call(arguments, 1);
- const func = filter[name];
- filterChain.push({ func, args });
- };
- this.reset = function() {
- filterChain = [];
- };
- this.get = function() {
- return filterChain;
- };
- this.apply = function(image26) {
- resize(image26.width, image26.height);
- drawCount = 0;
- if (!sourceTexture)
- sourceTexture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image26);
- for (let i = 0; i < filterChain.length; i++) {
- lastInChain = i === filterChain.length - 1;
- const f = filterChain[i];
- f.func.apply(this, f.args || []);
- }
- return fxcanvas;
- };
- this.draw = function(image26) {
- this.add("brightness", 0);
- return this.apply(image26);
- };
-}
-
-// src/image/enhance.ts
-async function histogramEqualization(inputImage) {
- const squeeze12 = inputImage.shape.length === 4 ? tfjs_esm_exports.squeeze(inputImage) : inputImage;
- const channels = tfjs_esm_exports.split(squeeze12, 3, 2);
- const min2 = [tfjs_esm_exports.min(channels[0]), tfjs_esm_exports.min(channels[1]), tfjs_esm_exports.min(channels[2])];
- const max4 = [tfjs_esm_exports.max(channels[0]), tfjs_esm_exports.max(channels[1]), tfjs_esm_exports.max(channels[2])];
- const absMax = await Promise.all(max4.map((channel) => channel.data()));
- const maxValue = 0.99 * Math.max(absMax[0][0], absMax[1][0], absMax[2][0]);
- const sub11 = [tfjs_esm_exports.sub(channels[0], min2[0]), tfjs_esm_exports.sub(channels[1], min2[1]), tfjs_esm_exports.sub(channels[2], min2[2])];
- const range = [tfjs_esm_exports.sub(max4[0], min2[0]), tfjs_esm_exports.sub(max4[1], min2[1]), tfjs_esm_exports.sub(max4[2], min2[2])];
- const fact = [tfjs_esm_exports.div(maxValue, range[0]), tfjs_esm_exports.div(maxValue, range[1]), tfjs_esm_exports.div(maxValue, range[2])];
- const enh = [tfjs_esm_exports.mul(sub11[0], fact[0]), tfjs_esm_exports.mul(sub11[1], fact[1]), tfjs_esm_exports.mul(sub11[2], fact[2])];
- const rgb2 = tfjs_esm_exports.stack([enh[0], enh[1], enh[2]], 2);
- const reshape8 = tfjs_esm_exports.reshape(rgb2, [1, squeeze12.shape[0], squeeze12.shape[1], 3]);
- tfjs_esm_exports.dispose([...channels, ...min2, ...max4, ...sub11, ...range, ...fact, ...enh, rgb2, squeeze12]);
- return reshape8;
-}
-
-// src/image/image.ts
-var maxSize = 3840;
-var inCanvas = null;
-var outCanvas = null;
-var tmpCanvas = null;
-var fx;
-var last = {
- inputSum: 0,
- cacheDiff: 1,
- sumMethod: 0,
- inputTensor: void 0
-};
-function canvas(width, height) {
- let c;
- if (env.browser) {
- if (env.worker) {
- if (typeof OffscreenCanvas === "undefined")
- throw new Error("canvas error: attempted to run in web worker but OffscreenCanvas is not supported");
- c = new OffscreenCanvas(width, height);
- } else {
- if (typeof document === "undefined")
- throw new Error("canvas error: attempted to run in browser but DOM is not defined");
- c = document.createElement("canvas");
- c.width = width;
- c.height = height;
- }
- } else {
- if (typeof env.Canvas !== "undefined")
- c = new env.Canvas(width, height);
- else if (typeof globalThis.Canvas !== "undefined")
- c = new globalThis.Canvas(width, height);
- }
- return c;
-}
-function copy(input, output) {
- const outputCanvas = output || canvas(input.width, input.height);
- const ctx = outputCanvas.getContext("2d");
- ctx.drawImage(input, 0, 0);
- return outputCanvas;
-}
-async function process2(input, config3, getTensor = true) {
- var _a, _b;
- if (!input) {
- if (config3.debug)
- log("input error: input is missing");
- return { tensor: null, canvas: null };
- }
- if (!(input instanceof Tensor) && !(typeof Image !== "undefined" && input instanceof Image) && !(typeof env.Canvas !== "undefined" && input instanceof env.Canvas) && !(typeof globalThis.Canvas !== "undefined" && input instanceof globalThis.Canvas) && !(typeof ImageData !== "undefined" && input instanceof ImageData) && !(typeof ImageBitmap !== "undefined" && input instanceof ImageBitmap) && !(typeof HTMLImageElement !== "undefined" && input instanceof HTMLImageElement) && !(typeof HTMLMediaElement !== "undefined" && input instanceof HTMLMediaElement) && !(typeof HTMLVideoElement !== "undefined" && input instanceof HTMLVideoElement) && !(typeof HTMLCanvasElement !== "undefined" && input instanceof HTMLCanvasElement) && !(typeof OffscreenCanvas !== "undefined" && input instanceof OffscreenCanvas)) {
- throw new Error("input error: type is not recognized");
- }
- if (input instanceof Tensor) {
- let tensor4 = null;
- if (input["isDisposedInternal"])
- throw new Error("input error: attempted to use tensor but it is disposed");
- if (!input.shape)
- throw new Error("input error: attempted to use tensor without a shape");
- if (input.shape.length === 3) {
- if (input.shape[2] === 3) {
- tensor4 = tfjs_esm_exports.expandDims(input, 0);
- } else if (input.shape[2] === 4) {
- const rgb2 = tfjs_esm_exports.slice3d(input, [0, 0, 0], [-1, -1, 3]);
- tensor4 = tfjs_esm_exports.expandDims(rgb2, 0);
- tfjs_esm_exports.dispose(rgb2);
- }
- } else if (input.shape.length === 4) {
- if (input.shape[3] === 3) {
- tensor4 = tfjs_esm_exports.clone(input);
- } else if (input.shape[3] === 4) {
- tensor4 = tfjs_esm_exports.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);
- }
- }
- if (tensor4 == null || tensor4.shape.length !== 4 || tensor4.shape[0] !== 1 || tensor4.shape[3] !== 3)
- throw new Error(`input error: attempted to use tensor with unrecognized shape: ${input.shape.toString()}`);
- if (tensor4.dtype === "int32") {
- const cast5 = tfjs_esm_exports.cast(tensor4, "float32");
- tfjs_esm_exports.dispose(tensor4);
- tensor4 = cast5;
- }
- return { tensor: tensor4, canvas: config3.filter.return ? outCanvas : null };
- }
- if (typeof input["readyState"] !== "undefined" && input.readyState <= 2) {
- if (config3.debug)
- log("input stream is not ready");
- return { tensor: null, canvas: inCanvas };
- }
- const originalWidth = input["naturalWidth"] || input["videoWidth"] || input["width"] || input["shape"] && input["shape"][1] > 0;
- const originalHeight = input["naturalHeight"] || input["videoHeight"] || input["height"] || input["shape"] && input["shape"][2] > 0;
- if (!originalWidth || !originalHeight) {
- if (config3.debug)
- log("cannot determine input dimensions");
- return { tensor: null, canvas: inCanvas };
- }
- let targetWidth = originalWidth;
- let targetHeight = originalHeight;
- if (targetWidth > maxSize) {
- targetWidth = maxSize;
- targetHeight = Math.trunc(targetWidth * originalHeight / originalWidth);
- }
- if (targetHeight > maxSize) {
- targetHeight = maxSize;
- targetWidth = Math.trunc(targetHeight * originalWidth / originalHeight);
- }
- if ((((_a = config3.filter) == null ? void 0 : _a.width) || 0) > 0)
- targetWidth = config3.filter.width;
- else if ((((_b = config3.filter) == null ? void 0 : _b.height) || 0) > 0)
- targetWidth = originalWidth * ((config3.filter.height || 0) / originalHeight);
- if ((config3.filter.height || 0) > 0)
- targetHeight = config3.filter.height;
- else if ((config3.filter.width || 0) > 0)
- targetHeight = originalHeight * ((config3.filter.width || 0) / originalWidth);
- if (!targetWidth || !targetHeight)
- throw new Error("input error: cannot determine dimension");
- if (!inCanvas || inCanvas.width !== targetWidth || inCanvas.height !== targetHeight)
- inCanvas = canvas(targetWidth, targetHeight);
- const inCtx = inCanvas.getContext("2d");
- if (typeof ImageData !== "undefined" && input instanceof ImageData) {
- inCtx.putImageData(input, 0, 0);
- } else {
- if (config3.filter.flip && typeof inCtx.translate !== "undefined") {
- inCtx.translate(originalWidth, 0);
- inCtx.scale(-1, 1);
- inCtx.drawImage(input, 0, 0, originalWidth, originalHeight, 0, 0, inCanvas.width, inCanvas.height);
- inCtx.setTransform(1, 0, 0, 1, 0, 0);
- } else {
- inCtx.drawImage(input, 0, 0, originalWidth, originalHeight, 0, 0, inCanvas.width, inCanvas.height);
- }
- }
- if (!outCanvas || inCanvas.width !== outCanvas.width || inCanvas.height !== outCanvas.height)
- outCanvas = canvas(inCanvas.width, inCanvas.height);
- if (config3.filter.enabled && env.webgl.supported) {
- if (!fx)
- fx = env.browser ? new GLImageFilter() : null;
- env.filter = !!fx;
- if (!(fx == null ? void 0 : fx.add)) {
- if (config3.debug)
- log("input process error: cannot initialize filters");
- env.webgl.supported = false;
- config3.filter.enabled = false;
- copy(inCanvas, outCanvas);
- } else {
- fx.reset();
- if (config3.filter.brightness !== 0)
- fx.add("brightness", config3.filter.brightness);
- if (config3.filter.contrast !== 0)
- fx.add("contrast", config3.filter.contrast);
- if (config3.filter.sharpness !== 0)
- fx.add("sharpen", config3.filter.sharpness);
- if (config3.filter.blur !== 0)
- fx.add("blur", config3.filter.blur);
- if (config3.filter.saturation !== 0)
- fx.add("saturation", config3.filter.saturation);
- if (config3.filter.hue !== 0)
- fx.add("hue", config3.filter.hue);
- if (config3.filter.negative)
- fx.add("negative");
- if (config3.filter.sepia)
- fx.add("sepia");
- if (config3.filter.vintage)
- fx.add("brownie");
- if (config3.filter.sepia)
- fx.add("sepia");
- if (config3.filter.kodachrome)
- fx.add("kodachrome");
- if (config3.filter.technicolor)
- fx.add("technicolor");
- if (config3.filter.polaroid)
- fx.add("polaroid");
- if (config3.filter.pixelate !== 0)
- fx.add("pixelate", config3.filter.pixelate);
- if (fx.get() > 0)
- outCanvas = fx.apply(inCanvas);
- else
- outCanvas = fx.draw(inCanvas);
- }
- } else {
- copy(inCanvas, outCanvas);
- if (fx)
- fx = null;
- env.filter = !!fx;
- }
- if (!getTensor)
- return { tensor: null, canvas: outCanvas };
- if (!outCanvas)
- throw new Error("canvas error: cannot create output");
- let pixels;
- let depth = 3;
- if (typeof ImageData !== "undefined" && input instanceof ImageData || input.data && input.width && input.height) {
- if (env.browser && tfjs_esm_exports.browser) {
- pixels = tfjs_esm_exports.browser ? tfjs_esm_exports.browser.fromPixels(input) : null;
- } else {
- depth = input.data.length / input.height / input.width;
- const arr = new Uint8Array(input.data.buffer);
- pixels = tfjs_esm_exports.tensor(arr, [input.height, input.width, depth], "int32");
- }
- } else {
- if (!tmpCanvas || outCanvas.width !== tmpCanvas.width || outCanvas.height !== tmpCanvas.height)
- tmpCanvas = canvas(outCanvas.width, outCanvas.height);
- if (tfjs_esm_exports.browser && env.browser) {
- if (config3.backend === "webgl" || config3.backend === "humangl" || config3.backend === "webgpu") {
- pixels = tfjs_esm_exports.browser.fromPixels(outCanvas);
- } else {
- tmpCanvas = copy(outCanvas);
- pixels = tfjs_esm_exports.browser.fromPixels(tmpCanvas);
- }
- } else {
- const tempCanvas = copy(outCanvas);
- const tempCtx = tempCanvas.getContext("2d");
- const tempData = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
- depth = tempData.data.length / targetWidth / targetHeight;
- const arr = new Uint8Array(tempData.data.buffer);
- pixels = tfjs_esm_exports.tensor(arr, [targetWidth, targetHeight, depth]);
- }
- }
- if (depth === 4) {
- const rgb2 = tfjs_esm_exports.slice3d(pixels, [0, 0, 0], [-1, -1, 3]);
- tfjs_esm_exports.dispose(pixels);
- pixels = rgb2;
- }
- if (!pixels)
- throw new Error("input error: cannot create tensor");
- const casted = tfjs_esm_exports.cast(pixels, "float32");
- const tensor3 = config3.filter.equalization ? await histogramEqualization(casted) : tfjs_esm_exports.expandDims(casted, 0);
- tfjs_esm_exports.dispose([pixels, casted]);
- return { tensor: tensor3, canvas: config3.filter.return ? outCanvas : null };
-}
-async function skip(config3, input) {
- let skipFrame = false;
- if (config3.cacheSensitivity === 0 || !input.shape || input.shape.length !== 4 || input.shape[1] > 2048 || input.shape[2] > 2048)
- return skipFrame;
- if (!last.inputTensor) {
- last.inputTensor = tfjs_esm_exports.clone(input);
- } else if (last.inputTensor.shape[1] !== input.shape[1] || last.inputTensor.shape[2] !== input.shape[2]) {
- tfjs_esm_exports.dispose(last.inputTensor);
- last.inputTensor = tfjs_esm_exports.clone(input);
- } else {
- const t = {};
- t.diff = tfjs_esm_exports.sub(input, last.inputTensor);
- t.squared = tfjs_esm_exports.mul(t.diff, t.diff);
- t.sum = tfjs_esm_exports.sum(t.squared);
- const diffSum = await t.sum.data();
- const diffRelative = diffSum[0] / (input.shape[1] || 1) / (input.shape[2] || 1) / 255 / 3;
- tfjs_esm_exports.dispose([last.inputTensor, t.diff, t.squared, t.sum]);
- last.inputTensor = tfjs_esm_exports.clone(input);
- skipFrame = diffRelative <= (config3.cacheSensitivity || 0);
- }
- return skipFrame;
-}
-async function compare(config3, input1, input2) {
- const t = {};
- if (!input1 || !input2 || input1.shape.length !== 4 || input1.shape.length !== input2.shape.length) {
- if (!config3.debug)
- log("invalid input tensor or tensor shapes do not match:", input1.shape, input2.shape);
- return 0;
- }
- if (input1.shape[0] !== 1 || input2.shape[0] !== 1 || input1.shape[3] !== 3 || input2.shape[3] !== 3) {
- if (!config3.debug)
- log("input tensors must be of shape [1, height, width, 3]:", input1.shape, input2.shape);
- return 0;
- }
- t.input1 = tfjs_esm_exports.clone(input1);
- t.input2 = input1.shape[1] !== input2.shape[1] || input1.shape[2] !== input2.shape[2] ? tfjs_esm_exports.image.resizeBilinear(input2, [input1.shape[1], input1.shape[2]]) : tfjs_esm_exports.clone(input2);
- t.diff = tfjs_esm_exports.sub(t.input1, t.input2);
- t.squared = tfjs_esm_exports.mul(t.diff, t.diff);
- t.sum = tfjs_esm_exports.sum(t.squared);
- const diffSum = await t.sum.data();
- const diffRelative = diffSum[0] / (input1.shape[1] || 1) / (input1.shape[2] || 1) / 255 / 3;
- tfjs_esm_exports.dispose([t.input1, t.input2, t.diff, t.squared, t.sum]);
- return diffRelative;
-}
-
-// src/util/env.ts
-var Env = class {
- constructor() {
- __publicField(this, "browser");
- __publicField(this, "node");
- __publicField(this, "worker");
- __publicField(this, "platform", "");
- __publicField(this, "agent", "");
- __publicField(this, "backends", []);
- __publicField(this, "initial");
- __publicField(this, "filter");
- __publicField(this, "tfjs");
- __publicField(this, "offscreen");
- __publicField(this, "perfadd", false);
- __publicField(this, "tensorflow", {
- version: void 0,
- gpu: void 0
- });
- __publicField(this, "wasm", {
- supported: void 0,
- backend: void 0,
- simd: void 0,
- multithread: void 0
- });
- __publicField(this, "webgl", {
- supported: void 0,
- backend: void 0,
- version: void 0,
- renderer: void 0
- });
- __publicField(this, "webgpu", {
- supported: void 0,
- backend: void 0,
- adapter: void 0
- });
- __publicField(this, "cpu", {
- model: void 0,
- flags: []
- });
- __publicField(this, "kernels", []);
- __publicField(this, "Canvas");
- __publicField(this, "Image");
- __publicField(this, "ImageData");
- this.browser = typeof navigator !== "undefined";
- this.node = typeof process !== "undefined" && typeof process.versions !== "undefined" && typeof process.versions.node !== "undefined";
- this.tfjs = { version: version8["tfjs-core"] };
- this.offscreen = typeof OffscreenCanvas !== "undefined";
- this.initial = true;
- this.worker = this.browser && this.offscreen ? typeof WorkerGlobalScope !== "undefined" : void 0;
- if (typeof navigator !== "undefined") {
- const raw = navigator.userAgent.match(/\(([^()]+)\)/g);
- if (raw == null ? void 0 : raw[0]) {
- const platformMatch = raw[0].match(/\(([^()]+)\)/g);
- this.platform = (platformMatch == null ? void 0 : platformMatch[0]) ? platformMatch[0].replace(/\(|\)/g, "") : "";
- this.agent = navigator.userAgent.replace(raw[0], "");
- if (this.platform[1])
- this.agent = this.agent.replace(raw[1], "");
- this.agent = this.agent.replace(/ /g, " ");
- }
- } else if (typeof process !== "undefined") {
- this.platform = `${process.platform} ${process.arch}`;
- this.agent = `NodeJS ${process.version}`;
- }
- }
- async updateBackend() {
- this.backends = Object.keys(tfjs_esm_exports.engine().registryFactory);
- this.tensorflow = {
- version: tfjs_esm_exports.backend().binding ? tfjs_esm_exports.backend().binding.TF_Version : void 0,
- gpu: tfjs_esm_exports.backend().binding ? tfjs_esm_exports.backend().binding.isUsingGpuDevice() : void 0
- };
- this.wasm.supported = typeof WebAssembly !== "undefined";
- this.wasm.backend = this.backends.includes("wasm");
- if (this.wasm.supported && this.wasm.backend && tfjs_esm_exports.getBackend() === "wasm") {
- this.wasm.simd = tfjs_esm_exports.env().get("WASM_HAS_SIMD_SUPPORT");
- this.wasm.multithread = tfjs_esm_exports.env().get("WASM_HAS_MULTITHREAD_SUPPORT");
- }
- const c = canvas(100, 100);
- const ctx = c ? c.getContext("webgl2") : void 0;
- this.webgl.supported = typeof ctx !== "undefined";
- this.webgl.backend = this.backends.includes("webgl");
- if (this.webgl.supported && this.webgl.backend && (tfjs_esm_exports.getBackend() === "webgl" || tfjs_esm_exports.getBackend() === "humangl")) {
- const gl = tfjs_esm_exports.backend().gpgpu !== "undefined" ? await tfjs_esm_exports.backend().getGPGPUContext().gl : null;
- if (gl) {
- this.webgl.version = gl.getParameter(gl.VERSION);
- this.webgl.renderer = gl.getParameter(gl.RENDERER);
- }
- }
- this.webgpu.supported = this.browser && typeof navigator.gpu !== "undefined";
- this.webgpu.backend = this.backends.includes("webgpu");
- try {
- if (this.webgpu.supported) {
- const adapter = await navigator.gpu.requestAdapter();
- this.webgpu.adapter = adapter ? adapter.name : void 0;
- }
- } catch (e) {
- this.webgpu.supported = false;
- }
- try {
- this.kernels = tfjs_esm_exports.getKernelsForBackend(tfjs_esm_exports.getBackend()).map((kernel) => kernel.kernelName.toLowerCase());
- } catch (e) {
- }
- }
- updateCPU() {
- const cpu = { model: "", flags: [] };
- if (this.node && this.platform.startsWith("linux")) {
- }
- if (!this.cpu)
- Object.defineProperty(this, "cpu", { value: cpu });
- else
- this.cpu = cpu;
- }
-};
-var env = new Env();
-
-// models/models.json
-var models_exports = {};
-__export(models_exports, {
- age: () => age,
- "anti-spoofing": () => anti_spoofing,
- antispoof: () => antispoof,
- blazeface: () => blazeface,
- "blazeface-back": () => blazeface_back,
- "blazeface-front": () => blazeface_front,
- "blazepose-detect": () => blazepose_detect,
- "blazepose-detector2d": () => blazepose_detector2d,
- "blazepose-detector3d": () => blazepose_detector3d,
- "blazepose-full": () => blazepose_full,
- "blazepose-heavy": () => blazepose_heavy,
- "blazepose-lite": () => blazepose_lite,
- default: () => models_default,
- efficientpose: () => efficientpose,
- "efficientpose-i-lite": () => efficientpose_i_lite,
- "efficientpose-ii-lite": () => efficientpose_ii_lite,
- "efficientpose-iv": () => efficientpose_iv,
- emotion: () => emotion,
- faceboxes: () => faceboxes,
- facemesh: () => facemesh,
- "facemesh-attention": () => facemesh_attention,
- "facemesh-attention-alt": () => facemesh_attention_alt,
- "facemesh-detection-full": () => facemesh_detection_full,
- "facemesh-detection-short": () => facemesh_detection_short,
- "facemesh-orig": () => facemesh_orig,
- faceres: () => faceres,
- "faceres-deep": () => faceres_deep,
- gear: () => gear,
- gender: () => gender,
- "gender-ssrnet-imdb": () => gender_ssrnet_imdb,
- handdetect: () => handdetect,
- "handlandmark-full": () => handlandmark_full,
- "handlandmark-lite": () => handlandmark_lite,
- "handlandmark-sparse": () => handlandmark_sparse,
- handskeleton: () => handskeleton,
- handtrack: () => handtrack,
- "insightface-efficientnet-b0": () => insightface_efficientnet_b0,
- "insightface-ghostnet-strides1": () => insightface_ghostnet_strides1,
- "insightface-ghostnet-strides2": () => insightface_ghostnet_strides2,
- "insightface-mobilenet-emore": () => insightface_mobilenet_emore,
- "insightface-mobilenet-swish": () => insightface_mobilenet_swish,
- iris: () => iris,
- liveness: () => liveness,
- "mb3-centernet": () => mb3_centernet,
- meet: () => meet,
- mobileface: () => mobileface,
- mobilefacenet: () => mobilefacenet,
- models: () => models,
- "movenet-lightning": () => movenet_lightning,
- "movenet-multipose": () => movenet_multipose,
- "movenet-thunder": () => movenet_thunder,
- nanodet: () => nanodet,
- "nanodet-e": () => nanodet_e,
- "nanodet-g": () => nanodet_g,
- "nanodet-m": () => nanodet_m,
- "nanodet-t": () => nanodet_t,
- posenet: () => posenet,
- selfie: () => selfie
-});
-var antispoof = 853098;
-var blazeface = 538928;
-var emotion = 820516;
-var facemesh = 1477958;
-var faceres = 6978814;
-var handlandmark_full = 5431368;
-var handtrack = 2964837;
-var iris = 2599092;
-var liveness = 592976;
-var mb3_centernet = 4030290;
-var models = 0;
-var movenet_lightning = 4650216;
-var selfie = 212886;
-var age = 161240;
-var blazeface_back = 538928;
-var blazeface_front = 402048;
-var blazepose_detector2d = 7499400;
-var blazepose_detector3d = 5928856;
-var blazepose_full = 6338290;
-var blazepose_heavy = 27501554;
-var blazepose_lite = 2725490;
-var efficientpose = 5651240;
-var faceboxes = 2013002;
-var facemesh_attention_alt = 2387598;
-var facemesh_attention = 2382414;
-var facemesh_detection_full = 1026192;
-var facemesh_detection_short = 201268;
-var facemesh_orig = 2955780;
-var faceres_deep = 13957620;
-var gear = 1498916;
-var gender_ssrnet_imdb = 161236;
-var gender = 201808;
-var handdetect = 3515612;
-var handlandmark_lite = 2023432;
-var handlandmark_sparse = 5286322;
-var handskeleton = 5502280;
-var meet = 372228;
-var mobileface = 2183192;
-var mobilefacenet = 5171976;
-var movenet_multipose = 9448838;
-var movenet_thunder = 12477112;
-var nanodet = 7574558;
-var posenet = 5032780;
-var blazepose_detect = 5928804;
-var anti_spoofing = 853098;
-var efficientpose_i_lite = 2269064;
-var efficientpose_ii_lite = 5651240;
-var efficientpose_iv = 25643252;
-var insightface_efficientnet_b0 = 13013224;
-var insightface_ghostnet_strides1 = 8093408;
-var insightface_ghostnet_strides2 = 8049584;
-var insightface_mobilenet_emore = 6938536;
-var insightface_mobilenet_swish = 12168584;
-var nanodet_e = 12319156;
-var nanodet_g = 7574558;
-var nanodet_m = 1887474;
-var nanodet_t = 5294216;
-var models_default = {
- antispoof,
- blazeface,
- emotion,
- facemesh,
- faceres,
- "handlandmark-full": handlandmark_full,
- handtrack,
- iris,
- liveness,
- "mb3-centernet": mb3_centernet,
- models,
- "movenet-lightning": movenet_lightning,
- selfie,
- age,
- "blazeface-back": blazeface_back,
- "blazeface-front": blazeface_front,
- "blazepose-detector2d": blazepose_detector2d,
- "blazepose-detector3d": blazepose_detector3d,
- "blazepose-full": blazepose_full,
- "blazepose-heavy": blazepose_heavy,
- "blazepose-lite": blazepose_lite,
- efficientpose,
- faceboxes,
- "facemesh-attention-alt": facemesh_attention_alt,
- "facemesh-attention": facemesh_attention,
- "facemesh-detection-full": facemesh_detection_full,
- "facemesh-detection-short": facemesh_detection_short,
- "facemesh-orig": facemesh_orig,
- "faceres-deep": faceres_deep,
- gear,
- "gender-ssrnet-imdb": gender_ssrnet_imdb,
- gender,
- handdetect,
- "handlandmark-lite": handlandmark_lite,
- "handlandmark-sparse": handlandmark_sparse,
- handskeleton,
- meet,
- mobileface,
- mobilefacenet,
- "movenet-multipose": movenet_multipose,
- "movenet-thunder": movenet_thunder,
- nanodet,
- posenet,
- "blazepose-detect": blazepose_detect,
- "anti-spoofing": anti_spoofing,
- "efficientpose-i-lite": efficientpose_i_lite,
- "efficientpose-ii-lite": efficientpose_ii_lite,
- "efficientpose-iv": efficientpose_iv,
- "insightface-efficientnet-b0": insightface_efficientnet_b0,
- "insightface-ghostnet-strides1": insightface_ghostnet_strides1,
- "insightface-ghostnet-strides2": insightface_ghostnet_strides2,
- "insightface-mobilenet-emore": insightface_mobilenet_emore,
- "insightface-mobilenet-swish": insightface_mobilenet_swish,
- "nanodet-e": nanodet_e,
- "nanodet-g": nanodet_g,
- "nanodet-m": nanodet_m,
- "nanodet-t": nanodet_t
-};
-
-// src/models.ts
-var models_exports2 = {};
-__export(models_exports2, {
- Models: () => Models,
- getModelStats: () => getModelStats,
- load: () => load20,
- reset: () => reset,
- validate: () => validate2,
- validateModel: () => validateModel
-});
-
-// src/gear/gear.ts
-var model;
-var last2 = [];
-var raceNames = ["white", "black", "asian", "indian", "other"];
-var ageWeights = [15, 23, 28, 35.5, 45.5, 55.5, 65];
-var lastCount = 0;
-var lastTime = 0;
-var skipped = Number.MAX_SAFE_INTEGER;
-async function load(config3) {
- var _a;
- if (env.initial)
- model = null;
- if (!model)
- model = await loadModel((_a = config3.face.gear) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model["modelUrl"]);
- return model;
-}
-async function predict(image26, config3, idx, count2) {
- var _a, _b;
- if (!model)
- return { age: 0, gender: "unknown", genderScore: 0, race: [] };
- const skipFrame = skipped < (((_a = config3.face.gear) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face.gear) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime;
- if (config3.skipAllowed && skipTime && skipFrame && lastCount === count2 && last2[idx]) {
- skipped++;
- return last2[idx];
- }
- skipped = 0;
- return new Promise(async (resolve) => {
- var _a2, _b2;
- if (!(model == null ? void 0 : model.inputs[0].shape))
- return;
- const t = {};
- const box = [[0, 0.1, 0.9, 0.9]];
- t.resize = tfjs_esm_exports.image.cropAndResize(image26, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
- const obj = { age: 0, gender: "unknown", genderScore: 0, race: [] };
- if ((_a2 = config3.face.gear) == null ? void 0 : _a2.enabled)
- [t.age, t.gender, t.race] = model.execute(t.resize, ["age_output", "gender_output", "race_output"]);
- const gender2 = await t.gender.data();
- obj.gender = gender2[0] > gender2[1] ? "male" : "female";
- obj.genderScore = Math.round(100 * (gender2[0] > gender2[1] ? gender2[0] : gender2[1])) / 100;
- const race = await t.race.data();
- for (let i = 0; i < race.length; i++) {
- if (race[i] > (((_b2 = config3.face.gear) == null ? void 0 : _b2.minConfidence) || 0.2))
- obj.race.push({ score: Math.round(100 * race[i]) / 100, race: raceNames[i] });
- }
- obj.race.sort((a, b) => b.score - a.score);
- const ageDistribution = Array.from(await t.age.data());
- const ageSorted = ageDistribution.map((a, i) => [ageWeights[i], a]).sort((a, b) => b[1] - a[1]);
- let age2 = ageSorted[0][0];
- for (let i = 1; i < ageSorted.length; i++)
- age2 += ageSorted[i][1] * (ageSorted[i][0] - age2);
- obj.age = Math.round(10 * age2) / 10;
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- last2[idx] = obj;
- lastCount = count2;
- lastTime = now();
- resolve(obj);
- });
-}
-
-// src/tfjs/constants.ts
-var constants = {
- tf255: 255,
- tf1: 1,
- tf2: 2,
- tf05: 0.5,
- tf127: 127.5,
- rgb: [0.2989, 0.587, 0.114]
-};
-function init() {
- constants.tf255 = tfjs_esm_exports.scalar(255, "float32");
- constants.tf1 = tfjs_esm_exports.scalar(1, "float32");
- constants.tf2 = tfjs_esm_exports.scalar(2, "float32");
- constants.tf05 = tfjs_esm_exports.scalar(0.5, "float32");
- constants.tf127 = tfjs_esm_exports.scalar(127.5, "float32");
- constants.rgb = tfjs_esm_exports.tensor1d([0.2989, 0.587, 0.114], "float32");
-}
-
-// src/gear/ssrnet-age.ts
-var model2;
-var last3 = [];
-var lastCount2 = 0;
-var lastTime2 = 0;
-var skipped2 = Number.MAX_SAFE_INTEGER;
-async function load2(config3) {
- if (env.initial)
- model2 = null;
- if (!model2)
- model2 = await loadModel(config3.face["ssrnet"].modelPathAge);
- else if (config3.debug)
- log("cached model:", model2["modelUrl"]);
- return model2;
-}
-async function predict2(image26, config3, idx, count2) {
- var _a, _b, _c, _d;
- if (!model2)
- return { age: 0 };
- const skipFrame = skipped2 < (((_a = config3.face["ssrnet"]) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face["ssrnet"]) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime2;
- if (config3.skipAllowed && skipFrame && skipTime && lastCount2 === count2 && ((_c = last3[idx]) == null ? void 0 : _c.age) && ((_d = last3[idx]) == null ? void 0 : _d.age) > 0) {
- skipped2++;
- return last3[idx];
- }
- skipped2 = 0;
- return new Promise(async (resolve) => {
- var _a2;
- if (!(model2 == null ? void 0 : model2.inputs) || !model2.inputs[0] || !model2.inputs[0].shape)
- return;
- const t = {};
- t.resize = tfjs_esm_exports.image.resizeBilinear(image26, [model2.inputs[0].shape[2], model2.inputs[0].shape[1]], false);
- t.enhance = tfjs_esm_exports.mul(t.resize, constants.tf255);
- const obj = { age: 0 };
- if ((_a2 = config3.face["ssrnet"]) == null ? void 0 : _a2.enabled)
- t.age = model2.execute(t.enhance);
- if (t.age) {
- const data = await t.age.data();
- obj.age = Math.trunc(10 * data[0]) / 10;
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- last3[idx] = obj;
- lastCount2 = count2;
- lastTime2 = now();
- resolve(obj);
- });
-}
-
-// src/gear/ssrnet-gender.ts
-var model3;
-var last4 = [];
-var lastCount3 = 0;
-var lastTime3 = 0;
-var skipped3 = Number.MAX_SAFE_INTEGER;
-var rgb = [0.2989, 0.587, 0.114];
-async function load3(config3) {
- var _a;
- if (env.initial)
- model3 = null;
- if (!model3)
- model3 = await loadModel((_a = config3.face["ssrnet"]) == null ? void 0 : _a.modelPathGender);
- else if (config3.debug)
- log("cached model:", model3["modelUrl"]);
- return model3;
-}
-async function predict3(image26, config3, idx, count2) {
- var _a, _b, _c, _d;
- if (!model3)
- return { gender: "unknown", genderScore: 0 };
- const skipFrame = skipped3 < (((_a = config3.face["ssrnet"]) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face["ssrnet"]) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime3;
- if (config3.skipAllowed && skipFrame && skipTime && lastCount3 === count2 && ((_c = last4[idx]) == null ? void 0 : _c.gender) && ((_d = last4[idx]) == null ? void 0 : _d.genderScore) > 0) {
- skipped3++;
- return last4[idx];
- }
- skipped3 = 0;
- return new Promise(async (resolve) => {
- var _a2;
- if (!(model3 == null ? void 0 : model3.inputs[0].shape))
- return;
- const t = {};
- t.resize = tfjs_esm_exports.image.resizeBilinear(image26, [model3.inputs[0].shape[2], model3.inputs[0].shape[1]], false);
- t.enhance = tfjs_esm_exports.tidy(() => {
- const [red, green, blue] = tfjs_esm_exports.split(t.resize, 3, 3);
- const redNorm = tfjs_esm_exports.mul(red, rgb[0]);
- const greenNorm = tfjs_esm_exports.mul(green, rgb[1]);
- const blueNorm = tfjs_esm_exports.mul(blue, rgb[2]);
- const grayscale = tfjs_esm_exports.addN([redNorm, greenNorm, blueNorm]);
- const normalize = tfjs_esm_exports.mul(tfjs_esm_exports.sub(grayscale, constants.tf05), 2);
- return normalize;
- });
- const obj = { gender: "unknown", genderScore: 0 };
- if ((_a2 = config3.face["ssrnet"]) == null ? void 0 : _a2.enabled)
- t.gender = model3.execute(t.enhance);
- const data = await t.gender.data();
- obj.gender = data[0] > data[1] ? "female" : "male";
- obj.genderScore = data[0] > data[1] ? Math.trunc(100 * data[0]) / 100 : Math.trunc(100 * data[1]) / 100;
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- last4[idx] = obj;
- lastCount3 = count2;
- lastTime3 = now();
- resolve(obj);
- });
-}
-
-// src/face/antispoof.ts
-var model4;
-var cached = [];
-var skipped4 = Number.MAX_SAFE_INTEGER;
-var lastCount4 = 0;
-var lastTime4 = 0;
-async function load4(config3) {
- var _a;
- if (env.initial)
- model4 = null;
- if (!model4)
- model4 = await loadModel((_a = config3.face.antispoof) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model4["modelUrl"]);
- return model4;
-}
-async function predict4(image26, config3, idx, count2) {
- var _a, _b;
- if (!model4 || !(model4 == null ? void 0 : model4["executor"]))
- return 0;
- const skipTime = (((_a = config3.face.antispoof) == null ? void 0 : _a.skipTime) || 0) > now() - lastTime4;
- const skipFrame = skipped4 < (((_b = config3.face.antispoof) == null ? void 0 : _b.skipFrames) || 0);
- if (config3.skipAllowed && skipTime && skipFrame && lastCount4 === count2 && cached[idx]) {
- skipped4++;
- return cached[idx];
- }
- skipped4 = 0;
- return new Promise(async (resolve) => {
- const resize = tfjs_esm_exports.image.resizeBilinear(image26, [(model4 == null ? void 0 : model4.inputs[0].shape) ? model4.inputs[0].shape[2] : 0, (model4 == null ? void 0 : model4.inputs[0].shape) ? model4.inputs[0].shape[1] : 0], false);
- const res = model4 == null ? void 0 : model4.execute(resize);
- const num = (await res.data())[0];
- cached[idx] = Math.round(100 * num) / 100;
- lastCount4 = count2;
- lastTime4 = now();
- tfjs_esm_exports.dispose([resize, res]);
- resolve(cached[idx]);
- });
-}
-
-// src/face/facemeshcoords.ts
-var meshAnnotations = {
- silhouette: [
- 10,
- 338,
- 297,
- 332,
- 284,
- 251,
- 389,
- 356,
- 454,
- 323,
- 361,
- 288,
- 397,
- 365,
- 379,
- 378,
- 400,
- 377,
- 152,
- 148,
- 176,
- 149,
- 150,
- 136,
- 172,
- 58,
- 132,
- 93,
- 234,
- 127,
- 162,
- 21,
- 54,
- 103,
- 67,
- 109
- ],
- lipsUpperOuter: [185, 40, 39, 37, 0, 267, 269, 270, 409],
- lipsLowerOuter: [61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291],
- lipsUpperInner: [191, 80, 81, 82, 13, 312, 311, 310, 415],
- lipsLowerInner: [78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308],
- lipsLowerSemiOuter: [76, 77, 90, 180, 85, 16, 315, 404, 320, 307, 306],
- lipsUpperSemiOuter: [184, 74, 73, 72, 11, 302, 303, 304, 408],
- lipsLowerSemiInner: [62, 96, 89, 179, 86, 15, 316, 403, 319, 325, 292],
- lipsUpperSemiInner: [183, 42, 41, 38, 12, 268, 271, 272, 407],
- rightEyeUpper0: [246, 161, 160, 159, 158, 157, 173],
- rightEyeLower0: [33, 7, 163, 144, 145, 153, 154, 155, 133],
- rightEyeUpper1: [247, 30, 29, 27, 28, 56, 190],
- rightEyeLower1: [130, 25, 110, 24, 23, 22, 26, 112, 243],
- rightEyeUpper2: [113, 225, 224, 223, 222, 221, 189],
- rightEyeLower2: [226, 31, 228, 229, 230, 231, 232, 233, 244],
- rightEyeLower3: [143, 111, 117, 118, 119, 120, 121, 128, 245],
- rightEyebrowUpper: [156, 70, 63, 105, 66, 107, 55, 193],
- rightEyebrowLower: [35, 124, 46, 53, 52, 65],
- rightEyeIris: [473, 474, 475, 476, 477],
- leftEyeUpper0: [466, 388, 387, 386, 385, 384, 398],
- leftEyeLower0: [263, 249, 390, 373, 374, 380, 381, 382, 362],
- leftEyeUpper1: [467, 260, 259, 257, 258, 286, 414],
- leftEyeLower1: [359, 255, 339, 254, 253, 252, 256, 341, 463],
- leftEyeUpper2: [342, 445, 444, 443, 442, 441, 413],
- leftEyeLower2: [446, 261, 448, 449, 450, 451, 452, 453, 464],
- leftEyeLower3: [372, 340, 346, 347, 348, 349, 350, 357, 465],
- leftEyebrowUpper: [383, 300, 293, 334, 296, 336, 285, 417],
- leftEyebrowLower: [265, 353, 276, 283, 282, 295],
- leftEyeIris: [468, 469, 470, 471, 472],
- midwayBetweenEyes: [168],
- noseTip: [1],
- noseBottom: [2],
- noseRightCorner: [98],
- noseLeftCorner: [327],
- rightCheek: [205],
- leftCheek: [425]
-};
-var meshLandmarks = {
- count: 468,
- mouth: 13,
- symmetryLine: [13, meshAnnotations.midwayBetweenEyes[0]]
-};
-var blazeFaceLandmarks = {
- leftEye: 0,
- rightEye: 1,
- nose: 2,
- mouth: 3,
- leftEar: 4,
- rightEar: 5,
- symmetryLine: [3, 2]
-};
-var irisIndices = [
- { key: "EyeUpper0", indices: [9, 10, 11, 12, 13, 14, 15] },
- { key: "EyeUpper1", indices: [25, 26, 27, 28, 29, 30, 31] },
- { key: "EyeUpper2", indices: [41, 42, 43, 44, 45, 46, 47] },
- { key: "EyeLower0", indices: [0, 1, 2, 3, 4, 5, 6, 7, 8] },
- { key: "EyeLower1", indices: [16, 17, 18, 19, 20, 21, 22, 23, 24] },
- { key: "EyeLower2", indices: [32, 33, 34, 35, 36, 37, 38, 39, 40] },
- { key: "EyeLower3", indices: [54, 55, 56, 57, 58, 59, 60, 61, 62] },
- { key: "EyebrowUpper", indices: [63, 64, 65, 66, 67, 68, 69, 70] },
- { key: "EyebrowLower", indices: [48, 49, 50, 51, 52, 53] }
-];
-var UV468 = [
- [0.499976992607117, 0.652534008026123],
- [0.500025987625122, 0.547487020492554],
- [0.499974012374878, 0.602371990680695],
- [0.482113003730774, 0.471979022026062],
- [0.500150978565216, 0.527155995368958],
- [0.499909996986389, 0.498252987861633],
- [0.499523013830185, 0.40106201171875],
- [0.289712011814117, 0.380764007568359],
- [0.499954998493195, 0.312398016452789],
- [0.499987006187439, 0.269918978214264],
- [0.500023007392883, 0.107050001621246],
- [0.500023007392883, 0.666234016418457],
- [0.5000159740448, 0.679224014282227],
- [0.500023007392883, 0.692348003387451],
- [0.499976992607117, 0.695277988910675],
- [0.499976992607117, 0.70593398809433],
- [0.499976992607117, 0.719385027885437],
- [0.499976992607117, 0.737019002437592],
- [0.499967992305756, 0.781370997428894],
- [0.499816000461578, 0.562981009483337],
- [0.473773002624512, 0.573909997940063],
- [0.104906998574734, 0.254140973091125],
- [0.365929991006851, 0.409575998783112],
- [0.338757991790771, 0.41302502155304],
- [0.311120003461838, 0.409460008144379],
- [0.274657994508743, 0.389131009578705],
- [0.393361985683441, 0.403706014156342],
- [0.345234006643295, 0.344011008739471],
- [0.370094001293182, 0.346076011657715],
- [0.319321990013123, 0.347265005111694],
- [0.297903001308441, 0.353591024875641],
- [0.24779200553894, 0.410809993743896],
- [0.396889001131058, 0.842755019664764],
- [0.280097991228104, 0.375599980354309],
- [0.106310002505779, 0.399955987930298],
- [0.2099249958992, 0.391353011131287],
- [0.355807989835739, 0.534406006336212],
- [0.471751004457474, 0.65040397644043],
- [0.474155008792877, 0.680191993713379],
- [0.439785003662109, 0.657229006290436],
- [0.414617002010345, 0.66654098033905],
- [0.450374007225037, 0.680860996246338],
- [0.428770989179611, 0.682690978050232],
- [0.374971002340317, 0.727805018424988],
- [0.486716985702515, 0.547628998756409],
- [0.485300987958908, 0.527395009994507],
- [0.257764995098114, 0.314490020275116],
- [0.401223003864288, 0.455172002315521],
- [0.429818987846375, 0.548614978790283],
- [0.421351999044418, 0.533740997314453],
- [0.276895999908447, 0.532056987285614],
- [0.483370006084442, 0.499586999416351],
- [0.33721199631691, 0.282882988452911],
- [0.296391993761063, 0.293242990970612],
- [0.169294998049736, 0.193813979625702],
- [0.447580009698868, 0.302609980106354],
- [0.392390012741089, 0.353887975215912],
- [0.354490011930466, 0.696784019470215],
- [0.067304998636246, 0.730105042457581],
- [0.442739009857178, 0.572826027870178],
- [0.457098007202148, 0.584792017936707],
- [0.381974011659622, 0.694710969924927],
- [0.392388999462128, 0.694203019142151],
- [0.277076005935669, 0.271932005882263],
- [0.422551989555359, 0.563233017921448],
- [0.385919004678726, 0.281364023685455],
- [0.383103013038635, 0.255840003490448],
- [0.331431001424789, 0.119714021682739],
- [0.229923993349075, 0.232002973556519],
- [0.364500999450684, 0.189113974571228],
- [0.229622006416321, 0.299540996551514],
- [0.173287004232407, 0.278747975826263],
- [0.472878992557526, 0.666198015213013],
- [0.446828007698059, 0.668527007102966],
- [0.422762006521225, 0.673889994621277],
- [0.445307999849319, 0.580065965652466],
- [0.388103008270264, 0.693961024284363],
- [0.403039008378983, 0.706539988517761],
- [0.403629004955292, 0.693953037261963],
- [0.460041999816895, 0.557139039039612],
- [0.431158006191254, 0.692366003990173],
- [0.452181994915009, 0.692366003990173],
- [0.475387006998062, 0.692366003990173],
- [0.465828001499176, 0.779190003871918],
- [0.472328990697861, 0.736225962638855],
- [0.473087012767792, 0.717857003211975],
- [0.473122000694275, 0.704625964164734],
- [0.473033010959625, 0.695277988910675],
- [0.427942007780075, 0.695277988910675],
- [0.426479011774063, 0.703539967536926],
- [0.423162013292313, 0.711845993995667],
- [0.4183090031147, 0.720062971115112],
- [0.390094995498657, 0.639572978019714],
- [0.013953999616206, 0.560034036636353],
- [0.499913990497589, 0.58014702796936],
- [0.413199990987778, 0.69539999961853],
- [0.409626007080078, 0.701822996139526],
- [0.468080013990402, 0.601534962654114],
- [0.422728985548019, 0.585985004901886],
- [0.463079988956451, 0.593783974647522],
- [0.37211999297142, 0.47341400384903],
- [0.334562003612518, 0.496073007583618],
- [0.411671012639999, 0.546965003013611],
- [0.242175996303558, 0.14767599105835],
- [0.290776997804642, 0.201445996761322],
- [0.327338010072708, 0.256527006626129],
- [0.399509996175766, 0.748921036720276],
- [0.441727995872498, 0.261676013469696],
- [0.429764986038208, 0.187834024429321],
- [0.412198007106781, 0.108901023864746],
- [0.288955003023148, 0.398952007293701],
- [0.218936994671822, 0.435410976409912],
- [0.41278201341629, 0.398970007896423],
- [0.257135003805161, 0.355440020561218],
- [0.427684992551804, 0.437960982322693],
- [0.448339998722076, 0.536936044692993],
- [0.178560003638268, 0.45755398273468],
- [0.247308000922203, 0.457193970680237],
- [0.286267012357712, 0.467674970626831],
- [0.332827985286713, 0.460712015628815],
- [0.368755996227264, 0.447206974029541],
- [0.398963987827301, 0.432654976844788],
- [0.476410001516342, 0.405806005001068],
- [0.189241006970406, 0.523923993110657],
- [0.228962004184723, 0.348950982093811],
- [0.490725994110107, 0.562400996685028],
- [0.404670000076294, 0.485132992267609],
- [0.019469000399113, 0.401564002037048],
- [0.426243007183075, 0.420431017875671],
- [0.396993011236191, 0.548797011375427],
- [0.266469985246658, 0.376977026462555],
- [0.439121007919312, 0.51895797252655],
- [0.032313998788595, 0.644356966018677],
- [0.419054001569748, 0.387154996395111],
- [0.462783008813858, 0.505746960639954],
- [0.238978996872902, 0.779744982719421],
- [0.198220998048782, 0.831938028335571],
- [0.107550002634525, 0.540755033493042],
- [0.183610007166862, 0.740257024765015],
- [0.134409993886948, 0.333683013916016],
- [0.385764002799988, 0.883153975009918],
- [0.490967005491257, 0.579378008842468],
- [0.382384985685349, 0.508572995662689],
- [0.174399003386497, 0.397670984268188],
- [0.318785011768341, 0.39623498916626],
- [0.343364000320435, 0.400596976280212],
- [0.396100014448166, 0.710216999053955],
- [0.187885001301765, 0.588537991046906],
- [0.430987000465393, 0.944064974784851],
- [0.318993002176285, 0.898285031318665],
- [0.266247987747192, 0.869701027870178],
- [0.500023007392883, 0.190576016902924],
- [0.499976992607117, 0.954452991485596],
- [0.366169989109039, 0.398822009563446],
- [0.393207013607025, 0.39553701877594],
- [0.410373002290726, 0.391080021858215],
- [0.194993004202843, 0.342101991176605],
- [0.388664990663528, 0.362284004688263],
- [0.365961998701096, 0.355970978736877],
- [0.343364000320435, 0.355356991291046],
- [0.318785011768341, 0.35834002494812],
- [0.301414996385574, 0.363156020641327],
- [0.058132998645306, 0.319076001644135],
- [0.301414996385574, 0.387449026107788],
- [0.499987989664078, 0.618434011936188],
- [0.415838003158569, 0.624195992946625],
- [0.445681989192963, 0.566076993942261],
- [0.465844005346298, 0.620640993118286],
- [0.49992299079895, 0.351523995399475],
- [0.288718998432159, 0.819945991039276],
- [0.335278987884521, 0.852819979190826],
- [0.440512001514435, 0.902418971061707],
- [0.128294005990028, 0.791940987110138],
- [0.408771991729736, 0.373893976211548],
- [0.455606997013092, 0.451801002025604],
- [0.499877005815506, 0.908990025520325],
- [0.375436991453171, 0.924192011356354],
- [0.11421000212431, 0.615022003650665],
- [0.448662012815475, 0.695277988910675],
- [0.4480200111866, 0.704632043838501],
- [0.447111994028091, 0.715808033943176],
- [0.444831997156143, 0.730794012546539],
- [0.430011987686157, 0.766808986663818],
- [0.406787008047104, 0.685672998428345],
- [0.400738000869751, 0.681069016456604],
- [0.392399996519089, 0.677703022956848],
- [0.367855995893478, 0.663918972015381],
- [0.247923001646996, 0.601333022117615],
- [0.452769994735718, 0.420849978923798],
- [0.43639200925827, 0.359887003898621],
- [0.416164010763168, 0.368713974952698],
- [0.413385987281799, 0.692366003990173],
- [0.228018000721931, 0.683571994304657],
- [0.468268007040024, 0.352671027183533],
- [0.411361992359161, 0.804327011108398],
- [0.499989002943039, 0.469825029373169],
- [0.479153990745544, 0.442654013633728],
- [0.499974012374878, 0.439637005329132],
- [0.432112008333206, 0.493588984012604],
- [0.499886006116867, 0.866917014122009],
- [0.49991300702095, 0.821729004383087],
- [0.456548988819122, 0.819200992584229],
- [0.344549000263214, 0.745438992977142],
- [0.37890899181366, 0.574010014533997],
- [0.374292999505997, 0.780184984207153],
- [0.319687992334366, 0.570737957954407],
- [0.357154995203018, 0.604269981384277],
- [0.295284003019333, 0.621580958366394],
- [0.447750002145767, 0.862477004528046],
- [0.410986006259918, 0.508723020553589],
- [0.31395098567009, 0.775308012962341],
- [0.354128003120422, 0.812552988529205],
- [0.324548006057739, 0.703992962837219],
- [0.189096003770828, 0.646299958229065],
- [0.279776990413666, 0.71465802192688],
- [0.1338230073452, 0.682700991630554],
- [0.336768001317978, 0.644733011722565],
- [0.429883986711502, 0.466521978378296],
- [0.455527991056442, 0.548622965812683],
- [0.437114000320435, 0.558896005153656],
- [0.467287987470627, 0.529924988746643],
- [0.414712011814117, 0.335219979286194],
- [0.37704598903656, 0.322777986526489],
- [0.344107985496521, 0.320150971412659],
- [0.312875986099243, 0.32233202457428],
- [0.283526003360748, 0.333190023899078],
- [0.241245999932289, 0.382785975933075],
- [0.102986000478268, 0.468762993812561],
- [0.267612010240555, 0.424560010433197],
- [0.297879010438919, 0.433175981044769],
- [0.333433985710144, 0.433878004550934],
- [0.366427004337311, 0.426115989685059],
- [0.396012008190155, 0.416696012020111],
- [0.420121014118195, 0.41022801399231],
- [0.007561000064015, 0.480777025222778],
- [0.432949006557465, 0.569517970085144],
- [0.458638995885849, 0.479089021682739],
- [0.473466008901596, 0.545744001865387],
- [0.476087987422943, 0.563830018043518],
- [0.468472003936768, 0.555056989192963],
- [0.433990985155106, 0.582361996173859],
- [0.483518004417419, 0.562983989715576],
- [0.482482999563217, 0.57784903049469],
- [0.42645001411438, 0.389798998832703],
- [0.438998997211456, 0.39649498462677],
- [0.450067013502121, 0.400434017181396],
- [0.289712011814117, 0.368252992630005],
- [0.276670008897781, 0.363372981548309],
- [0.517862021923065, 0.471948027610779],
- [0.710287988185883, 0.380764007568359],
- [0.526226997375488, 0.573909997940063],
- [0.895093023777008, 0.254140973091125],
- [0.634069979190826, 0.409575998783112],
- [0.661242008209229, 0.41302502155304],
- [0.688880026340485, 0.409460008144379],
- [0.725341975688934, 0.389131009578705],
- [0.606630027294159, 0.40370500087738],
- [0.654766023159027, 0.344011008739471],
- [0.629905998706818, 0.346076011657715],
- [0.680678009986877, 0.347265005111694],
- [0.702096998691559, 0.353591024875641],
- [0.75221198797226, 0.410804986953735],
- [0.602918028831482, 0.842862963676453],
- [0.719901978969574, 0.375599980354309],
- [0.893692970275879, 0.399959981441498],
- [0.790081977844238, 0.391354024410248],
- [0.643998026847839, 0.534487962722778],
- [0.528249025344849, 0.65040397644043],
- [0.525849997997284, 0.680191040039062],
- [0.560214996337891, 0.657229006290436],
- [0.585384011268616, 0.66654098033905],
- [0.549625992774963, 0.680860996246338],
- [0.57122802734375, 0.682691991329193],
- [0.624852001667023, 0.72809898853302],
- [0.513050019741058, 0.547281980514526],
- [0.51509702205658, 0.527251958847046],
- [0.742246985435486, 0.314507007598877],
- [0.598631024360657, 0.454979002475739],
- [0.570338010787964, 0.548575043678284],
- [0.578631997108459, 0.533622980117798],
- [0.723087012767792, 0.532054007053375],
- [0.516445994377136, 0.499638974666595],
- [0.662801027297974, 0.282917976379395],
- [0.70362401008606, 0.293271005153656],
- [0.830704987049103, 0.193813979625702],
- [0.552385985851288, 0.302568018436432],
- [0.607609987258911, 0.353887975215912],
- [0.645429015159607, 0.696707010269165],
- [0.932694971561432, 0.730105042457581],
- [0.557260990142822, 0.572826027870178],
- [0.542901992797852, 0.584792017936707],
- [0.6180260181427, 0.694710969924927],
- [0.607590973377228, 0.694203019142151],
- [0.722943007946014, 0.271963000297546],
- [0.577413976192474, 0.563166975975037],
- [0.614082992076874, 0.281386971473694],
- [0.616907000541687, 0.255886018276215],
- [0.668509006500244, 0.119913995265961],
- [0.770092010498047, 0.232020974159241],
- [0.635536015033722, 0.189248979091644],
- [0.77039098739624, 0.299556016921997],
- [0.826722025871277, 0.278755009174347],
- [0.527121007442474, 0.666198015213013],
- [0.553171992301941, 0.668527007102966],
- [0.577238023281097, 0.673889994621277],
- [0.554691970348358, 0.580065965652466],
- [0.611896991729736, 0.693961024284363],
- [0.59696102142334, 0.706539988517761],
- [0.596370995044708, 0.693953037261963],
- [0.539958000183105, 0.557139039039612],
- [0.568841993808746, 0.692366003990173],
- [0.547818005084991, 0.692366003990173],
- [0.52461302280426, 0.692366003990173],
- [0.534089982509613, 0.779141008853912],
- [0.527670979499817, 0.736225962638855],
- [0.526912987232208, 0.717857003211975],
- [0.526877999305725, 0.704625964164734],
- [0.526966989040375, 0.695277988910675],
- [0.572058022022247, 0.695277988910675],
- [0.573521018028259, 0.703539967536926],
- [0.57683801651001, 0.711845993995667],
- [0.581691026687622, 0.720062971115112],
- [0.609944999217987, 0.639909982681274],
- [0.986046016216278, 0.560034036636353],
- [0.5867999792099, 0.69539999961853],
- [0.590372025966644, 0.701822996139526],
- [0.531915009021759, 0.601536989212036],
- [0.577268004417419, 0.585934996604919],
- [0.536915004253387, 0.593786001205444],
- [0.627542972564697, 0.473352015018463],
- [0.665585994720459, 0.495950996875763],
- [0.588353991508484, 0.546862006187439],
- [0.757824003696442, 0.14767599105835],
- [0.709249973297119, 0.201507985591888],
- [0.672684013843536, 0.256581008434296],
- [0.600408971309662, 0.74900496006012],
- [0.55826598405838, 0.261672019958496],
- [0.570303976535797, 0.187870979309082],
- [0.588165998458862, 0.109044015407562],
- [0.711045026779175, 0.398952007293701],
- [0.781069993972778, 0.435405015945435],
- [0.587247014045715, 0.398931980133057],
- [0.742869973182678, 0.355445981025696],
- [0.572156012058258, 0.437651991844177],
- [0.55186802148819, 0.536570012569427],
- [0.821442008018494, 0.457556009292603],
- [0.752701997756958, 0.457181990146637],
- [0.71375697851181, 0.467626988887787],
- [0.66711300611496, 0.460672974586487],
- [0.631101012229919, 0.447153985500336],
- [0.6008620262146, 0.432473003864288],
- [0.523481011390686, 0.405627012252808],
- [0.810747981071472, 0.523926019668579],
- [0.771045982837677, 0.348959028720856],
- [0.509127020835876, 0.562718033790588],
- [0.595292985439301, 0.485023975372314],
- [0.980530977249146, 0.401564002037048],
- [0.573499977588654, 0.420000016689301],
- [0.602994978427887, 0.548687994480133],
- [0.733529984951019, 0.376977026462555],
- [0.560611009597778, 0.519016981124878],
- [0.967685997486115, 0.644356966018677],
- [0.580985009670258, 0.387160003185272],
- [0.537728011608124, 0.505385041236877],
- [0.760966002941132, 0.779752969741821],
- [0.801778972148895, 0.831938028335571],
- [0.892440974712372, 0.54076099395752],
- [0.816350996494293, 0.740260004997253],
- [0.865594983100891, 0.333687007427216],
- [0.614073991775513, 0.883246004581451],
- [0.508952975273132, 0.579437971115112],
- [0.617941975593567, 0.508316040039062],
- [0.825608015060425, 0.397674977779388],
- [0.681214988231659, 0.39623498916626],
- [0.656635999679565, 0.400596976280212],
- [0.603900015354156, 0.710216999053955],
- [0.81208598613739, 0.588539004325867],
- [0.56801301240921, 0.944564998149872],
- [0.681007981300354, 0.898285031318665],
- [0.733752012252808, 0.869701027870178],
- [0.633830010890961, 0.398822009563446],
- [0.606792986392975, 0.39553701877594],
- [0.589659988880157, 0.391062021255493],
- [0.805015981197357, 0.342108011245728],
- [0.611334979534149, 0.362284004688263],
- [0.634037971496582, 0.355970978736877],
- [0.656635999679565, 0.355356991291046],
- [0.681214988231659, 0.35834002494812],
- [0.698584973812103, 0.363156020641327],
- [0.941866993904114, 0.319076001644135],
- [0.698584973812103, 0.387449026107788],
- [0.584177017211914, 0.624107003211975],
- [0.554318010807037, 0.566076993942261],
- [0.534153997898102, 0.62064003944397],
- [0.711217999458313, 0.819975018501282],
- [0.664629995822906, 0.852871000766754],
- [0.559099972248077, 0.902631998062134],
- [0.871706008911133, 0.791940987110138],
- [0.591234028339386, 0.373893976211548],
- [0.544341027736664, 0.451583981513977],
- [0.624562978744507, 0.924192011356354],
- [0.88577002286911, 0.615028977394104],
- [0.551338016986847, 0.695277988910675],
- [0.551980018615723, 0.704632043838501],
- [0.552887976169586, 0.715808033943176],
- [0.555167973041534, 0.730794012546539],
- [0.569944024085999, 0.767035007476807],
- [0.593203008174896, 0.685675978660583],
- [0.599261999130249, 0.681069016456604],
- [0.607599973678589, 0.677703022956848],
- [0.631937980651855, 0.663500010967255],
- [0.752032995223999, 0.601315021514893],
- [0.547226011753082, 0.420395016670227],
- [0.563543975353241, 0.359827995300293],
- [0.583841025829315, 0.368713974952698],
- [0.586614012718201, 0.692366003990173],
- [0.771915018558502, 0.683578014373779],
- [0.531597018241882, 0.352482974529266],
- [0.588370978832245, 0.804440975189209],
- [0.52079701423645, 0.442565023899078],
- [0.567984998226166, 0.493479013442993],
- [0.543282985687256, 0.819254994392395],
- [0.655317008495331, 0.745514988899231],
- [0.621008992195129, 0.574018001556396],
- [0.625559985637665, 0.78031200170517],
- [0.680198013782501, 0.570719003677368],
- [0.64276397228241, 0.604337990283966],
- [0.704662978649139, 0.621529996395111],
- [0.552012026309967, 0.862591981887817],
- [0.589071989059448, 0.508637011051178],
- [0.685944974422455, 0.775357007980347],
- [0.645735025405884, 0.812640011310577],
- [0.675342977046967, 0.703978002071381],
- [0.810858011245728, 0.646304965019226],
- [0.72012197971344, 0.714666962623596],
- [0.866151988506317, 0.682704985141754],
- [0.663187026977539, 0.644596993923187],
- [0.570082008838654, 0.466325998306274],
- [0.544561982154846, 0.548375964164734],
- [0.562758982181549, 0.558784961700439],
- [0.531987011432648, 0.530140042304993],
- [0.585271000862122, 0.335177004337311],
- [0.622952997684479, 0.32277899980545],
- [0.655896008014679, 0.320163011550903],
- [0.687132000923157, 0.322345972061157],
- [0.716481983661652, 0.333200991153717],
- [0.758756995201111, 0.382786989212036],
- [0.897013008594513, 0.468769013881683],
- [0.732392013072968, 0.424547016620636],
- [0.70211398601532, 0.433162987232208],
- [0.66652500629425, 0.433866024017334],
- [0.633504986763, 0.426087975502014],
- [0.603875994682312, 0.416586995124817],
- [0.579657971858978, 0.409945011138916],
- [0.992439985275269, 0.480777025222778],
- [0.567192018032074, 0.569419980049133],
- [0.54136598110199, 0.478899002075195],
- [0.526564002037048, 0.546118021011353],
- [0.523913025856018, 0.563830018043518],
- [0.531529009342194, 0.555056989192963],
- [0.566035985946655, 0.582329034805298],
- [0.51631098985672, 0.563053965568542],
- [0.5174720287323, 0.577877044677734],
- [0.573594987392426, 0.389806985855103],
- [0.560697972774506, 0.395331978797913],
- [0.549755990505219, 0.399751007556915],
- [0.710287988185883, 0.368252992630005],
- [0.723330020904541, 0.363372981548309]
-];
-var TRI468 = [
- 127,
- 34,
- 139,
- 11,
- 0,
- 37,
- 232,
- 231,
- 120,
- 72,
- 37,
- 39,
- 128,
- 121,
- 47,
- 232,
- 121,
- 128,
- 104,
- 69,
- 67,
- 175,
- 171,
- 148,
- 157,
- 154,
- 155,
- 118,
- 50,
- 101,
- 73,
- 39,
- 40,
- 9,
- 151,
- 108,
- 48,
- 115,
- 131,
- 194,
- 204,
- 211,
- 74,
- 40,
- 185,
- 80,
- 42,
- 183,
- 40,
- 92,
- 186,
- 230,
- 229,
- 118,
- 202,
- 212,
- 214,
- 83,
- 18,
- 17,
- 76,
- 61,
- 146,
- 160,
- 29,
- 30,
- 56,
- 157,
- 173,
- 106,
- 204,
- 194,
- 135,
- 214,
- 192,
- 203,
- 165,
- 98,
- 21,
- 71,
- 68,
- 51,
- 45,
- 4,
- 144,
- 24,
- 23,
- 77,
- 146,
- 91,
- 205,
- 50,
- 187,
- 201,
- 200,
- 18,
- 91,
- 106,
- 182,
- 90,
- 91,
- 181,
- 85,
- 84,
- 17,
- 206,
- 203,
- 36,
- 148,
- 171,
- 140,
- 92,
- 40,
- 39,
- 193,
- 189,
- 244,
- 159,
- 158,
- 28,
- 247,
- 246,
- 161,
- 236,
- 3,
- 196,
- 54,
- 68,
- 104,
- 193,
- 168,
- 8,
- 117,
- 228,
- 31,
- 189,
- 193,
- 55,
- 98,
- 97,
- 99,
- 126,
- 47,
- 100,
- 166,
- 79,
- 218,
- 155,
- 154,
- 26,
- 209,
- 49,
- 131,
- 135,
- 136,
- 150,
- 47,
- 126,
- 217,
- 223,
- 52,
- 53,
- 45,
- 51,
- 134,
- 211,
- 170,
- 140,
- 67,
- 69,
- 108,
- 43,
- 106,
- 91,
- 230,
- 119,
- 120,
- 226,
- 130,
- 247,
- 63,
- 53,
- 52,
- 238,
- 20,
- 242,
- 46,
- 70,
- 156,
- 78,
- 62,
- 96,
- 46,
- 53,
- 63,
- 143,
- 34,
- 227,
- 173,
- 155,
- 133,
- 123,
- 117,
- 111,
- 44,
- 125,
- 19,
- 236,
- 134,
- 51,
- 216,
- 206,
- 205,
- 154,
- 153,
- 22,
- 39,
- 37,
- 167,
- 200,
- 201,
- 208,
- 36,
- 142,
- 100,
- 57,
- 212,
- 202,
- 20,
- 60,
- 99,
- 28,
- 158,
- 157,
- 35,
- 226,
- 113,
- 160,
- 159,
- 27,
- 204,
- 202,
- 210,
- 113,
- 225,
- 46,
- 43,
- 202,
- 204,
- 62,
- 76,
- 77,
- 137,
- 123,
- 116,
- 41,
- 38,
- 72,
- 203,
- 129,
- 142,
- 64,
- 98,
- 240,
- 49,
- 102,
- 64,
- 41,
- 73,
- 74,
- 212,
- 216,
- 207,
- 42,
- 74,
- 184,
- 169,
- 170,
- 211,
- 170,
- 149,
- 176,
- 105,
- 66,
- 69,
- 122,
- 6,
- 168,
- 123,
- 147,
- 187,
- 96,
- 77,
- 90,
- 65,
- 55,
- 107,
- 89,
- 90,
- 180,
- 101,
- 100,
- 120,
- 63,
- 105,
- 104,
- 93,
- 137,
- 227,
- 15,
- 86,
- 85,
- 129,
- 102,
- 49,
- 14,
- 87,
- 86,
- 55,
- 8,
- 9,
- 100,
- 47,
- 121,
- 145,
- 23,
- 22,
- 88,
- 89,
- 179,
- 6,
- 122,
- 196,
- 88,
- 95,
- 96,
- 138,
- 172,
- 136,
- 215,
- 58,
- 172,
- 115,
- 48,
- 219,
- 42,
- 80,
- 81,
- 195,
- 3,
- 51,
- 43,
- 146,
- 61,
- 171,
- 175,
- 199,
- 81,
- 82,
- 38,
- 53,
- 46,
- 225,
- 144,
- 163,
- 110,
- 246,
- 33,
- 7,
- 52,
- 65,
- 66,
- 229,
- 228,
- 117,
- 34,
- 127,
- 234,
- 107,
- 108,
- 69,
- 109,
- 108,
- 151,
- 48,
- 64,
- 235,
- 62,
- 78,
- 191,
- 129,
- 209,
- 126,
- 111,
- 35,
- 143,
- 163,
- 161,
- 246,
- 117,
- 123,
- 50,
- 222,
- 65,
- 52,
- 19,
- 125,
- 141,
- 221,
- 55,
- 65,
- 3,
- 195,
- 197,
- 25,
- 7,
- 33,
- 220,
- 237,
- 44,
- 70,
- 71,
- 139,
- 122,
- 193,
- 245,
- 247,
- 130,
- 33,
- 71,
- 21,
- 162,
- 153,
- 158,
- 159,
- 170,
- 169,
- 150,
- 188,
- 174,
- 196,
- 216,
- 186,
- 92,
- 144,
- 160,
- 161,
- 2,
- 97,
- 167,
- 141,
- 125,
- 241,
- 164,
- 167,
- 37,
- 72,
- 38,
- 12,
- 145,
- 159,
- 160,
- 38,
- 82,
- 13,
- 63,
- 68,
- 71,
- 226,
- 35,
- 111,
- 158,
- 153,
- 154,
- 101,
- 50,
- 205,
- 206,
- 92,
- 165,
- 209,
- 198,
- 217,
- 165,
- 167,
- 97,
- 220,
- 115,
- 218,
- 133,
- 112,
- 243,
- 239,
- 238,
- 241,
- 214,
- 135,
- 169,
- 190,
- 173,
- 133,
- 171,
- 208,
- 32,
- 125,
- 44,
- 237,
- 86,
- 87,
- 178,
- 85,
- 86,
- 179,
- 84,
- 85,
- 180,
- 83,
- 84,
- 181,
- 201,
- 83,
- 182,
- 137,
- 93,
- 132,
- 76,
- 62,
- 183,
- 61,
- 76,
- 184,
- 57,
- 61,
- 185,
- 212,
- 57,
- 186,
- 214,
- 207,
- 187,
- 34,
- 143,
- 156,
- 79,
- 239,
- 237,
- 123,
- 137,
- 177,
- 44,
- 1,
- 4,
- 201,
- 194,
- 32,
- 64,
- 102,
- 129,
- 213,
- 215,
- 138,
- 59,
- 166,
- 219,
- 242,
- 99,
- 97,
- 2,
- 94,
- 141,
- 75,
- 59,
- 235,
- 24,
- 110,
- 228,
- 25,
- 130,
- 226,
- 23,
- 24,
- 229,
- 22,
- 23,
- 230,
- 26,
- 22,
- 231,
- 112,
- 26,
- 232,
- 189,
- 190,
- 243,
- 221,
- 56,
- 190,
- 28,
- 56,
- 221,
- 27,
- 28,
- 222,
- 29,
- 27,
- 223,
- 30,
- 29,
- 224,
- 247,
- 30,
- 225,
- 238,
- 79,
- 20,
- 166,
- 59,
- 75,
- 60,
- 75,
- 240,
- 147,
- 177,
- 215,
- 20,
- 79,
- 166,
- 187,
- 147,
- 213,
- 112,
- 233,
- 244,
- 233,
- 128,
- 245,
- 128,
- 114,
- 188,
- 114,
- 217,
- 174,
- 131,
- 115,
- 220,
- 217,
- 198,
- 236,
- 198,
- 131,
- 134,
- 177,
- 132,
- 58,
- 143,
- 35,
- 124,
- 110,
- 163,
- 7,
- 228,
- 110,
- 25,
- 356,
- 389,
- 368,
- 11,
- 302,
- 267,
- 452,
- 350,
- 349,
- 302,
- 303,
- 269,
- 357,
- 343,
- 277,
- 452,
- 453,
- 357,
- 333,
- 332,
- 297,
- 175,
- 152,
- 377,
- 384,
- 398,
- 382,
- 347,
- 348,
- 330,
- 303,
- 304,
- 270,
- 9,
- 336,
- 337,
- 278,
- 279,
- 360,
- 418,
- 262,
- 431,
- 304,
- 408,
- 409,
- 310,
- 415,
- 407,
- 270,
- 409,
- 410,
- 450,
- 348,
- 347,
- 422,
- 430,
- 434,
- 313,
- 314,
- 17,
- 306,
- 307,
- 375,
- 387,
- 388,
- 260,
- 286,
- 414,
- 398,
- 335,
- 406,
- 418,
- 364,
- 367,
- 416,
- 423,
- 358,
- 327,
- 251,
- 284,
- 298,
- 281,
- 5,
- 4,
- 373,
- 374,
- 253,
- 307,
- 320,
- 321,
- 425,
- 427,
- 411,
- 421,
- 313,
- 18,
- 321,
- 405,
- 406,
- 320,
- 404,
- 405,
- 315,
- 16,
- 17,
- 426,
- 425,
- 266,
- 377,
- 400,
- 369,
- 322,
- 391,
- 269,
- 417,
- 465,
- 464,
- 386,
- 257,
- 258,
- 466,
- 260,
- 388,
- 456,
- 399,
- 419,
- 284,
- 332,
- 333,
- 417,
- 285,
- 8,
- 346,
- 340,
- 261,
- 413,
- 441,
- 285,
- 327,
- 460,
- 328,
- 355,
- 371,
- 329,
- 392,
- 439,
- 438,
- 382,
- 341,
- 256,
- 429,
- 420,
- 360,
- 364,
- 394,
- 379,
- 277,
- 343,
- 437,
- 443,
- 444,
- 283,
- 275,
- 440,
- 363,
- 431,
- 262,
- 369,
- 297,
- 338,
- 337,
- 273,
- 375,
- 321,
- 450,
- 451,
- 349,
- 446,
- 342,
- 467,
- 293,
- 334,
- 282,
- 458,
- 461,
- 462,
- 276,
- 353,
- 383,
- 308,
- 324,
- 325,
- 276,
- 300,
- 293,
- 372,
- 345,
- 447,
- 382,
- 398,
- 362,
- 352,
- 345,
- 340,
- 274,
- 1,
- 19,
- 456,
- 248,
- 281,
- 436,
- 427,
- 425,
- 381,
- 256,
- 252,
- 269,
- 391,
- 393,
- 200,
- 199,
- 428,
- 266,
- 330,
- 329,
- 287,
- 273,
- 422,
- 250,
- 462,
- 328,
- 258,
- 286,
- 384,
- 265,
- 353,
- 342,
- 387,
- 259,
- 257,
- 424,
- 431,
- 430,
- 342,
- 353,
- 276,
- 273,
- 335,
- 424,
- 292,
- 325,
- 307,
- 366,
- 447,
- 345,
- 271,
- 303,
- 302,
- 423,
- 266,
- 371,
- 294,
- 455,
- 460,
- 279,
- 278,
- 294,
- 271,
- 272,
- 304,
- 432,
- 434,
- 427,
- 272,
- 407,
- 408,
- 394,
- 430,
- 431,
- 395,
- 369,
- 400,
- 334,
- 333,
- 299,
- 351,
- 417,
- 168,
- 352,
- 280,
- 411,
- 325,
- 319,
- 320,
- 295,
- 296,
- 336,
- 319,
- 403,
- 404,
- 330,
- 348,
- 349,
- 293,
- 298,
- 333,
- 323,
- 454,
- 447,
- 15,
- 16,
- 315,
- 358,
- 429,
- 279,
- 14,
- 15,
- 316,
- 285,
- 336,
- 9,
- 329,
- 349,
- 350,
- 374,
- 380,
- 252,
- 318,
- 402,
- 403,
- 6,
- 197,
- 419,
- 318,
- 319,
- 325,
- 367,
- 364,
- 365,
- 435,
- 367,
- 397,
- 344,
- 438,
- 439,
- 272,
- 271,
- 311,
- 195,
- 5,
- 281,
- 273,
- 287,
- 291,
- 396,
- 428,
- 199,
- 311,
- 271,
- 268,
- 283,
- 444,
- 445,
- 373,
- 254,
- 339,
- 263,
- 466,
- 249,
- 282,
- 334,
- 296,
- 449,
- 347,
- 346,
- 264,
- 447,
- 454,
- 336,
- 296,
- 299,
- 338,
- 10,
- 151,
- 278,
- 439,
- 455,
- 292,
- 407,
- 415,
- 358,
- 371,
- 355,
- 340,
- 345,
- 372,
- 390,
- 249,
- 466,
- 346,
- 347,
- 280,
- 442,
- 443,
- 282,
- 19,
- 94,
- 370,
- 441,
- 442,
- 295,
- 248,
- 419,
- 197,
- 263,
- 255,
- 359,
- 440,
- 275,
- 274,
- 300,
- 383,
- 368,
- 351,
- 412,
- 465,
- 263,
- 467,
- 466,
- 301,
- 368,
- 389,
- 380,
- 374,
- 386,
- 395,
- 378,
- 379,
- 412,
- 351,
- 419,
- 436,
- 426,
- 322,
- 373,
- 390,
- 388,
- 2,
- 164,
- 393,
- 370,
- 462,
- 461,
- 164,
- 0,
- 267,
- 302,
- 11,
- 12,
- 374,
- 373,
- 387,
- 268,
- 12,
- 13,
- 293,
- 300,
- 301,
- 446,
- 261,
- 340,
- 385,
- 384,
- 381,
- 330,
- 266,
- 425,
- 426,
- 423,
- 391,
- 429,
- 355,
- 437,
- 391,
- 327,
- 326,
- 440,
- 457,
- 438,
- 341,
- 382,
- 362,
- 459,
- 457,
- 461,
- 434,
- 430,
- 394,
- 414,
- 463,
- 362,
- 396,
- 369,
- 262,
- 354,
- 461,
- 457,
- 316,
- 403,
- 402,
- 315,
- 404,
- 403,
- 314,
- 405,
- 404,
- 313,
- 406,
- 405,
- 421,
- 418,
- 406,
- 366,
- 401,
- 361,
- 306,
- 408,
- 407,
- 291,
- 409,
- 408,
- 287,
- 410,
- 409,
- 432,
- 436,
- 410,
- 434,
- 416,
- 411,
- 264,
- 368,
- 383,
- 309,
- 438,
- 457,
- 352,
- 376,
- 401,
- 274,
- 275,
- 4,
- 421,
- 428,
- 262,
- 294,
- 327,
- 358,
- 433,
- 416,
- 367,
- 289,
- 455,
- 439,
- 462,
- 370,
- 326,
- 2,
- 326,
- 370,
- 305,
- 460,
- 455,
- 254,
- 449,
- 448,
- 255,
- 261,
- 446,
- 253,
- 450,
- 449,
- 252,
- 451,
- 450,
- 256,
- 452,
- 451,
- 341,
- 453,
- 452,
- 413,
- 464,
- 463,
- 441,
- 413,
- 414,
- 258,
- 442,
- 441,
- 257,
- 443,
- 442,
- 259,
- 444,
- 443,
- 260,
- 445,
- 444,
- 467,
- 342,
- 445,
- 459,
- 458,
- 250,
- 289,
- 392,
- 290,
- 290,
- 328,
- 460,
- 376,
- 433,
- 435,
- 250,
- 290,
- 392,
- 411,
- 416,
- 433,
- 341,
- 463,
- 464,
- 453,
- 464,
- 465,
- 357,
- 465,
- 412,
- 343,
- 412,
- 399,
- 360,
- 363,
- 440,
- 437,
- 399,
- 456,
- 420,
- 456,
- 363,
- 401,
- 435,
- 288,
- 372,
- 383,
- 353,
- 339,
- 255,
- 249,
- 448,
- 261,
- 255,
- 133,
- 243,
- 190,
- 133,
- 155,
- 112,
- 33,
- 246,
- 247,
- 33,
- 130,
- 25,
- 398,
- 384,
- 286,
- 362,
- 398,
- 414,
- 362,
- 463,
- 341,
- 263,
- 359,
- 467,
- 263,
- 249,
- 255,
- 466,
- 467,
- 260,
- 75,
- 60,
- 166,
- 238,
- 239,
- 79,
- 162,
- 127,
- 139,
- 72,
- 11,
- 37,
- 121,
- 232,
- 120,
- 73,
- 72,
- 39,
- 114,
- 128,
- 47,
- 233,
- 232,
- 128,
- 103,
- 104,
- 67,
- 152,
- 175,
- 148,
- 173,
- 157,
- 155,
- 119,
- 118,
- 101,
- 74,
- 73,
- 40,
- 107,
- 9,
- 108,
- 49,
- 48,
- 131,
- 32,
- 194,
- 211,
- 184,
- 74,
- 185,
- 191,
- 80,
- 183,
- 185,
- 40,
- 186,
- 119,
- 230,
- 118,
- 210,
- 202,
- 214,
- 84,
- 83,
- 17,
- 77,
- 76,
- 146,
- 161,
- 160,
- 30,
- 190,
- 56,
- 173,
- 182,
- 106,
- 194,
- 138,
- 135,
- 192,
- 129,
- 203,
- 98,
- 54,
- 21,
- 68,
- 5,
- 51,
- 4,
- 145,
- 144,
- 23,
- 90,
- 77,
- 91,
- 207,
- 205,
- 187,
- 83,
- 201,
- 18,
- 181,
- 91,
- 182,
- 180,
- 90,
- 181,
- 16,
- 85,
- 17,
- 205,
- 206,
- 36,
- 176,
- 148,
- 140,
- 165,
- 92,
- 39,
- 245,
- 193,
- 244,
- 27,
- 159,
- 28,
- 30,
- 247,
- 161,
- 174,
- 236,
- 196,
- 103,
- 54,
- 104,
- 55,
- 193,
- 8,
- 111,
- 117,
- 31,
- 221,
- 189,
- 55,
- 240,
- 98,
- 99,
- 142,
- 126,
- 100,
- 219,
- 166,
- 218,
- 112,
- 155,
- 26,
- 198,
- 209,
- 131,
- 169,
- 135,
- 150,
- 114,
- 47,
- 217,
- 224,
- 223,
- 53,
- 220,
- 45,
- 134,
- 32,
- 211,
- 140,
- 109,
- 67,
- 108,
- 146,
- 43,
- 91,
- 231,
- 230,
- 120,
- 113,
- 226,
- 247,
- 105,
- 63,
- 52,
- 241,
- 238,
- 242,
- 124,
- 46,
- 156,
- 95,
- 78,
- 96,
- 70,
- 46,
- 63,
- 116,
- 143,
- 227,
- 116,
- 123,
- 111,
- 1,
- 44,
- 19,
- 3,
- 236,
- 51,
- 207,
- 216,
- 205,
- 26,
- 154,
- 22,
- 165,
- 39,
- 167,
- 199,
- 200,
- 208,
- 101,
- 36,
- 100,
- 43,
- 57,
- 202,
- 242,
- 20,
- 99,
- 56,
- 28,
- 157,
- 124,
- 35,
- 113,
- 29,
- 160,
- 27,
- 211,
- 204,
- 210,
- 124,
- 113,
- 46,
- 106,
- 43,
- 204,
- 96,
- 62,
- 77,
- 227,
- 137,
- 116,
- 73,
- 41,
- 72,
- 36,
- 203,
- 142,
- 235,
- 64,
- 240,
- 48,
- 49,
- 64,
- 42,
- 41,
- 74,
- 214,
- 212,
- 207,
- 183,
- 42,
- 184,
- 210,
- 169,
- 211,
- 140,
- 170,
- 176,
- 104,
- 105,
- 69,
- 193,
- 122,
- 168,
- 50,
- 123,
- 187,
- 89,
- 96,
- 90,
- 66,
- 65,
- 107,
- 179,
- 89,
- 180,
- 119,
- 101,
- 120,
- 68,
- 63,
- 104,
- 234,
- 93,
- 227,
- 16,
- 15,
- 85,
- 209,
- 129,
- 49,
- 15,
- 14,
- 86,
- 107,
- 55,
- 9,
- 120,
- 100,
- 121,
- 153,
- 145,
- 22,
- 178,
- 88,
- 179,
- 197,
- 6,
- 196,
- 89,
- 88,
- 96,
- 135,
- 138,
- 136,
- 138,
- 215,
- 172,
- 218,
- 115,
- 219,
- 41,
- 42,
- 81,
- 5,
- 195,
- 51,
- 57,
- 43,
- 61,
- 208,
- 171,
- 199,
- 41,
- 81,
- 38,
- 224,
- 53,
- 225,
- 24,
- 144,
- 110,
- 105,
- 52,
- 66,
- 118,
- 229,
- 117,
- 227,
- 34,
- 234,
- 66,
- 107,
- 69,
- 10,
- 109,
- 151,
- 219,
- 48,
- 235,
- 183,
- 62,
- 191,
- 142,
- 129,
- 126,
- 116,
- 111,
- 143,
- 7,
- 163,
- 246,
- 118,
- 117,
- 50,
- 223,
- 222,
- 52,
- 94,
- 19,
- 141,
- 222,
- 221,
- 65,
- 196,
- 3,
- 197,
- 45,
- 220,
- 44,
- 156,
- 70,
- 139,
- 188,
- 122,
- 245,
- 139,
- 71,
- 162,
- 145,
- 153,
- 159,
- 149,
- 170,
- 150,
- 122,
- 188,
- 196,
- 206,
- 216,
- 92,
- 163,
- 144,
- 161,
- 164,
- 2,
- 167,
- 242,
- 141,
- 241,
- 0,
- 164,
- 37,
- 11,
- 72,
- 12,
- 144,
- 145,
- 160,
- 12,
- 38,
- 13,
- 70,
- 63,
- 71,
- 31,
- 226,
- 111,
- 157,
- 158,
- 154,
- 36,
- 101,
- 205,
- 203,
- 206,
- 165,
- 126,
- 209,
- 217,
- 98,
- 165,
- 97,
- 237,
- 220,
- 218,
- 237,
- 239,
- 241,
- 210,
- 214,
- 169,
- 140,
- 171,
- 32,
- 241,
- 125,
- 237,
- 179,
- 86,
- 178,
- 180,
- 85,
- 179,
- 181,
- 84,
- 180,
- 182,
- 83,
- 181,
- 194,
- 201,
- 182,
- 177,
- 137,
- 132,
- 184,
- 76,
- 183,
- 185,
- 61,
- 184,
- 186,
- 57,
- 185,
- 216,
- 212,
- 186,
- 192,
- 214,
- 187,
- 139,
- 34,
- 156,
- 218,
- 79,
- 237,
- 147,
- 123,
- 177,
- 45,
- 44,
- 4,
- 208,
- 201,
- 32,
- 98,
- 64,
- 129,
- 192,
- 213,
- 138,
- 235,
- 59,
- 219,
- 141,
- 242,
- 97,
- 97,
- 2,
- 141,
- 240,
- 75,
- 235,
- 229,
- 24,
- 228,
- 31,
- 25,
- 226,
- 230,
- 23,
- 229,
- 231,
- 22,
- 230,
- 232,
- 26,
- 231,
- 233,
- 112,
- 232,
- 244,
- 189,
- 243,
- 189,
- 221,
- 190,
- 222,
- 28,
- 221,
- 223,
- 27,
- 222,
- 224,
- 29,
- 223,
- 225,
- 30,
- 224,
- 113,
- 247,
- 225,
- 99,
- 60,
- 240,
- 213,
- 147,
- 215,
- 60,
- 20,
- 166,
- 192,
- 187,
- 213,
- 243,
- 112,
- 244,
- 244,
- 233,
- 245,
- 245,
- 128,
- 188,
- 188,
- 114,
- 174,
- 134,
- 131,
- 220,
- 174,
- 217,
- 236,
- 236,
- 198,
- 134,
- 215,
- 177,
- 58,
- 156,
- 143,
- 124,
- 25,
- 110,
- 7,
- 31,
- 228,
- 25,
- 264,
- 356,
- 368,
- 0,
- 11,
- 267,
- 451,
- 452,
- 349,
- 267,
- 302,
- 269,
- 350,
- 357,
- 277,
- 350,
- 452,
- 357,
- 299,
- 333,
- 297,
- 396,
- 175,
- 377,
- 381,
- 384,
- 382,
- 280,
- 347,
- 330,
- 269,
- 303,
- 270,
- 151,
- 9,
- 337,
- 344,
- 278,
- 360,
- 424,
- 418,
- 431,
- 270,
- 304,
- 409,
- 272,
- 310,
- 407,
- 322,
- 270,
- 410,
- 449,
- 450,
- 347,
- 432,
- 422,
- 434,
- 18,
- 313,
- 17,
- 291,
- 306,
- 375,
- 259,
- 387,
- 260,
- 424,
- 335,
- 418,
- 434,
- 364,
- 416,
- 391,
- 423,
- 327,
- 301,
- 251,
- 298,
- 275,
- 281,
- 4,
- 254,
- 373,
- 253,
- 375,
- 307,
- 321,
- 280,
- 425,
- 411,
- 200,
- 421,
- 18,
- 335,
- 321,
- 406,
- 321,
- 320,
- 405,
- 314,
- 315,
- 17,
- 423,
- 426,
- 266,
- 396,
- 377,
- 369,
- 270,
- 322,
- 269,
- 413,
- 417,
- 464,
- 385,
- 386,
- 258,
- 248,
- 456,
- 419,
- 298,
- 284,
- 333,
- 168,
- 417,
- 8,
- 448,
- 346,
- 261,
- 417,
- 413,
- 285,
- 326,
- 327,
- 328,
- 277,
- 355,
- 329,
- 309,
- 392,
- 438,
- 381,
- 382,
- 256,
- 279,
- 429,
- 360,
- 365,
- 364,
- 379,
- 355,
- 277,
- 437,
- 282,
- 443,
- 283,
- 281,
- 275,
- 363,
- 395,
- 431,
- 369,
- 299,
- 297,
- 337,
- 335,
- 273,
- 321,
- 348,
- 450,
- 349,
- 359,
- 446,
- 467,
- 283,
- 293,
- 282,
- 250,
- 458,
- 462,
- 300,
- 276,
- 383,
- 292,
- 308,
- 325,
- 283,
- 276,
- 293,
- 264,
- 372,
- 447,
- 346,
- 352,
- 340,
- 354,
- 274,
- 19,
- 363,
- 456,
- 281,
- 426,
- 436,
- 425,
- 380,
- 381,
- 252,
- 267,
- 269,
- 393,
- 421,
- 200,
- 428,
- 371,
- 266,
- 329,
- 432,
- 287,
- 422,
- 290,
- 250,
- 328,
- 385,
- 258,
- 384,
- 446,
- 265,
- 342,
- 386,
- 387,
- 257,
- 422,
- 424,
- 430,
- 445,
- 342,
- 276,
- 422,
- 273,
- 424,
- 306,
- 292,
- 307,
- 352,
- 366,
- 345,
- 268,
- 271,
- 302,
- 358,
- 423,
- 371,
- 327,
- 294,
- 460,
- 331,
- 279,
- 294,
- 303,
- 271,
- 304,
- 436,
- 432,
- 427,
- 304,
- 272,
- 408,
- 395,
- 394,
- 431,
- 378,
- 395,
- 400,
- 296,
- 334,
- 299,
- 6,
- 351,
- 168,
- 376,
- 352,
- 411,
- 307,
- 325,
- 320,
- 285,
- 295,
- 336,
- 320,
- 319,
- 404,
- 329,
- 330,
- 349,
- 334,
- 293,
- 333,
- 366,
- 323,
- 447,
- 316,
- 15,
- 315,
- 331,
- 358,
- 279,
- 317,
- 14,
- 316,
- 8,
- 285,
- 9,
- 277,
- 329,
- 350,
- 253,
- 374,
- 252,
- 319,
- 318,
- 403,
- 351,
- 6,
- 419,
- 324,
- 318,
- 325,
- 397,
- 367,
- 365,
- 288,
- 435,
- 397,
- 278,
- 344,
- 439,
- 310,
- 272,
- 311,
- 248,
- 195,
- 281,
- 375,
- 273,
- 291,
- 175,
- 396,
- 199,
- 312,
- 311,
- 268,
- 276,
- 283,
- 445,
- 390,
- 373,
- 339,
- 295,
- 282,
- 296,
- 448,
- 449,
- 346,
- 356,
- 264,
- 454,
- 337,
- 336,
- 299,
- 337,
- 338,
- 151,
- 294,
- 278,
- 455,
- 308,
- 292,
- 415,
- 429,
- 358,
- 355,
- 265,
- 340,
- 372,
- 388,
- 390,
- 466,
- 352,
- 346,
- 280,
- 295,
- 442,
- 282,
- 354,
- 19,
- 370,
- 285,
- 441,
- 295,
- 195,
- 248,
- 197,
- 457,
- 440,
- 274,
- 301,
- 300,
- 368,
- 417,
- 351,
- 465,
- 251,
- 301,
- 389,
- 385,
- 380,
- 386,
- 394,
- 395,
- 379,
- 399,
- 412,
- 419,
- 410,
- 436,
- 322,
- 387,
- 373,
- 388,
- 326,
- 2,
- 393,
- 354,
- 370,
- 461,
- 393,
- 164,
- 267,
- 268,
- 302,
- 12,
- 386,
- 374,
- 387,
- 312,
- 268,
- 13,
- 298,
- 293,
- 301,
- 265,
- 446,
- 340,
- 380,
- 385,
- 381,
- 280,
- 330,
- 425,
- 322,
- 426,
- 391,
- 420,
- 429,
- 437,
- 393,
- 391,
- 326,
- 344,
- 440,
- 438,
- 458,
- 459,
- 461,
- 364,
- 434,
- 394,
- 428,
- 396,
- 262,
- 274,
- 354,
- 457,
- 317,
- 316,
- 402,
- 316,
- 315,
- 403,
- 315,
- 314,
- 404,
- 314,
- 313,
- 405,
- 313,
- 421,
- 406,
- 323,
- 366,
- 361,
- 292,
- 306,
- 407,
- 306,
- 291,
- 408,
- 291,
- 287,
- 409,
- 287,
- 432,
- 410,
- 427,
- 434,
- 411,
- 372,
- 264,
- 383,
- 459,
- 309,
- 457,
- 366,
- 352,
- 401,
- 1,
- 274,
- 4,
- 418,
- 421,
- 262,
- 331,
- 294,
- 358,
- 435,
- 433,
- 367,
- 392,
- 289,
- 439,
- 328,
- 462,
- 326,
- 94,
- 2,
- 370,
- 289,
- 305,
- 455,
- 339,
- 254,
- 448,
- 359,
- 255,
- 446,
- 254,
- 253,
- 449,
- 253,
- 252,
- 450,
- 252,
- 256,
- 451,
- 256,
- 341,
- 452,
- 414,
- 413,
- 463,
- 286,
- 441,
- 414,
- 286,
- 258,
- 441,
- 258,
- 257,
- 442,
- 257,
- 259,
- 443,
- 259,
- 260,
- 444,
- 260,
- 467,
- 445,
- 309,
- 459,
- 250,
- 305,
- 289,
- 290,
- 305,
- 290,
- 460,
- 401,
- 376,
- 435,
- 309,
- 250,
- 392,
- 376,
- 411,
- 433,
- 453,
- 341,
- 464,
- 357,
- 453,
- 465,
- 343,
- 357,
- 412,
- 437,
- 343,
- 399,
- 344,
- 360,
- 440,
- 420,
- 437,
- 456,
- 360,
- 420,
- 363,
- 361,
- 401,
- 288,
- 265,
- 372,
- 353,
- 390,
- 339,
- 249,
- 339,
- 448,
- 255
-];
-var VTX68 = [
- 127,
- 234,
- 132,
- 58,
- 172,
- 150,
- 149,
- 148,
- 152,
- 377,
- 378,
- 379,
- 397,
- 288,
- 361,
- 454,
- 356,
- 70,
- 63,
- 105,
- 66,
- 107,
- 336,
- 296,
- 334,
- 293,
- 300,
- 168,
- 6,
- 195,
- 4,
- 98,
- 97,
- 2,
- 326,
- 327,
- 33,
- 160,
- 158,
- 133,
- 153,
- 144,
- 362,
- 385,
- 387,
- 263,
- 373,
- 380,
- 57,
- 40,
- 37,
- 0,
- 267,
- 270,
- 287,
- 321,
- 314,
- 17,
- 84,
- 91,
- 78,
- 81,
- 13,
- 311,
- 308,
- 402,
- 14,
- 178
-];
-var VTX33 = [33, 133, 362, 263, 1, 62, 308, 159, 145, 386, 374, 6, 102, 331, 2, 13, 14, 70, 105, 107, 336, 334, 300, 54, 10, 284, 50, 280, 234, 454, 58, 288, 152];
-var VTX7 = [33, 133, 362, 263, 1, 78, 308];
-var UV68 = VTX68.map((x) => UV468[x]);
-var UV33 = VTX33.map((x) => UV468[x]);
-var UV7 = VTX7.map((x) => UV468[x]);
-function connectionsToIndices(connections) {
- const indices = connections.map((connection) => connection[0]);
- indices.push(connections[connections.length - 1][1]);
- return indices;
-}
-var pairsLips = [
- [61, 146],
- [146, 91],
- [91, 181],
- [181, 84],
- [84, 17],
- [17, 314],
- [314, 405],
- [405, 321],
- [321, 375],
- [375, 291],
- [61, 185],
- [185, 40],
- [40, 39],
- [39, 37],
- [37, 0],
- [0, 267],
- [267, 269],
- [269, 270],
- [270, 409],
- [409, 291],
- [78, 95],
- [95, 88],
- [88, 178],
- [178, 87],
- [87, 14],
- [14, 317],
- [317, 402],
- [402, 318],
- [318, 324],
- [324, 308],
- [78, 191],
- [191, 80],
- [80, 81],
- [81, 82],
- [82, 13],
- [13, 312],
- [312, 311],
- [311, 310],
- [310, 415],
- [415, 308]
-];
-var pairsLeftEye = [[263, 249], [249, 390], [390, 373], [373, 374], [374, 380], [380, 381], [381, 382], [382, 362], [263, 466], [466, 388], [388, 387], [387, 386], [386, 385], [385, 384], [384, 398], [398, 362]];
-var pairsLeftEyebrow = [[276, 283], [283, 282], [282, 295], [295, 285], [300, 293], [293, 334], [334, 296], [296, 336]];
-var pairsLeftIris = [[474, 475], [475, 476], [476, 477], [477, 474]];
-var pairsRightEye = [[33, 7], [7, 163], [163, 144], [144, 145], [145, 153], [153, 154], [154, 155], [155, 133], [33, 246], [246, 161], [161, 160], [160, 159], [159, 158], [158, 157], [157, 173], [173, 133]];
-var pairsRightEyebrow = [[46, 53], [53, 52], [52, 65], [65, 55], [70, 63], [63, 105], [105, 66], [66, 107]];
-var pairsRightIris = [[469, 470], [470, 471], [471, 472], [472, 469]];
-var pairsFaceContour = [
- [10, 338],
- [338, 297],
- [297, 332],
- [332, 284],
- [284, 251],
- [251, 389],
- [389, 356],
- [356, 454],
- [454, 323],
- [323, 361],
- [361, 288],
- [288, 397],
- [397, 365],
- [365, 379],
- [379, 378],
- [378, 400],
- [400, 377],
- [377, 152],
- [152, 148],
- [148, 176],
- [176, 149],
- [149, 150],
- [150, 136],
- [136, 172],
- [172, 58],
- [58, 132],
- [132, 93],
- [93, 234],
- [234, 127],
- [127, 162],
- [162, 21],
- [21, 54],
- [54, 103],
- [103, 67],
- [67, 109],
- [109, 10]
-];
-var contourKeypoints = {
- lips: connectionsToIndices(pairsLips),
- leftEye: connectionsToIndices(pairsLeftEye),
- leftEyebrow: connectionsToIndices(pairsLeftEyebrow),
- leftIris: connectionsToIndices(pairsLeftIris),
- rightEye: connectionsToIndices(pairsRightEye),
- rightEyebrow: connectionsToIndices(pairsRightEyebrow),
- rightIris: connectionsToIndices(pairsRightIris),
- faceOval: connectionsToIndices(pairsFaceContour)
-};
-
-// src/face/facemeshutil.ts
-var getBoxSize = (box) => [Math.abs(box.endPoint[0] - box.startPoint[0]), Math.abs(box.endPoint[1] - box.startPoint[1])];
-var getBoxCenter = (box) => [box.startPoint[0] + (box.endPoint[0] - box.startPoint[0]) / 2, box.startPoint[1] + (box.endPoint[1] - box.startPoint[1]) / 2, 1];
-var clampBox = (box, input) => box ? [
- Math.trunc(Math.max(0, box.startPoint[0])),
- Math.trunc(Math.max(0, box.startPoint[1])),
- Math.trunc(Math.min(input.shape[2] || 0, box.endPoint[0]) - Math.max(0, box.startPoint[0])),
- Math.trunc(Math.min(input.shape[1] || 0, box.endPoint[1]) - Math.max(0, box.startPoint[1]))
-] : [0, 0, 0, 0];
-var getRawBox = (box, input) => box ? [
- box.startPoint[0] / (input.shape[2] || 0),
- box.startPoint[1] / (input.shape[1] || 0),
- (box.endPoint[0] - box.startPoint[0]) / (input.shape[2] || 0),
- (box.endPoint[1] - box.startPoint[1]) / (input.shape[1] || 0)
-] : [0, 0, 0, 0];
-var scaleBoxCoordinates = (box, factor) => {
- const startPoint = [box.startPoint[0] * factor[0], box.startPoint[1] * factor[1]];
- const endPoint = [box.endPoint[0] * factor[0], box.endPoint[1] * factor[1]];
- return { startPoint, endPoint, landmarks: box.landmarks, confidence: box.confidence };
-};
-var cutAndResize = (box, image26, cropSize) => {
- const h = image26.shape[1];
- const w = image26.shape[2];
- const cutBox = [box.startPoint[1] / h, box.startPoint[0] / w, box.endPoint[1] / h, box.endPoint[0] / w];
- const crop = tfjs_esm_exports.image.cropAndResize(image26, [cutBox], [0], cropSize);
- const norm = tfjs_esm_exports.div(crop, constants.tf255);
- tfjs_esm_exports.dispose(crop);
- return norm;
-};
-var enlargeBox = (box, factor) => {
- const center = getBoxCenter(box);
- const size2 = getBoxSize(box);
- const halfSize = [factor * size2[0] / 2, factor * size2[1] / 2];
- return { startPoint: [center[0] - halfSize[0], center[1] - halfSize[1]], endPoint: [center[0] + halfSize[0], center[1] + halfSize[1]], landmarks: box.landmarks, confidence: box.confidence };
-};
-var squarifyBox = (box) => {
- const centers = getBoxCenter(box);
- const size2 = getBoxSize(box);
- const halfSize = Math.max(...size2) / 2;
- return { startPoint: [Math.round(centers[0] - halfSize), Math.round(centers[1] - halfSize)], endPoint: [Math.round(centers[0] + halfSize), Math.round(centers[1] + halfSize)], landmarks: box.landmarks, confidence: box.confidence };
-};
-var calculateLandmarksBoundingBox = (landmarks) => {
- const x = landmarks.map((d) => d[0]);
- const y = landmarks.map((d) => d[1]);
- return { startPoint: [Math.min(...x), Math.min(...y)], endPoint: [Math.max(...x), Math.max(...y)], landmarks };
-};
-var fixedRotationMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
-var normalizeRadians = (angle) => angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI));
-var computeRotation = (point1, point2) => normalizeRadians(Math.PI / 2 - Math.atan2(-(point2[1] - point1[1]), point2[0] - point1[0]));
-var buildTranslationMatrix = (x, y) => [[1, 0, x], [0, 1, y], [0, 0, 1]];
-var dot = (v1, v2) => {
- let product = 0;
- for (let i = 0; i < v1.length; i++)
- product += v1[i] * v2[i];
- return product;
-};
-var getColumnFrom2DArr = (arr, columnIndex) => {
- const column = [];
- for (let i = 0; i < arr.length; i++)
- column.push(arr[i][columnIndex]);
- return column;
-};
-var multiplyTransformMatrices = (mat1, mat2) => {
- const product = [];
- const size2 = mat1.length;
- for (let row = 0; row < size2; row++) {
- product.push([]);
- for (let col = 0; col < size2; col++)
- product[row].push(dot(mat1[row], getColumnFrom2DArr(mat2, col)));
- }
- return product;
-};
-var buildRotationMatrix = (rotation, center) => {
- const cosA = Math.cos(rotation);
- const sinA = Math.sin(rotation);
- const rotationMatrix = [[cosA, -sinA, 0], [sinA, cosA, 0], [0, 0, 1]];
- const translationMatrix = buildTranslationMatrix(center[0], center[1]);
- const translationTimesRotation = multiplyTransformMatrices(translationMatrix, rotationMatrix);
- const negativeTranslationMatrix = buildTranslationMatrix(-center[0], -center[1]);
- return multiplyTransformMatrices(translationTimesRotation, negativeTranslationMatrix);
-};
-var invertTransformMatrix = (matrix) => {
- const rotationComponent = [[matrix[0][0], matrix[1][0]], [matrix[0][1], matrix[1][1]]];
- const translationComponent = [matrix[0][2], matrix[1][2]];
- const invertedTranslation = [-dot(rotationComponent[0], translationComponent), -dot(rotationComponent[1], translationComponent)];
- return [rotationComponent[0].concat(invertedTranslation[0]), rotationComponent[1].concat(invertedTranslation[1]), [0, 0, 1]];
-};
-var rotatePoint = (homogeneousCoordinate, rotationMatrix) => [dot(homogeneousCoordinate, rotationMatrix[0]), dot(homogeneousCoordinate, rotationMatrix[1])];
-function generateAnchors(inputSize10) {
- const spec = inputSize10 === 192 ? { strides: [4], anchors: [1] } : { strides: [inputSize10 / 16, inputSize10 / 8], anchors: [2, 6] };
- const anchors3 = [];
- for (let i = 0; i < spec.strides.length; i++) {
- const stride = spec.strides[i];
- const gridRows = Math.floor((inputSize10 + stride - 1) / stride);
- const gridCols = Math.floor((inputSize10 + stride - 1) / stride);
- const anchorsNum = spec.anchors[i];
- for (let gridY = 0; gridY < gridRows; gridY++) {
- const anchorY = stride * (gridY + 0.5);
- for (let gridX = 0; gridX < gridCols; gridX++) {
- const anchorX = stride * (gridX + 0.5);
- for (let n = 0; n < anchorsNum; n++)
- anchors3.push([anchorX, anchorY]);
- }
- }
- }
- return anchors3;
-}
-function transformRawCoords(coordsRaw, box, angle, rotationMatrix, inputSize10) {
- const boxSize = getBoxSize(box);
- const coordsScaled = coordsRaw.map((coord) => [
- boxSize[0] / inputSize10 * (coord[0] - inputSize10 / 2),
- boxSize[1] / inputSize10 * (coord[1] - inputSize10 / 2),
- coord[2] || 0
- ]);
- const largeAngle = angle && angle !== 0 && Math.abs(angle) > 0.2;
- const coordsRotationMatrix = largeAngle ? buildRotationMatrix(angle, [0, 0]) : fixedRotationMatrix;
- const coordsRotated = largeAngle ? coordsScaled.map((coord) => [...rotatePoint(coord, coordsRotationMatrix), coord[2]]) : coordsScaled;
- const inverseRotationMatrix = largeAngle ? invertTransformMatrix(rotationMatrix) : fixedRotationMatrix;
- const boxCenter = getBoxCenter(box);
- const offsets = [dot(boxCenter, inverseRotationMatrix[0]), dot(boxCenter, inverseRotationMatrix[1])];
- return coordsRotated.map((coord) => [
- Math.trunc(coord[0] + offsets[0]),
- Math.trunc(coord[1] + offsets[1]),
- Math.trunc(coord[2] || 0)
- ]);
-}
-function correctFaceRotation(rotate, box, input, inputSize10) {
- const symmetryLine = box.landmarks.length >= meshLandmarks.count ? meshLandmarks.symmetryLine : blazeFaceLandmarks.symmetryLine;
- let angle = 0;
- let rotationMatrix = fixedRotationMatrix;
- let face4;
- if (rotate && env.kernels.includes("rotatewithoffset")) {
- angle = computeRotation(box.landmarks[symmetryLine[0]], box.landmarks[symmetryLine[1]]);
- const largeAngle = angle && angle !== 0 && Math.abs(angle) > 0.2;
- if (largeAngle) {
- const center = getBoxCenter(box);
- const centerRaw = [center[0] / input.shape[2], center[1] / input.shape[1]];
- const rotated = tfjs_esm_exports.image.rotateWithOffset(input, angle, 0, centerRaw);
- rotationMatrix = buildRotationMatrix(-angle, center);
- face4 = cutAndResize(box, rotated, [inputSize10, inputSize10]);
- tfjs_esm_exports.dispose(rotated);
- } else {
- face4 = cutAndResize(box, input, [inputSize10, inputSize10]);
- }
- } else {
- face4 = cutAndResize(box, input, [inputSize10, inputSize10]);
- }
- return [angle, rotationMatrix, face4];
-}
-var findFaceCenter = (mesh) => {
- const x = mesh.map((m) => m[0]);
- const y = mesh.map((m) => m[1]);
- return [Math.min(...x) + (Math.max(...x) - Math.min(...x)) / 2, Math.min(...y) + (Math.max(...y) - Math.min(...y)) / 2];
-};
-var calculateFaceBox = (mesh, previousBox) => {
- const center = findFaceCenter(mesh);
- const boxSize = getBoxSize(previousBox);
- const calculatedBox = {
- startPoint: [center[0] - boxSize[0] / 2, center[1] - boxSize[1] / 2],
- endPoint: [center[0] + boxSize[0] / 2, center[1] + boxSize[1] / 2]
- };
- return calculatedBox;
-};
-
-// src/face/blazeface.ts
-var keypointsCount = 6;
-var faceBoxScaleFactor = 1.4;
-var model5;
-var anchors = null;
-var inputSize = 0;
-var inputSizeT = null;
-var size = () => inputSize;
-async function load5(config3) {
- var _a;
- if (env.initial)
- model5 = null;
- if (!model5)
- model5 = await loadModel((_a = config3.face.detector) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model5["modelUrl"]);
- inputSize = model5["executor"] && model5.inputs[0].shape ? model5.inputs[0].shape[2] : 256;
- inputSizeT = tfjs_esm_exports.scalar(inputSize, "int32");
- anchors = tfjs_esm_exports.tensor2d(generateAnchors(inputSize));
- return model5;
-}
-function decodeBoxes(boxOutputs) {
- const t = {};
- t.boxStarts = tfjs_esm_exports.slice(boxOutputs, [0, 1], [-1, 2]);
- t.centers = tfjs_esm_exports.add(t.boxStarts, anchors);
- t.boxSizes = tfjs_esm_exports.slice(boxOutputs, [0, 3], [-1, 2]);
- t.boxSizesNormalized = tfjs_esm_exports.div(t.boxSizes, inputSizeT);
- t.centersNormalized = tfjs_esm_exports.div(t.centers, inputSizeT);
- t.halfBoxSize = tfjs_esm_exports.div(t.boxSizesNormalized, constants.tf2);
- t.starts = tfjs_esm_exports.sub(t.centersNormalized, t.halfBoxSize);
- t.ends = tfjs_esm_exports.add(t.centersNormalized, t.halfBoxSize);
- t.startNormalized = tfjs_esm_exports.mul(t.starts, inputSizeT);
- t.endNormalized = tfjs_esm_exports.mul(t.ends, inputSizeT);
- const boxes = tfjs_esm_exports.concat2d([t.startNormalized, t.endNormalized], 1);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return boxes;
-}
-async function getBoxes(inputImage, config3) {
- var _a, _b, _c, _d;
- if (!inputImage || inputImage["isDisposedInternal"] || inputImage.shape.length !== 4 || inputImage.shape[1] < 1 || inputImage.shape[2] < 1)
- return [];
- const t = {};
- t.resized = tfjs_esm_exports.image.resizeBilinear(inputImage, [inputSize, inputSize]);
- t.div = tfjs_esm_exports.div(t.resized, constants.tf127);
- t.normalized = tfjs_esm_exports.sub(t.div, constants.tf05);
- const res = model5 == null ? void 0 : model5.execute(t.normalized);
- if (Array.isArray(res) && res.length > 2) {
- const sorted = res.sort((a, b) => a.size - b.size);
- t.concat384 = tfjs_esm_exports.concat([sorted[0], sorted[2]], 2);
- t.concat512 = tfjs_esm_exports.concat([sorted[1], sorted[3]], 2);
- t.concat = tfjs_esm_exports.concat([t.concat512, t.concat384], 1);
- t.batch = tfjs_esm_exports.squeeze(t.concat, 0);
- } else if (Array.isArray(res)) {
- t.batch = tfjs_esm_exports.squeeze(res[0]);
- } else {
- t.batch = tfjs_esm_exports.squeeze(res);
- }
- tfjs_esm_exports.dispose(res);
- t.boxes = decodeBoxes(t.batch);
- t.logits = tfjs_esm_exports.slice(t.batch, [0, 0], [-1, 1]);
- t.sigmoid = tfjs_esm_exports.sigmoid(t.logits);
- t.scores = tfjs_esm_exports.squeeze(t.sigmoid);
- t.nms = await tfjs_esm_exports.image.nonMaxSuppressionAsync(t.boxes, t.scores, ((_a = config3.face.detector) == null ? void 0 : _a.maxDetected) || 0, ((_b = config3.face.detector) == null ? void 0 : _b.iouThreshold) || 0, ((_c = config3.face.detector) == null ? void 0 : _c.minConfidence) || 0);
- const nms = await t.nms.array();
- const boxes = [];
- const scores = await t.scores.data();
- for (let i = 0; i < nms.length; i++) {
- const confidence = scores[nms[i]];
- if (confidence > (((_d = config3.face.detector) == null ? void 0 : _d.minConfidence) || 0)) {
- const b = {};
- b.bbox = tfjs_esm_exports.slice(t.boxes, [nms[i], 0], [1, -1]);
- b.slice = tfjs_esm_exports.slice(t.batch, [nms[i], keypointsCount - 1], [1, -1]);
- b.squeeze = tfjs_esm_exports.squeeze(b.slice);
- b.landmarks = tfjs_esm_exports.reshape(b.squeeze, [keypointsCount, -1]);
- const points = await b.bbox.data();
- const rawBox = {
- startPoint: [points[0], points[1]],
- endPoint: [points[2], points[3]],
- landmarks: await b.landmarks.array(),
- confidence
- };
- const scaledBox = scaleBoxCoordinates(rawBox, [(inputImage.shape[2] || 0) / inputSize, (inputImage.shape[1] || 0) / inputSize]);
- const enlargedBox = enlargeBox(scaledBox, config3.face["scale"] || faceBoxScaleFactor);
- const squaredBox = squarifyBox(enlargedBox);
- boxes.push(squaredBox);
- Object.keys(b).forEach((tensor3) => tfjs_esm_exports.dispose(b[tensor3]));
- }
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return boxes;
-}
-
-// src/body/blazeposecoords.ts
-var blazeposecoords_exports = {};
-__export(blazeposecoords_exports, {
- connected: () => connected,
- kpt: () => kpt
-});
-var kpt = [
- "nose",
- "leftEyeInside",
- "leftEye",
- "leftEyeOutside",
- "rightEyeInside",
- "rightEye",
- "rightEyeOutside",
- "leftEar",
- "rightEar",
- "leftMouth",
- "rightMouth",
- "leftShoulder",
- "rightShoulder",
- "leftElbow",
- "rightElbow",
- "leftWrist",
- "rightWrist",
- "leftPinky",
- "rightPinky",
- "leftIndex",
- "rightIndex",
- "leftThumb",
- "rightThumb",
- "leftHip",
- "rightHip",
- "leftKnee",
- "rightKnee",
- "leftAnkle",
- "rightAnkle",
- "leftHeel",
- "rightHeel",
- "leftFoot",
- "rightFoot",
- "bodyCenter",
- "bodyTop",
- "leftPalm",
- "leftHand",
- "rightPalm",
- "rightHand"
-];
-var connected = {
- shoulders: ["leftShoulder", "rightShoulder"],
- hips: ["rightHip", "leftHip"],
- mouth: ["leftMouth", "rightMouth"],
- leftLegUpper: ["leftHip", "leftKnee"],
- leftLegLower: ["leftKnee", "leftAnkle"],
- leftFoot: ["leftAnkle", "leftHeel", "leftFoot"],
- leftTorso: ["leftShoulder", "leftHip"],
- leftArmUpper: ["leftShoulder", "leftElbow"],
- leftArmLower: ["leftElbow", "leftWrist"],
- leftHand: ["leftWrist", "leftPalm"],
- leftHandPinky: ["leftPalm", "leftPinky"],
- leftHandIndex: ["leftPalm", "leftIndex"],
- leftHandThumb: ["leftPalm", "leftThumb"],
- leftEyeOutline: ["leftEyeInside", "leftEyeOutside"],
- rightLegUpper: ["rightHip", "rightKnee"],
- rightLegLower: ["rightKnee", "rightAnkle"],
- rightFoot: ["rightAnkle", "rightHeel", "rightFoot"],
- rightTorso: ["rightShoulder", "rightHip"],
- rightArmUpper: ["rightShoulder", "rightElbow"],
- rightArmLower: ["rightElbow", "rightWrist"],
- rightHand: ["rightWrist", "rightPalm"],
- rightHandPinky: ["rightPalm", "rightPinky"],
- rightHandIndex: ["rightPalm", "rightIndex"],
- rightHandThumb: ["rightPalm", "rightThumb"],
- rightEyeOutline: ["rightEyeInside", "rightEyeOutside"]
-};
-
-// src/body/blazeposedetector.ts
-var inputSize2 = 224;
-var anchorTensor;
-var numLayers = 5;
-var strides = [8, 16, 32, 32, 32];
-function createAnchors() {
- const anchors3 = [];
- let layerId = 0;
- while (layerId < numLayers) {
- let anchorCount = 0;
- let lastSameStrideLayer = layerId;
- while (lastSameStrideLayer < strides.length && strides[lastSameStrideLayer] === strides[layerId]) {
- anchorCount += 2;
- lastSameStrideLayer++;
- }
- const stride = strides[layerId];
- const featureMapHeight = Math.ceil(inputSize2 / stride);
- const featureMapWidth = Math.ceil(inputSize2 / stride);
- for (let y = 0; y < featureMapHeight; ++y) {
- for (let x = 0; x < featureMapWidth; ++x) {
- for (let anchorId = 0; anchorId < anchorCount; ++anchorId) {
- anchors3.push({ x: (x + 0.5) / featureMapWidth, y: (y + 0.5) / featureMapHeight });
- }
- }
- }
- layerId = lastSameStrideLayer;
- }
- anchorTensor = { x: tfjs_esm_exports.tensor1d(anchors3.map((a) => a.x)), y: tfjs_esm_exports.tensor1d(anchors3.map((a) => a.y)) };
-}
-
-// src/util/box.ts
-function calc(keypoints, outputSize2 = [1, 1]) {
- const coords = [keypoints.map((pt) => pt[0]), keypoints.map((pt) => pt[1])];
- const min2 = [Math.min(...coords[0]), Math.min(...coords[1])];
- const max4 = [Math.max(...coords[0]), Math.max(...coords[1])];
- const box = [min2[0], min2[1], max4[0] - min2[0], max4[1] - min2[1]];
- const boxRaw = [box[0] / outputSize2[0], box[1] / outputSize2[1], box[2] / outputSize2[0], box[3] / outputSize2[1]];
- return { box, boxRaw };
-}
-function square(keypoints, outputSize2 = [1, 1]) {
- const coords = [keypoints.map((pt) => pt[0]), keypoints.map((pt) => pt[1])];
- const min2 = [Math.min(...coords[0]), Math.min(...coords[1])];
- const max4 = [Math.max(...coords[0]), Math.max(...coords[1])];
- const center = [(min2[0] + max4[0]) / 2, (min2[1] + max4[1]) / 2];
- const dist = Math.max(center[0] - min2[0], center[1] - min2[1], -center[0] + max4[0], -center[1] + max4[1]);
- const box = [Math.trunc(center[0] - dist), Math.trunc(center[1] - dist), Math.trunc(2 * dist), Math.trunc(2 * dist)];
- const boxRaw = [box[0] / outputSize2[0], box[1] / outputSize2[1], box[2] / outputSize2[0], box[3] / outputSize2[1]];
- return { box, boxRaw };
-}
-function scale(box, scaleFact) {
- const dist = [box[2] * scaleFact, box[3] * scaleFact];
- const newBox = [
- box[0] - (dist[0] - box[2]) / 2,
- box[1] - (dist[1] - box[3]) / 2,
- dist[0],
- dist[1]
- ];
- return newBox;
-}
-
-// src/body/blazepose.ts
-var env3 = { initial: true };
-var models2 = { detector: null, landmarks: null };
-var inputSize3 = { detector: [224, 224], landmarks: [256, 256] };
-var skipped5 = Number.MAX_SAFE_INTEGER;
-var outputNodes = {
- landmarks: ["ld_3d", "activation_segmentation", "activation_heatmap", "world_3d", "output_poseflag"],
- detector: []
-};
-var cache = null;
-var cropBox;
-var padding = [[0, 0], [0, 0], [0, 0], [0, 0]];
-var lastTime5 = 0;
-var sigmoid3 = (x) => 1 - 1 / (1 + Math.exp(x));
-async function loadDetect(config3) {
- var _a;
- if (env3.initial)
- models2.detector = null;
- if (!models2.detector && config3.body["detector"] && config3.body["detector"].modelPath || "") {
- models2.detector = await loadModel(config3.body["detector"].modelPath);
- const inputs = ((_a = models2.detector) == null ? void 0 : _a["executor"]) ? Object.values(models2.detector.modelSignature["inputs"]) : void 0;
- inputSize3.detector[0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;
- inputSize3.detector[1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;
- } else if (config3.debug && models2.detector)
- log("cached model:", models2.detector["modelUrl"]);
- createAnchors();
- return models2.detector;
-}
-async function loadPose(config3) {
- var _a;
- if (env3.initial)
- models2.landmarks = null;
- if (!models2.landmarks) {
- models2.landmarks = await loadModel(config3.body.modelPath);
- const inputs = ((_a = models2.landmarks) == null ? void 0 : _a["executor"]) ? Object.values(models2.landmarks.modelSignature["inputs"]) : void 0;
- inputSize3.landmarks[0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;
- inputSize3.landmarks[1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;
- } else if (config3.debug)
- log("cached model:", models2.landmarks["modelUrl"]);
- return models2.landmarks;
-}
-function prepareImage(input, size2) {
- var _a, _b;
- const t = {};
- if (!((_a = input == null ? void 0 : input.shape) == null ? void 0 : _a[1]) || !((_b = input == null ? void 0 : input.shape) == null ? void 0 : _b[2]))
- return input;
- let final;
- if (cropBox) {
- t.cropped = tfjs_esm_exports.image.cropAndResize(input, [cropBox], [0], [input.shape[1], input.shape[2]]);
- }
- if (input.shape[1] !== input.shape[2]) {
- const height = [
- input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0,
- input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0
- ];
- const width = [
- input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0,
- input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0
- ];
- padding = [
- [0, 0],
- height,
- width,
- [0, 0]
- ];
- t.pad = tfjs_esm_exports.pad(t.cropped || input, padding);
- t.resize = tfjs_esm_exports.image.resizeBilinear(t.pad, [size2, size2]);
- final = tfjs_esm_exports.div(t.resize, constants.tf255);
- } else if (input.shape[1] !== size2) {
- t.resize = tfjs_esm_exports.image.resizeBilinear(t.cropped || input, [size2, size2]);
- final = tfjs_esm_exports.div(t.resize, constants.tf255);
- } else {
- final = tfjs_esm_exports.div(t.cropped || input, constants.tf255);
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return final;
-}
-function rescaleKeypoints(keypoints, outputSize2) {
- for (const kpt4 of keypoints) {
- kpt4.position = [
- Math.trunc(kpt4.position[0] * (outputSize2[0] + padding[2][0] + padding[2][1]) / outputSize2[0] - padding[2][0]),
- Math.trunc(kpt4.position[1] * (outputSize2[1] + padding[1][0] + padding[1][1]) / outputSize2[1] - padding[1][0]),
- kpt4.position[2]
- ];
- kpt4.positionRaw = [kpt4.position[0] / outputSize2[0], kpt4.position[1] / outputSize2[1], 2 * kpt4.position[2] / (outputSize2[0] + outputSize2[1])];
- }
- if (cropBox) {
- for (const kpt4 of keypoints) {
- kpt4.positionRaw = [
- kpt4.positionRaw[0] + cropBox[1],
- kpt4.positionRaw[1] + cropBox[0],
- kpt4.positionRaw[2]
- ];
- kpt4.position = [
- Math.trunc(kpt4.positionRaw[0] * outputSize2[0]),
- Math.trunc(kpt4.positionRaw[1] * outputSize2[1]),
- kpt4.positionRaw[2]
- ];
- }
- }
- return keypoints;
-}
-function fixKeypoints(keypoints) {
- const leftPalm = keypoints.find((k) => k.part === "leftPalm");
- const leftWrist = keypoints.find((k) => k.part === "leftWrist");
- const leftIndex = keypoints.find((k) => k.part === "leftIndex");
- leftPalm.position[2] = ((leftWrist.position[2] || 0) + (leftIndex.position[2] || 0)) / 2;
- const rightPalm = keypoints.find((k) => k.part === "rightPalm");
- const rightWrist = keypoints.find((k) => k.part === "rightWrist");
- const rightIndex = keypoints.find((k) => k.part === "rightIndex");
- rightPalm.position[2] = ((rightWrist.position[2] || 0) + (rightIndex.position[2] || 0)) / 2;
-}
-async function detectLandmarks(input, config3, outputSize2) {
- var _a, _b;
- if (!((_a = models2.landmarks) == null ? void 0 : _a["executor"]))
- return null;
- const t = {};
- [t.ld, t.segmentation, t.heatmap, t.world, t.poseflag] = (_b = models2.landmarks) == null ? void 0 : _b.execute(input, outputNodes.landmarks);
- const poseScore = (await t.poseflag.data())[0];
- const points = await t.ld.data();
- const distances = await t.world.data();
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- const keypointsRelative = [];
- const depth = 5;
- for (let i = 0; i < points.length / depth; i++) {
- const score = sigmoid3(points[depth * i + 3]);
- const presence = sigmoid3(points[depth * i + 4]);
- const adjScore = Math.trunc(100 * score * presence * poseScore) / 100;
- const positionRaw = [points[depth * i + 0] / inputSize3.landmarks[0], points[depth * i + 1] / inputSize3.landmarks[1], points[depth * i + 2] + 0];
- const position = [Math.trunc(outputSize2[0] * positionRaw[0]), Math.trunc(outputSize2[1] * positionRaw[1]), positionRaw[2]];
- const distance2 = [distances[depth * i + 0], distances[depth * i + 1], distances[depth * i + 2] + 0];
- keypointsRelative.push({ part: kpt[i], positionRaw, position, distance: distance2, score: adjScore });
- }
- if (poseScore < (config3.body.minConfidence || 0))
- return null;
- fixKeypoints(keypointsRelative);
- const keypoints = rescaleKeypoints(keypointsRelative, outputSize2);
- const kpts = keypoints.map((k) => k.position);
- const boxes = calc(kpts, [outputSize2[0], outputSize2[1]]);
- const annotations2 = {};
- for (const [name, indexes] of Object.entries(connected)) {
- const pt = [];
- for (let i = 0; i < indexes.length - 1; i++) {
- const pt0 = keypoints.find((kpt4) => kpt4.part === indexes[i]);
- const pt1 = keypoints.find((kpt4) => kpt4.part === indexes[i + 1]);
- if (pt0 && pt1)
- pt.push([pt0.position, pt1.position]);
- }
- annotations2[name] = pt;
- }
- const body4 = { id: 0, score: Math.trunc(100 * poseScore) / 100, box: boxes.box, boxRaw: boxes.boxRaw, keypoints, annotations: annotations2 };
- return body4;
-}
-async function predict5(input, config3) {
- const outputSize2 = [input.shape[2] || 0, input.shape[1] || 0];
- const skipTime = (config3.body.skipTime || 0) > now() - lastTime5;
- const skipFrame = skipped5 < (config3.body.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame && cache !== null) {
- skipped5++;
- } else {
- const t = {};
- t.landmarks = prepareImage(input, 256);
- cache = await detectLandmarks(t.landmarks, config3, outputSize2);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- lastTime5 = now();
- skipped5 = 0;
- }
- return cache ? [cache] : [];
-}
-
-// src/object/labels.ts
-var labels = [
- { class: 1, label: "person" },
- { class: 2, label: "bicycle" },
- { class: 3, label: "car" },
- { class: 4, label: "motorcycle" },
- { class: 5, label: "airplane" },
- { class: 6, label: "bus" },
- { class: 7, label: "train" },
- { class: 8, label: "truck" },
- { class: 9, label: "boat" },
- { class: 10, label: "traffic light" },
- { class: 11, label: "fire hydrant" },
- { class: 12, label: "stop sign" },
- { class: 13, label: "parking meter" },
- { class: 14, label: "bench" },
- { class: 15, label: "bird" },
- { class: 16, label: "cat" },
- { class: 17, label: "dog" },
- { class: 18, label: "horse" },
- { class: 19, label: "sheep" },
- { class: 20, label: "cow" },
- { class: 21, label: "elephant" },
- { class: 22, label: "bear" },
- { class: 23, label: "zebra" },
- { class: 24, label: "giraffe" },
- { class: 25, label: "backpack" },
- { class: 26, label: "umbrella" },
- { class: 27, label: "handbag" },
- { class: 28, label: "tie" },
- { class: 29, label: "suitcase" },
- { class: 30, label: "frisbee" },
- { class: 31, label: "skis" },
- { class: 32, label: "snowboard" },
- { class: 33, label: "sports ball" },
- { class: 34, label: "kite" },
- { class: 35, label: "baseball bat" },
- { class: 36, label: "baseball glove" },
- { class: 37, label: "skateboard" },
- { class: 38, label: "surfboard" },
- { class: 39, label: "tennis racket" },
- { class: 40, label: "bottle" },
- { class: 41, label: "wine glass" },
- { class: 42, label: "cup" },
- { class: 43, label: "fork" },
- { class: 44, label: "knife" },
- { class: 45, label: "spoon" },
- { class: 46, label: "bowl" },
- { class: 47, label: "banana" },
- { class: 48, label: "apple" },
- { class: 49, label: "sandwich" },
- { class: 50, label: "orange" },
- { class: 51, label: "broccoli" },
- { class: 52, label: "carrot" },
- { class: 53, label: "hot dog" },
- { class: 54, label: "pizza" },
- { class: 55, label: "donut" },
- { class: 56, label: "cake" },
- { class: 57, label: "chair" },
- { class: 58, label: "couch" },
- { class: 59, label: "potted plant" },
- { class: 60, label: "bed" },
- { class: 61, label: "dining table" },
- { class: 62, label: "toilet" },
- { class: 63, label: "tv" },
- { class: 64, label: "laptop" },
- { class: 65, label: "mouse" },
- { class: 66, label: "remote" },
- { class: 67, label: "keyboard" },
- { class: 68, label: "cell phone" },
- { class: 69, label: "microwave" },
- { class: 70, label: "oven" },
- { class: 71, label: "toaster" },
- { class: 72, label: "sink" },
- { class: 73, label: "refrigerator" },
- { class: 74, label: "book" },
- { class: 75, label: "clock" },
- { class: 76, label: "vase" },
- { class: 77, label: "scissors" },
- { class: 78, label: "teddy bear" },
- { class: 79, label: "hair drier" },
- { class: 80, label: "toothbrush" }
-];
-
-// src/object/centernet.ts
-var model6;
-var inputSize4 = 0;
-var last5 = [];
-var lastTime6 = 0;
-var skipped6 = Number.MAX_SAFE_INTEGER;
-async function load6(config3) {
- if (env.initial)
- model6 = null;
- if (!model6) {
- model6 = await loadModel(config3.object.modelPath);
- const inputs = (model6 == null ? void 0 : model6["executor"]) ? Object.values(model6.modelSignature["inputs"]) : void 0;
- inputSize4 = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;
- } else if (config3.debug)
- log("cached model:", model6["modelUrl"]);
- return model6;
-}
-async function process3(res, outputShape, config3) {
- if (!res)
- return [];
- const t = {};
- const results = [];
- const detections = await res.array();
- t.squeeze = tfjs_esm_exports.squeeze(res);
- const arr = tfjs_esm_exports.split(t.squeeze, 6, 1);
- t.stack = tfjs_esm_exports.stack([arr[1], arr[0], arr[3], arr[2]], 1);
- t.boxes = tfjs_esm_exports.squeeze(t.stack);
- t.scores = tfjs_esm_exports.squeeze(arr[4]);
- t.classes = tfjs_esm_exports.squeeze(arr[5]);
- tfjs_esm_exports.dispose([res, ...arr]);
- t.nms = await tfjs_esm_exports.image.nonMaxSuppressionAsync(t.boxes, t.scores, config3.object.maxDetected, config3.object.iouThreshold, config3.object.minConfidence || 0);
- const nms = await t.nms.data();
- let i = 0;
- for (const id of Array.from(nms)) {
- const score = Math.trunc(100 * detections[0][id][4]) / 100;
- const classVal = detections[0][id][5];
- const label = labels[classVal].label;
- const [x, y] = [
- detections[0][id][0] / inputSize4,
- detections[0][id][1] / inputSize4
- ];
- const boxRaw = [
- x,
- y,
- detections[0][id][2] / inputSize4 - x,
- detections[0][id][3] / inputSize4 - y
- ];
- const box = [
- Math.trunc(boxRaw[0] * outputShape[0]),
- Math.trunc(boxRaw[1] * outputShape[1]),
- Math.trunc(boxRaw[2] * outputShape[0]),
- Math.trunc(boxRaw[3] * outputShape[1])
- ];
- results.push({ id: i++, score, class: classVal, label, box, boxRaw });
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return results;
-}
-async function predict6(input, config3) {
- if (!(model6 == null ? void 0 : model6["executor"]))
- return [];
- const skipTime = (config3.object.skipTime || 0) > now() - lastTime6;
- const skipFrame = skipped6 < (config3.object.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame && last5.length > 0) {
- skipped6++;
- return last5;
- }
- skipped6 = 0;
- return new Promise(async (resolve) => {
- const outputSize2 = [input.shape[2] || 0, input.shape[1] || 0];
- const resize = tfjs_esm_exports.image.resizeBilinear(input, [inputSize4, inputSize4]);
- const objectT = config3.object.enabled ? model6 == null ? void 0 : model6.execute(resize, ["tower_0/detections"]) : null;
- lastTime6 = now();
- tfjs_esm_exports.dispose(resize);
- const obj = await process3(objectT, outputSize2, config3);
- last5 = obj;
- resolve(obj);
- });
-}
-
-// src/body/efficientposecoords.ts
-var efficientposecoords_exports = {};
-__export(efficientposecoords_exports, {
- connected: () => connected2,
- kpt: () => kpt2
-});
-var kpt2 = [
- "head",
- "neck",
- "rightShoulder",
- "rightElbow",
- "rightWrist",
- "chest",
- "leftShoulder",
- "leftElbow",
- "leftWrist",
- "bodyCenter",
- "rightHip",
- "rightKnee",
- "rightAnkle",
- "leftHip",
- "leftKnee",
- "leftAnkle"
-];
-var connected2 = {
- leftLeg: ["leftHip", "leftKnee", "leftAnkle"],
- rightLeg: ["rightHip", "rightKnee", "rightAnkle"],
- torso: ["leftShoulder", "rightShoulder", "rightHip", "leftHip", "leftShoulder"],
- leftArm: ["leftShoulder", "leftElbow", "leftWrist"],
- rightArm: ["rightShoulder", "rightElbow", "rightWrist"],
- head: []
-};
-
-// src/body/efficientpose.ts
-var model7;
-var lastTime7 = 0;
-var cache2 = { id: 0, keypoints: [], box: [0, 0, 0, 0], boxRaw: [0, 0, 0, 0], score: 0, annotations: {} };
-var skipped7 = Number.MAX_SAFE_INTEGER;
-async function load7(config3) {
- if (env.initial)
- model7 = null;
- if (!model7)
- model7 = await loadModel(config3.body.modelPath);
- else if (config3.debug)
- log("cached model:", model7["modelUrl"]);
- return model7;
-}
-async function max2d(inputs, minScore) {
- const [width, height] = inputs.shape;
- const reshaped = tfjs_esm_exports.reshape(inputs, [height * width]);
- const max4 = tfjs_esm_exports.max(reshaped, 0);
- const newScore = (await max4.data())[0];
- if (newScore > minScore) {
- const coordinates = tfjs_esm_exports.argMax(reshaped, 0);
- const mod3 = tfjs_esm_exports.mod(coordinates, width);
- const x = (await mod3.data())[0];
- const div14 = tfjs_esm_exports.div(coordinates, width);
- const y = (await div14.data())[0];
- tfjs_esm_exports.dispose([reshaped, max4, coordinates, mod3, div14]);
- return [x, y, newScore];
- }
- tfjs_esm_exports.dispose([reshaped, max4]);
- return [0, 0, newScore];
-}
-async function predict7(image26, config3) {
- if (!(model7 == null ? void 0 : model7["executor"]))
- return [];
- const skipTime = (config3.body.skipTime || 0) > now() - lastTime7;
- const skipFrame = skipped7 < (config3.body.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame && Object.keys(cache2.keypoints).length > 0) {
- skipped7++;
- return [cache2];
- }
- skipped7 = 0;
- return new Promise(async (resolve) => {
- const tensor3 = tfjs_esm_exports.tidy(() => {
- if (!(model7 == null ? void 0 : model7.inputs[0].shape))
- return null;
- const resize = tfjs_esm_exports.image.resizeBilinear(image26, [model7.inputs[0].shape[2], model7.inputs[0].shape[1]], false);
- const enhance2 = tfjs_esm_exports.mul(resize, constants.tf2);
- const norm = tfjs_esm_exports.sub(enhance2, constants.tf1);
- return norm;
- });
- let resT;
- if (config3.body.enabled)
- resT = model7 == null ? void 0 : model7.execute(tensor3);
- lastTime7 = now();
- tfjs_esm_exports.dispose(tensor3);
- if (resT) {
- cache2.keypoints.length = 0;
- const squeeze12 = tfjs_esm_exports.squeeze(resT);
- tfjs_esm_exports.dispose(resT);
- const stack5 = tfjs_esm_exports.unstack(squeeze12, 2);
- tfjs_esm_exports.dispose(squeeze12);
- for (let id = 0; id < stack5.length; id++) {
- const [x2, y2, partScore] = await max2d(stack5[id], config3.body.minConfidence);
- if (partScore > (config3.body.minConfidence || 0)) {
- cache2.keypoints.push({
- score: Math.round(100 * partScore) / 100,
- part: kpt2[id],
- positionRaw: [
- x2 / model7.inputs[0].shape[2],
- y2 / model7.inputs[0].shape[1]
- ],
- position: [
- Math.round(image26.shape[2] * x2 / model7.inputs[0].shape[2]),
- Math.round(image26.shape[1] * y2 / model7.inputs[0].shape[1])
- ]
- });
- }
- }
- stack5.forEach((s) => tfjs_esm_exports.dispose(s));
- }
- cache2.score = cache2.keypoints.reduce((prev, curr) => curr.score > prev ? curr.score : prev, 0);
- const x = cache2.keypoints.map((a) => a.position[0]);
- const y = cache2.keypoints.map((a) => a.position[1]);
- cache2.box = [
- Math.min(...x),
- Math.min(...y),
- Math.max(...x) - Math.min(...x),
- Math.max(...y) - Math.min(...y)
- ];
- const xRaw = cache2.keypoints.map((a) => a.positionRaw[0]);
- const yRaw = cache2.keypoints.map((a) => a.positionRaw[1]);
- cache2.boxRaw = [
- Math.min(...xRaw),
- Math.min(...yRaw),
- Math.max(...xRaw) - Math.min(...xRaw),
- Math.max(...yRaw) - Math.min(...yRaw)
- ];
- for (const [name, indexes] of Object.entries(connected2)) {
- const pt = [];
- for (let i = 0; i < indexes.length - 1; i++) {
- const pt0 = cache2.keypoints.find((kpt4) => kpt4.part === indexes[i]);
- const pt1 = cache2.keypoints.find((kpt4) => kpt4.part === indexes[i + 1]);
- if (pt0 && pt1 && pt0.score > (config3.body.minConfidence || 0) && pt1.score > (config3.body.minConfidence || 0))
- pt.push([pt0.position, pt1.position]);
- }
- cache2.annotations[name] = pt;
- }
- resolve([cache2]);
- });
-}
-
-// src/gear/emotion.ts
-var annotations = ["angry", "disgust", "fear", "happy", "sad", "surprise", "neutral"];
-var model8;
-var last6 = [];
-var lastCount5 = 0;
-var lastTime8 = 0;
-var skipped8 = Number.MAX_SAFE_INTEGER;
-async function load8(config3) {
- var _a;
- if (env.initial)
- model8 = null;
- if (!model8)
- model8 = await loadModel((_a = config3.face.emotion) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model8["modelUrl"]);
- return model8;
-}
-async function predict8(image26, config3, idx, count2) {
- var _a, _b;
- if (!model8)
- return [];
- const skipFrame = skipped8 < (((_a = config3.face.emotion) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face.emotion) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime8;
- if (config3.skipAllowed && skipTime && skipFrame && lastCount5 === count2 && last6[idx] && last6[idx].length > 0) {
- skipped8++;
- return last6[idx];
- }
- skipped8 = 0;
- return new Promise(async (resolve) => {
- var _a2;
- const obj = [];
- if ((_a2 = config3.face.emotion) == null ? void 0 : _a2.enabled) {
- const t = {};
- const inputSize10 = (model8 == null ? void 0 : model8.inputs[0].shape) ? model8.inputs[0].shape[2] : 0;
- t.resize = tfjs_esm_exports.image.resizeBilinear(image26, [inputSize10, inputSize10], false);
- t.channels = tfjs_esm_exports.mul(t.resize, constants.rgb);
- t.grayscale = tfjs_esm_exports.sum(t.channels, 3, true);
- t.grayscaleSub = tfjs_esm_exports.sub(t.grayscale, constants.tf05);
- t.grayscaleMul = tfjs_esm_exports.mul(t.grayscaleSub, constants.tf2);
- t.emotion = model8 == null ? void 0 : model8.execute(t.grayscaleMul);
- lastTime8 = now();
- const data = await t.emotion.data();
- for (let i = 0; i < data.length; i++) {
- if (data[i] > (config3.face.emotion.minConfidence || 0))
- obj.push({ score: Math.min(0.99, Math.trunc(100 * data[i]) / 100), emotion: annotations[i] });
- }
- obj.sort((a, b) => b.score - a.score);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- }
- last6[idx] = obj;
- lastCount5 = count2;
- resolve(obj);
- });
-}
-
-// src/face/mobilefacenet.ts
-var model9;
-var last7 = [];
-var lastCount6 = 0;
-var lastTime9 = 0;
-var skipped9 = Number.MAX_SAFE_INTEGER;
-async function load9(config3) {
- var _a;
- if (env.initial)
- model9 = null;
- if (!model9)
- model9 = await loadModel((_a = config3.face["mobilefacenet"]) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model9["modelUrl"]);
- return model9;
-}
-async function predict9(input, config3, idx, count2) {
- var _a, _b;
- if (!(model9 == null ? void 0 : model9["executor"]))
- return [];
- const skipFrame = skipped9 < (((_a = config3.face["mobilefacenet"]) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face["mobilefacenet"]) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime9;
- if (config3.skipAllowed && skipTime && skipFrame && lastCount6 === count2 && last7[idx]) {
- skipped9++;
- return last7[idx];
- }
- return new Promise(async (resolve) => {
- var _a2;
- let data = [];
- if (((_a2 = config3.face["mobilefacenet"]) == null ? void 0 : _a2.enabled) && (model9 == null ? void 0 : model9.inputs[0].shape)) {
- const t = {};
- t.crop = tfjs_esm_exports.image.resizeBilinear(input, [model9.inputs[0].shape[2], model9.inputs[0].shape[1]], false);
- t.data = model9.execute(t.crop);
- const output = await t.data.data();
- data = Array.from(output);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- }
- last7[idx] = data;
- lastCount6 = count2;
- lastTime9 = now();
- resolve(data);
- });
-}
-
-// src/face/insightface.ts
-var model10;
-var last8 = [];
-var lastCount7 = 0;
-var lastTime10 = 0;
-var skipped10 = Number.MAX_SAFE_INTEGER;
-async function load10(config3) {
- if (env.initial)
- model10 = null;
- if (!model10)
- model10 = await loadModel(config3.face["insightface"].modelPath);
- else if (config3.debug)
- log("cached model:", model10["modelUrl"]);
- return model10;
-}
-async function predict10(input, config3, idx, count2) {
- var _a, _b;
- if (!(model10 == null ? void 0 : model10["executor"]))
- return [];
- const skipFrame = skipped10 < (((_a = config3.face["insightface"]) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face["insightface"]) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime10;
- if (config3.skipAllowed && skipTime && skipFrame && lastCount7 === count2 && last8[idx]) {
- skipped10++;
- return last8[idx];
- }
- return new Promise(async (resolve) => {
- var _a2;
- let data = [];
- if (((_a2 = config3.face["insightface"]) == null ? void 0 : _a2.enabled) && (model10 == null ? void 0 : model10.inputs[0].shape)) {
- const t = {};
- t.crop = tfjs_esm_exports.image.resizeBilinear(input, [model10.inputs[0].shape[2], model10.inputs[0].shape[1]], false);
- t.data = model10.execute(t.crop);
- const output = await t.data.data();
- data = Array.from(output);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- }
- last8[idx] = data;
- lastCount7 = count2;
- lastTime10 = now();
- resolve(data);
- });
-}
-
-// src/face/iris.ts
-var model11;
-var inputSize5 = 0;
-var irisEnlarge = 2.3;
-var leftOutline = meshAnnotations.leftEyeLower0;
-var rightOutline = meshAnnotations.rightEyeLower0;
-var eyeLandmarks = {
- leftBounds: [leftOutline[0], leftOutline[leftOutline.length - 1]],
- rightBounds: [rightOutline[0], rightOutline[rightOutline.length - 1]]
-};
-var irisLandmarks = {
- upperCenter: 3,
- lowerCenter: 4,
- index: 71,
- numCoordinates: 76
-};
-async function load11(config3) {
- var _a, _b;
- if (env.initial)
- model11 = null;
- if (!model11)
- model11 = await loadModel((_a = config3.face.iris) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model11["modelUrl"]);
- inputSize5 = (model11 == null ? void 0 : model11["executor"]) && ((_b = model11.inputs) == null ? void 0 : _b[0].shape) ? model11.inputs[0].shape[2] : 0;
- if (inputSize5 === -1)
- inputSize5 = 64;
- return model11;
-}
-function replaceIrisCoords(rawCoords, newCoords, prefix, keys) {
- for (let i = 0; i < irisIndices.length; i++) {
- const { key, indices } = irisIndices[i];
- const originalIndices = meshAnnotations[`${prefix}${key}`];
- if (!keys || keys.includes(key)) {
- for (let j = 0; j < indices.length; j++) {
- const index2 = indices[j];
- rawCoords[originalIndices[j]] = [
- newCoords[index2][0],
- newCoords[index2][1],
- (newCoords[index2][2] + rawCoords[originalIndices[j]][2]) / 2
- ];
- }
- }
- }
-}
-var getLeftToRightEyeDepthDifference = (rawCoords) => {
- const leftEyeZ = rawCoords[eyeLandmarks.leftBounds[0]][2];
- const rightEyeZ = rawCoords[eyeLandmarks.rightBounds[0]][2];
- return leftEyeZ - rightEyeZ;
-};
-var getEyeBox = (rawCoords, face4, eyeInnerCornerIndex, eyeOuterCornerIndex, meshSize, flip = false) => {
- const box = squarifyBox(enlargeBox(calculateLandmarksBoundingBox([rawCoords[eyeInnerCornerIndex], rawCoords[eyeOuterCornerIndex]]), irisEnlarge));
- const boxSize = getBoxSize(box);
- let crop = tfjs_esm_exports.image.cropAndResize(face4, [[
- box.startPoint[1] / meshSize,
- box.startPoint[0] / meshSize,
- box.endPoint[1] / meshSize,
- box.endPoint[0] / meshSize
- ]], [0], [inputSize5, inputSize5]);
- if (flip && env.kernels.includes("flipleftright")) {
- const flipped = tfjs_esm_exports.image.flipLeftRight(crop);
- tfjs_esm_exports.dispose(crop);
- crop = flipped;
- }
- return { box, boxSize, crop };
-};
-var getEyeCoords = (eyeData, eyeBox, eyeBoxSize, flip = false) => {
- const eyeRawCoords = [];
- for (let i = 0; i < irisLandmarks.numCoordinates; i++) {
- const x = eyeData[i * 3];
- const y = eyeData[i * 3 + 1];
- const z = eyeData[i * 3 + 2];
- eyeRawCoords.push([
- (flip ? 1 - x / inputSize5 : x / inputSize5) * eyeBoxSize[0] + eyeBox.startPoint[0],
- y / inputSize5 * eyeBoxSize[1] + eyeBox.startPoint[1],
- z
- ]);
- }
- return { rawCoords: eyeRawCoords, iris: eyeRawCoords.slice(irisLandmarks.index) };
-};
-var getAdjustedIrisCoords = (rawCoords, irisCoords, direction) => {
- const upperCenterZ = rawCoords[meshAnnotations[`${direction}EyeUpper0`][irisLandmarks.upperCenter]][2];
- const lowerCenterZ = rawCoords[meshAnnotations[`${direction}EyeLower0`][irisLandmarks.lowerCenter]][2];
- const averageZ = (upperCenterZ + lowerCenterZ) / 2;
- return irisCoords.map((coord, i) => {
- let z = averageZ;
- if (i === 2) {
- z = upperCenterZ;
- } else if (i === 4) {
- z = lowerCenterZ;
- }
- return [coord[0], coord[1], z];
- });
-};
-async function augmentIris(rawCoords, face4, meshSize) {
- if (!(model11 == null ? void 0 : model11["executor"]))
- return rawCoords;
- const { box: leftEyeBox, boxSize: leftEyeBoxSize, crop: leftEyeCrop } = getEyeBox(rawCoords, face4, eyeLandmarks.leftBounds[0], eyeLandmarks.leftBounds[1], meshSize, true);
- const { box: rightEyeBox, boxSize: rightEyeBoxSize, crop: rightEyeCrop } = getEyeBox(rawCoords, face4, eyeLandmarks.rightBounds[0], eyeLandmarks.rightBounds[1], meshSize, true);
- const combined = tfjs_esm_exports.concat([leftEyeCrop, rightEyeCrop]);
- tfjs_esm_exports.dispose(leftEyeCrop);
- tfjs_esm_exports.dispose(rightEyeCrop);
- const eyePredictions = model11.execute(combined);
- tfjs_esm_exports.dispose(combined);
- const eyePredictionsData = await eyePredictions.data();
- tfjs_esm_exports.dispose(eyePredictions);
- const leftEyeData = eyePredictionsData.slice(0, irisLandmarks.numCoordinates * 3);
- const { rawCoords: leftEyeRawCoords, iris: leftIrisRawCoords } = getEyeCoords(leftEyeData, leftEyeBox, leftEyeBoxSize, true);
- const rightEyeData = eyePredictionsData.slice(irisLandmarks.numCoordinates * 3);
- const { rawCoords: rightEyeRawCoords, iris: rightIrisRawCoords } = getEyeCoords(rightEyeData, rightEyeBox, rightEyeBoxSize, false);
- const leftToRightEyeDepthDifference = getLeftToRightEyeDepthDifference(rawCoords);
- if (Math.abs(leftToRightEyeDepthDifference) < 30) {
- replaceIrisCoords(rawCoords, leftEyeRawCoords, "left", null);
- replaceIrisCoords(rawCoords, rightEyeRawCoords, "right", null);
- } else if (leftToRightEyeDepthDifference < 1) {
- replaceIrisCoords(rawCoords, leftEyeRawCoords, "left", ["EyeUpper0", "EyeLower0"]);
- } else {
- replaceIrisCoords(rawCoords, rightEyeRawCoords, "right", ["EyeUpper0", "EyeLower0"]);
- }
- const adjustedLeftIrisCoords = getAdjustedIrisCoords(rawCoords, leftIrisRawCoords, "left");
- const adjustedRightIrisCoords = getAdjustedIrisCoords(rawCoords, rightIrisRawCoords, "right");
- const newCoords = rawCoords.concat(adjustedLeftIrisCoords).concat(adjustedRightIrisCoords);
- return newCoords;
-}
-
-// src/face/constants.ts
-var LIPS_CONNECTIONS = [
- [61, 146],
- [146, 91],
- [91, 181],
- [181, 84],
- [84, 17],
- [17, 314],
- [314, 405],
- [405, 321],
- [321, 375],
- [375, 291],
- [61, 185],
- [185, 40],
- [40, 39],
- [39, 37],
- [37, 0],
- [0, 267],
- [267, 269],
- [269, 270],
- [270, 409],
- [409, 291],
- [78, 95],
- [95, 88],
- [88, 178],
- [178, 87],
- [87, 14],
- [14, 317],
- [317, 402],
- [402, 318],
- [318, 324],
- [324, 308],
- [78, 191],
- [191, 80],
- [80, 81],
- [81, 82],
- [82, 13],
- [13, 312],
- [312, 311],
- [311, 310],
- [310, 415],
- [415, 308]
-];
-var LEFT_EYE_CONNECTIONS = [[263, 249], [249, 390], [390, 373], [373, 374], [374, 380], [380, 381], [381, 382], [382, 362], [263, 466], [466, 388], [388, 387], [387, 386], [386, 385], [385, 384], [384, 398], [398, 362]];
-var LEFT_EYEBROW_CONNECTIONS = [[276, 283], [283, 282], [282, 295], [295, 285], [300, 293], [293, 334], [334, 296], [296, 336]];
-var LEFT_IRIS_CONNECTIONS = [[474, 475], [475, 476], [476, 477], [477, 474]];
-var RIGHT_EYE_CONNECTIONS = [[33, 7], [7, 163], [163, 144], [144, 145], [145, 153], [153, 154], [154, 155], [155, 133], [33, 246], [246, 161], [161, 160], [160, 159], [159, 158], [158, 157], [157, 173], [173, 133]];
-var RIGHT_EYEBROW_CONNECTIONS = [[46, 53], [53, 52], [52, 65], [65, 55], [70, 63], [63, 105], [105, 66], [66, 107]];
-var RIGHT_IRIS_CONNECTIONS = [[469, 470], [470, 471], [471, 472], [472, 469]];
-var FACE_OVAL_CONNECTIONS = [
- [10, 338],
- [338, 297],
- [297, 332],
- [332, 284],
- [284, 251],
- [251, 389],
- [389, 356],
- [356, 454],
- [454, 323],
- [323, 361],
- [361, 288],
- [288, 397],
- [397, 365],
- [365, 379],
- [379, 378],
- [378, 400],
- [400, 377],
- [377, 152],
- [152, 148],
- [148, 176],
- [176, 149],
- [149, 150],
- [150, 136],
- [136, 172],
- [172, 58],
- [58, 132],
- [132, 93],
- [93, 234],
- [234, 127],
- [127, 162],
- [162, 21],
- [21, 54],
- [54, 103],
- [103, 67],
- [67, 109],
- [109, 10]
-];
-function connectionsToIndices2(connections) {
- const indices = connections.map((connection) => connection[0]);
- indices.push(connections[connections.length - 1][1]);
- return indices;
-}
-var MEDIAPIPE_FACE_MESH_KEYPOINTS_BY_CONTOUR = {
- lips: connectionsToIndices2(LIPS_CONNECTIONS),
- leftEye: connectionsToIndices2(LEFT_EYE_CONNECTIONS),
- leftEyebrow: connectionsToIndices2(LEFT_EYEBROW_CONNECTIONS),
- leftIris: connectionsToIndices2(LEFT_IRIS_CONNECTIONS),
- rightEye: connectionsToIndices2(RIGHT_EYE_CONNECTIONS),
- rightEyebrow: connectionsToIndices2(RIGHT_EYEBROW_CONNECTIONS),
- rightIris: connectionsToIndices2(RIGHT_IRIS_CONNECTIONS),
- faceOval: connectionsToIndices2(FACE_OVAL_CONNECTIONS)
-};
-var indexLabelPairs = Object.entries(MEDIAPIPE_FACE_MESH_KEYPOINTS_BY_CONTOUR).map(([label, indices]) => indices.map((index2) => [index2, label])).flat();
-var MEDIAPIPE_FACE_MESH_KEYPOINTS = new Map(indexLabelPairs);
-var LANDMARKS_REFINEMENT_LIPS_CONFIG = [
- 61,
- 146,
- 91,
- 181,
- 84,
- 17,
- 314,
- 405,
- 321,
- 375,
- 291,
- 185,
- 40,
- 39,
- 37,
- 0,
- 267,
- 269,
- 270,
- 409,
- 78,
- 95,
- 88,
- 178,
- 87,
- 14,
- 317,
- 402,
- 318,
- 324,
- 308,
- 191,
- 80,
- 81,
- 82,
- 13,
- 312,
- 311,
- 310,
- 415,
- 76,
- 77,
- 90,
- 180,
- 85,
- 16,
- 315,
- 404,
- 320,
- 307,
- 306,
- 184,
- 74,
- 73,
- 72,
- 11,
- 302,
- 303,
- 304,
- 408,
- 62,
- 96,
- 89,
- 179,
- 86,
- 15,
- 316,
- 403,
- 319,
- 325,
- 292,
- 183,
- 42,
- 41,
- 38,
- 12,
- 268,
- 271,
- 272,
- 407
-];
-var LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG = [
- 33,
- 7,
- 163,
- 144,
- 145,
- 153,
- 154,
- 155,
- 133,
- 246,
- 161,
- 160,
- 159,
- 158,
- 157,
- 173,
- 130,
- 25,
- 110,
- 24,
- 23,
- 22,
- 26,
- 112,
- 243,
- 247,
- 30,
- 29,
- 27,
- 28,
- 56,
- 190,
- 226,
- 31,
- 228,
- 229,
- 230,
- 231,
- 232,
- 233,
- 244,
- 113,
- 225,
- 224,
- 223,
- 222,
- 221,
- 189,
- 35,
- 124,
- 46,
- 53,
- 52,
- 65,
- 143,
- 111,
- 117,
- 118,
- 119,
- 120,
- 121,
- 128,
- 245,
- 156,
- 70,
- 63,
- 105,
- 66,
- 107,
- 55,
- 193
-];
-var LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG = [
- 263,
- 249,
- 390,
- 373,
- 374,
- 380,
- 381,
- 382,
- 362,
- 466,
- 388,
- 387,
- 386,
- 385,
- 384,
- 398,
- 359,
- 255,
- 339,
- 254,
- 253,
- 252,
- 256,
- 341,
- 463,
- 467,
- 260,
- 259,
- 257,
- 258,
- 286,
- 414,
- 446,
- 261,
- 448,
- 449,
- 450,
- 451,
- 452,
- 453,
- 464,
- 342,
- 445,
- 444,
- 443,
- 442,
- 441,
- 413,
- 265,
- 353,
- 276,
- 283,
- 282,
- 295,
- 372,
- 340,
- 346,
- 347,
- 348,
- 349,
- 350,
- 357,
- 465,
- 383,
- 300,
- 293,
- 334,
- 296,
- 336,
- 285,
- 417
-];
-
-// src/face/attention.ts
-async function augment(rawCoords, results) {
- const t = {
- lips: await results.filter((r) => r.size === 160)[0].data(),
- irisL: await results.filter((r) => r.size === 10)[0].data(),
- eyeL: await results.filter((r) => r.size === 142)[0].data(),
- irisR: await results.filter((r) => r.size === 10)[1].data(),
- eyeR: await results.filter((r) => r.size === 142)[1].data()
- };
- const irisLDepth = LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.reduce((prev, curr) => prev += rawCoords[curr][2], 0) / LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.length;
- for (let i = 0; i < t.irisL.length / 2; i++)
- rawCoords.push([t.irisL[2 * i + 0], t.irisL[2 * i + 1], irisLDepth]);
- const irisRDepth = LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.reduce((prev, curr) => prev += rawCoords[curr][2], 0) / LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.length;
- for (let i = 0; i < t.irisR.length / 2; i++)
- rawCoords.push([t.irisR[2 * i + 0], t.irisR[2 * i + 1], irisRDepth]);
- for (let i = 0; i < t.eyeL.length / 2; i++)
- rawCoords[LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG[i]] = [t.eyeL[2 * i + 0], t.eyeL[2 * i + 1], rawCoords[LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG[i]][2]];
- for (let i = 0; i < t.eyeR.length / 2; i++)
- rawCoords[LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG[i]] = [t.eyeR[2 * i + 0], t.eyeR[2 * i + 1], rawCoords[LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG[i]][2]];
- for (let i = 0; i < t.lips.length / 2; i++)
- rawCoords[LANDMARKS_REFINEMENT_LIPS_CONFIG[i]] = [t.lips[2 * i + 0], t.lips[2 * i + 1], rawCoords[LANDMARKS_REFINEMENT_LIPS_CONFIG[i]][2]];
- return rawCoords;
-}
-
-// src/face/facemesh.ts
-var cache3 = {
- boxes: [],
- skipped: Number.MAX_SAFE_INTEGER,
- timestamp: 0
-};
-var model12 = null;
-var inputSize6 = 0;
-async function predict11(input, config3) {
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
- if (!(model12 == null ? void 0 : model12["executor"]))
- return [];
- const skipTime = (((_a = config3.face.detector) == null ? void 0 : _a.skipTime) || 0) > now() - cache3.timestamp;
- const skipFrame = cache3.skipped < (((_b = config3.face.detector) == null ? void 0 : _b.skipFrames) || 0);
- if (!config3.skipAllowed || !skipTime || !skipFrame || cache3.boxes.length === 0) {
- cache3.boxes = await getBoxes(input, config3);
- cache3.timestamp = now();
- cache3.skipped = 0;
- } else {
- cache3.skipped++;
- }
- const faces = [];
- const newCache = [];
- let id = 0;
- const size2 = inputSize6;
- for (let i = 0; i < cache3.boxes.length; i++) {
- const box = cache3.boxes[i];
- let angle = 0;
- let rotationMatrix;
- const face4 = {
- id: id++,
- mesh: [],
- meshRaw: [],
- box: [0, 0, 0, 0],
- boxRaw: [0, 0, 0, 0],
- score: 0,
- boxScore: 0,
- faceScore: 0,
- annotations: {}
- };
- [angle, rotationMatrix, face4.tensor] = correctFaceRotation((_c = config3.face.detector) == null ? void 0 : _c.rotation, box, input, ((_d = config3.face.mesh) == null ? void 0 : _d.enabled) ? inputSize6 : size());
- if (config3.filter.equalization) {
- const equilized = face4.tensor ? await histogramEqualization(face4.tensor) : void 0;
- tfjs_esm_exports.dispose(face4.tensor);
- if (equilized)
- face4.tensor = equilized;
- }
- face4.boxScore = Math.round(100 * box.confidence) / 100;
- if (!((_e = config3.face.mesh) == null ? void 0 : _e.enabled)) {
- face4.box = clampBox(box, input);
- face4.boxRaw = getRawBox(box, input);
- face4.score = face4.boxScore;
- face4.mesh = box.landmarks.map((pt) => [
- (box.startPoint[0] + box.endPoint[0]) / 2 + (box.endPoint[0] + box.startPoint[0]) * pt[0] / size(),
- (box.startPoint[1] + box.endPoint[1]) / 2 + (box.endPoint[1] + box.startPoint[1]) * pt[1] / size()
- ]);
- face4.meshRaw = face4.mesh.map((pt) => [pt[0] / (input.shape[2] || 0), pt[1] / (input.shape[1] || 0), (pt[2] || 0) / size2]);
- for (const key of Object.keys(blazeFaceLandmarks)) {
- face4.annotations[key] = [face4.mesh[blazeFaceLandmarks[key]]];
- }
- } else if (!model12) {
- if (config3.debug)
- log("face mesh detection requested, but model is not loaded");
- } else {
- if (((_f = config3.face.attention) == null ? void 0 : _f.enabled) && !env.kernels.includes("atan2")) {
- tfjs_esm_exports.dispose(face4.tensor);
- return faces;
- }
- const results = model12.execute(face4.tensor);
- const confidenceT = results.find((t) => t.shape[t.shape.length - 1] === 1);
- const faceConfidence = await confidenceT.data();
- face4.faceScore = Math.round(100 * faceConfidence[0]) / 100;
- if (face4.faceScore < (((_g = config3.face.detector) == null ? void 0 : _g.minConfidence) || 1)) {
- box.confidence = face4.faceScore;
- if (config3.face.mesh.keepInvalid) {
- face4.box = clampBox(box, input);
- face4.boxRaw = getRawBox(box, input);
- face4.score = face4.boxScore;
- face4.mesh = box.landmarks.map((pt) => [
- (box.startPoint[0] + box.endPoint[0]) / 2 + (box.endPoint[0] + box.startPoint[0]) * pt[0] / size(),
- (box.startPoint[1] + box.endPoint[1]) / 2 + (box.endPoint[1] + box.startPoint[1]) * pt[1] / size()
- ]);
- face4.meshRaw = face4.mesh.map((pt) => [pt[0] / (input.shape[2] || 1), pt[1] / (input.shape[1] || 1), (pt[2] || 0) / size2]);
- for (const key of Object.keys(blazeFaceLandmarks)) {
- face4.annotations[key] = [face4.mesh[blazeFaceLandmarks[key]]];
- }
- }
- } else {
- const meshT = results.find((t) => t.shape[t.shape.length - 1] === 1404);
- const coordsReshaped = tfjs_esm_exports.reshape(meshT, [-1, 3]);
- let rawCoords = await coordsReshaped.array();
- tfjs_esm_exports.dispose(coordsReshaped);
- if ((_h = config3.face.attention) == null ? void 0 : _h.enabled) {
- rawCoords = await augment(rawCoords, results);
- } else if ((_i = config3.face.iris) == null ? void 0 : _i.enabled) {
- rawCoords = await augmentIris(rawCoords, face4.tensor, inputSize6);
- }
- face4.mesh = transformRawCoords(rawCoords, box, angle, rotationMatrix, inputSize6);
- face4.meshRaw = face4.mesh.map((pt) => [pt[0] / (input.shape[2] || 0), pt[1] / (input.shape[1] || 0), (pt[2] || 0) / size2]);
- for (const key of Object.keys(meshAnnotations))
- face4.annotations[key] = meshAnnotations[key].map((index2) => face4.mesh[index2]);
- face4.score = face4.faceScore;
- const calculatedBox = { ...calculateFaceBox(face4.mesh, box), confidence: box.confidence, landmarks: box.landmarks };
- face4.box = clampBox(calculatedBox, input);
- face4.boxRaw = getRawBox(calculatedBox, input);
- newCache.push(calculatedBox);
- }
- tfjs_esm_exports.dispose(results);
- }
- if (face4.score > (((_j = config3.face.detector) == null ? void 0 : _j.minConfidence) || 1))
- faces.push(face4);
- else
- tfjs_esm_exports.dispose(face4.tensor);
- }
- cache3.boxes = newCache;
- return faces;
-}
-async function load12(config3) {
- var _a, _b, _c, _d, _e, _f;
- if (env.initial)
- model12 = null;
- if (((_a = config3.face.attention) == null ? void 0 : _a.enabled) && (model12 == null ? void 0 : model12["signature"])) {
- if (Object.keys(((_b = model12 == null ? void 0 : model12["signature"]) == null ? void 0 : _b.outputs) || {}).length < 6)
- model12 = null;
- }
- if (!model12) {
- if ((_c = config3.face.attention) == null ? void 0 : _c.enabled)
- model12 = await loadModel(config3.face.attention.modelPath);
- else
- model12 = await loadModel((_d = config3.face.mesh) == null ? void 0 : _d.modelPath);
- } else if (config3.debug) {
- log("cached model:", model12["modelUrl"]);
- }
- inputSize6 = model12["executor"] && ((_e = model12 == null ? void 0 : model12.inputs) == null ? void 0 : _e[0].shape) ? (_f = model12 == null ? void 0 : model12.inputs) == null ? void 0 : _f[0].shape[2] : 256;
- return model12;
-}
-var triangulation = TRI468;
-var uvmap = UV468;
-
-// src/face/faceres.ts
-var model13;
-var last9 = [];
-var lastTime11 = 0;
-var lastCount8 = 0;
-var skipped11 = Number.MAX_SAFE_INTEGER;
-async function load13(config3) {
- var _a;
- if (env.initial)
- model13 = null;
- if (!model13)
- model13 = await loadModel((_a = config3.face.description) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model13["modelUrl"]);
- return model13;
-}
-function enhance(input) {
- const tensor3 = input.image || input.tensor || input;
- if (!(model13 == null ? void 0 : model13.inputs[0].shape))
- return tensor3;
- const crop = tfjs_esm_exports.image.resizeBilinear(tensor3, [model13.inputs[0].shape[2], model13.inputs[0].shape[1]], false);
- const norm = tfjs_esm_exports.mul(crop, constants.tf255);
- tfjs_esm_exports.dispose(crop);
- return norm;
-}
-async function predict12(image26, config3, idx, count2) {
- var _a, _b, _c, _d;
- if (!(model13 == null ? void 0 : model13["executor"]))
- return { age: 0, gender: "unknown", genderScore: 0, descriptor: [] };
- const skipFrame = skipped11 < (((_a = config3.face.description) == null ? void 0 : _a.skipFrames) || 0);
- const skipTime = (((_b = config3.face.description) == null ? void 0 : _b.skipTime) || 0) > now() - lastTime11;
- if (config3.skipAllowed && skipFrame && skipTime && lastCount8 === count2 && ((_c = last9[idx]) == null ? void 0 : _c.age) && ((_d = last9[idx]) == null ? void 0 : _d.age) > 0) {
- skipped11++;
- return last9[idx];
- }
- skipped11 = 0;
- return new Promise(async (resolve) => {
- var _a2;
- const obj = {
- age: 0,
- gender: "unknown",
- genderScore: 0,
- descriptor: []
- };
- if ((_a2 = config3.face.description) == null ? void 0 : _a2.enabled) {
- const enhanced = enhance(image26);
- const resT = model13 == null ? void 0 : model13.execute(enhanced);
- lastTime11 = now();
- tfjs_esm_exports.dispose(enhanced);
- const genderT = resT.find((t) => t.shape[1] === 1);
- const gender2 = await genderT.data();
- const confidence = Math.trunc(200 * Math.abs(gender2[0] - 0.5)) / 100;
- if (confidence > (config3.face.description.minConfidence || 0)) {
- obj.gender = gender2[0] <= 0.5 ? "female" : "male";
- obj.genderScore = Math.min(0.99, confidence);
- }
- const argmax = tfjs_esm_exports.argMax(resT.find((t) => t.shape[1] === 100), 1);
- const age2 = (await argmax.data())[0];
- tfjs_esm_exports.dispose(argmax);
- const ageT = resT.find((t) => t.shape[1] === 100);
- const all2 = await ageT.data();
- obj.age = Math.round(all2[age2 - 1] > all2[age2 + 1] ? 10 * age2 - 100 * all2[age2 - 1] : 10 * age2 + 100 * all2[age2 + 1]) / 10;
- const desc = resT.find((t) => t.shape[1] === 1024);
- const descriptor = desc ? await desc.data() : [];
- obj.descriptor = Array.from(descriptor);
- resT.forEach((t) => tfjs_esm_exports.dispose(t));
- }
- last9[idx] = obj;
- lastCount8 = count2;
- resolve(obj);
- });
-}
-
-// src/hand/handposeutil.ts
-function getBoxSize2(box) {
- return [
- Math.abs(box.endPoint[0] - box.startPoint[0]),
- Math.abs(box.endPoint[1] - box.startPoint[1])
- ];
-}
-function getBoxCenter2(box) {
- return [
- box.startPoint[0] + (box.endPoint[0] - box.startPoint[0]) / 2,
- box.startPoint[1] + (box.endPoint[1] - box.startPoint[1]) / 2
- ];
-}
-function cutBoxFromImageAndResize(box, image26, cropSize) {
- const h = image26.shape[1];
- const w = image26.shape[2];
- const boxes = [[
- box.startPoint[1] / h,
- box.startPoint[0] / w,
- box.endPoint[1] / h,
- box.endPoint[0] / w
- ]];
- return tfjs_esm_exports.image.cropAndResize(image26, boxes, [0], cropSize);
-}
-function scaleBoxCoordinates2(box, factor) {
- const startPoint = [box.startPoint[0] * factor[0], box.startPoint[1] * factor[1]];
- const endPoint = [box.endPoint[0] * factor[0], box.endPoint[1] * factor[1]];
- const palmLandmarks = box.palmLandmarks.map((coord) => {
- const scaledCoord = [coord[0] * factor[0], coord[1] * factor[1]];
- return scaledCoord;
- });
- return { startPoint, endPoint, palmLandmarks, confidence: box.confidence };
-}
-function enlargeBox2(box, factor = 1.5) {
- const center = getBoxCenter2(box);
- const size2 = getBoxSize2(box);
- const newHalfSize = [factor * size2[0] / 2, factor * size2[1] / 2];
- const startPoint = [center[0] - newHalfSize[0], center[1] - newHalfSize[1]];
- const endPoint = [center[0] + newHalfSize[0], center[1] + newHalfSize[1]];
- return { startPoint, endPoint, palmLandmarks: box.palmLandmarks };
-}
-function squarifyBox2(box) {
- const centers = getBoxCenter2(box);
- const size2 = getBoxSize2(box);
- const maxEdge = Math.max(...size2);
- const halfSize = maxEdge / 2;
- const startPoint = [centers[0] - halfSize, centers[1] - halfSize];
- const endPoint = [centers[0] + halfSize, centers[1] + halfSize];
- return { startPoint, endPoint, palmLandmarks: box.palmLandmarks };
-}
-function normalizeRadians2(angle) {
- return angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI));
-}
-function computeRotation2(point1, point2) {
- const radians = Math.PI / 2 - Math.atan2(-(point2[1] - point1[1]), point2[0] - point1[0]);
- return normalizeRadians2(radians);
-}
-var buildTranslationMatrix2 = (x, y) => [[1, 0, x], [0, 1, y], [0, 0, 1]];
-function dot2(v1, v2) {
- let product = 0;
- for (let i = 0; i < v1.length; i++) {
- product += v1[i] * v2[i];
- }
- return product;
-}
-function getColumnFrom2DArr2(arr, columnIndex) {
- const column = [];
- for (let i = 0; i < arr.length; i++) {
- column.push(arr[i][columnIndex]);
- }
- return column;
-}
-function multiplyTransformMatrices2(mat1, mat2) {
- const product = [];
- const size2 = mat1.length;
- for (let row = 0; row < size2; row++) {
- product.push([]);
- for (let col = 0; col < size2; col++) {
- product[row].push(dot2(mat1[row], getColumnFrom2DArr2(mat2, col)));
- }
- }
- return product;
-}
-function buildRotationMatrix2(rotation, center) {
- const cosA = Math.cos(rotation);
- const sinA = Math.sin(rotation);
- const rotationMatrix = [[cosA, -sinA, 0], [sinA, cosA, 0], [0, 0, 1]];
- const translationMatrix = buildTranslationMatrix2(center[0], center[1]);
- const translationTimesRotation = multiplyTransformMatrices2(translationMatrix, rotationMatrix);
- const negativeTranslationMatrix = buildTranslationMatrix2(-center[0], -center[1]);
- return multiplyTransformMatrices2(translationTimesRotation, negativeTranslationMatrix);
-}
-function invertTransformMatrix2(matrix) {
- const rotationComponent = [[matrix[0][0], matrix[1][0]], [matrix[0][1], matrix[1][1]]];
- const translationComponent = [matrix[0][2], matrix[1][2]];
- const invertedTranslation = [
- -dot2(rotationComponent[0], translationComponent),
- -dot2(rotationComponent[1], translationComponent)
- ];
- return [
- rotationComponent[0].concat(invertedTranslation[0]),
- rotationComponent[1].concat(invertedTranslation[1]),
- [0, 0, 1]
- ];
-}
-function rotatePoint2(homogeneousCoordinate, rotationMatrix) {
- return [
- dot2(homogeneousCoordinate, rotationMatrix[0]),
- dot2(homogeneousCoordinate, rotationMatrix[1])
- ];
-}
-
-// src/hand/handposeanchors.ts
-var anchors2 = [
- { x: 0.015625, y: 0.015625 },
- { x: 0.015625, y: 0.015625 },
- { x: 0.046875, y: 0.015625 },
- { x: 0.046875, y: 0.015625 },
- { x: 0.078125, y: 0.015625 },
- { x: 0.078125, y: 0.015625 },
- { x: 0.109375, y: 0.015625 },
- { x: 0.109375, y: 0.015625 },
- { x: 0.140625, y: 0.015625 },
- { x: 0.140625, y: 0.015625 },
- { x: 0.171875, y: 0.015625 },
- { x: 0.171875, y: 0.015625 },
- { x: 0.203125, y: 0.015625 },
- { x: 0.203125, y: 0.015625 },
- { x: 0.234375, y: 0.015625 },
- { x: 0.234375, y: 0.015625 },
- { x: 0.265625, y: 0.015625 },
- { x: 0.265625, y: 0.015625 },
- { x: 0.296875, y: 0.015625 },
- { x: 0.296875, y: 0.015625 },
- { x: 0.328125, y: 0.015625 },
- { x: 0.328125, y: 0.015625 },
- { x: 0.359375, y: 0.015625 },
- { x: 0.359375, y: 0.015625 },
- { x: 0.390625, y: 0.015625 },
- { x: 0.390625, y: 0.015625 },
- { x: 0.421875, y: 0.015625 },
- { x: 0.421875, y: 0.015625 },
- { x: 0.453125, y: 0.015625 },
- { x: 0.453125, y: 0.015625 },
- { x: 0.484375, y: 0.015625 },
- { x: 0.484375, y: 0.015625 },
- { x: 0.515625, y: 0.015625 },
- { x: 0.515625, y: 0.015625 },
- { x: 0.546875, y: 0.015625 },
- { x: 0.546875, y: 0.015625 },
- { x: 0.578125, y: 0.015625 },
- { x: 0.578125, y: 0.015625 },
- { x: 0.609375, y: 0.015625 },
- { x: 0.609375, y: 0.015625 },
- { x: 0.640625, y: 0.015625 },
- { x: 0.640625, y: 0.015625 },
- { x: 0.671875, y: 0.015625 },
- { x: 0.671875, y: 0.015625 },
- { x: 0.703125, y: 0.015625 },
- { x: 0.703125, y: 0.015625 },
- { x: 0.734375, y: 0.015625 },
- { x: 0.734375, y: 0.015625 },
- { x: 0.765625, y: 0.015625 },
- { x: 0.765625, y: 0.015625 },
- { x: 0.796875, y: 0.015625 },
- { x: 0.796875, y: 0.015625 },
- { x: 0.828125, y: 0.015625 },
- { x: 0.828125, y: 0.015625 },
- { x: 0.859375, y: 0.015625 },
- { x: 0.859375, y: 0.015625 },
- { x: 0.890625, y: 0.015625 },
- { x: 0.890625, y: 0.015625 },
- { x: 0.921875, y: 0.015625 },
- { x: 0.921875, y: 0.015625 },
- { x: 0.953125, y: 0.015625 },
- { x: 0.953125, y: 0.015625 },
- { x: 0.984375, y: 0.015625 },
- { x: 0.984375, y: 0.015625 },
- { x: 0.015625, y: 0.046875 },
- { x: 0.015625, y: 0.046875 },
- { x: 0.046875, y: 0.046875 },
- { x: 0.046875, y: 0.046875 },
- { x: 0.078125, y: 0.046875 },
- { x: 0.078125, y: 0.046875 },
- { x: 0.109375, y: 0.046875 },
- { x: 0.109375, y: 0.046875 },
- { x: 0.140625, y: 0.046875 },
- { x: 0.140625, y: 0.046875 },
- { x: 0.171875, y: 0.046875 },
- { x: 0.171875, y: 0.046875 },
- { x: 0.203125, y: 0.046875 },
- { x: 0.203125, y: 0.046875 },
- { x: 0.234375, y: 0.046875 },
- { x: 0.234375, y: 0.046875 },
- { x: 0.265625, y: 0.046875 },
- { x: 0.265625, y: 0.046875 },
- { x: 0.296875, y: 0.046875 },
- { x: 0.296875, y: 0.046875 },
- { x: 0.328125, y: 0.046875 },
- { x: 0.328125, y: 0.046875 },
- { x: 0.359375, y: 0.046875 },
- { x: 0.359375, y: 0.046875 },
- { x: 0.390625, y: 0.046875 },
- { x: 0.390625, y: 0.046875 },
- { x: 0.421875, y: 0.046875 },
- { x: 0.421875, y: 0.046875 },
- { x: 0.453125, y: 0.046875 },
- { x: 0.453125, y: 0.046875 },
- { x: 0.484375, y: 0.046875 },
- { x: 0.484375, y: 0.046875 },
- { x: 0.515625, y: 0.046875 },
- { x: 0.515625, y: 0.046875 },
- { x: 0.546875, y: 0.046875 },
- { x: 0.546875, y: 0.046875 },
- { x: 0.578125, y: 0.046875 },
- { x: 0.578125, y: 0.046875 },
- { x: 0.609375, y: 0.046875 },
- { x: 0.609375, y: 0.046875 },
- { x: 0.640625, y: 0.046875 },
- { x: 0.640625, y: 0.046875 },
- { x: 0.671875, y: 0.046875 },
- { x: 0.671875, y: 0.046875 },
- { x: 0.703125, y: 0.046875 },
- { x: 0.703125, y: 0.046875 },
- { x: 0.734375, y: 0.046875 },
- { x: 0.734375, y: 0.046875 },
- { x: 0.765625, y: 0.046875 },
- { x: 0.765625, y: 0.046875 },
- { x: 0.796875, y: 0.046875 },
- { x: 0.796875, y: 0.046875 },
- { x: 0.828125, y: 0.046875 },
- { x: 0.828125, y: 0.046875 },
- { x: 0.859375, y: 0.046875 },
- { x: 0.859375, y: 0.046875 },
- { x: 0.890625, y: 0.046875 },
- { x: 0.890625, y: 0.046875 },
- { x: 0.921875, y: 0.046875 },
- { x: 0.921875, y: 0.046875 },
- { x: 0.953125, y: 0.046875 },
- { x: 0.953125, y: 0.046875 },
- { x: 0.984375, y: 0.046875 },
- { x: 0.984375, y: 0.046875 },
- { x: 0.015625, y: 0.078125 },
- { x: 0.015625, y: 0.078125 },
- { x: 0.046875, y: 0.078125 },
- { x: 0.046875, y: 0.078125 },
- { x: 0.078125, y: 0.078125 },
- { x: 0.078125, y: 0.078125 },
- { x: 0.109375, y: 0.078125 },
- { x: 0.109375, y: 0.078125 },
- { x: 0.140625, y: 0.078125 },
- { x: 0.140625, y: 0.078125 },
- { x: 0.171875, y: 0.078125 },
- { x: 0.171875, y: 0.078125 },
- { x: 0.203125, y: 0.078125 },
- { x: 0.203125, y: 0.078125 },
- { x: 0.234375, y: 0.078125 },
- { x: 0.234375, y: 0.078125 },
- { x: 0.265625, y: 0.078125 },
- { x: 0.265625, y: 0.078125 },
- { x: 0.296875, y: 0.078125 },
- { x: 0.296875, y: 0.078125 },
- { x: 0.328125, y: 0.078125 },
- { x: 0.328125, y: 0.078125 },
- { x: 0.359375, y: 0.078125 },
- { x: 0.359375, y: 0.078125 },
- { x: 0.390625, y: 0.078125 },
- { x: 0.390625, y: 0.078125 },
- { x: 0.421875, y: 0.078125 },
- { x: 0.421875, y: 0.078125 },
- { x: 0.453125, y: 0.078125 },
- { x: 0.453125, y: 0.078125 },
- { x: 0.484375, y: 0.078125 },
- { x: 0.484375, y: 0.078125 },
- { x: 0.515625, y: 0.078125 },
- { x: 0.515625, y: 0.078125 },
- { x: 0.546875, y: 0.078125 },
- { x: 0.546875, y: 0.078125 },
- { x: 0.578125, y: 0.078125 },
- { x: 0.578125, y: 0.078125 },
- { x: 0.609375, y: 0.078125 },
- { x: 0.609375, y: 0.078125 },
- { x: 0.640625, y: 0.078125 },
- { x: 0.640625, y: 0.078125 },
- { x: 0.671875, y: 0.078125 },
- { x: 0.671875, y: 0.078125 },
- { x: 0.703125, y: 0.078125 },
- { x: 0.703125, y: 0.078125 },
- { x: 0.734375, y: 0.078125 },
- { x: 0.734375, y: 0.078125 },
- { x: 0.765625, y: 0.078125 },
- { x: 0.765625, y: 0.078125 },
- { x: 0.796875, y: 0.078125 },
- { x: 0.796875, y: 0.078125 },
- { x: 0.828125, y: 0.078125 },
- { x: 0.828125, y: 0.078125 },
- { x: 0.859375, y: 0.078125 },
- { x: 0.859375, y: 0.078125 },
- { x: 0.890625, y: 0.078125 },
- { x: 0.890625, y: 0.078125 },
- { x: 0.921875, y: 0.078125 },
- { x: 0.921875, y: 0.078125 },
- { x: 0.953125, y: 0.078125 },
- { x: 0.953125, y: 0.078125 },
- { x: 0.984375, y: 0.078125 },
- { x: 0.984375, y: 0.078125 },
- { x: 0.015625, y: 0.109375 },
- { x: 0.015625, y: 0.109375 },
- { x: 0.046875, y: 0.109375 },
- { x: 0.046875, y: 0.109375 },
- { x: 0.078125, y: 0.109375 },
- { x: 0.078125, y: 0.109375 },
- { x: 0.109375, y: 0.109375 },
- { x: 0.109375, y: 0.109375 },
- { x: 0.140625, y: 0.109375 },
- { x: 0.140625, y: 0.109375 },
- { x: 0.171875, y: 0.109375 },
- { x: 0.171875, y: 0.109375 },
- { x: 0.203125, y: 0.109375 },
- { x: 0.203125, y: 0.109375 },
- { x: 0.234375, y: 0.109375 },
- { x: 0.234375, y: 0.109375 },
- { x: 0.265625, y: 0.109375 },
- { x: 0.265625, y: 0.109375 },
- { x: 0.296875, y: 0.109375 },
- { x: 0.296875, y: 0.109375 },
- { x: 0.328125, y: 0.109375 },
- { x: 0.328125, y: 0.109375 },
- { x: 0.359375, y: 0.109375 },
- { x: 0.359375, y: 0.109375 },
- { x: 0.390625, y: 0.109375 },
- { x: 0.390625, y: 0.109375 },
- { x: 0.421875, y: 0.109375 },
- { x: 0.421875, y: 0.109375 },
- { x: 0.453125, y: 0.109375 },
- { x: 0.453125, y: 0.109375 },
- { x: 0.484375, y: 0.109375 },
- { x: 0.484375, y: 0.109375 },
- { x: 0.515625, y: 0.109375 },
- { x: 0.515625, y: 0.109375 },
- { x: 0.546875, y: 0.109375 },
- { x: 0.546875, y: 0.109375 },
- { x: 0.578125, y: 0.109375 },
- { x: 0.578125, y: 0.109375 },
- { x: 0.609375, y: 0.109375 },
- { x: 0.609375, y: 0.109375 },
- { x: 0.640625, y: 0.109375 },
- { x: 0.640625, y: 0.109375 },
- { x: 0.671875, y: 0.109375 },
- { x: 0.671875, y: 0.109375 },
- { x: 0.703125, y: 0.109375 },
- { x: 0.703125, y: 0.109375 },
- { x: 0.734375, y: 0.109375 },
- { x: 0.734375, y: 0.109375 },
- { x: 0.765625, y: 0.109375 },
- { x: 0.765625, y: 0.109375 },
- { x: 0.796875, y: 0.109375 },
- { x: 0.796875, y: 0.109375 },
- { x: 0.828125, y: 0.109375 },
- { x: 0.828125, y: 0.109375 },
- { x: 0.859375, y: 0.109375 },
- { x: 0.859375, y: 0.109375 },
- { x: 0.890625, y: 0.109375 },
- { x: 0.890625, y: 0.109375 },
- { x: 0.921875, y: 0.109375 },
- { x: 0.921875, y: 0.109375 },
- { x: 0.953125, y: 0.109375 },
- { x: 0.953125, y: 0.109375 },
- { x: 0.984375, y: 0.109375 },
- { x: 0.984375, y: 0.109375 },
- { x: 0.015625, y: 0.140625 },
- { x: 0.015625, y: 0.140625 },
- { x: 0.046875, y: 0.140625 },
- { x: 0.046875, y: 0.140625 },
- { x: 0.078125, y: 0.140625 },
- { x: 0.078125, y: 0.140625 },
- { x: 0.109375, y: 0.140625 },
- { x: 0.109375, y: 0.140625 },
- { x: 0.140625, y: 0.140625 },
- { x: 0.140625, y: 0.140625 },
- { x: 0.171875, y: 0.140625 },
- { x: 0.171875, y: 0.140625 },
- { x: 0.203125, y: 0.140625 },
- { x: 0.203125, y: 0.140625 },
- { x: 0.234375, y: 0.140625 },
- { x: 0.234375, y: 0.140625 },
- { x: 0.265625, y: 0.140625 },
- { x: 0.265625, y: 0.140625 },
- { x: 0.296875, y: 0.140625 },
- { x: 0.296875, y: 0.140625 },
- { x: 0.328125, y: 0.140625 },
- { x: 0.328125, y: 0.140625 },
- { x: 0.359375, y: 0.140625 },
- { x: 0.359375, y: 0.140625 },
- { x: 0.390625, y: 0.140625 },
- { x: 0.390625, y: 0.140625 },
- { x: 0.421875, y: 0.140625 },
- { x: 0.421875, y: 0.140625 },
- { x: 0.453125, y: 0.140625 },
- { x: 0.453125, y: 0.140625 },
- { x: 0.484375, y: 0.140625 },
- { x: 0.484375, y: 0.140625 },
- { x: 0.515625, y: 0.140625 },
- { x: 0.515625, y: 0.140625 },
- { x: 0.546875, y: 0.140625 },
- { x: 0.546875, y: 0.140625 },
- { x: 0.578125, y: 0.140625 },
- { x: 0.578125, y: 0.140625 },
- { x: 0.609375, y: 0.140625 },
- { x: 0.609375, y: 0.140625 },
- { x: 0.640625, y: 0.140625 },
- { x: 0.640625, y: 0.140625 },
- { x: 0.671875, y: 0.140625 },
- { x: 0.671875, y: 0.140625 },
- { x: 0.703125, y: 0.140625 },
- { x: 0.703125, y: 0.140625 },
- { x: 0.734375, y: 0.140625 },
- { x: 0.734375, y: 0.140625 },
- { x: 0.765625, y: 0.140625 },
- { x: 0.765625, y: 0.140625 },
- { x: 0.796875, y: 0.140625 },
- { x: 0.796875, y: 0.140625 },
- { x: 0.828125, y: 0.140625 },
- { x: 0.828125, y: 0.140625 },
- { x: 0.859375, y: 0.140625 },
- { x: 0.859375, y: 0.140625 },
- { x: 0.890625, y: 0.140625 },
- { x: 0.890625, y: 0.140625 },
- { x: 0.921875, y: 0.140625 },
- { x: 0.921875, y: 0.140625 },
- { x: 0.953125, y: 0.140625 },
- { x: 0.953125, y: 0.140625 },
- { x: 0.984375, y: 0.140625 },
- { x: 0.984375, y: 0.140625 },
- { x: 0.015625, y: 0.171875 },
- { x: 0.015625, y: 0.171875 },
- { x: 0.046875, y: 0.171875 },
- { x: 0.046875, y: 0.171875 },
- { x: 0.078125, y: 0.171875 },
- { x: 0.078125, y: 0.171875 },
- { x: 0.109375, y: 0.171875 },
- { x: 0.109375, y: 0.171875 },
- { x: 0.140625, y: 0.171875 },
- { x: 0.140625, y: 0.171875 },
- { x: 0.171875, y: 0.171875 },
- { x: 0.171875, y: 0.171875 },
- { x: 0.203125, y: 0.171875 },
- { x: 0.203125, y: 0.171875 },
- { x: 0.234375, y: 0.171875 },
- { x: 0.234375, y: 0.171875 },
- { x: 0.265625, y: 0.171875 },
- { x: 0.265625, y: 0.171875 },
- { x: 0.296875, y: 0.171875 },
- { x: 0.296875, y: 0.171875 },
- { x: 0.328125, y: 0.171875 },
- { x: 0.328125, y: 0.171875 },
- { x: 0.359375, y: 0.171875 },
- { x: 0.359375, y: 0.171875 },
- { x: 0.390625, y: 0.171875 },
- { x: 0.390625, y: 0.171875 },
- { x: 0.421875, y: 0.171875 },
- { x: 0.421875, y: 0.171875 },
- { x: 0.453125, y: 0.171875 },
- { x: 0.453125, y: 0.171875 },
- { x: 0.484375, y: 0.171875 },
- { x: 0.484375, y: 0.171875 },
- { x: 0.515625, y: 0.171875 },
- { x: 0.515625, y: 0.171875 },
- { x: 0.546875, y: 0.171875 },
- { x: 0.546875, y: 0.171875 },
- { x: 0.578125, y: 0.171875 },
- { x: 0.578125, y: 0.171875 },
- { x: 0.609375, y: 0.171875 },
- { x: 0.609375, y: 0.171875 },
- { x: 0.640625, y: 0.171875 },
- { x: 0.640625, y: 0.171875 },
- { x: 0.671875, y: 0.171875 },
- { x: 0.671875, y: 0.171875 },
- { x: 0.703125, y: 0.171875 },
- { x: 0.703125, y: 0.171875 },
- { x: 0.734375, y: 0.171875 },
- { x: 0.734375, y: 0.171875 },
- { x: 0.765625, y: 0.171875 },
- { x: 0.765625, y: 0.171875 },
- { x: 0.796875, y: 0.171875 },
- { x: 0.796875, y: 0.171875 },
- { x: 0.828125, y: 0.171875 },
- { x: 0.828125, y: 0.171875 },
- { x: 0.859375, y: 0.171875 },
- { x: 0.859375, y: 0.171875 },
- { x: 0.890625, y: 0.171875 },
- { x: 0.890625, y: 0.171875 },
- { x: 0.921875, y: 0.171875 },
- { x: 0.921875, y: 0.171875 },
- { x: 0.953125, y: 0.171875 },
- { x: 0.953125, y: 0.171875 },
- { x: 0.984375, y: 0.171875 },
- { x: 0.984375, y: 0.171875 },
- { x: 0.015625, y: 0.203125 },
- { x: 0.015625, y: 0.203125 },
- { x: 0.046875, y: 0.203125 },
- { x: 0.046875, y: 0.203125 },
- { x: 0.078125, y: 0.203125 },
- { x: 0.078125, y: 0.203125 },
- { x: 0.109375, y: 0.203125 },
- { x: 0.109375, y: 0.203125 },
- { x: 0.140625, y: 0.203125 },
- { x: 0.140625, y: 0.203125 },
- { x: 0.171875, y: 0.203125 },
- { x: 0.171875, y: 0.203125 },
- { x: 0.203125, y: 0.203125 },
- { x: 0.203125, y: 0.203125 },
- { x: 0.234375, y: 0.203125 },
- { x: 0.234375, y: 0.203125 },
- { x: 0.265625, y: 0.203125 },
- { x: 0.265625, y: 0.203125 },
- { x: 0.296875, y: 0.203125 },
- { x: 0.296875, y: 0.203125 },
- { x: 0.328125, y: 0.203125 },
- { x: 0.328125, y: 0.203125 },
- { x: 0.359375, y: 0.203125 },
- { x: 0.359375, y: 0.203125 },
- { x: 0.390625, y: 0.203125 },
- { x: 0.390625, y: 0.203125 },
- { x: 0.421875, y: 0.203125 },
- { x: 0.421875, y: 0.203125 },
- { x: 0.453125, y: 0.203125 },
- { x: 0.453125, y: 0.203125 },
- { x: 0.484375, y: 0.203125 },
- { x: 0.484375, y: 0.203125 },
- { x: 0.515625, y: 0.203125 },
- { x: 0.515625, y: 0.203125 },
- { x: 0.546875, y: 0.203125 },
- { x: 0.546875, y: 0.203125 },
- { x: 0.578125, y: 0.203125 },
- { x: 0.578125, y: 0.203125 },
- { x: 0.609375, y: 0.203125 },
- { x: 0.609375, y: 0.203125 },
- { x: 0.640625, y: 0.203125 },
- { x: 0.640625, y: 0.203125 },
- { x: 0.671875, y: 0.203125 },
- { x: 0.671875, y: 0.203125 },
- { x: 0.703125, y: 0.203125 },
- { x: 0.703125, y: 0.203125 },
- { x: 0.734375, y: 0.203125 },
- { x: 0.734375, y: 0.203125 },
- { x: 0.765625, y: 0.203125 },
- { x: 0.765625, y: 0.203125 },
- { x: 0.796875, y: 0.203125 },
- { x: 0.796875, y: 0.203125 },
- { x: 0.828125, y: 0.203125 },
- { x: 0.828125, y: 0.203125 },
- { x: 0.859375, y: 0.203125 },
- { x: 0.859375, y: 0.203125 },
- { x: 0.890625, y: 0.203125 },
- { x: 0.890625, y: 0.203125 },
- { x: 0.921875, y: 0.203125 },
- { x: 0.921875, y: 0.203125 },
- { x: 0.953125, y: 0.203125 },
- { x: 0.953125, y: 0.203125 },
- { x: 0.984375, y: 0.203125 },
- { x: 0.984375, y: 0.203125 },
- { x: 0.015625, y: 0.234375 },
- { x: 0.015625, y: 0.234375 },
- { x: 0.046875, y: 0.234375 },
- { x: 0.046875, y: 0.234375 },
- { x: 0.078125, y: 0.234375 },
- { x: 0.078125, y: 0.234375 },
- { x: 0.109375, y: 0.234375 },
- { x: 0.109375, y: 0.234375 },
- { x: 0.140625, y: 0.234375 },
- { x: 0.140625, y: 0.234375 },
- { x: 0.171875, y: 0.234375 },
- { x: 0.171875, y: 0.234375 },
- { x: 0.203125, y: 0.234375 },
- { x: 0.203125, y: 0.234375 },
- { x: 0.234375, y: 0.234375 },
- { x: 0.234375, y: 0.234375 },
- { x: 0.265625, y: 0.234375 },
- { x: 0.265625, y: 0.234375 },
- { x: 0.296875, y: 0.234375 },
- { x: 0.296875, y: 0.234375 },
- { x: 0.328125, y: 0.234375 },
- { x: 0.328125, y: 0.234375 },
- { x: 0.359375, y: 0.234375 },
- { x: 0.359375, y: 0.234375 },
- { x: 0.390625, y: 0.234375 },
- { x: 0.390625, y: 0.234375 },
- { x: 0.421875, y: 0.234375 },
- { x: 0.421875, y: 0.234375 },
- { x: 0.453125, y: 0.234375 },
- { x: 0.453125, y: 0.234375 },
- { x: 0.484375, y: 0.234375 },
- { x: 0.484375, y: 0.234375 },
- { x: 0.515625, y: 0.234375 },
- { x: 0.515625, y: 0.234375 },
- { x: 0.546875, y: 0.234375 },
- { x: 0.546875, y: 0.234375 },
- { x: 0.578125, y: 0.234375 },
- { x: 0.578125, y: 0.234375 },
- { x: 0.609375, y: 0.234375 },
- { x: 0.609375, y: 0.234375 },
- { x: 0.640625, y: 0.234375 },
- { x: 0.640625, y: 0.234375 },
- { x: 0.671875, y: 0.234375 },
- { x: 0.671875, y: 0.234375 },
- { x: 0.703125, y: 0.234375 },
- { x: 0.703125, y: 0.234375 },
- { x: 0.734375, y: 0.234375 },
- { x: 0.734375, y: 0.234375 },
- { x: 0.765625, y: 0.234375 },
- { x: 0.765625, y: 0.234375 },
- { x: 0.796875, y: 0.234375 },
- { x: 0.796875, y: 0.234375 },
- { x: 0.828125, y: 0.234375 },
- { x: 0.828125, y: 0.234375 },
- { x: 0.859375, y: 0.234375 },
- { x: 0.859375, y: 0.234375 },
- { x: 0.890625, y: 0.234375 },
- { x: 0.890625, y: 0.234375 },
- { x: 0.921875, y: 0.234375 },
- { x: 0.921875, y: 0.234375 },
- { x: 0.953125, y: 0.234375 },
- { x: 0.953125, y: 0.234375 },
- { x: 0.984375, y: 0.234375 },
- { x: 0.984375, y: 0.234375 },
- { x: 0.015625, y: 0.265625 },
- { x: 0.015625, y: 0.265625 },
- { x: 0.046875, y: 0.265625 },
- { x: 0.046875, y: 0.265625 },
- { x: 0.078125, y: 0.265625 },
- { x: 0.078125, y: 0.265625 },
- { x: 0.109375, y: 0.265625 },
- { x: 0.109375, y: 0.265625 },
- { x: 0.140625, y: 0.265625 },
- { x: 0.140625, y: 0.265625 },
- { x: 0.171875, y: 0.265625 },
- { x: 0.171875, y: 0.265625 },
- { x: 0.203125, y: 0.265625 },
- { x: 0.203125, y: 0.265625 },
- { x: 0.234375, y: 0.265625 },
- { x: 0.234375, y: 0.265625 },
- { x: 0.265625, y: 0.265625 },
- { x: 0.265625, y: 0.265625 },
- { x: 0.296875, y: 0.265625 },
- { x: 0.296875, y: 0.265625 },
- { x: 0.328125, y: 0.265625 },
- { x: 0.328125, y: 0.265625 },
- { x: 0.359375, y: 0.265625 },
- { x: 0.359375, y: 0.265625 },
- { x: 0.390625, y: 0.265625 },
- { x: 0.390625, y: 0.265625 },
- { x: 0.421875, y: 0.265625 },
- { x: 0.421875, y: 0.265625 },
- { x: 0.453125, y: 0.265625 },
- { x: 0.453125, y: 0.265625 },
- { x: 0.484375, y: 0.265625 },
- { x: 0.484375, y: 0.265625 },
- { x: 0.515625, y: 0.265625 },
- { x: 0.515625, y: 0.265625 },
- { x: 0.546875, y: 0.265625 },
- { x: 0.546875, y: 0.265625 },
- { x: 0.578125, y: 0.265625 },
- { x: 0.578125, y: 0.265625 },
- { x: 0.609375, y: 0.265625 },
- { x: 0.609375, y: 0.265625 },
- { x: 0.640625, y: 0.265625 },
- { x: 0.640625, y: 0.265625 },
- { x: 0.671875, y: 0.265625 },
- { x: 0.671875, y: 0.265625 },
- { x: 0.703125, y: 0.265625 },
- { x: 0.703125, y: 0.265625 },
- { x: 0.734375, y: 0.265625 },
- { x: 0.734375, y: 0.265625 },
- { x: 0.765625, y: 0.265625 },
- { x: 0.765625, y: 0.265625 },
- { x: 0.796875, y: 0.265625 },
- { x: 0.796875, y: 0.265625 },
- { x: 0.828125, y: 0.265625 },
- { x: 0.828125, y: 0.265625 },
- { x: 0.859375, y: 0.265625 },
- { x: 0.859375, y: 0.265625 },
- { x: 0.890625, y: 0.265625 },
- { x: 0.890625, y: 0.265625 },
- { x: 0.921875, y: 0.265625 },
- { x: 0.921875, y: 0.265625 },
- { x: 0.953125, y: 0.265625 },
- { x: 0.953125, y: 0.265625 },
- { x: 0.984375, y: 0.265625 },
- { x: 0.984375, y: 0.265625 },
- { x: 0.015625, y: 0.296875 },
- { x: 0.015625, y: 0.296875 },
- { x: 0.046875, y: 0.296875 },
- { x: 0.046875, y: 0.296875 },
- { x: 0.078125, y: 0.296875 },
- { x: 0.078125, y: 0.296875 },
- { x: 0.109375, y: 0.296875 },
- { x: 0.109375, y: 0.296875 },
- { x: 0.140625, y: 0.296875 },
- { x: 0.140625, y: 0.296875 },
- { x: 0.171875, y: 0.296875 },
- { x: 0.171875, y: 0.296875 },
- { x: 0.203125, y: 0.296875 },
- { x: 0.203125, y: 0.296875 },
- { x: 0.234375, y: 0.296875 },
- { x: 0.234375, y: 0.296875 },
- { x: 0.265625, y: 0.296875 },
- { x: 0.265625, y: 0.296875 },
- { x: 0.296875, y: 0.296875 },
- { x: 0.296875, y: 0.296875 },
- { x: 0.328125, y: 0.296875 },
- { x: 0.328125, y: 0.296875 },
- { x: 0.359375, y: 0.296875 },
- { x: 0.359375, y: 0.296875 },
- { x: 0.390625, y: 0.296875 },
- { x: 0.390625, y: 0.296875 },
- { x: 0.421875, y: 0.296875 },
- { x: 0.421875, y: 0.296875 },
- { x: 0.453125, y: 0.296875 },
- { x: 0.453125, y: 0.296875 },
- { x: 0.484375, y: 0.296875 },
- { x: 0.484375, y: 0.296875 },
- { x: 0.515625, y: 0.296875 },
- { x: 0.515625, y: 0.296875 },
- { x: 0.546875, y: 0.296875 },
- { x: 0.546875, y: 0.296875 },
- { x: 0.578125, y: 0.296875 },
- { x: 0.578125, y: 0.296875 },
- { x: 0.609375, y: 0.296875 },
- { x: 0.609375, y: 0.296875 },
- { x: 0.640625, y: 0.296875 },
- { x: 0.640625, y: 0.296875 },
- { x: 0.671875, y: 0.296875 },
- { x: 0.671875, y: 0.296875 },
- { x: 0.703125, y: 0.296875 },
- { x: 0.703125, y: 0.296875 },
- { x: 0.734375, y: 0.296875 },
- { x: 0.734375, y: 0.296875 },
- { x: 0.765625, y: 0.296875 },
- { x: 0.765625, y: 0.296875 },
- { x: 0.796875, y: 0.296875 },
- { x: 0.796875, y: 0.296875 },
- { x: 0.828125, y: 0.296875 },
- { x: 0.828125, y: 0.296875 },
- { x: 0.859375, y: 0.296875 },
- { x: 0.859375, y: 0.296875 },
- { x: 0.890625, y: 0.296875 },
- { x: 0.890625, y: 0.296875 },
- { x: 0.921875, y: 0.296875 },
- { x: 0.921875, y: 0.296875 },
- { x: 0.953125, y: 0.296875 },
- { x: 0.953125, y: 0.296875 },
- { x: 0.984375, y: 0.296875 },
- { x: 0.984375, y: 0.296875 },
- { x: 0.015625, y: 0.328125 },
- { x: 0.015625, y: 0.328125 },
- { x: 0.046875, y: 0.328125 },
- { x: 0.046875, y: 0.328125 },
- { x: 0.078125, y: 0.328125 },
- { x: 0.078125, y: 0.328125 },
- { x: 0.109375, y: 0.328125 },
- { x: 0.109375, y: 0.328125 },
- { x: 0.140625, y: 0.328125 },
- { x: 0.140625, y: 0.328125 },
- { x: 0.171875, y: 0.328125 },
- { x: 0.171875, y: 0.328125 },
- { x: 0.203125, y: 0.328125 },
- { x: 0.203125, y: 0.328125 },
- { x: 0.234375, y: 0.328125 },
- { x: 0.234375, y: 0.328125 },
- { x: 0.265625, y: 0.328125 },
- { x: 0.265625, y: 0.328125 },
- { x: 0.296875, y: 0.328125 },
- { x: 0.296875, y: 0.328125 },
- { x: 0.328125, y: 0.328125 },
- { x: 0.328125, y: 0.328125 },
- { x: 0.359375, y: 0.328125 },
- { x: 0.359375, y: 0.328125 },
- { x: 0.390625, y: 0.328125 },
- { x: 0.390625, y: 0.328125 },
- { x: 0.421875, y: 0.328125 },
- { x: 0.421875, y: 0.328125 },
- { x: 0.453125, y: 0.328125 },
- { x: 0.453125, y: 0.328125 },
- { x: 0.484375, y: 0.328125 },
- { x: 0.484375, y: 0.328125 },
- { x: 0.515625, y: 0.328125 },
- { x: 0.515625, y: 0.328125 },
- { x: 0.546875, y: 0.328125 },
- { x: 0.546875, y: 0.328125 },
- { x: 0.578125, y: 0.328125 },
- { x: 0.578125, y: 0.328125 },
- { x: 0.609375, y: 0.328125 },
- { x: 0.609375, y: 0.328125 },
- { x: 0.640625, y: 0.328125 },
- { x: 0.640625, y: 0.328125 },
- { x: 0.671875, y: 0.328125 },
- { x: 0.671875, y: 0.328125 },
- { x: 0.703125, y: 0.328125 },
- { x: 0.703125, y: 0.328125 },
- { x: 0.734375, y: 0.328125 },
- { x: 0.734375, y: 0.328125 },
- { x: 0.765625, y: 0.328125 },
- { x: 0.765625, y: 0.328125 },
- { x: 0.796875, y: 0.328125 },
- { x: 0.796875, y: 0.328125 },
- { x: 0.828125, y: 0.328125 },
- { x: 0.828125, y: 0.328125 },
- { x: 0.859375, y: 0.328125 },
- { x: 0.859375, y: 0.328125 },
- { x: 0.890625, y: 0.328125 },
- { x: 0.890625, y: 0.328125 },
- { x: 0.921875, y: 0.328125 },
- { x: 0.921875, y: 0.328125 },
- { x: 0.953125, y: 0.328125 },
- { x: 0.953125, y: 0.328125 },
- { x: 0.984375, y: 0.328125 },
- { x: 0.984375, y: 0.328125 },
- { x: 0.015625, y: 0.359375 },
- { x: 0.015625, y: 0.359375 },
- { x: 0.046875, y: 0.359375 },
- { x: 0.046875, y: 0.359375 },
- { x: 0.078125, y: 0.359375 },
- { x: 0.078125, y: 0.359375 },
- { x: 0.109375, y: 0.359375 },
- { x: 0.109375, y: 0.359375 },
- { x: 0.140625, y: 0.359375 },
- { x: 0.140625, y: 0.359375 },
- { x: 0.171875, y: 0.359375 },
- { x: 0.171875, y: 0.359375 },
- { x: 0.203125, y: 0.359375 },
- { x: 0.203125, y: 0.359375 },
- { x: 0.234375, y: 0.359375 },
- { x: 0.234375, y: 0.359375 },
- { x: 0.265625, y: 0.359375 },
- { x: 0.265625, y: 0.359375 },
- { x: 0.296875, y: 0.359375 },
- { x: 0.296875, y: 0.359375 },
- { x: 0.328125, y: 0.359375 },
- { x: 0.328125, y: 0.359375 },
- { x: 0.359375, y: 0.359375 },
- { x: 0.359375, y: 0.359375 },
- { x: 0.390625, y: 0.359375 },
- { x: 0.390625, y: 0.359375 },
- { x: 0.421875, y: 0.359375 },
- { x: 0.421875, y: 0.359375 },
- { x: 0.453125, y: 0.359375 },
- { x: 0.453125, y: 0.359375 },
- { x: 0.484375, y: 0.359375 },
- { x: 0.484375, y: 0.359375 },
- { x: 0.515625, y: 0.359375 },
- { x: 0.515625, y: 0.359375 },
- { x: 0.546875, y: 0.359375 },
- { x: 0.546875, y: 0.359375 },
- { x: 0.578125, y: 0.359375 },
- { x: 0.578125, y: 0.359375 },
- { x: 0.609375, y: 0.359375 },
- { x: 0.609375, y: 0.359375 },
- { x: 0.640625, y: 0.359375 },
- { x: 0.640625, y: 0.359375 },
- { x: 0.671875, y: 0.359375 },
- { x: 0.671875, y: 0.359375 },
- { x: 0.703125, y: 0.359375 },
- { x: 0.703125, y: 0.359375 },
- { x: 0.734375, y: 0.359375 },
- { x: 0.734375, y: 0.359375 },
- { x: 0.765625, y: 0.359375 },
- { x: 0.765625, y: 0.359375 },
- { x: 0.796875, y: 0.359375 },
- { x: 0.796875, y: 0.359375 },
- { x: 0.828125, y: 0.359375 },
- { x: 0.828125, y: 0.359375 },
- { x: 0.859375, y: 0.359375 },
- { x: 0.859375, y: 0.359375 },
- { x: 0.890625, y: 0.359375 },
- { x: 0.890625, y: 0.359375 },
- { x: 0.921875, y: 0.359375 },
- { x: 0.921875, y: 0.359375 },
- { x: 0.953125, y: 0.359375 },
- { x: 0.953125, y: 0.359375 },
- { x: 0.984375, y: 0.359375 },
- { x: 0.984375, y: 0.359375 },
- { x: 0.015625, y: 0.390625 },
- { x: 0.015625, y: 0.390625 },
- { x: 0.046875, y: 0.390625 },
- { x: 0.046875, y: 0.390625 },
- { x: 0.078125, y: 0.390625 },
- { x: 0.078125, y: 0.390625 },
- { x: 0.109375, y: 0.390625 },
- { x: 0.109375, y: 0.390625 },
- { x: 0.140625, y: 0.390625 },
- { x: 0.140625, y: 0.390625 },
- { x: 0.171875, y: 0.390625 },
- { x: 0.171875, y: 0.390625 },
- { x: 0.203125, y: 0.390625 },
- { x: 0.203125, y: 0.390625 },
- { x: 0.234375, y: 0.390625 },
- { x: 0.234375, y: 0.390625 },
- { x: 0.265625, y: 0.390625 },
- { x: 0.265625, y: 0.390625 },
- { x: 0.296875, y: 0.390625 },
- { x: 0.296875, y: 0.390625 },
- { x: 0.328125, y: 0.390625 },
- { x: 0.328125, y: 0.390625 },
- { x: 0.359375, y: 0.390625 },
- { x: 0.359375, y: 0.390625 },
- { x: 0.390625, y: 0.390625 },
- { x: 0.390625, y: 0.390625 },
- { x: 0.421875, y: 0.390625 },
- { x: 0.421875, y: 0.390625 },
- { x: 0.453125, y: 0.390625 },
- { x: 0.453125, y: 0.390625 },
- { x: 0.484375, y: 0.390625 },
- { x: 0.484375, y: 0.390625 },
- { x: 0.515625, y: 0.390625 },
- { x: 0.515625, y: 0.390625 },
- { x: 0.546875, y: 0.390625 },
- { x: 0.546875, y: 0.390625 },
- { x: 0.578125, y: 0.390625 },
- { x: 0.578125, y: 0.390625 },
- { x: 0.609375, y: 0.390625 },
- { x: 0.609375, y: 0.390625 },
- { x: 0.640625, y: 0.390625 },
- { x: 0.640625, y: 0.390625 },
- { x: 0.671875, y: 0.390625 },
- { x: 0.671875, y: 0.390625 },
- { x: 0.703125, y: 0.390625 },
- { x: 0.703125, y: 0.390625 },
- { x: 0.734375, y: 0.390625 },
- { x: 0.734375, y: 0.390625 },
- { x: 0.765625, y: 0.390625 },
- { x: 0.765625, y: 0.390625 },
- { x: 0.796875, y: 0.390625 },
- { x: 0.796875, y: 0.390625 },
- { x: 0.828125, y: 0.390625 },
- { x: 0.828125, y: 0.390625 },
- { x: 0.859375, y: 0.390625 },
- { x: 0.859375, y: 0.390625 },
- { x: 0.890625, y: 0.390625 },
- { x: 0.890625, y: 0.390625 },
- { x: 0.921875, y: 0.390625 },
- { x: 0.921875, y: 0.390625 },
- { x: 0.953125, y: 0.390625 },
- { x: 0.953125, y: 0.390625 },
- { x: 0.984375, y: 0.390625 },
- { x: 0.984375, y: 0.390625 },
- { x: 0.015625, y: 0.421875 },
- { x: 0.015625, y: 0.421875 },
- { x: 0.046875, y: 0.421875 },
- { x: 0.046875, y: 0.421875 },
- { x: 0.078125, y: 0.421875 },
- { x: 0.078125, y: 0.421875 },
- { x: 0.109375, y: 0.421875 },
- { x: 0.109375, y: 0.421875 },
- { x: 0.140625, y: 0.421875 },
- { x: 0.140625, y: 0.421875 },
- { x: 0.171875, y: 0.421875 },
- { x: 0.171875, y: 0.421875 },
- { x: 0.203125, y: 0.421875 },
- { x: 0.203125, y: 0.421875 },
- { x: 0.234375, y: 0.421875 },
- { x: 0.234375, y: 0.421875 },
- { x: 0.265625, y: 0.421875 },
- { x: 0.265625, y: 0.421875 },
- { x: 0.296875, y: 0.421875 },
- { x: 0.296875, y: 0.421875 },
- { x: 0.328125, y: 0.421875 },
- { x: 0.328125, y: 0.421875 },
- { x: 0.359375, y: 0.421875 },
- { x: 0.359375, y: 0.421875 },
- { x: 0.390625, y: 0.421875 },
- { x: 0.390625, y: 0.421875 },
- { x: 0.421875, y: 0.421875 },
- { x: 0.421875, y: 0.421875 },
- { x: 0.453125, y: 0.421875 },
- { x: 0.453125, y: 0.421875 },
- { x: 0.484375, y: 0.421875 },
- { x: 0.484375, y: 0.421875 },
- { x: 0.515625, y: 0.421875 },
- { x: 0.515625, y: 0.421875 },
- { x: 0.546875, y: 0.421875 },
- { x: 0.546875, y: 0.421875 },
- { x: 0.578125, y: 0.421875 },
- { x: 0.578125, y: 0.421875 },
- { x: 0.609375, y: 0.421875 },
- { x: 0.609375, y: 0.421875 },
- { x: 0.640625, y: 0.421875 },
- { x: 0.640625, y: 0.421875 },
- { x: 0.671875, y: 0.421875 },
- { x: 0.671875, y: 0.421875 },
- { x: 0.703125, y: 0.421875 },
- { x: 0.703125, y: 0.421875 },
- { x: 0.734375, y: 0.421875 },
- { x: 0.734375, y: 0.421875 },
- { x: 0.765625, y: 0.421875 },
- { x: 0.765625, y: 0.421875 },
- { x: 0.796875, y: 0.421875 },
- { x: 0.796875, y: 0.421875 },
- { x: 0.828125, y: 0.421875 },
- { x: 0.828125, y: 0.421875 },
- { x: 0.859375, y: 0.421875 },
- { x: 0.859375, y: 0.421875 },
- { x: 0.890625, y: 0.421875 },
- { x: 0.890625, y: 0.421875 },
- { x: 0.921875, y: 0.421875 },
- { x: 0.921875, y: 0.421875 },
- { x: 0.953125, y: 0.421875 },
- { x: 0.953125, y: 0.421875 },
- { x: 0.984375, y: 0.421875 },
- { x: 0.984375, y: 0.421875 },
- { x: 0.015625, y: 0.453125 },
- { x: 0.015625, y: 0.453125 },
- { x: 0.046875, y: 0.453125 },
- { x: 0.046875, y: 0.453125 },
- { x: 0.078125, y: 0.453125 },
- { x: 0.078125, y: 0.453125 },
- { x: 0.109375, y: 0.453125 },
- { x: 0.109375, y: 0.453125 },
- { x: 0.140625, y: 0.453125 },
- { x: 0.140625, y: 0.453125 },
- { x: 0.171875, y: 0.453125 },
- { x: 0.171875, y: 0.453125 },
- { x: 0.203125, y: 0.453125 },
- { x: 0.203125, y: 0.453125 },
- { x: 0.234375, y: 0.453125 },
- { x: 0.234375, y: 0.453125 },
- { x: 0.265625, y: 0.453125 },
- { x: 0.265625, y: 0.453125 },
- { x: 0.296875, y: 0.453125 },
- { x: 0.296875, y: 0.453125 },
- { x: 0.328125, y: 0.453125 },
- { x: 0.328125, y: 0.453125 },
- { x: 0.359375, y: 0.453125 },
- { x: 0.359375, y: 0.453125 },
- { x: 0.390625, y: 0.453125 },
- { x: 0.390625, y: 0.453125 },
- { x: 0.421875, y: 0.453125 },
- { x: 0.421875, y: 0.453125 },
- { x: 0.453125, y: 0.453125 },
- { x: 0.453125, y: 0.453125 },
- { x: 0.484375, y: 0.453125 },
- { x: 0.484375, y: 0.453125 },
- { x: 0.515625, y: 0.453125 },
- { x: 0.515625, y: 0.453125 },
- { x: 0.546875, y: 0.453125 },
- { x: 0.546875, y: 0.453125 },
- { x: 0.578125, y: 0.453125 },
- { x: 0.578125, y: 0.453125 },
- { x: 0.609375, y: 0.453125 },
- { x: 0.609375, y: 0.453125 },
- { x: 0.640625, y: 0.453125 },
- { x: 0.640625, y: 0.453125 },
- { x: 0.671875, y: 0.453125 },
- { x: 0.671875, y: 0.453125 },
- { x: 0.703125, y: 0.453125 },
- { x: 0.703125, y: 0.453125 },
- { x: 0.734375, y: 0.453125 },
- { x: 0.734375, y: 0.453125 },
- { x: 0.765625, y: 0.453125 },
- { x: 0.765625, y: 0.453125 },
- { x: 0.796875, y: 0.453125 },
- { x: 0.796875, y: 0.453125 },
- { x: 0.828125, y: 0.453125 },
- { x: 0.828125, y: 0.453125 },
- { x: 0.859375, y: 0.453125 },
- { x: 0.859375, y: 0.453125 },
- { x: 0.890625, y: 0.453125 },
- { x: 0.890625, y: 0.453125 },
- { x: 0.921875, y: 0.453125 },
- { x: 0.921875, y: 0.453125 },
- { x: 0.953125, y: 0.453125 },
- { x: 0.953125, y: 0.453125 },
- { x: 0.984375, y: 0.453125 },
- { x: 0.984375, y: 0.453125 },
- { x: 0.015625, y: 0.484375 },
- { x: 0.015625, y: 0.484375 },
- { x: 0.046875, y: 0.484375 },
- { x: 0.046875, y: 0.484375 },
- { x: 0.078125, y: 0.484375 },
- { x: 0.078125, y: 0.484375 },
- { x: 0.109375, y: 0.484375 },
- { x: 0.109375, y: 0.484375 },
- { x: 0.140625, y: 0.484375 },
- { x: 0.140625, y: 0.484375 },
- { x: 0.171875, y: 0.484375 },
- { x: 0.171875, y: 0.484375 },
- { x: 0.203125, y: 0.484375 },
- { x: 0.203125, y: 0.484375 },
- { x: 0.234375, y: 0.484375 },
- { x: 0.234375, y: 0.484375 },
- { x: 0.265625, y: 0.484375 },
- { x: 0.265625, y: 0.484375 },
- { x: 0.296875, y: 0.484375 },
- { x: 0.296875, y: 0.484375 },
- { x: 0.328125, y: 0.484375 },
- { x: 0.328125, y: 0.484375 },
- { x: 0.359375, y: 0.484375 },
- { x: 0.359375, y: 0.484375 },
- { x: 0.390625, y: 0.484375 },
- { x: 0.390625, y: 0.484375 },
- { x: 0.421875, y: 0.484375 },
- { x: 0.421875, y: 0.484375 },
- { x: 0.453125, y: 0.484375 },
- { x: 0.453125, y: 0.484375 },
- { x: 0.484375, y: 0.484375 },
- { x: 0.484375, y: 0.484375 },
- { x: 0.515625, y: 0.484375 },
- { x: 0.515625, y: 0.484375 },
- { x: 0.546875, y: 0.484375 },
- { x: 0.546875, y: 0.484375 },
- { x: 0.578125, y: 0.484375 },
- { x: 0.578125, y: 0.484375 },
- { x: 0.609375, y: 0.484375 },
- { x: 0.609375, y: 0.484375 },
- { x: 0.640625, y: 0.484375 },
- { x: 0.640625, y: 0.484375 },
- { x: 0.671875, y: 0.484375 },
- { x: 0.671875, y: 0.484375 },
- { x: 0.703125, y: 0.484375 },
- { x: 0.703125, y: 0.484375 },
- { x: 0.734375, y: 0.484375 },
- { x: 0.734375, y: 0.484375 },
- { x: 0.765625, y: 0.484375 },
- { x: 0.765625, y: 0.484375 },
- { x: 0.796875, y: 0.484375 },
- { x: 0.796875, y: 0.484375 },
- { x: 0.828125, y: 0.484375 },
- { x: 0.828125, y: 0.484375 },
- { x: 0.859375, y: 0.484375 },
- { x: 0.859375, y: 0.484375 },
- { x: 0.890625, y: 0.484375 },
- { x: 0.890625, y: 0.484375 },
- { x: 0.921875, y: 0.484375 },
- { x: 0.921875, y: 0.484375 },
- { x: 0.953125, y: 0.484375 },
- { x: 0.953125, y: 0.484375 },
- { x: 0.984375, y: 0.484375 },
- { x: 0.984375, y: 0.484375 },
- { x: 0.015625, y: 0.515625 },
- { x: 0.015625, y: 0.515625 },
- { x: 0.046875, y: 0.515625 },
- { x: 0.046875, y: 0.515625 },
- { x: 0.078125, y: 0.515625 },
- { x: 0.078125, y: 0.515625 },
- { x: 0.109375, y: 0.515625 },
- { x: 0.109375, y: 0.515625 },
- { x: 0.140625, y: 0.515625 },
- { x: 0.140625, y: 0.515625 },
- { x: 0.171875, y: 0.515625 },
- { x: 0.171875, y: 0.515625 },
- { x: 0.203125, y: 0.515625 },
- { x: 0.203125, y: 0.515625 },
- { x: 0.234375, y: 0.515625 },
- { x: 0.234375, y: 0.515625 },
- { x: 0.265625, y: 0.515625 },
- { x: 0.265625, y: 0.515625 },
- { x: 0.296875, y: 0.515625 },
- { x: 0.296875, y: 0.515625 },
- { x: 0.328125, y: 0.515625 },
- { x: 0.328125, y: 0.515625 },
- { x: 0.359375, y: 0.515625 },
- { x: 0.359375, y: 0.515625 },
- { x: 0.390625, y: 0.515625 },
- { x: 0.390625, y: 0.515625 },
- { x: 0.421875, y: 0.515625 },
- { x: 0.421875, y: 0.515625 },
- { x: 0.453125, y: 0.515625 },
- { x: 0.453125, y: 0.515625 },
- { x: 0.484375, y: 0.515625 },
- { x: 0.484375, y: 0.515625 },
- { x: 0.515625, y: 0.515625 },
- { x: 0.515625, y: 0.515625 },
- { x: 0.546875, y: 0.515625 },
- { x: 0.546875, y: 0.515625 },
- { x: 0.578125, y: 0.515625 },
- { x: 0.578125, y: 0.515625 },
- { x: 0.609375, y: 0.515625 },
- { x: 0.609375, y: 0.515625 },
- { x: 0.640625, y: 0.515625 },
- { x: 0.640625, y: 0.515625 },
- { x: 0.671875, y: 0.515625 },
- { x: 0.671875, y: 0.515625 },
- { x: 0.703125, y: 0.515625 },
- { x: 0.703125, y: 0.515625 },
- { x: 0.734375, y: 0.515625 },
- { x: 0.734375, y: 0.515625 },
- { x: 0.765625, y: 0.515625 },
- { x: 0.765625, y: 0.515625 },
- { x: 0.796875, y: 0.515625 },
- { x: 0.796875, y: 0.515625 },
- { x: 0.828125, y: 0.515625 },
- { x: 0.828125, y: 0.515625 },
- { x: 0.859375, y: 0.515625 },
- { x: 0.859375, y: 0.515625 },
- { x: 0.890625, y: 0.515625 },
- { x: 0.890625, y: 0.515625 },
- { x: 0.921875, y: 0.515625 },
- { x: 0.921875, y: 0.515625 },
- { x: 0.953125, y: 0.515625 },
- { x: 0.953125, y: 0.515625 },
- { x: 0.984375, y: 0.515625 },
- { x: 0.984375, y: 0.515625 },
- { x: 0.015625, y: 0.546875 },
- { x: 0.015625, y: 0.546875 },
- { x: 0.046875, y: 0.546875 },
- { x: 0.046875, y: 0.546875 },
- { x: 0.078125, y: 0.546875 },
- { x: 0.078125, y: 0.546875 },
- { x: 0.109375, y: 0.546875 },
- { x: 0.109375, y: 0.546875 },
- { x: 0.140625, y: 0.546875 },
- { x: 0.140625, y: 0.546875 },
- { x: 0.171875, y: 0.546875 },
- { x: 0.171875, y: 0.546875 },
- { x: 0.203125, y: 0.546875 },
- { x: 0.203125, y: 0.546875 },
- { x: 0.234375, y: 0.546875 },
- { x: 0.234375, y: 0.546875 },
- { x: 0.265625, y: 0.546875 },
- { x: 0.265625, y: 0.546875 },
- { x: 0.296875, y: 0.546875 },
- { x: 0.296875, y: 0.546875 },
- { x: 0.328125, y: 0.546875 },
- { x: 0.328125, y: 0.546875 },
- { x: 0.359375, y: 0.546875 },
- { x: 0.359375, y: 0.546875 },
- { x: 0.390625, y: 0.546875 },
- { x: 0.390625, y: 0.546875 },
- { x: 0.421875, y: 0.546875 },
- { x: 0.421875, y: 0.546875 },
- { x: 0.453125, y: 0.546875 },
- { x: 0.453125, y: 0.546875 },
- { x: 0.484375, y: 0.546875 },
- { x: 0.484375, y: 0.546875 },
- { x: 0.515625, y: 0.546875 },
- { x: 0.515625, y: 0.546875 },
- { x: 0.546875, y: 0.546875 },
- { x: 0.546875, y: 0.546875 },
- { x: 0.578125, y: 0.546875 },
- { x: 0.578125, y: 0.546875 },
- { x: 0.609375, y: 0.546875 },
- { x: 0.609375, y: 0.546875 },
- { x: 0.640625, y: 0.546875 },
- { x: 0.640625, y: 0.546875 },
- { x: 0.671875, y: 0.546875 },
- { x: 0.671875, y: 0.546875 },
- { x: 0.703125, y: 0.546875 },
- { x: 0.703125, y: 0.546875 },
- { x: 0.734375, y: 0.546875 },
- { x: 0.734375, y: 0.546875 },
- { x: 0.765625, y: 0.546875 },
- { x: 0.765625, y: 0.546875 },
- { x: 0.796875, y: 0.546875 },
- { x: 0.796875, y: 0.546875 },
- { x: 0.828125, y: 0.546875 },
- { x: 0.828125, y: 0.546875 },
- { x: 0.859375, y: 0.546875 },
- { x: 0.859375, y: 0.546875 },
- { x: 0.890625, y: 0.546875 },
- { x: 0.890625, y: 0.546875 },
- { x: 0.921875, y: 0.546875 },
- { x: 0.921875, y: 0.546875 },
- { x: 0.953125, y: 0.546875 },
- { x: 0.953125, y: 0.546875 },
- { x: 0.984375, y: 0.546875 },
- { x: 0.984375, y: 0.546875 },
- { x: 0.015625, y: 0.578125 },
- { x: 0.015625, y: 0.578125 },
- { x: 0.046875, y: 0.578125 },
- { x: 0.046875, y: 0.578125 },
- { x: 0.078125, y: 0.578125 },
- { x: 0.078125, y: 0.578125 },
- { x: 0.109375, y: 0.578125 },
- { x: 0.109375, y: 0.578125 },
- { x: 0.140625, y: 0.578125 },
- { x: 0.140625, y: 0.578125 },
- { x: 0.171875, y: 0.578125 },
- { x: 0.171875, y: 0.578125 },
- { x: 0.203125, y: 0.578125 },
- { x: 0.203125, y: 0.578125 },
- { x: 0.234375, y: 0.578125 },
- { x: 0.234375, y: 0.578125 },
- { x: 0.265625, y: 0.578125 },
- { x: 0.265625, y: 0.578125 },
- { x: 0.296875, y: 0.578125 },
- { x: 0.296875, y: 0.578125 },
- { x: 0.328125, y: 0.578125 },
- { x: 0.328125, y: 0.578125 },
- { x: 0.359375, y: 0.578125 },
- { x: 0.359375, y: 0.578125 },
- { x: 0.390625, y: 0.578125 },
- { x: 0.390625, y: 0.578125 },
- { x: 0.421875, y: 0.578125 },
- { x: 0.421875, y: 0.578125 },
- { x: 0.453125, y: 0.578125 },
- { x: 0.453125, y: 0.578125 },
- { x: 0.484375, y: 0.578125 },
- { x: 0.484375, y: 0.578125 },
- { x: 0.515625, y: 0.578125 },
- { x: 0.515625, y: 0.578125 },
- { x: 0.546875, y: 0.578125 },
- { x: 0.546875, y: 0.578125 },
- { x: 0.578125, y: 0.578125 },
- { x: 0.578125, y: 0.578125 },
- { x: 0.609375, y: 0.578125 },
- { x: 0.609375, y: 0.578125 },
- { x: 0.640625, y: 0.578125 },
- { x: 0.640625, y: 0.578125 },
- { x: 0.671875, y: 0.578125 },
- { x: 0.671875, y: 0.578125 },
- { x: 0.703125, y: 0.578125 },
- { x: 0.703125, y: 0.578125 },
- { x: 0.734375, y: 0.578125 },
- { x: 0.734375, y: 0.578125 },
- { x: 0.765625, y: 0.578125 },
- { x: 0.765625, y: 0.578125 },
- { x: 0.796875, y: 0.578125 },
- { x: 0.796875, y: 0.578125 },
- { x: 0.828125, y: 0.578125 },
- { x: 0.828125, y: 0.578125 },
- { x: 0.859375, y: 0.578125 },
- { x: 0.859375, y: 0.578125 },
- { x: 0.890625, y: 0.578125 },
- { x: 0.890625, y: 0.578125 },
- { x: 0.921875, y: 0.578125 },
- { x: 0.921875, y: 0.578125 },
- { x: 0.953125, y: 0.578125 },
- { x: 0.953125, y: 0.578125 },
- { x: 0.984375, y: 0.578125 },
- { x: 0.984375, y: 0.578125 },
- { x: 0.015625, y: 0.609375 },
- { x: 0.015625, y: 0.609375 },
- { x: 0.046875, y: 0.609375 },
- { x: 0.046875, y: 0.609375 },
- { x: 0.078125, y: 0.609375 },
- { x: 0.078125, y: 0.609375 },
- { x: 0.109375, y: 0.609375 },
- { x: 0.109375, y: 0.609375 },
- { x: 0.140625, y: 0.609375 },
- { x: 0.140625, y: 0.609375 },
- { x: 0.171875, y: 0.609375 },
- { x: 0.171875, y: 0.609375 },
- { x: 0.203125, y: 0.609375 },
- { x: 0.203125, y: 0.609375 },
- { x: 0.234375, y: 0.609375 },
- { x: 0.234375, y: 0.609375 },
- { x: 0.265625, y: 0.609375 },
- { x: 0.265625, y: 0.609375 },
- { x: 0.296875, y: 0.609375 },
- { x: 0.296875, y: 0.609375 },
- { x: 0.328125, y: 0.609375 },
- { x: 0.328125, y: 0.609375 },
- { x: 0.359375, y: 0.609375 },
- { x: 0.359375, y: 0.609375 },
- { x: 0.390625, y: 0.609375 },
- { x: 0.390625, y: 0.609375 },
- { x: 0.421875, y: 0.609375 },
- { x: 0.421875, y: 0.609375 },
- { x: 0.453125, y: 0.609375 },
- { x: 0.453125, y: 0.609375 },
- { x: 0.484375, y: 0.609375 },
- { x: 0.484375, y: 0.609375 },
- { x: 0.515625, y: 0.609375 },
- { x: 0.515625, y: 0.609375 },
- { x: 0.546875, y: 0.609375 },
- { x: 0.546875, y: 0.609375 },
- { x: 0.578125, y: 0.609375 },
- { x: 0.578125, y: 0.609375 },
- { x: 0.609375, y: 0.609375 },
- { x: 0.609375, y: 0.609375 },
- { x: 0.640625, y: 0.609375 },
- { x: 0.640625, y: 0.609375 },
- { x: 0.671875, y: 0.609375 },
- { x: 0.671875, y: 0.609375 },
- { x: 0.703125, y: 0.609375 },
- { x: 0.703125, y: 0.609375 },
- { x: 0.734375, y: 0.609375 },
- { x: 0.734375, y: 0.609375 },
- { x: 0.765625, y: 0.609375 },
- { x: 0.765625, y: 0.609375 },
- { x: 0.796875, y: 0.609375 },
- { x: 0.796875, y: 0.609375 },
- { x: 0.828125, y: 0.609375 },
- { x: 0.828125, y: 0.609375 },
- { x: 0.859375, y: 0.609375 },
- { x: 0.859375, y: 0.609375 },
- { x: 0.890625, y: 0.609375 },
- { x: 0.890625, y: 0.609375 },
- { x: 0.921875, y: 0.609375 },
- { x: 0.921875, y: 0.609375 },
- { x: 0.953125, y: 0.609375 },
- { x: 0.953125, y: 0.609375 },
- { x: 0.984375, y: 0.609375 },
- { x: 0.984375, y: 0.609375 },
- { x: 0.015625, y: 0.640625 },
- { x: 0.015625, y: 0.640625 },
- { x: 0.046875, y: 0.640625 },
- { x: 0.046875, y: 0.640625 },
- { x: 0.078125, y: 0.640625 },
- { x: 0.078125, y: 0.640625 },
- { x: 0.109375, y: 0.640625 },
- { x: 0.109375, y: 0.640625 },
- { x: 0.140625, y: 0.640625 },
- { x: 0.140625, y: 0.640625 },
- { x: 0.171875, y: 0.640625 },
- { x: 0.171875, y: 0.640625 },
- { x: 0.203125, y: 0.640625 },
- { x: 0.203125, y: 0.640625 },
- { x: 0.234375, y: 0.640625 },
- { x: 0.234375, y: 0.640625 },
- { x: 0.265625, y: 0.640625 },
- { x: 0.265625, y: 0.640625 },
- { x: 0.296875, y: 0.640625 },
- { x: 0.296875, y: 0.640625 },
- { x: 0.328125, y: 0.640625 },
- { x: 0.328125, y: 0.640625 },
- { x: 0.359375, y: 0.640625 },
- { x: 0.359375, y: 0.640625 },
- { x: 0.390625, y: 0.640625 },
- { x: 0.390625, y: 0.640625 },
- { x: 0.421875, y: 0.640625 },
- { x: 0.421875, y: 0.640625 },
- { x: 0.453125, y: 0.640625 },
- { x: 0.453125, y: 0.640625 },
- { x: 0.484375, y: 0.640625 },
- { x: 0.484375, y: 0.640625 },
- { x: 0.515625, y: 0.640625 },
- { x: 0.515625, y: 0.640625 },
- { x: 0.546875, y: 0.640625 },
- { x: 0.546875, y: 0.640625 },
- { x: 0.578125, y: 0.640625 },
- { x: 0.578125, y: 0.640625 },
- { x: 0.609375, y: 0.640625 },
- { x: 0.609375, y: 0.640625 },
- { x: 0.640625, y: 0.640625 },
- { x: 0.640625, y: 0.640625 },
- { x: 0.671875, y: 0.640625 },
- { x: 0.671875, y: 0.640625 },
- { x: 0.703125, y: 0.640625 },
- { x: 0.703125, y: 0.640625 },
- { x: 0.734375, y: 0.640625 },
- { x: 0.734375, y: 0.640625 },
- { x: 0.765625, y: 0.640625 },
- { x: 0.765625, y: 0.640625 },
- { x: 0.796875, y: 0.640625 },
- { x: 0.796875, y: 0.640625 },
- { x: 0.828125, y: 0.640625 },
- { x: 0.828125, y: 0.640625 },
- { x: 0.859375, y: 0.640625 },
- { x: 0.859375, y: 0.640625 },
- { x: 0.890625, y: 0.640625 },
- { x: 0.890625, y: 0.640625 },
- { x: 0.921875, y: 0.640625 },
- { x: 0.921875, y: 0.640625 },
- { x: 0.953125, y: 0.640625 },
- { x: 0.953125, y: 0.640625 },
- { x: 0.984375, y: 0.640625 },
- { x: 0.984375, y: 0.640625 },
- { x: 0.015625, y: 0.671875 },
- { x: 0.015625, y: 0.671875 },
- { x: 0.046875, y: 0.671875 },
- { x: 0.046875, y: 0.671875 },
- { x: 0.078125, y: 0.671875 },
- { x: 0.078125, y: 0.671875 },
- { x: 0.109375, y: 0.671875 },
- { x: 0.109375, y: 0.671875 },
- { x: 0.140625, y: 0.671875 },
- { x: 0.140625, y: 0.671875 },
- { x: 0.171875, y: 0.671875 },
- { x: 0.171875, y: 0.671875 },
- { x: 0.203125, y: 0.671875 },
- { x: 0.203125, y: 0.671875 },
- { x: 0.234375, y: 0.671875 },
- { x: 0.234375, y: 0.671875 },
- { x: 0.265625, y: 0.671875 },
- { x: 0.265625, y: 0.671875 },
- { x: 0.296875, y: 0.671875 },
- { x: 0.296875, y: 0.671875 },
- { x: 0.328125, y: 0.671875 },
- { x: 0.328125, y: 0.671875 },
- { x: 0.359375, y: 0.671875 },
- { x: 0.359375, y: 0.671875 },
- { x: 0.390625, y: 0.671875 },
- { x: 0.390625, y: 0.671875 },
- { x: 0.421875, y: 0.671875 },
- { x: 0.421875, y: 0.671875 },
- { x: 0.453125, y: 0.671875 },
- { x: 0.453125, y: 0.671875 },
- { x: 0.484375, y: 0.671875 },
- { x: 0.484375, y: 0.671875 },
- { x: 0.515625, y: 0.671875 },
- { x: 0.515625, y: 0.671875 },
- { x: 0.546875, y: 0.671875 },
- { x: 0.546875, y: 0.671875 },
- { x: 0.578125, y: 0.671875 },
- { x: 0.578125, y: 0.671875 },
- { x: 0.609375, y: 0.671875 },
- { x: 0.609375, y: 0.671875 },
- { x: 0.640625, y: 0.671875 },
- { x: 0.640625, y: 0.671875 },
- { x: 0.671875, y: 0.671875 },
- { x: 0.671875, y: 0.671875 },
- { x: 0.703125, y: 0.671875 },
- { x: 0.703125, y: 0.671875 },
- { x: 0.734375, y: 0.671875 },
- { x: 0.734375, y: 0.671875 },
- { x: 0.765625, y: 0.671875 },
- { x: 0.765625, y: 0.671875 },
- { x: 0.796875, y: 0.671875 },
- { x: 0.796875, y: 0.671875 },
- { x: 0.828125, y: 0.671875 },
- { x: 0.828125, y: 0.671875 },
- { x: 0.859375, y: 0.671875 },
- { x: 0.859375, y: 0.671875 },
- { x: 0.890625, y: 0.671875 },
- { x: 0.890625, y: 0.671875 },
- { x: 0.921875, y: 0.671875 },
- { x: 0.921875, y: 0.671875 },
- { x: 0.953125, y: 0.671875 },
- { x: 0.953125, y: 0.671875 },
- { x: 0.984375, y: 0.671875 },
- { x: 0.984375, y: 0.671875 },
- { x: 0.015625, y: 0.703125 },
- { x: 0.015625, y: 0.703125 },
- { x: 0.046875, y: 0.703125 },
- { x: 0.046875, y: 0.703125 },
- { x: 0.078125, y: 0.703125 },
- { x: 0.078125, y: 0.703125 },
- { x: 0.109375, y: 0.703125 },
- { x: 0.109375, y: 0.703125 },
- { x: 0.140625, y: 0.703125 },
- { x: 0.140625, y: 0.703125 },
- { x: 0.171875, y: 0.703125 },
- { x: 0.171875, y: 0.703125 },
- { x: 0.203125, y: 0.703125 },
- { x: 0.203125, y: 0.703125 },
- { x: 0.234375, y: 0.703125 },
- { x: 0.234375, y: 0.703125 },
- { x: 0.265625, y: 0.703125 },
- { x: 0.265625, y: 0.703125 },
- { x: 0.296875, y: 0.703125 },
- { x: 0.296875, y: 0.703125 },
- { x: 0.328125, y: 0.703125 },
- { x: 0.328125, y: 0.703125 },
- { x: 0.359375, y: 0.703125 },
- { x: 0.359375, y: 0.703125 },
- { x: 0.390625, y: 0.703125 },
- { x: 0.390625, y: 0.703125 },
- { x: 0.421875, y: 0.703125 },
- { x: 0.421875, y: 0.703125 },
- { x: 0.453125, y: 0.703125 },
- { x: 0.453125, y: 0.703125 },
- { x: 0.484375, y: 0.703125 },
- { x: 0.484375, y: 0.703125 },
- { x: 0.515625, y: 0.703125 },
- { x: 0.515625, y: 0.703125 },
- { x: 0.546875, y: 0.703125 },
- { x: 0.546875, y: 0.703125 },
- { x: 0.578125, y: 0.703125 },
- { x: 0.578125, y: 0.703125 },
- { x: 0.609375, y: 0.703125 },
- { x: 0.609375, y: 0.703125 },
- { x: 0.640625, y: 0.703125 },
- { x: 0.640625, y: 0.703125 },
- { x: 0.671875, y: 0.703125 },
- { x: 0.671875, y: 0.703125 },
- { x: 0.703125, y: 0.703125 },
- { x: 0.703125, y: 0.703125 },
- { x: 0.734375, y: 0.703125 },
- { x: 0.734375, y: 0.703125 },
- { x: 0.765625, y: 0.703125 },
- { x: 0.765625, y: 0.703125 },
- { x: 0.796875, y: 0.703125 },
- { x: 0.796875, y: 0.703125 },
- { x: 0.828125, y: 0.703125 },
- { x: 0.828125, y: 0.703125 },
- { x: 0.859375, y: 0.703125 },
- { x: 0.859375, y: 0.703125 },
- { x: 0.890625, y: 0.703125 },
- { x: 0.890625, y: 0.703125 },
- { x: 0.921875, y: 0.703125 },
- { x: 0.921875, y: 0.703125 },
- { x: 0.953125, y: 0.703125 },
- { x: 0.953125, y: 0.703125 },
- { x: 0.984375, y: 0.703125 },
- { x: 0.984375, y: 0.703125 },
- { x: 0.015625, y: 0.734375 },
- { x: 0.015625, y: 0.734375 },
- { x: 0.046875, y: 0.734375 },
- { x: 0.046875, y: 0.734375 },
- { x: 0.078125, y: 0.734375 },
- { x: 0.078125, y: 0.734375 },
- { x: 0.109375, y: 0.734375 },
- { x: 0.109375, y: 0.734375 },
- { x: 0.140625, y: 0.734375 },
- { x: 0.140625, y: 0.734375 },
- { x: 0.171875, y: 0.734375 },
- { x: 0.171875, y: 0.734375 },
- { x: 0.203125, y: 0.734375 },
- { x: 0.203125, y: 0.734375 },
- { x: 0.234375, y: 0.734375 },
- { x: 0.234375, y: 0.734375 },
- { x: 0.265625, y: 0.734375 },
- { x: 0.265625, y: 0.734375 },
- { x: 0.296875, y: 0.734375 },
- { x: 0.296875, y: 0.734375 },
- { x: 0.328125, y: 0.734375 },
- { x: 0.328125, y: 0.734375 },
- { x: 0.359375, y: 0.734375 },
- { x: 0.359375, y: 0.734375 },
- { x: 0.390625, y: 0.734375 },
- { x: 0.390625, y: 0.734375 },
- { x: 0.421875, y: 0.734375 },
- { x: 0.421875, y: 0.734375 },
- { x: 0.453125, y: 0.734375 },
- { x: 0.453125, y: 0.734375 },
- { x: 0.484375, y: 0.734375 },
- { x: 0.484375, y: 0.734375 },
- { x: 0.515625, y: 0.734375 },
- { x: 0.515625, y: 0.734375 },
- { x: 0.546875, y: 0.734375 },
- { x: 0.546875, y: 0.734375 },
- { x: 0.578125, y: 0.734375 },
- { x: 0.578125, y: 0.734375 },
- { x: 0.609375, y: 0.734375 },
- { x: 0.609375, y: 0.734375 },
- { x: 0.640625, y: 0.734375 },
- { x: 0.640625, y: 0.734375 },
- { x: 0.671875, y: 0.734375 },
- { x: 0.671875, y: 0.734375 },
- { x: 0.703125, y: 0.734375 },
- { x: 0.703125, y: 0.734375 },
- { x: 0.734375, y: 0.734375 },
- { x: 0.734375, y: 0.734375 },
- { x: 0.765625, y: 0.734375 },
- { x: 0.765625, y: 0.734375 },
- { x: 0.796875, y: 0.734375 },
- { x: 0.796875, y: 0.734375 },
- { x: 0.828125, y: 0.734375 },
- { x: 0.828125, y: 0.734375 },
- { x: 0.859375, y: 0.734375 },
- { x: 0.859375, y: 0.734375 },
- { x: 0.890625, y: 0.734375 },
- { x: 0.890625, y: 0.734375 },
- { x: 0.921875, y: 0.734375 },
- { x: 0.921875, y: 0.734375 },
- { x: 0.953125, y: 0.734375 },
- { x: 0.953125, y: 0.734375 },
- { x: 0.984375, y: 0.734375 },
- { x: 0.984375, y: 0.734375 },
- { x: 0.015625, y: 0.765625 },
- { x: 0.015625, y: 0.765625 },
- { x: 0.046875, y: 0.765625 },
- { x: 0.046875, y: 0.765625 },
- { x: 0.078125, y: 0.765625 },
- { x: 0.078125, y: 0.765625 },
- { x: 0.109375, y: 0.765625 },
- { x: 0.109375, y: 0.765625 },
- { x: 0.140625, y: 0.765625 },
- { x: 0.140625, y: 0.765625 },
- { x: 0.171875, y: 0.765625 },
- { x: 0.171875, y: 0.765625 },
- { x: 0.203125, y: 0.765625 },
- { x: 0.203125, y: 0.765625 },
- { x: 0.234375, y: 0.765625 },
- { x: 0.234375, y: 0.765625 },
- { x: 0.265625, y: 0.765625 },
- { x: 0.265625, y: 0.765625 },
- { x: 0.296875, y: 0.765625 },
- { x: 0.296875, y: 0.765625 },
- { x: 0.328125, y: 0.765625 },
- { x: 0.328125, y: 0.765625 },
- { x: 0.359375, y: 0.765625 },
- { x: 0.359375, y: 0.765625 },
- { x: 0.390625, y: 0.765625 },
- { x: 0.390625, y: 0.765625 },
- { x: 0.421875, y: 0.765625 },
- { x: 0.421875, y: 0.765625 },
- { x: 0.453125, y: 0.765625 },
- { x: 0.453125, y: 0.765625 },
- { x: 0.484375, y: 0.765625 },
- { x: 0.484375, y: 0.765625 },
- { x: 0.515625, y: 0.765625 },
- { x: 0.515625, y: 0.765625 },
- { x: 0.546875, y: 0.765625 },
- { x: 0.546875, y: 0.765625 },
- { x: 0.578125, y: 0.765625 },
- { x: 0.578125, y: 0.765625 },
- { x: 0.609375, y: 0.765625 },
- { x: 0.609375, y: 0.765625 },
- { x: 0.640625, y: 0.765625 },
- { x: 0.640625, y: 0.765625 },
- { x: 0.671875, y: 0.765625 },
- { x: 0.671875, y: 0.765625 },
- { x: 0.703125, y: 0.765625 },
- { x: 0.703125, y: 0.765625 },
- { x: 0.734375, y: 0.765625 },
- { x: 0.734375, y: 0.765625 },
- { x: 0.765625, y: 0.765625 },
- { x: 0.765625, y: 0.765625 },
- { x: 0.796875, y: 0.765625 },
- { x: 0.796875, y: 0.765625 },
- { x: 0.828125, y: 0.765625 },
- { x: 0.828125, y: 0.765625 },
- { x: 0.859375, y: 0.765625 },
- { x: 0.859375, y: 0.765625 },
- { x: 0.890625, y: 0.765625 },
- { x: 0.890625, y: 0.765625 },
- { x: 0.921875, y: 0.765625 },
- { x: 0.921875, y: 0.765625 },
- { x: 0.953125, y: 0.765625 },
- { x: 0.953125, y: 0.765625 },
- { x: 0.984375, y: 0.765625 },
- { x: 0.984375, y: 0.765625 },
- { x: 0.015625, y: 0.796875 },
- { x: 0.015625, y: 0.796875 },
- { x: 0.046875, y: 0.796875 },
- { x: 0.046875, y: 0.796875 },
- { x: 0.078125, y: 0.796875 },
- { x: 0.078125, y: 0.796875 },
- { x: 0.109375, y: 0.796875 },
- { x: 0.109375, y: 0.796875 },
- { x: 0.140625, y: 0.796875 },
- { x: 0.140625, y: 0.796875 },
- { x: 0.171875, y: 0.796875 },
- { x: 0.171875, y: 0.796875 },
- { x: 0.203125, y: 0.796875 },
- { x: 0.203125, y: 0.796875 },
- { x: 0.234375, y: 0.796875 },
- { x: 0.234375, y: 0.796875 },
- { x: 0.265625, y: 0.796875 },
- { x: 0.265625, y: 0.796875 },
- { x: 0.296875, y: 0.796875 },
- { x: 0.296875, y: 0.796875 },
- { x: 0.328125, y: 0.796875 },
- { x: 0.328125, y: 0.796875 },
- { x: 0.359375, y: 0.796875 },
- { x: 0.359375, y: 0.796875 },
- { x: 0.390625, y: 0.796875 },
- { x: 0.390625, y: 0.796875 },
- { x: 0.421875, y: 0.796875 },
- { x: 0.421875, y: 0.796875 },
- { x: 0.453125, y: 0.796875 },
- { x: 0.453125, y: 0.796875 },
- { x: 0.484375, y: 0.796875 },
- { x: 0.484375, y: 0.796875 },
- { x: 0.515625, y: 0.796875 },
- { x: 0.515625, y: 0.796875 },
- { x: 0.546875, y: 0.796875 },
- { x: 0.546875, y: 0.796875 },
- { x: 0.578125, y: 0.796875 },
- { x: 0.578125, y: 0.796875 },
- { x: 0.609375, y: 0.796875 },
- { x: 0.609375, y: 0.796875 },
- { x: 0.640625, y: 0.796875 },
- { x: 0.640625, y: 0.796875 },
- { x: 0.671875, y: 0.796875 },
- { x: 0.671875, y: 0.796875 },
- { x: 0.703125, y: 0.796875 },
- { x: 0.703125, y: 0.796875 },
- { x: 0.734375, y: 0.796875 },
- { x: 0.734375, y: 0.796875 },
- { x: 0.765625, y: 0.796875 },
- { x: 0.765625, y: 0.796875 },
- { x: 0.796875, y: 0.796875 },
- { x: 0.796875, y: 0.796875 },
- { x: 0.828125, y: 0.796875 },
- { x: 0.828125, y: 0.796875 },
- { x: 0.859375, y: 0.796875 },
- { x: 0.859375, y: 0.796875 },
- { x: 0.890625, y: 0.796875 },
- { x: 0.890625, y: 0.796875 },
- { x: 0.921875, y: 0.796875 },
- { x: 0.921875, y: 0.796875 },
- { x: 0.953125, y: 0.796875 },
- { x: 0.953125, y: 0.796875 },
- { x: 0.984375, y: 0.796875 },
- { x: 0.984375, y: 0.796875 },
- { x: 0.015625, y: 0.828125 },
- { x: 0.015625, y: 0.828125 },
- { x: 0.046875, y: 0.828125 },
- { x: 0.046875, y: 0.828125 },
- { x: 0.078125, y: 0.828125 },
- { x: 0.078125, y: 0.828125 },
- { x: 0.109375, y: 0.828125 },
- { x: 0.109375, y: 0.828125 },
- { x: 0.140625, y: 0.828125 },
- { x: 0.140625, y: 0.828125 },
- { x: 0.171875, y: 0.828125 },
- { x: 0.171875, y: 0.828125 },
- { x: 0.203125, y: 0.828125 },
- { x: 0.203125, y: 0.828125 },
- { x: 0.234375, y: 0.828125 },
- { x: 0.234375, y: 0.828125 },
- { x: 0.265625, y: 0.828125 },
- { x: 0.265625, y: 0.828125 },
- { x: 0.296875, y: 0.828125 },
- { x: 0.296875, y: 0.828125 },
- { x: 0.328125, y: 0.828125 },
- { x: 0.328125, y: 0.828125 },
- { x: 0.359375, y: 0.828125 },
- { x: 0.359375, y: 0.828125 },
- { x: 0.390625, y: 0.828125 },
- { x: 0.390625, y: 0.828125 },
- { x: 0.421875, y: 0.828125 },
- { x: 0.421875, y: 0.828125 },
- { x: 0.453125, y: 0.828125 },
- { x: 0.453125, y: 0.828125 },
- { x: 0.484375, y: 0.828125 },
- { x: 0.484375, y: 0.828125 },
- { x: 0.515625, y: 0.828125 },
- { x: 0.515625, y: 0.828125 },
- { x: 0.546875, y: 0.828125 },
- { x: 0.546875, y: 0.828125 },
- { x: 0.578125, y: 0.828125 },
- { x: 0.578125, y: 0.828125 },
- { x: 0.609375, y: 0.828125 },
- { x: 0.609375, y: 0.828125 },
- { x: 0.640625, y: 0.828125 },
- { x: 0.640625, y: 0.828125 },
- { x: 0.671875, y: 0.828125 },
- { x: 0.671875, y: 0.828125 },
- { x: 0.703125, y: 0.828125 },
- { x: 0.703125, y: 0.828125 },
- { x: 0.734375, y: 0.828125 },
- { x: 0.734375, y: 0.828125 },
- { x: 0.765625, y: 0.828125 },
- { x: 0.765625, y: 0.828125 },
- { x: 0.796875, y: 0.828125 },
- { x: 0.796875, y: 0.828125 },
- { x: 0.828125, y: 0.828125 },
- { x: 0.828125, y: 0.828125 },
- { x: 0.859375, y: 0.828125 },
- { x: 0.859375, y: 0.828125 },
- { x: 0.890625, y: 0.828125 },
- { x: 0.890625, y: 0.828125 },
- { x: 0.921875, y: 0.828125 },
- { x: 0.921875, y: 0.828125 },
- { x: 0.953125, y: 0.828125 },
- { x: 0.953125, y: 0.828125 },
- { x: 0.984375, y: 0.828125 },
- { x: 0.984375, y: 0.828125 },
- { x: 0.015625, y: 0.859375 },
- { x: 0.015625, y: 0.859375 },
- { x: 0.046875, y: 0.859375 },
- { x: 0.046875, y: 0.859375 },
- { x: 0.078125, y: 0.859375 },
- { x: 0.078125, y: 0.859375 },
- { x: 0.109375, y: 0.859375 },
- { x: 0.109375, y: 0.859375 },
- { x: 0.140625, y: 0.859375 },
- { x: 0.140625, y: 0.859375 },
- { x: 0.171875, y: 0.859375 },
- { x: 0.171875, y: 0.859375 },
- { x: 0.203125, y: 0.859375 },
- { x: 0.203125, y: 0.859375 },
- { x: 0.234375, y: 0.859375 },
- { x: 0.234375, y: 0.859375 },
- { x: 0.265625, y: 0.859375 },
- { x: 0.265625, y: 0.859375 },
- { x: 0.296875, y: 0.859375 },
- { x: 0.296875, y: 0.859375 },
- { x: 0.328125, y: 0.859375 },
- { x: 0.328125, y: 0.859375 },
- { x: 0.359375, y: 0.859375 },
- { x: 0.359375, y: 0.859375 },
- { x: 0.390625, y: 0.859375 },
- { x: 0.390625, y: 0.859375 },
- { x: 0.421875, y: 0.859375 },
- { x: 0.421875, y: 0.859375 },
- { x: 0.453125, y: 0.859375 },
- { x: 0.453125, y: 0.859375 },
- { x: 0.484375, y: 0.859375 },
- { x: 0.484375, y: 0.859375 },
- { x: 0.515625, y: 0.859375 },
- { x: 0.515625, y: 0.859375 },
- { x: 0.546875, y: 0.859375 },
- { x: 0.546875, y: 0.859375 },
- { x: 0.578125, y: 0.859375 },
- { x: 0.578125, y: 0.859375 },
- { x: 0.609375, y: 0.859375 },
- { x: 0.609375, y: 0.859375 },
- { x: 0.640625, y: 0.859375 },
- { x: 0.640625, y: 0.859375 },
- { x: 0.671875, y: 0.859375 },
- { x: 0.671875, y: 0.859375 },
- { x: 0.703125, y: 0.859375 },
- { x: 0.703125, y: 0.859375 },
- { x: 0.734375, y: 0.859375 },
- { x: 0.734375, y: 0.859375 },
- { x: 0.765625, y: 0.859375 },
- { x: 0.765625, y: 0.859375 },
- { x: 0.796875, y: 0.859375 },
- { x: 0.796875, y: 0.859375 },
- { x: 0.828125, y: 0.859375 },
- { x: 0.828125, y: 0.859375 },
- { x: 0.859375, y: 0.859375 },
- { x: 0.859375, y: 0.859375 },
- { x: 0.890625, y: 0.859375 },
- { x: 0.890625, y: 0.859375 },
- { x: 0.921875, y: 0.859375 },
- { x: 0.921875, y: 0.859375 },
- { x: 0.953125, y: 0.859375 },
- { x: 0.953125, y: 0.859375 },
- { x: 0.984375, y: 0.859375 },
- { x: 0.984375, y: 0.859375 },
- { x: 0.015625, y: 0.890625 },
- { x: 0.015625, y: 0.890625 },
- { x: 0.046875, y: 0.890625 },
- { x: 0.046875, y: 0.890625 },
- { x: 0.078125, y: 0.890625 },
- { x: 0.078125, y: 0.890625 },
- { x: 0.109375, y: 0.890625 },
- { x: 0.109375, y: 0.890625 },
- { x: 0.140625, y: 0.890625 },
- { x: 0.140625, y: 0.890625 },
- { x: 0.171875, y: 0.890625 },
- { x: 0.171875, y: 0.890625 },
- { x: 0.203125, y: 0.890625 },
- { x: 0.203125, y: 0.890625 },
- { x: 0.234375, y: 0.890625 },
- { x: 0.234375, y: 0.890625 },
- { x: 0.265625, y: 0.890625 },
- { x: 0.265625, y: 0.890625 },
- { x: 0.296875, y: 0.890625 },
- { x: 0.296875, y: 0.890625 },
- { x: 0.328125, y: 0.890625 },
- { x: 0.328125, y: 0.890625 },
- { x: 0.359375, y: 0.890625 },
- { x: 0.359375, y: 0.890625 },
- { x: 0.390625, y: 0.890625 },
- { x: 0.390625, y: 0.890625 },
- { x: 0.421875, y: 0.890625 },
- { x: 0.421875, y: 0.890625 },
- { x: 0.453125, y: 0.890625 },
- { x: 0.453125, y: 0.890625 },
- { x: 0.484375, y: 0.890625 },
- { x: 0.484375, y: 0.890625 },
- { x: 0.515625, y: 0.890625 },
- { x: 0.515625, y: 0.890625 },
- { x: 0.546875, y: 0.890625 },
- { x: 0.546875, y: 0.890625 },
- { x: 0.578125, y: 0.890625 },
- { x: 0.578125, y: 0.890625 },
- { x: 0.609375, y: 0.890625 },
- { x: 0.609375, y: 0.890625 },
- { x: 0.640625, y: 0.890625 },
- { x: 0.640625, y: 0.890625 },
- { x: 0.671875, y: 0.890625 },
- { x: 0.671875, y: 0.890625 },
- { x: 0.703125, y: 0.890625 },
- { x: 0.703125, y: 0.890625 },
- { x: 0.734375, y: 0.890625 },
- { x: 0.734375, y: 0.890625 },
- { x: 0.765625, y: 0.890625 },
- { x: 0.765625, y: 0.890625 },
- { x: 0.796875, y: 0.890625 },
- { x: 0.796875, y: 0.890625 },
- { x: 0.828125, y: 0.890625 },
- { x: 0.828125, y: 0.890625 },
- { x: 0.859375, y: 0.890625 },
- { x: 0.859375, y: 0.890625 },
- { x: 0.890625, y: 0.890625 },
- { x: 0.890625, y: 0.890625 },
- { x: 0.921875, y: 0.890625 },
- { x: 0.921875, y: 0.890625 },
- { x: 0.953125, y: 0.890625 },
- { x: 0.953125, y: 0.890625 },
- { x: 0.984375, y: 0.890625 },
- { x: 0.984375, y: 0.890625 },
- { x: 0.015625, y: 0.921875 },
- { x: 0.015625, y: 0.921875 },
- { x: 0.046875, y: 0.921875 },
- { x: 0.046875, y: 0.921875 },
- { x: 0.078125, y: 0.921875 },
- { x: 0.078125, y: 0.921875 },
- { x: 0.109375, y: 0.921875 },
- { x: 0.109375, y: 0.921875 },
- { x: 0.140625, y: 0.921875 },
- { x: 0.140625, y: 0.921875 },
- { x: 0.171875, y: 0.921875 },
- { x: 0.171875, y: 0.921875 },
- { x: 0.203125, y: 0.921875 },
- { x: 0.203125, y: 0.921875 },
- { x: 0.234375, y: 0.921875 },
- { x: 0.234375, y: 0.921875 },
- { x: 0.265625, y: 0.921875 },
- { x: 0.265625, y: 0.921875 },
- { x: 0.296875, y: 0.921875 },
- { x: 0.296875, y: 0.921875 },
- { x: 0.328125, y: 0.921875 },
- { x: 0.328125, y: 0.921875 },
- { x: 0.359375, y: 0.921875 },
- { x: 0.359375, y: 0.921875 },
- { x: 0.390625, y: 0.921875 },
- { x: 0.390625, y: 0.921875 },
- { x: 0.421875, y: 0.921875 },
- { x: 0.421875, y: 0.921875 },
- { x: 0.453125, y: 0.921875 },
- { x: 0.453125, y: 0.921875 },
- { x: 0.484375, y: 0.921875 },
- { x: 0.484375, y: 0.921875 },
- { x: 0.515625, y: 0.921875 },
- { x: 0.515625, y: 0.921875 },
- { x: 0.546875, y: 0.921875 },
- { x: 0.546875, y: 0.921875 },
- { x: 0.578125, y: 0.921875 },
- { x: 0.578125, y: 0.921875 },
- { x: 0.609375, y: 0.921875 },
- { x: 0.609375, y: 0.921875 },
- { x: 0.640625, y: 0.921875 },
- { x: 0.640625, y: 0.921875 },
- { x: 0.671875, y: 0.921875 },
- { x: 0.671875, y: 0.921875 },
- { x: 0.703125, y: 0.921875 },
- { x: 0.703125, y: 0.921875 },
- { x: 0.734375, y: 0.921875 },
- { x: 0.734375, y: 0.921875 },
- { x: 0.765625, y: 0.921875 },
- { x: 0.765625, y: 0.921875 },
- { x: 0.796875, y: 0.921875 },
- { x: 0.796875, y: 0.921875 },
- { x: 0.828125, y: 0.921875 },
- { x: 0.828125, y: 0.921875 },
- { x: 0.859375, y: 0.921875 },
- { x: 0.859375, y: 0.921875 },
- { x: 0.890625, y: 0.921875 },
- { x: 0.890625, y: 0.921875 },
- { x: 0.921875, y: 0.921875 },
- { x: 0.921875, y: 0.921875 },
- { x: 0.953125, y: 0.921875 },
- { x: 0.953125, y: 0.921875 },
- { x: 0.984375, y: 0.921875 },
- { x: 0.984375, y: 0.921875 },
- { x: 0.015625, y: 0.953125 },
- { x: 0.015625, y: 0.953125 },
- { x: 0.046875, y: 0.953125 },
- { x: 0.046875, y: 0.953125 },
- { x: 0.078125, y: 0.953125 },
- { x: 0.078125, y: 0.953125 },
- { x: 0.109375, y: 0.953125 },
- { x: 0.109375, y: 0.953125 },
- { x: 0.140625, y: 0.953125 },
- { x: 0.140625, y: 0.953125 },
- { x: 0.171875, y: 0.953125 },
- { x: 0.171875, y: 0.953125 },
- { x: 0.203125, y: 0.953125 },
- { x: 0.203125, y: 0.953125 },
- { x: 0.234375, y: 0.953125 },
- { x: 0.234375, y: 0.953125 },
- { x: 0.265625, y: 0.953125 },
- { x: 0.265625, y: 0.953125 },
- { x: 0.296875, y: 0.953125 },
- { x: 0.296875, y: 0.953125 },
- { x: 0.328125, y: 0.953125 },
- { x: 0.328125, y: 0.953125 },
- { x: 0.359375, y: 0.953125 },
- { x: 0.359375, y: 0.953125 },
- { x: 0.390625, y: 0.953125 },
- { x: 0.390625, y: 0.953125 },
- { x: 0.421875, y: 0.953125 },
- { x: 0.421875, y: 0.953125 },
- { x: 0.453125, y: 0.953125 },
- { x: 0.453125, y: 0.953125 },
- { x: 0.484375, y: 0.953125 },
- { x: 0.484375, y: 0.953125 },
- { x: 0.515625, y: 0.953125 },
- { x: 0.515625, y: 0.953125 },
- { x: 0.546875, y: 0.953125 },
- { x: 0.546875, y: 0.953125 },
- { x: 0.578125, y: 0.953125 },
- { x: 0.578125, y: 0.953125 },
- { x: 0.609375, y: 0.953125 },
- { x: 0.609375, y: 0.953125 },
- { x: 0.640625, y: 0.953125 },
- { x: 0.640625, y: 0.953125 },
- { x: 0.671875, y: 0.953125 },
- { x: 0.671875, y: 0.953125 },
- { x: 0.703125, y: 0.953125 },
- { x: 0.703125, y: 0.953125 },
- { x: 0.734375, y: 0.953125 },
- { x: 0.734375, y: 0.953125 },
- { x: 0.765625, y: 0.953125 },
- { x: 0.765625, y: 0.953125 },
- { x: 0.796875, y: 0.953125 },
- { x: 0.796875, y: 0.953125 },
- { x: 0.828125, y: 0.953125 },
- { x: 0.828125, y: 0.953125 },
- { x: 0.859375, y: 0.953125 },
- { x: 0.859375, y: 0.953125 },
- { x: 0.890625, y: 0.953125 },
- { x: 0.890625, y: 0.953125 },
- { x: 0.921875, y: 0.953125 },
- { x: 0.921875, y: 0.953125 },
- { x: 0.953125, y: 0.953125 },
- { x: 0.953125, y: 0.953125 },
- { x: 0.984375, y: 0.953125 },
- { x: 0.984375, y: 0.953125 },
- { x: 0.015625, y: 0.984375 },
- { x: 0.015625, y: 0.984375 },
- { x: 0.046875, y: 0.984375 },
- { x: 0.046875, y: 0.984375 },
- { x: 0.078125, y: 0.984375 },
- { x: 0.078125, y: 0.984375 },
- { x: 0.109375, y: 0.984375 },
- { x: 0.109375, y: 0.984375 },
- { x: 0.140625, y: 0.984375 },
- { x: 0.140625, y: 0.984375 },
- { x: 0.171875, y: 0.984375 },
- { x: 0.171875, y: 0.984375 },
- { x: 0.203125, y: 0.984375 },
- { x: 0.203125, y: 0.984375 },
- { x: 0.234375, y: 0.984375 },
- { x: 0.234375, y: 0.984375 },
- { x: 0.265625, y: 0.984375 },
- { x: 0.265625, y: 0.984375 },
- { x: 0.296875, y: 0.984375 },
- { x: 0.296875, y: 0.984375 },
- { x: 0.328125, y: 0.984375 },
- { x: 0.328125, y: 0.984375 },
- { x: 0.359375, y: 0.984375 },
- { x: 0.359375, y: 0.984375 },
- { x: 0.390625, y: 0.984375 },
- { x: 0.390625, y: 0.984375 },
- { x: 0.421875, y: 0.984375 },
- { x: 0.421875, y: 0.984375 },
- { x: 0.453125, y: 0.984375 },
- { x: 0.453125, y: 0.984375 },
- { x: 0.484375, y: 0.984375 },
- { x: 0.484375, y: 0.984375 },
- { x: 0.515625, y: 0.984375 },
- { x: 0.515625, y: 0.984375 },
- { x: 0.546875, y: 0.984375 },
- { x: 0.546875, y: 0.984375 },
- { x: 0.578125, y: 0.984375 },
- { x: 0.578125, y: 0.984375 },
- { x: 0.609375, y: 0.984375 },
- { x: 0.609375, y: 0.984375 },
- { x: 0.640625, y: 0.984375 },
- { x: 0.640625, y: 0.984375 },
- { x: 0.671875, y: 0.984375 },
- { x: 0.671875, y: 0.984375 },
- { x: 0.703125, y: 0.984375 },
- { x: 0.703125, y: 0.984375 },
- { x: 0.734375, y: 0.984375 },
- { x: 0.734375, y: 0.984375 },
- { x: 0.765625, y: 0.984375 },
- { x: 0.765625, y: 0.984375 },
- { x: 0.796875, y: 0.984375 },
- { x: 0.796875, y: 0.984375 },
- { x: 0.828125, y: 0.984375 },
- { x: 0.828125, y: 0.984375 },
- { x: 0.859375, y: 0.984375 },
- { x: 0.859375, y: 0.984375 },
- { x: 0.890625, y: 0.984375 },
- { x: 0.890625, y: 0.984375 },
- { x: 0.921875, y: 0.984375 },
- { x: 0.921875, y: 0.984375 },
- { x: 0.953125, y: 0.984375 },
- { x: 0.953125, y: 0.984375 },
- { x: 0.984375, y: 0.984375 },
- { x: 0.984375, y: 0.984375 },
- { x: 0.03125, y: 0.03125 },
- { x: 0.03125, y: 0.03125 },
- { x: 0.09375, y: 0.03125 },
- { x: 0.09375, y: 0.03125 },
- { x: 0.15625, y: 0.03125 },
- { x: 0.15625, y: 0.03125 },
- { x: 0.21875, y: 0.03125 },
- { x: 0.21875, y: 0.03125 },
- { x: 0.28125, y: 0.03125 },
- { x: 0.28125, y: 0.03125 },
- { x: 0.34375, y: 0.03125 },
- { x: 0.34375, y: 0.03125 },
- { x: 0.40625, y: 0.03125 },
- { x: 0.40625, y: 0.03125 },
- { x: 0.46875, y: 0.03125 },
- { x: 0.46875, y: 0.03125 },
- { x: 0.53125, y: 0.03125 },
- { x: 0.53125, y: 0.03125 },
- { x: 0.59375, y: 0.03125 },
- { x: 0.59375, y: 0.03125 },
- { x: 0.65625, y: 0.03125 },
- { x: 0.65625, y: 0.03125 },
- { x: 0.71875, y: 0.03125 },
- { x: 0.71875, y: 0.03125 },
- { x: 0.78125, y: 0.03125 },
- { x: 0.78125, y: 0.03125 },
- { x: 0.84375, y: 0.03125 },
- { x: 0.84375, y: 0.03125 },
- { x: 0.90625, y: 0.03125 },
- { x: 0.90625, y: 0.03125 },
- { x: 0.96875, y: 0.03125 },
- { x: 0.96875, y: 0.03125 },
- { x: 0.03125, y: 0.09375 },
- { x: 0.03125, y: 0.09375 },
- { x: 0.09375, y: 0.09375 },
- { x: 0.09375, y: 0.09375 },
- { x: 0.15625, y: 0.09375 },
- { x: 0.15625, y: 0.09375 },
- { x: 0.21875, y: 0.09375 },
- { x: 0.21875, y: 0.09375 },
- { x: 0.28125, y: 0.09375 },
- { x: 0.28125, y: 0.09375 },
- { x: 0.34375, y: 0.09375 },
- { x: 0.34375, y: 0.09375 },
- { x: 0.40625, y: 0.09375 },
- { x: 0.40625, y: 0.09375 },
- { x: 0.46875, y: 0.09375 },
- { x: 0.46875, y: 0.09375 },
- { x: 0.53125, y: 0.09375 },
- { x: 0.53125, y: 0.09375 },
- { x: 0.59375, y: 0.09375 },
- { x: 0.59375, y: 0.09375 },
- { x: 0.65625, y: 0.09375 },
- { x: 0.65625, y: 0.09375 },
- { x: 0.71875, y: 0.09375 },
- { x: 0.71875, y: 0.09375 },
- { x: 0.78125, y: 0.09375 },
- { x: 0.78125, y: 0.09375 },
- { x: 0.84375, y: 0.09375 },
- { x: 0.84375, y: 0.09375 },
- { x: 0.90625, y: 0.09375 },
- { x: 0.90625, y: 0.09375 },
- { x: 0.96875, y: 0.09375 },
- { x: 0.96875, y: 0.09375 },
- { x: 0.03125, y: 0.15625 },
- { x: 0.03125, y: 0.15625 },
- { x: 0.09375, y: 0.15625 },
- { x: 0.09375, y: 0.15625 },
- { x: 0.15625, y: 0.15625 },
- { x: 0.15625, y: 0.15625 },
- { x: 0.21875, y: 0.15625 },
- { x: 0.21875, y: 0.15625 },
- { x: 0.28125, y: 0.15625 },
- { x: 0.28125, y: 0.15625 },
- { x: 0.34375, y: 0.15625 },
- { x: 0.34375, y: 0.15625 },
- { x: 0.40625, y: 0.15625 },
- { x: 0.40625, y: 0.15625 },
- { x: 0.46875, y: 0.15625 },
- { x: 0.46875, y: 0.15625 },
- { x: 0.53125, y: 0.15625 },
- { x: 0.53125, y: 0.15625 },
- { x: 0.59375, y: 0.15625 },
- { x: 0.59375, y: 0.15625 },
- { x: 0.65625, y: 0.15625 },
- { x: 0.65625, y: 0.15625 },
- { x: 0.71875, y: 0.15625 },
- { x: 0.71875, y: 0.15625 },
- { x: 0.78125, y: 0.15625 },
- { x: 0.78125, y: 0.15625 },
- { x: 0.84375, y: 0.15625 },
- { x: 0.84375, y: 0.15625 },
- { x: 0.90625, y: 0.15625 },
- { x: 0.90625, y: 0.15625 },
- { x: 0.96875, y: 0.15625 },
- { x: 0.96875, y: 0.15625 },
- { x: 0.03125, y: 0.21875 },
- { x: 0.03125, y: 0.21875 },
- { x: 0.09375, y: 0.21875 },
- { x: 0.09375, y: 0.21875 },
- { x: 0.15625, y: 0.21875 },
- { x: 0.15625, y: 0.21875 },
- { x: 0.21875, y: 0.21875 },
- { x: 0.21875, y: 0.21875 },
- { x: 0.28125, y: 0.21875 },
- { x: 0.28125, y: 0.21875 },
- { x: 0.34375, y: 0.21875 },
- { x: 0.34375, y: 0.21875 },
- { x: 0.40625, y: 0.21875 },
- { x: 0.40625, y: 0.21875 },
- { x: 0.46875, y: 0.21875 },
- { x: 0.46875, y: 0.21875 },
- { x: 0.53125, y: 0.21875 },
- { x: 0.53125, y: 0.21875 },
- { x: 0.59375, y: 0.21875 },
- { x: 0.59375, y: 0.21875 },
- { x: 0.65625, y: 0.21875 },
- { x: 0.65625, y: 0.21875 },
- { x: 0.71875, y: 0.21875 },
- { x: 0.71875, y: 0.21875 },
- { x: 0.78125, y: 0.21875 },
- { x: 0.78125, y: 0.21875 },
- { x: 0.84375, y: 0.21875 },
- { x: 0.84375, y: 0.21875 },
- { x: 0.90625, y: 0.21875 },
- { x: 0.90625, y: 0.21875 },
- { x: 0.96875, y: 0.21875 },
- { x: 0.96875, y: 0.21875 },
- { x: 0.03125, y: 0.28125 },
- { x: 0.03125, y: 0.28125 },
- { x: 0.09375, y: 0.28125 },
- { x: 0.09375, y: 0.28125 },
- { x: 0.15625, y: 0.28125 },
- { x: 0.15625, y: 0.28125 },
- { x: 0.21875, y: 0.28125 },
- { x: 0.21875, y: 0.28125 },
- { x: 0.28125, y: 0.28125 },
- { x: 0.28125, y: 0.28125 },
- { x: 0.34375, y: 0.28125 },
- { x: 0.34375, y: 0.28125 },
- { x: 0.40625, y: 0.28125 },
- { x: 0.40625, y: 0.28125 },
- { x: 0.46875, y: 0.28125 },
- { x: 0.46875, y: 0.28125 },
- { x: 0.53125, y: 0.28125 },
- { x: 0.53125, y: 0.28125 },
- { x: 0.59375, y: 0.28125 },
- { x: 0.59375, y: 0.28125 },
- { x: 0.65625, y: 0.28125 },
- { x: 0.65625, y: 0.28125 },
- { x: 0.71875, y: 0.28125 },
- { x: 0.71875, y: 0.28125 },
- { x: 0.78125, y: 0.28125 },
- { x: 0.78125, y: 0.28125 },
- { x: 0.84375, y: 0.28125 },
- { x: 0.84375, y: 0.28125 },
- { x: 0.90625, y: 0.28125 },
- { x: 0.90625, y: 0.28125 },
- { x: 0.96875, y: 0.28125 },
- { x: 0.96875, y: 0.28125 },
- { x: 0.03125, y: 0.34375 },
- { x: 0.03125, y: 0.34375 },
- { x: 0.09375, y: 0.34375 },
- { x: 0.09375, y: 0.34375 },
- { x: 0.15625, y: 0.34375 },
- { x: 0.15625, y: 0.34375 },
- { x: 0.21875, y: 0.34375 },
- { x: 0.21875, y: 0.34375 },
- { x: 0.28125, y: 0.34375 },
- { x: 0.28125, y: 0.34375 },
- { x: 0.34375, y: 0.34375 },
- { x: 0.34375, y: 0.34375 },
- { x: 0.40625, y: 0.34375 },
- { x: 0.40625, y: 0.34375 },
- { x: 0.46875, y: 0.34375 },
- { x: 0.46875, y: 0.34375 },
- { x: 0.53125, y: 0.34375 },
- { x: 0.53125, y: 0.34375 },
- { x: 0.59375, y: 0.34375 },
- { x: 0.59375, y: 0.34375 },
- { x: 0.65625, y: 0.34375 },
- { x: 0.65625, y: 0.34375 },
- { x: 0.71875, y: 0.34375 },
- { x: 0.71875, y: 0.34375 },
- { x: 0.78125, y: 0.34375 },
- { x: 0.78125, y: 0.34375 },
- { x: 0.84375, y: 0.34375 },
- { x: 0.84375, y: 0.34375 },
- { x: 0.90625, y: 0.34375 },
- { x: 0.90625, y: 0.34375 },
- { x: 0.96875, y: 0.34375 },
- { x: 0.96875, y: 0.34375 },
- { x: 0.03125, y: 0.40625 },
- { x: 0.03125, y: 0.40625 },
- { x: 0.09375, y: 0.40625 },
- { x: 0.09375, y: 0.40625 },
- { x: 0.15625, y: 0.40625 },
- { x: 0.15625, y: 0.40625 },
- { x: 0.21875, y: 0.40625 },
- { x: 0.21875, y: 0.40625 },
- { x: 0.28125, y: 0.40625 },
- { x: 0.28125, y: 0.40625 },
- { x: 0.34375, y: 0.40625 },
- { x: 0.34375, y: 0.40625 },
- { x: 0.40625, y: 0.40625 },
- { x: 0.40625, y: 0.40625 },
- { x: 0.46875, y: 0.40625 },
- { x: 0.46875, y: 0.40625 },
- { x: 0.53125, y: 0.40625 },
- { x: 0.53125, y: 0.40625 },
- { x: 0.59375, y: 0.40625 },
- { x: 0.59375, y: 0.40625 },
- { x: 0.65625, y: 0.40625 },
- { x: 0.65625, y: 0.40625 },
- { x: 0.71875, y: 0.40625 },
- { x: 0.71875, y: 0.40625 },
- { x: 0.78125, y: 0.40625 },
- { x: 0.78125, y: 0.40625 },
- { x: 0.84375, y: 0.40625 },
- { x: 0.84375, y: 0.40625 },
- { x: 0.90625, y: 0.40625 },
- { x: 0.90625, y: 0.40625 },
- { x: 0.96875, y: 0.40625 },
- { x: 0.96875, y: 0.40625 },
- { x: 0.03125, y: 0.46875 },
- { x: 0.03125, y: 0.46875 },
- { x: 0.09375, y: 0.46875 },
- { x: 0.09375, y: 0.46875 },
- { x: 0.15625, y: 0.46875 },
- { x: 0.15625, y: 0.46875 },
- { x: 0.21875, y: 0.46875 },
- { x: 0.21875, y: 0.46875 },
- { x: 0.28125, y: 0.46875 },
- { x: 0.28125, y: 0.46875 },
- { x: 0.34375, y: 0.46875 },
- { x: 0.34375, y: 0.46875 },
- { x: 0.40625, y: 0.46875 },
- { x: 0.40625, y: 0.46875 },
- { x: 0.46875, y: 0.46875 },
- { x: 0.46875, y: 0.46875 },
- { x: 0.53125, y: 0.46875 },
- { x: 0.53125, y: 0.46875 },
- { x: 0.59375, y: 0.46875 },
- { x: 0.59375, y: 0.46875 },
- { x: 0.65625, y: 0.46875 },
- { x: 0.65625, y: 0.46875 },
- { x: 0.71875, y: 0.46875 },
- { x: 0.71875, y: 0.46875 },
- { x: 0.78125, y: 0.46875 },
- { x: 0.78125, y: 0.46875 },
- { x: 0.84375, y: 0.46875 },
- { x: 0.84375, y: 0.46875 },
- { x: 0.90625, y: 0.46875 },
- { x: 0.90625, y: 0.46875 },
- { x: 0.96875, y: 0.46875 },
- { x: 0.96875, y: 0.46875 },
- { x: 0.03125, y: 0.53125 },
- { x: 0.03125, y: 0.53125 },
- { x: 0.09375, y: 0.53125 },
- { x: 0.09375, y: 0.53125 },
- { x: 0.15625, y: 0.53125 },
- { x: 0.15625, y: 0.53125 },
- { x: 0.21875, y: 0.53125 },
- { x: 0.21875, y: 0.53125 },
- { x: 0.28125, y: 0.53125 },
- { x: 0.28125, y: 0.53125 },
- { x: 0.34375, y: 0.53125 },
- { x: 0.34375, y: 0.53125 },
- { x: 0.40625, y: 0.53125 },
- { x: 0.40625, y: 0.53125 },
- { x: 0.46875, y: 0.53125 },
- { x: 0.46875, y: 0.53125 },
- { x: 0.53125, y: 0.53125 },
- { x: 0.53125, y: 0.53125 },
- { x: 0.59375, y: 0.53125 },
- { x: 0.59375, y: 0.53125 },
- { x: 0.65625, y: 0.53125 },
- { x: 0.65625, y: 0.53125 },
- { x: 0.71875, y: 0.53125 },
- { x: 0.71875, y: 0.53125 },
- { x: 0.78125, y: 0.53125 },
- { x: 0.78125, y: 0.53125 },
- { x: 0.84375, y: 0.53125 },
- { x: 0.84375, y: 0.53125 },
- { x: 0.90625, y: 0.53125 },
- { x: 0.90625, y: 0.53125 },
- { x: 0.96875, y: 0.53125 },
- { x: 0.96875, y: 0.53125 },
- { x: 0.03125, y: 0.59375 },
- { x: 0.03125, y: 0.59375 },
- { x: 0.09375, y: 0.59375 },
- { x: 0.09375, y: 0.59375 },
- { x: 0.15625, y: 0.59375 },
- { x: 0.15625, y: 0.59375 },
- { x: 0.21875, y: 0.59375 },
- { x: 0.21875, y: 0.59375 },
- { x: 0.28125, y: 0.59375 },
- { x: 0.28125, y: 0.59375 },
- { x: 0.34375, y: 0.59375 },
- { x: 0.34375, y: 0.59375 },
- { x: 0.40625, y: 0.59375 },
- { x: 0.40625, y: 0.59375 },
- { x: 0.46875, y: 0.59375 },
- { x: 0.46875, y: 0.59375 },
- { x: 0.53125, y: 0.59375 },
- { x: 0.53125, y: 0.59375 },
- { x: 0.59375, y: 0.59375 },
- { x: 0.59375, y: 0.59375 },
- { x: 0.65625, y: 0.59375 },
- { x: 0.65625, y: 0.59375 },
- { x: 0.71875, y: 0.59375 },
- { x: 0.71875, y: 0.59375 },
- { x: 0.78125, y: 0.59375 },
- { x: 0.78125, y: 0.59375 },
- { x: 0.84375, y: 0.59375 },
- { x: 0.84375, y: 0.59375 },
- { x: 0.90625, y: 0.59375 },
- { x: 0.90625, y: 0.59375 },
- { x: 0.96875, y: 0.59375 },
- { x: 0.96875, y: 0.59375 },
- { x: 0.03125, y: 0.65625 },
- { x: 0.03125, y: 0.65625 },
- { x: 0.09375, y: 0.65625 },
- { x: 0.09375, y: 0.65625 },
- { x: 0.15625, y: 0.65625 },
- { x: 0.15625, y: 0.65625 },
- { x: 0.21875, y: 0.65625 },
- { x: 0.21875, y: 0.65625 },
- { x: 0.28125, y: 0.65625 },
- { x: 0.28125, y: 0.65625 },
- { x: 0.34375, y: 0.65625 },
- { x: 0.34375, y: 0.65625 },
- { x: 0.40625, y: 0.65625 },
- { x: 0.40625, y: 0.65625 },
- { x: 0.46875, y: 0.65625 },
- { x: 0.46875, y: 0.65625 },
- { x: 0.53125, y: 0.65625 },
- { x: 0.53125, y: 0.65625 },
- { x: 0.59375, y: 0.65625 },
- { x: 0.59375, y: 0.65625 },
- { x: 0.65625, y: 0.65625 },
- { x: 0.65625, y: 0.65625 },
- { x: 0.71875, y: 0.65625 },
- { x: 0.71875, y: 0.65625 },
- { x: 0.78125, y: 0.65625 },
- { x: 0.78125, y: 0.65625 },
- { x: 0.84375, y: 0.65625 },
- { x: 0.84375, y: 0.65625 },
- { x: 0.90625, y: 0.65625 },
- { x: 0.90625, y: 0.65625 },
- { x: 0.96875, y: 0.65625 },
- { x: 0.96875, y: 0.65625 },
- { x: 0.03125, y: 0.71875 },
- { x: 0.03125, y: 0.71875 },
- { x: 0.09375, y: 0.71875 },
- { x: 0.09375, y: 0.71875 },
- { x: 0.15625, y: 0.71875 },
- { x: 0.15625, y: 0.71875 },
- { x: 0.21875, y: 0.71875 },
- { x: 0.21875, y: 0.71875 },
- { x: 0.28125, y: 0.71875 },
- { x: 0.28125, y: 0.71875 },
- { x: 0.34375, y: 0.71875 },
- { x: 0.34375, y: 0.71875 },
- { x: 0.40625, y: 0.71875 },
- { x: 0.40625, y: 0.71875 },
- { x: 0.46875, y: 0.71875 },
- { x: 0.46875, y: 0.71875 },
- { x: 0.53125, y: 0.71875 },
- { x: 0.53125, y: 0.71875 },
- { x: 0.59375, y: 0.71875 },
- { x: 0.59375, y: 0.71875 },
- { x: 0.65625, y: 0.71875 },
- { x: 0.65625, y: 0.71875 },
- { x: 0.71875, y: 0.71875 },
- { x: 0.71875, y: 0.71875 },
- { x: 0.78125, y: 0.71875 },
- { x: 0.78125, y: 0.71875 },
- { x: 0.84375, y: 0.71875 },
- { x: 0.84375, y: 0.71875 },
- { x: 0.90625, y: 0.71875 },
- { x: 0.90625, y: 0.71875 },
- { x: 0.96875, y: 0.71875 },
- { x: 0.96875, y: 0.71875 },
- { x: 0.03125, y: 0.78125 },
- { x: 0.03125, y: 0.78125 },
- { x: 0.09375, y: 0.78125 },
- { x: 0.09375, y: 0.78125 },
- { x: 0.15625, y: 0.78125 },
- { x: 0.15625, y: 0.78125 },
- { x: 0.21875, y: 0.78125 },
- { x: 0.21875, y: 0.78125 },
- { x: 0.28125, y: 0.78125 },
- { x: 0.28125, y: 0.78125 },
- { x: 0.34375, y: 0.78125 },
- { x: 0.34375, y: 0.78125 },
- { x: 0.40625, y: 0.78125 },
- { x: 0.40625, y: 0.78125 },
- { x: 0.46875, y: 0.78125 },
- { x: 0.46875, y: 0.78125 },
- { x: 0.53125, y: 0.78125 },
- { x: 0.53125, y: 0.78125 },
- { x: 0.59375, y: 0.78125 },
- { x: 0.59375, y: 0.78125 },
- { x: 0.65625, y: 0.78125 },
- { x: 0.65625, y: 0.78125 },
- { x: 0.71875, y: 0.78125 },
- { x: 0.71875, y: 0.78125 },
- { x: 0.78125, y: 0.78125 },
- { x: 0.78125, y: 0.78125 },
- { x: 0.84375, y: 0.78125 },
- { x: 0.84375, y: 0.78125 },
- { x: 0.90625, y: 0.78125 },
- { x: 0.90625, y: 0.78125 },
- { x: 0.96875, y: 0.78125 },
- { x: 0.96875, y: 0.78125 },
- { x: 0.03125, y: 0.84375 },
- { x: 0.03125, y: 0.84375 },
- { x: 0.09375, y: 0.84375 },
- { x: 0.09375, y: 0.84375 },
- { x: 0.15625, y: 0.84375 },
- { x: 0.15625, y: 0.84375 },
- { x: 0.21875, y: 0.84375 },
- { x: 0.21875, y: 0.84375 },
- { x: 0.28125, y: 0.84375 },
- { x: 0.28125, y: 0.84375 },
- { x: 0.34375, y: 0.84375 },
- { x: 0.34375, y: 0.84375 },
- { x: 0.40625, y: 0.84375 },
- { x: 0.40625, y: 0.84375 },
- { x: 0.46875, y: 0.84375 },
- { x: 0.46875, y: 0.84375 },
- { x: 0.53125, y: 0.84375 },
- { x: 0.53125, y: 0.84375 },
- { x: 0.59375, y: 0.84375 },
- { x: 0.59375, y: 0.84375 },
- { x: 0.65625, y: 0.84375 },
- { x: 0.65625, y: 0.84375 },
- { x: 0.71875, y: 0.84375 },
- { x: 0.71875, y: 0.84375 },
- { x: 0.78125, y: 0.84375 },
- { x: 0.78125, y: 0.84375 },
- { x: 0.84375, y: 0.84375 },
- { x: 0.84375, y: 0.84375 },
- { x: 0.90625, y: 0.84375 },
- { x: 0.90625, y: 0.84375 },
- { x: 0.96875, y: 0.84375 },
- { x: 0.96875, y: 0.84375 },
- { x: 0.03125, y: 0.90625 },
- { x: 0.03125, y: 0.90625 },
- { x: 0.09375, y: 0.90625 },
- { x: 0.09375, y: 0.90625 },
- { x: 0.15625, y: 0.90625 },
- { x: 0.15625, y: 0.90625 },
- { x: 0.21875, y: 0.90625 },
- { x: 0.21875, y: 0.90625 },
- { x: 0.28125, y: 0.90625 },
- { x: 0.28125, y: 0.90625 },
- { x: 0.34375, y: 0.90625 },
- { x: 0.34375, y: 0.90625 },
- { x: 0.40625, y: 0.90625 },
- { x: 0.40625, y: 0.90625 },
- { x: 0.46875, y: 0.90625 },
- { x: 0.46875, y: 0.90625 },
- { x: 0.53125, y: 0.90625 },
- { x: 0.53125, y: 0.90625 },
- { x: 0.59375, y: 0.90625 },
- { x: 0.59375, y: 0.90625 },
- { x: 0.65625, y: 0.90625 },
- { x: 0.65625, y: 0.90625 },
- { x: 0.71875, y: 0.90625 },
- { x: 0.71875, y: 0.90625 },
- { x: 0.78125, y: 0.90625 },
- { x: 0.78125, y: 0.90625 },
- { x: 0.84375, y: 0.90625 },
- { x: 0.84375, y: 0.90625 },
- { x: 0.90625, y: 0.90625 },
- { x: 0.90625, y: 0.90625 },
- { x: 0.96875, y: 0.90625 },
- { x: 0.96875, y: 0.90625 },
- { x: 0.03125, y: 0.96875 },
- { x: 0.03125, y: 0.96875 },
- { x: 0.09375, y: 0.96875 },
- { x: 0.09375, y: 0.96875 },
- { x: 0.15625, y: 0.96875 },
- { x: 0.15625, y: 0.96875 },
- { x: 0.21875, y: 0.96875 },
- { x: 0.21875, y: 0.96875 },
- { x: 0.28125, y: 0.96875 },
- { x: 0.28125, y: 0.96875 },
- { x: 0.34375, y: 0.96875 },
- { x: 0.34375, y: 0.96875 },
- { x: 0.40625, y: 0.96875 },
- { x: 0.40625, y: 0.96875 },
- { x: 0.46875, y: 0.96875 },
- { x: 0.46875, y: 0.96875 },
- { x: 0.53125, y: 0.96875 },
- { x: 0.53125, y: 0.96875 },
- { x: 0.59375, y: 0.96875 },
- { x: 0.59375, y: 0.96875 },
- { x: 0.65625, y: 0.96875 },
- { x: 0.65625, y: 0.96875 },
- { x: 0.71875, y: 0.96875 },
- { x: 0.71875, y: 0.96875 },
- { x: 0.78125, y: 0.96875 },
- { x: 0.78125, y: 0.96875 },
- { x: 0.84375, y: 0.96875 },
- { x: 0.84375, y: 0.96875 },
- { x: 0.90625, y: 0.96875 },
- { x: 0.90625, y: 0.96875 },
- { x: 0.96875, y: 0.96875 },
- { x: 0.96875, y: 0.96875 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.0625, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.1875, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.3125, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.4375, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.5625, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.6875, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.8125, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.9375, y: 0.0625 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.0625, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.1875, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.3125, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.4375, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.5625, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.6875, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.8125, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.9375, y: 0.1875 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.0625, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.1875, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.3125, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.4375, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.5625, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.6875, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.8125, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.9375, y: 0.3125 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.0625, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.1875, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.3125, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.4375, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.5625, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.6875, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.8125, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.9375, y: 0.4375 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.0625, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.1875, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.3125, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.4375, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.5625, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.6875, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.8125, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.9375, y: 0.5625 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.0625, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.1875, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.3125, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.4375, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.5625, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.6875, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.8125, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.9375, y: 0.6875 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.0625, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.1875, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.3125, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.4375, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.5625, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.6875, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.8125, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.9375, y: 0.8125 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.0625, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.1875, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.3125, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.4375, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.5625, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.6875, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.8125, y: 0.9375 },
- { x: 0.9375, y: 0.9375 },
- { x: 0.9375, y: 0.9375 },
- { x: 0.9375, y: 0.9375 },
- { x: 0.9375, y: 0.9375 },
- { x: 0.9375, y: 0.9375 },
- { x: 0.9375, y: 0.9375 }
-];
-
-// src/hand/handposedetector.ts
-var HandDetector = class {
- constructor(model19) {
- __publicField(this, "model");
- __publicField(this, "anchors");
- __publicField(this, "anchorsTensor");
- __publicField(this, "inputSize");
- __publicField(this, "inputSizeTensor");
- __publicField(this, "doubleInputSizeTensor");
- var _a, _b, _c, _d;
- this.model = model19;
- this.anchors = anchors2.map((anchor) => [anchor.x, anchor.y]);
- this.anchorsTensor = tfjs_esm_exports.tensor2d(this.anchors);
- this.inputSize = ((_d = (_c = (_b = (_a = this == null ? void 0 : this.model) == null ? void 0 : _a.inputs) == null ? void 0 : _b[0]) == null ? void 0 : _c.shape) == null ? void 0 : _d[2]) || 0;
- this.inputSizeTensor = tfjs_esm_exports.tensor1d([this.inputSize, this.inputSize]);
- this.doubleInputSizeTensor = tfjs_esm_exports.tensor1d([this.inputSize * 2, this.inputSize * 2]);
- }
- normalizeBoxes(boxes) {
- const t = {};
- t.boxOffsets = tfjs_esm_exports.slice(boxes, [0, 0], [-1, 2]);
- t.boxSizes = tfjs_esm_exports.slice(boxes, [0, 2], [-1, 2]);
- t.div = tfjs_esm_exports.div(t.boxOffsets, this.inputSizeTensor);
- t.boxCenterPoints = tfjs_esm_exports.add(t.div, this.anchorsTensor);
- t.halfBoxSizes = tfjs_esm_exports.div(t.boxSizes, this.doubleInputSizeTensor);
- t.sub = tfjs_esm_exports.sub(t.boxCenterPoints, t.halfBoxSizes);
- t.startPoints = tfjs_esm_exports.mul(t.sub, this.inputSizeTensor);
- t.add = tfjs_esm_exports.add(t.boxCenterPoints, t.halfBoxSizes);
- t.endPoints = tfjs_esm_exports.mul(t.add, this.inputSizeTensor);
- const res = tfjs_esm_exports.concat2d([t.startPoints, t.endPoints], 1);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return res;
- }
- normalizeLandmarks(rawPalmLandmarks, index2) {
- const t = {};
- t.reshape = tfjs_esm_exports.reshape(rawPalmLandmarks, [-1, 7, 2]);
- t.div = tfjs_esm_exports.div(t.reshape, this.inputSizeTensor);
- t.landmarks = tfjs_esm_exports.add(t.div, this.anchors[index2] ? this.anchors[index2] : 0);
- const res = tfjs_esm_exports.mul(t.landmarks, this.inputSizeTensor);
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return res;
- }
- async predict(input, config3) {
- var _a;
- const t = {};
- t.resize = tfjs_esm_exports.image.resizeBilinear(input, [this.inputSize, this.inputSize]);
- t.div = tfjs_esm_exports.div(t.resize, constants.tf127);
- t.image = tfjs_esm_exports.sub(t.div, constants.tf1);
- t.batched = this.model.execute(t.image);
- t.predictions = tfjs_esm_exports.squeeze(t.batched);
- t.slice = tfjs_esm_exports.slice(t.predictions, [0, 0], [-1, 1]);
- t.sigmoid = tfjs_esm_exports.sigmoid(t.slice);
- t.scores = tfjs_esm_exports.squeeze(t.sigmoid);
- const scores = await t.scores.data();
- t.boxes = tfjs_esm_exports.slice(t.predictions, [0, 1], [-1, 4]);
- t.norm = this.normalizeBoxes(t.boxes);
- t.nms = await tfjs_esm_exports.image.nonMaxSuppressionAsync(t.norm, t.scores, 3 * (((_a = config3.hand) == null ? void 0 : _a.maxDetected) || 1), config3.hand.iouThreshold, config3.hand.minConfidence);
- const nms = await t.nms.array();
- const hands = [];
- for (const index2 of nms) {
- const p = {};
- p.box = tfjs_esm_exports.slice(t.norm, [index2, 0], [1, -1]);
- p.slice = tfjs_esm_exports.slice(t.predictions, [index2, 5], [1, 14]);
- p.norm = this.normalizeLandmarks(p.slice, index2);
- p.palmLandmarks = tfjs_esm_exports.reshape(p.norm, [-1, 2]);
- const box = await p.box.data();
- const startPoint = box.slice(0, 2);
- const endPoint = box.slice(2, 4);
- const palmLandmarks = await p.palmLandmarks.array();
- const hand3 = { startPoint, endPoint, palmLandmarks, confidence: scores[index2] };
- const scaled = scaleBoxCoordinates2(hand3, [(input.shape[2] || 1) / this.inputSize, (input.shape[1] || 0) / this.inputSize]);
- hands.push(scaled);
- Object.keys(p).forEach((tensor3) => tfjs_esm_exports.dispose(p[tensor3]));
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return hands;
- }
-};
-
-// src/hand/handposepipeline.ts
-var palmBoxEnlargeFactor = 5;
-var handBoxEnlargeFactor = 1.65;
-var palmLandmarkIds = [0, 5, 9, 13, 17, 1, 2];
-var palmLandmarksPalmBase = 0;
-var palmLandmarksMiddleFingerBase = 2;
-var lastTime12 = 0;
-var HandPipeline = class {
- constructor(handDetector, handPoseModel2) {
- __publicField(this, "handDetector");
- __publicField(this, "handPoseModel");
- __publicField(this, "inputSize");
- __publicField(this, "storedBoxes");
- __publicField(this, "skipped");
- __publicField(this, "detectedHands");
- var _a, _b, _c;
- this.handDetector = handDetector;
- this.handPoseModel = handPoseModel2;
- this.inputSize = ((_c = (_b = (_a = this.handPoseModel) == null ? void 0 : _a.inputs) == null ? void 0 : _b[0].shape) == null ? void 0 : _c[2]) || 0;
- this.storedBoxes = [];
- this.skipped = Number.MAX_SAFE_INTEGER;
- this.detectedHands = 0;
- }
- calculateLandmarksBoundingBox(landmarks) {
- const xs = landmarks.map((d) => d[0]);
- const ys = landmarks.map((d) => d[1]);
- const startPoint = [Math.min(...xs), Math.min(...ys)];
- const endPoint = [Math.max(...xs), Math.max(...ys)];
- return { startPoint, endPoint };
- }
- getBoxForPalmLandmarks(palmLandmarks, rotationMatrix) {
- const rotatedPalmLandmarks = palmLandmarks.map((coord) => rotatePoint2([...coord, 1], rotationMatrix));
- const boxAroundPalm = this.calculateLandmarksBoundingBox(rotatedPalmLandmarks);
- return enlargeBox2(squarifyBox2(boxAroundPalm), palmBoxEnlargeFactor);
- }
- getBoxForHandLandmarks(landmarks) {
- const boundingBox = this.calculateLandmarksBoundingBox(landmarks);
- const boxAroundHand = enlargeBox2(squarifyBox2(boundingBox), handBoxEnlargeFactor);
- boxAroundHand.palmLandmarks = [];
- for (let i = 0; i < palmLandmarkIds.length; i++) {
- boxAroundHand.palmLandmarks.push(landmarks[palmLandmarkIds[i]].slice(0, 2));
- }
- return boxAroundHand;
- }
- transformRawCoords(rawCoords, box2, angle, rotationMatrix) {
- const boxSize = getBoxSize2(box2);
- const scaleFactor = [boxSize[0] / this.inputSize, boxSize[1] / this.inputSize, (boxSize[0] + boxSize[1]) / this.inputSize / 2];
- const coordsScaled = rawCoords.map((coord) => [
- scaleFactor[0] * (coord[0] - this.inputSize / 2),
- scaleFactor[1] * (coord[1] - this.inputSize / 2),
- scaleFactor[2] * coord[2]
- ]);
- const coordsRotationMatrix = buildRotationMatrix2(angle, [0, 0]);
- const coordsRotated = coordsScaled.map((coord) => {
- const rotated = rotatePoint2(coord, coordsRotationMatrix);
- return [...rotated, coord[2]];
- });
- const inverseRotationMatrix = invertTransformMatrix2(rotationMatrix);
- const boxCenter = [...getBoxCenter2(box2), 1];
- const originalBoxCenter = [
- dot2(boxCenter, inverseRotationMatrix[0]),
- dot2(boxCenter, inverseRotationMatrix[1])
- ];
- return coordsRotated.map((coord) => [
- Math.trunc(coord[0] + originalBoxCenter[0]),
- Math.trunc(coord[1] + originalBoxCenter[1]),
- Math.trunc(coord[2])
- ]);
- }
- async estimateHands(image26, config3) {
- let useFreshBox = false;
- let boxes;
- const skipTime = (config3.hand.skipTime || 0) > now() - lastTime12;
- const skipFrame = this.skipped < (config3.hand.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame) {
- boxes = await this.handDetector.predict(image26, config3);
- this.skipped = 0;
- }
- if (config3.skipAllowed)
- this.skipped++;
- if (boxes && boxes.length > 0 && (boxes.length !== this.detectedHands && this.detectedHands !== config3.hand.maxDetected || !config3.hand.landmarks)) {
- this.detectedHands = 0;
- this.storedBoxes = [...boxes];
- if (this.storedBoxes.length > 0)
- useFreshBox = true;
- }
- const hands = [];
- for (let i = 0; i < this.storedBoxes.length; i++) {
- const currentBox = this.storedBoxes[i];
- if (!currentBox)
- continue;
- if (config3.hand.landmarks) {
- const angle = config3.hand.rotation ? computeRotation2(currentBox.palmLandmarks[palmLandmarksPalmBase], currentBox.palmLandmarks[palmLandmarksMiddleFingerBase]) : 0;
- const palmCenter = getBoxCenter2(currentBox);
- const palmCenterNormalized = [palmCenter[0] / image26.shape[2], palmCenter[1] / image26.shape[1]];
- const rotatedImage = config3.hand.rotation && env.kernels.includes("rotatewithoffset") ? tfjs_esm_exports.image.rotateWithOffset(image26, angle, 0, palmCenterNormalized) : image26.clone();
- const rotationMatrix = buildRotationMatrix2(-angle, palmCenter);
- const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
- const croppedInput = cutBoxFromImageAndResize(newBox, rotatedImage, [this.inputSize, this.inputSize]);
- const handImage = tfjs_esm_exports.div(croppedInput, constants.tf255);
- tfjs_esm_exports.dispose(croppedInput);
- tfjs_esm_exports.dispose(rotatedImage);
- const [confidenceT, keypoints] = this.handPoseModel.execute(handImage);
- lastTime12 = now();
- tfjs_esm_exports.dispose(handImage);
- const confidence = (await confidenceT.data())[0];
- tfjs_esm_exports.dispose(confidenceT);
- if (confidence >= config3.hand.minConfidence / 4) {
- const keypointsReshaped = tfjs_esm_exports.reshape(keypoints, [-1, 3]);
- const rawCoords = await keypointsReshaped.array();
- tfjs_esm_exports.dispose(keypoints);
- tfjs_esm_exports.dispose(keypointsReshaped);
- const coords = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
- const nextBoundingBox = this.getBoxForHandLandmarks(coords);
- this.storedBoxes[i] = { ...nextBoundingBox, confidence };
- const result = {
- landmarks: coords,
- confidence,
- boxConfidence: currentBox.confidence,
- fingerConfidence: confidence,
- box: { topLeft: nextBoundingBox.startPoint, bottomRight: nextBoundingBox.endPoint }
- };
- hands.push(result);
- } else {
- this.storedBoxes[i] = null;
- }
- tfjs_esm_exports.dispose(keypoints);
- } else {
- const enlarged = enlargeBox2(squarifyBox2(currentBox), handBoxEnlargeFactor);
- const result = {
- confidence: currentBox.confidence,
- boxConfidence: currentBox.confidence,
- fingerConfidence: 0,
- box: { topLeft: enlarged.startPoint, bottomRight: enlarged.endPoint },
- landmarks: []
- };
- hands.push(result);
- }
- }
- this.storedBoxes = this.storedBoxes.filter((a) => a !== null);
- this.detectedHands = hands.length;
- if (hands.length > config3.hand.maxDetected)
- hands.length = config3.hand.maxDetected;
- return hands;
- }
-};
-
-// src/hand/fingerdef.ts
-var Finger = {
- thumb: 0,
- index: 1,
- middle: 2,
- ring: 3,
- pinky: 4,
- all: [0, 1, 2, 3, 4],
- nameMapping: { 0: "thumb", 1: "index", 2: "middle", 3: "ring", 4: "pinky" },
- pointsMapping: {
- 0: [[0, 1], [1, 2], [2, 3], [3, 4]],
- 1: [[0, 5], [5, 6], [6, 7], [7, 8]],
- 2: [[0, 9], [9, 10], [10, 11], [11, 12]],
- 3: [[0, 13], [13, 14], [14, 15], [15, 16]],
- 4: [[0, 17], [17, 18], [18, 19], [19, 20]]
- },
- getName: (value) => Finger.nameMapping[value],
- getPoints: (value) => Finger.pointsMapping[value]
-};
-var FingerCurl = {
- none: 0,
- half: 1,
- full: 2,
- nameMapping: { 0: "none", 1: "half", 2: "full" },
- getName: (value) => FingerCurl.nameMapping[value]
-};
-var FingerDirection = {
- verticalUp: 0,
- verticalDown: 1,
- horizontalLeft: 2,
- horizontalRight: 3,
- diagonalUpRight: 4,
- diagonalUpLeft: 5,
- diagonalDownRight: 6,
- diagonalDownLeft: 7,
- nameMapping: { 0: "verticalUp", 1: "verticalDown", 2: "horizontalLeft", 3: "horizontalRight", 4: "diagonalUpRight", 5: "diagonalUpLeft", 6: "diagonalDownRight", 7: "diagonalDownLeft" },
- getName: (value) => FingerDirection.nameMapping[value]
-};
-var FingerGesture = class {
- constructor(name) {
- __publicField(this, "name");
- __publicField(this, "curls");
- __publicField(this, "directions");
- __publicField(this, "weights");
- __publicField(this, "weightsRelative");
- this.name = name;
- this.curls = {};
- this.directions = {};
- this.weights = [1, 1, 1, 1, 1];
- this.weightsRelative = [1, 1, 1, 1, 1];
- }
- curl(finger, curl, confidence) {
- if (typeof this.curls[finger] === "undefined")
- this.curls[finger] = [];
- this.curls[finger].push([curl, confidence]);
- }
- direction(finger, position, confidence) {
- if (!this.directions[finger])
- this.directions[finger] = [];
- this.directions[finger].push([position, confidence]);
- }
- weight(finger, weight) {
- this.weights[finger] = weight;
- const total = this.weights.reduce((a, b) => a + b, 0);
- this.weightsRelative = this.weights.map((el) => el * 5 / total);
- }
- matchAgainst(detectedCurls, detectedDirections) {
- let confidence = 0;
- for (const fingerIdx in detectedCurls) {
- const detectedCurl = detectedCurls[fingerIdx];
- const expectedCurls = this.curls[fingerIdx];
- if (typeof expectedCurls === "undefined") {
- confidence += this.weightsRelative[fingerIdx];
- continue;
- }
- for (const [expectedCurl, score] of expectedCurls) {
- if (detectedCurl === expectedCurl) {
- confidence += score * this.weightsRelative[fingerIdx];
- break;
- }
- }
- }
- for (const fingerIdx in detectedDirections) {
- const detectedDirection = detectedDirections[fingerIdx];
- const expectedDirections = this.directions[fingerIdx];
- if (typeof expectedDirections === "undefined") {
- confidence += this.weightsRelative[fingerIdx];
- continue;
- }
- for (const [expectedDirection, score] of expectedDirections) {
- if (detectedDirection === expectedDirection) {
- confidence += score * this.weightsRelative[fingerIdx];
- break;
- }
- }
- }
- return confidence / 10;
- }
-};
-
-// src/hand/fingergesture.ts
-var { thumb, index, middle, ring, pinky } = Finger;
-var { none, half, full } = FingerCurl;
-var { verticalUp, verticalDown, horizontalLeft, horizontalRight, diagonalUpRight, diagonalUpLeft, diagonalDownRight, diagonalDownLeft } = FingerDirection;
-var ThumbsUp = new FingerGesture("thumbs up");
-ThumbsUp.curl(thumb, none, 1);
-ThumbsUp.direction(thumb, verticalUp, 1);
-ThumbsUp.direction(thumb, diagonalUpLeft, 0.25);
-ThumbsUp.direction(thumb, diagonalUpRight, 0.25);
-for (const finger of [Finger.index, Finger.middle, Finger.ring, Finger.pinky]) {
- ThumbsUp.curl(finger, full, 1);
- ThumbsUp.direction(finger, horizontalLeft, 1);
- ThumbsUp.direction(finger, horizontalRight, 1);
-}
-var Victory = new FingerGesture("victory");
-Victory.curl(thumb, half, 0.5);
-Victory.curl(thumb, none, 0.5);
-Victory.direction(thumb, verticalUp, 1);
-Victory.direction(thumb, diagonalUpLeft, 1);
-Victory.curl(index, none, 1);
-Victory.direction(index, verticalUp, 0.75);
-Victory.direction(index, diagonalUpLeft, 1);
-Victory.curl(middle, none, 1);
-Victory.direction(middle, verticalUp, 1);
-Victory.direction(middle, diagonalUpLeft, 0.75);
-Victory.curl(ring, full, 1);
-Victory.direction(ring, verticalUp, 0.2);
-Victory.direction(ring, diagonalUpLeft, 1);
-Victory.direction(ring, horizontalLeft, 0.2);
-Victory.curl(pinky, full, 1);
-Victory.direction(pinky, verticalUp, 0.2);
-Victory.direction(pinky, diagonalUpLeft, 1);
-Victory.direction(pinky, horizontalLeft, 0.2);
-Victory.weight(index, 2);
-Victory.weight(middle, 2);
-var Point = new FingerGesture("point");
-Point.curl(thumb, full, 1);
-Point.curl(index, none, 0.5);
-Point.curl(middle, full, 0.5);
-Point.curl(ring, full, 0.5);
-Point.curl(pinky, full, 0.5);
-Point.weight(index, 2);
-Point.weight(middle, 2);
-var MiddleFinger = new FingerGesture("middle finger");
-MiddleFinger.curl(thumb, none, 1);
-MiddleFinger.curl(index, full, 0.5);
-MiddleFinger.curl(middle, full, 0.5);
-MiddleFinger.curl(ring, full, 0.5);
-MiddleFinger.curl(pinky, full, 0.5);
-MiddleFinger.weight(index, 2);
-MiddleFinger.weight(middle, 2);
-var OpenPalm = new FingerGesture("open palm");
-OpenPalm.curl(thumb, none, 0.75);
-OpenPalm.curl(index, none, 0.75);
-OpenPalm.curl(middle, none, 0.75);
-OpenPalm.curl(ring, none, 0.75);
-OpenPalm.curl(pinky, none, 0.75);
-var fingergesture_default = [ThumbsUp, Victory, Point, MiddleFinger, OpenPalm];
-
-// src/hand/fingerpose.ts
-var minConfidence = 0.7;
-var options = {
- HALF_CURL_START_LIMIT: 60,
- NO_CURL_START_LIMIT: 130,
- DISTANCE_VOTE_POWER: 1.1,
- SINGLE_ANGLE_VOTE_POWER: 0.9,
- TOTAL_ANGLE_VOTE_POWER: 1.6
-};
-function calculateSlope(point1x, point1y, point2x, point2y) {
- const value = (point1y - point2y) / (point1x - point2x);
- let slope = Math.atan(value) * 180 / Math.PI;
- if (slope <= 0)
- slope = -slope;
- else if (slope > 0)
- slope = 180 - slope;
- return slope;
-}
-function getSlopes(point1, point2) {
- if (!point1 || !point2)
- return [0, 0];
- const slopeXY = calculateSlope(point1[0], point1[1], point2[0], point2[1]);
- if (point1.length === 2)
- return slopeXY;
- const slopeYZ = calculateSlope(point1[1], point1[2], point2[1], point2[2]);
- return [slopeXY, slopeYZ];
-}
-function angleOrientationAt(angle, weightageAt = 1) {
- let isVertical = 0;
- let isDiagonal = 0;
- let isHorizontal = 0;
- if (angle >= 75 && angle <= 105)
- isVertical = 1 * weightageAt;
- else if (angle >= 25 && angle <= 155)
- isDiagonal = 1 * weightageAt;
- else
- isHorizontal = 1 * weightageAt;
- return [isVertical, isDiagonal, isHorizontal];
-}
-function estimateFingerCurl(startPoint, midPoint, endPoint) {
- const start_mid_x_dist = startPoint[0] - midPoint[0];
- const start_end_x_dist = startPoint[0] - endPoint[0];
- const mid_end_x_dist = midPoint[0] - endPoint[0];
- const start_mid_y_dist = startPoint[1] - midPoint[1];
- const start_end_y_dist = startPoint[1] - endPoint[1];
- const mid_end_y_dist = midPoint[1] - endPoint[1];
- const start_mid_z_dist = startPoint[2] - midPoint[2];
- const start_end_z_dist = startPoint[2] - endPoint[2];
- const mid_end_z_dist = midPoint[2] - endPoint[2];
- const start_mid_dist = Math.sqrt(start_mid_x_dist * start_mid_x_dist + start_mid_y_dist * start_mid_y_dist + start_mid_z_dist * start_mid_z_dist);
- const start_end_dist = Math.sqrt(start_end_x_dist * start_end_x_dist + start_end_y_dist * start_end_y_dist + start_end_z_dist * start_end_z_dist);
- const mid_end_dist = Math.sqrt(mid_end_x_dist * mid_end_x_dist + mid_end_y_dist * mid_end_y_dist + mid_end_z_dist * mid_end_z_dist);
- let cos_in = (mid_end_dist * mid_end_dist + start_mid_dist * start_mid_dist - start_end_dist * start_end_dist) / (2 * mid_end_dist * start_mid_dist);
- if (cos_in > 1)
- cos_in = 1;
- else if (cos_in < -1)
- cos_in = -1;
- let angleOfCurve = Math.acos(cos_in);
- angleOfCurve = 57.2958 * angleOfCurve % 180;
- let fingerCurl;
- if (angleOfCurve > options.NO_CURL_START_LIMIT)
- fingerCurl = FingerCurl.none;
- else if (angleOfCurve > options.HALF_CURL_START_LIMIT)
- fingerCurl = FingerCurl.half;
- else
- fingerCurl = FingerCurl.full;
- return fingerCurl;
-}
-function estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x) {
- let estimatedDirection;
- if (max_dist_x === Math.abs(start_end_x_dist)) {
- if (start_end_x_dist > 0)
- estimatedDirection = FingerDirection.horizontalLeft;
- else
- estimatedDirection = FingerDirection.horizontalRight;
- } else if (max_dist_x === Math.abs(start_mid_x_dist)) {
- if (start_mid_x_dist > 0)
- estimatedDirection = FingerDirection.horizontalLeft;
- else
- estimatedDirection = FingerDirection.horizontalRight;
- } else {
- if (mid_end_x_dist > 0)
- estimatedDirection = FingerDirection.horizontalLeft;
- else
- estimatedDirection = FingerDirection.horizontalRight;
- }
- return estimatedDirection;
-}
-function estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y) {
- let estimatedDirection;
- if (max_dist_y === Math.abs(start_end_y_dist)) {
- if (start_end_y_dist < 0)
- estimatedDirection = FingerDirection.verticalDown;
- else
- estimatedDirection = FingerDirection.verticalUp;
- } else if (max_dist_y === Math.abs(start_mid_y_dist)) {
- if (start_mid_y_dist < 0)
- estimatedDirection = FingerDirection.verticalDown;
- else
- estimatedDirection = FingerDirection.verticalUp;
- } else {
- if (mid_end_y_dist < 0)
- estimatedDirection = FingerDirection.verticalDown;
- else
- estimatedDirection = FingerDirection.verticalUp;
- }
- return estimatedDirection;
-}
-function estimateDiagonalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y, start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x) {
- let estimatedDirection;
- const reqd_vertical_direction = estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y);
- const reqd_horizontal_direction = estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);
- if (reqd_vertical_direction === FingerDirection.verticalUp) {
- if (reqd_horizontal_direction === FingerDirection.horizontalLeft)
- estimatedDirection = FingerDirection.diagonalUpLeft;
- else
- estimatedDirection = FingerDirection.diagonalUpRight;
- } else {
- if (reqd_horizontal_direction === FingerDirection.horizontalLeft)
- estimatedDirection = FingerDirection.diagonalDownLeft;
- else
- estimatedDirection = FingerDirection.diagonalDownRight;
- }
- return estimatedDirection;
-}
-function calculateFingerDirection(startPoint, midPoint, endPoint, fingerSlopes) {
- const start_mid_x_dist = startPoint[0] - midPoint[0];
- const start_end_x_dist = startPoint[0] - endPoint[0];
- const mid_end_x_dist = midPoint[0] - endPoint[0];
- const start_mid_y_dist = startPoint[1] - midPoint[1];
- const start_end_y_dist = startPoint[1] - endPoint[1];
- const mid_end_y_dist = midPoint[1] - endPoint[1];
- const max_dist_x = Math.max(Math.abs(start_mid_x_dist), Math.abs(start_end_x_dist), Math.abs(mid_end_x_dist));
- const max_dist_y = Math.max(Math.abs(start_mid_y_dist), Math.abs(start_end_y_dist), Math.abs(mid_end_y_dist));
- let voteVertical = 0;
- let voteDiagonal = 0;
- let voteHorizontal = 0;
- const start_end_x_y_dist_ratio = max_dist_y / (max_dist_x + 1e-5);
- if (start_end_x_y_dist_ratio > 1.5)
- voteVertical += options.DISTANCE_VOTE_POWER;
- else if (start_end_x_y_dist_ratio > 0.66)
- voteDiagonal += options.DISTANCE_VOTE_POWER;
- else
- voteHorizontal += options.DISTANCE_VOTE_POWER;
- const start_mid_dist = Math.sqrt(start_mid_x_dist * start_mid_x_dist + start_mid_y_dist * start_mid_y_dist);
- const start_end_dist = Math.sqrt(start_end_x_dist * start_end_x_dist + start_end_y_dist * start_end_y_dist);
- const mid_end_dist = Math.sqrt(mid_end_x_dist * mid_end_x_dist + mid_end_y_dist * mid_end_y_dist);
- const max_dist = Math.max(start_mid_dist, start_end_dist, mid_end_dist);
- let calc_start_point_x = startPoint[0];
- let calc_start_point_y = startPoint[1];
- let calc_end_point_x = endPoint[0];
- let calc_end_point_y = endPoint[1];
- if (max_dist === start_mid_dist) {
- calc_end_point_x = endPoint[0];
- calc_end_point_y = endPoint[1];
- } else if (max_dist === mid_end_dist) {
- calc_start_point_x = midPoint[0];
- calc_start_point_y = midPoint[1];
- }
- const calcStartPoint = [calc_start_point_x, calc_start_point_y];
- const calcEndPoint = [calc_end_point_x, calc_end_point_y];
- const totalAngle = getSlopes(calcStartPoint, calcEndPoint);
- const votes = angleOrientationAt(totalAngle, options.TOTAL_ANGLE_VOTE_POWER);
- voteVertical += votes[0];
- voteDiagonal += votes[1];
- voteHorizontal += votes[2];
- for (const fingerSlope of fingerSlopes) {
- const fingerVotes = angleOrientationAt(fingerSlope, options.SINGLE_ANGLE_VOTE_POWER);
- voteVertical += fingerVotes[0];
- voteDiagonal += fingerVotes[1];
- voteHorizontal += fingerVotes[2];
- }
- let estimatedDirection;
- if (voteVertical === Math.max(voteVertical, voteDiagonal, voteHorizontal)) {
- estimatedDirection = estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y);
- } else if (voteHorizontal === Math.max(voteDiagonal, voteHorizontal)) {
- estimatedDirection = estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);
- } else {
- estimatedDirection = estimateDiagonalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y, start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);
- }
- return estimatedDirection;
-}
-function estimate(landmarks) {
- const slopesXY = [];
- const slopesYZ = [];
- const fingerCurls = [];
- const fingerDirections = [];
- if (!landmarks)
- return { curls: fingerCurls, directions: fingerDirections };
- for (const finger of Finger.all) {
- const points = Finger.getPoints(finger);
- const slopeAtXY = [];
- const slopeAtYZ = [];
- for (const point2 of points) {
- const point1 = landmarks[point2[0]];
- const point22 = landmarks[point2[1]];
- const slopes = getSlopes(point1, point22);
- const slopeXY = slopes[0];
- const slopeYZ = slopes[1];
- slopeAtXY.push(slopeXY);
- slopeAtYZ.push(slopeYZ);
- }
- slopesXY.push(slopeAtXY);
- slopesYZ.push(slopeAtYZ);
- }
- for (const finger of Finger.all) {
- const pointIndexAt = finger === Finger.thumb ? 1 : 0;
- const fingerPointsAt = Finger.getPoints(finger);
- const startPoint = landmarks[fingerPointsAt[pointIndexAt][0]];
- const midPoint = landmarks[fingerPointsAt[pointIndexAt + 1][1]];
- const endPoint = landmarks[fingerPointsAt[3][1]];
- const fingerCurled = estimateFingerCurl(startPoint, midPoint, endPoint);
- const fingerPosition = calculateFingerDirection(startPoint, midPoint, endPoint, slopesXY[finger].slice(pointIndexAt));
- fingerCurls[finger] = fingerCurled;
- fingerDirections[finger] = fingerPosition;
- }
- return { curls: fingerCurls, directions: fingerDirections };
-}
-function analyze(keypoints) {
- if (!keypoints || keypoints.length === 0)
- return null;
- const estimatorRes = estimate(keypoints);
- const landmarks = {};
- for (const fingerIdx of Finger.all) {
- landmarks[Finger.getName(fingerIdx)] = {
- curl: FingerCurl.getName(estimatorRes.curls[fingerIdx]),
- direction: FingerDirection.getName(estimatorRes.directions[fingerIdx])
- };
- }
- return landmarks;
-}
-function match(keypoints) {
- const poses = [];
- if (!keypoints || keypoints.length === 0)
- return poses;
- const estimatorRes = estimate(keypoints);
- for (const gesture2 of fingergesture_default) {
- const confidence = gesture2.matchAgainst(estimatorRes.curls, estimatorRes.directions);
- if (confidence >= minConfidence)
- poses.push({ name: gesture2.name, confidence });
- }
- return poses;
-}
-
-// src/hand/handpose.ts
-var meshAnnotations2 = {
- thumb: [1, 2, 3, 4],
- index: [5, 6, 7, 8],
- middle: [9, 10, 11, 12],
- ring: [13, 14, 15, 16],
- pinky: [17, 18, 19, 20],
- palm: [0]
-};
-var handDetectorModel;
-var handPoseModel;
-var handPipeline;
-async function predict13(input, config3) {
- const predictions = await handPipeline.estimateHands(input, config3);
- if (!predictions)
- return [];
- const hands = [];
- for (let i = 0; i < predictions.length; i++) {
- const annotations2 = {};
- if (predictions[i].landmarks) {
- for (const key of Object.keys(meshAnnotations2)) {
- annotations2[key] = meshAnnotations2[key].map((index2) => predictions[i].landmarks[index2]);
- }
- }
- const keypoints = predictions[i].landmarks;
- let box = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 0, 0];
- let boxRaw = [0, 0, 0, 0];
- if (keypoints && keypoints.length > 0) {
- for (const pt of keypoints) {
- if (pt[0] < box[0])
- box[0] = pt[0];
- if (pt[1] < box[1])
- box[1] = pt[1];
- if (pt[0] > box[2])
- box[2] = pt[0];
- if (pt[1] > box[3])
- box[3] = pt[1];
- }
- box[2] -= box[0];
- box[3] -= box[1];
- boxRaw = [box[0] / (input.shape[2] || 0), box[1] / (input.shape[1] || 0), box[2] / (input.shape[2] || 0), box[3] / (input.shape[1] || 0)];
- } else {
- box = predictions[i].box ? [
- Math.trunc(Math.max(0, predictions[i].box.topLeft[0])),
- Math.trunc(Math.max(0, predictions[i].box.topLeft[1])),
- Math.trunc(Math.min(input.shape[2] || 0, predictions[i].box.bottomRight[0]) - Math.max(0, predictions[i].box.topLeft[0])),
- Math.trunc(Math.min(input.shape[1] || 0, predictions[i].box.bottomRight[1]) - Math.max(0, predictions[i].box.topLeft[1]))
- ] : [0, 0, 0, 0];
- boxRaw = [
- predictions[i].box.topLeft[0] / (input.shape[2] || 0),
- predictions[i].box.topLeft[1] / (input.shape[1] || 0),
- (predictions[i].box.bottomRight[0] - predictions[i].box.topLeft[0]) / (input.shape[2] || 0),
- (predictions[i].box.bottomRight[1] - predictions[i].box.topLeft[1]) / (input.shape[1] || 0)
- ];
- }
- const landmarks = analyze(keypoints);
- hands.push({
- id: i,
- score: Math.round(100 * predictions[i].confidence) / 100,
- boxScore: Math.round(100 * predictions[i].boxConfidence) / 100,
- fingerScore: Math.round(100 * predictions[i].fingerConfidence) / 100,
- label: "hand",
- box,
- boxRaw,
- keypoints,
- annotations: annotations2,
- landmarks
- });
- }
- return hands;
-}
-async function load14(config3) {
- var _a, _b;
- if (env.initial) {
- handDetectorModel = null;
- handPoseModel = null;
- }
- if (!handDetectorModel || !handPoseModel) {
- [handDetectorModel, handPoseModel] = await Promise.all([
- config3.hand.enabled ? loadModel((_a = config3.hand.detector) == null ? void 0 : _a.modelPath) : null,
- config3.hand.landmarks ? loadModel((_b = config3.hand.skeleton) == null ? void 0 : _b.modelPath) : null
- ]);
- } else {
- if (config3.debug)
- log("cached model:", handDetectorModel["modelUrl"]);
- if (config3.debug)
- log("cached model:", handPoseModel["modelUrl"]);
- }
- const handDetector = handDetectorModel ? new HandDetector(handDetectorModel) : void 0;
- if (handDetector && handPoseModel)
- handPipeline = new HandPipeline(handDetector, handPoseModel);
- return [handDetectorModel, handPoseModel];
-}
-
-// src/tfjs/humangl.ts
-var config2 = {
- name: "humangl",
- priority: 999,
- canvas: null,
- gl: null,
- extensions: [],
- webGLattr: {
- alpha: false,
- antialias: false,
- premultipliedAlpha: false,
- preserveDrawingBuffer: false,
- depth: false,
- stencil: false,
- failIfMajorPerformanceCaveat: false,
- desynchronized: true
- }
-};
-function extensions() {
- const gl = config2.gl;
- if (!gl)
- return;
- config2.extensions = gl.getSupportedExtensions();
-}
-function register(instance2) {
- var _a;
- if (instance2.config.backend !== "humangl")
- return;
- if (config2.name in tfjs_esm_exports.engine().registry && !((_a = config2 == null ? void 0 : config2.gl) == null ? void 0 : _a.getParameter(config2.gl.VERSION))) {
- log("error: humangl backend invalid context");
- reset(instance2);
- }
- if (!tfjs_esm_exports.findBackend(config2.name)) {
- try {
- config2.canvas = canvas(100, 100);
- } catch (err) {
- log("error: cannot create canvas:", err);
- return;
- }
- try {
- config2.gl = config2.canvas.getContext("webgl2", config2.webGLattr);
- if (!config2.gl) {
- log("error: cannot get WebGL context");
- return;
- }
- const glv2 = config2.gl.getParameter(config2.gl.VERSION).includes("2.0");
- if (!glv2) {
- log("override: using fallback webgl backend as webgl 2.0 is not detected");
- instance2.config.backend = "webgl";
- return;
- }
- if (config2.canvas) {
- config2.canvas.addEventListener("webglcontextlost", (e) => {
- log("error: humangl:", e.type);
- log("possible browser memory leak using webgl or conflict with multiple backend registrations");
- instance2.emit("error");
- throw new Error("backend error: webgl context lost");
- });
- config2.canvas.addEventListener("webglcontextrestored", (e) => {
- log("error: humangl context restored:", e);
- });
- config2.canvas.addEventListener("webglcontextcreationerror", (e) => {
- log("error: humangl context create:", e);
- });
- }
- } catch (err) {
- log("error: cannot get WebGL context:", err);
- return;
- }
- try {
- tfjs_esm_exports.setWebGLContext(2, config2.gl);
- } catch (err) {
- log("error: cannot set WebGL context:", err);
- return;
- }
- try {
- const ctx = new tfjs_esm_exports.GPGPUContext(config2.gl);
- tfjs_esm_exports.registerBackend(config2.name, () => new tfjs_esm_exports.MathBackendWebGL(ctx), config2.priority);
- } catch (err) {
- log("error: cannot register WebGL backend:", err);
- return;
- }
- try {
- const kernels = tfjs_esm_exports.getKernelsForBackend("webgl");
- kernels.forEach((kernelConfig) => {
- const newKernelConfig = { ...kernelConfig, backendName: config2.name };
- tfjs_esm_exports.registerKernel(newKernelConfig);
- });
- } catch (err) {
- log("error: cannot update WebGL backend registration:", err);
- return;
- }
- const current = tfjs_esm_exports.backend().getGPGPUContext ? tfjs_esm_exports.backend().getGPGPUContext().gl : null;
- if (current) {
- log(`humangl webgl version:${current.getParameter(current.VERSION)} renderer:${current.getParameter(current.RENDERER)}`);
- } else {
- log("error: no current gl context:", current, config2.gl);
- return;
- }
- try {
- if (tfjs_esm_exports.env().flagRegistry.WEBGL_VERSION)
- tfjs_esm_exports.env().set("WEBGL_VERSION", 2);
- } catch (err) {
- log("error: cannot set WebGL backend flags:", err);
- return;
- }
- extensions();
- log("backend registered:", config2.name);
- }
-}
-
-// src/tfjs/backend.ts
-function registerCustomOps(config3) {
- if (!env.kernels.includes("mod")) {
- const kernelMod = {
- kernelName: "Mod",
- backendName: tfjs_esm_exports.getBackend(),
- kernelFunc: (op) => tfjs_esm_exports.tidy(() => tfjs_esm_exports.sub(op.inputs.a, tfjs_esm_exports.mul(tfjs_esm_exports.div(op.inputs.a, op.inputs.b), op.inputs.b)))
- };
- if (config3.debug)
- log("registered kernel:", "Mod");
- tfjs_esm_exports.registerKernel(kernelMod);
- env.kernels.push("mod");
- }
- if (!env.kernels.includes("floormod")) {
- const kernelFloorMod = {
- kernelName: "FloorMod",
- backendName: tfjs_esm_exports.getBackend(),
- kernelFunc: (op) => tfjs_esm_exports.tidy(() => tfjs_esm_exports.add(tfjs_esm_exports.mul(tfjs_esm_exports.floorDiv(op.inputs.a / op.inputs.b), op.inputs.b), tfjs_esm_exports.mod(op.inputs.a, op.inputs.b)))
- };
- if (config3.debug)
- log("registered kernel:", "FloorMod");
- tfjs_esm_exports.registerKernel(kernelFloorMod);
- env.kernels.push("floormod");
- }
- if (!env.kernels.includes("rotatewithoffset") && config3.softwareKernels) {
- const kernelRotateWithOffset = {
- kernelName: "RotateWithOffset",
- backendName: tfjs_esm_exports.getBackend(),
- kernelFunc: (op) => tfjs_esm_exports.tidy(() => {
- const backend5 = tfjs_esm_exports.getBackend();
- tfjs_esm_exports.setBackend("cpu");
- const t = tfjs_esm_exports.image.rotateWithOffset(op.inputs.image, op.attrs.radians, op.attrs.fillValue, op.attrs.center);
- tfjs_esm_exports.setBackend(backend5);
- return t;
- })
- };
- if (config3.debug)
- log("registered kernel:", "RotateWithOffset");
- tfjs_esm_exports.registerKernel(kernelRotateWithOffset);
- env.kernels.push("rotatewithoffset");
- }
-}
-async function check(instance2, force = false) {
- instance2.state = "backend";
- if (force || env.initial || instance2.config.backend && instance2.config.backend.length > 0 && tfjs_esm_exports.getBackend() !== instance2.config.backend) {
- const timeStamp = now();
- if (instance2.config.backend && instance2.config.backend.length > 0) {
- if (typeof window === "undefined" && typeof WorkerGlobalScope !== "undefined" && instance2.config.debug) {
- if (instance2.config.debug)
- log("running inside web worker");
- }
- if (env.browser && instance2.config.backend === "tensorflow") {
- if (instance2.config.debug)
- log("override: backend set to tensorflow while running in browser");
- instance2.config.backend = "humangl";
- }
- if (env.node && (instance2.config.backend === "webgl" || instance2.config.backend === "humangl")) {
- if (instance2.config.debug)
- log(`override: backend set to ${instance2.config.backend} while running in nodejs`);
- instance2.config.backend = "tensorflow";
- }
- if (env.browser && instance2.config.backend === "webgpu") {
- if (typeof navigator === "undefined" || typeof navigator.gpu === "undefined") {
- log("override: backend set to webgpu but browser does not support webgpu");
- instance2.config.backend = "humangl";
- } else {
- const adapter = await navigator.gpu.requestAdapter();
- if (instance2.config.debug)
- log("enumerated webgpu adapter:", adapter);
- if (!adapter) {
- log("override: backend set to webgpu but browser reports no available gpu");
- instance2.config.backend = "humangl";
- } else {
- const adapterInfo = "requestAdapterInfo" in adapter ? await adapter.requestAdapterInfo() : void 0;
- log("webgpu adapter info:", adapterInfo);
- }
- }
- }
- if (instance2.config.backend === "humangl")
- register(instance2);
- const available = Object.keys(tfjs_esm_exports.engine().registryFactory);
- if (instance2.config.debug)
- log("available backends:", available);
- if (!available.includes(instance2.config.backend)) {
- log(`error: backend ${instance2.config.backend} not found in registry`);
- instance2.config.backend = env.node ? "tensorflow" : "webgl";
- if (instance2.config.debug)
- log(`override: setting backend ${instance2.config.backend}`);
- }
- if (instance2.config.debug)
- log("setting backend:", instance2.config.backend);
- if (instance2.config.backend === "wasm") {
- if (tfjs_esm_exports.env().flagRegistry.CANVAS2D_WILL_READ_FREQUENTLY)
- tfjs_esm_exports.env().set("CANVAS2D_WILL_READ_FREQUENTLY", true);
- if (instance2.config.debug)
- log("wasm path:", instance2.config.wasmPath);
- if (typeof tfjs_esm_exports.setWasmPaths !== "undefined")
- tfjs_esm_exports.setWasmPaths(instance2.config.wasmPath, instance2.config.wasmPlatformFetch);
- else
- throw new Error("backend error: attempting to use wasm backend but wasm path is not set");
- let mt = false;
- let simd = false;
- try {
- mt = await tfjs_esm_exports.env().getAsync("WASM_HAS_MULTITHREAD_SUPPORT");
- simd = await tfjs_esm_exports.env().getAsync("WASM_HAS_SIMD_SUPPORT");
- if (instance2.config.debug)
- log(`wasm execution: ${simd ? "simd" : "no simd"} ${mt ? "multithreaded" : "singlethreaded"}`);
- if (instance2.config.debug && !simd)
- log("warning: wasm simd support is not enabled");
- } catch (e) {
- log("wasm detection failed");
- }
- }
- try {
- await tfjs_esm_exports.setBackend(instance2.config.backend);
- await tfjs_esm_exports.ready();
- init();
- } catch (err) {
- log("error: cannot set backend:", instance2.config.backend, err);
- return false;
- }
- }
- if (tfjs_esm_exports.getBackend() === "humangl") {
- if (tfjs_esm_exports.env().flagRegistry.CHECK_COMPUTATION_FOR_ERRORS)
- tfjs_esm_exports.env().set("CHECK_COMPUTATION_FOR_ERRORS", false);
- if (tfjs_esm_exports.env().flagRegistry.WEBGL_CPU_FORWARD)
- tfjs_esm_exports.env().set("WEBGL_CPU_FORWARD", true);
- if (tfjs_esm_exports.env().flagRegistry.WEBGL_USE_SHAPES_UNIFORMS)
- tfjs_esm_exports.env().set("WEBGL_USE_SHAPES_UNIFORMS", true);
- if (tfjs_esm_exports.env().flagRegistry.CPU_HANDOFF_SIZE_THRESHOLD)
- tfjs_esm_exports.env().set("CPU_HANDOFF_SIZE_THRESHOLD", 256);
- if (tfjs_esm_exports.env().flagRegistry.WEBGL_EXP_CONV)
- tfjs_esm_exports.env().set("WEBGL_EXP_CONV", true);
- if (tfjs_esm_exports.env().flagRegistry.USE_SETTIMEOUTCUSTOM)
- tfjs_esm_exports.env().set("USE_SETTIMEOUTCUSTOM", true);
- if (typeof instance2.config.deallocate !== "undefined" && instance2.config.deallocate) {
- log("changing webgl: WEBGL_DELETE_TEXTURE_THRESHOLD:", true);
- tfjs_esm_exports.env().set("WEBGL_DELETE_TEXTURE_THRESHOLD", 0);
- }
- if (tfjs_esm_exports.backend().getGPGPUContext) {
- const gl = await tfjs_esm_exports.backend().getGPGPUContext().gl;
- if (instance2.config.debug)
- log(`gl version:${gl.getParameter(gl.VERSION)} renderer:${gl.getParameter(gl.RENDERER)}`);
- }
- }
- if (tfjs_esm_exports.getBackend() === "webgpu") {
- }
- tfjs_esm_exports.enableProdMode();
- await tfjs_esm_exports.ready();
- instance2.performance.initBackend = Math.trunc(now() - timeStamp);
- instance2.config.backend = tfjs_esm_exports.getBackend();
- await env.updateBackend();
- registerCustomOps(instance2.config);
- }
- return true;
-}
-function fakeOps(kernelNames, config3) {
- for (const kernelName of kernelNames) {
- const kernelConfig = {
- kernelName,
- backendName: config3.backend,
- kernelFunc: () => {
- if (config3.debug)
- log("kernelFunc", kernelName, config3.backend);
- }
- };
- tfjs_esm_exports.registerKernel(kernelConfig);
- }
- env.kernels = tfjs_esm_exports.getKernelsForBackend(tfjs_esm_exports.getBackend()).map((kernel) => kernel.kernelName.toLowerCase());
-}
-
-// src/hand/handtrack.ts
-var models3 = [null, null];
-var modelOutputNodes = ["StatefulPartitionedCall/Postprocessor/Slice", "StatefulPartitionedCall/Postprocessor/ExpandDims_1"];
-var inputSize7 = [[0, 0], [0, 0]];
-var classes = ["hand", "fist", "pinch", "point", "face", "tip", "pinchtip"];
-var faceIndex = 4;
-var boxExpandFact = 1.6;
-var maxDetectorResolution = 512;
-var detectorExpandFact = 1.4;
-var skipped12 = Number.MAX_SAFE_INTEGER;
-var lastTime13 = 0;
-var outputSize = [0, 0];
-var cache4 = {
- boxes: [],
- hands: []
-};
-var fingerMap = {
- thumb: [1, 2, 3, 4],
- index: [5, 6, 7, 8],
- middle: [9, 10, 11, 12],
- ring: [13, 14, 15, 16],
- pinky: [17, 18, 19, 20],
- base: [0],
- palm: [0, 17, 13, 9, 5, 1, 0]
-};
-async function loadDetect2(config3) {
- var _a;
- if (env.initial)
- models3[0] = null;
- if (!models3[0]) {
- fakeOps(["tensorlistreserve", "enter", "tensorlistfromtensor", "merge", "loopcond", "switch", "exit", "tensorliststack", "nextiteration", "tensorlistsetitem", "tensorlistgetitem", "reciprocal", "shape", "split", "where"], config3);
- models3[0] = await loadModel((_a = config3.hand.detector) == null ? void 0 : _a.modelPath);
- const inputs = models3[0]["executor"] ? Object.values(models3[0].modelSignature["inputs"]) : void 0;
- inputSize7[0][0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;
- inputSize7[0][1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;
- } else if (config3.debug)
- log("cached model:", models3[0]["modelUrl"]);
- return models3[0];
-}
-async function loadSkeleton(config3) {
- var _a;
- if (env.initial)
- models3[1] = null;
- if (!models3[1]) {
- models3[1] = await loadModel((_a = config3.hand.skeleton) == null ? void 0 : _a.modelPath);
- const inputs = models3[1]["executor"] ? Object.values(models3[1].modelSignature["inputs"]) : void 0;
- inputSize7[1][0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;
- inputSize7[1][1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;
- } else if (config3.debug)
- log("cached model:", models3[1]["modelUrl"]);
- return models3[1];
-}
-async function detectHands(input, config3) {
- const hands = [];
- if (!input || !models3[0])
- return hands;
- const t = {};
- const ratio = (input.shape[2] || 1) / (input.shape[1] || 1);
- const height = Math.min(Math.round((input.shape[1] || 0) / 8) * 8, maxDetectorResolution);
- const width = Math.round(height * ratio / 8) * 8;
- t.resize = tfjs_esm_exports.image.resizeBilinear(input, [height, width]);
- t.cast = tfjs_esm_exports.cast(t.resize, "int32");
- [t.rawScores, t.rawBoxes] = await models3[0].executeAsync(t.cast, modelOutputNodes);
- t.boxes = tfjs_esm_exports.squeeze(t.rawBoxes, [0, 2]);
- t.scores = tfjs_esm_exports.squeeze(t.rawScores, [0]);
- const classScores = tfjs_esm_exports.unstack(t.scores, 1);
- tfjs_esm_exports.dispose(classScores[faceIndex]);
- classScores.splice(faceIndex, 1);
- t.filtered = tfjs_esm_exports.stack(classScores, 1);
- tfjs_esm_exports.dispose(classScores);
- t.max = tfjs_esm_exports.max(t.filtered, 1);
- t.argmax = tfjs_esm_exports.argMax(t.filtered, 1);
- let id = 0;
- t.nms = await tfjs_esm_exports.image.nonMaxSuppressionAsync(t.boxes, t.max, (config3.hand.maxDetected || 0) + 1, config3.hand.iouThreshold || 0, config3.hand.minConfidence || 1);
- const nms = await t.nms.data();
- const scores = await t.max.data();
- const classNum = await t.argmax.data();
- for (const nmsIndex of Array.from(nms)) {
- const boxSlice = tfjs_esm_exports.slice(t.boxes, nmsIndex, 1);
- const boxYX = await boxSlice.data();
- tfjs_esm_exports.dispose(boxSlice);
- const boxData = [boxYX[1], boxYX[0], boxYX[3] - boxYX[1], boxYX[2] - boxYX[0]];
- const boxRaw = scale(boxData, detectorExpandFact);
- const boxFull = [Math.trunc(boxData[0] * outputSize[0]), Math.trunc(boxData[1] * outputSize[1]), Math.trunc(boxData[2] * outputSize[0]), Math.trunc(boxData[3] * outputSize[1])];
- const score = scores[nmsIndex];
- const label = classes[classNum[nmsIndex]];
- const hand3 = { id: id++, score, box: boxFull, boxRaw, label };
- hands.push(hand3);
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- hands.sort((a, b) => b.score - a.score);
- if (hands.length > (config3.hand.maxDetected || 1))
- hands.length = config3.hand.maxDetected || 1;
- return hands;
-}
-async function detectFingers(input, h, config3) {
- const hand3 = {
- id: h.id,
- score: Math.round(100 * h.score) / 100,
- boxScore: Math.round(100 * h.score) / 100,
- fingerScore: 0,
- box: h.box,
- boxRaw: h.boxRaw,
- label: h.label,
- keypoints: [],
- landmarks: {},
- annotations: {}
- };
- if (input && models3[1] && config3.hand.landmarks && h.score > (config3.hand.minConfidence || 0)) {
- const t = {};
- const boxCrop = [h.boxRaw[1], h.boxRaw[0], h.boxRaw[3] + h.boxRaw[1], h.boxRaw[2] + h.boxRaw[0]];
- t.crop = tfjs_esm_exports.image.cropAndResize(input, [boxCrop], [0], [inputSize7[1][0], inputSize7[1][1]], "bilinear");
- t.div = tfjs_esm_exports.div(t.crop, constants.tf255);
- [t.score, t.keypoints] = models3[1].execute(t.div, ["Identity_1", "Identity"]);
- const rawScore = (await t.score.data())[0];
- const score = (100 - Math.trunc(100 / (1 + Math.exp(rawScore)))) / 100;
- if (score >= (config3.hand.minConfidence || 0)) {
- hand3.fingerScore = score;
- t.reshaped = tfjs_esm_exports.reshape(t.keypoints, [-1, 3]);
- const coordsData = await t.reshaped.array();
- const coordsRaw = coordsData.map((kpt4) => [kpt4[0] / inputSize7[1][1], kpt4[1] / inputSize7[1][0], kpt4[2] || 0]);
- const coordsNorm = coordsRaw.map((kpt4) => [kpt4[0] * h.boxRaw[2], kpt4[1] * h.boxRaw[3], kpt4[2] || 0]);
- hand3.keypoints = coordsNorm.map((kpt4) => [outputSize[0] * (kpt4[0] + h.boxRaw[0]), outputSize[1] * (kpt4[1] + h.boxRaw[1]), kpt4[2] || 0]);
- hand3.landmarks = analyze(hand3.keypoints);
- for (const key of Object.keys(fingerMap)) {
- hand3.annotations[key] = fingerMap[key].map((index2) => hand3.landmarks && hand3.keypoints[index2] ? hand3.keypoints[index2] : null);
- }
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- }
- return hand3;
-}
-async function predict14(input, config3) {
- var _a, _b;
- if (!((_a = models3[0]) == null ? void 0 : _a["executor"]) || !((_b = models3[1]) == null ? void 0 : _b["executor"]) || !models3[0].inputs[0].shape || !models3[1].inputs[0].shape)
- return [];
- outputSize = [input.shape[2] || 0, input.shape[1] || 0];
- skipped12++;
- const skipTime = (config3.hand.skipTime || 0) > now() - lastTime13;
- const skipFrame = skipped12 < (config3.hand.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame) {
- return cache4.hands;
- }
- return new Promise(async (resolve) => {
- const skipTimeExtended = 3 * (config3.hand.skipTime || 0) > now() - lastTime13;
- const skipFrameExtended = skipped12 < 3 * (config3.hand.skipFrames || 0);
- if (config3.skipAllowed && cache4.hands.length === config3.hand.maxDetected) {
- cache4.hands = await Promise.all(cache4.boxes.map((handBox) => detectFingers(input, handBox, config3)));
- } else if (config3.skipAllowed && skipTimeExtended && skipFrameExtended && cache4.hands.length > 0) {
- cache4.hands = await Promise.all(cache4.boxes.map((handBox) => detectFingers(input, handBox, config3)));
- } else {
- cache4.boxes = await detectHands(input, config3);
- lastTime13 = now();
- cache4.hands = await Promise.all(cache4.boxes.map((handBox) => detectFingers(input, handBox, config3)));
- skipped12 = 0;
- }
- const oldCache = [...cache4.boxes];
- cache4.boxes.length = 0;
- if (config3.cacheSensitivity > 0) {
- for (let i = 0; i < cache4.hands.length; i++) {
- const boxKpt = square(cache4.hands[i].keypoints, outputSize);
- if (boxKpt.box[2] / (input.shape[2] || 1) > 0.05 && boxKpt.box[3] / (input.shape[1] || 1) > 0.05 && cache4.hands[i].fingerScore && cache4.hands[i].fingerScore > (config3.hand.minConfidence || 0)) {
- const boxScale = scale(boxKpt.box, boxExpandFact);
- const boxScaleRaw = scale(boxKpt.boxRaw, boxExpandFact);
- cache4.boxes.push({ ...oldCache[i], box: boxScale, boxRaw: boxScaleRaw });
- }
- }
- }
- for (let i = 0; i < cache4.hands.length; i++) {
- const bbox = calc(cache4.hands[i].keypoints, outputSize);
- cache4.hands[i].box = bbox.box;
- cache4.hands[i].boxRaw = bbox.boxRaw;
- }
- resolve(cache4.hands);
- });
-}
-
-// src/face/liveness.ts
-var model14;
-var cached2 = [];
-var skipped13 = Number.MAX_SAFE_INTEGER;
-var lastCount9 = 0;
-var lastTime14 = 0;
-async function load15(config3) {
- var _a;
- if (env.initial)
- model14 = null;
- if (!model14)
- model14 = await loadModel((_a = config3.face.liveness) == null ? void 0 : _a.modelPath);
- else if (config3.debug)
- log("cached model:", model14["modelUrl"]);
- return model14;
-}
-async function predict15(image26, config3, idx, count2) {
- var _a, _b;
- if (!(model14 == null ? void 0 : model14["executor"]))
- return 0;
- const skipTime = (((_a = config3.face.liveness) == null ? void 0 : _a.skipTime) || 0) > now() - lastTime14;
- const skipFrame = skipped13 < (((_b = config3.face.liveness) == null ? void 0 : _b.skipFrames) || 0);
- if (config3.skipAllowed && skipTime && skipFrame && lastCount9 === count2 && cached2[idx]) {
- skipped13++;
- return cached2[idx];
- }
- skipped13 = 0;
- return new Promise(async (resolve) => {
- const resize = tfjs_esm_exports.image.resizeBilinear(image26, [(model14 == null ? void 0 : model14.inputs[0].shape) ? model14.inputs[0].shape[2] : 0, (model14 == null ? void 0 : model14.inputs[0].shape) ? model14.inputs[0].shape[1] : 0], false);
- const res = model14 == null ? void 0 : model14.execute(resize);
- const num = (await res.data())[0];
- cached2[idx] = Math.round(100 * num) / 100;
- lastCount9 = count2;
- lastTime14 = now();
- tfjs_esm_exports.dispose([resize, res]);
- resolve(cached2[idx]);
- });
-}
-
-// src/body/movenetcoords.ts
-var movenetcoords_exports = {};
-__export(movenetcoords_exports, {
- connected: () => connected3,
- horizontal: () => horizontal,
- kpt: () => kpt3,
- relative: () => relative,
- vertical: () => vertical
-});
-var kpt3 = [
- "nose",
- "leftEye",
- "rightEye",
- "leftEar",
- "rightEar",
- "leftShoulder",
- "rightShoulder",
- "leftElbow",
- "rightElbow",
- "leftWrist",
- "rightWrist",
- "leftHip",
- "rightHip",
- "leftKnee",
- "rightKnee",
- "leftAnkle",
- "rightAnkle"
-];
-var horizontal = [
- ["leftEye", "rightEye"],
- ["leftEar", "rightEar"],
- ["leftShoulder", "rightShoulder"],
- ["leftElbow", "rightElbow"],
- ["leftWrist", "rightWrist"],
- ["leftHip", "rightHip"],
- ["leftKnee", "rightKnee"],
- ["leftAnkle", "rightAnkle"]
-];
-var vertical = [
- ["leftKnee", "leftShoulder"],
- ["rightKnee", "rightShoulder"],
- ["leftAnkle", "leftKnee"],
- ["rightAnkle", "rightKnee"]
-];
-var relative = [
- [["leftHip", "rightHip"], ["leftShoulder", "rightShoulder"]],
- [["leftElbow", "rightElbow"], ["leftShoulder", "rightShoulder"]]
-];
-var connected3 = {
- leftLeg: ["leftHip", "leftKnee", "leftAnkle"],
- rightLeg: ["rightHip", "rightKnee", "rightAnkle"],
- torso: ["leftShoulder", "rightShoulder", "rightHip", "leftHip", "leftShoulder"],
- leftArm: ["leftShoulder", "leftElbow", "leftWrist"],
- rightArm: ["rightShoulder", "rightElbow", "rightWrist"],
- head: []
-};
-
-// src/body/movenetfix.ts
-var maxJitter = 5e-3;
-var cache5 = {
- keypoints: [],
- padding: [[0, 0], [0, 0], [0, 0], [0, 0]]
-};
-function bodyParts(body4) {
- for (const pair of horizontal) {
- const left = body4.keypoints.findIndex((kp) => kp.part === pair[0]);
- const right = body4.keypoints.findIndex((kp) => kp.part === pair[1]);
- if (body4.keypoints[left] && body4.keypoints[right]) {
- if (body4.keypoints[left].position[0] < body4.keypoints[right].position[0]) {
- const tmp = body4.keypoints[left];
- body4.keypoints[left] = body4.keypoints[right];
- body4.keypoints[right] = tmp;
- }
- }
- }
- for (const pair of vertical) {
- const lower = body4.keypoints.findIndex((kp) => kp && kp.part === pair[0]);
- const higher = body4.keypoints.findIndex((kp) => kp && kp.part === pair[1]);
- if (body4.keypoints[lower] && body4.keypoints[higher]) {
- if (body4.keypoints[lower].position[1] < body4.keypoints[higher].position[1]) {
- body4.keypoints.splice(lower, 1);
- }
- }
- }
- for (const [pair, compare2] of relative) {
- const left = body4.keypoints.findIndex((kp) => kp && kp.part === pair[0]);
- const right = body4.keypoints.findIndex((kp) => kp && kp.part === pair[1]);
- const leftTo = body4.keypoints.findIndex((kp) => kp && kp.part === compare2[0]);
- const rightTo = body4.keypoints.findIndex((kp) => kp && kp.part === compare2[1]);
- if (!body4.keypoints[leftTo] || !body4.keypoints[rightTo])
- continue;
- const distanceLeft = body4.keypoints[left] ? [
- Math.abs(body4.keypoints[leftTo].position[0] - body4.keypoints[left].position[0]),
- Math.abs(body4.keypoints[rightTo].position[0] - body4.keypoints[left].position[0])
- ] : [0, 0];
- const distanceRight = body4.keypoints[right] ? [
- Math.abs(body4.keypoints[rightTo].position[0] - body4.keypoints[right].position[0]),
- Math.abs(body4.keypoints[leftTo].position[0] - body4.keypoints[right].position[0])
- ] : [0, 0];
- if (distanceLeft[0] > distanceLeft[1] || distanceRight[0] > distanceRight[1]) {
- const tmp = body4.keypoints[left];
- body4.keypoints[left] = body4.keypoints[right];
- body4.keypoints[right] = tmp;
- }
- }
-}
-function jitter(keypoints) {
- for (let i = 0; i < keypoints.length; i++) {
- if (keypoints[i] && cache5.keypoints[i]) {
- const diff = [Math.abs(keypoints[i].positionRaw[0] - cache5.keypoints[i].positionRaw[0]), Math.abs(keypoints[i].positionRaw[1] - cache5.keypoints[i].positionRaw[1])];
- if (diff[0] < maxJitter && diff[1] < maxJitter) {
- keypoints[i] = cache5.keypoints[i];
- } else {
- cache5.keypoints[i] = keypoints[i];
- }
- } else {
- cache5.keypoints[i] = keypoints[i];
- }
- }
- return keypoints;
-}
-function padInput(input, inputSize10) {
- var _a, _b;
- const t = {};
- if (!((_a = input == null ? void 0 : input.shape) == null ? void 0 : _a[1]) || !((_b = input == null ? void 0 : input.shape) == null ? void 0 : _b[2]))
- return input;
- cache5.padding = [
- [0, 0],
- [input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0, input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0],
- [input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0, input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0],
- [0, 0]
- ];
- t.pad = tfjs_esm_exports.pad(input, cache5.padding);
- t.resize = tfjs_esm_exports.image.resizeBilinear(t.pad, [inputSize10, inputSize10]);
- const final = tfjs_esm_exports.cast(t.resize, "int32");
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return final;
-}
-function rescaleBody(body4, outputSize2) {
- body4.keypoints = body4.keypoints.filter((kpt4) => kpt4 == null ? void 0 : kpt4.position);
- for (const kpt4 of body4.keypoints) {
- kpt4.position = [
- kpt4.position[0] * (outputSize2[0] + cache5.padding[2][0] + cache5.padding[2][1]) / outputSize2[0] - cache5.padding[2][0],
- kpt4.position[1] * (outputSize2[1] + cache5.padding[1][0] + cache5.padding[1][1]) / outputSize2[1] - cache5.padding[1][0]
- ];
- kpt4.positionRaw = [
- kpt4.position[0] / outputSize2[0],
- kpt4.position[1] / outputSize2[1]
- ];
- }
- const rescaledBoxes = calc(body4.keypoints.map((pt) => pt.position), outputSize2);
- body4.box = rescaledBoxes.box;
- body4.boxRaw = rescaledBoxes.boxRaw;
- return body4;
-}
-
-// src/body/movenet.ts
-var model15;
-var inputSize8 = 0;
-var skipped14 = Number.MAX_SAFE_INTEGER;
-var cache6 = {
- boxes: [],
- bodies: [],
- last: 0
-};
-async function load16(config3) {
- var _a;
- if (env.initial)
- model15 = null;
- if (!model15) {
- fakeOps(["size"], config3);
- model15 = await loadModel(config3.body.modelPath);
- } else if (config3.debug)
- log("cached model:", model15["modelUrl"]);
- inputSize8 = (model15 == null ? void 0 : model15["executor"]) && ((_a = model15 == null ? void 0 : model15.inputs) == null ? void 0 : _a[0].shape) ? model15.inputs[0].shape[2] : 0;
- if (inputSize8 < 64)
- inputSize8 = 256;
- return model15;
-}
-function parseSinglePose(res, config3, image26) {
- const kpt4 = res[0][0];
- const keypoints = [];
- let score = 0;
- for (let id = 0; id < kpt4.length; id++) {
- score = kpt4[id][2];
- if (score > config3.body.minConfidence) {
- const positionRaw = [kpt4[id][1], kpt4[id][0]];
- keypoints.push({
- score: Math.round(100 * score) / 100,
- part: kpt3[id],
- positionRaw,
- position: [
- Math.round((image26.shape[2] || 0) * positionRaw[0]),
- Math.round((image26.shape[1] || 0) * positionRaw[1])
- ]
- });
- }
- }
- score = keypoints.reduce((prev, curr) => curr.score > prev ? curr.score : prev, 0);
- const bodies = [];
- const newBox = calc(keypoints.map((pt) => pt.position), [image26.shape[2], image26.shape[1]]);
- const annotations2 = {};
- for (const [name, indexes] of Object.entries(connected3)) {
- const pt = [];
- for (let i = 0; i < indexes.length - 1; i++) {
- const pt0 = keypoints.find((kp) => kp.part === indexes[i]);
- const pt1 = keypoints.find((kp) => kp.part === indexes[i + 1]);
- if (pt0 && pt1 && pt0.score > (config3.body.minConfidence || 0) && pt1.score > (config3.body.minConfidence || 0))
- pt.push([pt0.position, pt1.position]);
- }
- annotations2[name] = pt;
- }
- const body4 = { id: 0, score, box: newBox.box, boxRaw: newBox.boxRaw, keypoints, annotations: annotations2 };
- bodyParts(body4);
- bodies.push(body4);
- return bodies;
-}
-function parseMultiPose(res, config3, image26) {
- const bodies = [];
- for (let id = 0; id < res[0].length; id++) {
- const kpt4 = res[0][id];
- const totalScore = Math.round(100 * kpt4[51 + 4]) / 100;
- if (totalScore > config3.body.minConfidence) {
- const keypoints = [];
- for (let i = 0; i < 17; i++) {
- const score = kpt4[3 * i + 2];
- if (score > config3.body.minConfidence) {
- const positionRaw = [kpt4[3 * i + 1], kpt4[3 * i + 0]];
- keypoints.push({
- part: kpt3[i],
- score: Math.round(100 * score) / 100,
- positionRaw,
- position: [Math.round((image26.shape[2] || 0) * positionRaw[0]), Math.round((image26.shape[1] || 0) * positionRaw[1])]
- });
- }
- }
- const newBox = calc(keypoints.map((pt) => pt.position), [image26.shape[2], image26.shape[1]]);
- const annotations2 = {};
- for (const [name, indexes] of Object.entries(connected3)) {
- const pt = [];
- for (let i = 0; i < indexes.length - 1; i++) {
- const pt0 = keypoints.find((kp) => kp.part === indexes[i]);
- const pt1 = keypoints.find((kp) => kp.part === indexes[i + 1]);
- if (pt0 && pt1 && pt0.score > (config3.body.minConfidence || 0) && pt1.score > (config3.body.minConfidence || 0))
- pt.push([pt0.position, pt1.position]);
- }
- annotations2[name] = pt;
- }
- const body4 = { id, score: totalScore, box: newBox.box, boxRaw: newBox.boxRaw, keypoints: [...keypoints], annotations: annotations2 };
- bodyParts(body4);
- bodies.push(body4);
- }
- }
- bodies.sort((a, b) => b.score - a.score);
- if (bodies.length > config3.body.maxDetected)
- bodies.length = config3.body.maxDetected;
- return bodies;
-}
-async function predict16(input, config3) {
- var _a;
- if (!(model15 == null ? void 0 : model15["executor"]) || !((_a = model15 == null ? void 0 : model15.inputs) == null ? void 0 : _a[0].shape))
- return [];
- if (!config3.skipAllowed)
- cache6.boxes.length = 0;
- skipped14++;
- const skipTime = (config3.body.skipTime || 0) > now() - cache6.last;
- const skipFrame = skipped14 < (config3.body.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame) {
- return cache6.bodies;
- }
- return new Promise(async (resolve) => {
- const t = {};
- skipped14 = 0;
- t.input = padInput(input, inputSize8);
- t.res = model15 == null ? void 0 : model15.execute(t.input);
- cache6.last = now();
- const res = await t.res.array();
- cache6.bodies = t.res.shape[2] === 17 ? parseSinglePose(res, config3, input) : parseMultiPose(res, config3, input);
- for (const body4 of cache6.bodies) {
- rescaleBody(body4, [input.shape[2] || 1, input.shape[1] || 1]);
- jitter(body4.keypoints);
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- resolve(cache6.bodies);
- });
-}
-
-// src/object/nanodet.ts
-var model16;
-var last10 = [];
-var lastTime15 = 0;
-var skipped15 = Number.MAX_SAFE_INTEGER;
-var inputSize9 = 0;
-var scaleBox = 2.5;
-async function load17(config3) {
- if (!model16 || env.initial) {
- model16 = await loadModel(config3.object.modelPath);
- const inputs = (model16 == null ? void 0 : model16["executor"]) ? Object.values(model16.modelSignature["inputs"]) : void 0;
- inputSize9 = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 416;
- } else if (config3.debug)
- log("cached model:", model16["modelUrl"]);
- return model16;
-}
-async function process4(res, outputShape, config3) {
- let id = 0;
- let results = [];
- const size2 = inputSize9;
- for (const strideSize of [1, 2, 4]) {
- const baseSize = strideSize * 13;
- const scoresT = tfjs_esm_exports.squeeze(res.find((a) => a.shape[1] === baseSize ** 2 && (a.shape[2] || 0) === labels.length));
- const scores = await scoresT.array();
- const featuresT = tfjs_esm_exports.squeeze(res.find((a) => a.shape[1] === baseSize ** 2 && (a.shape[2] || 0) < labels.length));
- const boxesMaxT = featuresT.reshape([-1, 4, featuresT.shape[1] / 4]);
- const boxIdxT = boxesMaxT.argMax(2);
- const boxIdx = await boxIdxT.array();
- for (let i = 0; i < scoresT.shape[0]; i++) {
- for (let j = 0; j < scoresT.shape[1]; j++) {
- const score = scores[i][j];
- if (score > (config3.object.minConfidence || 0) && j !== 61) {
- const cx = (0.5 + Math.trunc(i % baseSize)) / baseSize;
- const cy = (0.5 + Math.trunc(i / baseSize)) / baseSize;
- const boxOffset = boxIdx[i].map((a) => a * (baseSize / strideSize / size2));
- 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 = [x, y, w, h];
- boxRaw = boxRaw.map((a) => Math.max(0, Math.min(a, 1)));
- const box = [
- boxRaw[0] * outputShape[0],
- boxRaw[1] * outputShape[1],
- boxRaw[2] * outputShape[0],
- boxRaw[3] * outputShape[1]
- ];
- const result = {
- id: id++,
- score: Math.round(100 * score) / 100,
- class: j + 1,
- label: labels[j].label,
- box: box.map((a) => Math.trunc(a)),
- boxRaw
- };
- results.push(result);
- }
- }
- }
- tfjs_esm_exports.dispose([scoresT, featuresT, boxesMaxT, boxIdxT]);
- }
- const nmsBoxes = results.map((a) => [a.boxRaw[1], a.boxRaw[0], a.boxRaw[3], a.boxRaw[2]]);
- const nmsScores = results.map((a) => a.score);
- let nmsIdx = [];
- if (nmsBoxes && nmsBoxes.length > 0) {
- const nms = await tfjs_esm_exports.image.nonMaxSuppressionAsync(nmsBoxes, nmsScores, config3.object.maxDetected, config3.object.iouThreshold, config3.object.minConfidence);
- nmsIdx = await nms.data();
- tfjs_esm_exports.dispose(nms);
- }
- results = results.filter((_val, idx) => nmsIdx.includes(idx)).sort((a, b) => b.score - a.score);
- return results;
-}
-async function predict17(image26, config3) {
- if (!(model16 == null ? void 0 : model16["executor"]))
- return [];
- const skipTime = (config3.object.skipTime || 0) > now() - lastTime15;
- const skipFrame = skipped15 < (config3.object.skipFrames || 0);
- if (config3.skipAllowed && skipTime && skipFrame && last10.length > 0) {
- skipped15++;
- return last10;
- }
- skipped15 = 0;
- if (!env.kernels.includes("mod") || !env.kernels.includes("sparsetodense"))
- return last10;
- return new Promise(async (resolve) => {
- const outputSize2 = [image26.shape[2] || 0, image26.shape[1] || 0];
- const resizeT = tfjs_esm_exports.image.resizeBilinear(image26, [inputSize9, inputSize9], false);
- const normT = tfjs_esm_exports.div(resizeT, constants.tf255);
- const transposeT = tfjs_esm_exports.transpose(normT, [0, 3, 1, 2]);
- let objectT;
- if (config3.object.enabled)
- objectT = model16.execute(transposeT);
- lastTime15 = now();
- const obj = await process4(objectT, outputSize2, config3);
- last10 = obj;
- tfjs_esm_exports.dispose([resizeT, normT, transposeT, ...objectT]);
- resolve(obj);
- });
-}
-
-// src/body/posenetutils.ts
-var partNames = [
- "nose",
- "leftEye",
- "rightEye",
- "leftEar",
- "rightEar",
- "leftShoulder",
- "rightShoulder",
- "leftElbow",
- "rightElbow",
- "leftWrist",
- "rightWrist",
- "leftHip",
- "rightHip",
- "leftKnee",
- "rightKnee",
- "leftAnkle",
- "rightAnkle"
-];
-var count = partNames.length;
-var partIds = partNames.reduce((result, jointName, i) => {
- result[jointName] = i;
- return result;
-}, {});
-var connectedPartNames = [
- ["leftHip", "leftShoulder"],
- ["leftElbow", "leftShoulder"],
- ["leftElbow", "leftWrist"],
- ["leftHip", "leftKnee"],
- ["leftKnee", "leftAnkle"],
- ["rightHip", "rightShoulder"],
- ["rightElbow", "rightShoulder"],
- ["rightElbow", "rightWrist"],
- ["rightHip", "rightKnee"],
- ["rightKnee", "rightAnkle"],
- ["leftShoulder", "rightShoulder"],
- ["leftHip", "rightHip"]
-];
-var connectedPartIndices = connectedPartNames.map(([jointNameA, jointNameB]) => [partIds[jointNameA], partIds[jointNameB]]);
-var poseChain = [
- ["nose", "leftEye"],
- ["leftEye", "leftEar"],
- ["nose", "rightEye"],
- ["rightEye", "rightEar"],
- ["nose", "leftShoulder"],
- ["leftShoulder", "leftElbow"],
- ["leftElbow", "leftWrist"],
- ["leftShoulder", "leftHip"],
- ["leftHip", "leftKnee"],
- ["leftKnee", "leftAnkle"],
- ["nose", "rightShoulder"],
- ["rightShoulder", "rightElbow"],
- ["rightElbow", "rightWrist"],
- ["rightShoulder", "rightHip"],
- ["rightHip", "rightKnee"],
- ["rightKnee", "rightAnkle"]
-];
-function getBoundingBox(keypoints) {
- const coord = keypoints.reduce(({ maxX, maxY, minX, minY }, { position: { x, y } }) => ({
- maxX: Math.max(maxX, x),
- maxY: Math.max(maxY, y),
- minX: Math.min(minX, x),
- minY: Math.min(minY, y)
- }), {
- maxX: Number.NEGATIVE_INFINITY,
- maxY: Number.NEGATIVE_INFINITY,
- minX: Number.POSITIVE_INFINITY,
- minY: Number.POSITIVE_INFINITY
- });
- return [coord.minX, coord.minY, coord.maxX - coord.minX, coord.maxY - coord.minY];
-}
-function scalePoses(poses, [height, width], [inputResolutionHeight, inputResolutionWidth]) {
- const scaleY = height / inputResolutionHeight;
- const scaleX = width / inputResolutionWidth;
- const scalePose = (pose, i) => ({
- id: i,
- score: pose.score,
- boxRaw: [pose.box[0] / inputResolutionWidth, pose.box[1] / inputResolutionHeight, pose.box[2] / inputResolutionWidth, pose.box[3] / inputResolutionHeight],
- box: [Math.trunc(pose.box[0] * scaleX), Math.trunc(pose.box[1] * scaleY), Math.trunc(pose.box[2] * scaleX), Math.trunc(pose.box[3] * scaleY)],
- keypoints: pose.keypoints.map(({ score, part, position }) => ({
- score,
- part,
- position: [Math.trunc(position.x * scaleX), Math.trunc(position.y * scaleY)],
- positionRaw: [position.x / inputResolutionHeight, position.y / inputResolutionHeight]
- })),
- annotations: {}
- });
- const scaledPoses = poses.map((pose, i) => scalePose(pose, i));
- return scaledPoses;
-}
-var MaxHeap = class {
- constructor(maxSize2, getElementValue) {
- __publicField(this, "priorityQueue");
- __publicField(this, "numberOfElements");
- __publicField(this, "getElementValue");
- this.priorityQueue = new Array(maxSize2);
- this.numberOfElements = -1;
- this.getElementValue = getElementValue;
- }
- enqueue(x) {
- this.priorityQueue[++this.numberOfElements] = x;
- this.swim(this.numberOfElements);
- }
- dequeue() {
- const max4 = this.priorityQueue[0];
- this.exchange(0, this.numberOfElements--);
- this.sink(0);
- this.priorityQueue[this.numberOfElements + 1] = null;
- return max4;
- }
- empty() {
- return this.numberOfElements === -1;
- }
- size() {
- return this.numberOfElements + 1;
- }
- all() {
- return this.priorityQueue.slice(0, this.numberOfElements + 1);
- }
- max() {
- return this.priorityQueue[0];
- }
- swim(k) {
- while (k > 0 && this.less(Math.floor(k / 2), k)) {
- this.exchange(k, Math.floor(k / 2));
- k = Math.floor(k / 2);
- }
- }
- sink(k) {
- while (2 * k <= this.numberOfElements) {
- let j = 2 * k;
- if (j < this.numberOfElements && this.less(j, j + 1))
- j++;
- if (!this.less(k, j))
- break;
- this.exchange(k, j);
- k = j;
- }
- }
- getValueAt(i) {
- return this.getElementValue(this.priorityQueue[i]);
- }
- less(i, j) {
- return this.getValueAt(i) < this.getValueAt(j);
- }
- exchange(i, j) {
- const t = this.priorityQueue[i];
- this.priorityQueue[i] = this.priorityQueue[j];
- this.priorityQueue[j] = t;
- }
-};
-function getOffsetPoint(y, x, keypoint, offsets) {
- return {
- y: offsets.get(y, x, keypoint),
- x: offsets.get(y, x, keypoint + count)
- };
-}
-function getImageCoords(part, outputStride2, offsets) {
- const { heatmapY, heatmapX, id: keypoint } = part;
- const { y, x } = getOffsetPoint(heatmapY, heatmapX, keypoint, offsets);
- return {
- x: part.heatmapX * outputStride2 + x,
- y: part.heatmapY * outputStride2 + y
- };
-}
-function clamp(a, min2, max4) {
- if (a < min2)
- return min2;
- if (a > max4)
- return max4;
- return a;
-}
-function squaredDistance(y1, x1, y2, x2) {
- const dy = y2 - y1;
- const dx = x2 - x1;
- return dy * dy + dx * dx;
-}
-function addVectors(a, b) {
- return { x: a.x + b.x, y: a.y + b.y };
-}
-
-// src/body/posenet.ts
-var model17;
-var poseNetOutputs = ["MobilenetV1/offset_2/BiasAdd", "MobilenetV1/heatmap_2/BiasAdd", "MobilenetV1/displacement_fwd_2/BiasAdd", "MobilenetV1/displacement_bwd_2/BiasAdd"];
-var localMaximumRadius = 1;
-var outputStride = 16;
-var squaredNmsRadius = 50 ** 2;
-function traverse(edgeId, sourceKeypoint, targetId, scores, offsets, displacements, offsetRefineStep = 2) {
- const getDisplacement = (point2) => ({
- y: displacements.get(point2.y, point2.x, edgeId),
- x: displacements.get(point2.y, point2.x, displacements.shape[2] / 2 + edgeId)
- });
- const getStridedIndexNearPoint = (point2, height2, width2) => ({
- y: clamp(Math.round(point2.y / outputStride), 0, height2 - 1),
- x: clamp(Math.round(point2.x / outputStride), 0, width2 - 1)
- });
- const [height, width] = scores.shape;
- const sourceKeypointIndices = getStridedIndexNearPoint(sourceKeypoint.position, height, width);
- const displacement = getDisplacement(sourceKeypointIndices);
- const displacedPoint = addVectors(sourceKeypoint.position, displacement);
- let targetKeypoint = displacedPoint;
- for (let i = 0; i < offsetRefineStep; i++) {
- const targetKeypointIndices = getStridedIndexNearPoint(targetKeypoint, height, width);
- const offsetPoint = getOffsetPoint(targetKeypointIndices.y, targetKeypointIndices.x, targetId, offsets);
- targetKeypoint = addVectors(
- { x: targetKeypointIndices.x * outputStride, y: targetKeypointIndices.y * outputStride },
- { x: offsetPoint.x, y: offsetPoint.y }
- );
- }
- const targetKeyPointIndices = getStridedIndexNearPoint(targetKeypoint, height, width);
- const score = scores.get(targetKeyPointIndices.y, targetKeyPointIndices.x, targetId);
- return { position: targetKeypoint, part: partNames[targetId], score };
-}
-function decodePose(root, scores, offsets, displacementsFwd, displacementsBwd) {
- const tuples = poseChain.map(([parentJoinName, childJoinName]) => [partIds[parentJoinName], partIds[childJoinName]]);
- const edgesFwd = tuples.map(([, childJointId]) => childJointId);
- const edgesBwd = tuples.map(([parentJointId]) => parentJointId);
- const numParts = scores.shape[2];
- const numEdges = edgesFwd.length;
- const keypoints = new Array(numParts);
- const rootPoint = getImageCoords(root.part, outputStride, offsets);
- keypoints[root.part.id] = {
- score: root.score,
- part: partNames[root.part.id],
- position: rootPoint
- };
- for (let edge = numEdges - 1; edge >= 0; --edge) {
- const sourceId = edgesFwd[edge];
- const targetId = edgesBwd[edge];
- if (keypoints[sourceId] && !keypoints[targetId]) {
- keypoints[targetId] = traverse(edge, keypoints[sourceId], targetId, scores, offsets, displacementsBwd);
- }
- }
- for (let edge = 0; edge < numEdges; ++edge) {
- const sourceId = edgesBwd[edge];
- const targetId = edgesFwd[edge];
- if (keypoints[sourceId] && !keypoints[targetId]) {
- keypoints[targetId] = traverse(edge, keypoints[sourceId], targetId, scores, offsets, displacementsFwd);
- }
- }
- return keypoints;
-}
-function scoreIsMaximumInLocalWindow(keypointId, score, heatmapY, heatmapX, scores) {
- const [height, width] = scores.shape;
- let localMaximum = true;
- const yStart = Math.max(heatmapY - localMaximumRadius, 0);
- const yEnd = Math.min(heatmapY + localMaximumRadius + 1, height);
- for (let yCurrent = yStart; yCurrent < yEnd; ++yCurrent) {
- const xStart = Math.max(heatmapX - localMaximumRadius, 0);
- const xEnd = Math.min(heatmapX + localMaximumRadius + 1, width);
- for (let xCurrent = xStart; xCurrent < xEnd; ++xCurrent) {
- if (scores.get(yCurrent, xCurrent, keypointId) > score) {
- localMaximum = false;
- break;
- }
- }
- if (!localMaximum)
- break;
- }
- return localMaximum;
-}
-function buildPartWithScoreQueue(minConfidence2, scores) {
- const [height, width, numKeypoints] = scores.shape;
- const queue = new MaxHeap(height * width * numKeypoints, ({ score }) => score);
- for (let heatmapY = 0; heatmapY < height; ++heatmapY) {
- for (let heatmapX = 0; heatmapX < width; ++heatmapX) {
- for (let keypointId = 0; keypointId < numKeypoints; ++keypointId) {
- const score = scores.get(heatmapY, heatmapX, keypointId);
- if (score < minConfidence2)
- continue;
- if (scoreIsMaximumInLocalWindow(keypointId, score, heatmapY, heatmapX, scores))
- queue.enqueue({ score, part: { heatmapY, heatmapX, id: keypointId } });
- }
- }
- }
- return queue;
-}
-function withinRadius(poses, { x, y }, keypointId) {
- return poses.some(({ keypoints }) => {
- var _a;
- const correspondingKeypoint = (_a = keypoints[keypointId]) == null ? void 0 : _a.position;
- if (!correspondingKeypoint)
- return false;
- return squaredDistance(y, x, correspondingKeypoint.y, correspondingKeypoint.x) <= squaredNmsRadius;
- });
-}
-function getInstanceScore(existingPoses, keypoints) {
- const notOverlappedKeypointScores = keypoints.reduce((result, { position, score }, keypointId) => {
- if (!withinRadius(existingPoses, position, keypointId))
- result += score;
- return result;
- }, 0);
- return notOverlappedKeypointScores / keypoints.length;
-}
-function decode(offsets, scores, displacementsFwd, displacementsBwd, maxDetected, minConfidence2) {
- const poses = [];
- const queue = buildPartWithScoreQueue(minConfidence2, scores);
- while (poses.length < maxDetected && !queue.empty()) {
- const root = queue.dequeue();
- const rootImageCoords = getImageCoords(root.part, outputStride, offsets);
- if (withinRadius(poses, rootImageCoords, root.part.id))
- continue;
- let keypoints = decodePose(root, scores, offsets, displacementsFwd, displacementsBwd);
- keypoints = keypoints.filter((a) => a.score > minConfidence2);
- const score = getInstanceScore(poses, keypoints);
- const box = getBoundingBox(keypoints);
- if (score > minConfidence2)
- poses.push({ keypoints, box, score: Math.round(100 * score) / 100 });
- }
- return poses;
-}
-async function predict18(input, config3) {
- if (!(model17 == null ? void 0 : model17["executor"]))
- return [];
- const res = tfjs_esm_exports.tidy(() => {
- if (!model17.inputs[0].shape)
- return [];
- const resized = tfjs_esm_exports.image.resizeBilinear(input, [model17.inputs[0].shape[2], model17.inputs[0].shape[1]]);
- const normalized = tfjs_esm_exports.sub(tfjs_esm_exports.div(tfjs_esm_exports.cast(resized, "float32"), 127.5), 1);
- const results = model17.execute(normalized, poseNetOutputs);
- const results3d = results.map((y) => tfjs_esm_exports.squeeze(y, [0]));
- results3d[1] = tfjs_esm_exports.sigmoid(results3d[1]);
- return results3d;
- });
- const buffers = await Promise.all(res.map((tensor3) => tensor3.buffer()));
- for (const t of res)
- tfjs_esm_exports.dispose(t);
- const decoded = decode(buffers[0], buffers[1], buffers[2], buffers[3], config3.body.maxDetected, config3.body.minConfidence);
- if (!model17.inputs[0].shape)
- return [];
- const scaled = scalePoses(decoded, [input.shape[1], input.shape[2]], [model17.inputs[0].shape[2], model17.inputs[0].shape[1]]);
- return scaled;
-}
-async function load18(config3) {
- if (!model17 || env.initial)
- model17 = await loadModel(config3.body.modelPath);
- else if (config3.debug)
- log("cached model:", model17["modelUrl"]);
- return model17;
-}
-
-// src/segmentation/segmentation.ts
-var model18;
-var busy = false;
-async function load19(config3) {
- if (!model18 || env.initial)
- model18 = await loadModel(config3.segmentation.modelPath);
- else if (config3.debug)
- log("cached model:", model18["modelUrl"]);
- return model18;
-}
-async function process5(input, background, config3) {
- var _a, _b;
- if (busy)
- return { data: [], canvas: null, alpha: null };
- busy = true;
- if (!model18)
- await load19(config3);
- const inputImage = await process2(input, config3);
- const width = ((_a = inputImage.tensor) == null ? void 0 : _a.shape[2]) || 0;
- const height = ((_b = inputImage.tensor) == null ? void 0 : _b.shape[1]) || 0;
- if (!inputImage.tensor)
- return { data: [], canvas: null, alpha: null };
- const t = {};
- t.resize = tfjs_esm_exports.image.resizeBilinear(inputImage.tensor, [model18.inputs[0].shape ? model18.inputs[0].shape[1] : 0, model18.inputs[0].shape ? model18.inputs[0].shape[2] : 0], false);
- tfjs_esm_exports.dispose(inputImage.tensor);
- t.norm = tfjs_esm_exports.div(t.resize, constants.tf255);
- t.res = model18.execute(t.norm);
- t.squeeze = tfjs_esm_exports.squeeze(t.res, 0);
- if (t.squeeze.shape[2] === 2) {
- t.softmax = tfjs_esm_exports.softmax(t.squeeze);
- [t.bg, t.fg] = tfjs_esm_exports.unstack(t.softmax, 2);
- t.expand = tfjs_esm_exports.expandDims(t.fg, 2);
- t.pad = tfjs_esm_exports.expandDims(t.expand, 0);
- t.crop = tfjs_esm_exports.image.cropAndResize(t.pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
- t.data = tfjs_esm_exports.squeeze(t.crop, 0);
- } else {
- t.data = tfjs_esm_exports.image.resizeBilinear(t.squeeze, [height, width]);
- }
- const data = Array.from(await t.data.data());
- if (env.node && !env.Canvas && typeof ImageData === "undefined") {
- if (config3.debug)
- log("canvas support missing");
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- return { data, canvas: null, alpha: null };
- }
- const alphaCanvas = canvas(width, height);
- if (tfjs_esm_exports.browser)
- await tfjs_esm_exports.browser.toPixels(t.data, alphaCanvas);
- const alphaCtx = alphaCanvas.getContext("2d");
- if (config3.segmentation.blur && config3.segmentation.blur > 0)
- alphaCtx.filter = `blur(${config3.segmentation.blur}px)`;
- const alphaData = alphaCtx.getImageData(0, 0, width, height);
- const compositeCanvas = canvas(width, height);
- const compositeCtx = compositeCanvas.getContext("2d");
- if (inputImage.canvas)
- compositeCtx.drawImage(inputImage.canvas, 0, 0);
- compositeCtx.globalCompositeOperation = "darken";
- if (config3.segmentation.blur && config3.segmentation.blur > 0)
- compositeCtx.filter = `blur(${config3.segmentation.blur}px)`;
- compositeCtx.drawImage(alphaCanvas, 0, 0);
- compositeCtx.globalCompositeOperation = "source-over";
- compositeCtx.filter = "none";
- const compositeData = compositeCtx.getImageData(0, 0, width, height);
- for (let i = 0; i < width * height; i++)
- compositeData.data[4 * i + 3] = alphaData.data[4 * i + 0];
- compositeCtx.putImageData(compositeData, 0, 0);
- let mergedCanvas = null;
- if (background && compositeCanvas) {
- mergedCanvas = canvas(width, height);
- const bgImage = await process2(background, config3);
- tfjs_esm_exports.dispose(bgImage.tensor);
- const ctxMerge = mergedCanvas.getContext("2d");
- ctxMerge.drawImage(bgImage.canvas, 0, 0, mergedCanvas.width, mergedCanvas.height);
- ctxMerge.drawImage(compositeCanvas, 0, 0);
- }
- Object.keys(t).forEach((tensor3) => tfjs_esm_exports.dispose(t[tensor3]));
- busy = false;
- return { data, canvas: compositeCanvas, alpha: alphaCanvas };
-}
-
-// src/models.ts
-var Models = class {
- constructor() {
- __publicField(this, "ssrnetage", null);
- __publicField(this, "gear", null);
- __publicField(this, "blazeposedetect", null);
- __publicField(this, "blazepose", null);
- __publicField(this, "centernet", null);
- __publicField(this, "efficientpose", null);
- __publicField(this, "mobilefacenet", null);
- __publicField(this, "insightface", null);
- __publicField(this, "emotion", null);
- __publicField(this, "facedetect", null);
- __publicField(this, "faceiris", null);
- __publicField(this, "facemesh", null);
- __publicField(this, "faceres", null);
- __publicField(this, "ssrnetgender", null);
- __publicField(this, "handpose", null);
- __publicField(this, "handskeleton", null);
- __publicField(this, "handtrack", null);
- __publicField(this, "liveness", null);
- __publicField(this, "movenet", null);
- __publicField(this, "nanodet", null);
- __publicField(this, "posenet", null);
- __publicField(this, "segmentation", null);
- __publicField(this, "antispoof", null);
- }
-};
-var getModelStats = (instance2) => {
- let totalSizeFromManifest = 0;
- let totalSizeWeights = 0;
- let totalSizeLoading = 0;
- for (const m of Object.values(modelStats)) {
- totalSizeFromManifest += m.sizeFromManifest;
- totalSizeWeights += m.sizeLoadedWeights;
- totalSizeLoading += m.sizeDesired;
- }
- const percentageLoaded = totalSizeLoading > 0 ? totalSizeWeights / totalSizeLoading : 0;
- return {
- numLoadedModels: Object.values(modelStats).length,
- numEnabledModels: void 0,
- numDefinedModels: Object.keys(instance2.models).length,
- percentageLoaded,
- totalSizeFromManifest,
- totalSizeWeights,
- totalSizeLoading,
- totalSizeEnabled: void 0,
- modelStats: Object.values(modelStats)
- };
-};
-function reset(instance2) {
- for (const model19 of Object.keys(instance2.models))
- instance2.models[model19] = null;
-}
-async function load20(instance2) {
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
- if (env.initial)
- reset(instance2);
- if (instance2.config.hand.enabled) {
- if (!instance2.models.handpose && ((_b = (_a = instance2.config.hand.detector) == null ? void 0 : _a.modelPath) == null ? void 0 : _b.includes("handdetect"))) {
- [instance2.models.handpose, instance2.models.handskeleton] = await load14(instance2.config);
- }
- if (!instance2.models.handskeleton && instance2.config.hand.landmarks && ((_d = (_c = instance2.config.hand.detector) == null ? void 0 : _c.modelPath) == null ? void 0 : _d.includes("handdetect"))) {
- [instance2.models.handpose, instance2.models.handskeleton] = await load14(instance2.config);
- }
- }
- if (instance2.config.body.enabled && !instance2.models.blazepose && ((_e = instance2.config.body.modelPath) == null ? void 0 : _e.includes("blazepose")))
- instance2.models.blazepose = loadPose(instance2.config);
- if (instance2.config.body.enabled && !instance2.models.blazeposedetect && instance2.config.body["detector"] && instance2.config.body["detector"].modelPath)
- instance2.models.blazeposedetect = loadDetect(instance2.config);
- if (instance2.config.body.enabled && !instance2.models.efficientpose && ((_f = instance2.config.body.modelPath) == null ? void 0 : _f.includes("efficientpose")))
- instance2.models.efficientpose = load7(instance2.config);
- if (instance2.config.body.enabled && !instance2.models.movenet && ((_g = instance2.config.body.modelPath) == null ? void 0 : _g.includes("movenet")))
- instance2.models.movenet = load16(instance2.config);
- if (instance2.config.body.enabled && !instance2.models.posenet && ((_h = instance2.config.body.modelPath) == null ? void 0 : _h.includes("posenet")))
- instance2.models.posenet = load18(instance2.config);
- if (instance2.config.face.enabled && !instance2.models.facedetect)
- instance2.models.facedetect = load5(instance2.config);
- if (instance2.config.face.enabled && ((_i = instance2.config.face.antispoof) == null ? void 0 : _i.enabled) && !instance2.models.antispoof)
- instance2.models.antispoof = load4(instance2.config);
- if (instance2.config.face.enabled && ((_j = instance2.config.face.liveness) == null ? void 0 : _j.enabled) && !instance2.models.liveness)
- instance2.models.liveness = load15(instance2.config);
- if (instance2.config.face.enabled && ((_k = instance2.config.face.description) == null ? void 0 : _k.enabled) && !instance2.models.faceres)
- instance2.models.faceres = load13(instance2.config);
- if (instance2.config.face.enabled && ((_l = instance2.config.face.emotion) == null ? void 0 : _l.enabled) && !instance2.models.emotion)
- instance2.models.emotion = load8(instance2.config);
- if (instance2.config.face.enabled && ((_m = instance2.config.face.iris) == null ? void 0 : _m.enabled) && !((_n = instance2.config.face.attention) == null ? void 0 : _n.enabled) && !instance2.models.faceiris)
- instance2.models.faceiris = load11(instance2.config);
- if (instance2.config.face.enabled && ((_o = instance2.config.face.mesh) == null ? void 0 : _o.enabled) && !instance2.models.facemesh)
- instance2.models.facemesh = load12(instance2.config);
- if (instance2.config.face.enabled && ((_p = instance2.config.face["gear"]) == null ? void 0 : _p.enabled) && !instance2.models.gear)
- instance2.models.gear = load(instance2.config);
- if (instance2.config.face.enabled && ((_q = instance2.config.face["ssrnet"]) == null ? void 0 : _q.enabled) && !instance2.models.ssrnetage)
- instance2.models.ssrnetage = load2(instance2.config);
- if (instance2.config.face.enabled && ((_r = instance2.config.face["ssrnet"]) == null ? void 0 : _r.enabled) && !instance2.models.ssrnetgender)
- instance2.models.ssrnetgender = load3(instance2.config);
- if (instance2.config.face.enabled && ((_s = instance2.config.face["mobilefacenet"]) == null ? void 0 : _s.enabled) && !instance2.models.mobilefacenet)
- instance2.models.mobilefacenet = load9(instance2.config);
- if (instance2.config.face.enabled && ((_t = instance2.config.face["insightface"]) == null ? void 0 : _t.enabled) && !instance2.models.insightface)
- instance2.models.insightface = load10(instance2.config);
- if (instance2.config.hand.enabled && !instance2.models.handtrack && ((_v = (_u = instance2.config.hand.detector) == null ? void 0 : _u.modelPath) == null ? void 0 : _v.includes("handtrack")))
- instance2.models.handtrack = loadDetect2(instance2.config);
- if (instance2.config.hand.enabled && instance2.config.hand.landmarks && !instance2.models.handskeleton && ((_x = (_w = instance2.config.hand.detector) == null ? void 0 : _w.modelPath) == null ? void 0 : _x.includes("handtrack")))
- instance2.models.handskeleton = loadSkeleton(instance2.config);
- if (instance2.config.object.enabled && !instance2.models.centernet && ((_y = instance2.config.object.modelPath) == null ? void 0 : _y.includes("centernet")))
- instance2.models.centernet = load6(instance2.config);
- if (instance2.config.object.enabled && !instance2.models.nanodet && ((_z = instance2.config.object.modelPath) == null ? void 0 : _z.includes("nanodet")))
- instance2.models.nanodet = load17(instance2.config);
- if (instance2.config.segmentation.enabled && !instance2.models.segmentation)
- instance2.models.segmentation = load19(instance2.config);
- for await (const model19 of Object.keys(instance2.models)) {
- if (instance2.models[model19] && typeof instance2.models[model19] !== "undefined") {
- instance2.models[model19] = await instance2.models[model19];
- }
- }
-}
-var instance;
-function validateModel(newInstance, model19, name) {
- var _a;
- if (newInstance)
- instance = newInstance;
- if (!model19)
- return null;
- if (!instance)
- log("instance not registred");
- if (!instance.config.validateModels)
- return null;
- const simpleOps = ["const", "placeholder", "noop", "pad", "squeeze", "add", "sub", "mul", "div"];
- const ignoreOps = ["biasadd", "fusedbatchnormv3", "matmul"];
- const ops = [];
- const missing = [];
- const url = model19["modelUrl"];
- const executor = model19["executor"];
- if ((_a = executor == null ? void 0 : executor.graph) == null ? void 0 : _a.nodes) {
- for (const kernel of Object.values(executor.graph.nodes)) {
- const op = kernel.op.toLowerCase();
- if (!ops.includes(op))
- ops.push(op);
- }
- } else {
- if (!executor && instance.config.debug) {
- log("model not loaded", name);
- }
- }
- for (const op of ops) {
- if (!simpleOps.includes(op) && !ignoreOps.includes(op) && !instance.env.kernels.includes(op) && !instance.env.kernels.includes(op.replace("_", "")) && !instance.env.kernels.includes(op.replace("native", "")) && !instance.env.kernels.includes(op.replace("v2", ""))) {
- missing.push(op);
- }
- }
- if (instance.config.debug && missing.length > 0)
- log("model validation failed:", name, missing);
- return missing.length > 0 ? { name, missing, ops, url } : null;
-}
-function validate2(newInstance) {
- instance = newInstance;
- const missing = [];
- for (const defined of Object.keys(instance.models)) {
- const model19 = instance.models[defined];
- if (!model19)
- continue;
- const res = validateModel(instance, model19, defined);
- if (res)
- missing.push(res);
- }
- return missing;
-}
-
-// src/tfjs/load.ts
-var options2 = {
- cacheModels: true,
- cacheSupported: true,
- verbose: true,
- debug: false,
- modelBasePath: ""
-};
-var modelStats = {};
-async function httpHandler(url, init2) {
- if (options2.debug)
- log("load model fetch:", url, init2);
- return fetch(url, init2);
-}
-function setModelLoadOptions(config3) {
- options2.cacheModels = config3.cacheModels;
- options2.verbose = config3.debug;
- options2.modelBasePath = config3.modelBasePath;
-}
-async function loadModel(modelPath) {
- var _a, _b, _c;
- let modelUrl = join(options2.modelBasePath, modelPath || "");
- if (!modelUrl.toLowerCase().endsWith(".json"))
- modelUrl += ".json";
- const modelPathSegments = modelUrl.includes("/") ? modelUrl.split("/") : modelUrl.split("\\");
- const shortModelName = modelPathSegments[modelPathSegments.length - 1].replace(".json", "");
- const cachedModelName = "indexeddb://" + shortModelName;
- modelStats[shortModelName] = {
- name: shortModelName,
- sizeFromManifest: 0,
- sizeLoadedWeights: 0,
- sizeDesired: models_exports[shortModelName],
- inCache: false
- };
- options2.cacheSupported = typeof window !== "undefined" && typeof window.localStorage !== "undefined" && typeof window.indexedDB !== "undefined";
- let cachedModels = {};
- try {
- cachedModels = options2.cacheSupported && options2.cacheModels ? await tfjs_esm_exports.io.listModels() : {};
- } catch (e) {
- options2.cacheSupported = false;
- }
- modelStats[shortModelName].inCache = options2.cacheSupported && options2.cacheModels && Object.keys(cachedModels).includes(cachedModelName);
- const tfLoadOptions = typeof fetch === "undefined" ? {} : { fetchFunc: (url, init2) => httpHandler(url, init2) };
- const model19 = new GraphModel(modelStats[shortModelName].inCache ? cachedModelName : modelUrl, tfLoadOptions);
- let loaded = false;
- try {
- model19.findIOHandler();
- if (options2.debug)
- log("model load handler:", model19["handler"]);
- const artifacts = await model19.handler.load();
- modelStats[shortModelName].sizeFromManifest = ((_a = artifacts == null ? void 0 : artifacts.weightData) == null ? void 0 : _a.byteLength) || 0;
- model19.loadSync(artifacts);
- modelStats[shortModelName].sizeLoadedWeights = ((_c = (_b = model19.artifacts) == null ? void 0 : _b.weightData) == null ? void 0 : _c.byteLength) || 0;
- if (options2.verbose)
- log("load model:", model19["modelUrl"], { bytes: modelStats[shortModelName].sizeLoadedWeights }, options2);
- loaded = true;
- } catch (err) {
- log("error loading model:", modelUrl, err);
- }
- if (loaded && options2.cacheModels && options2.cacheSupported && !modelStats[shortModelName].inCache) {
- try {
- const saveResult = await model19.save(cachedModelName);
- log("model saved:", cachedModelName, saveResult);
- } catch (err) {
- log("error saving model:", modelUrl, err);
- }
- }
- validateModel(null, model19, `${modelPath || ""}`);
- return model19;
-}
-
-// package.json
-var version9 = "2.9.4";
-
-// src/draw/draw.ts
-var draw_exports = {};
-__export(draw_exports, {
- all: () => all,
- body: () => body,
- canvas: () => canvas2,
- face: () => face,
- gesture: () => gesture,
- hand: () => hand,
- object: () => object,
- options: () => options3,
- person: () => person
-});
-
-// src/draw/primitives.ts
-var getCanvasContext = (input) => {
- if (!input)
- log("draw error: invalid canvas");
- else if (!input.getContext)
- log("draw error: canvas context not defined");
- else {
- const ctx = input.getContext("2d");
- if (!ctx)
- log("draw error: cannot get canvas context");
- else
- return ctx;
- }
- return null;
-};
-var rad2deg = (theta) => Math.round(theta * 180 / Math.PI);
-var colorDepth = (z, opt2) => {
- if (!opt2.useDepth || typeof z === "undefined")
- return opt2.color;
- const rgb2 = Uint8ClampedArray.from([127 + 2 * z, 127 - 2 * z, 255]);
- return `rgba(${rgb2[0]}, ${rgb2[1]}, ${rgb2[2]}, ${opt2.alpha})`;
-};
-function point(ctx, x, y, z, localOptions) {
- ctx.fillStyle = colorDepth(z, localOptions);
- ctx.beginPath();
- ctx.arc(x, y, localOptions.pointSize, 0, 2 * Math.PI);
- ctx.fill();
-}
-function rect(ctx, x, y, width, height, localOptions) {
- ctx.beginPath();
- ctx.lineWidth = localOptions.lineWidth;
- if (localOptions.useCurves) {
- const cx = (x + x + width) / 2;
- const cy = (y + y + height) / 2;
- ctx.ellipse(cx, cy, width / 2, height / 2, 0, 0, 2 * Math.PI);
- } else {
- ctx.moveTo(x + localOptions.roundRect, y);
- ctx.lineTo(x + width - localOptions.roundRect, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + localOptions.roundRect);
- ctx.lineTo(x + width, y + height - localOptions.roundRect);
- ctx.quadraticCurveTo(x + width, y + height, x + width - localOptions.roundRect, y + height);
- ctx.lineTo(x + localOptions.roundRect, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - localOptions.roundRect);
- ctx.lineTo(x, y + localOptions.roundRect);
- ctx.quadraticCurveTo(x, y, x + localOptions.roundRect, y);
- ctx.closePath();
- }
- ctx.stroke();
-}
-function lines(ctx, points, localOptions) {
- if (points.length < 2)
- return;
- ctx.beginPath();
- ctx.moveTo(points[0][0], points[0][1]);
- for (const pt of points) {
- ctx.strokeStyle = colorDepth(pt[2] || 0, localOptions);
- ctx.lineTo(Math.trunc(pt[0]), Math.trunc(pt[1]));
- }
- ctx.stroke();
- if (localOptions.fillPolygons) {
- ctx.closePath();
- ctx.fill();
- }
-}
-function curves(ctx, points, localOptions) {
- if (points.length < 2)
- return;
- ctx.lineWidth = localOptions.lineWidth;
- if (!localOptions.useCurves || points.length <= 2) {
- lines(ctx, points, localOptions);
- return;
- }
- ctx.moveTo(points[0][0], points[0][1]);
- for (let i = 0; i < points.length - 2; i++) {
- const xc = (points[i][0] + points[i + 1][0]) / 2;
- const yc = (points[i][1] + points[i + 1][1]) / 2;
- ctx.quadraticCurveTo(points[i][0], points[i][1], xc, yc);
- }
- ctx.quadraticCurveTo(points[points.length - 2][0], points[points.length - 2][1], points[points.length - 1][0], points[points.length - 1][1]);
- ctx.stroke();
- if (localOptions.fillPolygons) {
- ctx.closePath();
- ctx.fill();
- }
-}
-function arrow(ctx, from, to, radius = 5) {
- let angle;
- let x;
- let y;
- ctx.beginPath();
- ctx.moveTo(from[0], from[1]);
- ctx.lineTo(to[0], to[1]);
- angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
- x = radius * Math.cos(angle) + to[0];
- y = radius * Math.sin(angle) + to[1];
- ctx.moveTo(x, y);
- angle += 1 / 3 * (2 * Math.PI);
- x = radius * Math.cos(angle) + to[0];
- y = radius * Math.sin(angle) + to[1];
- ctx.lineTo(x, y);
- angle += 1 / 3 * (2 * Math.PI);
- x = radius * Math.cos(angle) + to[0];
- y = radius * Math.sin(angle) + to[1];
- ctx.lineTo(x, y);
- ctx.closePath();
- ctx.stroke();
- ctx.fill();
-}
-
-// src/draw/options.ts
-var options3 = {
- color: "rgba(173, 216, 230, 0.6)",
- labelColor: "rgba(173, 216, 230, 1)",
- shadowColor: "black",
- alpha: 0.5,
- font: 'small-caps 16px "Segoe UI"',
- lineHeight: 18,
- lineWidth: 4,
- pointSize: 2,
- roundRect: 8,
- drawPoints: false,
- drawLabels: true,
- drawBoxes: true,
- drawAttention: true,
- drawGestures: true,
- drawPolygons: true,
- drawGaze: true,
- fillPolygons: false,
- useDepth: true,
- useCurves: false
-};
-
-// src/draw/face.ts
-var opt;
-function drawLabels(f, ctx) {
- var _a, _b;
- if (opt.drawLabels) {
- const labels2 = [];
- labels2.push(`face: ${Math.trunc(100 * f.score)}%`);
- if (f.genderScore)
- labels2.push(`${f.gender || ""} ${Math.trunc(100 * f.genderScore)}%`);
- if (f.age)
- labels2.push(`age: ${f.age || ""}`);
- if (f.iris)
- labels2.push(`distance: ${f.iris}`);
- if (f.real)
- labels2.push(`real: ${Math.trunc(100 * f.real)}%`);
- if (f.live)
- labels2.push(`live: ${Math.trunc(100 * f.live)}%`);
- if (f.emotion && f.emotion.length > 0) {
- const emotion2 = f.emotion.map((a) => `${Math.trunc(100 * a.score)}% ${a.emotion}`);
- if (emotion2.length > 3)
- emotion2.length = 3;
- labels2.push(emotion2.join(" "));
- }
- if (((_a = f.rotation) == null ? void 0 : _a.angle) && ((_b = f.rotation) == null ? void 0 : _b.gaze)) {
- if (f.rotation.angle.roll)
- labels2.push(`roll: ${rad2deg(f.rotation.angle.roll)}\xB0 yaw:${rad2deg(f.rotation.angle.yaw)}\xB0 pitch:${rad2deg(f.rotation.angle.pitch)}\xB0`);
- if (f.rotation.gaze.bearing)
- labels2.push(`gaze: ${rad2deg(f.rotation.gaze.bearing)}\xB0`);
- }
- if (labels2.length === 0)
- labels2.push("face");
- ctx.fillStyle = opt.color;
- for (let i = labels2.length - 1; i >= 0; i--) {
- const x = Math.max(f.box[0], 0);
- const y = i * opt.lineHeight + f.box[1];
- if (opt.shadowColor && opt.shadowColor !== "") {
- ctx.fillStyle = opt.shadowColor;
- ctx.fillText(labels2[i], x + 5, y + 16);
- }
- ctx.fillStyle = opt.labelColor;
- ctx.fillText(labels2[i], x + 4, y + 15);
- }
- }
-}
-function drawIrisElipse(f, ctx) {
- var _a, _b, _c, _d;
- if (((_a = f.annotations) == null ? void 0 : _a.leftEyeIris) && ((_b = f.annotations) == null ? void 0 : _b.leftEyeIris[0])) {
- ctx.strokeStyle = opt.useDepth ? "rgba(255, 200, 255, 0.3)" : opt.color;
- ctx.beginPath();
- const sizeX = Math.abs(f.annotations.leftEyeIris[3][0] - f.annotations.leftEyeIris[1][0]) / 2;
- const sizeY = Math.abs(f.annotations.leftEyeIris[4][1] - f.annotations.leftEyeIris[2][1]) / 2;
- ctx.ellipse(f.annotations.leftEyeIris[0][0], f.annotations.leftEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);
- ctx.stroke();
- if (opt.fillPolygons) {
- ctx.fillStyle = opt.useDepth ? "rgba(255, 255, 200, 0.3)" : opt.color;
- ctx.fill();
- }
- }
- if (((_c = f.annotations) == null ? void 0 : _c.rightEyeIris) && ((_d = f.annotations) == null ? void 0 : _d.rightEyeIris[0])) {
- ctx.strokeStyle = opt.useDepth ? "rgba(255, 200, 255, 0.3)" : opt.color;
- ctx.beginPath();
- const sizeX = Math.abs(f.annotations.rightEyeIris[3][0] - f.annotations.rightEyeIris[1][0]) / 2;
- const sizeY = Math.abs(f.annotations.rightEyeIris[4][1] - f.annotations.rightEyeIris[2][1]) / 2;
- ctx.ellipse(f.annotations.rightEyeIris[0][0], f.annotations.rightEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);
- ctx.stroke();
- if (opt.fillPolygons) {
- ctx.fillStyle = opt.useDepth ? "rgba(255, 255, 200, 0.3)" : opt.color;
- ctx.fill();
- }
- }
-}
-function drawGazeSpheres(f, ctx) {
- var _a;
- if (opt.drawGaze && ((_a = f.rotation) == null ? void 0 : _a.angle) && typeof Path2D !== "undefined") {
- ctx.strokeStyle = "pink";
- const valX = f.box[0] + f.box[2] / 2 - f.box[3] * rad2deg(f.rotation.angle.yaw) / 90;
- const valY = f.box[1] + f.box[3] / 2 + f.box[2] * rad2deg(f.rotation.angle.pitch) / 90;
- const pathV = new Path2D(`
- M ${f.box[0] + f.box[2] / 2} ${f.box[1]}
+`;var e5=(e,t,o)=>{let n=new RegExp("\\b"+t+" \\w+ (\\w+)","ig");e.replace(n,(r,s)=>(o[s]=0,r))},t5=class{constructor(t,o,n){w(this,"uniform",{});w(this,"attribute",{});w(this,"gl");w(this,"id");w(this,"compile",(t,o)=>{let n=this.gl.createShader(o);return n?(this.gl.shaderSource(n,t),this.gl.compileShader(n),this.gl.getShaderParameter(n,this.gl.COMPILE_STATUS)?n:(b(`filter: gl compile failed: ${this.gl.getShaderInfoLog(n)||"unknown"}`),null)):(b("filter: could not create shader"),null)});this.gl=t;let r=this.compile(o,this.gl.VERTEX_SHADER),s=this.compile(n,this.gl.FRAGMENT_SHADER);if(this.id=this.gl.createProgram(),!(!r||!s)){if(!this.id){b("filter: could not create webgl program");return}if(this.gl.attachShader(this.id,r),this.gl.attachShader(this.id,s),this.gl.linkProgram(this.id),!this.gl.getProgramParameter(this.id,this.gl.LINK_STATUS)){b(`filter: gl link failed: ${this.gl.getProgramInfoLog(this.id)||"unknown"}`);return}this.gl.useProgram(this.id),e5(o,"attribute",this.attribute);for(let a in this.attribute)this.attribute[a]=this.gl.getAttribLocation(this.id,a);e5(o,"uniform",this.uniform),e5(n,"uniform",this.uniform);for(let a in this.uniform)this.uniform[a]=this.gl.getUniformLocation(this.id,a)}}};function q1(){let e=0,t=null,o=!1,n=-1,r=[null,null],s=[],a=null,i=null,c=s0(100,100),x={},d={INTERMEDIATE:1},l=c.getContext("webgl");if(!l){b("filter: cannot get webgl context");return}this.gl=l;function f(P,m){if(!(P===c.width&&m===c.height)){if(c.width=P,c.height=m,!a){let u=new Float32Array([-1,-1,0,1,1,-1,1,1,-1,1,0,0,-1,1,0,0,1,-1,1,1,1,1,1,0]);a=l.createBuffer(),l.bindBuffer(l.ARRAY_BUFFER,a),l.bufferData(l.ARRAY_BUFFER,u,l.STATIC_DRAW),l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0)}l.viewport(0,0,c.width,c.height),r=[null,null]}}function y(P,m){let u=l.createFramebuffer();l.bindFramebuffer(l.FRAMEBUFFER,u);let z=l.createRenderbuffer();l.bindRenderbuffer(l.RENDERBUFFER,z);let k=l.createTexture();return l.bindTexture(l.TEXTURE_2D,k),l.texImage2D(l.TEXTURE_2D,0,l.RGBA,P,m,0,l.RGBA,l.UNSIGNED_BYTE,null),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_MAG_FILTER,l.LINEAR),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_MIN_FILTER,l.LINEAR),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_WRAP_S,l.CLAMP_TO_EDGE),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_WRAP_T,l.CLAMP_TO_EDGE),l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,l.TEXTURE_2D,k,0),l.bindTexture(l.TEXTURE_2D,null),l.bindFramebuffer(l.FRAMEBUFFER,null),{fbo:u,texture:k}}function p(P){return r[P]=r[P]||y(c.width,c.height),r[P]}function g(P=0){if(!i)return;let m=null,u=null,z=!1;e===0?m=t:m=p(n).texture||null,e++,o&&!(P&d.INTERMEDIATE)?(u=null,z=e%2===0):(n=(n+1)%2,u=p(n).fbo||null),l.bindTexture(l.TEXTURE_2D,m),l.bindFramebuffer(l.FRAMEBUFFER,u),l.uniform1f(i.uniform.flipY,z?-1:1),l.drawArrays(l.TRIANGLES,0,6)}function M(P){if(x[P])return i=x[P],l.useProgram((i?i.id:null)||null),i;if(i=new t5(l,B1,P),!i)return b("filter: could not get webgl program"),null;let m=Float32Array.BYTES_PER_ELEMENT,u=4*m;return l.enableVertexAttribArray(i.attribute.pos),l.vertexAttribPointer(i.attribute.pos,2,l.FLOAT,!1,u,0*m),l.enableVertexAttribArray(i.attribute.uv),l.vertexAttribPointer(i.attribute.uv,2,l.FLOAT,!1,u,2*m),x[P]=i,i}let R={colorMatrix:P=>{let m=new Float32Array(P);m[4]/=255,m[9]/=255,m[14]/=255,m[19]/=255;let u=m[18]===1&&m[3]===0&&m[8]===0&&m[13]===0&&m[15]===0&&m[16]===0&&m[17]===0&&m[19]===0?D1:H1,z=M(u);!z||(l.uniform1fv(z.uniform.m,m),g())},brightness:P=>{let m=(P||0)+1;R.colorMatrix([m,0,0,0,0,0,m,0,0,0,0,0,m,0,0,0,0,0,1,0])},saturation:P=>{let m=(P||0)*2/3+1,u=(m-1)*-.5;R.colorMatrix([m,u,u,0,0,u,m,u,0,0,u,u,m,0,0,0,0,0,1,0])},desaturate:()=>{R.saturation(-1)},contrast:P=>{let m=(P||0)+1,u=-128*(m-1);R.colorMatrix([m,0,0,0,u,0,m,0,0,u,0,0,m,0,u,0,0,0,1,0])},negative:()=>{R.contrast(-2)},hue:P=>{P=(P||0)/180*Math.PI;let m=Math.cos(P),u=Math.sin(P),z=.213,k=.715,h=.072;R.colorMatrix([z+m*(1-z)+u*-z,k+m*-k+u*-k,h+m*-h+u*(1-h),0,0,z+m*-z+u*.143,k+m*(1-k)+u*.14,h+m*-h+u*-.283,0,0,z+m*-z+u*-(1-z),k+m*-k+u*k,h+m*(1-h)+u*h,0,0,0,0,0,1,0])},desaturateLuminance:()=>{R.colorMatrix([.2764723,.929708,.0938197,0,-37.1,.2764723,.929708,.0938197,0,-37.1,.2764723,.929708,.0938197,0,-37.1,0,0,0,1,0])},sepia:()=>{R.colorMatrix([.393,.7689999,.18899999,0,0,.349,.6859999,.16799999,0,0,.272,.5339999,.13099999,0,0,0,0,0,1,0])},brownie:()=>{R.colorMatrix([.5997023498159715,.34553243048391263,-.2708298674538042,0,47.43192855600873,-.037703249837783157,.8609577587992641,.15059552388459913,0,-36.96841498319127,.24113635128153335,-.07441037908422492,.44972182064877153,0,-7.562075277591283,0,0,0,1,0])},vintagePinhole:()=>{R.colorMatrix([.6279345635605994,.3202183420819367,-.03965408211312453,0,9.651285835294123,.02578397704808868,.6441188644374771,.03259127616149294,0,7.462829176470591,.0466055556782719,-.0851232987247891,.5241648018700465,0,5.159190588235296,0,0,0,1,0])},kodachrome:()=>{R.colorMatrix([1.1285582396593525,-.3967382283601348,-.03992559172921793,0,63.72958762196502,-.16404339962244616,1.0835251566291304,-.05498805115633132,0,24.732407896706203,-.16786010706155763,-.5603416277695248,1.6014850761964943,0,35.62982807460946,0,0,0,1,0])},technicolor:()=>{R.colorMatrix([1.9125277891456083,-.8545344976951645,-.09155508482755585,0,11.793603434377337,-.3087833385928097,1.7658908555458428,-.10601743074722245,0,-70.35205161461398,-.231103377548616,-.7501899197440212,1.847597816108189,0,30.950940869491138,0,0,0,1,0])},polaroid:()=>{R.colorMatrix([1.438,-.062,-.062,0,0,-.122,1.378,-.122,0,0,-.016,-.016,1.483,0,0,0,0,0,1,0])},shiftToBGR:()=>{R.colorMatrix([0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0])},convolution:P=>{let m=new Float32Array(P),u=1/c.width,z=1/c.height,k=M(X1);!k||(l.uniform1fv(k.uniform.m,m),l.uniform2f(k.uniform.px,u,z),g())},detectEdges:()=>{R.convolution.call(this,[0,1,0,1,-4,1,0,1,0])},sobelX:()=>{R.convolution.call(this,[-1,0,1,-2,0,2,-1,0,1])},sobelY:()=>{R.convolution.call(this,[-1,-2,-1,0,0,0,1,2,1])},sharpen:P=>{let m=P||1;R.convolution.call(this,[0,-1*m,0,-1*m,1+4*m,-1*m,0,-1*m,0])},emboss:P=>{let m=P||1;R.convolution.call(this,[-2*m,-1*m,0,-1*m,1,1*m,0,1*m,2*m])},blur:P=>{let m=P/7/c.width,u=P/7/c.height,z=M(Z1);!z||(l.uniform2f(z.uniform.px,0,u),g(d.INTERMEDIATE),l.uniform2f(z.uniform.px,m,0),g())},pixelate:P=>{let m=P/c.width,u=P/c.height,z=M(V1);!z||(l.uniform2f(z.uniform.size,m,u),g())}};this.add=function(P){let m=Array.prototype.slice.call(arguments,1),u=R[P];s.push({func:u,args:m})},this.reset=function(){s=[]},this.get=function(){return s},this.apply=function(P){f(P.width,P.height),e=0,t||(t=l.createTexture()),l.bindTexture(l.TEXTURE_2D,t),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_WRAP_S,l.CLAMP_TO_EDGE),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_WRAP_T,l.CLAMP_TO_EDGE),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_MIN_FILTER,l.NEAREST),l.texParameteri(l.TEXTURE_2D,l.TEXTURE_MAG_FILTER,l.NEAREST),l.texImage2D(l.TEXTURE_2D,0,l.RGBA,l.RGBA,l.UNSIGNED_BYTE,P);for(let m=0;my.data())),a=.99*Math.max(s[0][0],s[1][0],s[2][0]),i=[A.sub(o[0],n[0]),A.sub(o[1],n[1]),A.sub(o[2],n[2])],c=[A.sub(r[0],n[0]),A.sub(r[1],n[1]),A.sub(r[2],n[2])],x=[A.div(a,c[0]),A.div(a,c[1]),A.div(a,c[2])],d=[A.mul(i[0],x[0]),A.mul(i[1],x[1]),A.mul(i[2],x[2])],l=A.stack([d[0],d[1],d[2]],2),f=A.reshape(l,[1,t.shape[0],t.shape[1],3]);return A.dispose([...o,...n,...r,...i,...c,...x,...d,l,t]),f}var i2=3840,e0=null,t0=null,ge=null,V,F0={inputSum:0,cacheDiff:1,sumMethod:0,inputTensor:void 0};function s0(e,t){let o;if(T.browser)if(T.worker){if(typeof OffscreenCanvas=="undefined")throw new Error("canvas error: attempted to run in web worker but OffscreenCanvas is not supported");o=new OffscreenCanvas(e,t)}else{if(typeof document=="undefined")throw new Error("canvas error: attempted to run in browser but DOM is not defined");o=document.createElement("canvas"),o.width=e,o.height=t}else typeof T.Canvas!="undefined"?o=new T.Canvas(e,t):typeof globalThis.Canvas!="undefined"&&(o=new globalThis.Canvas(e,t));return o}function l2(e,t){let o=t||s0(e.width,e.height);return o.getContext("2d").drawImage(e,0,0),o}async function Me(e,t,o=!0){var f,y;if(!e)return t.debug&&b("input error: input is missing"),{tensor:null,canvas:null};if(!(e instanceof be)&&!(typeof Image!="undefined"&&e instanceof Image)&&!(typeof T.Canvas!="undefined"&&e instanceof T.Canvas)&&!(typeof globalThis.Canvas!="undefined"&&e instanceof globalThis.Canvas)&&!(typeof ImageData!="undefined"&&e instanceof ImageData)&&!(typeof ImageBitmap!="undefined"&&e instanceof ImageBitmap)&&!(typeof HTMLImageElement!="undefined"&&e instanceof HTMLImageElement)&&!(typeof HTMLMediaElement!="undefined"&&e instanceof HTMLMediaElement)&&!(typeof HTMLVideoElement!="undefined"&&e instanceof HTMLVideoElement)&&!(typeof HTMLCanvasElement!="undefined"&&e instanceof HTMLCanvasElement)&&!(typeof OffscreenCanvas!="undefined"&&e instanceof OffscreenCanvas))throw new Error("input error: type is not recognized");if(e instanceof be){let p=null;if(e.isDisposedInternal)throw new Error("input error: attempted to use tensor but it is disposed");if(!e.shape)throw new Error("input error: attempted to use tensor without a shape");if(e.shape.length===3){if(e.shape[2]===3)p=A.expandDims(e,0);else if(e.shape[2]===4){let g=A.slice3d(e,[0,0,0],[-1,-1,3]);p=A.expandDims(g,0),A.dispose(g)}}else e.shape.length===4&&(e.shape[3]===3?p=A.clone(e):e.shape[3]===4&&(p=A.slice4d(e,[0,0,0,0],[-1,-1,-1,3])));if(p==null||p.shape.length!==4||p.shape[0]!==1||p.shape[3]!==3)throw new Error(`input error: attempted to use tensor with unrecognized shape: ${e.shape.toString()}`);if(p.dtype==="int32"){let g=A.cast(p,"float32");A.dispose(p),p=g}return{tensor:p,canvas:t.filter.return?t0:null}}if(typeof e.readyState!="undefined"&&e.readyState<=2)return t.debug&&b("input stream is not ready"),{tensor:null,canvas:e0};let n=e.naturalWidth||e.videoWidth||e.width||e.shape&&e.shape[1]>0,r=e.naturalHeight||e.videoHeight||e.height||e.shape&&e.shape[2]>0;if(!n||!r)return t.debug&&b("cannot determine input dimensions"),{tensor:null,canvas:e0};let s=n,a=r;if(s>i2&&(s=i2,a=Math.trunc(s*r/n)),a>i2&&(a=i2,s=Math.trunc(a*n/r)),(((f=t.filter)==null?void 0:f.width)||0)>0?s=t.filter.width:(((y=t.filter)==null?void 0:y.height)||0)>0&&(s=n*((t.filter.height||0)/r)),(t.filter.height||0)>0?a=t.filter.height:(t.filter.width||0)>0&&(a=r*((t.filter.width||0)/n)),!s||!a)throw new Error("input error: cannot determine dimension");(!e0||e0.width!==s||e0.height!==a)&&(e0=s0(s,a));let i=e0.getContext("2d");if(typeof ImageData!="undefined"&&e instanceof ImageData?i.putImageData(e,0,0):t.filter.flip&&typeof i.translate!="undefined"?(i.translate(n,0),i.scale(-1,1),i.drawImage(e,0,0,n,r,0,0,e0.width,e0.height),i.setTransform(1,0,0,1,0,0)):i.drawImage(e,0,0,n,r,0,0,e0.width,e0.height),(!t0||e0.width!==t0.width||e0.height!==t0.height)&&(t0=s0(e0.width,e0.height)),t.filter.enabled&&T.webgl.supported?(V||(V=T.browser?new q1:null),T.filter=!!V,V!=null&&V.add?(V.reset(),t.filter.brightness!==0&&V.add("brightness",t.filter.brightness),t.filter.contrast!==0&&V.add("contrast",t.filter.contrast),t.filter.sharpness!==0&&V.add("sharpen",t.filter.sharpness),t.filter.blur!==0&&V.add("blur",t.filter.blur),t.filter.saturation!==0&&V.add("saturation",t.filter.saturation),t.filter.hue!==0&&V.add("hue",t.filter.hue),t.filter.negative&&V.add("negative"),t.filter.sepia&&V.add("sepia"),t.filter.vintage&&V.add("brownie"),t.filter.sepia&&V.add("sepia"),t.filter.kodachrome&&V.add("kodachrome"),t.filter.technicolor&&V.add("technicolor"),t.filter.polaroid&&V.add("polaroid"),t.filter.pixelate!==0&&V.add("pixelate",t.filter.pixelate),V.get()>0?t0=V.apply(e0):t0=V.draw(e0)):(t.debug&&b("input process error: cannot initialize filters"),T.webgl.supported=!1,t.filter.enabled=!1,l2(e0,t0))):(l2(e0,t0),V&&(V=null),T.filter=!!V),!o)return{tensor:null,canvas:t0};if(!t0)throw new Error("canvas error: cannot create output");let c,x=3;if(typeof ImageData!="undefined"&&e instanceof ImageData||e.data&&e.width&&e.height)if(T.browser&&A.browser)c=A.browser?A.browser.fromPixels(e):null;else{x=e.data.length/e.height/e.width;let p=new Uint8Array(e.data.buffer);c=A.tensor(p,[e.height,e.width,x],"int32")}else if((!ge||t0.width!==ge.width||t0.height!==ge.height)&&(ge=s0(t0.width,t0.height)),A.browser&&T.browser)t.backend==="webgl"||t.backend==="humangl"||t.backend==="webgpu"?c=A.browser.fromPixels(t0):(ge=l2(t0),c=A.browser.fromPixels(ge));else{let M=l2(t0).getContext("2d").getImageData(0,0,s,a);x=M.data.length/s/a;let R=new Uint8Array(M.data.buffer);c=A.tensor(R,[s,a,x])}if(x===4){let p=A.slice3d(c,[0,0,0],[-1,-1,3]);A.dispose(c),c=p}if(!c)throw new Error("input error: cannot create tensor");let d=A.cast(c,"float32"),l=t.filter.equalization?await a2(d):A.expandDims(d,0);return A.dispose([c,d]),{tensor:l,canvas:t.filter.return?t0:null}}async function U1(e,t){let o=!1;if(e.cacheSensitivity===0||!t.shape||t.shape.length!==4||t.shape[1]>2048||t.shape[2]>2048)return o;if(!F0.inputTensor)F0.inputTensor=A.clone(t);else if(F0.inputTensor.shape[1]!==t.shape[1]||F0.inputTensor.shape[2]!==t.shape[2])A.dispose(F0.inputTensor),F0.inputTensor=A.clone(t);else{let n={};n.diff=A.sub(t,F0.inputTensor),n.squared=A.mul(n.diff,n.diff),n.sum=A.sum(n.squared);let s=(await n.sum.data())[0]/(t.shape[1]||1)/(t.shape[2]||1)/255/3;A.dispose([F0.inputTensor,n.diff,n.squared,n.sum]),F0.inputTensor=A.clone(t),o=s<=(e.cacheSensitivity||0)}return o}async function Y1(e,t,o){let n={};if(!t||!o||t.shape.length!==4||t.shape.length!==o.shape.length)return e.debug||b("invalid input tensor or tensor shapes do not match:",t.shape,o.shape),0;if(t.shape[0]!==1||o.shape[0]!==1||t.shape[3]!==3||o.shape[3]!==3)return e.debug||b("input tensors must be of shape [1, height, width, 3]:",t.shape,o.shape),0;n.input1=A.clone(t),n.input2=t.shape[1]!==o.shape[1]||t.shape[2]!==o.shape[2]?A.image.resizeBilinear(o,[t.shape[1],t.shape[2]]):A.clone(o),n.diff=A.sub(n.input1,n.input2),n.squared=A.mul(n.diff,n.diff),n.sum=A.sum(n.squared);let s=(await n.sum.data())[0]/(t.shape[1]||1)/(t.shape[2]||1)/255/3;return A.dispose([n.input1,n.input2,n.diff,n.squared,n.sum]),s}var o5=class{constructor(){w(this,"browser");w(this,"node");w(this,"worker");w(this,"platform","");w(this,"agent","");w(this,"backends",[]);w(this,"initial");w(this,"filter");w(this,"tfjs");w(this,"offscreen");w(this,"perfadd",!1);w(this,"tensorflow",{version:void 0,gpu:void 0});w(this,"wasm",{supported:void 0,backend:void 0,simd:void 0,multithread:void 0});w(this,"webgl",{supported:void 0,backend:void 0,version:void 0,renderer:void 0});w(this,"webgpu",{supported:void 0,backend:void 0,adapter:void 0});w(this,"cpu",{model:void 0,flags:[]});w(this,"kernels",[]);w(this,"Canvas");w(this,"Image");w(this,"ImageData");if(this.browser=typeof navigator!="undefined",this.node=typeof process!="undefined"&&typeof process.versions!="undefined"&&typeof process.versions.node!="undefined",this.tfjs={version:qe["tfjs-core"]},this.offscreen=typeof OffscreenCanvas!="undefined",this.initial=!0,this.worker=this.browser&&this.offscreen?typeof WorkerGlobalScope!="undefined":void 0,typeof navigator!="undefined"){let t=navigator.userAgent.match(/\(([^()]+)\)/g);if(t!=null&&t[0]){let o=t[0].match(/\(([^()]+)\)/g);this.platform=o!=null&&o[0]?o[0].replace(/\(|\)/g,""):"",this.agent=navigator.userAgent.replace(t[0],""),this.platform[1]&&(this.agent=this.agent.replace(t[1],"")),this.agent=this.agent.replace(/ /g," ")}}else typeof process!="undefined"&&(this.platform=`${process.platform} ${process.arch}`,this.agent=`NodeJS ${process.version}`)}async updateBackend(){this.backends=Object.keys(A.engine().registryFactory),this.tensorflow={version:A.backend().binding?A.backend().binding.TF_Version:void 0,gpu:A.backend().binding?A.backend().binding.isUsingGpuDevice():void 0},this.wasm.supported=typeof WebAssembly!="undefined",this.wasm.backend=this.backends.includes("wasm"),this.wasm.supported&&this.wasm.backend&&A.getBackend()==="wasm"&&(this.wasm.simd=A.env().get("WASM_HAS_SIMD_SUPPORT"),this.wasm.multithread=A.env().get("WASM_HAS_MULTITHREAD_SUPPORT"));let t=s0(100,100),o=t?t.getContext("webgl2"):void 0;if(this.webgl.supported=typeof o!="undefined",this.webgl.backend=this.backends.includes("webgl"),this.webgl.supported&&this.webgl.backend&&(A.getBackend()==="webgl"||A.getBackend()==="humangl")){let n=A.backend().gpgpu!=="undefined"?await A.backend().getGPGPUContext().gl:null;n&&(this.webgl.version=n.getParameter(n.VERSION),this.webgl.renderer=n.getParameter(n.RENDERER))}this.webgpu.supported=this.browser&&typeof navigator.gpu!="undefined",this.webgpu.backend=this.backends.includes("webgpu");try{if(this.webgpu.supported){let n=await navigator.gpu.requestAdapter();this.webgpu.adapter=n?n.name:void 0}}catch(n){this.webgpu.supported=!1}try{this.kernels=A.getKernelsForBackend(A.getBackend()).map(n=>n.kernelName.toLowerCase())}catch(n){}}updateCPU(){let t={model:"",flags:[]};this.node&&this.platform.startsWith("linux"),this.cpu?this.cpu=t:Object.defineProperty(this,"cpu",{value:t})}},T=new o5;var n5={};q0(n5,{age:()=>rn,"anti-spoofing":()=>Ln,antispoof:()=>Xo,blazeface:()=>qo,"blazeface-back":()=>An,"blazeface-front":()=>sn,"blazepose-detect":()=>Nn,"blazepose-detector2d":()=>an,"blazepose-detector3d":()=>ln,"blazepose-full":()=>cn,"blazepose-heavy":()=>xn,"blazepose-lite":()=>yn,default:()=>Kn,efficientpose:()=>dn,"efficientpose-i-lite":()=>Wn,"efficientpose-ii-lite":()=>Fn,"efficientpose-iv":()=>Gn,emotion:()=>Uo,faceboxes:()=>fn,facemesh:()=>Yo,"facemesh-attention":()=>pn,"facemesh-attention-alt":()=>mn,"facemesh-detection-full":()=>un,"facemesh-detection-short":()=>hn,"facemesh-orig":()=>bn,faceres:()=>Ko,"faceres-deep":()=>gn,gear:()=>Mn,gender:()=>vn,"gender-ssrnet-imdb":()=>Rn,handdetect:()=>Pn,"handlandmark-full":()=>Jo,"handlandmark-lite":()=>Tn,"handlandmark-sparse":()=>wn,handskeleton:()=>kn,handtrack:()=>Qo,"insightface-efficientnet-b0":()=>Bn,"insightface-ghostnet-strides1":()=>Hn,"insightface-ghostnet-strides2":()=>Dn,"insightface-mobilenet-emore":()=>Vn,"insightface-mobilenet-swish":()=>Zn,iris:()=>_o,liveness:()=>$o,"mb3-centernet":()=>en,meet:()=>En,mobileface:()=>zn,mobilefacenet:()=>Sn,models:()=>tn,"movenet-lightning":()=>on,"movenet-multipose":()=>Cn,"movenet-thunder":()=>In,nanodet:()=>jn,"nanodet-e":()=>Xn,"nanodet-g":()=>qn,"nanodet-m":()=>Un,"nanodet-t":()=>Yn,posenet:()=>On,selfie:()=>nn});var Xo=853098,qo=538928,Uo=820516,Yo=1477958,Ko=6978814,Jo=5431368,Qo=2964837,_o=2599092,$o=592976,en=4030290,tn=0,on=4650216,nn=212886,rn=161240,An=538928,sn=402048,an=7499400,ln=5928856,cn=6338290,xn=27501554,yn=2725490,dn=5651240,fn=2013002,mn=2387598,pn=2382414,un=1026192,hn=201268,bn=2955780,gn=13957620,Mn=1498916,Rn=161236,vn=201808,Pn=3515612,Tn=2023432,wn=5286322,kn=5502280,En=372228,zn=2183192,Sn=5171976,Cn=9448838,In=12477112,jn=7574558,On=5032780,Nn=5928804,Ln=853098,Wn=2269064,Fn=5651240,Gn=25643252,Bn=13013224,Hn=8093408,Dn=8049584,Vn=6938536,Zn=12168584,Xn=12319156,qn=7574558,Un=1887474,Yn=5294216,Kn={antispoof:Xo,blazeface:qo,emotion:Uo,facemesh:Yo,faceres:Ko,"handlandmark-full":Jo,handtrack:Qo,iris:_o,liveness:$o,"mb3-centernet":en,models:tn,"movenet-lightning":on,selfie:nn,age:rn,"blazeface-back":An,"blazeface-front":sn,"blazepose-detector2d":an,"blazepose-detector3d":ln,"blazepose-full":cn,"blazepose-heavy":xn,"blazepose-lite":yn,efficientpose:dn,faceboxes:fn,"facemesh-attention-alt":mn,"facemesh-attention":pn,"facemesh-detection-full":un,"facemesh-detection-short":hn,"facemesh-orig":bn,"faceres-deep":gn,gear:Mn,"gender-ssrnet-imdb":Rn,gender:vn,handdetect:Pn,"handlandmark-lite":Tn,"handlandmark-sparse":wn,handskeleton:kn,meet:En,mobileface:zn,mobilefacenet:Sn,"movenet-multipose":Cn,"movenet-thunder":In,nanodet:jn,posenet:On,"blazepose-detect":Nn,"anti-spoofing":Ln,"efficientpose-i-lite":Wn,"efficientpose-ii-lite":Fn,"efficientpose-iv":Gn,"insightface-efficientnet-b0":Bn,"insightface-ghostnet-strides1":Hn,"insightface-ghostnet-strides2":Dn,"insightface-mobilenet-emore":Vn,"insightface-mobilenet-swish":Zn,"nanodet-e":Xn,"nanodet-g":qn,"nanodet-m":Un,"nanodet-t":Yn};var J5={};q0(J5,{Models:()=>n2,getModelStats:()=>u1,load:()=>h1,reset:()=>j2,validate:()=>q2,validateModel:()=>Ie});var P0,r5=[],Jn=["white","black","asian","indian","other"],Qn=[15,23,28,35.5,45.5,55.5,65],K1=0,J1=0,A5=Number.MAX_SAFE_INTEGER;async function Q1(e){var t;return T.initial&&(P0=null),P0?e.debug&&b("cached model:",P0.modelUrl):P0=await L((t=e.face.gear)==null?void 0:t.modelPath),P0}async function s5(e,t,o,n){var a,i;if(!P0)return{age:0,gender:"unknown",genderScore:0,race:[]};let r=A5<(((a=t.face.gear)==null?void 0:a.skipFrames)||0),s=(((i=t.face.gear)==null?void 0:i.skipTime)||0)>v()-J1;return t.skipAllowed&&s&&r&&K1===n&&r5[o]?(A5++,r5[o]):(A5=0,new Promise(async c=>{var R,P;if(!(P0!=null&&P0.inputs[0].shape))return;let x={},d=[[0,.1,.9,.9]];x.resize=A.image.cropAndResize(e,d,[0],[P0.inputs[0].shape[2],P0.inputs[0].shape[1]]);let l={age:0,gender:"unknown",genderScore:0,race:[]};(R=t.face.gear)!=null&&R.enabled&&([x.age,x.gender,x.race]=P0.execute(x.resize,["age_output","gender_output","race_output"]));let f=await x.gender.data();l.gender=f[0]>f[1]?"male":"female",l.genderScore=Math.round(100*(f[0]>f[1]?f[0]:f[1]))/100;let y=await x.race.data();for(let m=0;m(((P=t.face.gear)==null?void 0:P.minConfidence)||.2)&&l.race.push({score:Math.round(100*y[m])/100,race:Jn[m]});l.race.sort((m,u)=>u.score-m.score);let g=Array.from(await x.age.data()).map((m,u)=>[Qn[u],m]).sort((m,u)=>u[1]-m[1]),M=g[0][0];for(let m=1;mA.dispose(x[m])),r5[o]=l,K1=n,J1=v(),c(l)}))}var F={tf255:255,tf1:1,tf2:2,tf05:.5,tf127:127.5,rgb:[.2989,.587,.114]};function $1(){F.tf255=A.scalar(255,"float32"),F.tf1=A.scalar(1,"float32"),F.tf2=A.scalar(2,"float32"),F.tf05=A.scalar(.5,"float32"),F.tf127=A.scalar(127.5,"float32"),F.rgb=A.tensor1d([.2989,.587,.114],"float32")}var d0,c2=[],et=0,tt=0,a5=Number.MAX_SAFE_INTEGER;async function ot(e){return T.initial&&(d0=null),d0?e.debug&&b("cached model:",d0.modelUrl):d0=await L(e.face.ssrnet.modelPathAge),d0}async function i5(e,t,o,n){var a,i,c,x;if(!d0)return{age:0};let r=a5<(((a=t.face.ssrnet)==null?void 0:a.skipFrames)||0),s=(((i=t.face.ssrnet)==null?void 0:i.skipTime)||0)>v()-tt;return t.skipAllowed&&r&&s&&et===n&&((c=c2[o])==null?void 0:c.age)&&((x=c2[o])==null?void 0:x.age)>0?(a5++,c2[o]):(a5=0,new Promise(async d=>{var y;if(!(d0!=null&&d0.inputs)||!d0.inputs[0]||!d0.inputs[0].shape)return;let l={};l.resize=A.image.resizeBilinear(e,[d0.inputs[0].shape[2],d0.inputs[0].shape[1]],!1),l.enhance=A.mul(l.resize,F.tf255);let f={age:0};if((y=t.face.ssrnet)!=null&&y.enabled&&(l.age=d0.execute(l.enhance)),l.age){let p=await l.age.data();f.age=Math.trunc(10*p[0])/10}Object.keys(l).forEach(p=>A.dispose(l[p])),c2[o]=f,et=n,tt=v(),d(f)}))}var T0,x2=[],rt=0,At=0,l5=Number.MAX_SAFE_INTEGER,c5=[.2989,.587,.114];async function st(e){var t;return T.initial&&(T0=null),T0?e.debug&&b("cached model:",T0.modelUrl):T0=await L((t=e.face.ssrnet)==null?void 0:t.modelPathGender),T0}async function x5(e,t,o,n){var a,i,c,x;if(!T0)return{gender:"unknown",genderScore:0};let r=l5<(((a=t.face.ssrnet)==null?void 0:a.skipFrames)||0),s=(((i=t.face.ssrnet)==null?void 0:i.skipTime)||0)>v()-At;return t.skipAllowed&&r&&s&&rt===n&&((c=x2[o])==null?void 0:c.gender)&&((x=x2[o])==null?void 0:x.genderScore)>0?(l5++,x2[o]):(l5=0,new Promise(async d=>{var p;if(!(T0!=null&&T0.inputs[0].shape))return;let l={};l.resize=A.image.resizeBilinear(e,[T0.inputs[0].shape[2],T0.inputs[0].shape[1]],!1),l.enhance=A.tidy(()=>{let[g,M,R]=A.split(l.resize,3,3),P=A.mul(g,c5[0]),m=A.mul(M,c5[1]),u=A.mul(R,c5[2]),z=A.addN([P,m,u]);return A.mul(A.sub(z,F.tf05),2)});let f={gender:"unknown",genderScore:0};(p=t.face.ssrnet)!=null&&p.enabled&&(l.gender=T0.execute(l.enhance));let y=await l.gender.data();f.gender=y[0]>y[1]?"female":"male",f.genderScore=y[0]>y[1]?Math.trunc(100*y[0])/100:Math.trunc(100*y[1])/100,Object.keys(l).forEach(g=>A.dispose(l[g])),x2[o]=f,rt=n,At=v(),d(f)}))}var _,y2=[],y5=Number.MAX_SAFE_INTEGER,it=0,lt=0;async function ct(e){var t;return T.initial&&(_=null),_?e.debug&&b("cached model:",_.modelUrl):_=await L((t=e.face.antispoof)==null?void 0:t.modelPath),_}async function d5(e,t,o,n){var a,i;if(!_||!(_!=null&&_.executor))return 0;let r=(((a=t.face.antispoof)==null?void 0:a.skipTime)||0)>v()-lt,s=y5<(((i=t.face.antispoof)==null?void 0:i.skipFrames)||0);return t.skipAllowed&&r&&s&&it===n&&y2[o]?(y5++,y2[o]):(y5=0,new Promise(async c=>{let x=A.image.resizeBilinear(e,[_!=null&&_.inputs[0].shape?_.inputs[0].shape[2]:0,_!=null&&_.inputs[0].shape?_.inputs[0].shape[1]:0],!1),d=_==null?void 0:_.execute(x),l=(await d.data())[0];y2[o]=Math.round(100*l)/100,it=n,lt=v(),A.dispose([x,d]),c(y2[o])}))}var w0={silhouette:[10,338,297,332,284,251,389,356,454,323,361,288,397,365,379,378,400,377,152,148,176,149,150,136,172,58,132,93,234,127,162,21,54,103,67,109],lipsUpperOuter:[185,40,39,37,0,267,269,270,409],lipsLowerOuter:[61,146,91,181,84,17,314,405,321,375,291],lipsUpperInner:[191,80,81,82,13,312,311,310,415],lipsLowerInner:[78,95,88,178,87,14,317,402,318,324,308],lipsLowerSemiOuter:[76,77,90,180,85,16,315,404,320,307,306],lipsUpperSemiOuter:[184,74,73,72,11,302,303,304,408],lipsLowerSemiInner:[62,96,89,179,86,15,316,403,319,325,292],lipsUpperSemiInner:[183,42,41,38,12,268,271,272,407],rightEyeUpper0:[246,161,160,159,158,157,173],rightEyeLower0:[33,7,163,144,145,153,154,155,133],rightEyeUpper1:[247,30,29,27,28,56,190],rightEyeLower1:[130,25,110,24,23,22,26,112,243],rightEyeUpper2:[113,225,224,223,222,221,189],rightEyeLower2:[226,31,228,229,230,231,232,233,244],rightEyeLower3:[143,111,117,118,119,120,121,128,245],rightEyebrowUpper:[156,70,63,105,66,107,55,193],rightEyebrowLower:[35,124,46,53,52,65],rightEyeIris:[473,474,475,476,477],leftEyeUpper0:[466,388,387,386,385,384,398],leftEyeLower0:[263,249,390,373,374,380,381,382,362],leftEyeUpper1:[467,260,259,257,258,286,414],leftEyeLower1:[359,255,339,254,253,252,256,341,463],leftEyeUpper2:[342,445,444,443,442,441,413],leftEyeLower2:[446,261,448,449,450,451,452,453,464],leftEyeLower3:[372,340,346,347,348,349,350,357,465],leftEyebrowUpper:[383,300,293,334,296,336,285,417],leftEyebrowLower:[265,353,276,283,282,295],leftEyeIris:[468,469,470,471,472],midwayBetweenEyes:[168],noseTip:[1],noseBottom:[2],noseRightCorner:[98],noseLeftCorner:[327],rightCheek:[205],leftCheek:[425]},f5={count:468,mouth:13,symmetryLine:[13,w0.midwayBetweenEyes[0]]},se={leftEye:0,rightEye:1,nose:2,mouth:3,leftEar:4,rightEar:5,symmetryLine:[3,2]},m5=[{key:"EyeUpper0",indices:[9,10,11,12,13,14,15]},{key:"EyeUpper1",indices:[25,26,27,28,29,30,31]},{key:"EyeUpper2",indices:[41,42,43,44,45,46,47]},{key:"EyeLower0",indices:[0,1,2,3,4,5,6,7,8]},{key:"EyeLower1",indices:[16,17,18,19,20,21,22,23,24]},{key:"EyeLower2",indices:[32,33,34,35,36,37,38,39,40]},{key:"EyeLower3",indices:[54,55,56,57,58,59,60,61,62]},{key:"EyebrowUpper",indices:[63,64,65,66,67,68,69,70]},{key:"EyebrowLower",indices:[48,49,50,51,52,53]}],Ye=[[.499976992607117,.652534008026123],[.500025987625122,.547487020492554],[.499974012374878,.602371990680695],[.482113003730774,.471979022026062],[.500150978565216,.527155995368958],[.499909996986389,.498252987861633],[.499523013830185,.40106201171875],[.289712011814117,.380764007568359],[.499954998493195,.312398016452789],[.499987006187439,.269918978214264],[.500023007392883,.107050001621246],[.500023007392883,.666234016418457],[.5000159740448,.679224014282227],[.500023007392883,.692348003387451],[.499976992607117,.695277988910675],[.499976992607117,.70593398809433],[.499976992607117,.719385027885437],[.499976992607117,.737019002437592],[.499967992305756,.781370997428894],[.499816000461578,.562981009483337],[.473773002624512,.573909997940063],[.104906998574734,.254140973091125],[.365929991006851,.409575998783112],[.338757991790771,.41302502155304],[.311120003461838,.409460008144379],[.274657994508743,.389131009578705],[.393361985683441,.403706014156342],[.345234006643295,.344011008739471],[.370094001293182,.346076011657715],[.319321990013123,.347265005111694],[.297903001308441,.353591024875641],[.24779200553894,.410809993743896],[.396889001131058,.842755019664764],[.280097991228104,.375599980354309],[.106310002505779,.399955987930298],[.2099249958992,.391353011131287],[.355807989835739,.534406006336212],[.471751004457474,.65040397644043],[.474155008792877,.680191993713379],[.439785003662109,.657229006290436],[.414617002010345,.66654098033905],[.450374007225037,.680860996246338],[.428770989179611,.682690978050232],[.374971002340317,.727805018424988],[.486716985702515,.547628998756409],[.485300987958908,.527395009994507],[.257764995098114,.314490020275116],[.401223003864288,.455172002315521],[.429818987846375,.548614978790283],[.421351999044418,.533740997314453],[.276895999908447,.532056987285614],[.483370006084442,.499586999416351],[.33721199631691,.282882988452911],[.296391993761063,.293242990970612],[.169294998049736,.193813979625702],[.447580009698868,.302609980106354],[.392390012741089,.353887975215912],[.354490011930466,.696784019470215],[.067304998636246,.730105042457581],[.442739009857178,.572826027870178],[.457098007202148,.584792017936707],[.381974011659622,.694710969924927],[.392388999462128,.694203019142151],[.277076005935669,.271932005882263],[.422551989555359,.563233017921448],[.385919004678726,.281364023685455],[.383103013038635,.255840003490448],[.331431001424789,.119714021682739],[.229923993349075,.232002973556519],[.364500999450684,.189113974571228],[.229622006416321,.299540996551514],[.173287004232407,.278747975826263],[.472878992557526,.666198015213013],[.446828007698059,.668527007102966],[.422762006521225,.673889994621277],[.445307999849319,.580065965652466],[.388103008270264,.693961024284363],[.403039008378983,.706539988517761],[.403629004955292,.693953037261963],[.460041999816895,.557139039039612],[.431158006191254,.692366003990173],[.452181994915009,.692366003990173],[.475387006998062,.692366003990173],[.465828001499176,.779190003871918],[.472328990697861,.736225962638855],[.473087012767792,.717857003211975],[.473122000694275,.704625964164734],[.473033010959625,.695277988910675],[.427942007780075,.695277988910675],[.426479011774063,.703539967536926],[.423162013292313,.711845993995667],[.4183090031147,.720062971115112],[.390094995498657,.639572978019714],[.013953999616206,.560034036636353],[.499913990497589,.58014702796936],[.413199990987778,.69539999961853],[.409626007080078,.701822996139526],[.468080013990402,.601534962654114],[.422728985548019,.585985004901886],[.463079988956451,.593783974647522],[.37211999297142,.47341400384903],[.334562003612518,.496073007583618],[.411671012639999,.546965003013611],[.242175996303558,.14767599105835],[.290776997804642,.201445996761322],[.327338010072708,.256527006626129],[.399509996175766,.748921036720276],[.441727995872498,.261676013469696],[.429764986038208,.187834024429321],[.412198007106781,.108901023864746],[.288955003023148,.398952007293701],[.218936994671822,.435410976409912],[.41278201341629,.398970007896423],[.257135003805161,.355440020561218],[.427684992551804,.437960982322693],[.448339998722076,.536936044692993],[.178560003638268,.45755398273468],[.247308000922203,.457193970680237],[.286267012357712,.467674970626831],[.332827985286713,.460712015628815],[.368755996227264,.447206974029541],[.398963987827301,.432654976844788],[.476410001516342,.405806005001068],[.189241006970406,.523923993110657],[.228962004184723,.348950982093811],[.490725994110107,.562400996685028],[.404670000076294,.485132992267609],[.019469000399113,.401564002037048],[.426243007183075,.420431017875671],[.396993011236191,.548797011375427],[.266469985246658,.376977026462555],[.439121007919312,.51895797252655],[.032313998788595,.644356966018677],[.419054001569748,.387154996395111],[.462783008813858,.505746960639954],[.238978996872902,.779744982719421],[.198220998048782,.831938028335571],[.107550002634525,.540755033493042],[.183610007166862,.740257024765015],[.134409993886948,.333683013916016],[.385764002799988,.883153975009918],[.490967005491257,.579378008842468],[.382384985685349,.508572995662689],[.174399003386497,.397670984268188],[.318785011768341,.39623498916626],[.343364000320435,.400596976280212],[.396100014448166,.710216999053955],[.187885001301765,.588537991046906],[.430987000465393,.944064974784851],[.318993002176285,.898285031318665],[.266247987747192,.869701027870178],[.500023007392883,.190576016902924],[.499976992607117,.954452991485596],[.366169989109039,.398822009563446],[.393207013607025,.39553701877594],[.410373002290726,.391080021858215],[.194993004202843,.342101991176605],[.388664990663528,.362284004688263],[.365961998701096,.355970978736877],[.343364000320435,.355356991291046],[.318785011768341,.35834002494812],[.301414996385574,.363156020641327],[.058132998645306,.319076001644135],[.301414996385574,.387449026107788],[.499987989664078,.618434011936188],[.415838003158569,.624195992946625],[.445681989192963,.566076993942261],[.465844005346298,.620640993118286],[.49992299079895,.351523995399475],[.288718998432159,.819945991039276],[.335278987884521,.852819979190826],[.440512001514435,.902418971061707],[.128294005990028,.791940987110138],[.408771991729736,.373893976211548],[.455606997013092,.451801002025604],[.499877005815506,.908990025520325],[.375436991453171,.924192011356354],[.11421000212431,.615022003650665],[.448662012815475,.695277988910675],[.4480200111866,.704632043838501],[.447111994028091,.715808033943176],[.444831997156143,.730794012546539],[.430011987686157,.766808986663818],[.406787008047104,.685672998428345],[.400738000869751,.681069016456604],[.392399996519089,.677703022956848],[.367855995893478,.663918972015381],[.247923001646996,.601333022117615],[.452769994735718,.420849978923798],[.43639200925827,.359887003898621],[.416164010763168,.368713974952698],[.413385987281799,.692366003990173],[.228018000721931,.683571994304657],[.468268007040024,.352671027183533],[.411361992359161,.804327011108398],[.499989002943039,.469825029373169],[.479153990745544,.442654013633728],[.499974012374878,.439637005329132],[.432112008333206,.493588984012604],[.499886006116867,.866917014122009],[.49991300702095,.821729004383087],[.456548988819122,.819200992584229],[.344549000263214,.745438992977142],[.37890899181366,.574010014533997],[.374292999505997,.780184984207153],[.319687992334366,.570737957954407],[.357154995203018,.604269981384277],[.295284003019333,.621580958366394],[.447750002145767,.862477004528046],[.410986006259918,.508723020553589],[.31395098567009,.775308012962341],[.354128003120422,.812552988529205],[.324548006057739,.703992962837219],[.189096003770828,.646299958229065],[.279776990413666,.71465802192688],[.1338230073452,.682700991630554],[.336768001317978,.644733011722565],[.429883986711502,.466521978378296],[.455527991056442,.548622965812683],[.437114000320435,.558896005153656],[.467287987470627,.529924988746643],[.414712011814117,.335219979286194],[.37704598903656,.322777986526489],[.344107985496521,.320150971412659],[.312875986099243,.32233202457428],[.283526003360748,.333190023899078],[.241245999932289,.382785975933075],[.102986000478268,.468762993812561],[.267612010240555,.424560010433197],[.297879010438919,.433175981044769],[.333433985710144,.433878004550934],[.366427004337311,.426115989685059],[.396012008190155,.416696012020111],[.420121014118195,.41022801399231],[.007561000064015,.480777025222778],[.432949006557465,.569517970085144],[.458638995885849,.479089021682739],[.473466008901596,.545744001865387],[.476087987422943,.563830018043518],[.468472003936768,.555056989192963],[.433990985155106,.582361996173859],[.483518004417419,.562983989715576],[.482482999563217,.57784903049469],[.42645001411438,.389798998832703],[.438998997211456,.39649498462677],[.450067013502121,.400434017181396],[.289712011814117,.368252992630005],[.276670008897781,.363372981548309],[.517862021923065,.471948027610779],[.710287988185883,.380764007568359],[.526226997375488,.573909997940063],[.895093023777008,.254140973091125],[.634069979190826,.409575998783112],[.661242008209229,.41302502155304],[.688880026340485,.409460008144379],[.725341975688934,.389131009578705],[.606630027294159,.40370500087738],[.654766023159027,.344011008739471],[.629905998706818,.346076011657715],[.680678009986877,.347265005111694],[.702096998691559,.353591024875641],[.75221198797226,.410804986953735],[.602918028831482,.842862963676453],[.719901978969574,.375599980354309],[.893692970275879,.399959981441498],[.790081977844238,.391354024410248],[.643998026847839,.534487962722778],[.528249025344849,.65040397644043],[.525849997997284,.680191040039062],[.560214996337891,.657229006290436],[.585384011268616,.66654098033905],[.549625992774963,.680860996246338],[.57122802734375,.682691991329193],[.624852001667023,.72809898853302],[.513050019741058,.547281980514526],[.51509702205658,.527251958847046],[.742246985435486,.314507007598877],[.598631024360657,.454979002475739],[.570338010787964,.548575043678284],[.578631997108459,.533622980117798],[.723087012767792,.532054007053375],[.516445994377136,.499638974666595],[.662801027297974,.282917976379395],[.70362401008606,.293271005153656],[.830704987049103,.193813979625702],[.552385985851288,.302568018436432],[.607609987258911,.353887975215912],[.645429015159607,.696707010269165],[.932694971561432,.730105042457581],[.557260990142822,.572826027870178],[.542901992797852,.584792017936707],[.6180260181427,.694710969924927],[.607590973377228,.694203019142151],[.722943007946014,.271963000297546],[.577413976192474,.563166975975037],[.614082992076874,.281386971473694],[.616907000541687,.255886018276215],[.668509006500244,.119913995265961],[.770092010498047,.232020974159241],[.635536015033722,.189248979091644],[.77039098739624,.299556016921997],[.826722025871277,.278755009174347],[.527121007442474,.666198015213013],[.553171992301941,.668527007102966],[.577238023281097,.673889994621277],[.554691970348358,.580065965652466],[.611896991729736,.693961024284363],[.59696102142334,.706539988517761],[.596370995044708,.693953037261963],[.539958000183105,.557139039039612],[.568841993808746,.692366003990173],[.547818005084991,.692366003990173],[.52461302280426,.692366003990173],[.534089982509613,.779141008853912],[.527670979499817,.736225962638855],[.526912987232208,.717857003211975],[.526877999305725,.704625964164734],[.526966989040375,.695277988910675],[.572058022022247,.695277988910675],[.573521018028259,.703539967536926],[.57683801651001,.711845993995667],[.581691026687622,.720062971115112],[.609944999217987,.639909982681274],[.986046016216278,.560034036636353],[.5867999792099,.69539999961853],[.590372025966644,.701822996139526],[.531915009021759,.601536989212036],[.577268004417419,.585934996604919],[.536915004253387,.593786001205444],[.627542972564697,.473352015018463],[.665585994720459,.495950996875763],[.588353991508484,.546862006187439],[.757824003696442,.14767599105835],[.709249973297119,.201507985591888],[.672684013843536,.256581008434296],[.600408971309662,.74900496006012],[.55826598405838,.261672019958496],[.570303976535797,.187870979309082],[.588165998458862,.109044015407562],[.711045026779175,.398952007293701],[.781069993972778,.435405015945435],[.587247014045715,.398931980133057],[.742869973182678,.355445981025696],[.572156012058258,.437651991844177],[.55186802148819,.536570012569427],[.821442008018494,.457556009292603],[.752701997756958,.457181990146637],[.71375697851181,.467626988887787],[.66711300611496,.460672974586487],[.631101012229919,.447153985500336],[.6008620262146,.432473003864288],[.523481011390686,.405627012252808],[.810747981071472,.523926019668579],[.771045982837677,.348959028720856],[.509127020835876,.562718033790588],[.595292985439301,.485023975372314],[.980530977249146,.401564002037048],[.573499977588654,.420000016689301],[.602994978427887,.548687994480133],[.733529984951019,.376977026462555],[.560611009597778,.519016981124878],[.967685997486115,.644356966018677],[.580985009670258,.387160003185272],[.537728011608124,.505385041236877],[.760966002941132,.779752969741821],[.801778972148895,.831938028335571],[.892440974712372,.54076099395752],[.816350996494293,.740260004997253],[.865594983100891,.333687007427216],[.614073991775513,.883246004581451],[.508952975273132,.579437971115112],[.617941975593567,.508316040039062],[.825608015060425,.397674977779388],[.681214988231659,.39623498916626],[.656635999679565,.400596976280212],[.603900015354156,.710216999053955],[.81208598613739,.588539004325867],[.56801301240921,.944564998149872],[.681007981300354,.898285031318665],[.733752012252808,.869701027870178],[.633830010890961,.398822009563446],[.606792986392975,.39553701877594],[.589659988880157,.391062021255493],[.805015981197357,.342108011245728],[.611334979534149,.362284004688263],[.634037971496582,.355970978736877],[.656635999679565,.355356991291046],[.681214988231659,.35834002494812],[.698584973812103,.363156020641327],[.941866993904114,.319076001644135],[.698584973812103,.387449026107788],[.584177017211914,.624107003211975],[.554318010807037,.566076993942261],[.534153997898102,.62064003944397],[.711217999458313,.819975018501282],[.664629995822906,.852871000766754],[.559099972248077,.902631998062134],[.871706008911133,.791940987110138],[.591234028339386,.373893976211548],[.544341027736664,.451583981513977],[.624562978744507,.924192011356354],[.88577002286911,.615028977394104],[.551338016986847,.695277988910675],[.551980018615723,.704632043838501],[.552887976169586,.715808033943176],[.555167973041534,.730794012546539],[.569944024085999,.767035007476807],[.593203008174896,.685675978660583],[.599261999130249,.681069016456604],[.607599973678589,.677703022956848],[.631937980651855,.663500010967255],[.752032995223999,.601315021514893],[.547226011753082,.420395016670227],[.563543975353241,.359827995300293],[.583841025829315,.368713974952698],[.586614012718201,.692366003990173],[.771915018558502,.683578014373779],[.531597018241882,.352482974529266],[.588370978832245,.804440975189209],[.52079701423645,.442565023899078],[.567984998226166,.493479013442993],[.543282985687256,.819254994392395],[.655317008495331,.745514988899231],[.621008992195129,.574018001556396],[.625559985637665,.78031200170517],[.680198013782501,.570719003677368],[.64276397228241,.604337990283966],[.704662978649139,.621529996395111],[.552012026309967,.862591981887817],[.589071989059448,.508637011051178],[.685944974422455,.775357007980347],[.645735025405884,.812640011310577],[.675342977046967,.703978002071381],[.810858011245728,.646304965019226],[.72012197971344,.714666962623596],[.866151988506317,.682704985141754],[.663187026977539,.644596993923187],[.570082008838654,.466325998306274],[.544561982154846,.548375964164734],[.562758982181549,.558784961700439],[.531987011432648,.530140042304993],[.585271000862122,.335177004337311],[.622952997684479,.32277899980545],[.655896008014679,.320163011550903],[.687132000923157,.322345972061157],[.716481983661652,.333200991153717],[.758756995201111,.382786989212036],[.897013008594513,.468769013881683],[.732392013072968,.424547016620636],[.70211398601532,.433162987232208],[.66652500629425,.433866024017334],[.633504986763,.426087975502014],[.603875994682312,.416586995124817],[.579657971858978,.409945011138916],[.992439985275269,.480777025222778],[.567192018032074,.569419980049133],[.54136598110199,.478899002075195],[.526564002037048,.546118021011353],[.523913025856018,.563830018043518],[.531529009342194,.555056989192963],[.566035985946655,.582329034805298],[.51631098985672,.563053965568542],[.5174720287323,.577877044677734],[.573594987392426,.389806985855103],[.560697972774506,.395331978797913],[.549755990505219,.399751007556915],[.710287988185883,.368252992630005],[.723330020904541,.363372981548309]],ae=[127,34,139,11,0,37,232,231,120,72,37,39,128,121,47,232,121,128,104,69,67,175,171,148,157,154,155,118,50,101,73,39,40,9,151,108,48,115,131,194,204,211,74,40,185,80,42,183,40,92,186,230,229,118,202,212,214,83,18,17,76,61,146,160,29,30,56,157,173,106,204,194,135,214,192,203,165,98,21,71,68,51,45,4,144,24,23,77,146,91,205,50,187,201,200,18,91,106,182,90,91,181,85,84,17,206,203,36,148,171,140,92,40,39,193,189,244,159,158,28,247,246,161,236,3,196,54,68,104,193,168,8,117,228,31,189,193,55,98,97,99,126,47,100,166,79,218,155,154,26,209,49,131,135,136,150,47,126,217,223,52,53,45,51,134,211,170,140,67,69,108,43,106,91,230,119,120,226,130,247,63,53,52,238,20,242,46,70,156,78,62,96,46,53,63,143,34,227,173,155,133,123,117,111,44,125,19,236,134,51,216,206,205,154,153,22,39,37,167,200,201,208,36,142,100,57,212,202,20,60,99,28,158,157,35,226,113,160,159,27,204,202,210,113,225,46,43,202,204,62,76,77,137,123,116,41,38,72,203,129,142,64,98,240,49,102,64,41,73,74,212,216,207,42,74,184,169,170,211,170,149,176,105,66,69,122,6,168,123,147,187,96,77,90,65,55,107,89,90,180,101,100,120,63,105,104,93,137,227,15,86,85,129,102,49,14,87,86,55,8,9,100,47,121,145,23,22,88,89,179,6,122,196,88,95,96,138,172,136,215,58,172,115,48,219,42,80,81,195,3,51,43,146,61,171,175,199,81,82,38,53,46,225,144,163,110,246,33,7,52,65,66,229,228,117,34,127,234,107,108,69,109,108,151,48,64,235,62,78,191,129,209,126,111,35,143,163,161,246,117,123,50,222,65,52,19,125,141,221,55,65,3,195,197,25,7,33,220,237,44,70,71,139,122,193,245,247,130,33,71,21,162,153,158,159,170,169,150,188,174,196,216,186,92,144,160,161,2,97,167,141,125,241,164,167,37,72,38,12,145,159,160,38,82,13,63,68,71,226,35,111,158,153,154,101,50,205,206,92,165,209,198,217,165,167,97,220,115,218,133,112,243,239,238,241,214,135,169,190,173,133,171,208,32,125,44,237,86,87,178,85,86,179,84,85,180,83,84,181,201,83,182,137,93,132,76,62,183,61,76,184,57,61,185,212,57,186,214,207,187,34,143,156,79,239,237,123,137,177,44,1,4,201,194,32,64,102,129,213,215,138,59,166,219,242,99,97,2,94,141,75,59,235,24,110,228,25,130,226,23,24,229,22,23,230,26,22,231,112,26,232,189,190,243,221,56,190,28,56,221,27,28,222,29,27,223,30,29,224,247,30,225,238,79,20,166,59,75,60,75,240,147,177,215,20,79,166,187,147,213,112,233,244,233,128,245,128,114,188,114,217,174,131,115,220,217,198,236,198,131,134,177,132,58,143,35,124,110,163,7,228,110,25,356,389,368,11,302,267,452,350,349,302,303,269,357,343,277,452,453,357,333,332,297,175,152,377,384,398,382,347,348,330,303,304,270,9,336,337,278,279,360,418,262,431,304,408,409,310,415,407,270,409,410,450,348,347,422,430,434,313,314,17,306,307,375,387,388,260,286,414,398,335,406,418,364,367,416,423,358,327,251,284,298,281,5,4,373,374,253,307,320,321,425,427,411,421,313,18,321,405,406,320,404,405,315,16,17,426,425,266,377,400,369,322,391,269,417,465,464,386,257,258,466,260,388,456,399,419,284,332,333,417,285,8,346,340,261,413,441,285,327,460,328,355,371,329,392,439,438,382,341,256,429,420,360,364,394,379,277,343,437,443,444,283,275,440,363,431,262,369,297,338,337,273,375,321,450,451,349,446,342,467,293,334,282,458,461,462,276,353,383,308,324,325,276,300,293,372,345,447,382,398,362,352,345,340,274,1,19,456,248,281,436,427,425,381,256,252,269,391,393,200,199,428,266,330,329,287,273,422,250,462,328,258,286,384,265,353,342,387,259,257,424,431,430,342,353,276,273,335,424,292,325,307,366,447,345,271,303,302,423,266,371,294,455,460,279,278,294,271,272,304,432,434,427,272,407,408,394,430,431,395,369,400,334,333,299,351,417,168,352,280,411,325,319,320,295,296,336,319,403,404,330,348,349,293,298,333,323,454,447,15,16,315,358,429,279,14,15,316,285,336,9,329,349,350,374,380,252,318,402,403,6,197,419,318,319,325,367,364,365,435,367,397,344,438,439,272,271,311,195,5,281,273,287,291,396,428,199,311,271,268,283,444,445,373,254,339,263,466,249,282,334,296,449,347,346,264,447,454,336,296,299,338,10,151,278,439,455,292,407,415,358,371,355,340,345,372,390,249,466,346,347,280,442,443,282,19,94,370,441,442,295,248,419,197,263,255,359,440,275,274,300,383,368,351,412,465,263,467,466,301,368,389,380,374,386,395,378,379,412,351,419,436,426,322,373,390,388,2,164,393,370,462,461,164,0,267,302,11,12,374,373,387,268,12,13,293,300,301,446,261,340,385,384,381,330,266,425,426,423,391,429,355,437,391,327,326,440,457,438,341,382,362,459,457,461,434,430,394,414,463,362,396,369,262,354,461,457,316,403,402,315,404,403,314,405,404,313,406,405,421,418,406,366,401,361,306,408,407,291,409,408,287,410,409,432,436,410,434,416,411,264,368,383,309,438,457,352,376,401,274,275,4,421,428,262,294,327,358,433,416,367,289,455,439,462,370,326,2,326,370,305,460,455,254,449,448,255,261,446,253,450,449,252,451,450,256,452,451,341,453,452,413,464,463,441,413,414,258,442,441,257,443,442,259,444,443,260,445,444,467,342,445,459,458,250,289,392,290,290,328,460,376,433,435,250,290,392,411,416,433,341,463,464,453,464,465,357,465,412,343,412,399,360,363,440,437,399,456,420,456,363,401,435,288,372,383,353,339,255,249,448,261,255,133,243,190,133,155,112,33,246,247,33,130,25,398,384,286,362,398,414,362,463,341,263,359,467,263,249,255,466,467,260,75,60,166,238,239,79,162,127,139,72,11,37,121,232,120,73,72,39,114,128,47,233,232,128,103,104,67,152,175,148,173,157,155,119,118,101,74,73,40,107,9,108,49,48,131,32,194,211,184,74,185,191,80,183,185,40,186,119,230,118,210,202,214,84,83,17,77,76,146,161,160,30,190,56,173,182,106,194,138,135,192,129,203,98,54,21,68,5,51,4,145,144,23,90,77,91,207,205,187,83,201,18,181,91,182,180,90,181,16,85,17,205,206,36,176,148,140,165,92,39,245,193,244,27,159,28,30,247,161,174,236,196,103,54,104,55,193,8,111,117,31,221,189,55,240,98,99,142,126,100,219,166,218,112,155,26,198,209,131,169,135,150,114,47,217,224,223,53,220,45,134,32,211,140,109,67,108,146,43,91,231,230,120,113,226,247,105,63,52,241,238,242,124,46,156,95,78,96,70,46,63,116,143,227,116,123,111,1,44,19,3,236,51,207,216,205,26,154,22,165,39,167,199,200,208,101,36,100,43,57,202,242,20,99,56,28,157,124,35,113,29,160,27,211,204,210,124,113,46,106,43,204,96,62,77,227,137,116,73,41,72,36,203,142,235,64,240,48,49,64,42,41,74,214,212,207,183,42,184,210,169,211,140,170,176,104,105,69,193,122,168,50,123,187,89,96,90,66,65,107,179,89,180,119,101,120,68,63,104,234,93,227,16,15,85,209,129,49,15,14,86,107,55,9,120,100,121,153,145,22,178,88,179,197,6,196,89,88,96,135,138,136,138,215,172,218,115,219,41,42,81,5,195,51,57,43,61,208,171,199,41,81,38,224,53,225,24,144,110,105,52,66,118,229,117,227,34,234,66,107,69,10,109,151,219,48,235,183,62,191,142,129,126,116,111,143,7,163,246,118,117,50,223,222,52,94,19,141,222,221,65,196,3,197,45,220,44,156,70,139,188,122,245,139,71,162,145,153,159,149,170,150,122,188,196,206,216,92,163,144,161,164,2,167,242,141,241,0,164,37,11,72,12,144,145,160,12,38,13,70,63,71,31,226,111,157,158,154,36,101,205,203,206,165,126,209,217,98,165,97,237,220,218,237,239,241,210,214,169,140,171,32,241,125,237,179,86,178,180,85,179,181,84,180,182,83,181,194,201,182,177,137,132,184,76,183,185,61,184,186,57,185,216,212,186,192,214,187,139,34,156,218,79,237,147,123,177,45,44,4,208,201,32,98,64,129,192,213,138,235,59,219,141,242,97,97,2,141,240,75,235,229,24,228,31,25,226,230,23,229,231,22,230,232,26,231,233,112,232,244,189,243,189,221,190,222,28,221,223,27,222,224,29,223,225,30,224,113,247,225,99,60,240,213,147,215,60,20,166,192,187,213,243,112,244,244,233,245,245,128,188,188,114,174,134,131,220,174,217,236,236,198,134,215,177,58,156,143,124,25,110,7,31,228,25,264,356,368,0,11,267,451,452,349,267,302,269,350,357,277,350,452,357,299,333,297,396,175,377,381,384,382,280,347,330,269,303,270,151,9,337,344,278,360,424,418,431,270,304,409,272,310,407,322,270,410,449,450,347,432,422,434,18,313,17,291,306,375,259,387,260,424,335,418,434,364,416,391,423,327,301,251,298,275,281,4,254,373,253,375,307,321,280,425,411,200,421,18,335,321,406,321,320,405,314,315,17,423,426,266,396,377,369,270,322,269,413,417,464,385,386,258,248,456,419,298,284,333,168,417,8,448,346,261,417,413,285,326,327,328,277,355,329,309,392,438,381,382,256,279,429,360,365,364,379,355,277,437,282,443,283,281,275,363,395,431,369,299,297,337,335,273,321,348,450,349,359,446,467,283,293,282,250,458,462,300,276,383,292,308,325,283,276,293,264,372,447,346,352,340,354,274,19,363,456,281,426,436,425,380,381,252,267,269,393,421,200,428,371,266,329,432,287,422,290,250,328,385,258,384,446,265,342,386,387,257,422,424,430,445,342,276,422,273,424,306,292,307,352,366,345,268,271,302,358,423,371,327,294,460,331,279,294,303,271,304,436,432,427,304,272,408,395,394,431,378,395,400,296,334,299,6,351,168,376,352,411,307,325,320,285,295,336,320,319,404,329,330,349,334,293,333,366,323,447,316,15,315,331,358,279,317,14,316,8,285,9,277,329,350,253,374,252,319,318,403,351,6,419,324,318,325,397,367,365,288,435,397,278,344,439,310,272,311,248,195,281,375,273,291,175,396,199,312,311,268,276,283,445,390,373,339,295,282,296,448,449,346,356,264,454,337,336,299,337,338,151,294,278,455,308,292,415,429,358,355,265,340,372,388,390,466,352,346,280,295,442,282,354,19,370,285,441,295,195,248,197,457,440,274,301,300,368,417,351,465,251,301,389,385,380,386,394,395,379,399,412,419,410,436,322,387,373,388,326,2,393,354,370,461,393,164,267,268,302,12,386,374,387,312,268,13,298,293,301,265,446,340,380,385,381,280,330,425,322,426,391,420,429,437,393,391,326,344,440,438,458,459,461,364,434,394,428,396,262,274,354,457,317,316,402,316,315,403,315,314,404,314,313,405,313,421,406,323,366,361,292,306,407,306,291,408,291,287,409,287,432,410,427,434,411,372,264,383,459,309,457,366,352,401,1,274,4,418,421,262,331,294,358,435,433,367,392,289,439,328,462,326,94,2,370,289,305,455,339,254,448,359,255,446,254,253,449,253,252,450,252,256,451,256,341,452,414,413,463,286,441,414,286,258,441,258,257,442,257,259,443,259,260,444,260,467,445,309,459,250,305,289,290,305,290,460,401,376,435,309,250,392,376,411,433,453,341,464,357,453,465,343,357,412,437,343,399,344,360,440,420,437,456,360,420,363,361,401,288,265,372,353,390,339,249,339,448,255];var $n=[127,234,132,58,172,150,149,148,152,377,378,379,397,288,361,454,356,70,63,105,66,107,336,296,334,293,300,168,6,195,4,98,97,2,326,327,33,160,158,133,153,144,362,385,387,263,373,380,57,40,37,0,267,270,287,321,314,17,84,91,78,81,13,311,308,402,14,178],er=[33,133,362,263,1,62,308,159,145,386,374,6,102,331,2,13,14,70,105,107,336,334,300,54,10,284,50,280,234,454,58,288,152],tr=[33,133,362,263,1,78,308],M7=$n.map(e=>Ye[e]),R7=er.map(e=>Ye[e]),v7=tr.map(e=>Ye[e]);function U0(e){let t=e.map(o=>o[0]);return t.push(e[e.length-1][1]),t}var or=[[61,146],[146,91],[91,181],[181,84],[84,17],[17,314],[314,405],[405,321],[321,375],[375,291],[61,185],[185,40],[40,39],[39,37],[37,0],[0,267],[267,269],[269,270],[270,409],[409,291],[78,95],[95,88],[88,178],[178,87],[87,14],[14,317],[317,402],[402,318],[318,324],[324,308],[78,191],[191,80],[80,81],[81,82],[82,13],[13,312],[312,311],[311,310],[310,415],[415,308]],nr=[[263,249],[249,390],[390,373],[373,374],[374,380],[380,381],[381,382],[382,362],[263,466],[466,388],[388,387],[387,386],[386,385],[385,384],[384,398],[398,362]],rr=[[276,283],[283,282],[282,295],[295,285],[300,293],[293,334],[334,296],[296,336]],Ar=[[474,475],[475,476],[476,477],[477,474]],sr=[[33,7],[7,163],[163,144],[144,145],[145,153],[153,154],[154,155],[155,133],[33,246],[246,161],[161,160],[160,159],[159,158],[158,157],[157,173],[173,133]],ar=[[46,53],[53,52],[52,65],[65,55],[70,63],[63,105],[105,66],[66,107]],ir=[[469,470],[470,471],[471,472],[472,469]],lr=[[10,338],[338,297],[297,332],[332,284],[284,251],[251,389],[389,356],[356,454],[454,323],[323,361],[361,288],[288,397],[397,365],[365,379],[379,378],[378,400],[400,377],[377,152],[152,148],[148,176],[176,149],[149,150],[150,136],[136,172],[172,58],[58,132],[132,93],[93,234],[234,127],[127,162],[162,21],[21,54],[54,103],[103,67],[67,109],[109,10]],P7={lips:U0(or),leftEye:U0(nr),leftEyebrow:U0(rr),leftIris:U0(Ar),rightEye:U0(sr),rightEyebrow:U0(ar),rightIris:U0(ir),faceOval:U0(lr)};var Re=e=>[Math.abs(e.endPoint[0]-e.startPoint[0]),Math.abs(e.endPoint[1]-e.startPoint[1])],d2=e=>[e.startPoint[0]+(e.endPoint[0]-e.startPoint[0])/2,e.startPoint[1]+(e.endPoint[1]-e.startPoint[1])/2,1],f2=(e,t)=>e?[Math.trunc(Math.max(0,e.startPoint[0])),Math.trunc(Math.max(0,e.startPoint[1])),Math.trunc(Math.min(t.shape[2]||0,e.endPoint[0])-Math.max(0,e.startPoint[0])),Math.trunc(Math.min(t.shape[1]||0,e.endPoint[1])-Math.max(0,e.startPoint[1]))]:[0,0,0,0],m2=(e,t)=>e?[e.startPoint[0]/(t.shape[2]||0),e.startPoint[1]/(t.shape[1]||0),(e.endPoint[0]-e.startPoint[0])/(t.shape[2]||0),(e.endPoint[1]-e.startPoint[1])/(t.shape[1]||0)]:[0,0,0,0],ft=(e,t)=>{let o=[e.startPoint[0]*t[0],e.startPoint[1]*t[1]],n=[e.endPoint[0]*t[0],e.endPoint[1]*t[1]];return{startPoint:o,endPoint:n,landmarks:e.landmarks,confidence:e.confidence}},u5=(e,t,o)=>{let n=t.shape[1],r=t.shape[2],s=[e.startPoint[1]/n,e.startPoint[0]/r,e.endPoint[1]/n,e.endPoint[0]/r],a=A.image.cropAndResize(t,[s],[0],o),i=A.div(a,F.tf255);return A.dispose(a),i},p2=(e,t)=>{let o=d2(e),n=Re(e),r=[t*n[0]/2,t*n[1]/2];return{startPoint:[o[0]-r[0],o[1]-r[1]],endPoint:[o[0]+r[0],o[1]+r[1]],landmarks:e.landmarks,confidence:e.confidence}},u2=e=>{let t=d2(e),o=Re(e),n=Math.max(...o)/2;return{startPoint:[Math.round(t[0]-n),Math.round(t[1]-n)],endPoint:[Math.round(t[0]+n),Math.round(t[1]+n)],landmarks:e.landmarks,confidence:e.confidence}},mt=e=>{let t=e.map(n=>n[0]),o=e.map(n=>n[1]);return{startPoint:[Math.min(...t),Math.min(...o)],endPoint:[Math.max(...t),Math.max(...o)],landmarks:e}},h5=[[1,0,0],[0,1,0],[0,0,1]],cr=e=>e-2*Math.PI*Math.floor((e+Math.PI)/(2*Math.PI)),xr=(e,t)=>cr(Math.PI/2-Math.atan2(-(t[1]-e[1]),t[0]-e[0]));var yt=(e,t)=>[[1,0,e],[0,1,t],[0,0,1]],ie=(e,t)=>{let o=0;for(let n=0;n{let o=[];for(let n=0;n{let o=[],n=e.length;for(let r=0;r{let o=Math.cos(e),n=Math.sin(e),r=[[o,-n,0],[n,o,0],[0,0,1]],s=yt(t[0],t[1]),a=dt(s,r),i=yt(-t[0],-t[1]);return dt(a,i)},dr=e=>{let t=[[e[0][0],e[1][0]],[e[0][1],e[1][1]]],o=[e[0][2],e[1][2]],n=[-ie(t[0],o),-ie(t[1],o)];return[t[0].concat(n[0]),t[1].concat(n[1]),[0,0,1]]},fr=(e,t)=>[ie(e,t[0]),ie(e,t[1])];function ut(e){let t=e===192?{strides:[4],anchors:[1]}:{strides:[e/16,e/8],anchors:[2,6]},o=[];for(let n=0;n[s[0]/r*(y[0]-r/2),s[1]/r*(y[1]-r/2),y[2]||0]),i=o&&o!==0&&Math.abs(o)>.2,c=i?pt(o,[0,0]):h5,x=i?a.map(y=>[...fr(y,c),y[2]]):a,d=i?dr(n):h5,l=d2(t),f=[ie(l,d[0]),ie(l,d[1])];return x.map(y=>[Math.trunc(y[0]+f[0]),Math.trunc(y[1]+f[1]),Math.trunc(y[2]||0)])}function bt(e,t,o,n){let r=t.landmarks.length>=f5.count?f5.symmetryLine:se.symmetryLine,s=0,a=h5,i;if(e&&T.kernels.includes("rotatewithoffset"))if(s=xr(t.landmarks[r[0]],t.landmarks[r[1]]),s&&s!==0&&Math.abs(s)>.2){let x=d2(t),d=[x[0]/o.shape[2],x[1]/o.shape[1]],l=A.image.rotateWithOffset(o,s,0,d);a=pt(-s,x),i=u5(t,l,[n,n]),A.dispose(l)}else i=u5(t,o,[n,n]);else i=u5(t,o,[n,n]);return[s,a,i]}var mr=e=>{let t=e.map(n=>n[0]),o=e.map(n=>n[1]);return[Math.min(...t)+(Math.max(...t)-Math.min(...t))/2,Math.min(...o)+(Math.max(...o)-Math.min(...o))/2]},gt=(e,t)=>{let o=mr(e),n=Re(t);return{startPoint:[o[0]-n[0]/2,o[1]-n[1]/2],endPoint:[o[0]+n[0]/2,o[1]+n[1]/2]}};var Mt=6,pr=1.4,S0,Rt=null,Y0=0,Ke=null,ve=()=>Y0;async function vt(e){var t;return T.initial&&(S0=null),S0?e.debug&&b("cached model:",S0.modelUrl):S0=await L((t=e.face.detector)==null?void 0:t.modelPath),Y0=S0.executor&&S0.inputs[0].shape?S0.inputs[0].shape[2]:256,Ke=A.scalar(Y0,"int32"),Rt=A.tensor2d(ut(Y0)),S0}function ur(e){let t={};t.boxStarts=A.slice(e,[0,1],[-1,2]),t.centers=A.add(t.boxStarts,Rt),t.boxSizes=A.slice(e,[0,3],[-1,2]),t.boxSizesNormalized=A.div(t.boxSizes,Ke),t.centersNormalized=A.div(t.centers,Ke),t.halfBoxSize=A.div(t.boxSizesNormalized,F.tf2),t.starts=A.sub(t.centersNormalized,t.halfBoxSize),t.ends=A.add(t.centersNormalized,t.halfBoxSize),t.startNormalized=A.mul(t.starts,Ke),t.endNormalized=A.mul(t.ends,Ke);let o=A.concat2d([t.startNormalized,t.endNormalized],1);return Object.keys(t).forEach(n=>A.dispose(t[n])),o}async function Pt(e,t){var i,c,x,d;if(!e||e.isDisposedInternal||e.shape.length!==4||e.shape[1]<1||e.shape[2]<1)return[];let o={};o.resized=A.image.resizeBilinear(e,[Y0,Y0]),o.div=A.div(o.resized,F.tf127),o.normalized=A.sub(o.div,F.tf05);let n=S0==null?void 0:S0.execute(o.normalized);if(Array.isArray(n)&&n.length>2){let l=n.sort((f,y)=>f.size-y.size);o.concat384=A.concat([l[0],l[2]],2),o.concat512=A.concat([l[1],l[3]],2),o.concat=A.concat([o.concat512,o.concat384],1),o.batch=A.squeeze(o.concat,0)}else Array.isArray(n)?o.batch=A.squeeze(n[0]):o.batch=A.squeeze(n);A.dispose(n),o.boxes=ur(o.batch),o.logits=A.slice(o.batch,[0,0],[-1,1]),o.sigmoid=A.sigmoid(o.logits),o.scores=A.squeeze(o.sigmoid),o.nms=await A.image.nonMaxSuppressionAsync(o.boxes,o.scores,((i=t.face.detector)==null?void 0:i.maxDetected)||0,((c=t.face.detector)==null?void 0:c.iouThreshold)||0,((x=t.face.detector)==null?void 0:x.minConfidence)||0);let r=await o.nms.array(),s=[],a=await o.scores.data();for(let l=0;l(((d=t.face.detector)==null?void 0:d.minConfidence)||0)){let y={};y.bbox=A.slice(o.boxes,[r[l],0],[1,-1]),y.slice=A.slice(o.batch,[r[l],Mt-1],[1,-1]),y.squeeze=A.squeeze(y.slice),y.landmarks=A.reshape(y.squeeze,[Mt,-1]);let p=await y.bbox.data(),g={startPoint:[p[0],p[1]],endPoint:[p[2],p[3]],landmarks:await y.landmarks.array(),confidence:f},M=ft(g,[(e.shape[2]||0)/Y0,(e.shape[1]||0)/Y0]),R=p2(M,t.face.scale||pr),P=u2(R);s.push(P),Object.keys(y).forEach(m=>A.dispose(y[m]))}}return Object.keys(o).forEach(l=>A.dispose(o[l])),s}var h2={};q0(h2,{connected:()=>M5,kpt:()=>g5});var g5=["nose","leftEyeInside","leftEye","leftEyeOutside","rightEyeInside","rightEye","rightEyeOutside","leftEar","rightEar","leftMouth","rightMouth","leftShoulder","rightShoulder","leftElbow","rightElbow","leftWrist","rightWrist","leftPinky","rightPinky","leftIndex","rightIndex","leftThumb","rightThumb","leftHip","rightHip","leftKnee","rightKnee","leftAnkle","rightAnkle","leftHeel","rightHeel","leftFoot","rightFoot","bodyCenter","bodyTop","leftPalm","leftHand","rightPalm","rightHand"],M5={shoulders:["leftShoulder","rightShoulder"],hips:["rightHip","leftHip"],mouth:["leftMouth","rightMouth"],leftLegUpper:["leftHip","leftKnee"],leftLegLower:["leftKnee","leftAnkle"],leftFoot:["leftAnkle","leftHeel","leftFoot"],leftTorso:["leftShoulder","leftHip"],leftArmUpper:["leftShoulder","leftElbow"],leftArmLower:["leftElbow","leftWrist"],leftHand:["leftWrist","leftPalm"],leftHandPinky:["leftPalm","leftPinky"],leftHandIndex:["leftPalm","leftIndex"],leftHandThumb:["leftPalm","leftThumb"],leftEyeOutline:["leftEyeInside","leftEyeOutside"],rightLegUpper:["rightHip","rightKnee"],rightLegLower:["rightKnee","rightAnkle"],rightFoot:["rightAnkle","rightHeel","rightFoot"],rightTorso:["rightShoulder","rightHip"],rightArmUpper:["rightShoulder","rightElbow"],rightArmLower:["rightElbow","rightWrist"],rightHand:["rightWrist","rightPalm"],rightHandPinky:["rightPalm","rightPinky"],rightHandIndex:["rightPalm","rightIndex"],rightHandThumb:["rightPalm","rightThumb"],rightEyeOutline:["rightEyeInside","rightEyeOutside"]};var wt=224,hr,br=5,b2=[8,16,32,32,32];function kt(){let e=[],t=0;for(;t
o.x)),y:A.tensor1d(e.map(o=>o.y))}}function G0(e,t=[1,1]){let o=[e.map(i=>i[0]),e.map(i=>i[1])],n=[Math.min(...o[0]),Math.min(...o[1])],r=[Math.max(...o[0]),Math.max(...o[1])],s=[n[0],n[1],r[0]-n[0],r[1]-n[1]],a=[s[0]/t[0],s[1]/t[1],s[2]/t[0],s[3]/t[1]];return{box:s,boxRaw:a}}function Et(e,t=[1,1]){let o=[e.map(x=>x[0]),e.map(x=>x[1])],n=[Math.min(...o[0]),Math.min(...o[1])],r=[Math.max(...o[0]),Math.max(...o[1])],s=[(n[0]+r[0])/2,(n[1]+r[1])/2],a=Math.max(s[0]-n[0],s[1]-n[1],-s[0]+r[0],-s[1]+r[1]),i=[Math.trunc(s[0]-a),Math.trunc(s[1]-a),Math.trunc(2*a),Math.trunc(2*a)],c=[i[0]/t[0],i[1]/t[1],i[2]/t[0],i[3]/t[1]];return{box:i,boxRaw:c}}function g2(e,t){let o=[e[2]*t,e[3]*t];return[e[0]-(o[0]-e[2])/2,e[1]-(o[1]-e[3])/2,o[0],o[1]]}var Ct={initial:!0},r0={detector:null,landmarks:null},Pe={detector:[224,224],landmarks:[256,256]},R5=Number.MAX_SAFE_INTEGER,Mr={landmarks:["ld_3d","activation_segmentation","activation_heatmap","world_3d","output_poseflag"],detector:[]},R2=null,Je,K0=[[0,0],[0,0],[0,0],[0,0]],zt=0,St=e=>1-1/(1+Math.exp(e));async function It(e){var t;if(Ct.initial&&(r0.detector=null),!r0.detector&&e.body.detector&&e.body.detector.modelPath){r0.detector=await L(e.body.detector.modelPath);let o=(t=r0.detector)!=null&&t.executor?Object.values(r0.detector.modelSignature.inputs):void 0;Pe.detector[0]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[1].size):0,Pe.detector[1]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[2].size):0}else e.debug&&r0.detector&&b("cached model:",r0.detector.modelUrl);return kt(),r0.detector}async function jt(e){var t;if(Ct.initial&&(r0.landmarks=null),r0.landmarks)e.debug&&b("cached model:",r0.landmarks.modelUrl);else{r0.landmarks=await L(e.body.modelPath);let o=(t=r0.landmarks)!=null&&t.executor?Object.values(r0.landmarks.modelSignature.inputs):void 0;Pe.landmarks[0]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[1].size):0,Pe.landmarks[1]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[2].size):0}return r0.landmarks}function Rr(e,t){var r,s;let o={};if(!((r=e==null?void 0:e.shape)!=null&&r[1])||!((s=e==null?void 0:e.shape)!=null&&s[2]))return e;let n;if(Je&&(o.cropped=A.image.cropAndResize(e,[Je],[0],[e.shape[1],e.shape[2]])),e.shape[1]!==e.shape[2]){let a=[e.shape[2]>e.shape[1]?Math.trunc((e.shape[2]-e.shape[1])/2):0,e.shape[2]>e.shape[1]?Math.trunc((e.shape[2]-e.shape[1])/2):0],i=[e.shape[1]>e.shape[2]?Math.trunc((e.shape[1]-e.shape[2])/2):0,e.shape[1]>e.shape[2]?Math.trunc((e.shape[1]-e.shape[2])/2):0];K0=[[0,0],a,i,[0,0]],o.pad=A.pad(o.cropped||e,K0),o.resize=A.image.resizeBilinear(o.pad,[t,t]),n=A.div(o.resize,F.tf255)}else e.shape[1]!==t?(o.resize=A.image.resizeBilinear(o.cropped||e,[t,t]),n=A.div(o.resize,F.tf255)):n=A.div(o.cropped||e,F.tf255);return Object.keys(o).forEach(a=>A.dispose(o[a])),n}function vr(e,t){for(let o of e)o.position=[Math.trunc(o.position[0]*(t[0]+K0[2][0]+K0[2][1])/t[0]-K0[2][0]),Math.trunc(o.position[1]*(t[1]+K0[1][0]+K0[1][1])/t[1]-K0[1][0]),o.position[2]],o.positionRaw=[o.position[0]/t[0],o.position[1]/t[1],2*o.position[2]/(t[0]+t[1])];if(Je)for(let o of e)o.positionRaw=[o.positionRaw[0]+Je[1],o.positionRaw[1]+Je[0],o.positionRaw[2]],o.position=[Math.trunc(o.positionRaw[0]*t[0]),Math.trunc(o.positionRaw[1]*t[1]),o.positionRaw[2]];return e}function Pr(e){let t=e.find(i=>i.part==="leftPalm"),o=e.find(i=>i.part==="leftWrist"),n=e.find(i=>i.part==="leftIndex");t.position[2]=((o.position[2]||0)+(n.position[2]||0))/2;let r=e.find(i=>i.part==="rightPalm"),s=e.find(i=>i.part==="rightWrist"),a=e.find(i=>i.part==="rightIndex");r.position[2]=((s.position[2]||0)+(a.position[2]||0))/2}async function Tr(e,t,o){var p,g;if(!((p=r0.landmarks)!=null&&p.executor))return null;let n={};[n.ld,n.segmentation,n.heatmap,n.world,n.poseflag]=(g=r0.landmarks)==null?void 0:g.execute(e,Mr.landmarks);let r=(await n.poseflag.data())[0],s=await n.ld.data(),a=await n.world.data();Object.keys(n).forEach(M=>A.dispose(n[M]));let i=[],c=5;for(let M=0;MM.position),l=G0(d,[o[0],o[1]]),f={};for(let[M,R]of Object.entries(M5)){let P=[];for(let m=0;mk.part===R[m]),z=x.find(k=>k.part===R[m+1]);u&&z&&P.push([u.position,z.position])}f[M]=P}return{id:0,score:Math.trunc(100*r)/100,box:l.box,boxRaw:l.boxRaw,keypoints:x,annotations:f}}async function v5(e,t){let o=[e.shape[2]||0,e.shape[1]||0],n=(t.body.skipTime||0)>v()-zt,r=R5<(t.body.skipFrames||0);if(t.skipAllowed&&n&&r&&R2!==null)R5++;else{let s={};s.landmarks=Rr(e,256),R2=await Tr(s.landmarks,t,o),Object.keys(s).forEach(a=>A.dispose(s[a])),zt=v(),R5=0}return R2?[R2]:[]}var Te=[{class:1,label:"person"},{class:2,label:"bicycle"},{class:3,label:"car"},{class:4,label:"motorcycle"},{class:5,label:"airplane"},{class:6,label:"bus"},{class:7,label:"train"},{class:8,label:"truck"},{class:9,label:"boat"},{class:10,label:"traffic light"},{class:11,label:"fire hydrant"},{class:12,label:"stop sign"},{class:13,label:"parking meter"},{class:14,label:"bench"},{class:15,label:"bird"},{class:16,label:"cat"},{class:17,label:"dog"},{class:18,label:"horse"},{class:19,label:"sheep"},{class:20,label:"cow"},{class:21,label:"elephant"},{class:22,label:"bear"},{class:23,label:"zebra"},{class:24,label:"giraffe"},{class:25,label:"backpack"},{class:26,label:"umbrella"},{class:27,label:"handbag"},{class:28,label:"tie"},{class:29,label:"suitcase"},{class:30,label:"frisbee"},{class:31,label:"skis"},{class:32,label:"snowboard"},{class:33,label:"sports ball"},{class:34,label:"kite"},{class:35,label:"baseball bat"},{class:36,label:"baseball glove"},{class:37,label:"skateboard"},{class:38,label:"surfboard"},{class:39,label:"tennis racket"},{class:40,label:"bottle"},{class:41,label:"wine glass"},{class:42,label:"cup"},{class:43,label:"fork"},{class:44,label:"knife"},{class:45,label:"spoon"},{class:46,label:"bowl"},{class:47,label:"banana"},{class:48,label:"apple"},{class:49,label:"sandwich"},{class:50,label:"orange"},{class:51,label:"broccoli"},{class:52,label:"carrot"},{class:53,label:"hot dog"},{class:54,label:"pizza"},{class:55,label:"donut"},{class:56,label:"cake"},{class:57,label:"chair"},{class:58,label:"couch"},{class:59,label:"potted plant"},{class:60,label:"bed"},{class:61,label:"dining table"},{class:62,label:"toilet"},{class:63,label:"tv"},{class:64,label:"laptop"},{class:65,label:"mouse"},{class:66,label:"remote"},{class:67,label:"keyboard"},{class:68,label:"cell phone"},{class:69,label:"microwave"},{class:70,label:"oven"},{class:71,label:"toaster"},{class:72,label:"sink"},{class:73,label:"refrigerator"},{class:74,label:"book"},{class:75,label:"clock"},{class:76,label:"vase"},{class:77,label:"scissors"},{class:78,label:"teddy bear"},{class:79,label:"hair drier"},{class:80,label:"toothbrush"}];var y0,le=0,P5=[],Nt=0,T5=Number.MAX_SAFE_INTEGER;async function Lt(e){if(T.initial&&(y0=null),y0)e.debug&&b("cached model:",y0.modelUrl);else{y0=await L(e.object.modelPath);let t=y0!=null&&y0.executor?Object.values(y0.modelSignature.inputs):void 0;le=Array.isArray(t)?parseInt(t[0].tensorShape.dim[2].size):0}return y0}async function wr(e,t,o){if(!e)return[];let n={},r=[],s=await e.array();n.squeeze=A.squeeze(e);let a=A.split(n.squeeze,6,1);n.stack=A.stack([a[1],a[0],a[3],a[2]],1),n.boxes=A.squeeze(n.stack),n.scores=A.squeeze(a[4]),n.classes=A.squeeze(a[5]),A.dispose([e,...a]),n.nms=await A.image.nonMaxSuppressionAsync(n.boxes,n.scores,o.object.maxDetected,o.object.iouThreshold,o.object.minConfidence||0);let i=await n.nms.data(),c=0;for(let x of Array.from(i)){let d=Math.trunc(100*s[0][x][4])/100,l=s[0][x][5],f=Te[l].label,[y,p]=[s[0][x][0]/le,s[0][x][1]/le],g=[y,p,s[0][x][2]/le-y,s[0][x][3]/le-p],M=[Math.trunc(g[0]*t[0]),Math.trunc(g[1]*t[1]),Math.trunc(g[2]*t[0]),Math.trunc(g[3]*t[1])];r.push({id:c++,score:d,class:l,label:f,box:M,boxRaw:g})}return Object.keys(n).forEach(x=>A.dispose(n[x])),r}async function w5(e,t){if(!(y0!=null&&y0.executor))return[];let o=(t.object.skipTime||0)>v()-Nt,n=T5<(t.object.skipFrames||0);return t.skipAllowed&&o&&n&&P5.length>0?(T5++,P5):(T5=0,new Promise(async r=>{let s=[e.shape[2]||0,e.shape[1]||0],a=A.image.resizeBilinear(e,[le,le]),i=t.object.enabled?y0==null?void 0:y0.execute(a,["tower_0/detections"]):null;Nt=v(),A.dispose(a);let c=await wr(i,s,t);P5=c,r(c)}))}var v2={};q0(v2,{connected:()=>E5,kpt:()=>k5});var k5=["head","neck","rightShoulder","rightElbow","rightWrist","chest","leftShoulder","leftElbow","leftWrist","bodyCenter","rightHip","rightKnee","rightAnkle","leftHip","leftKnee","leftAnkle"],E5={leftLeg:["leftHip","leftKnee","leftAnkle"],rightLeg:["rightHip","rightKnee","rightAnkle"],torso:["leftShoulder","rightShoulder","rightHip","leftHip","leftShoulder"],leftArm:["leftShoulder","leftElbow","leftWrist"],rightArm:["rightShoulder","rightElbow","rightWrist"],head:[]};var $,Ft=0,a0={id:0,keypoints:[],box:[0,0,0,0],boxRaw:[0,0,0,0],score:0,annotations:{}},z5=Number.MAX_SAFE_INTEGER;async function Gt(e){return T.initial&&($=null),$?e.debug&&b("cached model:",$.modelUrl):$=await L(e.body.modelPath),$}async function kr(e,t){let[o,n]=e.shape,r=A.reshape(e,[n*o]),s=A.max(r,0),a=(await s.data())[0];if(a>t){let i=A.argMax(r,0),c=A.mod(i,o),x=(await c.data())[0],d=A.div(i,o),l=(await d.data())[0];return A.dispose([r,s,i,c,d]),[x,l,a]}return A.dispose([r,s]),[0,0,a]}async function S5(e,t){if(!($!=null&&$.executor))return[];let o=(t.body.skipTime||0)>v()-Ft,n=z5<(t.body.skipFrames||0);return t.skipAllowed&&o&&n&&Object.keys(a0.keypoints).length>0?(z5++,[a0]):(z5=0,new Promise(async r=>{let s=A.tidy(()=>{if(!($!=null&&$.inputs[0].shape))return null;let l=A.image.resizeBilinear(e,[$.inputs[0].shape[2],$.inputs[0].shape[1]],!1),f=A.mul(l,F.tf2);return A.sub(f,F.tf1)}),a;if(t.body.enabled&&(a=$==null?void 0:$.execute(s)),Ft=v(),A.dispose(s),a){a0.keypoints.length=0;let l=A.squeeze(a);A.dispose(a);let f=A.unstack(l,2);A.dispose(l);for(let y=0;y(t.body.minConfidence||0)&&a0.keypoints.push({score:Math.round(100*M)/100,part:k5[y],positionRaw:[p/$.inputs[0].shape[2],g/$.inputs[0].shape[1]],position:[Math.round(e.shape[2]*p/$.inputs[0].shape[2]),Math.round(e.shape[1]*g/$.inputs[0].shape[1])]})}f.forEach(y=>A.dispose(y))}a0.score=a0.keypoints.reduce((l,f)=>f.score>l?f.score:l,0);let i=a0.keypoints.map(l=>l.position[0]),c=a0.keypoints.map(l=>l.position[1]);a0.box=[Math.min(...i),Math.min(...c),Math.max(...i)-Math.min(...i),Math.max(...c)-Math.min(...c)];let x=a0.keypoints.map(l=>l.positionRaw[0]),d=a0.keypoints.map(l=>l.positionRaw[1]);a0.boxRaw=[Math.min(...x),Math.min(...d),Math.max(...x)-Math.min(...x),Math.max(...d)-Math.min(...d)];for(let[l,f]of Object.entries(E5)){let y=[];for(let p=0;pR.part===f[p]),M=a0.keypoints.find(R=>R.part===f[p+1]);g&&M&&g.score>(t.body.minConfidence||0)&&M.score>(t.body.minConfidence||0)&&y.push([g.position,M.position])}a0.annotations[l]=y}r([a0])}))}var Er=["angry","disgust","fear","happy","sad","surprise","neutral"],b0,P2=[],Ht=0,Dt=0,C5=Number.MAX_SAFE_INTEGER;async function Vt(e){var t;return T.initial&&(b0=null),b0?e.debug&&b("cached model:",b0.modelUrl):b0=await L((t=e.face.emotion)==null?void 0:t.modelPath),b0}async function I5(e,t,o,n){var a,i;if(!b0)return[];let r=C5<(((a=t.face.emotion)==null?void 0:a.skipFrames)||0),s=(((i=t.face.emotion)==null?void 0:i.skipTime)||0)>v()-Dt;return t.skipAllowed&&s&&r&&Ht===n&&P2[o]&&P2[o].length>0?(C5++,P2[o]):(C5=0,new Promise(async c=>{var d;let x=[];if((d=t.face.emotion)!=null&&d.enabled){let l={},f=b0!=null&&b0.inputs[0].shape?b0.inputs[0].shape[2]:0;l.resize=A.image.resizeBilinear(e,[f,f],!1),l.channels=A.mul(l.resize,F.rgb),l.grayscale=A.sum(l.channels,3,!0),l.grayscaleSub=A.sub(l.grayscale,F.tf05),l.grayscaleMul=A.mul(l.grayscaleSub,F.tf2),l.emotion=b0==null?void 0:b0.execute(l.grayscaleMul),Dt=v();let y=await l.emotion.data();for(let p=0;p(t.face.emotion.minConfidence||0)&&x.push({score:Math.min(.99,Math.trunc(100*y[p])/100),emotion:Er[p]});x.sort((p,g)=>g.score-p.score),Object.keys(l).forEach(p=>A.dispose(l[p]))}P2[o]=x,Ht=n,c(x)}))}var f0,j5=[],Xt=0,qt=0,Ut=Number.MAX_SAFE_INTEGER;async function Yt(e){var t;return T.initial&&(f0=null),f0?e.debug&&b("cached model:",f0.modelUrl):f0=await L((t=e.face.mobilefacenet)==null?void 0:t.modelPath),f0}async function O5(e,t,o,n){var a,i;if(!(f0!=null&&f0.executor))return[];let r=Ut<(((a=t.face.mobilefacenet)==null?void 0:a.skipFrames)||0),s=(((i=t.face.mobilefacenet)==null?void 0:i.skipTime)||0)>v()-qt;return t.skipAllowed&&s&&r&&Xt===n&&j5[o]?(Ut++,j5[o]):new Promise(async c=>{var d;let x=[];if(((d=t.face.mobilefacenet)==null?void 0:d.enabled)&&(f0==null?void 0:f0.inputs[0].shape)){let l={};l.crop=A.image.resizeBilinear(e,[f0.inputs[0].shape[2],f0.inputs[0].shape[1]],!1),l.data=f0.execute(l.crop);let f=await l.data.data();x=Array.from(f),Object.keys(l).forEach(y=>A.dispose(l[y]))}j5[o]=x,Xt=n,qt=v(),c(x)})}var m0,N5=[],Jt=0,Qt=0,_t=Number.MAX_SAFE_INTEGER;async function $t(e){return T.initial&&(m0=null),m0?e.debug&&b("cached model:",m0.modelUrl):m0=await L(e.face.insightface.modelPath),m0}async function L5(e,t,o,n){var a,i;if(!(m0!=null&&m0.executor))return[];let r=_t<(((a=t.face.insightface)==null?void 0:a.skipFrames)||0),s=(((i=t.face.insightface)==null?void 0:i.skipTime)||0)>v()-Qt;return t.skipAllowed&&s&&r&&Jt===n&&N5[o]?(_t++,N5[o]):new Promise(async c=>{var d;let x=[];if(((d=t.face.insightface)==null?void 0:d.enabled)&&(m0==null?void 0:m0.inputs[0].shape)){let l={};l.crop=A.image.resizeBilinear(e,[m0.inputs[0].shape[2],m0.inputs[0].shape[1]],!1),l.data=m0.execute(l.crop);let f=await l.data.data();x=Array.from(f),Object.keys(l).forEach(y=>A.dispose(l[y]))}N5[o]=x,Jt=n,Qt=v(),c(x)})}var p0,J0=0,zr=2.3,W5=w0.leftEyeLower0,F5=w0.rightEyeLower0,we={leftBounds:[W5[0],W5[W5.length-1]],rightBounds:[F5[0],F5[F5.length-1]]},ke={upperCenter:3,lowerCenter:4,index:71,numCoordinates:76};async function r3(e){var t,o;return T.initial&&(p0=null),p0?e.debug&&b("cached model:",p0.modelUrl):p0=await L((t=e.face.iris)==null?void 0:t.modelPath),J0=(p0==null?void 0:p0.executor)&&((o=p0.inputs)==null?void 0:o[0].shape)?p0.inputs[0].shape[2]:0,J0===-1&&(J0=64),p0}function T2(e,t,o,n){for(let r=0;r{let t=e[we.leftBounds[0]][2],o=e[we.rightBounds[0]][2];return t-o},t3=(e,t,o,n,r,s=!1)=>{let a=u2(p2(mt([e[o],e[n]]),zr)),i=Re(a),c=A.image.cropAndResize(t,[[a.startPoint[1]/r,a.startPoint[0]/r,a.endPoint[1]/r,a.endPoint[0]/r]],[0],[J0,J0]);if(s&&T.kernels.includes("flipleftright")){let x=A.image.flipLeftRight(c);A.dispose(c),c=x}return{box:a,boxSize:i,crop:c}},o3=(e,t,o,n=!1)=>{let r=[];for(let s=0;s{let n=e[w0[`${o}EyeUpper0`][ke.upperCenter]][2],r=e[w0[`${o}EyeLower0`][ke.lowerCenter]][2],s=(n+r)/2;return t.map((a,i)=>{let c=s;return i===2?c=n:i===4&&(c=r),[a[0],a[1],c]})};async function A3(e,t,o){if(!(p0!=null&&p0.executor))return e;let{box:n,boxSize:r,crop:s}=t3(e,t,we.leftBounds[0],we.leftBounds[1],o,!0),{box:a,boxSize:i,crop:c}=t3(e,t,we.rightBounds[0],we.rightBounds[1],o,!0),x=A.concat([s,c]);A.dispose(s),A.dispose(c);let d=p0.execute(x);A.dispose(x);let l=await d.data();A.dispose(d);let f=l.slice(0,ke.numCoordinates*3),{rawCoords:y,iris:p}=o3(f,n,r,!0),g=l.slice(ke.numCoordinates*3),{rawCoords:M,iris:R}=o3(g,a,i,!1),P=Sr(e);Math.abs(P)<30?(T2(e,y,"left",null),T2(e,M,"right",null)):P<1?T2(e,y,"left",["EyeUpper0","EyeLower0"]):T2(e,M,"right",["EyeUpper0","EyeLower0"]);let m=n3(e,p,"left"),u=n3(e,R,"right");return e.concat(m).concat(u)}var Cr=[[61,146],[146,91],[91,181],[181,84],[84,17],[17,314],[314,405],[405,321],[321,375],[375,291],[61,185],[185,40],[40,39],[39,37],[37,0],[0,267],[267,269],[269,270],[270,409],[409,291],[78,95],[95,88],[88,178],[178,87],[87,14],[14,317],[317,402],[402,318],[318,324],[324,308],[78,191],[191,80],[80,81],[81,82],[82,13],[13,312],[312,311],[311,310],[310,415],[415,308]],Ir=[[263,249],[249,390],[390,373],[373,374],[374,380],[380,381],[381,382],[382,362],[263,466],[466,388],[388,387],[387,386],[386,385],[385,384],[384,398],[398,362]],jr=[[276,283],[283,282],[282,295],[295,285],[300,293],[293,334],[334,296],[296,336]],Or=[[474,475],[475,476],[476,477],[477,474]],Nr=[[33,7],[7,163],[163,144],[144,145],[145,153],[153,154],[154,155],[155,133],[33,246],[246,161],[161,160],[160,159],[159,158],[158,157],[157,173],[173,133]],Lr=[[46,53],[53,52],[52,65],[65,55],[70,63],[63,105],[105,66],[66,107]],Wr=[[469,470],[470,471],[471,472],[472,469]],Fr=[[10,338],[338,297],[297,332],[332,284],[284,251],[251,389],[389,356],[356,454],[454,323],[323,361],[361,288],[288,397],[397,365],[365,379],[379,378],[378,400],[400,377],[377,152],[152,148],[148,176],[176,149],[149,150],[150,136],[136,172],[172,58],[58,132],[132,93],[93,234],[234,127],[127,162],[162,21],[21,54],[54,103],[103,67],[67,109],[109,10]];function Q0(e){let t=e.map(o=>o[0]);return t.push(e[e.length-1][1]),t}var Gr={lips:Q0(Cr),leftEye:Q0(Ir),leftEyebrow:Q0(jr),leftIris:Q0(Or),rightEye:Q0(Nr),rightEyebrow:Q0(Lr),rightIris:Q0(Wr),faceOval:Q0(Fr)},Br=Object.entries(Gr).map(([e,t])=>t.map(o=>[o,e])).flat(),o4=new Map(Br),Qe=[61,146,91,181,84,17,314,405,321,375,291,185,40,39,37,0,267,269,270,409,78,95,88,178,87,14,317,402,318,324,308,191,80,81,82,13,312,311,310,415,76,77,90,180,85,16,315,404,320,307,306,184,74,73,72,11,302,303,304,408,62,96,89,179,86,15,316,403,319,325,292,183,42,41,38,12,268,271,272,407],ce=[33,7,163,144,145,153,154,155,133,246,161,160,159,158,157,173,130,25,110,24,23,22,26,112,243,247,30,29,27,28,56,190,226,31,228,229,230,231,232,233,244,113,225,224,223,222,221,189,35,124,46,53,52,65,143,111,117,118,119,120,121,128,245,156,70,63,105,66,107,55,193],xe=[263,249,390,373,374,380,381,382,362,466,388,387,386,385,384,398,359,255,339,254,253,252,256,341,463,467,260,259,257,258,286,414,446,261,448,449,450,451,452,453,464,342,445,444,443,442,441,413,265,353,276,283,282,295,372,340,346,347,348,349,350,357,465,383,300,293,334,296,336,285,417];async function i3(e,t){let o={lips:await t.filter(s=>s.size===160)[0].data(),irisL:await t.filter(s=>s.size===10)[0].data(),eyeL:await t.filter(s=>s.size===142)[0].data(),irisR:await t.filter(s=>s.size===10)[1].data(),eyeR:await t.filter(s=>s.size===142)[1].data()},n=ce.reduce((s,a)=>s+=e[a][2],0)/ce.length;for(let s=0;ss+=e[a][2],0)/xe.length;for(let s=0;sv()-N0.timestamp,n=N0.skipped<(((x=t.face.detector)==null?void 0:x.skipFrames)||0);!t.skipAllowed||!o||!n||N0.boxes.length===0?(N0.boxes=await Pt(e,t),N0.timestamp=v(),N0.skipped=0):N0.skipped++;let r=[],s=[],a=0,i=_e;for(let P=0;Pj.shape[j.shape.length-1]===1).data();if(k.faceScore=Math.round(100*B[0])/100,k.faceScore<(((p=t.face.detector)==null?void 0:p.minConfidence)||1)){if(m.confidence=k.faceScore,t.face.mesh.keepInvalid){k.box=f2(m,e),k.boxRaw=m2(m,e),k.score=k.boxScore,k.mesh=m.landmarks.map(j=>[(m.startPoint[0]+m.endPoint[0])/2+(m.endPoint[0]+m.startPoint[0])*j[0]/ve(),(m.startPoint[1]+m.endPoint[1])/2+(m.endPoint[1]+m.startPoint[1])*j[1]/ve()]),k.meshRaw=k.mesh.map(j=>[j[0]/(e.shape[2]||1),j[1]/(e.shape[1]||1),(j[2]||0)/i]);for(let j of Object.keys(se))k.annotations[j]=[k.mesh[se[j]]]}}else{let j=h.find(I=>I.shape[I.shape.length-1]===1404),N=A.reshape(j,[-1,3]),G=await N.array();A.dispose(N),(g=t.face.attention)!=null&&g.enabled?G=await i3(G,h):(M=t.face.iris)!=null&&M.enabled&&(G=await A3(G,k.tensor,_e)),k.mesh=ht(G,m,u,z,_e),k.meshRaw=k.mesh.map(I=>[I[0]/(e.shape[2]||0),I[1]/(e.shape[1]||0),(I[2]||0)/i]);for(let I of Object.keys(w0))k.annotations[I]=w0[I].map(n0=>k.mesh[n0]);k.score=k.faceScore;let O={...gt(k.mesh,m),confidence:m.confidence,landmarks:m.landmarks};k.box=f2(O,e),k.boxRaw=m2(O,e),s.push(O)}A.dispose(h)}else{k.box=f2(m,e),k.boxRaw=m2(m,e),k.score=k.boxScore,k.mesh=m.landmarks.map(h=>[(m.startPoint[0]+m.endPoint[0])/2+(m.endPoint[0]+m.startPoint[0])*h[0]/ve(),(m.startPoint[1]+m.endPoint[1])/2+(m.endPoint[1]+m.startPoint[1])*h[1]/ve()]),k.meshRaw=k.mesh.map(h=>[h[0]/(e.shape[2]||0),h[1]/(e.shape[1]||0),(h[2]||0)/i]);for(let h of Object.keys(se))k.annotations[h]=[k.mesh[se[h]]]}k.score>(((R=t.face.detector)==null?void 0:R.minConfidence)||1)?r.push(k):A.dispose(k.tensor)}return N0.boxes=s,r}async function c3(e){var t,o,n,r,s,a;return T.initial&&(X=null),((t=e.face.attention)==null?void 0:t.enabled)&&(X==null?void 0:X.signature)&&Object.keys(((o=X==null?void 0:X.signature)==null?void 0:o.outputs)||{}).length<6&&(X=null),X?e.debug&&b("cached model:",X.modelUrl):(n=e.face.attention)!=null&&n.enabled?X=await L(e.face.attention.modelPath):X=await L((r=e.face.mesh)==null?void 0:r.modelPath),_e=X.executor&&((s=X==null?void 0:X.inputs)==null?void 0:s[0].shape)?(a=X==null?void 0:X.inputs)==null?void 0:a[0].shape[2]:256,X}var x3=ae,y3=Ye;var i0,w2=[],d3=0,f3=0,B5=Number.MAX_SAFE_INTEGER;async function m3(e){var t;return T.initial&&(i0=null),i0?e.debug&&b("cached model:",i0.modelUrl):i0=await L((t=e.face.description)==null?void 0:t.modelPath),i0}function H5(e){let t=e.image||e.tensor||e;if(!(i0!=null&&i0.inputs[0].shape))return t;let o=A.image.resizeBilinear(t,[i0.inputs[0].shape[2],i0.inputs[0].shape[1]],!1),n=A.mul(o,F.tf255);return A.dispose(o),n}async function D5(e,t,o,n){var a,i,c,x;if(!(i0!=null&&i0.executor))return{age:0,gender:"unknown",genderScore:0,descriptor:[]};let r=B5<(((a=t.face.description)==null?void 0:a.skipFrames)||0),s=(((i=t.face.description)==null?void 0:i.skipTime)||0)>v()-d3;return t.skipAllowed&&r&&s&&f3===n&&((c=w2[o])==null?void 0:c.age)&&((x=w2[o])==null?void 0:x.age)>0?(B5++,w2[o]):(B5=0,new Promise(async d=>{var f;let l={age:0,gender:"unknown",genderScore:0,descriptor:[]};if((f=t.face.description)!=null&&f.enabled){let y=H5(e),p=i0==null?void 0:i0.execute(y);d3=v(),A.dispose(y);let M=await p.find(W=>W.shape[1]===1).data(),R=Math.trunc(200*Math.abs(M[0]-.5))/100;R>(t.face.description.minConfidence||0)&&(l.gender=M[0]<=.5?"female":"male",l.genderScore=Math.min(.99,R));let P=A.argMax(p.find(W=>W.shape[1]===100),1),m=(await P.data())[0];A.dispose(P);let z=await p.find(W=>W.shape[1]===100).data();l.age=Math.round(z[m-1]>z[m+1]?10*m-100*z[m-1]:10*m+100*z[m+1])/10;let k=p.find(W=>W.shape[1]===1024),h=k?await k.data():[];l.descriptor=Array.from(h),p.forEach(W=>A.dispose(W))}w2[o]=l,f3=n,d(l)}))}function k2(e){return[Math.abs(e.endPoint[0]-e.startPoint[0]),Math.abs(e.endPoint[1]-e.startPoint[1])]}function $e(e){return[e.startPoint[0]+(e.endPoint[0]-e.startPoint[0])/2,e.startPoint[1]+(e.endPoint[1]-e.startPoint[1])/2]}function h3(e,t,o){let n=t.shape[1],r=t.shape[2],s=[[e.startPoint[1]/n,e.startPoint[0]/r,e.endPoint[1]/n,e.endPoint[0]/r]];return A.image.cropAndResize(t,s,[0],o)}function b3(e,t){let o=[e.startPoint[0]*t[0],e.startPoint[1]*t[1]],n=[e.endPoint[0]*t[0],e.endPoint[1]*t[1]],r=e.palmLandmarks.map(s=>[s[0]*t[0],s[1]*t[1]]);return{startPoint:o,endPoint:n,palmLandmarks:r,confidence:e.confidence}}function E2(e,t=1.5){let o=$e(e),n=k2(e),r=[t*n[0]/2,t*n[1]/2],s=[o[0]-r[0],o[1]-r[1]],a=[o[0]+r[0],o[1]+r[1]];return{startPoint:s,endPoint:a,palmLandmarks:e.palmLandmarks}}function z2(e){let t=$e(e),o=k2(e),r=Math.max(...o)/2,s=[t[0]-r,t[1]-r],a=[t[0]+r,t[1]+r];return{startPoint:s,endPoint:a,palmLandmarks:e.palmLandmarks}}function Dr(e){return e-2*Math.PI*Math.floor((e+Math.PI)/(2*Math.PI))}function g3(e,t){let o=Math.PI/2-Math.atan2(-(t[1]-e[1]),t[0]-e[0]);return Dr(o)}var p3=(e,t)=>[[1,0,e],[0,1,t],[0,0,1]];function _0(e,t){let o=0;for(let n=0;n[a.x,a.y]),this.anchorsTensor=A.tensor2d(this.anchors),this.inputSize=((s=(r=(n=(o=this==null?void 0:this.model)==null?void 0:o.inputs)==null?void 0:n[0])==null?void 0:r.shape)==null?void 0:s[2])||0,this.inputSizeTensor=A.tensor1d([this.inputSize,this.inputSize]),this.doubleInputSizeTensor=A.tensor1d([this.inputSize*2,this.inputSize*2])}normalizeBoxes(t){let o={};o.boxOffsets=A.slice(t,[0,0],[-1,2]),o.boxSizes=A.slice(t,[0,2],[-1,2]),o.div=A.div(o.boxOffsets,this.inputSizeTensor),o.boxCenterPoints=A.add(o.div,this.anchorsTensor),o.halfBoxSizes=A.div(o.boxSizes,this.doubleInputSizeTensor),o.sub=A.sub(o.boxCenterPoints,o.halfBoxSizes),o.startPoints=A.mul(o.sub,this.inputSizeTensor),o.add=A.add(o.boxCenterPoints,o.halfBoxSizes),o.endPoints=A.mul(o.add,this.inputSizeTensor);let n=A.concat2d([o.startPoints,o.endPoints],1);return Object.keys(o).forEach(r=>A.dispose(o[r])),n}normalizeLandmarks(t,o){let n={};n.reshape=A.reshape(t,[-1,7,2]),n.div=A.div(n.reshape,this.inputSizeTensor),n.landmarks=A.add(n.div,this.anchors[o]?this.anchors[o]:0);let r=A.mul(n.landmarks,this.inputSizeTensor);return Object.keys(n).forEach(s=>A.dispose(n[s])),r}async predict(t,o){var i;let n={};n.resize=A.image.resizeBilinear(t,[this.inputSize,this.inputSize]),n.div=A.div(n.resize,F.tf127),n.image=A.sub(n.div,F.tf1),n.batched=this.model.execute(n.image),n.predictions=A.squeeze(n.batched),n.slice=A.slice(n.predictions,[0,0],[-1,1]),n.sigmoid=A.sigmoid(n.slice),n.scores=A.squeeze(n.sigmoid);let r=await n.scores.data();n.boxes=A.slice(n.predictions,[0,1],[-1,4]),n.norm=this.normalizeBoxes(n.boxes),n.nms=await A.image.nonMaxSuppressionAsync(n.norm,n.scores,3*(((i=o.hand)==null?void 0:i.maxDetected)||1),o.hand.iouThreshold,o.hand.minConfidence);let s=await n.nms.array(),a=[];for(let c of s){let x={};x.box=A.slice(n.norm,[c,0],[1,-1]),x.slice=A.slice(n.predictions,[c,5],[1,14]),x.norm=this.normalizeLandmarks(x.slice,c),x.palmLandmarks=A.reshape(x.norm,[-1,2]);let d=await x.box.data(),l=d.slice(0,2),f=d.slice(2,4),y=await x.palmLandmarks.array(),p={startPoint:l,endPoint:f,palmLandmarks:y,confidence:r[c]},g=b3(p,[(t.shape[2]||1)/this.inputSize,(t.shape[1]||0)/this.inputSize]);a.push(g),Object.keys(x).forEach(M=>A.dispose(x[M]))}return Object.keys(n).forEach(c=>A.dispose(n[c])),a}};var qr=5,P3=1.65,T3=[0,5,9,13,17,1,2],Ur=0,Yr=2,w3=0,C2=class{constructor(t,o){w(this,"handDetector");w(this,"handPoseModel");w(this,"inputSize");w(this,"storedBoxes");w(this,"skipped");w(this,"detectedHands");var n,r,s;this.handDetector=t,this.handPoseModel=o,this.inputSize=((s=(r=(n=this.handPoseModel)==null?void 0:n.inputs)==null?void 0:r[0].shape)==null?void 0:s[2])||0,this.storedBoxes=[],this.skipped=Number.MAX_SAFE_INTEGER,this.detectedHands=0}calculateLandmarksBoundingBox(t){let o=t.map(a=>a[0]),n=t.map(a=>a[1]),r=[Math.min(...o),Math.min(...n)],s=[Math.max(...o),Math.max(...n)];return{startPoint:r,endPoint:s}}getBoxForPalmLandmarks(t,o){let n=t.map(s=>X5([...s,1],o)),r=this.calculateLandmarksBoundingBox(n);return E2(z2(r),qr)}getBoxForHandLandmarks(t){let o=this.calculateLandmarksBoundingBox(t),n=E2(z2(o),P3);n.palmLandmarks=[];for(let r=0;r[a[0]*(y[0]-this.inputSize/2),a[1]*(y[1]-this.inputSize/2),a[2]*y[2]]),c=Z5(n,[0,0]),x=i.map(y=>[...X5(y,c),y[2]]),d=M3(r),l=[...$e(o),1],f=[_0(l,d[0]),_0(l,d[1])];return x.map(y=>[Math.trunc(y[0]+f[0]),Math.trunc(y[1]+f[1]),Math.trunc(y[2])])}async estimateHands(t,o){let n=!1,r,s=(o.hand.skipTime||0)>v()-w3,a=this.skipped<(o.hand.skipFrames||0);o.skipAllowed&&s&&a&&(r=await this.handDetector.predict(t,o),this.skipped=0),o.skipAllowed&&this.skipped++,r&&r.length>0&&(r.length!==this.detectedHands&&this.detectedHands!==o.hand.maxDetected||!o.hand.landmarks)&&(this.detectedHands=0,this.storedBoxes=[...r],this.storedBoxes.length>0&&(n=!0));let i=[];for(let c=0;c=o.hand.minConfidence/4){let z=A.reshape(m,[-1,3]),k=await z.array();A.dispose(m),A.dispose(z);let h=this.transformRawCoords(k,g,d,p),W=this.getBoxForHandLandmarks(h);this.storedBoxes[c]={...W,confidence:u};let B={landmarks:h,confidence:u,boxConfidence:x.confidence,fingerConfidence:u,box:{topLeft:W.startPoint,bottomRight:W.endPoint}};i.push(B)}else this.storedBoxes[c]=null;A.dispose(m)}else{let d=E2(z2(x),P3),l={confidence:x.confidence,boxConfidence:x.confidence,fingerConfidence:0,box:{topLeft:d.startPoint,bottomRight:d.endPoint},landmarks:[]};i.push(l)}}return this.storedBoxes=this.storedBoxes.filter(c=>c!==null),this.detectedHands=i.length,i.length>o.hand.maxDetected&&(i.length=o.hand.maxDetected),i}};var l0={thumb:0,index:1,middle:2,ring:3,pinky:4,all:[0,1,2,3,4],nameMapping:{0:"thumb",1:"index",2:"middle",3:"ring",4:"pinky"},pointsMapping:{0:[[0,1],[1,2],[2,3],[3,4]],1:[[0,5],[5,6],[6,7],[7,8]],2:[[0,9],[9,10],[10,11],[11,12]],3:[[0,13],[13,14],[14,15],[15,16]],4:[[0,17],[17,18],[18,19],[19,20]]},getName:e=>l0.nameMapping[e],getPoints:e=>l0.pointsMapping[e]},ee={none:0,half:1,full:2,nameMapping:{0:"none",1:"half",2:"full"},getName:e=>ee.nameMapping[e]},q={verticalUp:0,verticalDown:1,horizontalLeft:2,horizontalRight:3,diagonalUpRight:4,diagonalUpLeft:5,diagonalDownRight:6,diagonalDownLeft:7,nameMapping:{0:"verticalUp",1:"verticalDown",2:"horizontalLeft",3:"horizontalRight",4:"diagonalUpRight",5:"diagonalUpLeft",6:"diagonalDownRight",7:"diagonalDownLeft"},getName:e=>q.nameMapping[e]},$0=class{constructor(t){w(this,"name");w(this,"curls");w(this,"directions");w(this,"weights");w(this,"weightsRelative");this.name=t,this.curls={},this.directions={},this.weights=[1,1,1,1,1],this.weightsRelative=[1,1,1,1,1]}curl(t,o,n){typeof this.curls[t]=="undefined"&&(this.curls[t]=[]),this.curls[t].push([o,n])}direction(t,o,n){this.directions[t]||(this.directions[t]=[]),this.directions[t].push([o,n])}weight(t,o){this.weights[t]=o;let n=this.weights.reduce((r,s)=>r+s,0);this.weightsRelative=this.weights.map(r=>r*5/n)}matchAgainst(t,o){let n=0;for(let r in t){let s=t[r],a=this.curls[r];if(typeof a=="undefined"){n+=this.weightsRelative[r];continue}for(let[i,c]of a)if(s===i){n+=c*this.weightsRelative[r];break}}for(let r in o){let s=o[r],a=this.directions[r];if(typeof a=="undefined"){n+=this.weightsRelative[r];continue}for(let[i,c]of a)if(s===i){n+=c*this.weightsRelative[r];break}}return n/10}};var{thumb:C0,index:B0,middle:H0,ring:ye,pinky:de}=l0,{none:I0,half:Jr,full:j0}=ee,{verticalUp:Ee,verticalDown:u4,horizontalLeft:q5,horizontalRight:Qr,diagonalUpRight:_r,diagonalUpLeft:ze,diagonalDownRight:h4,diagonalDownLeft:b4}=q,te=new $0("thumbs up");te.curl(C0,I0,1);te.direction(C0,Ee,1);te.direction(C0,ze,.25);te.direction(C0,_r,.25);for(let e of[l0.index,l0.middle,l0.ring,l0.pinky])te.curl(e,j0,1),te.direction(e,q5,1),te.direction(e,Qr,1);var J=new $0("victory");J.curl(C0,Jr,.5);J.curl(C0,I0,.5);J.direction(C0,Ee,1);J.direction(C0,ze,1);J.curl(B0,I0,1);J.direction(B0,Ee,.75);J.direction(B0,ze,1);J.curl(H0,I0,1);J.direction(H0,Ee,1);J.direction(H0,ze,.75);J.curl(ye,j0,1);J.direction(ye,Ee,.2);J.direction(ye,ze,1);J.direction(ye,q5,.2);J.curl(de,j0,1);J.direction(de,Ee,.2);J.direction(de,ze,1);J.direction(de,q5,.2);J.weight(B0,2);J.weight(H0,2);var oe=new $0("point");oe.curl(C0,j0,1);oe.curl(B0,I0,.5);oe.curl(H0,j0,.5);oe.curl(ye,j0,.5);oe.curl(de,j0,.5);oe.weight(B0,2);oe.weight(H0,2);var ne=new $0("middle finger");ne.curl(C0,I0,1);ne.curl(B0,j0,.5);ne.curl(H0,j0,.5);ne.curl(ye,j0,.5);ne.curl(de,j0,.5);ne.weight(B0,2);ne.weight(H0,2);var Se=new $0("open palm");Se.curl(C0,I0,.75);Se.curl(B0,I0,.75);Se.curl(H0,I0,.75);Se.curl(ye,I0,.75);Se.curl(de,I0,.75);var k3=[te,J,oe,ne,Se];var $r=.7,fe={HALF_CURL_START_LIMIT:60,NO_CURL_START_LIMIT:130,DISTANCE_VOTE_POWER:1.1,SINGLE_ANGLE_VOTE_POWER:.9,TOTAL_ANGLE_VOTE_POWER:1.6};function E3(e,t,o,n){let r=(t-n)/(e-o),s=Math.atan(r)*180/Math.PI;return s<=0?s=-s:s>0&&(s=180-s),s}function S3(e,t){if(!e||!t)return[0,0];let o=E3(e[0],e[1],t[0],t[1]);if(e.length===2)return o;let n=E3(e[1],e[2],t[1],t[2]);return[o,n]}function z3(e,t=1){let o=0,n=0,r=0;return e>=75&&e<=105?o=1*t:e>=25&&e<=155?n=1*t:r=1*t,[o,n,r]}function eA(e,t,o){let n=e[0]-t[0],r=e[0]-o[0],s=t[0]-o[0],a=e[1]-t[1],i=e[1]-o[1],c=t[1]-o[1],x=e[2]-t[2],d=e[2]-o[2],l=t[2]-o[2],f=Math.sqrt(n*n+a*a+x*x),y=Math.sqrt(r*r+i*i+d*d),p=Math.sqrt(s*s+c*c+l*l),g=(p*p+f*f-y*y)/(2*p*f);g>1?g=1:g<-1&&(g=-1);let M=Math.acos(g);M=57.2958*M%180;let R;return M>fe.NO_CURL_START_LIMIT?R=ee.none:M>fe.HALF_CURL_START_LIMIT?R=ee.half:R=ee.full,R}function C3(e,t,o,n){let r;return n===Math.abs(e)?e>0?r=q.horizontalLeft:r=q.horizontalRight:n===Math.abs(t)?t>0?r=q.horizontalLeft:r=q.horizontalRight:o>0?r=q.horizontalLeft:r=q.horizontalRight,r}function I3(e,t,o,n){let r;return n===Math.abs(e)?e<0?r=q.verticalDown:r=q.verticalUp:n===Math.abs(t)?t<0?r=q.verticalDown:r=q.verticalUp:o<0?r=q.verticalDown:r=q.verticalUp,r}function tA(e,t,o,n,r,s,a,i){let c,x=I3(e,t,o,n),d=C3(r,s,a,i);return x===q.verticalUp?d===q.horizontalLeft?c=q.diagonalUpLeft:c=q.diagonalUpRight:d===q.horizontalLeft?c=q.diagonalDownLeft:c=q.diagonalDownRight,c}function oA(e,t,o,n){let r=e[0]-t[0],s=e[0]-o[0],a=t[0]-o[0],i=e[1]-t[1],c=e[1]-o[1],x=t[1]-o[1],d=Math.max(Math.abs(r),Math.abs(s),Math.abs(a)),l=Math.max(Math.abs(i),Math.abs(c),Math.abs(x)),f=0,y=0,p=0,g=l/(d+1e-5);g>1.5?f+=fe.DISTANCE_VOTE_POWER:g>.66?y+=fe.DISTANCE_VOTE_POWER:p+=fe.DISTANCE_VOTE_POWER;let M=Math.sqrt(r*r+i*i),R=Math.sqrt(s*s+c*c),P=Math.sqrt(a*a+x*x),m=Math.max(M,R,P),u=e[0],z=e[1],k=o[0],h=o[1];m===M?(k=o[0],h=o[1]):m===P&&(u=t[0],z=t[1]);let j=S3([u,z],[k,h]),N=z3(j,fe.TOTAL_ANGLE_VOTE_POWER);f+=N[0],y+=N[1],p+=N[2];for(let O of n){let I=z3(O,fe.SINGLE_ANGLE_VOTE_POWER);f+=I[0],y+=I[1],p+=I[2]}let G;return f===Math.max(f,y,p)?G=I3(c,i,x,l):p===Math.max(y,p)?G=C3(s,r,a,d):G=tA(c,i,x,l,s,r,a,d),G}function j3(e){let t=[],o=[],n=[],r=[];if(!e)return{curls:n,directions:r};for(let s of l0.all){let a=l0.getPoints(s),i=[],c=[];for(let x of a){let d=e[x[0]],l=e[x[1]],f=S3(d,l),y=f[0],p=f[1];i.push(y),c.push(p)}t.push(i),o.push(c)}for(let s of l0.all){let a=s===l0.thumb?1:0,i=l0.getPoints(s),c=e[i[a][0]],x=e[i[a+1][1]],d=e[i[3][1]],l=eA(c,x,d),f=oA(c,x,d,t[s].slice(a));n[s]=l,r[s]=f}return{curls:n,directions:r}}function I2(e){if(!e||e.length===0)return null;let t=j3(e),o={};for(let n of l0.all)o[l0.getName(n)]={curl:ee.getName(t.curls[n]),direction:q.getName(t.directions[n])};return o}function O3(e){let t=[];if(!e||e.length===0)return t;let o=j3(e);for(let n of k3){let r=n.matchAgainst(o.curls,o.directions);r>=$r&&t.push({name:n.name,confidence:r})}return t}var N3={thumb:[1,2,3,4],index:[5,6,7,8],middle:[9,10,11,12],ring:[13,14,15,16],pinky:[17,18,19,20],palm:[0]},me,pe,L3;async function Y5(e,t){let o=await L3.estimateHands(e,t);if(!o)return[];let n=[];for(let r=0;ro[r].landmarks[l]);let a=o[r].landmarks,i=[Number.MAX_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,0,0],c=[0,0,0,0];if(a&&a.length>0){for(let d of a)d[0]i[2]&&(i[2]=d[0]),d[1]>i[3]&&(i[3]=d[1]);i[2]-=i[0],i[3]-=i[1],c=[i[0]/(e.shape[2]||0),i[1]/(e.shape[1]||0),i[2]/(e.shape[2]||0),i[3]/(e.shape[1]||0)]}else i=o[r].box?[Math.trunc(Math.max(0,o[r].box.topLeft[0])),Math.trunc(Math.max(0,o[r].box.topLeft[1])),Math.trunc(Math.min(e.shape[2]||0,o[r].box.bottomRight[0])-Math.max(0,o[r].box.topLeft[0])),Math.trunc(Math.min(e.shape[1]||0,o[r].box.bottomRight[1])-Math.max(0,o[r].box.topLeft[1]))]:[0,0,0,0],c=[o[r].box.topLeft[0]/(e.shape[2]||0),o[r].box.topLeft[1]/(e.shape[1]||0),(o[r].box.bottomRight[0]-o[r].box.topLeft[0])/(e.shape[2]||0),(o[r].box.bottomRight[1]-o[r].box.topLeft[1])/(e.shape[1]||0)];let x=I2(a);n.push({id:r,score:Math.round(100*o[r].confidence)/100,boxScore:Math.round(100*o[r].boxConfidence)/100,fingerScore:Math.round(100*o[r].fingerConfidence)/100,label:"hand",box:i,boxRaw:c,keypoints:a,annotations:s,landmarks:x})}return n}async function K5(e){var o,n;T.initial&&(me=null,pe=null),!me||!pe?[me,pe]=await Promise.all([e.hand.enabled?L((o=e.hand.detector)==null?void 0:o.modelPath):null,e.hand.landmarks?L((n=e.hand.skeleton)==null?void 0:n.modelPath):null]):(e.debug&&b("cached model:",me.modelUrl),e.debug&&b("cached model:",pe.modelUrl));let t=me?new S2(me):void 0;return t&&pe&&(L3=new C2(t,pe)),[me,pe]}var D={name:"humangl",priority:999,canvas:null,gl:null,extensions:[],webGLattr:{alpha:!1,antialias:!1,premultipliedAlpha:!1,preserveDrawingBuffer:!1,depth:!1,stencil:!1,failIfMajorPerformanceCaveat:!1,desynchronized:!0}};function nA(){let e=D.gl;!e||(D.extensions=e.getSupportedExtensions())}function F3(e){var t;if(e.config.backend==="humangl"&&(D.name in A.engine().registry&&!((t=D==null?void 0:D.gl)!=null&&t.getParameter(D.gl.VERSION))&&(b("error: humangl backend invalid context"),j2(e)),!A.findBackend(D.name))){try{D.canvas=s0(100,100)}catch(n){b("error: cannot create canvas:",n);return}try{if(D.gl=D.canvas.getContext("webgl2",D.webGLattr),!D.gl){b("error: cannot get WebGL context");return}if(!D.gl.getParameter(D.gl.VERSION).includes("2.0")){b("override: using fallback webgl backend as webgl 2.0 is not detected"),e.config.backend="webgl";return}D.canvas&&(D.canvas.addEventListener("webglcontextlost",r=>{throw b("error: humangl:",r.type),b("possible browser memory leak using webgl or conflict with multiple backend registrations"),e.emit("error"),new Error("backend error: webgl context lost")}),D.canvas.addEventListener("webglcontextrestored",r=>{b("error: humangl context restored:",r)}),D.canvas.addEventListener("webglcontextcreationerror",r=>{b("error: humangl context create:",r)}))}catch(n){b("error: cannot get WebGL context:",n);return}try{A.setWebGLContext(2,D.gl)}catch(n){b("error: cannot set WebGL context:",n);return}try{let n=new A.GPGPUContext(D.gl);A.registerBackend(D.name,()=>new A.MathBackendWebGL(n),D.priority)}catch(n){b("error: cannot register WebGL backend:",n);return}try{A.getKernelsForBackend("webgl").forEach(r=>{let s={...r,backendName:D.name};A.registerKernel(s)})}catch(n){b("error: cannot update WebGL backend registration:",n);return}let o=A.backend().getGPGPUContext?A.backend().getGPGPUContext().gl:null;if(o)b(`humangl webgl version:${o.getParameter(o.VERSION)} renderer:${o.getParameter(o.RENDERER)}`);else{b("error: no current gl context:",o,D.gl);return}try{A.env().flagRegistry.WEBGL_VERSION&&A.env().set("WEBGL_VERSION",2)}catch(n){b("error: cannot set WebGL backend flags:",n);return}nA(),b("backend registered:",D.name)}}function rA(e){if(!T.kernels.includes("mod")){let t={kernelName:"Mod",backendName:A.getBackend(),kernelFunc:o=>A.tidy(()=>A.sub(o.inputs.a,A.mul(A.div(o.inputs.a,o.inputs.b),o.inputs.b)))};e.debug&&b("registered kernel:","Mod"),A.registerKernel(t),T.kernels.push("mod")}if(!T.kernels.includes("floormod")){let t={kernelName:"FloorMod",backendName:A.getBackend(),kernelFunc:o=>A.tidy(()=>A.add(A.mul(A.floorDiv(o.inputs.a/o.inputs.b),o.inputs.b),A.mod(o.inputs.a,o.inputs.b)))};e.debug&&b("registered kernel:","FloorMod"),A.registerKernel(t),T.kernels.push("floormod")}if(!T.kernels.includes("rotatewithoffset")&&e.softwareKernels){let t={kernelName:"RotateWithOffset",backendName:A.getBackend(),kernelFunc:o=>A.tidy(()=>{let n=A.getBackend();A.setBackend("cpu");let r=A.image.rotateWithOffset(o.inputs.image,o.attrs.radians,o.attrs.fillValue,o.attrs.center);return A.setBackend(n),r})};e.debug&&b("registered kernel:","RotateWithOffset"),A.registerKernel(t),T.kernels.push("rotatewithoffset")}}async function O2(e,t=!1){if(e.state="backend",t||T.initial||e.config.backend&&e.config.backend.length>0&&A.getBackend()!==e.config.backend){let o=v();if(e.config.backend&&e.config.backend.length>0){if(typeof window=="undefined"&&typeof WorkerGlobalScope!="undefined"&&e.config.debug&&e.config.debug&&b("running inside web worker"),T.browser&&e.config.backend==="tensorflow"&&(e.config.debug&&b("override: backend set to tensorflow while running in browser"),e.config.backend="humangl"),T.node&&(e.config.backend==="webgl"||e.config.backend==="humangl")&&(e.config.debug&&b(`override: backend set to ${e.config.backend} while running in nodejs`),e.config.backend="tensorflow"),T.browser&&e.config.backend==="webgpu")if(typeof navigator=="undefined"||typeof navigator.gpu=="undefined")b("override: backend set to webgpu but browser does not support webgpu"),e.config.backend="humangl";else{let r=await navigator.gpu.requestAdapter();if(e.config.debug&&b("enumerated webgpu adapter:",r),!r)b("override: backend set to webgpu but browser reports no available gpu"),e.config.backend="humangl";else{let s="requestAdapterInfo"in r?await r.requestAdapterInfo():void 0;b("webgpu adapter info:",s)}}e.config.backend==="humangl"&&F3(e);let n=Object.keys(A.engine().registryFactory);if(e.config.debug&&b("available backends:",n),n.includes(e.config.backend)||(b(`error: backend ${e.config.backend} not found in registry`),e.config.backend=T.node?"tensorflow":"webgl",e.config.debug&&b(`override: setting backend ${e.config.backend}`)),e.config.debug&&b("setting backend:",e.config.backend),e.config.backend==="wasm"){if(A.env().flagRegistry.CANVAS2D_WILL_READ_FREQUENTLY&&A.env().set("CANVAS2D_WILL_READ_FREQUENTLY",!0),e.config.debug&&b("wasm path:",e.config.wasmPath),typeof A.setWasmPaths!="undefined")A.setWasmPaths(e.config.wasmPath,e.config.wasmPlatformFetch);else throw new Error("backend error: attempting to use wasm backend but wasm path is not set");let r=!1,s=!1;try{r=await A.env().getAsync("WASM_HAS_MULTITHREAD_SUPPORT"),s=await A.env().getAsync("WASM_HAS_SIMD_SUPPORT"),e.config.debug&&b(`wasm execution: ${s?"simd":"no simd"} ${r?"multithreaded":"singlethreaded"}`),e.config.debug&&!s&&b("warning: wasm simd support is not enabled")}catch(a){b("wasm detection failed")}}try{await A.setBackend(e.config.backend),await A.ready(),$1()}catch(r){return b("error: cannot set backend:",e.config.backend,r),!1}}if(A.getBackend()==="humangl"&&(A.env().flagRegistry.CHECK_COMPUTATION_FOR_ERRORS&&A.env().set("CHECK_COMPUTATION_FOR_ERRORS",!1),A.env().flagRegistry.WEBGL_CPU_FORWARD&&A.env().set("WEBGL_CPU_FORWARD",!0),A.env().flagRegistry.WEBGL_USE_SHAPES_UNIFORMS&&A.env().set("WEBGL_USE_SHAPES_UNIFORMS",!0),A.env().flagRegistry.CPU_HANDOFF_SIZE_THRESHOLD&&A.env().set("CPU_HANDOFF_SIZE_THRESHOLD",256),A.env().flagRegistry.WEBGL_EXP_CONV&&A.env().set("WEBGL_EXP_CONV",!0),A.env().flagRegistry.USE_SETTIMEOUTCUSTOM&&A.env().set("USE_SETTIMEOUTCUSTOM",!0),typeof e.config.deallocate!="undefined"&&e.config.deallocate&&(b("changing webgl: WEBGL_DELETE_TEXTURE_THRESHOLD:",!0),A.env().set("WEBGL_DELETE_TEXTURE_THRESHOLD",0)),A.backend().getGPGPUContext)){let n=await A.backend().getGPGPUContext().gl;e.config.debug&&b(`gl version:${n.getParameter(n.VERSION)} renderer:${n.getParameter(n.RENDERER)}`)}A.getBackend(),A.enableProdMode(),await A.ready(),e.performance.initBackend=Math.trunc(v()-o),e.config.backend=A.getBackend(),await T.updateBackend(),rA(e.config)}return!0}function N2(e,t){for(let o of e){let n={kernelName:o,backendName:t.backend,kernelFunc:()=>{t.debug&&b("kernelFunc",o,t.backend)}};A.registerKernel(n)}T.kernels=A.getKernelsForBackend(A.getBackend()).map(o=>o.kernelName.toLowerCase())}var K=[null,null],sA=["StatefulPartitionedCall/Postprocessor/Slice","StatefulPartitionedCall/Postprocessor/ExpandDims_1"],re=[[0,0],[0,0]],aA=["hand","fist","pinch","point","face","tip","pinchtip"],B3=4,H3=1.6,iA=512,lA=1.4,L2=Number.MAX_SAFE_INTEGER,Q5=0,D0=[0,0],Y={boxes:[],hands:[]},D3={thumb:[1,2,3,4],index:[5,6,7,8],middle:[9,10,11,12],ring:[13,14,15,16],pinky:[17,18,19,20],base:[0],palm:[0,17,13,9,5,1,0]};async function V3(e){var t;if(T.initial&&(K[0]=null),K[0])e.debug&&b("cached model:",K[0].modelUrl);else{N2(["tensorlistreserve","enter","tensorlistfromtensor","merge","loopcond","switch","exit","tensorliststack","nextiteration","tensorlistsetitem","tensorlistgetitem","reciprocal","shape","split","where"],e),K[0]=await L((t=e.hand.detector)==null?void 0:t.modelPath);let o=K[0].executor?Object.values(K[0].modelSignature.inputs):void 0;re[0][0]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[1].size):0,re[0][1]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[2].size):0}return K[0]}async function Z3(e){var t;if(T.initial&&(K[1]=null),K[1])e.debug&&b("cached model:",K[1].modelUrl);else{K[1]=await L((t=e.hand.skeleton)==null?void 0:t.modelPath);let o=K[1].executor?Object.values(K[1].modelSignature.inputs):void 0;re[1][0]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[1].size):0,re[1][1]=Array.isArray(o)?parseInt(o[0].tensorShape.dim[2].size):0}return K[1]}async function cA(e,t){let o=[];if(!e||!K[0])return o;let n={},r=(e.shape[2]||1)/(e.shape[1]||1),s=Math.min(Math.round((e.shape[1]||0)/8)*8,iA),a=Math.round(s*r/8)*8;n.resize=A.image.resizeBilinear(e,[s,a]),n.cast=A.cast(n.resize,"int32"),[n.rawScores,n.rawBoxes]=await K[0].executeAsync(n.cast,sA),n.boxes=A.squeeze(n.rawBoxes,[0,2]),n.scores=A.squeeze(n.rawScores,[0]);let i=A.unstack(n.scores,1);A.dispose(i[B3]),i.splice(B3,1),n.filtered=A.stack(i,1),A.dispose(i),n.max=A.max(n.filtered,1),n.argmax=A.argMax(n.filtered,1);let c=0;n.nms=await A.image.nonMaxSuppressionAsync(n.boxes,n.max,(t.hand.maxDetected||0)+1,t.hand.iouThreshold||0,t.hand.minConfidence||1);let x=await n.nms.data(),d=await n.max.data(),l=await n.argmax.data();for(let f of Array.from(x)){let y=A.slice(n.boxes,f,1),p=await y.data();A.dispose(y);let g=[p[1],p[0],p[3]-p[1],p[2]-p[0]],M=g2(g,lA),R=[Math.trunc(g[0]*D0[0]),Math.trunc(g[1]*D0[1]),Math.trunc(g[2]*D0[0]),Math.trunc(g[3]*D0[1])],P=d[f],m=aA[l[f]],u={id:c++,score:P,box:R,boxRaw:M,label:m};o.push(u)}return Object.keys(n).forEach(f=>A.dispose(n[f])),o.sort((f,y)=>y.score-f.score),o.length>(t.hand.maxDetected||1)&&(o.length=t.hand.maxDetected||1),o}async function _5(e,t,o){let n={id:t.id,score:Math.round(100*t.score)/100,boxScore:Math.round(100*t.score)/100,fingerScore:0,box:t.box,boxRaw:t.boxRaw,label:t.label,keypoints:[],landmarks:{},annotations:{}};if(e&&K[1]&&o.hand.landmarks&&t.score>(o.hand.minConfidence||0)){let r={},s=[t.boxRaw[1],t.boxRaw[0],t.boxRaw[3]+t.boxRaw[1],t.boxRaw[2]+t.boxRaw[0]];r.crop=A.image.cropAndResize(e,[s],[0],[re[1][0],re[1][1]],"bilinear"),r.div=A.div(r.crop,F.tf255),[r.score,r.keypoints]=K[1].execute(r.div,["Identity_1","Identity"]);let a=(await r.score.data())[0],i=(100-Math.trunc(100/(1+Math.exp(a))))/100;if(i>=(o.hand.minConfidence||0)){n.fingerScore=i,r.reshaped=A.reshape(r.keypoints,[-1,3]);let d=(await r.reshaped.array()).map(l=>[l[0]/re[1][1],l[1]/re[1][0],l[2]||0]).map(l=>[l[0]*t.boxRaw[2],l[1]*t.boxRaw[3],l[2]||0]);n.keypoints=d.map(l=>[D0[0]*(l[0]+t.boxRaw[0]),D0[1]*(l[1]+t.boxRaw[1]),l[2]||0]),n.landmarks=I2(n.keypoints);for(let l of Object.keys(D3))n.annotations[l]=D3[l].map(f=>n.landmarks&&n.keypoints[f]?n.keypoints[f]:null)}Object.keys(r).forEach(c=>A.dispose(r[c]))}return n}async function $5(e,t){var r,s;if(!((r=K[0])!=null&&r.executor)||!((s=K[1])!=null&&s.executor)||!K[0].inputs[0].shape||!K[1].inputs[0].shape)return[];D0=[e.shape[2]||0,e.shape[1]||0],L2++;let o=(t.hand.skipTime||0)>v()-Q5,n=L2<(t.hand.skipFrames||0);return t.skipAllowed&&o&&n?Y.hands:new Promise(async a=>{let i=3*(t.hand.skipTime||0)>v()-Q5,c=L2<3*(t.hand.skipFrames||0);t.skipAllowed&&Y.hands.length===t.hand.maxDetected?Y.hands=await Promise.all(Y.boxes.map(d=>_5(e,d,t))):t.skipAllowed&&i&&c&&Y.hands.length>0?Y.hands=await Promise.all(Y.boxes.map(d=>_5(e,d,t))):(Y.boxes=await cA(e,t),Q5=v(),Y.hands=await Promise.all(Y.boxes.map(d=>_5(e,d,t))),L2=0);let x=[...Y.boxes];if(Y.boxes.length=0,t.cacheSensitivity>0)for(let d=0;d.05&&l.box[3]/(e.shape[1]||1)>.05&&Y.hands[d].fingerScore&&Y.hands[d].fingerScore>(t.hand.minConfidence||0)){let f=g2(l.box,H3),y=g2(l.boxRaw,H3);Y.boxes.push({...x[d],box:f,boxRaw:y})}}for(let d=0;dv()-U3,s=e1<(((i=t.face.liveness)==null?void 0:i.skipFrames)||0);return t.skipAllowed&&r&&s&&q3===n&&W2[o]?(e1++,W2[o]):(e1=0,new Promise(async c=>{let x=A.image.resizeBilinear(e,[o0!=null&&o0.inputs[0].shape?o0.inputs[0].shape[2]:0,o0!=null&&o0.inputs[0].shape?o0.inputs[0].shape[1]:0],!1),d=o0==null?void 0:o0.execute(x),l=(await d.data())[0];W2[o]=Math.round(100*l)/100,q3=n,U3=v(),A.dispose([x,d]),c(W2[o])}))}var e2={};q0(e2,{connected:()=>G2,horizontal:()=>o1,kpt:()=>F2,relative:()=>r1,vertical:()=>n1});var F2=["nose","leftEye","rightEye","leftEar","rightEar","leftShoulder","rightShoulder","leftElbow","rightElbow","leftWrist","rightWrist","leftHip","rightHip","leftKnee","rightKnee","leftAnkle","rightAnkle"],o1=[["leftEye","rightEye"],["leftEar","rightEar"],["leftShoulder","rightShoulder"],["leftElbow","rightElbow"],["leftWrist","rightWrist"],["leftHip","rightHip"],["leftKnee","rightKnee"],["leftAnkle","rightAnkle"]],n1=[["leftKnee","leftShoulder"],["rightKnee","rightShoulder"],["leftAnkle","leftKnee"],["rightAnkle","rightKnee"]],r1=[[["leftHip","rightHip"],["leftShoulder","rightShoulder"]],[["leftElbow","rightElbow"],["leftShoulder","rightShoulder"]]],G2={leftLeg:["leftHip","leftKnee","leftAnkle"],rightLeg:["rightHip","rightKnee","rightAnkle"],torso:["leftShoulder","rightShoulder","rightHip","leftHip","leftShoulder"],leftArm:["leftShoulder","leftElbow","leftWrist"],rightArm:["rightShoulder","rightElbow","rightWrist"],head:[]};var J3=.005,u0={keypoints:[],padding:[[0,0],[0,0],[0,0],[0,0]]};function A1(e){for(let t of o1){let o=e.keypoints.findIndex(r=>r.part===t[0]),n=e.keypoints.findIndex(r=>r.part===t[1]);if(e.keypoints[o]&&e.keypoints[n]&&e.keypoints[o].position[0]r&&r.part===t[0]),n=e.keypoints.findIndex(r=>r&&r.part===t[1]);e.keypoints[o]&&e.keypoints[n]&&e.keypoints[o].position[1]x&&x.part===t[0]),r=e.keypoints.findIndex(x=>x&&x.part===t[1]),s=e.keypoints.findIndex(x=>x&&x.part===o[0]),a=e.keypoints.findIndex(x=>x&&x.part===o[1]);if(!e.keypoints[s]||!e.keypoints[a])continue;let i=e.keypoints[n]?[Math.abs(e.keypoints[s].position[0]-e.keypoints[n].position[0]),Math.abs(e.keypoints[a].position[0]-e.keypoints[n].position[0])]:[0,0],c=e.keypoints[r]?[Math.abs(e.keypoints[a].position[0]-e.keypoints[r].position[0]),Math.abs(e.keypoints[s].position[0]-e.keypoints[r].position[0])]:[0,0];if(i[0]>i[1]||c[0]>c[1]){let x=e.keypoints[n];e.keypoints[n]=e.keypoints[r],e.keypoints[r]=x}}}function Q3(e){for(let t=0;te.shape[1]?Math.trunc((e.shape[2]-e.shape[1])/2):0,e.shape[2]>e.shape[1]?Math.trunc((e.shape[2]-e.shape[1])/2):0],[e.shape[1]>e.shape[2]?Math.trunc((e.shape[1]-e.shape[2])/2):0,e.shape[1]>e.shape[2]?Math.trunc((e.shape[1]-e.shape[2])/2):0],[0,0]],o.pad=A.pad(e,u0.padding),o.resize=A.image.resizeBilinear(o.pad,[t,t]);let n=A.cast(o.resize,"int32");return Object.keys(o).forEach(a=>A.dispose(o[a])),n}function $3(e,t){e.keypoints=e.keypoints.filter(n=>n==null?void 0:n.position);for(let n of e.keypoints)n.position=[n.position[0]*(t[0]+u0.padding[2][0]+u0.padding[2][1])/t[0]-u0.padding[2][0],n.position[1]*(t[1]+u0.padding[1][0]+u0.padding[1][1])/t[1]-u0.padding[1][0]],n.positionRaw=[n.position[0]/t[0],n.position[1]/t[1]];let o=G0(e.keypoints.map(n=>n.position),t);return e.box=o.box,e.boxRaw=o.boxRaw,e}var Q,B2=0,s1=Number.MAX_SAFE_INTEGER,ue={boxes:[],bodies:[],last:0};async function eo(e){var t;return T.initial&&(Q=null),Q?e.debug&&b("cached model:",Q.modelUrl):(N2(["size"],e),Q=await L(e.body.modelPath)),B2=(Q==null?void 0:Q.executor)&&((t=Q==null?void 0:Q.inputs)==null?void 0:t[0].shape)?Q.inputs[0].shape[2]:0,B2<64&&(B2=256),Q}function yA(e,t,o){let n=e[0][0],r=[],s=0;for(let d=0;dt.body.minConfidence){let l=[n[d][1],n[d][0]];r.push({score:Math.round(100*s)/100,part:F2[d],positionRaw:l,position:[Math.round((o.shape[2]||0)*l[0]),Math.round((o.shape[1]||0)*l[1])]})}s=r.reduce((d,l)=>l.score>d?l.score:d,0);let a=[],i=G0(r.map(d=>d.position),[o.shape[2],o.shape[1]]),c={};for(let[d,l]of Object.entries(G2)){let f=[];for(let y=0;yM.part===l[y]),g=r.find(M=>M.part===l[y+1]);p&&g&&p.score>(t.body.minConfidence||0)&&g.score>(t.body.minConfidence||0)&&f.push([p.position,g.position])}c[d]=f}let x={id:0,score:s,box:i.box,boxRaw:i.boxRaw,keypoints:r,annotations:c};return A1(x),a.push(x),a}function dA(e,t,o){let n=[];for(let r=0;rt.body.minConfidence){let i=[];for(let l=0;l<17;l++){let f=s[3*l+2];if(f>t.body.minConfidence){let y=[s[3*l+1],s[3*l+0]];i.push({part:F2[l],score:Math.round(100*f)/100,positionRaw:y,position:[Math.round((o.shape[2]||0)*y[0]),Math.round((o.shape[1]||0)*y[1])]})}}let c=G0(i.map(l=>l.position),[o.shape[2],o.shape[1]]),x={};for(let[l,f]of Object.entries(G2)){let y=[];for(let p=0;pR.part===f[p]),M=i.find(R=>R.part===f[p+1]);g&&M&&g.score>(t.body.minConfidence||0)&&M.score>(t.body.minConfidence||0)&&y.push([g.position,M.position])}x[l]=y}let d={id:r,score:a,box:c.box,boxRaw:c.boxRaw,keypoints:[...i],annotations:x};A1(d),n.push(d)}}return n.sort((r,s)=>s.score-r.score),n.length>t.body.maxDetected&&(n.length=t.body.maxDetected),n}async function a1(e,t){var r;if(!(Q!=null&&Q.executor)||!((r=Q==null?void 0:Q.inputs)!=null&&r[0].shape))return[];t.skipAllowed||(ue.boxes.length=0),s1++;let o=(t.body.skipTime||0)>v()-ue.last,n=s1<(t.body.skipFrames||0);return t.skipAllowed&&o&&n?ue.bodies:new Promise(async s=>{let a={};s1=0,a.input=_3(e,B2),a.res=Q==null?void 0:Q.execute(a.input),ue.last=v();let i=await a.res.array();ue.bodies=a.res.shape[2]===17?yA(i,t,e):dA(i,t,e);for(let c of ue.bodies)$3(c,[e.shape[2]||1,e.shape[1]||1]),Q3(c.keypoints);Object.keys(a).forEach(c=>A.dispose(a[c])),s(ue.bodies)})}var k0,H2=[],oo=0,i1=Number.MAX_SAFE_INTEGER,V2=0,D2=2.5;async function no(e){if(!k0||T.initial){k0=await L(e.object.modelPath);let t=k0!=null&&k0.executor?Object.values(k0.modelSignature.inputs):void 0;V2=Array.isArray(t)?parseInt(t[0].tensorShape.dim[2].size):416}else e.debug&&b("cached model:",k0.modelUrl);return k0}async function fA(e,t,o){let n=0,r=[],s=V2;for(let x of[1,2,4]){let d=x*13,l=A.squeeze(e.find(R=>R.shape[1]===d**2&&(R.shape[2]||0)===Te.length)),f=await l.array(),y=A.squeeze(e.find(R=>R.shape[1]===d**2&&(R.shape[2]||0)(o.object.minConfidence||0)&&P!==61){let u=(.5+Math.trunc(R%d))/d,z=(.5+Math.trunc(R/d))/d,k=M[R].map(I=>I*(d/x/s)),[h,W]=[u-D2/x*k[0],z-D2/x*k[1]],[B,j]=[u+D2/x*k[2]-h,z+D2/x*k[3]-W],N=[h,W,B,j];N=N.map(I=>Math.max(0,Math.min(I,1)));let G=[N[0]*t[0],N[1]*t[1],N[2]*t[0],N[3]*t[1]],O={id:n++,score:Math.round(100*m)/100,class:P+1,label:Te[P].label,box:G.map(I=>Math.trunc(I)),boxRaw:N};r.push(O)}}A.dispose([l,y,p,g])}let a=r.map(x=>[x.boxRaw[1],x.boxRaw[0],x.boxRaw[3],x.boxRaw[2]]),i=r.map(x=>x.score),c=[];if(a&&a.length>0){let x=await A.image.nonMaxSuppressionAsync(a,i,o.object.maxDetected,o.object.iouThreshold,o.object.minConfidence);c=await x.data(),A.dispose(x)}return r=r.filter((x,d)=>c.includes(d)).sort((x,d)=>d.score-x.score),r}async function l1(e,t){if(!(k0!=null&&k0.executor))return[];let o=(t.object.skipTime||0)>v()-oo,n=i1<(t.object.skipFrames||0);return t.skipAllowed&&o&&n&&H2.length>0?(i1++,H2):(i1=0,!T.kernels.includes("mod")||!T.kernels.includes("sparsetodense")?H2:new Promise(async r=>{let s=[e.shape[2]||0,e.shape[1]||0],a=A.image.resizeBilinear(e,[V2,V2],!1),i=A.div(a,F.tf255),c=A.transpose(i,[0,3,1,2]),x;t.object.enabled&&(x=k0.execute(c)),oo=v();let d=await fA(x,s,t);H2=d,A.dispose([a,i,c,...x]),r(d)}))}var o2=["nose","leftEye","rightEye","leftEar","rightEar","leftShoulder","rightShoulder","leftElbow","rightElbow","leftWrist","rightWrist","leftHip","rightHip","leftKnee","rightKnee","leftAnkle","rightAnkle"],mA=o2.length,t2=o2.reduce((e,t,o)=>(e[t]=o,e),{}),pA=[["leftHip","leftShoulder"],["leftElbow","leftShoulder"],["leftElbow","leftWrist"],["leftHip","leftKnee"],["leftKnee","leftAnkle"],["rightHip","rightShoulder"],["rightElbow","rightShoulder"],["rightElbow","rightWrist"],["rightHip","rightKnee"],["rightKnee","rightAnkle"],["leftShoulder","rightShoulder"],["leftHip","rightHip"]],q4=pA.map(([e,t])=>[t2[e],t2[t]]),Ao=[["nose","leftEye"],["leftEye","leftEar"],["nose","rightEye"],["rightEye","rightEar"],["nose","leftShoulder"],["leftShoulder","leftElbow"],["leftElbow","leftWrist"],["leftShoulder","leftHip"],["leftHip","leftKnee"],["leftKnee","leftAnkle"],["nose","rightShoulder"],["rightShoulder","rightElbow"],["rightElbow","rightWrist"],["rightShoulder","rightHip"],["rightHip","rightKnee"],["rightKnee","rightAnkle"]];function so(e){let t=e.reduce(({maxX:o,maxY:n,minX:r,minY:s},{position:{x:a,y:i}})=>({maxX:Math.max(o,a),maxY:Math.max(n,i),minX:Math.min(r,a),minY:Math.min(s,i)}),{maxX:Number.NEGATIVE_INFINITY,maxY:Number.NEGATIVE_INFINITY,minX:Number.POSITIVE_INFINITY,minY:Number.POSITIVE_INFINITY});return[t.minX,t.minY,t.maxX-t.minX,t.maxY-t.minY]}function ao(e,[t,o],[n,r]){let s=t/n,a=o/r,i=(x,d)=>({id:d,score:x.score,boxRaw:[x.box[0]/r,x.box[1]/n,x.box[2]/r,x.box[3]/n],box:[Math.trunc(x.box[0]*a),Math.trunc(x.box[1]*s),Math.trunc(x.box[2]*a),Math.trunc(x.box[3]*s)],keypoints:x.keypoints.map(({score:l,part:f,position:y})=>({score:l,part:f,position:[Math.trunc(y.x*a),Math.trunc(y.y*s)],positionRaw:[y.x/n,y.y/n]})),annotations:{}});return e.map((x,d)=>i(x,d))}var Z2=class{constructor(t,o){w(this,"priorityQueue");w(this,"numberOfElements");w(this,"getElementValue");this.priorityQueue=new Array(t),this.numberOfElements=-1,this.getElementValue=o}enqueue(t){this.priorityQueue[++this.numberOfElements]=t,this.swim(this.numberOfElements)}dequeue(){let t=this.priorityQueue[0];return this.exchange(0,this.numberOfElements--),this.sink(0),this.priorityQueue[this.numberOfElements+1]=null,t}empty(){return this.numberOfElements===-1}size(){return this.numberOfElements+1}all(){return this.priorityQueue.slice(0,this.numberOfElements+1)}max(){return this.priorityQueue[0]}swim(t){for(;t>0&&this.less(Math.floor(t/2),t);)this.exchange(t,Math.floor(t/2)),t=Math.floor(t/2)}sink(t){for(;2*t<=this.numberOfElements;){let o=2*t;if(oo?o:e}function io(e,t,o,n){let r=o-e,s=n-t;return r*r+s*s}function d1(e,t){return{x:e.x+t.x,y:e.y+t.y}}var h0,hA=["MobilenetV1/offset_2/BiasAdd","MobilenetV1/heatmap_2/BiasAdd","MobilenetV1/displacement_fwd_2/BiasAdd","MobilenetV1/displacement_bwd_2/BiasAdd"],X2=1,Ce=16,bA=50**2;function lo(e,t,o,n,r,s,a=2){let i=R=>({y:s.get(R.y,R.x,e),x:s.get(R.y,R.x,s.shape[2]/2+e)}),c=(R,P,m)=>({y:y1(Math.round(R.y/Ce),0,P-1),x:y1(Math.round(R.x/Ce),0,m-1)}),[x,d]=n.shape,l=c(t.position,x,d),f=i(l),p=d1(t.position,f);for(let R=0;R[t2[f],t2[y]]),a=s.map(([,f])=>f),i=s.map(([f])=>f),c=t.shape[2],x=a.length,d=new Array(c),l=x1(e.part,Ce,o);d[e.part.id]={score:e.score,part:o2[e.part.id],position:l};for(let f=x-1;f>=0;--f){let y=a[f],p=i[f];d[y]&&!d[p]&&(d[p]=lo(f,d[y],p,t,o,r))}for(let f=0;ft){i=!1;break}if(!i)break}return i}function RA(e,t){let[o,n,r]=t.shape,s=new Z2(o*n*r,({score:a})=>a);for(let a=0;a{var a;let s=(a=r[n])==null?void 0:a.position;return s?io(o,t,s.y,s.x)<=bA:!1})}function vA(e,t){return t.reduce((n,{position:r,score:s},a)=>(co(e,r,a)||(n+=s),n),0)/t.length}function PA(e,t,o,n,r,s){let a=[],i=RA(s,t);for(;a.lengthy.score>s);let l=vA(a,d),f=so(d);l>s&&a.push({keypoints:d,box:f,score:Math.round(100*l)/100})}return a}async function f1(e,t){if(!(h0!=null&&h0.executor))return[];let o=A.tidy(()=>{if(!h0.inputs[0].shape)return[];let a=A.image.resizeBilinear(e,[h0.inputs[0].shape[2],h0.inputs[0].shape[1]]),i=A.sub(A.div(A.cast(a,"float32"),127.5),1),x=h0.execute(i,hA).map(d=>A.squeeze(d,[0]));return x[1]=A.sigmoid(x[1]),x}),n=await Promise.all(o.map(a=>a.buffer()));for(let a of o)A.dispose(a);let r=PA(n[0],n[1],n[2],n[3],t.body.maxDetected,t.body.minConfidence);return h0.inputs[0].shape?ao(r,[e.shape[1],e.shape[2]],[h0.inputs[0].shape[2],h0.inputs[0].shape[1]]):[]}async function xo(e){return!h0||T.initial?h0=await L(e.body.modelPath):e.debug&&b("cached model:",h0.modelUrl),h0}var L0,m1=!1;async function p1(e){return!L0||T.initial?L0=await L(e.segmentation.modelPath):e.debug&&b("cached model:",L0.modelUrl),L0}async function fo(e,t,o){var g,M;if(m1)return{data:[],canvas:null,alpha:null};m1=!0,L0||await p1(o);let n=await Me(e,o),r=((g=n.tensor)==null?void 0:g.shape[2])||0,s=((M=n.tensor)==null?void 0:M.shape[1])||0;if(!n.tensor)return{data:[],canvas:null,alpha:null};let a={};a.resize=A.image.resizeBilinear(n.tensor,[L0.inputs[0].shape?L0.inputs[0].shape[1]:0,L0.inputs[0].shape?L0.inputs[0].shape[2]:0],!1),A.dispose(n.tensor),a.norm=A.div(a.resize,F.tf255),a.res=L0.execute(a.norm),a.squeeze=A.squeeze(a.res,0),a.squeeze.shape[2]===2?(a.softmax=A.softmax(a.squeeze),[a.bg,a.fg]=A.unstack(a.softmax,2),a.expand=A.expandDims(a.fg,2),a.pad=A.expandDims(a.expand,0),a.crop=A.image.cropAndResize(a.pad,[[0,0,.5,.5]],[0],[r,s]),a.data=A.squeeze(a.crop,0)):a.data=A.image.resizeBilinear(a.squeeze,[s,r]);let i=Array.from(await a.data.data());if(T.node&&!T.Canvas&&typeof ImageData=="undefined")return o.debug&&b("canvas support missing"),Object.keys(a).forEach(R=>A.dispose(a[R])),{data:i,canvas:null,alpha:null};let c=s0(r,s);A.browser&&await A.browser.toPixels(a.data,c);let x=c.getContext("2d");o.segmentation.blur&&o.segmentation.blur>0&&(x.filter=`blur(${o.segmentation.blur}px)`);let d=x.getImageData(0,0,r,s),l=s0(r,s),f=l.getContext("2d");n.canvas&&f.drawImage(n.canvas,0,0),f.globalCompositeOperation="darken",o.segmentation.blur&&o.segmentation.blur>0&&(f.filter=`blur(${o.segmentation.blur}px)`),f.drawImage(c,0,0),f.globalCompositeOperation="source-over",f.filter="none";let y=f.getImageData(0,0,r,s);for(let R=0;RA.dispose(a[R])),m1=!1,{data:i,canvas:l,alpha:c}}var n2=class{constructor(){w(this,"ssrnetage",null);w(this,"gear",null);w(this,"blazeposedetect",null);w(this,"blazepose",null);w(this,"centernet",null);w(this,"efficientpose",null);w(this,"mobilefacenet",null);w(this,"insightface",null);w(this,"emotion",null);w(this,"facedetect",null);w(this,"faceiris",null);w(this,"facemesh",null);w(this,"faceres",null);w(this,"ssrnetgender",null);w(this,"handpose",null);w(this,"handskeleton",null);w(this,"handtrack",null);w(this,"liveness",null);w(this,"movenet",null);w(this,"nanodet",null);w(this,"posenet",null);w(this,"segmentation",null);w(this,"antispoof",null)}},u1=e=>{let t=0,o=0,n=0;for(let s of Object.values(O0))t+=s.sizeFromManifest,o+=s.sizeLoadedWeights,n+=s.sizeDesired;let r=n>0?o/n:0;return{numLoadedModels:Object.values(O0).length,numEnabledModels:void 0,numDefinedModels:Object.keys(e.models).length,percentageLoaded:r,totalSizeFromManifest:t,totalSizeWeights:o,totalSizeLoading:n,totalSizeEnabled:void 0,modelStats:Object.values(O0)}};function j2(e){for(let t of Object.keys(e.models))e.models[t]=null}async function h1(e){var t,o,n,r,s,a,i,c,x,d,l,f,y,p,g,M,R,P,m,u,z,k,h,W,B,j;T.initial&&j2(e),e.config.hand.enabled&&(!e.models.handpose&&((o=(t=e.config.hand.detector)==null?void 0:t.modelPath)==null?void 0:o.includes("handdetect"))&&([e.models.handpose,e.models.handskeleton]=await K5(e.config)),!e.models.handskeleton&&e.config.hand.landmarks&&((r=(n=e.config.hand.detector)==null?void 0:n.modelPath)==null?void 0:r.includes("handdetect"))&&([e.models.handpose,e.models.handskeleton]=await K5(e.config))),e.config.body.enabled&&!e.models.blazepose&&((s=e.config.body.modelPath)==null?void 0:s.includes("blazepose"))&&(e.models.blazepose=jt(e.config)),e.config.body.enabled&&!e.models.blazeposedetect&&e.config.body.detector&&e.config.body.detector.modelPath&&(e.models.blazeposedetect=It(e.config)),e.config.body.enabled&&!e.models.efficientpose&&((a=e.config.body.modelPath)==null?void 0:a.includes("efficientpose"))&&(e.models.efficientpose=Gt(e.config)),e.config.body.enabled&&!e.models.movenet&&((i=e.config.body.modelPath)==null?void 0:i.includes("movenet"))&&(e.models.movenet=eo(e.config)),e.config.body.enabled&&!e.models.posenet&&((c=e.config.body.modelPath)==null?void 0:c.includes("posenet"))&&(e.models.posenet=xo(e.config)),e.config.face.enabled&&!e.models.facedetect&&(e.models.facedetect=vt(e.config)),e.config.face.enabled&&((x=e.config.face.antispoof)==null?void 0:x.enabled)&&!e.models.antispoof&&(e.models.antispoof=ct(e.config)),e.config.face.enabled&&((d=e.config.face.liveness)==null?void 0:d.enabled)&&!e.models.liveness&&(e.models.liveness=Y3(e.config)),e.config.face.enabled&&((l=e.config.face.description)==null?void 0:l.enabled)&&!e.models.faceres&&(e.models.faceres=m3(e.config)),e.config.face.enabled&&((f=e.config.face.emotion)==null?void 0:f.enabled)&&!e.models.emotion&&(e.models.emotion=Vt(e.config)),e.config.face.enabled&&((y=e.config.face.iris)==null?void 0:y.enabled)&&!((p=e.config.face.attention)!=null&&p.enabled)&&!e.models.faceiris&&(e.models.faceiris=r3(e.config)),e.config.face.enabled&&((g=e.config.face.mesh)==null?void 0:g.enabled)&&!e.models.facemesh&&(e.models.facemesh=c3(e.config)),e.config.face.enabled&&((M=e.config.face.gear)==null?void 0:M.enabled)&&!e.models.gear&&(e.models.gear=Q1(e.config)),e.config.face.enabled&&((R=e.config.face.ssrnet)==null?void 0:R.enabled)&&!e.models.ssrnetage&&(e.models.ssrnetage=ot(e.config)),e.config.face.enabled&&((P=e.config.face.ssrnet)==null?void 0:P.enabled)&&!e.models.ssrnetgender&&(e.models.ssrnetgender=st(e.config)),e.config.face.enabled&&((m=e.config.face.mobilefacenet)==null?void 0:m.enabled)&&!e.models.mobilefacenet&&(e.models.mobilefacenet=Yt(e.config)),e.config.face.enabled&&((u=e.config.face.insightface)==null?void 0:u.enabled)&&!e.models.insightface&&(e.models.insightface=$t(e.config)),e.config.hand.enabled&&!e.models.handtrack&&((k=(z=e.config.hand.detector)==null?void 0:z.modelPath)==null?void 0:k.includes("handtrack"))&&(e.models.handtrack=V3(e.config)),e.config.hand.enabled&&e.config.hand.landmarks&&!e.models.handskeleton&&((W=(h=e.config.hand.detector)==null?void 0:h.modelPath)==null?void 0:W.includes("handtrack"))&&(e.models.handskeleton=Z3(e.config)),e.config.object.enabled&&!e.models.centernet&&((B=e.config.object.modelPath)==null?void 0:B.includes("centernet"))&&(e.models.centernet=Lt(e.config)),e.config.object.enabled&&!e.models.nanodet&&((j=e.config.object.modelPath)==null?void 0:j.includes("nanodet"))&&(e.models.nanodet=no(e.config)),e.config.segmentation.enabled&&!e.models.segmentation&&(e.models.segmentation=p1(e.config));for await(let N of Object.keys(e.models))e.models[N]&&typeof e.models[N]!="undefined"&&(e.models[N]=await e.models[N])}var g0;function Ie(e,t,o){var x;if(e&&(g0=e),!t||(g0||b("instance not registred"),!g0.config.validateModels))return null;let n=["const","placeholder","noop","pad","squeeze","add","sub","mul","div"],r=["biasadd","fusedbatchnormv3","matmul"],s=[],a=[],i=t.modelUrl,c=t.executor;if((x=c==null?void 0:c.graph)!=null&&x.nodes)for(let d of Object.values(c.graph.nodes)){let l=d.op.toLowerCase();s.includes(l)||s.push(l)}else!c&&g0.config.debug&&b("model not loaded",o);for(let d of s)!n.includes(d)&&!r.includes(d)&&!g0.env.kernels.includes(d)&&!g0.env.kernels.includes(d.replace("_",""))&&!g0.env.kernels.includes(d.replace("native",""))&&!g0.env.kernels.includes(d.replace("v2",""))&&a.push(d);return g0.config.debug&&a.length>0&&b("model validation failed:",o,a),a.length>0?{name:o,missing:a,ops:s,url:i}:null}function q2(e){g0=e;let t=[];for(let o of Object.keys(g0.models)){let n=g0.models[o];if(!n)continue;let r=Ie(g0,n,o);r&&t.push(r)}return t}var c0={cacheModels:!0,cacheSupported:!0,verbose:!0,debug:!1,modelBasePath:""},O0={};async function TA(e,t){return c0.debug&&b("load model fetch:",e,t),fetch(e,t)}function po(e){c0.cacheModels=e.cacheModels,c0.verbose=e.debug,c0.modelBasePath=e.modelBasePath}async function L(e){var x,d,l;let t=G1(c0.modelBasePath,e||"");t.toLowerCase().endsWith(".json")||(t+=".json");let o=t.includes("/")?t.split("/"):t.split("\\"),n=o[o.length-1].replace(".json",""),r="indexeddb://"+n;O0[n]={name:n,sizeFromManifest:0,sizeLoadedWeights:0,sizeDesired:n5[n],inCache:!1},c0.cacheSupported=typeof window!="undefined"&&typeof window.localStorage!="undefined"&&typeof window.indexedDB!="undefined";let s={};try{s=c0.cacheSupported&&c0.cacheModels?await A.io.listModels():{}}catch(f){c0.cacheSupported=!1}O0[n].inCache=c0.cacheSupported&&c0.cacheModels&&Object.keys(s).includes(r);let a=typeof fetch=="undefined"?{}:{fetchFunc:(f,y)=>TA(f,y)},i=new $2(O0[n].inCache?r:t,a),c=!1;try{i.findIOHandler(),c0.debug&&b("model load handler:",i.handler);let f=await i.handler.load();O0[n].sizeFromManifest=((x=f==null?void 0:f.weightData)==null?void 0:x.byteLength)||0,i.loadSync(f),O0[n].sizeLoadedWeights=((l=(d=i.artifacts)==null?void 0:d.weightData)==null?void 0:l.byteLength)||0,c0.verbose&&b("load model:",i.modelUrl,{bytes:O0[n].sizeLoadedWeights},c0),c=!0}catch(f){b("error loading model:",t,f)}if(c&&c0.cacheModels&&c0.cacheSupported&&!O0[n].inCache)try{let f=await i.save(r);b("model saved:",r,f)}catch(f){b("error saving model:",t,f)}return Ie(null,i,`${e||""}`),i}var b1="2.9.4";var ho={};q0(ho,{all:()=>T1,body:()=>Oe,canvas:()=>P1,face:()=>je,gesture:()=>We,hand:()=>Ne,object:()=>Le,options:()=>A0,person:()=>v1});var M0=e=>{if(!e)b("draw error: invalid canvas");else if(!e.getContext)b("draw error: canvas context not defined");else{let t=e.getContext("2d");if(!t)b("draw error: cannot get canvas context");else return t}return null},he=e=>Math.round(e*180/Math.PI),V0=(e,t)=>{if(!t.useDepth||typeof e=="undefined")return t.color;let o=Uint8ClampedArray.from([127+2*e,127-2*e,255]);return`rgba(${o[0]}, ${o[1]}, ${o[2]}, ${t.alpha})`};function Z0(e,t,o,n,r){e.fillStyle=V0(n,r),e.beginPath(),e.arc(t,o,r.pointSize,0,2*Math.PI),e.fill()}function W0(e,t,o,n,r,s){if(e.beginPath(),e.lineWidth=s.lineWidth,s.useCurves){let a=(t+t+n)/2,i=(o+o+r)/2;e.ellipse(a,i,n/2,r/2,0,0,2*Math.PI)}else e.moveTo(t+s.roundRect,o),e.lineTo(t+n-s.roundRect,o),e.quadraticCurveTo(t+n,o,t+n,o+s.roundRect),e.lineTo(t+n,o+r-s.roundRect),e.quadraticCurveTo(t+n,o+r,t+n-s.roundRect,o+r),e.lineTo(t+s.roundRect,o+r),e.quadraticCurveTo(t,o+r,t,o+r-s.roundRect),e.lineTo(t,o+s.roundRect),e.quadraticCurveTo(t,o,t+s.roundRect,o),e.closePath();e.stroke()}function g1(e,t,o){if(!(t.length<2)){e.beginPath(),e.moveTo(t[0][0],t[0][1]);for(let n of t)e.strokeStyle=V0(n[2]||0,o),e.lineTo(Math.trunc(n[0]),Math.trunc(n[1]));e.stroke(),o.fillPolygons&&(e.closePath(),e.fill())}}function uo(e,t,o){if(!(t.length<2)){if(e.lineWidth=o.lineWidth,!o.useCurves||t.length<=2){g1(e,t,o);return}e.moveTo(t[0][0],t[0][1]);for(let n=0;n0){let s=e.emotion.map(a=>`${Math.trunc(100*a.score)}% ${a.emotion}`);s.length>3&&(s.length=3),r.push(s.join(" "))}((o=e.rotation)==null?void 0:o.angle)&&((n=e.rotation)==null?void 0:n.gaze)&&(e.rotation.angle.roll&&r.push(`roll: ${he(e.rotation.angle.roll)}\xB0 yaw:${he(e.rotation.angle.yaw)}\xB0 pitch:${he(e.rotation.angle.pitch)}\xB0`),e.rotation.gaze.bearing&&r.push(`gaze: ${he(e.rotation.gaze.bearing)}\xB0`)),r.length===0&&r.push("face"),t.fillStyle=H.color;for(let s=r.length-1;s>=0;s--){let a=Math.max(e.box[0],0),i=s*H.lineHeight+e.box[1];H.shadowColor&&H.shadowColor!==""&&(t.fillStyle=H.shadowColor,t.fillText(r[s],a+5,i+16)),t.fillStyle=H.labelColor,t.fillText(r[s],a+4,i+15)}}}function zA(e,t){var o,n,r,s;if(((o=e.annotations)==null?void 0:o.leftEyeIris)&&((n=e.annotations)==null?void 0:n.leftEyeIris[0])){t.strokeStyle=H.useDepth?"rgba(255, 200, 255, 0.3)":H.color,t.beginPath();let a=Math.abs(e.annotations.leftEyeIris[3][0]-e.annotations.leftEyeIris[1][0])/2,i=Math.abs(e.annotations.leftEyeIris[4][1]-e.annotations.leftEyeIris[2][1])/2;t.ellipse(e.annotations.leftEyeIris[0][0],e.annotations.leftEyeIris[0][1],a,i,0,0,2*Math.PI),t.stroke(),H.fillPolygons&&(t.fillStyle=H.useDepth?"rgba(255, 255, 200, 0.3)":H.color,t.fill())}if(((r=e.annotations)==null?void 0:r.rightEyeIris)&&((s=e.annotations)==null?void 0:s.rightEyeIris[0])){t.strokeStyle=H.useDepth?"rgba(255, 200, 255, 0.3)":H.color,t.beginPath();let a=Math.abs(e.annotations.rightEyeIris[3][0]-e.annotations.rightEyeIris[1][0])/2,i=Math.abs(e.annotations.rightEyeIris[4][1]-e.annotations.rightEyeIris[2][1])/2;t.ellipse(e.annotations.rightEyeIris[0][0],e.annotations.rightEyeIris[0][1],a,i,0,0,2*Math.PI),t.stroke(),H.fillPolygons&&(t.fillStyle=H.useDepth?"rgba(255, 255, 200, 0.3)":H.color,t.fill())}}function SA(e,t){var o;if(H.drawGaze&&((o=e.rotation)==null?void 0:o.angle)&&typeof Path2D!="undefined"){t.strokeStyle="pink";let n=e.box[0]+e.box[2]/2-e.box[3]*he(e.rotation.angle.yaw)/90,r=e.box[1]+e.box[3]/2+e.box[2]*he(e.rotation.angle.pitch)/90,s=new Path2D(`
+ M ${e.box[0]+e.box[2]/2} ${e.box[1]}
C
- ${valX} ${f.box[1]},
- ${valX} ${f.box[1] + f.box[3]},
- ${f.box[0] + f.box[2] / 2} ${f.box[1] + f.box[3]}
- `);
- const pathH = new Path2D(`
- M ${f.box[0]} ${f.box[1] + f.box[3] / 2}
+ ${n} ${e.box[1]},
+ ${n} ${e.box[1]+e.box[3]},
+ ${e.box[0]+e.box[2]/2} ${e.box[1]+e.box[3]}
+ `),a=new Path2D(`
+ M ${e.box[0]} ${e.box[1]+e.box[3]/2}
C
- ${f.box[0]} ${valY},
- ${f.box[0] + f.box[2]} ${valY},
- ${f.box[0] + f.box[2]} ${f.box[1] + f.box[3] / 2}
- `);
- ctx.stroke(pathH);
- ctx.stroke(pathV);
- }
-}
-function drawGazeArrows(f, ctx) {
- var _a;
- if (opt.drawGaze && ((_a = f.rotation) == null ? void 0 : _a.gaze.strength) && f.rotation.gaze.bearing && f.annotations.leftEyeIris && f.annotations.rightEyeIris && f.annotations.leftEyeIris[0] && f.annotations.rightEyeIris[0]) {
- ctx.strokeStyle = "pink";
- ctx.fillStyle = "pink";
- const leftGaze = [
- f.annotations.leftEyeIris[0][0] + Math.sin(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[3],
- f.annotations.leftEyeIris[0][1] + Math.cos(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[2]
- ];
- arrow(ctx, [f.annotations.leftEyeIris[0][0], f.annotations.leftEyeIris[0][1]], [leftGaze[0], leftGaze[1]], 4);
- const rightGaze = [
- f.annotations.rightEyeIris[0][0] + Math.sin(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[3],
- f.annotations.rightEyeIris[0][1] + Math.cos(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[2]
- ];
- arrow(ctx, [f.annotations.rightEyeIris[0][0], f.annotations.rightEyeIris[0][1]], [rightGaze[0], rightGaze[1]], 4);
- }
-}
-function drawFacePolygons(f, ctx) {
- if (opt.drawPolygons && f.mesh.length >= 468) {
- ctx.lineWidth = 1;
- for (let i = 0; i < TRI468.length / 3; i++) {
- const points = [TRI468[i * 3 + 0], TRI468[i * 3 + 1], TRI468[i * 3 + 2]].map((index2) => f.mesh[index2]);
- lines(ctx, points, opt);
- }
- drawIrisElipse(f, ctx);
- }
-}
-function drawFacePoints(f, ctx) {
- if (opt.drawPoints && f.mesh.length >= 468) {
- for (let i = 0; i < f.mesh.length; i++) {
- point(ctx, f.mesh[i][0], f.mesh[i][1], f.mesh[i][2], opt);
- if (opt.drawAttention) {
- if (LANDMARKS_REFINEMENT_LIPS_CONFIG.includes(i))
- point(ctx, f.mesh[i][0], f.mesh[i][1], f.mesh[i][2] + 127, opt);
- if (LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.includes(i))
- point(ctx, f.mesh[i][0], f.mesh[i][1], f.mesh[i][2] - 127, opt);
- if (LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.includes(i))
- point(ctx, f.mesh[i][0], f.mesh[i][1], f.mesh[i][2] - 127, opt);
- }
- }
- }
-}
-function drawFaceBoxes(f, ctx) {
- if (opt.drawBoxes) {
- rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3], opt);
- }
-}
-function face(inCanvas2, result, drawOptions) {
- opt = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.font = opt.font;
- ctx.strokeStyle = opt.color;
- ctx.fillStyle = opt.color;
- for (const f of result) {
- drawFaceBoxes(f, ctx);
- drawLabels(f, ctx);
- if (f.mesh && f.mesh.length > 0) {
- drawFacePoints(f, ctx);
- drawFacePolygons(f, ctx);
- drawGazeSpheres(f, ctx);
- drawGazeArrows(f, ctx);
- }
- }
-}
-
-// src/draw/body.ts
-function body(inCanvas2, result, drawOptions) {
- const localOptions = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.lineJoin = "round";
- for (let i = 0; i < result.length; i++) {
- ctx.strokeStyle = localOptions.color;
- ctx.fillStyle = localOptions.color;
- ctx.lineWidth = localOptions.lineWidth;
- ctx.font = localOptions.font;
- if (localOptions.drawBoxes && result[i].box && result[i].box.length === 4) {
- rect(ctx, result[i].box[0], result[i].box[1], result[i].box[2], result[i].box[3], localOptions);
- if (localOptions.drawLabels) {
- if (localOptions.shadowColor && localOptions.shadowColor !== "") {
- ctx.fillStyle = localOptions.shadowColor;
- ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 3, 1 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);
- }
- ctx.fillStyle = localOptions.labelColor;
- ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 2, 0 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);
- }
- }
- if (localOptions.drawPoints && result[i].keypoints) {
- for (let pt = 0; pt < result[i].keypoints.length; pt++) {
- if (!result[i].keypoints[pt].score || result[i].keypoints[pt].score === 0)
- continue;
- ctx.fillStyle = colorDepth(result[i].keypoints[pt].position[2], localOptions);
- point(ctx, result[i].keypoints[pt].position[0], result[i].keypoints[pt].position[1], 0, localOptions);
- }
- }
- if (localOptions.drawLabels && result[i].keypoints) {
- ctx.font = localOptions.font;
- for (const pt of result[i].keypoints) {
- if (!pt.score || pt.score === 0)
- continue;
- ctx.fillStyle = colorDepth(pt.position[2], localOptions);
- ctx.fillText(`${pt.part} ${Math.trunc(100 * pt.score)}%`, pt.position[0] + 4, pt.position[1] + 4);
- }
- }
- if (localOptions.drawPolygons && result[i].keypoints && result[i].annotations) {
- for (const part of Object.values(result[i].annotations)) {
- for (const connected4 of part)
- curves(ctx, connected4, localOptions);
- }
- }
- }
-}
-
-// src/draw/hand.ts
-function hand(inCanvas2, result, drawOptions) {
- const localOptions = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.lineJoin = "round";
- ctx.font = localOptions.font;
- for (const h of result) {
- if (localOptions.drawBoxes) {
- ctx.strokeStyle = localOptions.color;
- ctx.fillStyle = localOptions.color;
- rect(ctx, h.box[0], h.box[1], h.box[2], h.box[3], localOptions);
- if (localOptions.drawLabels) {
- if (localOptions.shadowColor && localOptions.shadowColor !== "") {
- ctx.fillStyle = localOptions.shadowColor;
- ctx.fillText(`hand:${Math.trunc(100 * h.score)}%`, h.box[0] + 3, 1 + h.box[1] + localOptions.lineHeight, h.box[2]);
- }
- ctx.fillStyle = localOptions.labelColor;
- ctx.fillText(`hand:${Math.trunc(100 * h.score)}%`, h.box[0] + 2, 0 + h.box[1] + localOptions.lineHeight, h.box[2]);
- }
- ctx.stroke();
- }
- if (localOptions.drawPoints) {
- if (h.keypoints && h.keypoints.length > 0) {
- for (const pt of h.keypoints) {
- ctx.fillStyle = colorDepth(pt[2], localOptions);
- point(ctx, pt[0], pt[1], 0, localOptions);
- }
- }
- }
- if (localOptions.drawLabels && h.annotations) {
- const addHandLabel = (part, title) => {
- if (!part || part.length === 0 || !part[0])
- return;
- const z = part[part.length - 1][2] || -256;
- ctx.fillStyle = colorDepth(z, localOptions);
- ctx.fillText(title, part[part.length - 1][0] + 4, part[part.length - 1][1] + 4);
- };
- ctx.font = localOptions.font;
- addHandLabel(h.annotations.index, "index");
- addHandLabel(h.annotations.middle, "middle");
- addHandLabel(h.annotations.ring, "ring");
- addHandLabel(h.annotations.pinky, "pinky");
- addHandLabel(h.annotations.thumb, "thumb");
- addHandLabel(h.annotations.palm, "palm");
- }
- if (localOptions.drawPolygons && h.annotations) {
- const addHandLine = (part) => {
- if (!part || part.length === 0 || !part[0])
- return;
- for (let i = 0; i < part.length; i++) {
- ctx.beginPath();
- const z = part[i][2] || 0;
- ctx.strokeStyle = colorDepth(i * z, localOptions);
- ctx.moveTo(part[i > 0 ? i - 1 : 0][0], part[i > 0 ? i - 1 : 0][1]);
- ctx.lineTo(part[i][0], part[i][1]);
- ctx.stroke();
- }
- };
- ctx.lineWidth = localOptions.lineWidth;
- addHandLine(h.annotations.index);
- addHandLine(h.annotations.middle);
- addHandLine(h.annotations.ring);
- addHandLine(h.annotations.pinky);
- addHandLine(h.annotations.thumb);
- }
- }
-}
-
-// src/draw/object.ts
-function object(inCanvas2, result, drawOptions) {
- const localOptions = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.lineJoin = "round";
- ctx.font = localOptions.font;
- for (const h of result) {
- if (localOptions.drawBoxes) {
- ctx.strokeStyle = localOptions.color;
- ctx.fillStyle = localOptions.color;
- rect(ctx, h.box[0], h.box[1], h.box[2], h.box[3], localOptions);
- if (localOptions.drawLabels) {
- const label = `${h.label} ${Math.round(100 * h.score)}%`;
- if (localOptions.shadowColor && localOptions.shadowColor !== "") {
- ctx.fillStyle = localOptions.shadowColor;
- ctx.fillText(label, h.box[0] + 3, 1 + h.box[1] + localOptions.lineHeight, h.box[2]);
- }
- ctx.fillStyle = localOptions.labelColor;
- ctx.fillText(label, h.box[0] + 2, 0 + h.box[1] + localOptions.lineHeight, h.box[2]);
- }
- ctx.stroke();
- }
- }
-}
-
-// src/draw/gesture.ts
-function gesture(inCanvas2, result, drawOptions) {
- const localOptions = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- if (localOptions.drawGestures) {
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.font = localOptions.font;
- ctx.fillStyle = localOptions.color;
- let i = 1;
- for (let j = 0; j < result.length; j++) {
- let where = [];
- let what = [];
- [where, what] = Object.entries(result[j]);
- if (what.length > 1 && what[1].length > 0) {
- const who = where[1] > 0 ? `#${where[1]}` : "";
- const label = `${where[0]} ${who}: ${what[1]}`;
- if (localOptions.shadowColor && localOptions.shadowColor !== "") {
- ctx.fillStyle = localOptions.shadowColor;
- ctx.fillText(label, 8, 2 + i * localOptions.lineHeight);
- }
- ctx.fillStyle = localOptions.labelColor;
- ctx.fillText(label, 6, 0 + i * localOptions.lineHeight);
- i += 1;
- }
- }
- }
-}
-
-// src/draw/draw.ts
-var drawTime = 0;
-function person(inCanvas2, result, drawOptions) {
- const localOptions = mergeDeep(options3, drawOptions);
- if (!result || !inCanvas2)
- return;
- const ctx = getCanvasContext(inCanvas2);
- if (!ctx)
- return;
- ctx.lineJoin = "round";
- ctx.font = localOptions.font;
- for (let i = 0; i < result.length; i++) {
- if (localOptions.drawBoxes) {
- ctx.strokeStyle = localOptions.color;
- ctx.fillStyle = localOptions.color;
- rect(ctx, result[i].box[0], result[i].box[1], result[i].box[2], result[i].box[3], localOptions);
- if (localOptions.drawLabels) {
- const label = `person #${i}`;
- if (localOptions.shadowColor && localOptions.shadowColor !== "") {
- ctx.fillStyle = localOptions.shadowColor;
- ctx.fillText(label, result[i].box[0] + 3, 1 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);
- }
- ctx.fillStyle = localOptions.labelColor;
- ctx.fillText(label, result[i].box[0] + 2, 0 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);
- }
- ctx.stroke();
- }
- }
-}
-function canvas2(input, output) {
- if (!input || !output)
- return;
- const ctx = getCanvasContext(output);
- if (!ctx)
- return;
- ctx.drawImage(input, 0, 0);
-}
-async function all(inCanvas2, result, drawOptions) {
- if (!(result == null ? void 0 : result.performance) || !inCanvas2)
- return null;
- const timeStamp = now();
- const localOptions = mergeDeep(options3, drawOptions);
- const promise = Promise.all([
- face(inCanvas2, result.face, localOptions),
- body(inCanvas2, result.body, localOptions),
- hand(inCanvas2, result.hand, localOptions),
- object(inCanvas2, result.object, localOptions),
- gesture(inCanvas2, result.gesture, localOptions)
- ]);
- drawTime = env.perfadd ? drawTime + Math.round(now() - timeStamp) : Math.round(now() - timeStamp);
- result.performance.draw = drawTime;
- return promise;
-}
-
-// src/face/mask.ts
-var expandFact = 0.1;
-var alpha = 0.5;
-function insidePoly(x, y, polygon) {
- let inside = false;
- let j = polygon.length - 1;
- for (let i = 0; i < polygon.length; j = i++) {
- if (polygon[i].y > y !== polygon[j].y > y && x < (polygon[j].x - polygon[i].x) * (y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x)
- inside = !inside;
- }
- return inside;
-}
-async function mask(face4) {
- if (!face4.tensor)
- return face4.tensor;
- if (!face4.mesh || face4.mesh.length < 100)
- return face4.tensor;
- const width = face4.tensor.shape[2] || 0;
- const height = face4.tensor.shape[1] || 0;
- const buffer = await face4.tensor.buffer();
- let silhouette = [];
- for (const pt of meshAnnotations.silhouette)
- silhouette.push({ x: (face4.mesh[pt][0] - face4.box[0]) / face4.box[2], y: (face4.mesh[pt][1] - face4.box[1]) / face4.box[3] });
- if (expandFact && expandFact > 0)
- silhouette = silhouette.map((pt) => ({ x: pt.x > 0.5 ? pt.x + expandFact : pt.x - expandFact, y: pt.y > 0.5 ? pt.y + expandFact : pt.y - expandFact }));
- for (let x = 0; x < width; x++) {
- for (let y = 0; y < height; y++) {
- const inside = insidePoly(x / width, y / width, silhouette);
- if (!inside) {
- buffer.set(alpha * buffer.get(0, y, x, 0), 0, y, x, 0);
- buffer.set(alpha * buffer.get(0, y, x, 1), 0, y, x, 1);
- buffer.set(alpha * buffer.get(0, y, x, 2), 0, y, x, 2);
- }
- }
- }
- const output = buffer.toTensor();
- tfjs_esm_exports.dispose(buffer);
- return output;
-}
-
-// src/face/angles.ts
-var calculateGaze = (face4) => {
- const radians = (pt1, pt2) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]);
- if (!face4.annotations.rightEyeIris || !face4.annotations.leftEyeIris)
- return { bearing: 0, strength: 0 };
- const offsetIris = [0, -0.1];
- const eyeRatio = 1;
- const left = (face4.mesh[33][2] || 0) > (face4.mesh[263][2] || 0);
- const irisCenter = left ? face4.mesh[473] : face4.mesh[468];
- const eyeCenter = left ? [(face4.mesh[133][0] + face4.mesh[33][0]) / 2, (face4.mesh[133][1] + face4.mesh[33][1]) / 2] : [(face4.mesh[263][0] + face4.mesh[362][0]) / 2, (face4.mesh[263][1] + face4.mesh[362][1]) / 2];
- const eyeSize = left ? [face4.mesh[133][0] - face4.mesh[33][0], face4.mesh[23][1] - face4.mesh[27][1]] : [face4.mesh[263][0] - face4.mesh[362][0], face4.mesh[253][1] - face4.mesh[257][1]];
- const eyeDiff = [
- (eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],
- eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1]
- ];
- let strength = Math.sqrt(eyeDiff[0] * eyeDiff[0] + eyeDiff[1] * eyeDiff[1]);
- strength = Math.min(strength, face4.boxRaw[2] / 2, face4.boxRaw[3] / 2);
- const bearing = (radians([0, 0], eyeDiff) + Math.PI / 2) % Math.PI;
- return { bearing, strength };
-};
-var calculateFaceAngle = (face4, imageSize) => {
- const normalize = (v) => {
- const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
- v[0] /= length;
- v[1] /= length;
- v[2] /= length;
- return v;
- };
- const subVectors = (a, b) => {
- const x = a[0] - b[0];
- const y = a[1] - b[1];
- const z = a[2] - b[2];
- return [x, y, z];
- };
- const crossVectors = (a, b) => {
- const x = a[1] * b[2] - a[2] * b[1];
- const y = a[2] * b[0] - a[0] * b[2];
- const z = a[0] * b[1] - a[1] * b[0];
- return [x, y, z];
- };
- const rotationMatrixToEulerAngle = (r) => {
- const [r00, _r01, _r02, r10, r11, r12, r20, r21, r22] = r;
- let thetaX;
- let thetaY;
- let thetaZ;
- if (r10 < 1) {
- if (r10 > -1) {
- thetaZ = Math.asin(r10);
- thetaY = Math.atan2(-r20, r00);
- thetaX = Math.atan2(-r12, r11);
- } else {
- thetaZ = -Math.PI / 2;
- thetaY = -Math.atan2(r21, r22);
- thetaX = 0;
- }
- } else {
- thetaZ = Math.PI / 2;
- thetaY = Math.atan2(r21, r22);
- thetaX = 0;
- }
- if (Number.isNaN(thetaX))
- thetaX = 0;
- if (Number.isNaN(thetaY))
- thetaY = 0;
- if (Number.isNaN(thetaZ))
- thetaZ = 0;
- return { pitch: 2 * -thetaX, yaw: 2 * -thetaY, roll: 2 * -thetaZ };
- };
- const mesh = face4.meshRaw;
- if (!mesh || mesh.length < 300)
- return { angle: { pitch: 0, yaw: 0, roll: 0 }, matrix: [1, 0, 0, 0, 1, 0, 0, 0, 1], gaze: { bearing: 0, strength: 0 } };
- const size2 = Math.max(face4.boxRaw[2] * imageSize[0], face4.boxRaw[3] * imageSize[1]) / 1.5;
- const pts = [mesh[10], mesh[152], mesh[234], mesh[454]].map((pt) => [pt[0] * imageSize[0] / size2, pt[1] * imageSize[1] / size2, pt[2]]);
- const yAxis = normalize(subVectors(pts[1], pts[0]));
- let xAxis = normalize(subVectors(pts[3], pts[2]));
- const zAxis = normalize(crossVectors(xAxis, yAxis));
- xAxis = crossVectors(yAxis, zAxis);
- const matrix = [
- xAxis[0],
- xAxis[1],
- xAxis[2],
- yAxis[0],
- yAxis[1],
- yAxis[2],
- zAxis[0],
- zAxis[1],
- zAxis[2]
- ];
- const angle = rotationMatrixToEulerAngle(matrix);
- const gaze = mesh.length === 478 ? calculateGaze(face4) : { bearing: 0, strength: 0 };
- return { angle, matrix, gaze };
-};
-
-// src/face/face.ts
-var detectFace = async (instance2, input) => {
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
- let timeStamp = now();
- let ageRes;
- let gearRes;
- let genderRes;
- let emotionRes;
- let mobilefacenetRes;
- let insightfaceRes;
- let antispoofRes;
- let livenessRes;
- let descRes;
- const faceRes = [];
- instance2.state = "run:face";
- const faces = await predict11(input, instance2.config);
- instance2.performance.face = env.perfadd ? (instance2.performance.face || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- if (!input.shape || input.shape.length !== 4)
- return [];
- if (!faces)
- return [];
- for (let i = 0; i < faces.length; i++) {
- instance2.analyze("Get Face");
- if (!faces[i].tensor || faces[i].tensor.isDisposedInternal) {
- log("Face object is disposed:", faces[i].tensor);
- continue;
- }
- if ((_a = instance2.config.face.detector) == null ? void 0 : _a.mask) {
- const masked = await mask(faces[i]);
- tfjs_esm_exports.dispose(faces[i].tensor);
- if (masked)
- faces[i].tensor = masked;
- }
- const rotation = faces[i].mesh && faces[i].mesh.length > 200 ? calculateFaceAngle(faces[i], [input.shape[2], input.shape[1]]) : null;
- instance2.analyze("Start Emotion:");
- if (instance2.config.async) {
- emotionRes = ((_b = instance2.config.face.emotion) == null ? void 0 : _b.enabled) ? predict8(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : [];
- } else {
- instance2.state = "run:emotion";
- timeStamp = now();
- emotionRes = ((_c = instance2.config.face.emotion) == null ? void 0 : _c.enabled) ? await predict8(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : [];
- instance2.performance.emotion = env.perfadd ? (instance2.performance.emotion || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End Emotion:");
- instance2.analyze("Start AntiSpoof:");
- if (instance2.config.async) {
- antispoofRes = ((_d = instance2.config.face.antispoof) == null ? void 0 : _d.enabled) ? predict4(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : 0;
- } else {
- instance2.state = "run:antispoof";
- timeStamp = now();
- antispoofRes = ((_e = instance2.config.face.antispoof) == null ? void 0 : _e.enabled) ? await predict4(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : 0;
- instance2.performance.antispoof = env.perfadd ? (instance2.performance.antispoof || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End AntiSpoof:");
- instance2.analyze("Start Liveness:");
- if (instance2.config.async) {
- livenessRes = ((_f = instance2.config.face.liveness) == null ? void 0 : _f.enabled) ? predict15(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : 0;
- } else {
- instance2.state = "run:liveness";
- timeStamp = now();
- livenessRes = ((_g = instance2.config.face.liveness) == null ? void 0 : _g.enabled) ? await predict15(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : 0;
- instance2.performance.liveness = env.perfadd ? (instance2.performance.antispoof || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End Liveness:");
- instance2.analyze("Start GEAR:");
- if (instance2.config.async) {
- gearRes = ((_h = instance2.config.face.gear) == null ? void 0 : _h.enabled) ? predict(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- } else {
- instance2.state = "run:gear";
- timeStamp = now();
- gearRes = ((_i = instance2.config.face.gear) == null ? void 0 : _i.enabled) ? await predict(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- instance2.performance.gear = Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End GEAR:");
- instance2.analyze("Start SSRNet:");
- if (instance2.config.async) {
- ageRes = ((_j = instance2.config.face["ssrnet"]) == null ? void 0 : _j.enabled) ? predict2(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- genderRes = ((_k = instance2.config.face["ssrnet"]) == null ? void 0 : _k.enabled) ? predict3(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- } else {
- instance2.state = "run:ssrnet";
- timeStamp = now();
- ageRes = ((_l = instance2.config.face["ssrnet"]) == null ? void 0 : _l.enabled) ? await predict2(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- genderRes = ((_m = instance2.config.face["ssrnet"]) == null ? void 0 : _m.enabled) ? await predict3(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- instance2.performance.ssrnet = Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End SSRNet:");
- instance2.analyze("Start MobileFaceNet:");
- if (instance2.config.async) {
- mobilefacenetRes = ((_n = instance2.config.face["mobilefacenet"]) == null ? void 0 : _n.enabled) ? predict9(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- } else {
- instance2.state = "run:mobilefacenet";
- timeStamp = now();
- mobilefacenetRes = ((_o = instance2.config.face["mobilefacenet"]) == null ? void 0 : _o.enabled) ? await predict9(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- instance2.performance.mobilefacenet = Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End MobileFaceNet:");
- instance2.analyze("Start InsightFace:");
- if (instance2.config.async) {
- insightfaceRes = ((_p = instance2.config.face["insightface"]) == null ? void 0 : _p.enabled) ? predict10(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- } else {
- instance2.state = "run:mobilefacenet";
- timeStamp = now();
- insightfaceRes = ((_q = instance2.config.face["insightface"]) == null ? void 0 : _q.enabled) ? await predict10(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length) : null;
- instance2.performance.mobilefacenet = Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End InsightFace:");
- instance2.analyze("Start Description:");
- if (instance2.config.async) {
- descRes = predict12(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length);
- } else {
- instance2.state = "run:description";
- timeStamp = now();
- descRes = await predict12(faces[i].tensor || tfjs_esm_exports.tensor([]), instance2.config, i, faces.length);
- instance2.performance.description = env.perfadd ? (instance2.performance.description || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- instance2.analyze("End Description:");
- if (instance2.config.async) {
- [ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes] = await Promise.all([ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes]);
- }
- instance2.analyze("Finish Face:");
- if (((_r = instance2.config.face["ssrnet"]) == null ? void 0 : _r.enabled) && ageRes && genderRes) {
- descRes = {
- ...descRes,
- age: ageRes.age,
- gender: genderRes.gender,
- genderScore: genderRes.genderScore
- };
- }
- if (((_s = instance2.config.face.gear) == null ? void 0 : _s.enabled) && gearRes) {
- descRes = {
- ...descRes,
- age: gearRes.age,
- gender: gearRes.gender,
- genderScore: gearRes.genderScore,
- race: gearRes.race
- };
- }
- if (((_t = instance2.config.face["mobilefacenet"]) == null ? void 0 : _t.enabled) && mobilefacenetRes) {
- descRes.descriptor = mobilefacenetRes;
- }
- if (((_u = instance2.config.face["insightface"]) == null ? void 0 : _u.enabled) && insightfaceRes) {
- descRes.descriptor = insightfaceRes;
- }
- if (!((_v = instance2.config.face.iris) == null ? void 0 : _v.enabled)) {
- }
- const irisSize = ((_y = (_x = (_w = faces[i]) == null ? void 0 : _w.annotations) == null ? void 0 : _x.leftEyeIris) == null ? void 0 : _y[0]) && ((_B = (_A = (_z = faces[i]) == null ? void 0 : _z.annotations) == null ? void 0 : _A.rightEyeIris) == null ? void 0 : _B[0]) && faces[i].annotations.leftEyeIris.length > 0 && faces[i].annotations.rightEyeIris.length > 0 && faces[i].annotations.leftEyeIris[0] !== null && faces[i].annotations.rightEyeIris[0] !== null ? Math.max(Math.abs(faces[i].annotations.leftEyeIris[3][0] - faces[i].annotations.leftEyeIris[1][0]), Math.abs(faces[i].annotations.rightEyeIris[4][1] - faces[i].annotations.rightEyeIris[2][1])) / input.shape[2] : 0;
- const tensor3 = ((_C = instance2.config.face.detector) == null ? void 0 : _C.return) ? tfjs_esm_exports.squeeze(faces[i].tensor) : null;
- tfjs_esm_exports.dispose(faces[i].tensor);
- if (faces[i].tensor)
- delete faces[i].tensor;
- const res = {
- ...faces[i],
- id: i
- };
- if (descRes.age)
- res.age = descRes.age;
- if (descRes.gender)
- res.gender = descRes.gender;
- if (descRes.genderScore)
- res.genderScore = descRes.genderScore;
- if (descRes.descriptor)
- res.embedding = descRes.descriptor;
- if (descRes.race)
- res.race = descRes.race;
- if (emotionRes)
- res.emotion = emotionRes;
- if (antispoofRes)
- res.real = antispoofRes;
- if (livenessRes)
- res.live = livenessRes;
- if (irisSize && irisSize !== 0)
- res.iris = Math.trunc(500 / irisSize / 11.7) / 100;
- if (rotation)
- res.rotation = rotation;
- if (tensor3)
- res.tensor = tensor3;
- faceRes.push(res);
- instance2.analyze("End Face");
- }
- instance2.analyze("End FaceMesh:");
- if (instance2.config.async) {
- if (instance2.performance.face)
- delete instance2.performance.face;
- if (instance2.performance.age)
- delete instance2.performance.age;
- if (instance2.performance.gender)
- delete instance2.performance.gender;
- if (instance2.performance.emotion)
- delete instance2.performance.emotion;
- }
- return faceRes;
-};
-
-// src/gesture/gesture.ts
-var body2 = (res) => {
- if (!res)
- return [];
- const gestures = [];
- for (let i = 0; i < res.length; i++) {
- const leftWrist = res[i].keypoints.find((a) => a.part === "leftWrist");
- const rightWrist = res[i].keypoints.find((a) => a.part === "rightWrist");
- const nose = res[i].keypoints.find((a) => a.part === "nose");
- if (nose && leftWrist && rightWrist && leftWrist.position[1] < nose.position[1] && rightWrist.position[1] < nose.position[1])
- gestures.push({ body: i, gesture: "i give up" });
- else if (nose && leftWrist && leftWrist.position[1] < nose.position[1])
- gestures.push({ body: i, gesture: "raise left hand" });
- else if (nose && rightWrist && rightWrist.position[1] < nose.position[1])
- gestures.push({ body: i, gesture: "raise right hand" });
- const leftShoulder = res[i].keypoints.find((a) => a.part === "leftShoulder");
- const rightShoulder = res[i].keypoints.find((a) => a.part === "rightShoulder");
- if (leftShoulder && rightShoulder && Math.abs(leftShoulder.positionRaw[1] - rightShoulder.positionRaw[1]) > 0.1) {
- gestures.push({ body: i, gesture: `leaning ${leftShoulder.position[1] > rightShoulder.position[1] ? "left" : "right"}` });
- }
- }
- return gestures;
-};
-var face2 = (res) => {
- if (!res)
- return [];
- const gestures = [];
- for (let i = 0; i < res.length; i++) {
- if (res[i].mesh && res[i].mesh.length > 450) {
- const zDiff = (res[i].mesh[33][2] || 0) - (res[i].mesh[263][2] || 0);
- const xDiff = res[i].mesh[33][0] - res[i].mesh[263][0];
- if (Math.abs(zDiff / xDiff) <= 0.15)
- gestures.push({ face: i, gesture: "facing center" });
- else
- gestures.push({ face: i, gesture: `facing ${zDiff < 0 ? "left" : "right"}` });
- const openLeft = Math.abs(res[i].mesh[374][1] - res[i].mesh[386][1]) / Math.abs(res[i].mesh[443][1] - res[i].mesh[450][1]);
- if (openLeft < 0.2)
- gestures.push({ face: i, gesture: "blink left eye" });
- const openRight = Math.abs(res[i].mesh[145][1] - res[i].mesh[159][1]) / Math.abs(res[i].mesh[223][1] - res[i].mesh[230][1]);
- if (openRight < 0.2)
- gestures.push({ face: i, gesture: "blink right eye" });
- const mouthOpen = Math.min(100, 500 * Math.abs(res[i].mesh[13][1] - res[i].mesh[14][1]) / Math.abs(res[i].mesh[10][1] - res[i].mesh[152][1]));
- if (mouthOpen > 10)
- gestures.push({ face: i, gesture: `mouth ${Math.trunc(mouthOpen)}% open` });
- const chinDepth = res[i].mesh[152][2] || 0;
- if (Math.abs(chinDepth) > 10)
- gestures.push({ face: i, gesture: `head ${chinDepth < 0 ? "up" : "down"}` });
- }
- }
- return gestures;
-};
-var iris2 = (res) => {
- var _a, _b, _c, _d;
- if (!res)
- return [];
- const gestures = [];
- for (let i = 0; i < res.length; i++) {
- if (!((_b = (_a = res[i].annotations) == null ? void 0 : _a.leftEyeIris) == null ? void 0 : _b[0]) || !((_d = (_c = res[i].annotations) == null ? void 0 : _c.rightEyeIris) == null ? void 0 : _d[0]))
- continue;
- const sizeXLeft = res[i].annotations.leftEyeIris[3][0] - res[i].annotations.leftEyeIris[1][0];
- const sizeYLeft = res[i].annotations.leftEyeIris[4][1] - res[i].annotations.leftEyeIris[2][1];
- const areaLeft = Math.abs(sizeXLeft * sizeYLeft);
- const sizeXRight = res[i].annotations.rightEyeIris[3][0] - res[i].annotations.rightEyeIris[1][0];
- const sizeYRight = res[i].annotations.rightEyeIris[4][1] - res[i].annotations.rightEyeIris[2][1];
- const areaRight = Math.abs(sizeXRight * sizeYRight);
- let center = false;
- const difference = Math.abs(areaLeft - areaRight) / Math.max(areaLeft, areaRight);
- if (difference < 0.25) {
- center = true;
- gestures.push({ iris: i, gesture: "facing center" });
- }
- const leftIrisCenterX = Math.abs(res[i].mesh[263][0] - res[i].annotations.leftEyeIris[0][0]) / res[i].box[2];
- const rightIrisCenterX = Math.abs(res[i].mesh[33][0] - res[i].annotations.rightEyeIris[0][0]) / res[i].box[2];
- if (leftIrisCenterX > 0.06 || rightIrisCenterX > 0.06)
- center = false;
- if (leftIrisCenterX > rightIrisCenterX) {
- if (leftIrisCenterX > 0.05)
- gestures.push({ iris: i, gesture: "looking right" });
- } else {
- if (rightIrisCenterX > 0.05)
- gestures.push({ iris: i, gesture: "looking left" });
- }
- const rightIrisCenterY = Math.abs(res[i].mesh[145][1] - res[i].annotations.rightEyeIris[0][1]) / res[i].box[3];
- const leftIrisCenterY = Math.abs(res[i].mesh[374][1] - res[i].annotations.leftEyeIris[0][1]) / res[i].box[3];
- if (leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01 || leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022)
- center = false;
- if (leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01)
- gestures.push({ iris: i, gesture: "looking down" });
- if (leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022)
- gestures.push({ iris: i, gesture: "looking up" });
- if (center)
- gestures.push({ iris: i, gesture: "looking center" });
- }
- return gestures;
-};
-var hand2 = (res) => {
- if (!res)
- return [];
- const gestures = [];
- for (let i = 0; i < res.length; i++) {
- const fingers = [];
- if (res[i].annotations) {
- for (const [finger, pos] of Object.entries(res[i].annotations)) {
- if (finger !== "palmBase" && Array.isArray(pos) && pos[0])
- fingers.push({ name: finger.toLowerCase(), position: pos[0] });
- }
- }
- if (fingers && fingers.length > 0) {
- const closest = fingers.reduce((best, a) => (best.position[2] || 0) < (a.position[2] || 0) ? best : a);
- gestures.push({ hand: i, gesture: `${closest.name} forward` });
- const highest = fingers.reduce((best, a) => best.position[1] < a.position[1] ? best : a);
- gestures.push({ hand: i, gesture: `${highest.name} up` });
- }
- if (res[i].keypoints) {
- const poses = match(res[i].keypoints);
- for (const pose of poses)
- gestures.push({ hand: i, gesture: pose.name });
- }
- }
- return gestures;
-};
-
-// src/util/interpolate.ts
-var bufferedResult = { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0, error: null };
-var interpolateTime = 0;
-function calc2(newResult, config3) {
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
- const t0 = now();
- if (!newResult)
- return { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0, error: null };
- const elapsed = Date.now() - newResult.timestamp;
- const bufferedFactor = elapsed < 1e3 ? 8 - Math.log(elapsed + 1) : 1;
- if (newResult.canvas)
- bufferedResult.canvas = newResult.canvas;
- if (newResult.error)
- bufferedResult.error = newResult.error;
- if (!bufferedResult.body || newResult.body.length !== bufferedResult.body.length) {
- bufferedResult.body = JSON.parse(JSON.stringify(newResult.body));
- } else {
- for (let i = 0; i < newResult.body.length; i++) {
- const box = newResult.body[i].box.map((newBoxCoord, j) => ((bufferedFactor - 1) * bufferedResult.body[i].box[j] + newBoxCoord) / bufferedFactor);
- const boxRaw = newResult.body[i].boxRaw.map((newBoxCoord, j) => ((bufferedFactor - 1) * bufferedResult.body[i].boxRaw[j] + newBoxCoord) / bufferedFactor);
- const keypoints = newResult.body[i].keypoints.map((newKpt, j) => {
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2;
- return {
- score: newKpt.score,
- part: newKpt.part,
- position: [
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[0] || 0) + (newKpt.position[0] || 0)) / bufferedFactor : newKpt.position[0],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[1] || 0) + (newKpt.position[1] || 0)) / bufferedFactor : newKpt.position[1],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[2] || 0) + (newKpt.position[2] || 0)) / bufferedFactor : newKpt.position[2]
- ],
- positionRaw: [
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[0] || 0) + (newKpt.positionRaw[0] || 0)) / bufferedFactor : newKpt.positionRaw[0],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[1] || 0) + (newKpt.positionRaw[1] || 0)) / bufferedFactor : newKpt.positionRaw[1],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[2] || 0) + (newKpt.positionRaw[2] || 0)) / bufferedFactor : newKpt.positionRaw[2]
- ],
- distance: [
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (((_a2 = bufferedResult.body[i].keypoints[j].distance) == null ? void 0 : _a2[0]) || 0) + (((_b2 = newKpt.distance) == null ? void 0 : _b2[0]) || 0)) / bufferedFactor : (_c2 = newKpt.distance) == null ? void 0 : _c2[0],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (((_d2 = bufferedResult.body[i].keypoints[j].distance) == null ? void 0 : _d2[1]) || 0) + (((_e2 = newKpt.distance) == null ? void 0 : _e2[1]) || 0)) / bufferedFactor : (_f2 = newKpt.distance) == null ? void 0 : _f2[1],
- bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (((_g2 = bufferedResult.body[i].keypoints[j].distance) == null ? void 0 : _g2[2]) || 0) + (((_h2 = newKpt.distance) == null ? void 0 : _h2[2]) || 0)) / bufferedFactor : (_i2 = newKpt.distance) == null ? void 0 : _i2[2]
- ]
- };
- });
- const annotations2 = {};
- let coords = { connected: {} };
- if ((_a = config3.body.modelPath) == null ? void 0 : _a.includes("efficientpose"))
- coords = efficientposecoords_exports;
- else if ((_b = config3.body.modelPath) == null ? void 0 : _b.includes("blazepose"))
- coords = blazeposecoords_exports;
- else if ((_c = config3.body.modelPath) == null ? void 0 : _c.includes("movenet"))
- coords = movenetcoords_exports;
- for (const [name, indexes] of Object.entries(coords.connected)) {
- const pt = [];
- for (let j = 0; j < indexes.length - 1; j++) {
- const pt0 = keypoints.find((kp) => kp.part === indexes[j]);
- const pt1 = keypoints.find((kp) => kp.part === indexes[j + 1]);
- if (pt0 && pt1)
- pt.push([pt0.position, pt1.position]);
- }
- annotations2[name] = pt;
- }
- bufferedResult.body[i] = { ...newResult.body[i], box, boxRaw, keypoints, annotations: annotations2 };
- }
- }
- if (!bufferedResult.hand || newResult.hand.length !== bufferedResult.hand.length) {
- bufferedResult.hand = JSON.parse(JSON.stringify(newResult.hand));
- } else {
- for (let i = 0; i < newResult.hand.length; i++) {
- const box = newResult.hand[i].box.map((b, j) => ((bufferedFactor - 1) * bufferedResult.hand[i].box[j] + b) / bufferedFactor);
- const boxRaw = newResult.hand[i].boxRaw.map((b, j) => ((bufferedFactor - 1) * bufferedResult.hand[i].boxRaw[j] + b) / bufferedFactor);
- if (bufferedResult.hand[i].keypoints.length !== newResult.hand[i].keypoints.length)
- bufferedResult.hand[i].keypoints = newResult.hand[i].keypoints;
- const keypoints = newResult.hand[i].keypoints && newResult.hand[i].keypoints.length > 0 ? newResult.hand[i].keypoints.map((landmark, j) => landmark.map((coord, k) => ((bufferedFactor - 1) * (bufferedResult.hand[i].keypoints[j][k] || 1) + (coord || 0)) / bufferedFactor)) : [];
- let annotations2 = {};
- if (Object.keys(bufferedResult.hand[i].annotations).length !== Object.keys(newResult.hand[i].annotations).length) {
- bufferedResult.hand[i].annotations = newResult.hand[i].annotations;
- annotations2 = bufferedResult.hand[i].annotations;
- } else if (newResult.hand[i].annotations) {
- for (const key of Object.keys(newResult.hand[i].annotations)) {
- annotations2[key] = ((_f = (_e = (_d = newResult.hand[i]) == null ? void 0 : _d.annotations) == null ? void 0 : _e[key]) == null ? void 0 : _f[0]) ? newResult.hand[i].annotations[key].map((val, j) => val.map((coord, k) => ((bufferedFactor - 1) * bufferedResult.hand[i].annotations[key][j][k] + coord) / bufferedFactor)) : null;
- }
- }
- bufferedResult.hand[i] = { ...newResult.hand[i], box, boxRaw, keypoints, annotations: annotations2 };
- }
- }
- if (!bufferedResult.face || newResult.face.length !== bufferedResult.face.length) {
- bufferedResult.face = JSON.parse(JSON.stringify(newResult.face));
- } else {
- for (let i = 0; i < newResult.face.length; i++) {
- const box = newResult.face[i].box.map((b, j) => ((bufferedFactor - 1) * bufferedResult.face[i].box[j] + b) / bufferedFactor);
- const boxRaw = newResult.face[i].boxRaw.map((b, j) => ((bufferedFactor - 1) * bufferedResult.face[i].boxRaw[j] + b) / bufferedFactor);
- if (newResult.face[i].rotation) {
- const rotation = { matrix: [0, 0, 0, 0, 0, 0, 0, 0, 0], angle: { roll: 0, yaw: 0, pitch: 0 }, gaze: { bearing: 0, strength: 0 } };
- rotation.matrix = (_g = newResult.face[i].rotation) == null ? void 0 : _g.matrix;
- rotation.angle = {
- roll: ((bufferedFactor - 1) * (((_h = bufferedResult.face[i].rotation) == null ? void 0 : _h.angle.roll) || 0) + (((_i = newResult.face[i].rotation) == null ? void 0 : _i.angle.roll) || 0)) / bufferedFactor,
- yaw: ((bufferedFactor - 1) * (((_j = bufferedResult.face[i].rotation) == null ? void 0 : _j.angle.yaw) || 0) + (((_k = newResult.face[i].rotation) == null ? void 0 : _k.angle.yaw) || 0)) / bufferedFactor,
- pitch: ((bufferedFactor - 1) * (((_l = bufferedResult.face[i].rotation) == null ? void 0 : _l.angle.pitch) || 0) + (((_m = newResult.face[i].rotation) == null ? void 0 : _m.angle.pitch) || 0)) / bufferedFactor
- };
- rotation.gaze = {
- bearing: ((bufferedFactor - 1) * (((_n = bufferedResult.face[i].rotation) == null ? void 0 : _n.gaze.bearing) || 0) + (((_o = newResult.face[i].rotation) == null ? void 0 : _o.gaze.bearing) || 0)) / bufferedFactor,
- strength: ((bufferedFactor - 1) * (((_p = bufferedResult.face[i].rotation) == null ? void 0 : _p.gaze.strength) || 0) + (((_q = newResult.face[i].rotation) == null ? void 0 : _q.gaze.strength) || 0)) / bufferedFactor
- };
- bufferedResult.face[i] = { ...newResult.face[i], rotation, box, boxRaw };
- }
- bufferedResult.face[i] = { ...newResult.face[i], box, boxRaw };
- }
- }
- if (!bufferedResult.object || newResult.object.length !== bufferedResult.object.length) {
- bufferedResult.object = JSON.parse(JSON.stringify(newResult.object));
- } else {
- for (let i = 0; i < newResult.object.length; i++) {
- const box = newResult.object[i].box.map((b, j) => ((bufferedFactor - 1) * bufferedResult.object[i].box[j] + b) / bufferedFactor);
- const boxRaw = newResult.object[i].boxRaw.map((b, j) => ((bufferedFactor - 1) * bufferedResult.object[i].boxRaw[j] + b) / bufferedFactor);
- bufferedResult.object[i] = { ...newResult.object[i], box, boxRaw };
- }
- }
- if (newResult.persons) {
- const newPersons = newResult.persons;
- if (!bufferedResult.persons || newPersons.length !== bufferedResult.persons.length) {
- bufferedResult.persons = JSON.parse(JSON.stringify(newPersons));
- } else {
- for (let i = 0; i < newPersons.length; i++) {
- bufferedResult.persons[i].box = newPersons[i].box.map((box, j) => ((bufferedFactor - 1) * bufferedResult.persons[i].box[j] + box) / bufferedFactor);
- }
- }
- }
- if (newResult.gesture)
- bufferedResult.gesture = newResult.gesture;
- const t1 = now();
- interpolateTime = env.perfadd ? interpolateTime + Math.round(t1 - t0) : Math.round(t1 - t0);
- if (newResult.performance)
- bufferedResult.performance = { ...newResult.performance, interpolate: interpolateTime };
- return bufferedResult;
-}
-
-// src/face/match.ts
-var match_exports = {};
-__export(match_exports, {
- distance: () => distance,
- match: () => match2,
- similarity: () => similarity
-});
-function distance(descriptor1, descriptor2, options4 = { order: 2, multiplier: 25 }) {
- if (!descriptor1 || !descriptor1)
- return Number.MAX_SAFE_INTEGER;
- let sum3 = 0;
- for (let i = 0; i < descriptor1.length; i++) {
- const diff = !options4.order || options4.order === 2 ? descriptor1[i] - descriptor2[i] : Math.abs(descriptor1[i] - descriptor2[i]);
- sum3 += !options4.order || options4.order === 2 ? diff * diff : diff ** options4.order;
- }
- return (options4.multiplier || 20) * sum3;
-}
-var normalizeDistance = (dist, order, min2, max4) => {
- if (dist === 0)
- return 1;
- const root = order === 2 ? Math.sqrt(dist) : dist ** (1 / order);
- const norm = (1 - root / 100 - min2) / (max4 - min2);
- const clamp2 = Math.max(Math.min(norm, 1), 0);
- return clamp2;
-};
-function similarity(descriptor1, descriptor2, options4 = { order: 2, multiplier: 25, min: 0.2, max: 0.8 }) {
- const dist = distance(descriptor1, descriptor2, options4);
- return normalizeDistance(dist, options4.order || 2, options4.min || 0, options4.max || 1);
-}
-function match2(descriptor, descriptors, options4 = { 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) {
- return { index: -1, distance: Number.POSITIVE_INFINITY, similarity: 0 };
- }
- let lowestDistance = Number.MAX_SAFE_INTEGER;
- let index2 = -1;
- for (let i = 0; i < descriptors.length; i++) {
- const res = descriptors[i].length === descriptor.length ? distance(descriptor, descriptors[i], options4) : Number.MAX_SAFE_INTEGER;
- if (res < lowestDistance) {
- lowestDistance = res;
- index2 = i;
- }
- if (lowestDistance < (options4.threshold || 0))
- break;
- }
- const normalizedSimilarity = normalizeDistance(lowestDistance, options4.order || 2, options4.min || 0, options4.max || 1);
- return { index: index2, distance: lowestDistance, similarity: normalizedSimilarity };
-}
-
-// src/util/persons.ts
-function join2(faces, bodies, hands, gestures, shape) {
- var _a, _b, _c, _d, _e, _f;
- let id = 0;
- const persons = [];
- for (const face4 of faces) {
- const person2 = { id: id++, face: face4, body: null, hands: { left: null, right: null }, gestures: [], box: [0, 0, 0, 0] };
- for (const body4 of bodies) {
- if (face4.box[0] > body4.box[0] && face4.box[0] < body4.box[0] + body4.box[2] && face4.box[1] + face4.box[3] > body4.box[1] && face4.box[1] + face4.box[3] < body4.box[1] + body4.box[3]) {
- person2.body = body4;
- }
- }
- if (person2.body) {
- for (const hand3 of hands) {
- if (hand3.box[0] + hand3.box[2] > person2.body.box[0] && hand3.box[0] + hand3.box[2] < person2.body.box[0] + person2.body.box[2] && hand3.box[1] + hand3.box[3] > person2.body.box[1] && hand3.box[1] + hand3.box[3] < person2.body.box[1] + person2.body.box[3]) {
- if (person2.hands)
- person2.hands.left = hand3;
- }
- if (hand3.box[0] < person2.body.box[0] + person2.body.box[2] && hand3.box[0] > person2.body.box[0] && hand3.box[1] + hand3.box[3] > person2.body.box[1] && hand3.box[1] + hand3.box[3] < person2.body.box[1] + person2.body.box[3]) {
- if (person2.hands)
- person2.hands.right = hand3;
- }
- }
- }
- for (const gesture2 of gestures) {
- if (gesture2["face"] !== void 0 && gesture2["face"] === face4.id)
- person2.gestures.push(gesture2);
- else if (gesture2["iris"] !== void 0 && gesture2["iris"] === face4.id)
- person2.gestures.push(gesture2);
- else if (gesture2["body"] !== void 0 && gesture2["body"] === ((_a = person2.body) == null ? void 0 : _a.id))
- person2.gestures.push(gesture2);
- else if (gesture2["hand"] !== void 0 && gesture2["hand"] === ((_b = person2.hands.left) == null ? void 0 : _b.id))
- person2.gestures.push(gesture2);
- else if (gesture2["hand"] !== void 0 && gesture2["hand"] === ((_c = person2.hands.right) == null ? void 0 : _c.id))
- person2.gestures.push(gesture2);
- }
- const x = [];
- const y = [];
- const extractXY = (box) => {
- if (box && box.length === 4) {
- x.push(box[0], box[0] + box[2]);
- y.push(box[1], box[1] + box[3]);
- }
- };
- extractXY(person2.face.box);
- extractXY((_d = person2.body) == null ? void 0 : _d.box);
- extractXY((_e = person2.hands.left) == null ? void 0 : _e.box);
- extractXY((_f = person2.hands.right) == null ? void 0 : _f.box);
- const minX = Math.min(...x);
- const minY = Math.min(...y);
- person2.box = [minX, minY, Math.max(...x) - minX, Math.max(...y) - minY];
- if ((shape == null ? void 0 : shape[1]) && (shape == null ? void 0 : shape[2]))
- person2.boxRaw = [person2.box[0] / shape[2], person2.box[1] / shape[1], person2.box[2] / shape[2], person2.box[3] / shape[1]];
- persons.push(person2);
- }
- return persons;
-}
-
-// src/sample.ts
-var face3 = `
+ ${e.box[0]} ${r},
+ ${e.box[0]+e.box[2]} ${r},
+ ${e.box[0]+e.box[2]} ${e.box[1]+e.box[3]/2}
+ `);t.stroke(a),t.stroke(s)}}function CA(e,t){var o;if(H.drawGaze&&((o=e.rotation)==null?void 0:o.gaze.strength)&&e.rotation.gaze.bearing&&e.annotations.leftEyeIris&&e.annotations.rightEyeIris&&e.annotations.leftEyeIris[0]&&e.annotations.rightEyeIris[0]){t.strokeStyle="pink",t.fillStyle="pink";let n=[e.annotations.leftEyeIris[0][0]+Math.sin(e.rotation.gaze.bearing)*e.rotation.gaze.strength*e.box[3],e.annotations.leftEyeIris[0][1]+Math.cos(e.rotation.gaze.bearing)*e.rotation.gaze.strength*e.box[2]];M1(t,[e.annotations.leftEyeIris[0][0],e.annotations.leftEyeIris[0][1]],[n[0],n[1]],4);let r=[e.annotations.rightEyeIris[0][0]+Math.sin(e.rotation.gaze.bearing)*e.rotation.gaze.strength*e.box[3],e.annotations.rightEyeIris[0][1]+Math.cos(e.rotation.gaze.bearing)*e.rotation.gaze.strength*e.box[2]];M1(t,[e.annotations.rightEyeIris[0][0],e.annotations.rightEyeIris[0][1]],[r[0],r[1]],4)}}function IA(e,t){if(H.drawPolygons&&e.mesh.length>=468){t.lineWidth=1;for(let o=0;oe.mesh[r]);g1(t,n,H)}zA(e,t)}}function jA(e,t){if(H.drawPoints&&e.mesh.length>=468)for(let o=0;o0&&(jA(r,n),IA(r,n),SA(r,n),CA(r,n))}}function Oe(e,t,o){let n=U(A0,o);if(!t||!e)return;let r=M0(e);if(!!r){r.lineJoin="round";for(let s=0;s0)for(let a of s.keypoints)r.fillStyle=V0(a[2],n),Z0(r,a[0],a[1],0,n);if(n.drawLabels&&s.annotations){let a=(i,c)=>{if(!i||i.length===0||!i[0])return;let x=i[i.length-1][2]||-256;r.fillStyle=V0(x,n),r.fillText(c,i[i.length-1][0]+4,i[i.length-1][1]+4)};r.font=n.font,a(s.annotations.index,"index"),a(s.annotations.middle,"middle"),a(s.annotations.ring,"ring"),a(s.annotations.pinky,"pinky"),a(s.annotations.thumb,"thumb"),a(s.annotations.palm,"palm")}if(n.drawPolygons&&s.annotations){let a=i=>{if(!(!i||i.length===0||!i[0]))for(let c=0;c0?c-1:0][0],i[c>0?c-1:0][1]),r.lineTo(i[c][0],i[c][1]),r.stroke()}};r.lineWidth=n.lineWidth,a(s.annotations.index),a(s.annotations.middle),a(s.annotations.ring),a(s.annotations.pinky),a(s.annotations.thumb)}}}}function Le(e,t,o){let n=U(A0,o);if(!t||!e)return;let r=M0(e);if(!!r){r.lineJoin="round",r.font=n.font;for(let s of t)if(n.drawBoxes){if(r.strokeStyle=n.color,r.fillStyle=n.color,W0(r,s.box[0],s.box[1],s.box[2],s.box[3],n),n.drawLabels){let a=`${s.label} ${Math.round(100*s.score)}%`;n.shadowColor&&n.shadowColor!==""&&(r.fillStyle=n.shadowColor,r.fillText(a,s.box[0]+3,1+s.box[1]+n.lineHeight,s.box[2])),r.fillStyle=n.labelColor,r.fillText(a,s.box[0]+2,0+s.box[1]+n.lineHeight,s.box[2])}r.stroke()}}}function We(e,t,o){let n=U(A0,o);if(!(!t||!e)&&n.drawGestures){let r=M0(e);if(!r)return;r.font=n.font,r.fillStyle=n.color;let s=1;for(let a=0;a1&&c[1].length>0){let x=i[1]>0?`#${i[1]}`:"",d=`${i[0]} ${x}: ${c[1]}`;n.shadowColor&&n.shadowColor!==""&&(r.fillStyle=n.shadowColor,r.fillText(d,8,2+s*n.lineHeight)),r.fillStyle=n.labelColor,r.fillText(d,6,0+s*n.lineHeight),s+=1}}}}var R1=0;function v1(e,t,o){let n=U(A0,o);if(!t||!e)return;let r=M0(e);if(!!r){r.lineJoin="round",r.font=n.font;for(let s=0;st!=o[r].y>t&&e<(o[r].x-o[s].x)*(t-o[s].y)/(o[r].y-o[s].y)+o[s].x&&(n=!n);return n}async function bo(e){if(!e.tensor||!e.mesh||e.mesh.length<100)return e.tensor;let t=e.tensor.shape[2]||0,o=e.tensor.shape[1]||0,n=await e.tensor.buffer(),r=[];for(let a of w0.silhouette)r.push({x:(e.mesh[a][0]-e.box[0])/e.box[2],y:(e.mesh[a][1]-e.box[1])/e.box[3]});Fe&&Fe>0&&(r=r.map(a=>({x:a.x>.5?a.x+Fe:a.x-Fe,y:a.y>.5?a.y+Fe:a.y-Fe})));for(let a=0;a{let t=(l,f)=>Math.atan2(l[1]-f[1],l[0]-f[0]);if(!e.annotations.rightEyeIris||!e.annotations.leftEyeIris)return{bearing:0,strength:0};let o=[0,-.1],n=1,r=(e.mesh[33][2]||0)>(e.mesh[263][2]||0),s=r?e.mesh[473]:e.mesh[468],a=r?[(e.mesh[133][0]+e.mesh[33][0])/2,(e.mesh[133][1]+e.mesh[33][1])/2]:[(e.mesh[263][0]+e.mesh[362][0])/2,(e.mesh[263][1]+e.mesh[362][1])/2],i=r?[e.mesh[133][0]-e.mesh[33][0],e.mesh[23][1]-e.mesh[27][1]]:[e.mesh[263][0]-e.mesh[362][0],e.mesh[253][1]-e.mesh[257][1]],c=[(a[0]-s[0])/i[0]-o[0],n*(s[1]-a[1])/i[1]-o[1]],x=Math.sqrt(c[0]*c[0]+c[1]*c[1]);return x=Math.min(x,e.boxRaw[2]/2,e.boxRaw[3]/2),{bearing:(t([0,0],c)+Math.PI/2)%Math.PI,strength:x}},go=(e,t)=>{let o=g=>{let M=Math.sqrt(g[0]*g[0]+g[1]*g[1]+g[2]*g[2]);return g[0]/=M,g[1]/=M,g[2]/=M,g},n=(g,M)=>{let R=g[0]-M[0],P=g[1]-M[1],m=g[2]-M[2];return[R,P,m]},r=(g,M)=>{let R=g[1]*M[2]-g[2]*M[1],P=g[2]*M[0]-g[0]*M[2],m=g[0]*M[1]-g[1]*M[0];return[R,P,m]},s=g=>{let[M,R,P,m,u,z,k,h,W]=g,B,j,N;return m<1?m>-1?(N=Math.asin(m),j=Math.atan2(-k,M),B=Math.atan2(-z,u)):(N=-Math.PI/2,j=-Math.atan2(h,W),B=0):(N=Math.PI/2,j=Math.atan2(h,W),B=0),Number.isNaN(B)&&(B=0),Number.isNaN(j)&&(j=0),Number.isNaN(N)&&(N=0),{pitch:2*-B,yaw:2*-j,roll:2*-N}},a=e.meshRaw;if(!a||a.length<300)return{angle:{pitch:0,yaw:0,roll:0},matrix:[1,0,0,0,1,0,0,0,1],gaze:{bearing:0,strength:0}};let i=Math.max(e.boxRaw[2]*t[0],e.boxRaw[3]*t[1])/1.5,c=[a[10],a[152],a[234],a[454]].map(g=>[g[0]*t[0]/i,g[1]*t[1]/i,g[2]]),x=o(n(c[1],c[0])),d=o(n(c[3],c[2])),l=o(r(d,x));d=r(x,l);let f=[d[0],d[1],d[2],x[0],x[1],x[2],l[0],l[1],l[2]],y=s(f),p=a.length===478?WA(e):{bearing:0,strength:0};return{angle:y,matrix:f,gaze:p}};var k1=async(e,t)=>{var p,g,M,R,P,m,u,z,k,h,W,B,j,N,G,O,I,n0,x0,E0,z0,R0,X0,Be,He,De,C1,I1,j1;let o=v(),n,r,s,a,i,c,x,d,l,f=[];e.state="run:face";let y=await l3(t,e.config);if(e.performance.face=T.perfadd?(e.performance.face||0)+Math.trunc(v()-o):Math.trunc(v()-o),!t.shape||t.shape.length!==4)return[];if(!y)return[];for(let S=0;S200?go(y[S],[t.shape[2],t.shape[1]]):null;e.analyze("Start Emotion:"),e.config.async?a=(g=e.config.face.emotion)!=null&&g.enabled?I5(y[S].tensor||A.tensor([]),e.config,S,y.length):[]:(e.state="run:emotion",o=v(),a=(M=e.config.face.emotion)!=null&&M.enabled?await I5(y[S].tensor||A.tensor([]),e.config,S,y.length):[],e.performance.emotion=T.perfadd?(e.performance.emotion||0)+Math.trunc(v()-o):Math.trunc(v()-o)),e.analyze("End Emotion:"),e.analyze("Start AntiSpoof:"),e.config.async?x=(R=e.config.face.antispoof)!=null&&R.enabled?d5(y[S].tensor||A.tensor([]),e.config,S,y.length):0:(e.state="run:antispoof",o=v(),x=(P=e.config.face.antispoof)!=null&&P.enabled?await d5(y[S].tensor||A.tensor([]),e.config,S,y.length):0,e.performance.antispoof=T.perfadd?(e.performance.antispoof||0)+Math.trunc(v()-o):Math.trunc(v()-o)),e.analyze("End AntiSpoof:"),e.analyze("Start Liveness:"),e.config.async?d=(m=e.config.face.liveness)!=null&&m.enabled?t1(y[S].tensor||A.tensor([]),e.config,S,y.length):0:(e.state="run:liveness",o=v(),d=(u=e.config.face.liveness)!=null&&u.enabled?await t1(y[S].tensor||A.tensor([]),e.config,S,y.length):0,e.performance.liveness=T.perfadd?(e.performance.antispoof||0)+Math.trunc(v()-o):Math.trunc(v()-o)),e.analyze("End Liveness:"),e.analyze("Start GEAR:"),e.config.async?r=(z=e.config.face.gear)!=null&&z.enabled?s5(y[S].tensor||A.tensor([]),e.config,S,y.length):null:(e.state="run:gear",o=v(),r=(k=e.config.face.gear)!=null&&k.enabled?await s5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,e.performance.gear=Math.trunc(v()-o)),e.analyze("End GEAR:"),e.analyze("Start SSRNet:"),e.config.async?(n=(h=e.config.face.ssrnet)!=null&&h.enabled?i5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,s=(W=e.config.face.ssrnet)!=null&&W.enabled?x5(y[S].tensor||A.tensor([]),e.config,S,y.length):null):(e.state="run:ssrnet",o=v(),n=(B=e.config.face.ssrnet)!=null&&B.enabled?await i5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,s=(j=e.config.face.ssrnet)!=null&&j.enabled?await x5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,e.performance.ssrnet=Math.trunc(v()-o)),e.analyze("End SSRNet:"),e.analyze("Start MobileFaceNet:"),e.config.async?i=(N=e.config.face.mobilefacenet)!=null&&N.enabled?O5(y[S].tensor||A.tensor([]),e.config,S,y.length):null:(e.state="run:mobilefacenet",o=v(),i=(G=e.config.face.mobilefacenet)!=null&&G.enabled?await O5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,e.performance.mobilefacenet=Math.trunc(v()-o)),e.analyze("End MobileFaceNet:"),e.analyze("Start InsightFace:"),e.config.async?c=(O=e.config.face.insightface)!=null&&O.enabled?L5(y[S].tensor||A.tensor([]),e.config,S,y.length):null:(e.state="run:mobilefacenet",o=v(),c=(I=e.config.face.insightface)!=null&&I.enabled?await L5(y[S].tensor||A.tensor([]),e.config,S,y.length):null,e.performance.mobilefacenet=Math.trunc(v()-o)),e.analyze("End InsightFace:"),e.analyze("Start Description:"),e.config.async?l=D5(y[S].tensor||A.tensor([]),e.config,S,y.length):(e.state="run:description",o=v(),l=await D5(y[S].tensor||A.tensor([]),e.config,S,y.length),e.performance.description=T.perfadd?(e.performance.description||0)+Math.trunc(v()-o):Math.trunc(v()-o)),e.analyze("End Description:"),e.config.async&&([n,s,a,i,c,l,r,x,d]=await Promise.all([n,s,a,i,c,l,r,x,d])),e.analyze("Finish Face:"),((n0=e.config.face.ssrnet)==null?void 0:n0.enabled)&&n&&s&&(l={...l,age:n.age,gender:s.gender,genderScore:s.genderScore}),((x0=e.config.face.gear)==null?void 0:x0.enabled)&&r&&(l={...l,age:r.age,gender:r.gender,genderScore:r.genderScore,race:r.race}),((E0=e.config.face.mobilefacenet)==null?void 0:E0.enabled)&&i&&(l.descriptor=i),((z0=e.config.face.insightface)==null?void 0:z0.enabled)&&c&&(l.descriptor=c),(R0=e.config.face.iris)!=null&&R0.enabled;let J2=((He=(Be=(X0=y[S])==null?void 0:X0.annotations)==null?void 0:Be.leftEyeIris)==null?void 0:He[0])&&((I1=(C1=(De=y[S])==null?void 0:De.annotations)==null?void 0:C1.rightEyeIris)==null?void 0:I1[0])&&y[S].annotations.leftEyeIris.length>0&&y[S].annotations.rightEyeIris.length>0&&y[S].annotations.leftEyeIris[0]!==null&&y[S].annotations.rightEyeIris[0]!==null?Math.max(Math.abs(y[S].annotations.leftEyeIris[3][0]-y[S].annotations.leftEyeIris[1][0]),Math.abs(y[S].annotations.rightEyeIris[4][1]-y[S].annotations.rightEyeIris[2][1]))/t.shape[2]:0,N1=(j1=e.config.face.detector)!=null&&j1.return?A.squeeze(y[S].tensor):null;A.dispose(y[S].tensor),y[S].tensor&&delete y[S].tensor;let v0={...y[S],id:S};l.age&&(v0.age=l.age),l.gender&&(v0.gender=l.gender),l.genderScore&&(v0.genderScore=l.genderScore),l.descriptor&&(v0.embedding=l.descriptor),l.race&&(v0.race=l.race),a&&(v0.emotion=a),x&&(v0.real=x),d&&(v0.live=d),J2&&J2!==0&&(v0.iris=Math.trunc(500/J2/11.7)/100),O1&&(v0.rotation=O1),N1&&(v0.tensor=N1),f.push(v0),e.analyze("End Face")}return e.analyze("End FaceMesh:"),e.config.async&&(e.performance.face&&delete e.performance.face,e.performance.age&&delete e.performance.age,e.performance.gender&&delete e.performance.gender,e.performance.emotion&&delete e.performance.emotion),f};var Mo=e=>{if(!e)return[];let t=[];for(let o=0;oc.part==="leftWrist"),r=e[o].keypoints.find(c=>c.part==="rightWrist"),s=e[o].keypoints.find(c=>c.part==="nose");s&&n&&r&&n.position[1]c.part==="leftShoulder"),i=e[o].keypoints.find(c=>c.part==="rightShoulder");a&&i&&Math.abs(a.positionRaw[1]-i.positionRaw[1])>.1&&t.push({body:o,gesture:`leaning ${a.position[1]>i.position[1]?"left":"right"}`})}return t},Ro=e=>{if(!e)return[];let t=[];for(let o=0;o450){let n=(e[o].mesh[33][2]||0)-(e[o].mesh[263][2]||0),r=e[o].mesh[33][0]-e[o].mesh[263][0];Math.abs(n/r)<=.15?t.push({face:o,gesture:"facing center"}):t.push({face:o,gesture:`facing ${n<0?"left":"right"}`}),Math.abs(e[o].mesh[374][1]-e[o].mesh[386][1])/Math.abs(e[o].mesh[443][1]-e[o].mesh[450][1])<.2&&t.push({face:o,gesture:"blink left eye"}),Math.abs(e[o].mesh[145][1]-e[o].mesh[159][1])/Math.abs(e[o].mesh[223][1]-e[o].mesh[230][1])<.2&&t.push({face:o,gesture:"blink right eye"});let i=Math.min(100,500*Math.abs(e[o].mesh[13][1]-e[o].mesh[14][1])/Math.abs(e[o].mesh[10][1]-e[o].mesh[152][1]));i>10&&t.push({face:o,gesture:`mouth ${Math.trunc(i)}% open`});let c=e[o].mesh[152][2]||0;Math.abs(c)>10&&t.push({face:o,gesture:`head ${c<0?"up":"down"}`})}return t},vo=e=>{var o,n,r,s;if(!e)return[];let t=[];for(let a=0;a.06||M>.06)&&(y=!1),g>M?g>.05&&t.push({iris:a,gesture:"looking right"}):M>.05&&t.push({iris:a,gesture:"looking left"});let R=Math.abs(e[a].mesh[145][1]-e[a].annotations.rightEyeIris[0][1])/e[a].box[3],P=Math.abs(e[a].mesh[374][1]-e[a].annotations.leftEyeIris[0][1])/e[a].box[3];(P<.01||R<.01||P>.022||R>.022)&&(y=!1),(P<.01||R<.01)&&t.push({iris:a,gesture:"looking down"}),(P>.022||R>.022)&&t.push({iris:a,gesture:"looking up"}),y&&t.push({iris:a,gesture:"looking center"})}return t},Po=e=>{if(!e)return[];let t=[];for(let o=0;o0){let r=n.reduce((a,i)=>(a.position[2]||0)<(i.position[2]||0)?a:i);t.push({hand:o,gesture:`${r.name} forward`});let s=n.reduce((a,i)=>a.position[1]((r-1)*E.body[h].box[I]+O)/r),B=e.body[h].boxRaw.map((O,I)=>((r-1)*E.body[h].boxRaw[I]+O)/r),j=e.body[h].keypoints.map((O,I)=>{var n0,x0,E0,z0,R0,X0,Be,He,De;return{score:O.score,part:O.part,position:[E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].position[0]||0)+(O.position[0]||0))/r:O.position[0],E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].position[1]||0)+(O.position[1]||0))/r:O.position[1],E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].position[2]||0)+(O.position[2]||0))/r:O.position[2]],positionRaw:[E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].positionRaw[0]||0)+(O.positionRaw[0]||0))/r:O.positionRaw[0],E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].positionRaw[1]||0)+(O.positionRaw[1]||0))/r:O.positionRaw[1],E.body[h].keypoints[I]?((r-1)*(E.body[h].keypoints[I].positionRaw[2]||0)+(O.positionRaw[2]||0))/r:O.positionRaw[2]],distance:[E.body[h].keypoints[I]?((r-1)*(((n0=E.body[h].keypoints[I].distance)==null?void 0:n0[0])||0)+(((x0=O.distance)==null?void 0:x0[0])||0))/r:(E0=O.distance)==null?void 0:E0[0],E.body[h].keypoints[I]?((r-1)*(((z0=E.body[h].keypoints[I].distance)==null?void 0:z0[1])||0)+(((R0=O.distance)==null?void 0:R0[1])||0))/r:(X0=O.distance)==null?void 0:X0[1],E.body[h].keypoints[I]?((r-1)*(((Be=E.body[h].keypoints[I].distance)==null?void 0:Be[2])||0)+(((He=O.distance)==null?void 0:He[2])||0))/r:(De=O.distance)==null?void 0:De[2]]}}),N={},G={connected:{}};(a=t.body.modelPath)!=null&&a.includes("efficientpose")?G=v2:(i=t.body.modelPath)!=null&&i.includes("blazepose")?G=h2:(c=t.body.modelPath)!=null&&c.includes("movenet")&&(G=e2);for(let[O,I]of Object.entries(G.connected)){let n0=[];for(let x0=0;x0R0.part===I[x0]),z0=j.find(R0=>R0.part===I[x0+1]);E0&&z0&&n0.push([E0.position,z0.position])}N[O]=n0}E.body[h]={...e.body[h],box:W,boxRaw:B,keypoints:j,annotations:N}}if(!E.hand||e.hand.length!==E.hand.length)E.hand=JSON.parse(JSON.stringify(e.hand));else for(let h=0;h((r-1)*E.hand[h].box[O]+G)/r),B=e.hand[h].boxRaw.map((G,O)=>((r-1)*E.hand[h].boxRaw[O]+G)/r);E.hand[h].keypoints.length!==e.hand[h].keypoints.length&&(E.hand[h].keypoints=e.hand[h].keypoints);let j=e.hand[h].keypoints&&e.hand[h].keypoints.length>0?e.hand[h].keypoints.map((G,O)=>G.map((I,n0)=>((r-1)*(E.hand[h].keypoints[O][n0]||1)+(I||0))/r)):[],N={};if(Object.keys(E.hand[h].annotations).length!==Object.keys(e.hand[h].annotations).length)E.hand[h].annotations=e.hand[h].annotations,N=E.hand[h].annotations;else if(e.hand[h].annotations)for(let G of Object.keys(e.hand[h].annotations))N[G]=(l=(d=(x=e.hand[h])==null?void 0:x.annotations)==null?void 0:d[G])!=null&&l[0]?e.hand[h].annotations[G].map((O,I)=>O.map((n0,x0)=>((r-1)*E.hand[h].annotations[G][I][x0]+n0)/r)):null;E.hand[h]={...e.hand[h],box:W,boxRaw:B,keypoints:j,annotations:N}}if(!E.face||e.face.length!==E.face.length)E.face=JSON.parse(JSON.stringify(e.face));else for(let h=0;h((r-1)*E.face[h].box[N]+j)/r),B=e.face[h].boxRaw.map((j,N)=>((r-1)*E.face[h].boxRaw[N]+j)/r);if(e.face[h].rotation){let j={matrix:[0,0,0,0,0,0,0,0,0],angle:{roll:0,yaw:0,pitch:0},gaze:{bearing:0,strength:0}};j.matrix=(f=e.face[h].rotation)==null?void 0:f.matrix,j.angle={roll:((r-1)*(((y=E.face[h].rotation)==null?void 0:y.angle.roll)||0)+(((p=e.face[h].rotation)==null?void 0:p.angle.roll)||0))/r,yaw:((r-1)*(((g=E.face[h].rotation)==null?void 0:g.angle.yaw)||0)+(((M=e.face[h].rotation)==null?void 0:M.angle.yaw)||0))/r,pitch:((r-1)*(((R=E.face[h].rotation)==null?void 0:R.angle.pitch)||0)+(((P=e.face[h].rotation)==null?void 0:P.angle.pitch)||0))/r},j.gaze={bearing:((r-1)*(((m=E.face[h].rotation)==null?void 0:m.gaze.bearing)||0)+(((u=e.face[h].rotation)==null?void 0:u.gaze.bearing)||0))/r,strength:((r-1)*(((z=E.face[h].rotation)==null?void 0:z.gaze.strength)||0)+(((k=e.face[h].rotation)==null?void 0:k.gaze.strength)||0))/r},E.face[h]={...e.face[h],rotation:j,box:W,boxRaw:B}}E.face[h]={...e.face[h],box:W,boxRaw:B}}if(!E.object||e.object.length!==E.object.length)E.object=JSON.parse(JSON.stringify(e.object));else for(let h=0;h((r-1)*E.object[h].box[N]+j)/r),B=e.object[h].boxRaw.map((j,N)=>((r-1)*E.object[h].boxRaw[N]+j)/r);E.object[h]={...e.object[h],box:W,boxRaw:B}}if(e.persons){let h=e.persons;if(!E.persons||h.length!==E.persons.length)E.persons=JSON.parse(JSON.stringify(h));else for(let W=0;W((r-1)*E.persons[W].box[j]+B)/r)}e.gesture&&(E.gesture=e.gesture);let s=v();return E1=T.perfadd?E1+Math.round(s-o):Math.round(s-o),e.performance&&(E.performance={...e.performance,interpolate:E1}),E}var ko={};q0(ko,{distance:()=>r2,match:()=>S1,similarity:()=>z1});function r2(e,t,o={order:2,multiplier:25}){if(!e||!e)return Number.MAX_SAFE_INTEGER;let n=0;for(let r=0;r{if(e===0)return 1;let r=t===2?Math.sqrt(e):e**(1/t),s=(1-r/100-o)/(n-o);return Math.max(Math.min(s,1),0)};function z1(e,t,o={order:2,multiplier:25,min:.2,max:.8}){let n=r2(e,t,o);return wo(n,o.order||2,o.min||0,o.max||1)}function S1(e,t,o={order:2,multiplier:25,threshold:0,min:.2,max:.8}){if(!Array.isArray(e)||!Array.isArray(t)||e.length<64||t.length===0)return{index:-1,distance:Number.POSITIVE_INFINITY,similarity:0};let n=Number.MAX_SAFE_INTEGER,r=-1;for(let a=0;au.box[0]&&y.box[0]u.box[1]&&y.box[1]+y.box[3]p.body.box[0]&&u.box[0]+u.box[2]p.body.box[1]&&u.box[1]+u.box[3]p.body.box[0]&&u.box[1]+u.box[3]>p.body.box[1]&&u.box[1]+u.box[3]{u&&u.length===4&&(g.push(u[0],u[0]+u[2]),M.push(u[1],u[1]+u[3]))};R(p.face.box),R((d=p.body)==null?void 0:d.box),R((l=p.hands.left)==null?void 0:l.box),R((f=p.hands.right)==null?void 0:f.box);let P=Math.min(...g),m=Math.min(...M);p.box=[P,m,Math.max(...g)-P,Math.max(...M)-m],(r==null?void 0:r[1])&&(r==null?void 0:r[2])&&(p.boxRaw=[p.box[0]/r[2],p.box[1]/r[1],p.box[2]/r[2],p.box[3]/r[1]]),a.push(p)}return a}var U2=`
/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUA
AAABAAAARgEoAAMAAAABAAIAAAExAAIAAAARAAAATgAAAAAAAABgAAAAAQAAAGAAAAABcGFpbnQu
bmV0IDQuMi4xMwAA/9sAQwAGBAUGBQQGBgUGBwcGCAoQCgoJCQoUDg8MEBcUGBgXFBYWGh0lHxob
@@ -13599,8 +259,7 @@ PQ4GJ+ashuK0MhWaoWcA0AaOmASMK7jRNPWYBmHyiuepO2x10qfcv6vYxCzYqoGK4HVYVTJrmb5l
c6oaM5TUJ8EgGsG4kLNUHT0M64OaqMMikSRsuKbnFMRLG3zVehOaGNE445NNlnVFpDMu6uie9Vo1
8z5mOAOST2pDK91cNN+5tsrH3PrW54a06KxT7fdrlh/q1Pc+tJ6IUdZGvHPLezMcnBOWbsPap5r3
ylFtbdT1xUWNWzU0/Zbwlgfmx8zGsHWtRHmMqE59aAMyNifvHPc1f0gtPdqkY5JosJHeNci2tktY
-euPnNY+oXWZEVJNrZ9aun8SIq/CzodHuriIokhDIR1ronbKZr0o6o8ipoz//2Q==`;
-var body3 = `
+euPnNY+oXWZEVJNrZ9aun8SIq/CzodHuriIokhDIR1ronbKZr0o6o8ipoz//2Q==`,Y2=`
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAsICAoIBwsKCQoNDAsNERwSEQ8PESIZGhQcKSQrKigk
JyctMkA3LTA9MCcnOEw5PUNFSElIKzZPVU5GVEBHSEX/2wBDAQwNDREPESESEiFFLicuRUVFRUVF
RUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUX/wAARCASwBLADASIA
@@ -14168,524 +827,5 @@ AAAAAAJAAAAAAAAAAAAAABAJEAAAAAAAAAAAAAAAIEoBKAAAAAAAAAAAAAAABAlAAAAAAAIAAAAA
BAkBAkBAkBAlACEgMZjdjbFW8bWrEx8YWANb6Fp+bfwab+vLDKMFK9qxH5L0bAr8OPRPKz2AY7J2
SbAjYZAI2E7AIEgIEgIEgMdkSy2NgY7MdlmyNoBXsxmFuyNgVTVjNV3KjlBRNTlXTVHKCrlIqt5T
lBhEMohlFerLlBjEMohMVTEARDKCITsAk2AEgAAAkAAAAAAAAAAAAAAAAAAAAAAAASAAAAAAAAD/
-2Q==`;
-
-// src/warmup.ts
-async function warmupBitmap(instance2) {
- const b64toBlob = (base64, type = "application/octet-stream") => fetch(`data:${type};base64,${base64}`).then((res2) => res2.blob());
- let blob;
- let res;
- switch (instance2.config.warmup) {
- case "face":
- blob = await b64toBlob(face3);
- break;
- case "body":
- case "full":
- blob = await b64toBlob(body3);
- break;
- default:
- blob = null;
- }
- if (blob) {
- const bitmap = await createImageBitmap(blob);
- res = await instance2.detect(bitmap, instance2.config);
- bitmap.close();
- }
- return res;
-}
-async function warmupCanvas(instance2) {
- return new Promise((resolve) => {
- let src;
- switch (instance2.config.warmup) {
- case "face":
- src = "data:image/jpeg;base64," + face3;
- break;
- case "full":
- case "body":
- src = "data:image/jpeg;base64," + body3;
- break;
- default:
- src = "";
- }
- let img;
- if (typeof Image !== "undefined")
- img = new Image();
- else if (env.Image)
- img = new env.Image();
- else
- return;
- img.onload = async () => {
- const canvas3 = canvas(img.naturalWidth, img.naturalHeight);
- if (!canvas3) {
- log("Warmup: Canvas not found");
- resolve(void 0);
- } else {
- const ctx = canvas3.getContext("2d");
- if (ctx)
- ctx.drawImage(img, 0, 0);
- const tensor3 = await instance2.image(canvas3);
- const res = tensor3.tensor ? await instance2.detect(tensor3.tensor, instance2.config) : void 0;
- resolve(res);
- }
- };
- if (src)
- img.src = src;
- else
- resolve(void 0);
- });
-}
-async function warmupNode(instance2) {
- const atob = (str) => Buffer.from(str, "base64");
- let img;
- if (instance2.config.warmup === "face")
- img = atob(face3);
- else
- img = atob(body3);
- let res;
- if ("node" in tfjs_esm_exports && tfjs_esm_exports.getBackend() === "tensorflow") {
- const data = tfjs_esm_exports["node"].decodeJpeg(img);
- const expanded = tfjs_esm_exports.expandDims(data, 0);
- instance2.tf.dispose(data);
- res = await instance2.detect(expanded, instance2.config);
- instance2.tf.dispose(expanded);
- } else {
- if (instance2.config.debug)
- log("Warmup tfjs-node not loaded");
- }
- return res;
-}
-async function runInference(instance2) {
- let res;
- if (typeof createImageBitmap === "function")
- res = await warmupBitmap(instance2);
- else if (typeof Image !== "undefined" || env.Canvas !== void 0)
- res = await warmupCanvas(instance2);
- else
- res = await warmupNode(instance2);
- return res;
-}
-async function runCompile(allModels) {
- var _a, _b, _c, _d;
- if (!tfjs_esm_exports.env().flagRegistry.ENGINE_COMPILE_ONLY)
- return;
- const backendType = tfjs_esm_exports.getBackend();
- const webGLBackend = tfjs_esm_exports.backend();
- if (backendType !== "webgl" && backendType !== "humangl" || !(webGLBackend == null ? void 0 : webGLBackend.checkCompileCompletion)) {
- return;
- }
- tfjs_esm_exports.env().set("ENGINE_COMPILE_ONLY", true);
- const numTensorsStart = tfjs_esm_exports.engine().state.numTensors;
- const compiledModels = [];
- for (const [modelName, model19] of Object.entries(allModels).filter(([key, val]) => key !== null && val !== null)) {
- const shape = ((_b = (_a = model19.inputs) == null ? void 0 : _a[0]) == null ? void 0 : _b.shape) ? [...model19.inputs[0].shape] : [1, 64, 64, 3];
- const dtype = ((_d = (_c = model19.inputs) == null ? void 0 : _c[0]) == null ? void 0 : _d.dtype) ? model19.inputs[0].dtype : "float32";
- for (let dim = 0; dim < shape.length; dim++) {
- if (shape[dim] === -1)
- shape[dim] = dim === 0 ? 1 : 64;
- }
- const tensor3 = tfjs_esm_exports.zeros(shape, dtype);
- try {
- const res = model19.execute(tensor3);
- compiledModels.push(modelName);
- if (Array.isArray(res))
- res.forEach((t) => tfjs_esm_exports.dispose(t));
- else
- tfjs_esm_exports.dispose(res);
- } catch (e) {
- log("compile fail model:", modelName);
- }
- tfjs_esm_exports.dispose(tensor3);
- }
- const kernels = await webGLBackend.checkCompileCompletionAsync();
- webGLBackend.getUniformLocations();
- log("compile pass models:", compiledModels);
- log("compile pass kernels:", kernels.length);
- tfjs_esm_exports.env().set("ENGINE_COMPILE_ONLY", false);
- const numTensorsEnd = tfjs_esm_exports.engine().state.numTensors;
- if (numTensorsEnd - numTensorsStart > 0)
- log("tensor leak:", numTensorsEnd - numTensorsStart);
-}
-async function warmup(instance2, userConfig) {
- const t0 = now();
- instance2.state = "warmup";
- if (userConfig)
- instance2.config = mergeDeep(instance2.config, userConfig);
- if (!instance2.config.warmup || instance2.config.warmup.length === 0 || instance2.config.warmup === "none") {
- return { face: [], body: [], hand: [], gesture: [], object: [], performance: instance2.performance, timestamp: now(), persons: [], error: null };
- }
- return new Promise(async (resolve) => {
- await runCompile(instance2.models);
- const res = await runInference(instance2);
- const t1 = now();
- if (instance2.config.debug)
- log("warmup", instance2.config.warmup, Math.round(t1 - t0), "ms");
- instance2.emit("warmup");
- resolve(res);
- });
-}
-
-// src/human.ts
-var _numTensors, _analyzeMemoryLeaks, _checkSanity, _sanity;
-var Human = class {
- constructor(userConfig) {
- __publicField(this, "version");
- __publicField(this, "config");
- __publicField(this, "result");
- __publicField(this, "state");
- __publicField(this, "process");
- __publicField(this, "tf");
- __publicField(this, "env");
- __publicField(this, "draw");
- __publicField(this, "models");
- __publicField(this, "events");
- __publicField(this, "faceTriangulation");
- __publicField(this, "faceUVMap");
- __publicField(this, "performance");
- __privateAdd(this, _numTensors, void 0);
- __privateAdd(this, _analyzeMemoryLeaks, void 0);
- __privateAdd(this, _checkSanity, void 0);
- __publicField(this, "gl");
- __publicField(this, "analyze", (...msg) => {
- if (!__privateGet(this, _analyzeMemoryLeaks))
- return;
- const currentTensors = this.tf.engine().state.numTensors;
- const previousTensors = __privateGet(this, _numTensors);
- __privateSet(this, _numTensors, currentTensors);
- const leaked = currentTensors - previousTensors;
- if (leaked !== 0)
- log(...msg, leaked);
- });
- __privateAdd(this, _sanity, (input) => {
- if (!__privateGet(this, _checkSanity))
- return null;
- if (!input)
- return "input is not defined";
- if (this.env.node && !(input instanceof Tensor))
- return "input must be a tensor";
- try {
- this.tf.getBackend();
- } catch (e) {
- return "backend not loaded";
- }
- return null;
- });
- __publicField(this, "similarity", similarity);
- __publicField(this, "distance", distance);
- __publicField(this, "match", match2);
- __publicField(this, "emit", (event) => {
- var _a;
- if ((_a = this.events) == null ? void 0 : _a.dispatchEvent)
- this.events.dispatchEvent(new Event(event));
- });
- this.env = env;
- const tfVersion = (version8.tfjs || tfjs_esm_exports.version_core).replace(/-(.*)/, "");
- config.wasmPath = `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${tfVersion}/dist/`;
- config.modelBasePath = env.browser ? "../models/" : "file://models/";
- config.backend = env.browser ? "humangl" : "tensorflow";
- this.version = version9;
- Object.defineProperty(this, "version", { value: version9 });
- this.config = JSON.parse(JSON.stringify(config));
- Object.seal(this.config);
- this.config.cacheModels = typeof indexedDB !== "undefined";
- if (userConfig)
- this.config = mergeDeep(this.config, userConfig);
- setModelLoadOptions(this.config);
- this.tf = tfjs_esm_exports;
- this.state = "idle";
- __privateSet(this, _numTensors, 0);
- __privateSet(this, _analyzeMemoryLeaks, false);
- __privateSet(this, _checkSanity, false);
- this.performance = {};
- this.events = typeof EventTarget !== "undefined" ? new EventTarget() : void 0;
- this.models = new Models();
- this.draw = {
- options: options3,
- canvas: (input, output) => canvas2(input, output),
- face: (output, result, options4) => face(output, result, options4),
- body: (output, result, options4) => body(output, result, options4),
- hand: (output, result, options4) => hand(output, result, options4),
- gesture: (output, result, options4) => gesture(output, result, options4),
- object: (output, result, options4) => object(output, result, options4),
- person: (output, result, options4) => person(output, result, options4),
- all: (output, result, options4) => all(output, result, options4)
- };
- this.result = { face: [], body: [], hand: [], gesture: [], object: [], performance: {}, timestamp: 0, persons: [], error: null };
- this.process = { tensor: null, canvas: null };
- this.faceTriangulation = triangulation;
- this.faceUVMap = uvmap;
- this.gl = config2;
- validateModel(this, null, "");
- this.emit("create");
- }
- reset() {
- const currentBackend = this.config.backend;
- this.config = JSON.parse(JSON.stringify(config));
- this.config.backend = currentBackend;
- }
- validate(userConfig) {
- return validate(config, userConfig || this.config);
- }
- check() {
- return validate2(this);
- }
- now() {
- return now();
- }
- image(input, getTensor = true) {
- return process2(input, this.config, getTensor);
- }
- async segmentation(input, background) {
- return process5(input, background, this.config);
- }
- enhance(input) {
- return enhance(input);
- }
- compare(firstImageTensor, secondImageTensor) {
- return compare(this.config, firstImageTensor, secondImageTensor);
- }
- async init() {
- await check(this, true);
- await this.tf.ready();
- }
- async load(userConfig) {
- this.state = "load";
- const timeStamp = now();
- const count2 = Object.values(this.models).filter((model19) => model19).length;
- if (userConfig)
- this.config = mergeDeep(this.config, userConfig);
- if (this.env.initial) {
- if (this.config.debug)
- log(`version: ${this.version}`);
- if (this.config.debug)
- log(`tfjs version: ${this.tf.version["tfjs-core"]}`);
- if (!await check(this))
- log("error: backend check failed");
- await tfjs_esm_exports.ready();
- if (this.env.browser) {
- if (this.config.debug)
- log("configuration:", this.config);
- if (this.config.debug)
- log("environment:", this.env);
- if (this.config.debug)
- log("tf flags:", this.tf.ENV.flags);
- }
- }
- await load20(this);
- if (this.env.initial && this.config.debug)
- log("tf engine state:", this.tf.engine().state.numBytes, "bytes", this.tf.engine().state.numTensors, "tensors");
- this.env.initial = false;
- const loaded = Object.values(this.models).filter((model19) => model19).length;
- if (loaded !== count2) {
- validate2(this);
- this.emit("load");
- }
- const current = Math.trunc(now() - timeStamp);
- if (current > (this.performance.loadModels || 0))
- this.performance.loadModels = this.env.perfadd ? (this.performance.loadModels || 0) + current : current;
- }
- next(result = this.result) {
- return calc2(result, this.config);
- }
- getModelStats() {
- return getModelStats(this);
- }
- async warmup(userConfig) {
- const t0 = now();
- const res = await warmup(this, userConfig);
- const t1 = now();
- this.performance.warmup = Math.trunc(t1 - t0);
- return res;
- }
- async profile(input, userConfig) {
- const profile = await this.tf.profile(() => this.detect(input, userConfig));
- const kernels = {};
- let total = 0;
- for (const kernel of profile.kernels) {
- if (kernels[kernel.name])
- kernels[kernel.name] += kernel.kernelTimeMs;
- else
- kernels[kernel.name] = kernel.kernelTimeMs;
- total += kernel.kernelTimeMs;
- }
- const kernelArr = [];
- Object.entries(kernels).forEach((key) => kernelArr.push({ kernel: key[0], time: key[1], perc: 0 }));
- for (const kernel of kernelArr) {
- kernel.perc = Math.round(1e3 * kernel.time / total) / 1e3;
- kernel.time = Math.round(1e3 * kernel.time) / 1e3;
- }
- kernelArr.sort((a, b) => b.time - a.time);
- kernelArr.length = 20;
- return kernelArr;
- }
- async detect(input, userConfig) {
- this.state = "detect";
- return new Promise(async (resolve) => {
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
- this.state = "config";
- let timeStamp;
- this.config = mergeDeep(this.config, userConfig);
- this.state = "check";
- const error = __privateGet(this, _sanity).call(this, input);
- if (error) {
- log(error, input);
- this.emit("error");
- resolve({ face: [], body: [], hand: [], gesture: [], object: [], performance: this.performance, timestamp: now(), persons: [], error });
- }
- const timeStart = now();
- await check(this);
- await this.load();
- timeStamp = now();
- this.state = "image";
- const img = await process2(input, this.config);
- this.process = img;
- this.performance.inputProcess = this.env.perfadd ? (this.performance.inputProcess || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- this.analyze("Get Image:");
- if (!img.tensor) {
- if (this.config.debug)
- log("could not convert input to tensor");
- this.emit("error");
- resolve({ face: [], body: [], hand: [], gesture: [], object: [], performance: this.performance, timestamp: now(), persons: [], error: "could not convert input to tensor" });
- return;
- }
- this.emit("image");
- timeStamp = now();
- this.config.skipAllowed = await skip(this.config, img.tensor);
- if (!this.performance.totalFrames)
- this.performance.totalFrames = 0;
- if (!this.performance.cachedFrames)
- this.performance.cachedFrames = 0;
- this.performance.totalFrames++;
- if (this.config.skipAllowed)
- this.performance.cachedFrames++;
- this.performance.cacheCheck = this.env.perfadd ? (this.performance.cacheCheck || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- this.analyze("Check Changed:");
- let faceRes = [];
- let bodyRes = [];
- let handRes = [];
- let objectRes = [];
- this.state = "detect:face";
- if (this.config.async) {
- faceRes = this.config.face.enabled ? detectFace(this, img.tensor) : [];
- if (this.performance.face)
- delete this.performance.face;
- } else {
- timeStamp = now();
- faceRes = this.config.face.enabled ? await detectFace(this, img.tensor) : [];
- this.performance.face = this.env.perfadd ? (this.performance.face || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- if (this.config.async && (this.config.body.maxDetected === -1 || this.config.hand.maxDetected === -1))
- faceRes = await faceRes;
- this.analyze("Start Body:");
- this.state = "detect:body";
- const bodyConfig = this.config.body.maxDetected === -1 ? mergeDeep(this.config, { body: { maxDetected: this.config.face.enabled ? 1 * faceRes.length : 1 } }) : this.config;
- if (this.config.async) {
- if ((_a = this.config.body.modelPath) == null ? void 0 : _a.includes("posenet"))
- bodyRes = this.config.body.enabled ? predict18(img.tensor, bodyConfig) : [];
- else if ((_b = this.config.body.modelPath) == null ? void 0 : _b.includes("blazepose"))
- bodyRes = this.config.body.enabled ? predict5(img.tensor, bodyConfig) : [];
- else if ((_c = this.config.body.modelPath) == null ? void 0 : _c.includes("efficientpose"))
- bodyRes = this.config.body.enabled ? predict7(img.tensor, bodyConfig) : [];
- else if ((_d = this.config.body.modelPath) == null ? void 0 : _d.includes("movenet"))
- bodyRes = this.config.body.enabled ? predict16(img.tensor, bodyConfig) : [];
- if (this.performance.body)
- delete this.performance.body;
- } else {
- timeStamp = now();
- if ((_e = this.config.body.modelPath) == null ? void 0 : _e.includes("posenet"))
- bodyRes = this.config.body.enabled ? await predict18(img.tensor, bodyConfig) : [];
- else if ((_f = this.config.body.modelPath) == null ? void 0 : _f.includes("blazepose"))
- bodyRes = this.config.body.enabled ? await predict5(img.tensor, bodyConfig) : [];
- else if ((_g = this.config.body.modelPath) == null ? void 0 : _g.includes("efficientpose"))
- bodyRes = this.config.body.enabled ? await predict7(img.tensor, bodyConfig) : [];
- else if ((_h = this.config.body.modelPath) == null ? void 0 : _h.includes("movenet"))
- bodyRes = this.config.body.enabled ? await predict16(img.tensor, bodyConfig) : [];
- this.performance.body = this.env.perfadd ? (this.performance.body || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- this.analyze("End Body:");
- this.analyze("Start Hand:");
- this.state = "detect:hand";
- const handConfig = this.config.hand.maxDetected === -1 ? mergeDeep(this.config, { hand: { maxDetected: this.config.face.enabled ? 2 * faceRes.length : 1 } }) : this.config;
- if (this.config.async) {
- if ((_j = (_i = this.config.hand.detector) == null ? void 0 : _i.modelPath) == null ? void 0 : _j.includes("handdetect"))
- handRes = this.config.hand.enabled ? predict13(img.tensor, handConfig) : [];
- else if ((_l = (_k = this.config.hand.detector) == null ? void 0 : _k.modelPath) == null ? void 0 : _l.includes("handtrack"))
- handRes = this.config.hand.enabled ? predict14(img.tensor, handConfig) : [];
- if (this.performance.hand)
- delete this.performance.hand;
- } else {
- timeStamp = now();
- if ((_n = (_m = this.config.hand.detector) == null ? void 0 : _m.modelPath) == null ? void 0 : _n.includes("handdetect"))
- handRes = this.config.hand.enabled ? await predict13(img.tensor, handConfig) : [];
- else if ((_p = (_o = this.config.hand.detector) == null ? void 0 : _o.modelPath) == null ? void 0 : _p.includes("handtrack"))
- handRes = this.config.hand.enabled ? await predict14(img.tensor, handConfig) : [];
- this.performance.hand = this.env.perfadd ? (this.performance.hand || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- this.analyze("End Hand:");
- this.analyze("Start Object:");
- this.state = "detect:object";
- if (this.config.async) {
- if ((_q = this.config.object.modelPath) == null ? void 0 : _q.includes("nanodet"))
- objectRes = this.config.object.enabled ? predict17(img.tensor, this.config) : [];
- else if ((_r = this.config.object.modelPath) == null ? void 0 : _r.includes("centernet"))
- objectRes = this.config.object.enabled ? predict6(img.tensor, this.config) : [];
- if (this.performance.object)
- delete this.performance.object;
- } else {
- timeStamp = now();
- if ((_s = this.config.object.modelPath) == null ? void 0 : _s.includes("nanodet"))
- objectRes = this.config.object.enabled ? await predict17(img.tensor, this.config) : [];
- else if ((_t = this.config.object.modelPath) == null ? void 0 : _t.includes("centernet"))
- objectRes = this.config.object.enabled ? await predict6(img.tensor, this.config) : [];
- this.performance.object = this.env.perfadd ? (this.performance.object || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- }
- this.analyze("End Object:");
- this.state = "detect:await";
- if (this.config.async)
- [faceRes, bodyRes, handRes, objectRes] = await Promise.all([faceRes, bodyRes, handRes, objectRes]);
- this.state = "detect:gesture";
- let gestureRes = [];
- if (this.config.gesture.enabled) {
- timeStamp = now();
- gestureRes = [...face2(faceRes), ...body2(bodyRes), ...hand2(handRes), ...iris2(faceRes)];
- if (!this.config.async)
- this.performance.gesture = this.env.perfadd ? (this.performance.gesture || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);
- else if (this.performance.gesture)
- delete this.performance.gesture;
- }
- this.performance.total = this.env.perfadd ? (this.performance.total || 0) + Math.trunc(now() - timeStart) : Math.trunc(now() - timeStart);
- const shape = ((_u = this.process.tensor) == null ? void 0 : _u.shape) || [];
- this.result = {
- face: faceRes,
- body: bodyRes,
- hand: handRes,
- gesture: gestureRes,
- object: objectRes,
- performance: this.performance,
- canvas: this.process.canvas,
- timestamp: Date.now(),
- error: null,
- get persons() {
- return join2(faceRes, bodyRes, handRes, gestureRes, shape);
- }
- };
- tfjs_esm_exports.dispose(img.tensor);
- this.emit("detect");
- this.state = "idle";
- resolve(this.result);
- });
- }
-};
-_numTensors = new WeakMap();
-_analyzeMemoryLeaks = new WeakMap();
-_checkSanity = new WeakMap();
-_sanity = new WeakMap();
-export {
- Human,
- Human as default,
- config as defaults,
- draw_exports as draw,
- env,
- match_exports as match,
- models_exports2 as models
-};
+2Q==`;async function VA(e){let t=(r,s="application/octet-stream")=>fetch(`data:${s};base64,${r}`).then(a=>a.blob()),o,n;switch(e.config.warmup){case"face":o=await t(U2);break;case"body":case"full":o=await t(Y2);break;default:o=null}if(o){let r=await createImageBitmap(o);n=await e.detect(r,e.config),r.close()}return n}async function ZA(e){return new Promise(t=>{let o;switch(e.config.warmup){case"face":o="data:image/jpeg;base64,"+U2;break;case"full":case"body":o="data:image/jpeg;base64,"+Y2;break;default:o=""}let n;if(typeof Image!="undefined")n=new Image;else if(T.Image)n=new T.Image;else return;n.onload=async()=>{let r=s0(n.naturalWidth,n.naturalHeight);if(!r)b("Warmup: Canvas not found"),t(void 0);else{let s=r.getContext("2d");s&&s.drawImage(n,0,0);let a=await e.image(r),i=a.tensor?await e.detect(a.tensor,e.config):void 0;t(i)}},o?n.src=o:t(void 0)})}async function XA(e){let t=r=>Buffer.from(r,"base64"),o;e.config.warmup==="face"?o=t(U2):o=t(Y2);let n;if("node"in A&&A.getBackend()==="tensorflow"){let r=A.node.decodeJpeg(o),s=A.expandDims(r,0);e.tf.dispose(r),n=await e.detect(s,e.config),e.tf.dispose(s)}else e.config.debug&&b("Warmup tfjs-node not loaded");return n}async function qA(e){let t;return typeof createImageBitmap=="function"?t=await VA(e):typeof Image!="undefined"||T.Canvas!==void 0?t=await ZA(e):t=await XA(e),t}async function UA(e){var i,c,x,d;if(!A.env().flagRegistry.ENGINE_COMPILE_ONLY)return;let t=A.getBackend(),o=A.backend();if(t!=="webgl"&&t!=="humangl"||!(o!=null&&o.checkCompileCompletion))return;A.env().set("ENGINE_COMPILE_ONLY",!0);let n=A.engine().state.numTensors,r=[];for(let[l,f]of Object.entries(e).filter(([y,p])=>y!==null&&p!==null)){let y=(c=(i=f.inputs)==null?void 0:i[0])!=null&&c.shape?[...f.inputs[0].shape]:[1,64,64,3],p=(d=(x=f.inputs)==null?void 0:x[0])!=null&&d.dtype?f.inputs[0].dtype:"float32";for(let M=0;MA.dispose(R)):A.dispose(M)}catch(M){b("compile fail model:",l)}A.dispose(g)}let s=await o.checkCompileCompletionAsync();o.getUniformLocations(),b("compile pass models:",r),b("compile pass kernels:",s.length),A.env().set("ENGINE_COMPILE_ONLY",!1);let a=A.engine().state.numTensors;a-n>0&&b("tensor leak:",a-n)}async function zo(e,t){let o=v();return e.state="warmup",t&&(e.config=U(e.config,t)),!e.config.warmup||e.config.warmup.length===0||e.config.warmup==="none"?{face:[],body:[],hand:[],gesture:[],object:[],performance:e.performance,timestamp:v(),persons:[],error:null}:new Promise(async n=>{await UA(e.models);let r=await qA(e),s=v();e.config.debug&&b("warmup",e.config.warmup,Math.round(s-o),"ms"),e.emit("warmup"),n(r)})}var Ge,A2,s2,K2,So=class{constructor(t){w(this,"version");w(this,"config");w(this,"result");w(this,"state");w(this,"process");w(this,"tf");w(this,"env");w(this,"draw");w(this,"models");w(this,"events");w(this,"faceTriangulation");w(this,"faceUVMap");w(this,"performance");Ze(this,Ge,void 0);Ze(this,A2,void 0);Ze(this,s2,void 0);w(this,"gl");w(this,"analyze",(...t)=>{if(!Ve(this,A2))return;let o=this.tf.engine().state.numTensors,n=Ve(this,Ge);Xe(this,Ge,o);let r=o-n;r!==0&&b(...t,r)});Ze(this,K2,t=>{if(!Ve(this,s2))return null;if(!t)return"input is not defined";if(this.env.node&&!(t instanceof be))return"input must be a tensor";try{this.tf.getBackend()}catch(o){return"backend not loaded"}return null});w(this,"similarity",z1);w(this,"distance",r2);w(this,"match",S1);w(this,"emit",t=>{var o;(o=this.events)!=null&&o.dispatchEvent&&this.events.dispatchEvent(new Event(t))});this.env=T;let o=(qe.tfjs||A.version_core).replace(/-(.*)/,"");Ae.wasmPath=`https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${o}/dist/`,Ae.modelBasePath=T.browser?"../models/":"file://models/",Ae.backend=T.browser?"humangl":"tensorflow",this.version=b1,Object.defineProperty(this,"version",{value:b1}),this.config=JSON.parse(JSON.stringify(Ae)),Object.seal(this.config),this.config.cacheModels=typeof indexedDB!="undefined",t&&(this.config=U(this.config,t)),po(this.config),this.tf=A,this.state="idle",Xe(this,Ge,0),Xe(this,A2,!1),Xe(this,s2,!1),this.performance={},this.events=typeof EventTarget!="undefined"?new EventTarget:void 0,this.models=new n2,this.draw={options:A0,canvas:(n,r)=>P1(n,r),face:(n,r,s)=>je(n,r,s),body:(n,r,s)=>Oe(n,r,s),hand:(n,r,s)=>Ne(n,r,s),gesture:(n,r,s)=>We(n,r,s),object:(n,r,s)=>Le(n,r,s),person:(n,r,s)=>v1(n,r,s),all:(n,r,s)=>T1(n,r,s)},this.result={face:[],body:[],hand:[],gesture:[],object:[],performance:{},timestamp:0,persons:[],error:null},this.process={tensor:null,canvas:null},this.faceTriangulation=x3,this.faceUVMap=y3,this.gl=D,Ie(this,null,""),this.emit("create")}reset(){let t=this.config.backend;this.config=JSON.parse(JSON.stringify(Ae)),this.config.backend=t}validate(t){return _2(Ae,t||this.config)}check(){return q2(this)}now(){return v()}image(t,o=!0){return Me(t,this.config,o)}async segmentation(t,o){return fo(t,o,this.config)}enhance(t){return H5(t)}compare(t,o){return Y1(this.config,t,o)}async init(){await O2(this,!0),await this.tf.ready()}async load(t){this.state="load";let o=v(),n=Object.values(this.models).filter(a=>a).length;t&&(this.config=U(this.config,t)),this.env.initial&&(this.config.debug&&b(`version: ${this.version}`),this.config.debug&&b(`tfjs version: ${this.tf.version["tfjs-core"]}`),await O2(this)||b("error: backend check failed"),await A.ready(),this.env.browser&&(this.config.debug&&b("configuration:",this.config),this.config.debug&&b("environment:",this.env),this.config.debug&&b("tf flags:",this.tf.ENV.flags))),await h1(this),this.env.initial&&this.config.debug&&b("tf engine state:",this.tf.engine().state.numBytes,"bytes",this.tf.engine().state.numTensors,"tensors"),this.env.initial=!1,Object.values(this.models).filter(a=>a).length!==n&&(q2(this),this.emit("load"));let s=Math.trunc(v()-o);s>(this.performance.loadModels||0)&&(this.performance.loadModels=this.env.perfadd?(this.performance.loadModels||0)+s:s)}next(t=this.result){return To(t,this.config)}getModelStats(){return u1(this)}async warmup(t){let o=v(),n=await zo(this,t),r=v();return this.performance.warmup=Math.trunc(r-o),n}async profile(t,o){let n=await this.tf.profile(()=>this.detect(t,o)),r={},s=0;for(let i of n.kernels)r[i.name]?r[i.name]+=i.kernelTimeMs:r[i.name]=i.kernelTimeMs,s+=i.kernelTimeMs;let a=[];Object.entries(r).forEach(i=>a.push({kernel:i[0],time:i[1],perc:0}));for(let i of a)i.perc=Math.round(1e3*i.time/s)/1e3,i.time=Math.round(1e3*i.time)/1e3;return a.sort((i,c)=>c.time-i.time),a.length=20,a}async detect(t,o){return this.state="detect",new Promise(async n=>{var M,R,P,m,u,z,k,h,W,B,j,N,G,O,I,n0,x0,E0,z0,R0,X0;this.state="config";let r;this.config=U(this.config,o),this.state="check";let s=Ve(this,K2).call(this,t);s&&(b(s,t),this.emit("error"),n({face:[],body:[],hand:[],gesture:[],object:[],performance:this.performance,timestamp:v(),persons:[],error:s}));let a=v();await O2(this),await this.load(),r=v(),this.state="image";let i=await Me(t,this.config);if(this.process=i,this.performance.inputProcess=this.env.perfadd?(this.performance.inputProcess||0)+Math.trunc(v()-r):Math.trunc(v()-r),this.analyze("Get Image:"),!i.tensor){this.config.debug&&b("could not convert input to tensor"),this.emit("error"),n({face:[],body:[],hand:[],gesture:[],object:[],performance:this.performance,timestamp:v(),persons:[],error:"could not convert input to tensor"});return}this.emit("image"),r=v(),this.config.skipAllowed=await U1(this.config,i.tensor),this.performance.totalFrames||(this.performance.totalFrames=0),this.performance.cachedFrames||(this.performance.cachedFrames=0),this.performance.totalFrames++,this.config.skipAllowed&&this.performance.cachedFrames++,this.performance.cacheCheck=this.env.perfadd?(this.performance.cacheCheck||0)+Math.trunc(v()-r):Math.trunc(v()-r),this.analyze("Check Changed:");let c=[],x=[],d=[],l=[];this.state="detect:face",this.config.async?(c=this.config.face.enabled?k1(this,i.tensor):[],this.performance.face&&delete this.performance.face):(r=v(),c=this.config.face.enabled?await k1(this,i.tensor):[],this.performance.face=this.env.perfadd?(this.performance.face||0)+Math.trunc(v()-r):Math.trunc(v()-r)),this.config.async&&(this.config.body.maxDetected===-1||this.config.hand.maxDetected===-1)&&(c=await c),this.analyze("Start Body:"),this.state="detect:body";let f=this.config.body.maxDetected===-1?U(this.config,{body:{maxDetected:this.config.face.enabled?1*c.length:1}}):this.config;this.config.async?((M=this.config.body.modelPath)!=null&&M.includes("posenet")?x=this.config.body.enabled?f1(i.tensor,f):[]:(R=this.config.body.modelPath)!=null&&R.includes("blazepose")?x=this.config.body.enabled?v5(i.tensor,f):[]:(P=this.config.body.modelPath)!=null&&P.includes("efficientpose")?x=this.config.body.enabled?S5(i.tensor,f):[]:(m=this.config.body.modelPath)!=null&&m.includes("movenet")&&(x=this.config.body.enabled?a1(i.tensor,f):[]),this.performance.body&&delete this.performance.body):(r=v(),(u=this.config.body.modelPath)!=null&&u.includes("posenet")?x=this.config.body.enabled?await f1(i.tensor,f):[]:(z=this.config.body.modelPath)!=null&&z.includes("blazepose")?x=this.config.body.enabled?await v5(i.tensor,f):[]:(k=this.config.body.modelPath)!=null&&k.includes("efficientpose")?x=this.config.body.enabled?await S5(i.tensor,f):[]:(h=this.config.body.modelPath)!=null&&h.includes("movenet")&&(x=this.config.body.enabled?await a1(i.tensor,f):[]),this.performance.body=this.env.perfadd?(this.performance.body||0)+Math.trunc(v()-r):Math.trunc(v()-r)),this.analyze("End Body:"),this.analyze("Start Hand:"),this.state="detect:hand";let y=this.config.hand.maxDetected===-1?U(this.config,{hand:{maxDetected:this.config.face.enabled?2*c.length:1}}):this.config;this.config.async?((B=(W=this.config.hand.detector)==null?void 0:W.modelPath)!=null&&B.includes("handdetect")?d=this.config.hand.enabled?Y5(i.tensor,y):[]:(N=(j=this.config.hand.detector)==null?void 0:j.modelPath)!=null&&N.includes("handtrack")&&(d=this.config.hand.enabled?$5(i.tensor,y):[]),this.performance.hand&&delete this.performance.hand):(r=v(),(O=(G=this.config.hand.detector)==null?void 0:G.modelPath)!=null&&O.includes("handdetect")?d=this.config.hand.enabled?await Y5(i.tensor,y):[]:(n0=(I=this.config.hand.detector)==null?void 0:I.modelPath)!=null&&n0.includes("handtrack")&&(d=this.config.hand.enabled?await $5(i.tensor,y):[]),this.performance.hand=this.env.perfadd?(this.performance.hand||0)+Math.trunc(v()-r):Math.trunc(v()-r)),this.analyze("End Hand:"),this.analyze("Start Object:"),this.state="detect:object",this.config.async?((x0=this.config.object.modelPath)!=null&&x0.includes("nanodet")?l=this.config.object.enabled?l1(i.tensor,this.config):[]:(E0=this.config.object.modelPath)!=null&&E0.includes("centernet")&&(l=this.config.object.enabled?w5(i.tensor,this.config):[]),this.performance.object&&delete this.performance.object):(r=v(),(z0=this.config.object.modelPath)!=null&&z0.includes("nanodet")?l=this.config.object.enabled?await l1(i.tensor,this.config):[]:(R0=this.config.object.modelPath)!=null&&R0.includes("centernet")&&(l=this.config.object.enabled?await w5(i.tensor,this.config):[]),this.performance.object=this.env.perfadd?(this.performance.object||0)+Math.trunc(v()-r):Math.trunc(v()-r)),this.analyze("End Object:"),this.state="detect:await",this.config.async&&([c,x,d,l]=await Promise.all([c,x,d,l])),this.state="detect:gesture";let p=[];this.config.gesture.enabled&&(r=v(),p=[...Ro(c),...Mo(x),...Po(d),...vo(c)],this.config.async?this.performance.gesture&&delete this.performance.gesture:this.performance.gesture=this.env.perfadd?(this.performance.gesture||0)+Math.trunc(v()-r):Math.trunc(v()-r)),this.performance.total=this.env.perfadd?(this.performance.total||0)+Math.trunc(v()-a):Math.trunc(v()-a);let g=((X0=this.process.tensor)==null?void 0:X0.shape)||[];this.result={face:c,body:x,hand:d,gesture:p,object:l,performance:this.performance,canvas:this.process.canvas,timestamp:Date.now(),error:null,get persons(){return Eo(c,x,d,p,g)}},A.dispose(i.tensor),this.emit("detect"),this.state="idle",n(this.result)})}};Ge=new WeakMap,A2=new WeakMap,s2=new WeakMap,K2=new WeakMap;export{So as Human,So as default,Ae as defaults,ho as draw,T as env,ko as match,J5 as models};
//# sourceMappingURL=human.esm-nobundle.js.map
diff --git a/dist/human.esm-nobundle.js.map b/dist/human.esm-nobundle.js.map
index a76a43b8..cfad2409 100644
--- a/dist/human.esm-nobundle.js.map
+++ b/dist/human.esm-nobundle.js.map
@@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../src/util/util.ts", "../src/config.ts", "tfjs.esm.js", "../src/image/imagefxshaders.ts", "../src/image/imagefx.ts", "../src/image/enhance.ts", "../src/image/image.ts", "../src/util/env.ts", "../src/models.ts", "../src/gear/gear.ts", "../src/tfjs/constants.ts", "../src/gear/ssrnet-age.ts", "../src/gear/ssrnet-gender.ts", "../src/face/antispoof.ts", "../src/face/facemeshcoords.ts", "../src/face/facemeshutil.ts", "../src/face/blazeface.ts", "../src/body/blazeposecoords.ts", "../src/body/blazeposedetector.ts", "../src/util/box.ts", "../src/body/blazepose.ts", "../src/object/labels.ts", "../src/object/centernet.ts", "../src/body/efficientposecoords.ts", "../src/body/efficientpose.ts", "../src/gear/emotion.ts", "../src/face/mobilefacenet.ts", "../src/face/insightface.ts", "../src/face/iris.ts", "../src/face/constants.ts", "../src/face/attention.ts", "../src/face/facemesh.ts", "../src/face/faceres.ts", "../src/hand/handposeutil.ts", "../src/hand/handposeanchors.ts", "../src/hand/handposedetector.ts", "../src/hand/handposepipeline.ts", "../src/hand/fingerdef.ts", "../src/hand/fingergesture.ts", "../src/hand/fingerpose.ts", "../src/hand/handpose.ts", "../src/tfjs/humangl.ts", "../src/tfjs/backend.ts", "../src/hand/handtrack.ts", "../src/face/liveness.ts", "../src/body/movenetcoords.ts", "../src/body/movenetfix.ts", "../src/body/movenet.ts", "../src/object/nanodet.ts", "../src/body/posenetutils.ts", "../src/body/posenet.ts", "../src/segmentation/segmentation.ts", "../src/tfjs/load.ts", "../src/draw/draw.ts", "../src/draw/primitives.ts", "../src/draw/options.ts", "../src/draw/face.ts", "../src/draw/body.ts", "../src/draw/hand.ts", "../src/draw/object.ts", "../src/draw/gesture.ts", "../src/face/mask.ts", "../src/face/angles.ts", "../src/face/face.ts", "../src/gesture/gesture.ts", "../src/util/interpolate.ts", "../src/face/match.ts", "../src/util/persons.ts", "../src/sample.ts", "../src/warmup.ts", "../src/human.ts"],
- "sourcesContent": ["import type { Config } from '../exports';\n\n/**\n * Simple helper functions used accross codebase\n */\n\n// helper function: wrapper around console output\nexport function log(...msg): void {\n const dt = new Date();\n const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;\n if (msg) console.log(ts, 'Human:', ...msg); // eslint-disable-line no-console\n}\n\n// helper function: join two paths\nexport function join(folder: string, file: string): string {\n const separator = folder.endsWith('/') ? '' : '/';\n const skipJoin = file.startsWith('.') || file.startsWith('/') || file.startsWith('http:') || file.startsWith('https:') || file.startsWith('file:');\n const path = skipJoin ? `${file}` : `${folder}${separator}${file}`;\n if (!path.toLocaleLowerCase().includes('.json')) throw new Error(`modelpath error: expecting json file: ${path}`);\n return path;\n}\n\n// helper function: gets elapsed time on both browser and nodejs\nexport const now = () => {\n if (typeof performance !== 'undefined') return performance.now();\n return parseInt((Number(process.hrtime.bigint()) / 1000 / 1000).toString());\n};\n\n// helper function: checks current config validity\nexport function validate(defaults: Partial, config: Partial, parent = 'config', msgs: { reason: string, where: string, expected?: string }[] = []) {\n for (const key of Object.keys(config)) {\n if (typeof config[key] === 'object') {\n validate(defaults[key], config[key], key, msgs);\n } else {\n const defined = defaults && (typeof defaults[key] !== 'undefined');\n if (!defined) msgs.push({ reason: 'unknown property', where: `${parent}.${key} = ${config[key]}` });\n const same = defaults && typeof defaults[key] === typeof config[key];\n if (defined && !same) msgs.push({ reason: 'property type mismatch', where: `${parent}.${key} = ${config[key]}`, expected: typeof defaults[key] });\n }\n // ok = ok && defined && same;\n }\n if (config.debug && parent === 'config' && msgs.length > 0) log('invalid configuration', msgs);\n return msgs;\n}\n\n// helper function: perform deep merge of multiple objects so it allows full inheritance with overrides\nexport function mergeDeep(...objects) {\n const isObject = (obj) => obj && typeof obj === 'object';\n return objects.reduce((prev, obj) => {\n Object.keys(obj || {}).forEach((key) => {\n const pVal = prev[key];\n const oVal = obj[key];\n if (Array.isArray(pVal) && Array.isArray(oVal)) prev[key] = pVal.concat(...oVal);\n else if (isObject(pVal) && isObject(oVal)) prev[key] = mergeDeep(pVal, oVal);\n else prev[key] = oVal;\n });\n return prev;\n }, {});\n}\n\n// helper function: return min and max from input array\nexport const minmax = (data: number[]) => data.reduce((acc: number[], val) => {\n acc[0] = (acc[0] === undefined || val < acc[0]) ? val : acc[0];\n acc[1] = (acc[1] === undefined || val > acc[1]) ? val : acc[1];\n return acc;\n}, []);\n\n// helper function: async wait\nexport async function wait(time: number) {\n const waiting = new Promise((resolve) => { setTimeout(() => resolve(true), time); });\n await waiting;\n}\n", "/* eslint-disable no-multi-spaces */\n\n/** Generic config type inherited by all module types */\nexport interface GenericConfig {\n /** is module enabled? */\n enabled: boolean,\n /** path to model json file (relative to `modelBasePath` */\n modelPath: string,\n /** how many max frames to go without re-running model if cached results are acceptable\n * for two-phase models such as face and hand caching applies to bounding boxes detection only */\n skipFrames: number,\n /** how many max milliseconds to go without re-running model if cached results are acceptable\n * for two-phase models such as face and hand caching applies to bounding boxes detection only */\n skipTime: number,\n}\n\n/** Detector part of face configuration */\nexport interface FaceDetectorConfig extends GenericConfig {\n /** is face rotation correction performed after detecting face?\n * used to correctly analyze faces under high angles\n */\n rotation: boolean,\n /** maximum number of detected faces */\n maxDetected: number,\n /** minimum confidence for a detected face before results are discarded */\n minConfidence: number,\n /** minimum overlap between two detected faces before one is discarded */\n iouThreshold: number,\n /** should child models perform on masked image of a face */\n mask: boolean,\n /** should face detection return processed and cropped face tensor that can with an external model for addtional processing?\n * if enabled it must be manually deallocated to avoid memory leak */\n return: boolean,\n}\n\n/** Mesh part of face configuration */\nexport interface FaceMeshConfig extends GenericConfig {\n /** Keep detected faces that cannot be verified using facemesh */\n keepInvalid: boolean\n}\n\n/** Iris part of face configuration */\nexport interface FaceIrisConfig extends GenericConfig {}\n\n/** Attention part of face configuration */\nexport interface FaceAttentionConfig extends GenericConfig {}\n\n/** Description or face embedding part of face configuration\n * - also used by age and gender detection\n */\nexport interface FaceDescriptionConfig extends GenericConfig {\n /** minimum confidence for a detected face before results are discarded */\n minConfidence: number,\n}\n\n/** Emotion part of face configuration */\nexport interface FaceEmotionConfig extends GenericConfig {\n /** minimum confidence for a detected face before results are discarded */\n minConfidence: number,\n}\n\n/** Anti-spoofing part of face configuration */\nexport interface FaceAntiSpoofConfig extends GenericConfig {}\n\n/** Liveness part of face configuration */\nexport interface FaceLivenessConfig extends GenericConfig {}\n\n/** Gear part of face configuration */\nexport interface FaceGearConfig extends GenericConfig {\n /** minimum confidence for a detected race before results are discarded */\n minConfidence: number,\n}\n\n/** Configures all face-specific options: face detection, mesh analysis, age, gender, emotion detection and face description */\nexport interface FaceConfig extends GenericConfig {\n detector: Partial,\n mesh: Partial,\n attention: Partial,\n iris: Partial,\n description: Partial,\n emotion: Partial,\n antispoof: Partial,\n liveness: Partial,\n gear: Partial,\n}\n\n/** Configures all body detection specific options */\nexport interface BodyConfig extends GenericConfig {\n /** maximum number of detected bodies */\n maxDetected: number,\n /** minimum confidence for a detected body before results are discarded */\n minConfidence: number,\n /* experimental\n /** experimental: detector used for body model before actual analysis\n detector?: {\n /** experimental: enable body detector before body landmarks\n enabled: boolean,\n /** experimental: path to optional body detector model json file\n modelPath: string,\n /** experimental: minimum confidence for a detected body before results are discarded\n minConfidence: number,\n /** experimental: minimum overlap between two detected bodies before one is discarded\n iouThreshold: number\n },\n */\n}\n\n/** Configures all hand detection specific options */\nexport interface HandConfig extends GenericConfig {\n /** should hand rotation correction be performed after hand detection? */\n rotation: boolean,\n /** minimum confidence for a detected hand before results are discarded */\n minConfidence: number,\n /** minimum overlap between two detected hands before one is discarded */\n iouThreshold: number,\n /** maximum number of detected hands */\n maxDetected: number,\n /** should hand landmarks be detected or just return detected hand box */\n landmarks: boolean,\n detector: {\n /** path to hand detector model json */\n modelPath?: string,\n },\n skeleton: {\n /** path to hand skeleton model json */\n modelPath?: string,\n },\n}\n\n/** Configures all object detection specific options */\nexport interface ObjectConfig extends GenericConfig {\n /** minimum confidence for a detected objects before results are discarded */\n minConfidence: number,\n /** minimum overlap between two detected objects before one is discarded */\n iouThreshold: number,\n /** maximum number of detected objects */\n maxDetected: number,\n}\n\n/** Configures all body segmentation module\n * removes background from input containing person\n * if segmentation is enabled it will run as preprocessing task before any other model\n * alternatively leave it disabled and use it on-demand using human.segmentation method which can\n * remove background or replace it with user-provided background\n*/\nexport interface SegmentationConfig extends GenericConfig {\n /** blur segmentation output by pixels for more realistic image */\n blur: number,\n}\n\n/** Run input through image filters before inference\n * - available only in Browser environments\n * - image filters run with near-zero latency as they are executed on the GPU using WebGL\n*/\nexport interface FilterConfig {\n /** are image filters enabled? */\n enabled: boolean,\n /** perform image histogram equalization\n * - equalization is performed on input as a whole and detected face before its passed for further analysis\n */\n equalization: boolean,\n /** resize input width\n * - if both width and height are set to 0, there is no resizing\n * - if just one is set, second one is scaled automatically\n * - if both are set, values are used as-is\n */\n width: number,\n /** resize input height\n * - if both width and height are set to 0, there is no resizing\n * - if just one is set, second one is scaled automatically\n * - if both are set, values are used as-is\n */\n height: number,\n /** return processed canvas imagedata in result */\n return: boolean,\n /** flip input as mirror image */\n flip: boolean,\n /** range: -1 (darken) to 1 (lighten) */\n brightness: number,\n /** range: -1 (reduce contrast) to 1 (increase contrast) */\n contrast: number,\n /** range: 0 (no sharpening) to 1 (maximum sharpening) */\n sharpness: number,\n /** range: 0 (no blur) to N (blur radius in pixels) */\n blur: number\n /** range: -1 (reduce saturation) to 1 (increase saturation) */\n saturation: number,\n /** range: 0 (no change) to 360 (hue rotation in degrees) */\n hue: number,\n /** image negative */\n negative: boolean,\n /** image sepia colors */\n sepia: boolean,\n /** image vintage colors */\n vintage: boolean,\n /** image kodachrome colors */\n kodachrome: boolean,\n /** image technicolor colors */\n technicolor: boolean,\n /** image polaroid camera effect */\n polaroid: boolean,\n /** range: 0 (no pixelate) to N (number of pixels to pixelate) */\n pixelate: number,\n}\n\n/** Controlls gesture detection */\nexport interface GestureConfig {\n /** is gesture detection enabled? */\n enabled: boolean,\n}\n/** Possible TensorFlow backends */\nexport type BackendType = ['cpu', 'wasm', 'webgl', 'humangl', 'tensorflow', 'webgpu'];\n\n/** Possible values for `human.warmup` */\nexport type WarmupType = ['' | 'none' | 'face' | 'full' | 'body'];\n\n/**\n * Configuration interface definition for **Human** library\n * Contains all configurable parameters\n * Defaults: [config](https://github.com/vladmandic/human/blob/main/src/config.ts#L262)\n */\nexport interface Config {\n /** Backend used for TFJS operations\n * valid build-in backends are:\n * - Browser: `cpu`, `wasm`, `webgl`, `humangl`, `webgpu`\n * - NodeJS: `cpu`, `wasm`, `tensorflow`\n * default: `humangl` for browser and `tensorflow` for nodejs\n */\n backend: '' | 'cpu' | 'wasm' | 'webgl' | 'humangl' | 'tensorflow' | 'webgpu',\n\n /** Path to *.wasm files if backend is set to `wasm`\n *\n * default: auto-detects to link to CDN `jsdelivr` when running in browser\n */\n wasmPath: string,\n\n /** Force WASM loader to use platform fetch\n *\n * default: auto-detects to link to CDN `jsdelivr` when running in browser\n */\n wasmPlatformFetch: boolean,\n\n /** Print debug statements to console\n *\n * default: `true`\n */\n debug: boolean,\n\n /** Perform model loading and inference concurrently or sequentially\n *\n * default: `true`\n */\n async: boolean,\n\n /** What to use for `human.warmup()`\n * - warmup pre-initializes all models for faster inference but can take significant time on startup\n * - used by `webgl`, `humangl` and `webgpu` backends\n *\n * default: `full`\n */\n warmup: '' | 'none' | 'face' | 'full' | 'body',\n\n /** Base model path (typically starting with file://, http:// or https://) for all models\n * - individual modelPath values are relative to this path\n *\n * default: `../models/` for browsers and `file://models/` for nodejs\n */\n modelBasePath: string,\n\n /** Cache models in IndexDB on first sucessfull load\n * default: true if indexdb is available (browsers), false if its not (nodejs)\n */\n cacheModels: boolean,\n\n /** Validate kernel ops used in model during model load\n * default: true\n * any errors will be printed on console but will be treated as non-fatal\n */\n validateModels: boolean,\n\n /** Cache sensitivity\n * - values 0..1 where 0.01 means reset cache if input changed more than 1%\n * - set to 0 to disable caching\n *\n * default: 0.7\n */\n cacheSensitivity: number;\n\n /** Software Kernels\n * Registers software kernel ops running on CPU when accelerated version of kernel is not found in the current backend\n */\n softwareKernels: boolean,\n\n /** Perform immediate garbage collection on deallocated tensors instead of caching them */\n deallocate: boolean;\n\n /** Internal Variable */\n skipAllowed: boolean;\n\n /** Filter config {@link FilterConfig} */\n filter: Partial,\n\n /** Gesture config {@link GestureConfig} */\n gesture: Partial;\n\n /** Face config {@link FaceConfig} */\n face: Partial,\n\n /** Body config {@link BodyConfig} */\n body: Partial,\n\n /** Hand config {@link HandConfig} */\n hand: Partial,\n\n /** Object config {@link ObjectConfig} */\n object: Partial,\n\n /** Segmentation config {@link SegmentationConfig} */\n segmentation: Partial,\n}\n\n/** - [See all default Config values...](https://github.com/vladmandic/human/blob/main/src/config.ts#L262) */\nconst config: Config = {\n backend: '',\n modelBasePath: '',\n cacheModels: true,\n validateModels: true,\n wasmPath: '',\n wasmPlatformFetch: false,\n debug: false,\n async: true,\n warmup: 'full',\n cacheSensitivity: 0.70,\n skipAllowed: false,\n deallocate: false,\n softwareKernels: false,\n filter: {\n enabled: true,\n equalization: false,\n width: 0,\n height: 0,\n flip: false,\n return: true,\n brightness: 0,\n contrast: 0,\n sharpness: 0,\n blur: 0,\n saturation: 0,\n hue: 0,\n negative: false,\n sepia: false,\n vintage: false,\n kodachrome: false,\n technicolor: false,\n polaroid: false,\n pixelate: 0,\n },\n gesture: {\n enabled: true,\n },\n face: {\n enabled: true,\n detector: {\n modelPath: 'blazeface.json',\n rotation: true,\n maxDetected: 1,\n skipFrames: 99,\n skipTime: 2500,\n minConfidence: 0.2,\n iouThreshold: 0.1,\n mask: false,\n return: false,\n },\n mesh: {\n enabled: true,\n modelPath: 'facemesh.json',\n keepInvalid: false,\n },\n attention: {\n enabled: false,\n modelPath: 'facemesh-attention.json',\n },\n iris: {\n enabled: true,\n modelPath: 'iris.json',\n },\n emotion: {\n enabled: true,\n minConfidence: 0.1,\n skipFrames: 99,\n skipTime: 1500,\n modelPath: 'emotion.json',\n },\n description: {\n enabled: true,\n modelPath: 'faceres.json',\n skipFrames: 99,\n skipTime: 3000,\n minConfidence: 0.1,\n },\n antispoof: {\n enabled: false,\n skipFrames: 99,\n skipTime: 4000,\n modelPath: 'antispoof.json',\n },\n liveness: {\n enabled: false,\n skipFrames: 99,\n skipTime: 4000,\n modelPath: 'liveness.json',\n },\n },\n body: {\n enabled: true,\n modelPath: 'movenet-lightning.json',\n maxDetected: -1,\n minConfidence: 0.3,\n skipFrames: 1,\n skipTime: 200,\n },\n hand: {\n enabled: true,\n rotation: true,\n skipFrames: 99,\n skipTime: 1000,\n minConfidence: 0.50,\n iouThreshold: 0.2,\n maxDetected: -1,\n landmarks: true,\n detector: {\n modelPath: 'handtrack.json',\n },\n skeleton: {\n modelPath: 'handlandmark-full.json',\n },\n },\n object: {\n enabled: false,\n modelPath: 'mb3-centernet.json',\n minConfidence: 0.2,\n iouThreshold: 0.4,\n maxDetected: 10,\n skipFrames: 99,\n skipTime: 2000,\n },\n segmentation: {\n enabled: false,\n modelPath: 'selfie.json',\n blur: 8,\n },\n};\n\nexport { config as defaults };\n", "/*\n Human\n homepage: \n author: '\n*/\n\n\n// tfjs/tf-browser.ts\nexport * from \"@tensorflow/tfjs/dist/index.js\";\nexport * from \"@tensorflow/tfjs-backend-webgl/dist/index.js\";\n\n// dist/tfjs.version.js\nvar version = \"3.20.0\";\nvar version2 = \"3.20.0\";\nvar version3 = \"3.20.0\";\nvar version4 = \"3.20.0\";\nvar version5 = \"3.20.0\";\nvar version6 = \"3.20.0\";\nvar version7 = \"3.20.0\";\nvar version8 = {\n tfjs: version,\n \"tfjs-core\": version2,\n \"tfjs-data\": version3,\n \"tfjs-layers\": version4,\n \"tfjs-converter\": version5,\n \"tfjs-backend-webgl\": version6,\n \"tfjs-backend-wasm\": version7\n};\n\n// tfjs/tf-browser.ts\nimport { Tensor } from \"@tensorflow/tfjs/dist/index.js\";\nimport { GraphModel } from \"@tensorflow/tfjs-converter/dist/index\";\nexport {\n GraphModel,\n Tensor,\n version8 as version\n};\n", "export const vertexIdentity = `\n precision highp float;\n attribute vec2 pos;\n attribute vec2 uv;\n varying vec2 vUv;\n uniform float flipY;\n void main(void) {\n vUv = uv;\n gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);\n }\n`;\n\nexport const fragmentIdentity = `\n precision highp float;\n varying vec2 vUv;\n uniform sampler2D texture;\n void main(void) {\n gl_FragColor = texture2D(texture, vUv);\n }\n`;\n\nexport const colorMatrixWithAlpha = `\n precision highp float;\n varying vec2 vUv;\n uniform sampler2D texture;\n uniform float m[20];\n void main(void) {\n vec4 c = texture2D(texture, vUv);\n gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];\n gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];\n gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];\n gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];\n }\n`;\n\nexport const colorMatrixWithoutAlpha = `\n precision highp float;\n varying vec2 vUv;\n uniform sampler2D texture;\n uniform float m[20];\n void main(void) {\n vec4 c = texture2D(texture, vUv);\n gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];\n gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];\n gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];\n gl_FragColor.a = c.a;\n }\n`;\n\nexport const pixelate = `\n precision highp float;\n varying vec2 vUv;\n uniform vec2 size;\n uniform sampler2D texture;\n vec2 pixelate(vec2 coord, vec2 size) {\n return floor( coord / size ) * size;\n }\n void main(void) {\n gl_FragColor = vec4(0.0);\n vec2 coord = pixelate(vUv, size);\n gl_FragColor += texture2D(texture, coord);\n }\n`;\n\nexport const blur = `\n precision highp float;\n varying vec2 vUv;\n uniform sampler2D texture;\n uniform vec2 px;\n void main(void) {\n gl_FragColor = vec4(0.0);\n gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;\n gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;\n gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;\n gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;\n gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;\n gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;\n gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;\n gl_FragColor += texture2D(texture, vUv )*0.159576912161;\n gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;\n gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;\n gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;\n gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;\n gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;\n gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;\n gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;\n }\n`;\n\nexport const convolution = `\n precision highp float;\n varying vec2 vUv;\n uniform sampler2D texture;\n uniform vec2 px;\n uniform float m[9];\n void main(void) {\n vec4 c11 = texture2D(texture, vUv - px); // top left\n vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y)); // top center\n vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y)); // top right\n vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) ); // mid left\n vec4 c22 = texture2D(texture, vUv); // mid center\n vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) ); // mid right\n vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) ); // bottom left\n vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) ); // bottom center\n vec4 c33 = texture2D(texture, vUv + px ); // bottom right\n gl_FragColor = \n c11 * m[0] + c12 * m[1] + c22 * m[2] +\n c21 * m[3] + c22 * m[4] + c23 * m[5] +\n c31 * m[6] + c32 * m[7] + c33 * m[8];\n gl_FragColor.a = c22.a;\n }\n`;\n", "/**\n * Image Filters in WebGL algoritm implementation\n * Based on: [WebGLImageFilter](https://github.com/phoboslab/WebGLImageFilter)\n */\n\n/* eslint-disable func-names */\n\nimport * as shaders from './imagefxshaders';\nimport { canvas } from './image';\nimport { log } from '../util/util';\n\nconst collect = (source, prefix: string, collection) => {\n const r = new RegExp('\\\\b' + prefix + ' \\\\w+ (\\\\w+)', 'ig');\n source.replace(r, (match, name) => {\n collection[name] = 0;\n return match;\n });\n};\n\nclass GLProgram {\n uniform = {};\n attribute = {};\n gl: WebGLRenderingContext;\n id: WebGLProgram;\n\n constructor(gl, vertexSource, fragmentSource) {\n this.gl = gl;\n const vertexShader = this.compile(vertexSource, this.gl.VERTEX_SHADER);\n const fragmentShader = this.compile(fragmentSource, this.gl.FRAGMENT_SHADER);\n this.id = this.gl.createProgram() as WebGLProgram;\n if (!vertexShader || !fragmentShader) return;\n if (!this.id) {\n log('filter: could not create webgl program');\n return;\n }\n this.gl.attachShader(this.id, vertexShader);\n this.gl.attachShader(this.id, fragmentShader);\n this.gl.linkProgram(this.id);\n if (!this.gl.getProgramParameter(this.id, this.gl.LINK_STATUS)) {\n log(`filter: gl link failed: ${this.gl.getProgramInfoLog(this.id) || 'unknown'}`);\n return;\n }\n this.gl.useProgram(this.id);\n collect(vertexSource, 'attribute', this.attribute); // Collect attributes\n for (const a in this.attribute) this.attribute[a] = this.gl.getAttribLocation(this.id, a);\n collect(vertexSource, 'uniform', this.uniform); // Collect uniforms\n collect(fragmentSource, 'uniform', this.uniform);\n for (const u in this.uniform) this.uniform[u] = this.gl.getUniformLocation(this.id, u);\n }\n\n compile = (source, type): WebGLShader | null => {\n const shader = this.gl.createShader(type);\n if (!shader) {\n log('filter: could not create shader');\n return null;\n }\n this.gl.shaderSource(shader, source);\n this.gl.compileShader(shader);\n if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n log(`filter: gl compile failed: ${this.gl.getShaderInfoLog(shader) || 'unknown'}`);\n return null;\n }\n return shader;\n };\n}\n\n// function that is instantiated as class so it has private this members\n/**\n * @class GLImageFilter\n * @property {function} reset reset current filter chain\n * @property {function} add add specified filter to filter chain\n * @property {function} apply execute filter chain and draw result\n * @property {function} draw just draw input to result\n */\n\nexport function GLImageFilter() {\n let drawCount = 0;\n let sourceTexture: WebGLTexture | null = null;\n let lastInChain = false;\n let currentFramebufferIndex = -1;\n let tempFramebuffers: [null, null] | [{ fbo: WebGLFramebuffer | null, texture: WebGLTexture | null }] = [null, null];\n let filterChain: Record[] = [];\n let vertexBuffer: WebGLBuffer | null = null;\n let currentProgram: GLProgram | null = null;\n const fxcanvas = canvas(100, 100);\n const shaderProgramCache = { }; // key is the shader program source, value is the compiled program\n const DRAW = { INTERMEDIATE: 1 };\n const gl = fxcanvas.getContext('webgl') as WebGLRenderingContext;\n if (!gl) {\n log('filter: cannot get webgl context');\n return;\n }\n // @ts-ignore used for sanity checks outside of imagefx\n this.gl = gl;\n\n function resize(width, height) {\n if (width === fxcanvas.width && height === fxcanvas.height) return; // Same width/height? Nothing to do here\n fxcanvas.width = width;\n fxcanvas.height = height;\n if (!vertexBuffer) { // Create the context if we don't have it yet\n const vertices = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0]); // Create the vertex buffer for the two triangles [x, y, u, v] * 6\n vertexBuffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n }\n gl.viewport(0, 0, fxcanvas.width, fxcanvas.height);\n tempFramebuffers = [null, null]; // Delete old temp framebuffers\n }\n\n function createFramebufferTexture(width, height) {\n const fbo = gl.createFramebuffer();\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n const renderbuffer = gl.createRenderbuffer();\n gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);\n const texture = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n gl.bindTexture(gl.TEXTURE_2D, null);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n return { fbo, texture };\n }\n\n function getTempFramebuffer(index): { fbo: WebGLFramebuffer | null, texture: WebGLTexture | null } {\n tempFramebuffers[index] = tempFramebuffers[index] || createFramebufferTexture(fxcanvas.width, fxcanvas.height);\n return tempFramebuffers[index] as { fbo: WebGLFramebuffer, texture: WebGLTexture };\n }\n\n function draw(flags = 0) {\n if (!currentProgram) return;\n let source: WebGLTexture | null = null;\n let target: WebGLFramebuffer | null = null;\n let flipY = false;\n if (drawCount === 0) source = sourceTexture; // First draw call - use the source texture\n else source = getTempFramebuffer(currentFramebufferIndex).texture || null; // All following draw calls use the temp buffer last drawn to\n drawCount++;\n if (lastInChain && !(flags & DRAW.INTERMEDIATE)) { // Last filter in our chain - draw directly to the WebGL Canvas. We may also have to flip the image vertically now\n target = null;\n flipY = drawCount % 2 === 0;\n } else {\n currentFramebufferIndex = (currentFramebufferIndex + 1) % 2;\n target = getTempFramebuffer(currentFramebufferIndex).fbo || null; // Intermediate draw call - get a temp buffer to draw to\n }\n gl.bindTexture(gl.TEXTURE_2D, source); // Bind the source and target and draw the two triangles\n gl.bindFramebuffer(gl.FRAMEBUFFER, target);\n gl.uniform1f(currentProgram.uniform['flipY'], (flipY ? -1 : 1));\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n }\n\n function compileShader(fragmentSource): GLProgram | null {\n if (shaderProgramCache[fragmentSource]) {\n currentProgram = shaderProgramCache[fragmentSource];\n gl.useProgram((currentProgram ? currentProgram.id : null) || null);\n return currentProgram;\n }\n currentProgram = new GLProgram(gl, shaders.vertexIdentity, fragmentSource);\n if (!currentProgram) {\n log('filter: could not get webgl program');\n return null;\n }\n const floatSize = Float32Array.BYTES_PER_ELEMENT;\n const vertSize = 4 * floatSize;\n gl.enableVertexAttribArray(currentProgram.attribute['pos']);\n gl.vertexAttribPointer(currentProgram.attribute['pos'], 2, gl.FLOAT, false, vertSize, 0 * floatSize);\n gl.enableVertexAttribArray(currentProgram.attribute['uv']);\n gl.vertexAttribPointer(currentProgram.attribute['uv'], 2, gl.FLOAT, false, vertSize, 2 * floatSize);\n shaderProgramCache[fragmentSource] = currentProgram;\n return currentProgram;\n }\n\n const filter = {\n colorMatrix: (matrix: number[]) => { // general color matrix filter\n const m = new Float32Array(matrix);\n m[4] /= 255;\n m[9] /= 255;\n m[14] /= 255;\n m[19] /= 255;\n const shader = (m[18] === 1 && m[3] === 0 && m[8] === 0 && m[13] === 0 && m[15] === 0 && m[16] === 0 && m[17] === 0 && m[19] === 0) // Can we ignore the alpha value? Makes things a bit faster.\n ? shaders.colorMatrixWithoutAlpha\n : shaders.colorMatrixWithAlpha;\n const program = compileShader(shader);\n if (!program) return;\n gl.uniform1fv(program.uniform['m'], m);\n draw();\n },\n\n brightness: (brightness: number) => {\n const b = (brightness || 0) + 1;\n filter.colorMatrix([\n b, 0, 0, 0, 0,\n 0, b, 0, 0, 0,\n 0, 0, b, 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n saturation: (amount: number) => {\n const x = (amount || 0) * 2 / 3 + 1;\n const y = ((x - 1) * -0.5);\n filter.colorMatrix([\n x, y, y, 0, 0,\n y, x, y, 0, 0,\n y, y, x, 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n desaturate: () => {\n filter.saturation(-1);\n },\n\n contrast: (amount: number) => {\n const v = (amount || 0) + 1;\n const o = -128 * (v - 1);\n filter.colorMatrix([\n v, 0, 0, 0, o,\n 0, v, 0, 0, o,\n 0, 0, v, 0, o,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n negative: () => {\n filter.contrast(-2);\n },\n\n hue: (rotation: number) => {\n rotation = (rotation || 0) / 180 * Math.PI;\n const cos = Math.cos(rotation);\n const sin = Math.sin(rotation);\n const lumR = 0.213;\n const lumG = 0.715;\n const lumB = 0.072;\n filter.colorMatrix([\n lumR + cos * (1 - lumR) + sin * (-lumR), lumG + cos * (-lumG) + sin * (-lumG), lumB + cos * (-lumB) + sin * (1 - lumB), 0, 0,\n lumR + cos * (-lumR) + sin * (0.143), lumG + cos * (1 - lumG) + sin * (0.140), lumB + cos * (-lumB) + sin * (-0.283), 0, 0,\n lumR + cos * (-lumR) + sin * (-(1 - lumR)), lumG + cos * (-lumG) + sin * (lumG), lumB + cos * (1 - lumB) + sin * (lumB), 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n desaturateLuminance: () => {\n filter.colorMatrix([\n 0.2764723, 0.9297080, 0.0938197, 0, -37.1,\n 0.2764723, 0.9297080, 0.0938197, 0, -37.1,\n 0.2764723, 0.9297080, 0.0938197, 0, -37.1,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n sepia: () => {\n filter.colorMatrix([\n 0.393, 0.7689999, 0.18899999, 0, 0,\n 0.349, 0.6859999, 0.16799999, 0, 0,\n 0.272, 0.5339999, 0.13099999, 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n brownie: () => {\n filter.colorMatrix([\n 0.5997023498159715, 0.34553243048391263, -0.2708298674538042, 0, 47.43192855600873,\n -0.037703249837783157, 0.8609577587992641, 0.15059552388459913, 0, -36.96841498319127,\n 0.24113635128153335, -0.07441037908422492, 0.44972182064877153, 0, -7.562075277591283,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n vintagePinhole: () => {\n filter.colorMatrix([\n 0.6279345635605994, 0.3202183420819367, -0.03965408211312453, 0, 9.651285835294123,\n 0.02578397704808868, 0.6441188644374771, 0.03259127616149294, 0, 7.462829176470591,\n 0.0466055556782719, -0.0851232987247891, 0.5241648018700465, 0, 5.159190588235296,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n kodachrome: () => {\n filter.colorMatrix([\n 1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,\n -0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,\n -0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n technicolor: () => {\n filter.colorMatrix([\n 1.9125277891456083, -0.8545344976951645, -0.09155508482755585, 0, 11.793603434377337,\n -0.3087833385928097, 1.7658908555458428, -0.10601743074722245, 0, -70.35205161461398,\n -0.231103377548616, -0.7501899197440212, 1.847597816108189, 0, 30.950940869491138,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n polaroid: () => {\n filter.colorMatrix([\n 1.438, -0.062, -0.062, 0, 0,\n -0.122, 1.378, -0.122, 0, 0,\n -0.016, -0.016, 1.483, 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n shiftToBGR: () => {\n filter.colorMatrix([\n 0, 0, 1, 0, 0,\n 0, 1, 0, 0, 0,\n 1, 0, 0, 0, 0,\n 0, 0, 0, 1, 0,\n ]);\n },\n\n convolution: (matrix: number[]) => { // general convolution Filter\n const m = new Float32Array(matrix);\n const pixelSizeX = 1 / fxcanvas.width;\n const pixelSizeY = 1 / fxcanvas.height;\n const program = compileShader(shaders.convolution);\n if (!program) return;\n gl.uniform1fv(program.uniform['m'], m);\n gl.uniform2f(program.uniform['px'], pixelSizeX, pixelSizeY);\n draw();\n },\n\n detectEdges: () => {\n // @ts-ignore this\n filter.convolution.call(this, [\n 0, 1, 0,\n 1, -4, 1,\n 0, 1, 0,\n ]);\n },\n\n sobelX: () => {\n // @ts-ignore this\n filter.convolution.call(this, [\n -1, 0, 1,\n -2, 0, 2,\n -1, 0, 1,\n ]);\n },\n\n sobelY: () => {\n // @ts-ignore this\n filter.convolution.call(this, [\n -1, -2, -1,\n 0, 0, 0,\n 1, 2, 1,\n ]);\n },\n\n sharpen: (amount) => {\n const a = amount || 1;\n // @ts-ignore this\n filter.convolution.call(this, [\n 0, -1 * a, 0,\n -1 * a, 1 + 4 * a, -1 * a,\n 0, -1 * a, 0,\n ]);\n },\n\n emboss: (size: number) => {\n const s = size || 1;\n // @ts-ignore this\n filter.convolution.call(this, [\n -2 * s, -1 * s, 0,\n -1 * s, 1, 1 * s,\n 0, 1 * s, 2 * s,\n ]);\n },\n\n blur: (size: number) => {\n const blurSizeX = (size / 7) / fxcanvas.width;\n const blurSizeY = (size / 7) / fxcanvas.height;\n const program = compileShader(shaders.blur);\n if (!program) return;\n // Vertical\n gl.uniform2f(program.uniform['px'], 0, blurSizeY);\n draw(DRAW.INTERMEDIATE);\n // Horizontal\n gl.uniform2f(program.uniform['px'], blurSizeX, 0);\n draw();\n },\n\n pixelate: (size: number) => {\n const blurSizeX = (size) / fxcanvas.width;\n const blurSizeY = (size) / fxcanvas.height;\n const program = compileShader(shaders.pixelate);\n if (!program) return;\n gl.uniform2f(program.uniform['size'], blurSizeX, blurSizeY);\n draw();\n },\n };\n\n // @ts-ignore this\n this.add = function (name) {\n const args = Array.prototype.slice.call(arguments, 1); // eslint-disable-line prefer-rest-params\n const func = filter[name];\n filterChain.push({ func, args });\n };\n\n // @ts-ignore this\n this.reset = function () {\n filterChain = [];\n };\n\n // @ts-ignore this\n this.get = function () {\n return filterChain;\n };\n\n // @ts-ignore this\n this.apply = function (image) {\n resize(image.width, image.height);\n drawCount = 0;\n if (!sourceTexture) sourceTexture = gl.createTexture(); // Create the texture for the input image if we haven't yet\n gl.bindTexture(gl.TEXTURE_2D, sourceTexture);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);\n for (let i = 0; i < filterChain.length; i++) {\n lastInChain = (i === filterChain.length - 1);\n const f = filterChain[i];\n // @ts-ignore function assigment\n f.func.apply(this, f.args || []);\n }\n return fxcanvas;\n };\n\n // @ts-ignore this\n this.draw = function (image) {\n this.add('brightness', 0);\n return this.apply(image);\n };\n}\n", "/**\n * Image enhancements\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport type { Tensor } from '../exports';\n\nexport async function histogramEqualization(inputImage: Tensor): Promise {\n // const maxValue = 254; // using 255 results in values slightly larger than 1 due to math rounding errors\n const squeeze = inputImage.shape.length === 4 ? tf.squeeze(inputImage) : inputImage;\n const channels = tf.split(squeeze, 3, 2);\n const min: Tensor[] = [tf.min(channels[0]), tf.min(channels[1]), tf.min(channels[2])];\n const max: Tensor[] = [tf.max(channels[0]), tf.max(channels[1]), tf.max(channels[2])];\n const absMax = await Promise.all(max.map((channel) => channel.data()));\n const maxValue = 0.99 * Math.max(absMax[0][0], absMax[1][0], absMax[2][0]);\n const sub = [tf.sub(channels[0], min[0]), tf.sub(channels[1], min[1]), tf.sub(channels[2], min[2])];\n const range = [tf.sub(max[0], min[0]), tf.sub(max[1], min[1]), tf.sub(max[2], min[2])];\n const fact = [tf.div(maxValue, range[0]), tf.div(maxValue, range[1]), tf.div(maxValue, range[2])];\n const enh = [tf.mul(sub[0], fact[0]), tf.mul(sub[1], fact[1]), tf.mul(sub[2], fact[2])];\n const rgb = tf.stack([enh[0], enh[1], enh[2]], 2);\n const reshape = tf.reshape(rgb, [1, squeeze.shape[0], squeeze.shape[1], 3]);\n tf.dispose([...channels, ...min, ...max, ...sub, ...range, ...fact, ...enh, rgb, squeeze]);\n return reshape as Tensor; // output shape is [1, height, width, 3]\n}\n", "/**\n * Image Processing algorithm implementation\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as fxImage from './imagefx';\nimport type { Input, AnyCanvas, Tensor, Config } from '../exports';\nimport { env } from '../util/env';\nimport { log } from '../util/util';\nimport * as enhance from './enhance';\n\nconst maxSize = 3840;\n// internal temp canvases\nlet inCanvas: AnyCanvas | null = null; // use global variable to avoid recreating canvas on each frame\nlet outCanvas: AnyCanvas | null = null; // use global variable to avoid recreating canvas on each frame\nlet tmpCanvas: AnyCanvas | null = null; // use global variable to avoid recreating canvas on each frame\n// @ts-ignore // imagefx is js module that should be converted to a class\nlet fx: fxImage.GLImageFilter | null; // instance of imagefx\n\nconst last: { inputSum: number, cacheDiff: number, sumMethod: number, inputTensor: undefined | Tensor } = {\n inputSum: 0,\n cacheDiff: 1,\n sumMethod: 0,\n inputTensor: undefined,\n};\n\nexport function canvas(width: number, height: number): AnyCanvas {\n let c: AnyCanvas;\n if (env.browser) { // browser defines canvas object\n if (env.worker) { // if runing in web worker use OffscreenCanvas\n if (typeof OffscreenCanvas === 'undefined') throw new Error('canvas error: attempted to run in web worker but OffscreenCanvas is not supported');\n c = new OffscreenCanvas(width, height);\n } else { // otherwise use DOM canvas\n if (typeof document === 'undefined') throw new Error('canvas error: attempted to run in browser but DOM is not defined');\n c = document.createElement('canvas');\n c.width = width;\n c.height = height;\n }\n } else { // if not running in browser, there is no \"default\" canvas object, so we need monkey patch or fail\n // @ts-ignore // env.canvas is an external monkey-patch\n if (typeof env.Canvas !== 'undefined') c = new env.Canvas(width, height);\n else if (typeof globalThis.Canvas !== 'undefined') c = new globalThis.Canvas(width, height);\n // else throw new Error('canvas error: attempted to use canvas in nodejs without canvas support installed');\n }\n // @ts-ignore its either defined or we already threw an error\n return c;\n}\n\n// helper function to copy canvas from input to output\nexport function copy(input: AnyCanvas, output?: AnyCanvas) {\n const outputCanvas = output || canvas(input.width, input.height);\n const ctx = outputCanvas.getContext('2d') as CanvasRenderingContext2D;\n ctx.drawImage(input, 0, 0);\n return outputCanvas;\n}\n\n// process input image and return tensor\n// input can be tensor, imagedata, htmlimageelement, htmlvideoelement\n// input is resized and run through imagefx filter\nexport async function process(input: Input, config: Config, getTensor: boolean = true): Promise<{ tensor: Tensor | null, canvas: AnyCanvas | null }> {\n if (!input) {\n // throw new Error('input is missing');\n if (config.debug) log('input error: input is missing');\n return { tensor: null, canvas: null }; // video may become temporarily unavailable due to onresize\n }\n // sanity checks since different browsers do not implement all dom elements\n if (\n !(input instanceof tf.Tensor)\n && !(typeof Image !== 'undefined' && input instanceof Image)\n && !(typeof env.Canvas !== 'undefined' && input instanceof env.Canvas)\n && !(typeof globalThis.Canvas !== 'undefined' && input instanceof globalThis.Canvas)\n && !(typeof ImageData !== 'undefined' && input instanceof ImageData)\n && !(typeof ImageBitmap !== 'undefined' && input instanceof ImageBitmap)\n && !(typeof HTMLImageElement !== 'undefined' && input instanceof HTMLImageElement)\n && !(typeof HTMLMediaElement !== 'undefined' && input instanceof HTMLMediaElement)\n && !(typeof HTMLVideoElement !== 'undefined' && input instanceof HTMLVideoElement)\n && !(typeof HTMLCanvasElement !== 'undefined' && input instanceof HTMLCanvasElement)\n && !(typeof OffscreenCanvas !== 'undefined' && input instanceof OffscreenCanvas)\n ) {\n throw new Error('input error: type is not recognized');\n }\n if (input instanceof tf.Tensor) { // if input is tensor use as-is without filters but correct shape as needed\n let tensor: Tensor | null = null;\n if ((input as Tensor)['isDisposedInternal']) throw new Error('input error: attempted to use tensor but it is disposed');\n if (!(input as Tensor).shape) throw new Error('input error: attempted to use tensor without a shape');\n if ((input as Tensor).shape.length === 3) { // [height, width, 3 || 4]\n if ((input as Tensor).shape[2] === 3) { // [height, width, 3] so add batch\n tensor = tf.expandDims(input, 0);\n } else if ((input as Tensor).shape[2] === 4) { // [height, width, 4] so strip alpha and add batch\n const rgb = tf.slice3d(input, [0, 0, 0], [-1, -1, 3]);\n tensor = tf.expandDims(rgb, 0);\n tf.dispose(rgb);\n }\n } else if ((input as Tensor).shape.length === 4) { // [1, width, height, 3 || 4]\n if ((input as Tensor).shape[3] === 3) { // [1, width, height, 3] just clone\n tensor = tf.clone(input);\n } else if ((input as Tensor).shape[3] === 4) { // [1, width, height, 4] so strip alpha\n tensor = tf.slice4d(input, [0, 0, 0, 0], [-1, -1, -1, 3]);\n }\n }\n // at the end shape must be [1, height, width, 3]\n if (tensor == null || tensor.shape.length !== 4 || tensor.shape[0] !== 1 || tensor.shape[3] !== 3) throw new Error(`input error: attempted to use tensor with unrecognized shape: ${((input as Tensor).shape).toString()}`);\n if ((tensor).dtype === 'int32') {\n const cast = tf.cast(tensor, 'float32');\n tf.dispose(tensor);\n tensor = cast;\n }\n return { tensor, canvas: (config.filter.return ? outCanvas : null) };\n }\n // check if resizing will be needed\n if (typeof input['readyState'] !== 'undefined' && (input as HTMLMediaElement).readyState <= 2) {\n if (config.debug) log('input stream is not ready');\n return { tensor: null, canvas: inCanvas }; // video may become temporarily unavailable due to onresize\n }\n const originalWidth: number = input['naturalWidth'] || input['videoWidth'] || input['width'] || (input['shape'] && (input['shape'][1] > 0));\n const originalHeight: number = input['naturalHeight'] || input['videoHeight'] || input['height'] || (input['shape'] && (input['shape'][2] > 0));\n if (!originalWidth || !originalHeight) {\n if (config.debug) log('cannot determine input dimensions');\n return { tensor: null, canvas: inCanvas }; // video may become temporarily unavailable due to onresize\n }\n let targetWidth: number = originalWidth;\n let targetHeight: number = originalHeight;\n if (targetWidth > maxSize) {\n targetWidth = maxSize;\n targetHeight = Math.trunc(targetWidth * originalHeight / originalWidth);\n }\n if (targetHeight > maxSize) {\n targetHeight = maxSize;\n targetWidth = Math.trunc(targetHeight * originalWidth / originalHeight);\n }\n\n // create our canvas and resize it if needed\n if ((config.filter?.width || 0) > 0) targetWidth = config.filter.width as number;\n else if ((config.filter?.height || 0) > 0) targetWidth = originalWidth * ((config.filter.height || 0) / originalHeight);\n if ((config.filter.height || 0) > 0) targetHeight = config.filter.height as number;\n else if ((config.filter.width || 0) > 0) targetHeight = originalHeight * ((config.filter.width || 0) / originalWidth);\n if (!targetWidth || !targetHeight) throw new Error('input error: cannot determine dimension');\n if (!inCanvas || (inCanvas.width !== targetWidth) || (inCanvas.height !== targetHeight)) inCanvas = canvas(targetWidth, targetHeight);\n\n // draw input to our canvas\n const inCtx = inCanvas.getContext('2d') as CanvasRenderingContext2D;\n if ((typeof ImageData !== 'undefined') && (input instanceof ImageData)) {\n inCtx.putImageData(input, 0, 0);\n } else {\n if (config.filter.flip && typeof inCtx.translate !== 'undefined') {\n inCtx.translate(originalWidth, 0);\n inCtx.scale(-1, 1);\n inCtx.drawImage(input as AnyCanvas, 0, 0, originalWidth, originalHeight, 0, 0, inCanvas.width, inCanvas.height);\n inCtx.setTransform(1, 0, 0, 1, 0, 0); // resets transforms to defaults\n } else {\n inCtx.drawImage(input as AnyCanvas, 0, 0, originalWidth, originalHeight, 0, 0, inCanvas.width, inCanvas.height);\n }\n }\n\n if (!outCanvas || (inCanvas.width !== outCanvas.width) || (inCanvas.height !== outCanvas.height)) outCanvas = canvas(inCanvas.width, inCanvas.height); // init output canvas\n\n // imagefx transforms using gl from input canvas to output canvas\n if (config.filter.enabled && env.webgl.supported) {\n if (!fx) fx = env.browser ? new fxImage.GLImageFilter() : null; // && (typeof document !== 'undefined')\n env.filter = !!fx;\n if (!fx?.add) {\n if (config.debug) log('input process error: cannot initialize filters');\n env.webgl.supported = false;\n config.filter.enabled = false;\n copy(inCanvas, outCanvas); // filter failed to initialize\n // return { tensor: null, canvas: inCanvas };\n } else {\n fx.reset();\n if (config.filter.brightness !== 0) fx.add('brightness', config.filter.brightness);\n if (config.filter.contrast !== 0) fx.add('contrast', config.filter.contrast);\n if (config.filter.sharpness !== 0) fx.add('sharpen', config.filter.sharpness);\n if (config.filter.blur !== 0) fx.add('blur', config.filter.blur);\n if (config.filter.saturation !== 0) fx.add('saturation', config.filter.saturation);\n if (config.filter.hue !== 0) fx.add('hue', config.filter.hue);\n if (config.filter.negative) fx.add('negative');\n if (config.filter.sepia) fx.add('sepia');\n if (config.filter.vintage) fx.add('brownie');\n if (config.filter.sepia) fx.add('sepia');\n if (config.filter.kodachrome) fx.add('kodachrome');\n if (config.filter.technicolor) fx.add('technicolor');\n if (config.filter.polaroid) fx.add('polaroid');\n if (config.filter.pixelate !== 0) fx.add('pixelate', config.filter.pixelate);\n if (fx.get() > 0) outCanvas = fx.apply(inCanvas);\n else outCanvas = fx.draw(inCanvas);\n }\n } else {\n copy(inCanvas, outCanvas); // if no filters applied, output canvas is input canvas\n if (fx) fx = null;\n env.filter = !!fx;\n }\n\n if (!getTensor) return { tensor: null, canvas: outCanvas }; // just canvas was requested\n if (!outCanvas) throw new Error('canvas error: cannot create output');\n\n // create tensor from image unless input was a tensor already\n let pixels;\n let depth = 3;\n if ((typeof ImageData !== 'undefined' && input instanceof ImageData) || ((input as ImageData).data && (input as ImageData).width && (input as ImageData).height)) { // if input is imagedata, just use it\n if (env.browser && tf.browser) {\n pixels = tf.browser ? tf.browser.fromPixels(input) : null;\n } else {\n depth = (input as ImageData).data.length / (input as ImageData).height / (input as ImageData).width;\n // const arr = Uint8Array.from(input['data']);\n const arr = new Uint8Array((input as ImageData).data.buffer);\n pixels = tf.tensor(arr, [(input as ImageData).height, (input as ImageData).width, depth], 'int32');\n }\n } else {\n if (!tmpCanvas || (outCanvas.width !== tmpCanvas.width) || (outCanvas.height !== tmpCanvas.height)) tmpCanvas = canvas(outCanvas.width, outCanvas.height); // init output canvas\n if (tf.browser && env.browser) {\n if (config.backend === 'webgl' || config.backend === 'humangl' || config.backend === 'webgpu') {\n pixels = tf.browser.fromPixels(outCanvas); // safe to reuse since both backend and context are gl based\n } else {\n tmpCanvas = copy(outCanvas); // cannot use output canvas as it already has gl context so we do a silly one more canvas\n pixels = tf.browser.fromPixels(tmpCanvas);\n }\n } else {\n const tempCanvas = copy(outCanvas); // cannot use output canvas as it already has gl context so we do a silly one more canvas\n const tempCtx = tempCanvas.getContext('2d') as CanvasRenderingContext2D;\n const tempData = tempCtx.getImageData(0, 0, targetWidth, targetHeight);\n depth = tempData.data.length / targetWidth / targetHeight;\n const arr = new Uint8Array(tempData.data.buffer);\n pixels = tf.tensor(arr, [targetWidth, targetHeight, depth]);\n }\n }\n if (depth === 4) { // rgba to rgb\n const rgb = tf.slice3d(pixels, [0, 0, 0], [-1, -1, 3]); // strip alpha channel\n tf.dispose(pixels);\n pixels = rgb;\n }\n if (!pixels) throw new Error('input error: cannot create tensor');\n const casted: Tensor = tf.cast(pixels, 'float32');\n const tensor: Tensor = config.filter.equalization ? await enhance.histogramEqualization(casted) : tf.expandDims(casted, 0);\n tf.dispose([pixels, casted]);\n return { tensor, canvas: (config.filter.return ? outCanvas : null) };\n}\n\n/*\nconst checksum = async (input: Tensor): Promise => { // use tf sum or js based sum loop depending on which is faster\n const resizeFact = 48;\n const reduced: Tensor = tf.image.resizeBilinear(input, [Math.trunc((input.shape[1] || 1) / resizeFact), Math.trunc((input.shape[2] || 1) / resizeFact)]);\n const tfSum = async (): Promise => {\n const sumT = tf.sum(reduced);\n const sum0 = await sumT.data();\n tf.dispose(sumT);\n return sum0[0];\n };\n const jsSum = async (): Promise => {\n const reducedData = await reduced.data(); // raw image rgb array\n let sum0 = 0;\n for (let i = 0; i < reducedData.length / 3; i++) sum0 += reducedData[3 * i + 2]; // look only at green value of each pixel\n return sum0;\n };\n if (last.sumMethod === 0) {\n const t0 = now();\n await jsSum();\n const t1 = now();\n await tfSum();\n const t2 = now();\n last.sumMethod = t1 - t0 < t2 - t1 ? 1 : 2;\n }\n const res = last.sumMethod === 1 ? await jsSum() : await tfSum();\n tf.dispose(reduced);\n return res;\n};\n*/\n\nexport async function skip(config: Partial, input: Tensor) {\n let skipFrame = false;\n if (config.cacheSensitivity === 0 || !input.shape || input.shape.length !== 4 || input.shape[1] > 2048 || input.shape[2] > 2048) return skipFrame; // cache disabled or input is invalid or too large for cache analysis\n\n /*\n const checkSum = await checksum(input);\n const diff = 100 * (Math.max(checkSum, last.inputSum) / Math.min(checkSum, last.inputSum) - 1);\n last.inputSum = checkSum;\n // if previous frame was skipped, skip this frame if changed more than cacheSensitivity\n // if previous frame was not skipped, then look for cacheSensitivity or difference larger than one in previous frame to avoid resetting cache in subsequent frames unnecessarily\n let skipFrame = diff < Math.max(config.cacheSensitivity, last.cacheDiff);\n // if difference is above 10x threshold, don't use last value to force reset cache for significant change of scenes or images\n last.cacheDiff = diff > 10 * config.cacheSensitivity ? 0 : diff;\n skipFrame = skipFrame && (last.cacheDiff > 0); // if no cached diff value then force no skip\n */\n\n if (!last.inputTensor) {\n last.inputTensor = tf.clone(input);\n } else if (last.inputTensor.shape[1] !== input.shape[1] || last.inputTensor.shape[2] !== input.shape[2]) { // input resolution changed\n tf.dispose(last.inputTensor);\n last.inputTensor = tf.clone(input);\n } else {\n const t: Record = {};\n t.diff = tf.sub(input, last.inputTensor);\n t.squared = tf.mul(t.diff, t.diff);\n t.sum = tf.sum(t.squared);\n const diffSum = await t.sum.data();\n const diffRelative = diffSum[0] / (input.shape[1] || 1) / (input.shape[2] || 1) / 255 / 3; // squared difference relative to input resolution and averaged per channel\n tf.dispose([last.inputTensor, t.diff, t.squared, t.sum]);\n last.inputTensor = tf.clone(input);\n skipFrame = diffRelative <= (config.cacheSensitivity || 0);\n }\n return skipFrame;\n}\n\nexport async function compare(config: Partial, input1: Tensor, input2: Tensor): Promise {\n const t: Record = {};\n if (!input1 || !input2 || input1.shape.length !== 4 || input1.shape.length !== input2.shape.length) {\n if (!config.debug) log('invalid input tensor or tensor shapes do not match:', input1.shape, input2.shape);\n return 0;\n }\n if (input1.shape[0] !== 1 || input2.shape[0] !== 1 || input1.shape[3] !== 3 || input2.shape[3] !== 3) {\n if (!config.debug) log('input tensors must be of shape [1, height, width, 3]:', input1.shape, input2.shape);\n return 0;\n }\n t.input1 = tf.clone(input1);\n t.input2 = (input1.shape[1] !== input2.shape[1] || input1.shape[2] !== input2.shape[2]) ? tf.image.resizeBilinear(input2, [input1.shape[1], input1.shape[2]]) : tf.clone(input2);\n t.diff = tf.sub(t.input1, t.input2);\n t.squared = tf.mul(t.diff, t.diff);\n t.sum = tf.sum(t.squared);\n const diffSum = await t.sum.data();\n const diffRelative = diffSum[0] / (input1.shape[1] || 1) / (input1.shape[2] || 1) / 255 / 3;\n tf.dispose([t.input1, t.input2, t.diff, t.squared, t.sum]);\n return diffRelative;\n}\n", "import * as tf from '../../dist/tfjs.esm.js';\nimport * as image from '../image/image';\n\n/** Env class that holds detected capabilities */\nexport class Env {\n /** Running in Browser */\n browser: boolean;\n /** Running in NodeJS */\n node: boolean;\n /** Running in WebWorker thread */\n worker: boolean;\n /** Detected platform */\n platform: string = '';\n /** Detected agent */\n agent: string = '';\n /** List of supported backends */\n backends: string[] = [];\n /** Has any work been performed so far */\n initial: boolean;\n /** Are image filters supported? */\n filter: boolean | undefined;\n /** TFJS instance details */\n tfjs: {\n version: undefined | string,\n };\n /** Is offscreenCanvas supported? */\n offscreen: undefined | boolean;\n /** Are performance counter instant values or additive */\n perfadd: boolean = false;\n /** If using tfjs-node get version of underlying tensorflow shared library and if gpu acceleration is enabled */\n tensorflow: {\n version: undefined | string,\n gpu: undefined | boolean,\n } = {\n version: undefined,\n gpu: undefined,\n };\n /** WASM detected capabilities */\n wasm: {\n supported: undefined | boolean,\n backend: undefined | boolean,\n simd: undefined | boolean,\n multithread: undefined | boolean,\n } = {\n supported: undefined,\n backend: undefined,\n simd: undefined,\n multithread: undefined,\n };\n /** WebGL detected capabilities */\n webgl: {\n supported: undefined | boolean,\n backend: undefined | boolean,\n version: undefined | string,\n renderer: undefined | string,\n } = {\n supported: undefined,\n backend: undefined,\n version: undefined,\n renderer: undefined,\n };\n /** WebGPU detected capabilities */\n webgpu: {\n supported: undefined | boolean,\n backend: undefined | boolean,\n adapter: undefined | string,\n } = {\n supported: undefined,\n backend: undefined,\n adapter: undefined,\n };\n /** CPU info */\n cpu: {\n model: undefined | string,\n flags: string[],\n } = {\n model: undefined,\n flags: [],\n };\n /** List of supported kernels for current backend */\n kernels: string[] = [];\n /** MonkeyPatch for Canvas */\n Canvas: undefined;\n /** MonkeyPatch for Image */\n Image: undefined;\n /** MonkeyPatch for ImageData */\n ImageData: undefined;\n\n constructor() {\n this.browser = typeof navigator !== 'undefined';\n this.node = (typeof process !== 'undefined') && (typeof process.versions !== 'undefined') && (typeof process.versions.node !== 'undefined');\n this.tfjs = { version: tf.version['tfjs-core'] };\n this.offscreen = typeof OffscreenCanvas !== 'undefined';\n this.initial = true;\n\n // @ts-ignore WorkerGlobalScope evaluated in browser only\n this.worker = this.browser && this.offscreen ? (typeof WorkerGlobalScope !== 'undefined') : undefined;\n if (typeof navigator !== 'undefined') {\n const raw = navigator.userAgent.match(/\\(([^()]+)\\)/g);\n if (raw?.[0]) {\n const platformMatch = raw[0].match(/\\(([^()]+)\\)/g);\n this.platform = (platformMatch?.[0]) ? platformMatch[0].replace(/\\(|\\)/g, '') : '';\n this.agent = navigator.userAgent.replace(raw[0], '');\n if (this.platform[1]) this.agent = this.agent.replace(raw[1], '');\n this.agent = this.agent.replace(/ /g, ' ');\n // chrome offscreencanvas gpu memory leak\n /*\n const isChrome = env.agent.match(/Chrome\\/.[0-9]/g);\n const verChrome = isChrome && isChrome[0] ? isChrome[0].split('/')[1] : 0;\n if (verChrome > 92 && verChrome < 96) {\n log('disabling offscreenCanvas due to browser error:', isChrome ? isChrome[0] : 'unknown');\n this.offscreen = false;\n }\n */\n }\n } else if (typeof process !== 'undefined') {\n this.platform = `${process.platform} ${process.arch}`;\n this.agent = `NodeJS ${process.version}`;\n }\n }\n\n /** update backend information */\n async updateBackend() {\n // analyze backends\n this.backends = Object.keys(tf.engine().registryFactory);\n this.tensorflow = {\n version: (tf.backend().binding ? tf.backend().binding.TF_Version : undefined),\n gpu: (tf.backend().binding ? tf.backend().binding.isUsingGpuDevice() : undefined),\n };\n this.wasm.supported = typeof WebAssembly !== 'undefined';\n this.wasm.backend = this.backends.includes('wasm');\n if (this.wasm.supported && this.wasm.backend && tf.getBackend() === 'wasm') {\n this.wasm.simd = tf.env().get('WASM_HAS_SIMD_SUPPORT');\n this.wasm.multithread = tf.env().get('WASM_HAS_MULTITHREAD_SUPPORT');\n }\n const c = image.canvas(100, 100);\n const ctx = c ? c.getContext('webgl2') : undefined; // causes too many gl contexts\n // const ctx = typeof tf.backend().getGPGPUContext !== undefined ? tf.backend().getGPGPUContext : null;\n this.webgl.supported = typeof ctx !== 'undefined';\n this.webgl.backend = this.backends.includes('webgl');\n if (this.webgl.supported && this.webgl.backend && (tf.getBackend() === 'webgl' || tf.getBackend() === 'humangl')) {\n const gl = tf.backend().gpgpu !== 'undefined' ? await tf.backend().getGPGPUContext().gl : null;\n if (gl) {\n this.webgl.version = gl.getParameter(gl.VERSION);\n this.webgl.renderer = gl.getParameter(gl.RENDERER);\n }\n }\n this.webgpu.supported = this.browser && typeof navigator.gpu !== 'undefined';\n this.webgpu.backend = this.backends.includes('webgpu');\n try {\n if (this.webgpu.supported) {\n const adapter = await navigator.gpu.requestAdapter();\n this.webgpu.adapter = adapter ? adapter.name : undefined;\n }\n } catch {\n this.webgpu.supported = false;\n }\n try {\n this.kernels = tf.getKernelsForBackend(tf.getBackend()).map((kernel) => (kernel.kernelName as string).toLowerCase());\n } catch { /**/ }\n }\n\n /** update cpu information */\n updateCPU() {\n const cpu = { model: '', flags: [] };\n if (this.node && this.platform.startsWith('linux')) {\n /*\n const fs = require('fs');\n try {\n const data = fs.readFileSync('/proc/cpuinfo').toString();\n for (const line of data.split('\\n')) {\n if (line.startsWith('model name')) cpu.model = line.match(/:(.*)/g)[0].replace(':', '').trim();\n if (line.startsWith('flags')) cpu.flags = line.match(/:(.*)/g)[0].replace(':', '').trim().split(' ').sort();\n }\n } catch { }\n */\n }\n if (!this.cpu) Object.defineProperty(this, 'cpu', { value: cpu });\n else this.cpu = cpu;\n }\n}\n\nexport const env = new Env();\n", "/**\n * Loader and Validator for all models used by Human\n */\n\nimport { env } from './util/env';\nimport { log } from './util/util';\nimport * as gear from './gear/gear';\nimport * as ssrnetAge from './gear/ssrnet-age';\nimport * as ssrnetGender from './gear/ssrnet-gender';\nimport * as antispoof from './face/antispoof';\nimport * as blazeface from './face/blazeface';\nimport * as blazepose from './body/blazepose';\nimport * as centernet from './object/centernet';\nimport * as efficientpose from './body/efficientpose';\nimport * as emotion from './gear/emotion';\nimport * as mobilefacenet from './face/mobilefacenet';\nimport * as insightface from './face/insightface';\nimport * as facemesh from './face/facemesh';\nimport * as faceres from './face/faceres';\nimport * as handpose from './hand/handpose';\nimport * as handtrack from './hand/handtrack';\nimport * as iris from './face/iris';\nimport * as liveness from './face/liveness';\nimport * as movenet from './body/movenet';\nimport * as nanodet from './object/nanodet';\nimport * as posenet from './body/posenet';\nimport * as segmentation from './segmentation/segmentation';\nimport { modelStats, ModelInfo } from './tfjs/load';\nimport type { GraphModel } from './tfjs/types';\nimport type { Human } from './human';\n\n/** Instances of all possible TFJS Graph Models used by Human\n * - loaded as needed based on configuration\n * - initialized explictly with `human.load()` method\n * - initialized implicity on first call to `human.detect()`\n * - each model can be `null` if not loaded, instance of `GraphModel` if loaded or `Promise` if loading\n */\nexport class Models {\n ssrnetage: null | GraphModel | Promise = null;\n gear: null | GraphModel | Promise = null;\n blazeposedetect: null | GraphModel | Promise = null;\n blazepose: null | GraphModel | Promise = null;\n centernet: null | GraphModel | Promise = null;\n efficientpose: null | GraphModel | Promise = null;\n mobilefacenet: null | GraphModel | Promise = null;\n insightface: null | GraphModel | Promise = null;\n emotion: null | GraphModel | Promise = null;\n facedetect: null | GraphModel | Promise = null;\n faceiris: null | GraphModel | Promise = null;\n facemesh: null | GraphModel | Promise = null;\n faceres: null | GraphModel | Promise = null;\n ssrnetgender: null | GraphModel | Promise = null;\n handpose: null | GraphModel | Promise = null;\n handskeleton: null | GraphModel | Promise = null;\n handtrack: null | GraphModel | Promise = null;\n liveness: null | GraphModel | Promise = null;\n movenet: null | GraphModel | Promise = null;\n nanodet: null | GraphModel | Promise = null;\n posenet: null | GraphModel | Promise = null;\n segmentation: null | GraphModel | Promise = null;\n antispoof: null | GraphModel | Promise = null;\n}\n\nexport interface ModelStats {\n numLoadedModels: number,\n numEnabledModels: undefined,\n numDefinedModels: number,\n percentageLoaded: number,\n totalSizeFromManifest: number,\n totalSizeWeights: number,\n totalSizeLoading: number,\n totalSizeEnabled: undefined,\n modelStats: ModelInfo[],\n}\n\nexport const getModelStats = (instance: Human): ModelStats => {\n let totalSizeFromManifest = 0;\n let totalSizeWeights = 0;\n let totalSizeLoading = 0;\n for (const m of Object.values(modelStats)) {\n totalSizeFromManifest += m.sizeFromManifest;\n totalSizeWeights += m.sizeLoadedWeights;\n totalSizeLoading += m.sizeDesired;\n }\n const percentageLoaded = totalSizeLoading > 0 ? totalSizeWeights / totalSizeLoading : 0;\n return {\n numLoadedModels: Object.values(modelStats).length,\n numEnabledModels: undefined,\n numDefinedModels: Object.keys(instance.models).length,\n percentageLoaded,\n totalSizeFromManifest,\n totalSizeWeights,\n totalSizeLoading,\n totalSizeEnabled: undefined,\n modelStats: Object.values(modelStats),\n };\n};\n\nexport function reset(instance: Human): void {\n // if (instance.config.debug) log('resetting loaded models');\n for (const model of Object.keys(instance.models)) instance.models[model as keyof Models] = null;\n}\n\n/** Load method preloads all instance.configured models on-demand */\nexport async function load(instance: Human): Promise {\n if (env.initial) reset(instance);\n if (instance.config.hand.enabled) { // handpose model is a combo that must be loaded as a whole\n if (!instance.models.handpose && instance.config.hand.detector?.modelPath?.includes('handdetect')) {\n [instance.models.handpose, instance.models.handskeleton] = await handpose.load(instance.config);\n }\n if (!instance.models.handskeleton && instance.config.hand.landmarks && instance.config.hand.detector?.modelPath?.includes('handdetect')) {\n [instance.models.handpose, instance.models.handskeleton] = await handpose.load(instance.config);\n }\n }\n if (instance.config.body.enabled && !instance.models.blazepose && instance.config.body.modelPath?.includes('blazepose')) instance.models.blazepose = blazepose.loadPose(instance.config);\n if (instance.config.body.enabled && !instance.models.blazeposedetect && instance.config.body['detector'] && instance.config.body['detector'].modelPath) instance.models.blazeposedetect = blazepose.loadDetect(instance.config);\n if (instance.config.body.enabled && !instance.models.efficientpose && instance.config.body.modelPath?.includes('efficientpose')) instance.models.efficientpose = efficientpose.load(instance.config);\n if (instance.config.body.enabled && !instance.models.movenet && instance.config.body.modelPath?.includes('movenet')) instance.models.movenet = movenet.load(instance.config);\n if (instance.config.body.enabled && !instance.models.posenet && instance.config.body.modelPath?.includes('posenet')) instance.models.posenet = posenet.load(instance.config);\n if (instance.config.face.enabled && !instance.models.facedetect) instance.models.facedetect = blazeface.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.antispoof?.enabled && !instance.models.antispoof) instance.models.antispoof = antispoof.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.liveness?.enabled && !instance.models.liveness) instance.models.liveness = liveness.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.description?.enabled && !instance.models.faceres) instance.models.faceres = faceres.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.emotion?.enabled && !instance.models.emotion) instance.models.emotion = emotion.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.iris?.enabled && !instance.config.face.attention?.enabled && !instance.models.faceiris) instance.models.faceiris = iris.load(instance.config);\n if (instance.config.face.enabled && instance.config.face.mesh?.enabled && !instance.models.facemesh) instance.models.facemesh = facemesh.load(instance.config);\n if (instance.config.face.enabled && instance.config.face['gear']?.enabled && !instance.models.gear) instance.models.gear = gear.load(instance.config);\n if (instance.config.face.enabled && instance.config.face['ssrnet']?.enabled && !instance.models.ssrnetage) instance.models.ssrnetage = ssrnetAge.load(instance.config);\n if (instance.config.face.enabled && instance.config.face['ssrnet']?.enabled && !instance.models.ssrnetgender) instance.models.ssrnetgender = ssrnetGender.load(instance.config);\n if (instance.config.face.enabled && instance.config.face['mobilefacenet']?.enabled && !instance.models.mobilefacenet) instance.models.mobilefacenet = mobilefacenet.load(instance.config);\n if (instance.config.face.enabled && instance.config.face['insightface']?.enabled && !instance.models.insightface) instance.models.insightface = insightface.load(instance.config);\n if (instance.config.hand.enabled && !instance.models.handtrack && instance.config.hand.detector?.modelPath?.includes('handtrack')) instance.models.handtrack = handtrack.loadDetect(instance.config);\n 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);\n if (instance.config.object.enabled && !instance.models.centernet && instance.config.object.modelPath?.includes('centernet')) instance.models.centernet = centernet.load(instance.config);\n if (instance.config.object.enabled && !instance.models.nanodet && instance.config.object.modelPath?.includes('nanodet')) instance.models.nanodet = nanodet.load(instance.config);\n if (instance.config.segmentation.enabled && !instance.models.segmentation) instance.models.segmentation = segmentation.load(instance.config);\n\n // models are loaded in parallel asynchronously so lets wait until they are actually loaded\n for await (const model of Object.keys(instance.models)) {\n if (instance.models[model as keyof Models] && typeof instance.models[model as keyof Models] !== 'undefined') {\n instance.models[model as keyof Models] = await instance.models[model as keyof Models];\n }\n }\n}\n\nlet instance: Human;\nexport interface KernelOps { name: string, url: string, missing: string[], ops: string[] }\n\nexport function validateModel(newInstance: Human | null, model: GraphModel | null, name: string): KernelOps | null {\n if (newInstance) instance = newInstance;\n if (!model) return null;\n if (!instance) log('instance not registred');\n if (!instance.config.validateModels) return null;\n const simpleOps = ['const', 'placeholder', 'noop', 'pad', 'squeeze', 'add', 'sub', 'mul', 'div'];\n const ignoreOps = ['biasadd', 'fusedbatchnormv3', 'matmul'];\n const ops: string[] = [];\n const missing: string[] = [];\n interface Op { name: string, category: string, op: string }\n const url = model['modelUrl'] as string;\n const executor = model['executor'];\n if (executor?.graph?.nodes) {\n for (const kernel of Object.values(executor.graph.nodes)) {\n const op = (kernel as Op).op.toLowerCase();\n if (!ops.includes(op)) ops.push(op);\n }\n } else {\n if (!executor && instance.config.debug) {\n log('model not loaded', name);\n }\n }\n for (const op of ops) {\n if (!simpleOps.includes(op) // exclude simple ops\n && !ignoreOps.includes(op) // exclude specific ops\n && !instance.env.kernels.includes(op) // check actual kernel ops\n && !instance.env.kernels.includes(op.replace('_', '')) // check variation without _\n && !instance.env.kernels.includes(op.replace('native', '')) // check standard variation\n && !instance.env.kernels.includes(op.replace('v2', ''))) { // check non-versioned variation\n missing.push(op);\n }\n }\n if (instance.config.debug && missing.length > 0) log('model validation failed:', name, missing);\n return missing.length > 0 ? { name, missing, ops, url } : null;\n}\n\nexport function validate(newInstance: Human): { name: string, missing: string[] }[] {\n instance = newInstance;\n const missing: KernelOps[] = [];\n for (const defined of Object.keys(instance.models)) {\n const model: GraphModel | null = instance.models[defined as keyof Models] as GraphModel | null;\n if (!model) continue;\n const res = validateModel(instance, model, defined);\n if (res) missing.push(res);\n }\n return missing;\n}\n", "/**\n * GEAR [gender/emotion/age/race] model implementation\n *\n * Based on: [**GEAR Predictor**](https://github.com/Udolf15/GEAR-Predictor)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport type { Gender, Race } from '../result';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport { env } from '../util/env';\n\nexport interface GearType { age: number, gender: Gender, genderScore: number, race: { score: number, race: Race }[] }\nlet model: GraphModel | null;\nconst last: GearType[] = [];\nconst raceNames = ['white', 'black', 'asian', 'indian', 'other'];\nconst ageWeights = [15, 23, 28, 35.5, 45.5, 55.5, 65];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config) {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.gear?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise {\n if (!model) return { age: 0, gender: 'unknown', genderScore: 0, race: [] };\n const skipFrame = skipped < (config.face.gear?.skipFrames || 0);\n const skipTime = (config.face.gear?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx]) {\n skipped++;\n return last[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n if (!model?.inputs[0].shape) return;\n const t: Record = {};\n // t.resize = tf.image.resizeBilinear(image, [model?.inputs[0].shape[2], model?.inputs[0].shape[1]], false);\n const box = [[0.0, 0.10, 0.90, 0.90]]; // empyrical values for top, left, bottom, right\n t.resize = tf.image.cropAndResize(image, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n const obj: GearType = { age: 0, gender: 'unknown', genderScore: 0, race: [] };\n if (config.face.gear?.enabled) [t.age, t.gender, t.race] = model.execute(t.resize, ['age_output', 'gender_output', 'race_output']) as Tensor[];\n const gender = await t.gender.data();\n obj.gender = gender[0] > gender[1] ? 'male' : 'female';\n obj.genderScore = Math.round(100 * (gender[0] > gender[1] ? gender[0] : gender[1])) / 100;\n const race = await t.race.data();\n for (let i = 0; i < race.length; i++) {\n if (race[i] > (config.face.gear?.minConfidence || 0.2)) obj.race.push({ score: Math.round(100 * race[i]) / 100, race: raceNames[i] as Race });\n }\n obj.race.sort((a, b) => b.score - a.score);\n // {0: 'Below20', 1: '21-25', 2: '26-30', 3: '31-40',4: '41-50', 5: '51-60', 6: 'Above60'}\n const ageDistribution = Array.from(await t.age.data());\n const ageSorted = ageDistribution.map((a, i) => [ageWeights[i], a]).sort((a, b) => b[1] - a[1]);\n let age = ageSorted[0][0]; // pick best starting point\n for (let i = 1; i < ageSorted.length; i++) age += ageSorted[i][1] * (ageSorted[i][0] - age); // adjust with each other choice by weight\n obj.age = Math.round(10 * age) / 10;\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n last[idx] = obj;\n lastCount = count;\n lastTime = now();\n resolve(obj);\n });\n}\n", "import * as tf from '../../dist/tfjs.esm.js';\nimport type { Tensor } from './types';\n\nexport const constants: Record = {\n tf255: 255,\n tf1: 1,\n tf2: 2,\n tf05: 0.5,\n tf127: 127.5,\n rgb: [0.2989, 0.5870, 0.1140],\n};\n\nexport function init() {\n constants.tf255 = tf.scalar(255, 'float32');\n constants.tf1 = tf.scalar(1, 'float32');\n constants.tf2 = tf.scalar(2, 'float32');\n constants.tf05 = tf.scalar(0.5, 'float32');\n constants.tf127 = tf.scalar(127.5, 'float32');\n constants.rgb = tf.tensor1d([0.2989, 0.5870, 0.1140], 'float32'); // factors for red/green/blue colors when converting to grayscale\n}\n", "/**\n * Age model implementation\n *\n * Based on: [**SSR-Net**](https://github.com/shamangary/SSR-Net)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { env } from '../util/env';\nimport { constants } from '../tfjs/constants';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\n\nlet model: GraphModel | null;\nconst last: { age: number }[] = [];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config) {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face['ssrnet'].modelPathAge);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise<{ age: number }> {\n if (!model) return { age: 0 };\n const skipFrame = skipped < (config.face['ssrnet']?.skipFrames || 0);\n const skipTime = (config.face['ssrnet']?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipFrame && skipTime && (lastCount === count) && last[idx]?.age && (last[idx]?.age > 0)) {\n skipped++;\n return last[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n if (!model?.inputs || !model.inputs[0] || !model.inputs[0].shape) return;\n const t: Record = {};\n t.resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);\n t.enhance = tf.mul(t.resize, constants.tf255);\n const obj = { age: 0 };\n if (config.face['ssrnet']?.enabled) t.age = model.execute(t.enhance) as Tensor;\n if (t.age) {\n const data = await t.age.data();\n obj.age = Math.trunc(10 * data[0]) / 10;\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n last[idx] = obj;\n lastCount = count;\n lastTime = now();\n resolve(obj);\n });\n}\n", "/**\n * Gender model implementation\n *\n * Based on: [**SSR-Net**](https://github.com/shamangary/SSR-Net)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { constants } from '../tfjs/constants';\nimport type { Gender } from '../result';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nconst last: { gender: Gender, genderScore: number }[] = [];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\n// tuning values\nconst rgb = [0.2989, 0.5870, 0.1140]; // factors for red/green/blue colors when converting to grayscale\n\nexport async function load(config: Config) {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face['ssrnet']?.modelPathGender);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx, count): Promise<{ gender: Gender, genderScore: number }> {\n if (!model) return { gender: 'unknown', genderScore: 0 };\n const skipFrame = skipped < (config.face['ssrnet']?.skipFrames || 0);\n const skipTime = (config.face['ssrnet']?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipFrame && skipTime && (lastCount === count) && last[idx]?.gender && (last[idx]?.genderScore > 0)) {\n skipped++;\n return last[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n if (!model?.inputs[0].shape) return;\n const t: Record = {};\n t.resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);\n t.enhance = tf.tidy(() => {\n const [red, green, blue] = tf.split(t.resize, 3, 3);\n const redNorm = tf.mul(red, rgb[0]);\n const greenNorm = tf.mul(green, rgb[1]);\n const blueNorm = tf.mul(blue, rgb[2]);\n const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);\n const normalize = tf.mul(tf.sub(grayscale, constants.tf05), 2); // range grayscale:-1..1\n return normalize;\n });\n const obj: { gender: Gender, genderScore: number } = { gender: 'unknown', genderScore: 0 };\n if (config.face['ssrnet']?.enabled) t.gender = model.execute(t.enhance) as Tensor;\n const data = await t.gender.data();\n obj.gender = data[0] > data[1] ? 'female' : 'male'; // returns two values 0..1, bigger one is prediction\n obj.genderScore = data[0] > data[1] ? (Math.trunc(100 * data[0]) / 100) : (Math.trunc(100 * data[1]) / 100);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n last[idx] = obj;\n lastCount = count;\n lastTime = now();\n resolve(obj);\n });\n}\n", "/**\n * Anti-spoofing model implementation\n */\n\nimport { log, now } from '../util/util';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nconst cached: number[] = [];\nlet skipped = Number.MAX_SAFE_INTEGER;\nlet lastCount = 0;\nlet lastTime = 0;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.antispoof?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise {\n if (!model || !model?.['executor']) return 0;\n const skipTime = (config.face.antispoof?.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.face.antispoof?.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && cached[idx]) {\n skipped++;\n return cached[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const resize = tf.image.resizeBilinear(image, [model?.inputs[0].shape ? model.inputs[0].shape[2] : 0, model?.inputs[0].shape ? model.inputs[0].shape[1] : 0], false);\n const res = model?.execute(resize) as Tensor;\n const num = (await res.data())[0];\n cached[idx] = Math.round(100 * num) / 100;\n lastCount = count;\n lastTime = now();\n tf.dispose([resize, res]);\n resolve(cached[idx]);\n });\n}\n", "/**\n * BlazeFace, FaceMesh & Iris model implementation\n * See `facemesh.ts` for entry point\n */\n\nexport const meshAnnotations: Record = {\n silhouette: [\n 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288,\n 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136,\n 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109,\n ],\n // lipsUpperOuter: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291], // 11\n // lipsLowerOuter: [146, 91, 181, 84, 17, 314, 405, 321, 375, 291], // 10\n // lipsUpperInner: [78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308], // 11\n // lipsLowerInner: [78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308], // 11\n lipsUpperOuter: [185, 40, 39, 37, 0, 267, 269, 270, 409],\n lipsLowerOuter: [61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291],\n lipsUpperInner: [191, 80, 81, 82, 13, 312, 311, 310, 415],\n lipsLowerInner: [78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308],\n lipsLowerSemiOuter: [76, 77, 90, 180, 85, 16, 315, 404, 320, 307, 306],\n lipsUpperSemiOuter: [184, 74, 73, 72, 11, 302, 303, 304, 408],\n lipsLowerSemiInner: [62, 96, 89, 179, 86, 15, 316, 403, 319, 325, 292],\n lipsUpperSemiInner: [183, 42, 41, 38, 12, 268, 271, 272, 407],\n rightEyeUpper0: [246, 161, 160, 159, 158, 157, 173], // 7\n rightEyeLower0: [33, 7, 163, 144, 145, 153, 154, 155, 133], // 9\n rightEyeUpper1: [247, 30, 29, 27, 28, 56, 190], // 7\n rightEyeLower1: [130, 25, 110, 24, 23, 22, 26, 112, 243], // 9\n rightEyeUpper2: [113, 225, 224, 223, 222, 221, 189], // 7\n rightEyeLower2: [226, 31, 228, 229, 230, 231, 232, 233, 244], // 9\n rightEyeLower3: [143, 111, 117, 118, 119, 120, 121, 128, 245], // 9\n rightEyebrowUpper: [156, 70, 63, 105, 66, 107, 55, 193], // 8\n rightEyebrowLower: [35, 124, 46, 53, 52, 65], // 6\n rightEyeIris: [473, 474, 475, 476, 477], // 5\n leftEyeUpper0: [466, 388, 387, 386, 385, 384, 398],\n leftEyeLower0: [263, 249, 390, 373, 374, 380, 381, 382, 362],\n leftEyeUpper1: [467, 260, 259, 257, 258, 286, 414],\n leftEyeLower1: [359, 255, 339, 254, 253, 252, 256, 341, 463],\n leftEyeUpper2: [342, 445, 444, 443, 442, 441, 413],\n leftEyeLower2: [446, 261, 448, 449, 450, 451, 452, 453, 464],\n leftEyeLower3: [372, 340, 346, 347, 348, 349, 350, 357, 465],\n leftEyebrowUpper: [383, 300, 293, 334, 296, 336, 285, 417],\n leftEyebrowLower: [265, 353, 276, 283, 282, 295],\n leftEyeIris: [468, 469, 470, 471, 472],\n midwayBetweenEyes: [168],\n noseTip: [1],\n noseBottom: [2],\n noseRightCorner: [98],\n noseLeftCorner: [327],\n rightCheek: [205],\n leftCheek: [425],\n};\n\nexport const meshLandmarks: Record = {\n count: 468,\n mouth: 13,\n symmetryLine: [13, meshAnnotations.midwayBetweenEyes[0]],\n};\n\nexport const blazeFaceLandmarks: Record = {\n leftEye: 0,\n rightEye: 1,\n nose: 2,\n mouth: 3,\n leftEar: 4,\n rightEar: 5,\n symmetryLine: [3, 2],\n};\n\nexport const irisIndices: { key: string, indices: number[] }[] = [ // A mapping from facemesh model keypoints to iris model keypoints.\n { key: 'EyeUpper0', indices: [9, 10, 11, 12, 13, 14, 15] }, // 7 x 3d\n { key: 'EyeUpper1', indices: [25, 26, 27, 28, 29, 30, 31] }, // 7 x 3d\n { key: 'EyeUpper2', indices: [41, 42, 43, 44, 45, 46, 47] }, // 7 x 3d\n { key: 'EyeLower0', indices: [0, 1, 2, 3, 4, 5, 6, 7, 8] }, // 7 x 3d\n { key: 'EyeLower1', indices: [16, 17, 18, 19, 20, 21, 22, 23, 24] }, // 9 x 3d\n { key: 'EyeLower2', indices: [32, 33, 34, 35, 36, 37, 38, 39, 40] }, // 9 x 3d\n { key: 'EyeLower3', indices: [54, 55, 56, 57, 58, 59, 60, 61, 62] }, // 9 x 3d\n { key: 'EyebrowUpper', indices: [63, 64, 65, 66, 67, 68, 69, 70] }, // 8 x 3d\n { key: 'EyebrowLower', indices: [48, 49, 50, 51, 52, 53] }, // 6 x 3d\n];\n\nexport const UV468: [number, number][] = [\n [0.499976992607117, 0.652534008026123],\n [0.500025987625122, 0.547487020492554],\n [0.499974012374878, 0.602371990680695],\n [0.482113003730774, 0.471979022026062],\n [0.500150978565216, 0.527155995368958],\n [0.499909996986389, 0.498252987861633],\n [0.499523013830185, 0.40106201171875],\n [0.289712011814117, 0.380764007568359],\n [0.499954998493195, 0.312398016452789],\n [0.499987006187439, 0.269918978214264],\n [0.500023007392883, 0.107050001621246],\n [0.500023007392883, 0.666234016418457],\n [0.5000159740448, 0.679224014282227],\n [0.500023007392883, 0.692348003387451],\n [0.499976992607117, 0.695277988910675],\n [0.499976992607117, 0.70593398809433],\n [0.499976992607117, 0.719385027885437],\n [0.499976992607117, 0.737019002437592],\n [0.499967992305756, 0.781370997428894],\n [0.499816000461578, 0.562981009483337],\n [0.473773002624512, 0.573909997940063],\n [0.104906998574734, 0.254140973091125],\n [0.365929991006851, 0.409575998783112],\n [0.338757991790771, 0.41302502155304],\n [0.311120003461838, 0.409460008144379],\n [0.274657994508743, 0.389131009578705],\n [0.393361985683441, 0.403706014156342],\n [0.345234006643295, 0.344011008739471],\n [0.370094001293182, 0.346076011657715],\n [0.319321990013123, 0.347265005111694],\n [0.297903001308441, 0.353591024875641],\n [0.24779200553894, 0.410809993743896],\n [0.396889001131058, 0.842755019664764],\n [0.280097991228104, 0.375599980354309],\n [0.106310002505779, 0.399955987930298],\n [0.2099249958992, 0.391353011131287],\n [0.355807989835739, 0.534406006336212],\n [0.471751004457474, 0.65040397644043],\n [0.474155008792877, 0.680191993713379],\n [0.439785003662109, 0.657229006290436],\n [0.414617002010345, 0.66654098033905],\n [0.450374007225037, 0.680860996246338],\n [0.428770989179611, 0.682690978050232],\n [0.374971002340317, 0.727805018424988],\n [0.486716985702515, 0.547628998756409],\n [0.485300987958908, 0.527395009994507],\n [0.257764995098114, 0.314490020275116],\n [0.401223003864288, 0.455172002315521],\n [0.429818987846375, 0.548614978790283],\n [0.421351999044418, 0.533740997314453],\n [0.276895999908447, 0.532056987285614],\n [0.483370006084442, 0.499586999416351],\n [0.33721199631691, 0.282882988452911],\n [0.296391993761063, 0.293242990970612],\n [0.169294998049736, 0.193813979625702],\n [0.447580009698868, 0.302609980106354],\n [0.392390012741089, 0.353887975215912],\n [0.354490011930466, 0.696784019470215],\n [0.067304998636246, 0.730105042457581],\n [0.442739009857178, 0.572826027870178],\n [0.457098007202148, 0.584792017936707],\n [0.381974011659622, 0.694710969924927],\n [0.392388999462128, 0.694203019142151],\n [0.277076005935669, 0.271932005882263],\n [0.422551989555359, 0.563233017921448],\n [0.385919004678726, 0.281364023685455],\n [0.383103013038635, 0.255840003490448],\n [0.331431001424789, 0.119714021682739],\n [0.229923993349075, 0.232002973556519],\n [0.364500999450684, 0.189113974571228],\n [0.229622006416321, 0.299540996551514],\n [0.173287004232407, 0.278747975826263],\n [0.472878992557526, 0.666198015213013],\n [0.446828007698059, 0.668527007102966],\n [0.422762006521225, 0.673889994621277],\n [0.445307999849319, 0.580065965652466],\n [0.388103008270264, 0.693961024284363],\n [0.403039008378983, 0.706539988517761],\n [0.403629004955292, 0.693953037261963],\n [0.460041999816895, 0.557139039039612],\n [0.431158006191254, 0.692366003990173],\n [0.452181994915009, 0.692366003990173],\n [0.475387006998062, 0.692366003990173],\n [0.465828001499176, 0.779190003871918],\n [0.472328990697861, 0.736225962638855],\n [0.473087012767792, 0.717857003211975],\n [0.473122000694275, 0.704625964164734],\n [0.473033010959625, 0.695277988910675],\n [0.427942007780075, 0.695277988910675],\n [0.426479011774063, 0.703539967536926],\n [0.423162013292313, 0.711845993995667],\n [0.4183090031147, 0.720062971115112],\n [0.390094995498657, 0.639572978019714],\n [0.013953999616206, 0.560034036636353],\n [0.499913990497589, 0.58014702796936],\n [0.413199990987778, 0.69539999961853],\n [0.409626007080078, 0.701822996139526],\n [0.468080013990402, 0.601534962654114],\n [0.422728985548019, 0.585985004901886],\n [0.463079988956451, 0.593783974647522],\n [0.37211999297142, 0.47341400384903],\n [0.334562003612518, 0.496073007583618],\n [0.411671012639999, 0.546965003013611],\n [0.242175996303558, 0.14767599105835],\n [0.290776997804642, 0.201445996761322],\n [0.327338010072708, 0.256527006626129],\n [0.399509996175766, 0.748921036720276],\n [0.441727995872498, 0.261676013469696],\n [0.429764986038208, 0.187834024429321],\n [0.412198007106781, 0.108901023864746],\n [0.288955003023148, 0.398952007293701],\n [0.218936994671822, 0.435410976409912],\n [0.41278201341629, 0.398970007896423],\n [0.257135003805161, 0.355440020561218],\n [0.427684992551804, 0.437960982322693],\n [0.448339998722076, 0.536936044692993],\n [0.178560003638268, 0.45755398273468],\n [0.247308000922203, 0.457193970680237],\n [0.286267012357712, 0.467674970626831],\n [0.332827985286713, 0.460712015628815],\n [0.368755996227264, 0.447206974029541],\n [0.398963987827301, 0.432654976844788],\n [0.476410001516342, 0.405806005001068],\n [0.189241006970406, 0.523923993110657],\n [0.228962004184723, 0.348950982093811],\n [0.490725994110107, 0.562400996685028],\n [0.404670000076294, 0.485132992267609],\n [0.019469000399113, 0.401564002037048],\n [0.426243007183075, 0.420431017875671],\n [0.396993011236191, 0.548797011375427],\n [0.266469985246658, 0.376977026462555],\n [0.439121007919312, 0.51895797252655],\n [0.032313998788595, 0.644356966018677],\n [0.419054001569748, 0.387154996395111],\n [0.462783008813858, 0.505746960639954],\n [0.238978996872902, 0.779744982719421],\n [0.198220998048782, 0.831938028335571],\n [0.107550002634525, 0.540755033493042],\n [0.183610007166862, 0.740257024765015],\n [0.134409993886948, 0.333683013916016],\n [0.385764002799988, 0.883153975009918],\n [0.490967005491257, 0.579378008842468],\n [0.382384985685349, 0.508572995662689],\n [0.174399003386497, 0.397670984268188],\n [0.318785011768341, 0.39623498916626],\n [0.343364000320435, 0.400596976280212],\n [0.396100014448166, 0.710216999053955],\n [0.187885001301765, 0.588537991046906],\n [0.430987000465393, 0.944064974784851],\n [0.318993002176285, 0.898285031318665],\n [0.266247987747192, 0.869701027870178],\n [0.500023007392883, 0.190576016902924],\n [0.499976992607117, 0.954452991485596],\n [0.366169989109039, 0.398822009563446],\n [0.393207013607025, 0.39553701877594],\n [0.410373002290726, 0.391080021858215],\n [0.194993004202843, 0.342101991176605],\n [0.388664990663528, 0.362284004688263],\n [0.365961998701096, 0.355970978736877],\n [0.343364000320435, 0.355356991291046],\n [0.318785011768341, 0.35834002494812],\n [0.301414996385574, 0.363156020641327],\n [0.058132998645306, 0.319076001644135],\n [0.301414996385574, 0.387449026107788],\n [0.499987989664078, 0.618434011936188],\n [0.415838003158569, 0.624195992946625],\n [0.445681989192963, 0.566076993942261],\n [0.465844005346298, 0.620640993118286],\n [0.49992299079895, 0.351523995399475],\n [0.288718998432159, 0.819945991039276],\n [0.335278987884521, 0.852819979190826],\n [0.440512001514435, 0.902418971061707],\n [0.128294005990028, 0.791940987110138],\n [0.408771991729736, 0.373893976211548],\n [0.455606997013092, 0.451801002025604],\n [0.499877005815506, 0.908990025520325],\n [0.375436991453171, 0.924192011356354],\n [0.11421000212431, 0.615022003650665],\n [0.448662012815475, 0.695277988910675],\n [0.4480200111866, 0.704632043838501],\n [0.447111994028091, 0.715808033943176],\n [0.444831997156143, 0.730794012546539],\n [0.430011987686157, 0.766808986663818],\n [0.406787008047104, 0.685672998428345],\n [0.400738000869751, 0.681069016456604],\n [0.392399996519089, 0.677703022956848],\n [0.367855995893478, 0.663918972015381],\n [0.247923001646996, 0.601333022117615],\n [0.452769994735718, 0.420849978923798],\n [0.43639200925827, 0.359887003898621],\n [0.416164010763168, 0.368713974952698],\n [0.413385987281799, 0.692366003990173],\n [0.228018000721931, 0.683571994304657],\n [0.468268007040024, 0.352671027183533],\n [0.411361992359161, 0.804327011108398],\n [0.499989002943039, 0.469825029373169],\n [0.479153990745544, 0.442654013633728],\n [0.499974012374878, 0.439637005329132],\n [0.432112008333206, 0.493588984012604],\n [0.499886006116867, 0.866917014122009],\n [0.49991300702095, 0.821729004383087],\n [0.456548988819122, 0.819200992584229],\n [0.344549000263214, 0.745438992977142],\n [0.37890899181366, 0.574010014533997],\n [0.374292999505997, 0.780184984207153],\n [0.319687992334366, 0.570737957954407],\n [0.357154995203018, 0.604269981384277],\n [0.295284003019333, 0.621580958366394],\n [0.447750002145767, 0.862477004528046],\n [0.410986006259918, 0.508723020553589],\n [0.31395098567009, 0.775308012962341],\n [0.354128003120422, 0.812552988529205],\n [0.324548006057739, 0.703992962837219],\n [0.189096003770828, 0.646299958229065],\n [0.279776990413666, 0.71465802192688],\n [0.1338230073452, 0.682700991630554],\n [0.336768001317978, 0.644733011722565],\n [0.429883986711502, 0.466521978378296],\n [0.455527991056442, 0.548622965812683],\n [0.437114000320435, 0.558896005153656],\n [0.467287987470627, 0.529924988746643],\n [0.414712011814117, 0.335219979286194],\n [0.37704598903656, 0.322777986526489],\n [0.344107985496521, 0.320150971412659],\n [0.312875986099243, 0.32233202457428],\n [0.283526003360748, 0.333190023899078],\n [0.241245999932289, 0.382785975933075],\n [0.102986000478268, 0.468762993812561],\n [0.267612010240555, 0.424560010433197],\n [0.297879010438919, 0.433175981044769],\n [0.333433985710144, 0.433878004550934],\n [0.366427004337311, 0.426115989685059],\n [0.396012008190155, 0.416696012020111],\n [0.420121014118195, 0.41022801399231],\n [0.007561000064015, 0.480777025222778],\n [0.432949006557465, 0.569517970085144],\n [0.458638995885849, 0.479089021682739],\n [0.473466008901596, 0.545744001865387],\n [0.476087987422943, 0.563830018043518],\n [0.468472003936768, 0.555056989192963],\n [0.433990985155106, 0.582361996173859],\n [0.483518004417419, 0.562983989715576],\n [0.482482999563217, 0.57784903049469],\n [0.42645001411438, 0.389798998832703],\n [0.438998997211456, 0.39649498462677],\n [0.450067013502121, 0.400434017181396],\n [0.289712011814117, 0.368252992630005],\n [0.276670008897781, 0.363372981548309],\n [0.517862021923065, 0.471948027610779],\n [0.710287988185883, 0.380764007568359],\n [0.526226997375488, 0.573909997940063],\n [0.895093023777008, 0.254140973091125],\n [0.634069979190826, 0.409575998783112],\n [0.661242008209229, 0.41302502155304],\n [0.688880026340485, 0.409460008144379],\n [0.725341975688934, 0.389131009578705],\n [0.606630027294159, 0.40370500087738],\n [0.654766023159027, 0.344011008739471],\n [0.629905998706818, 0.346076011657715],\n [0.680678009986877, 0.347265005111694],\n [0.702096998691559, 0.353591024875641],\n [0.75221198797226, 0.410804986953735],\n [0.602918028831482, 0.842862963676453],\n [0.719901978969574, 0.375599980354309],\n [0.893692970275879, 0.399959981441498],\n [0.790081977844238, 0.391354024410248],\n [0.643998026847839, 0.534487962722778],\n [0.528249025344849, 0.65040397644043],\n [0.525849997997284, 0.680191040039062],\n [0.560214996337891, 0.657229006290436],\n [0.585384011268616, 0.66654098033905],\n [0.549625992774963, 0.680860996246338],\n [0.57122802734375, 0.682691991329193],\n [0.624852001667023, 0.72809898853302],\n [0.513050019741058, 0.547281980514526],\n [0.51509702205658, 0.527251958847046],\n [0.742246985435486, 0.314507007598877],\n [0.598631024360657, 0.454979002475739],\n [0.570338010787964, 0.548575043678284],\n [0.578631997108459, 0.533622980117798],\n [0.723087012767792, 0.532054007053375],\n [0.516445994377136, 0.499638974666595],\n [0.662801027297974, 0.282917976379395],\n [0.70362401008606, 0.293271005153656],\n [0.830704987049103, 0.193813979625702],\n [0.552385985851288, 0.302568018436432],\n [0.607609987258911, 0.353887975215912],\n [0.645429015159607, 0.696707010269165],\n [0.932694971561432, 0.730105042457581],\n [0.557260990142822, 0.572826027870178],\n [0.542901992797852, 0.584792017936707],\n [0.6180260181427, 0.694710969924927],\n [0.607590973377228, 0.694203019142151],\n [0.722943007946014, 0.271963000297546],\n [0.577413976192474, 0.563166975975037],\n [0.614082992076874, 0.281386971473694],\n [0.616907000541687, 0.255886018276215],\n [0.668509006500244, 0.119913995265961],\n [0.770092010498047, 0.232020974159241],\n [0.635536015033722, 0.189248979091644],\n [0.77039098739624, 0.299556016921997],\n [0.826722025871277, 0.278755009174347],\n [0.527121007442474, 0.666198015213013],\n [0.553171992301941, 0.668527007102966],\n [0.577238023281097, 0.673889994621277],\n [0.554691970348358, 0.580065965652466],\n [0.611896991729736, 0.693961024284363],\n [0.59696102142334, 0.706539988517761],\n [0.596370995044708, 0.693953037261963],\n [0.539958000183105, 0.557139039039612],\n [0.568841993808746, 0.692366003990173],\n [0.547818005084991, 0.692366003990173],\n [0.52461302280426, 0.692366003990173],\n [0.534089982509613, 0.779141008853912],\n [0.527670979499817, 0.736225962638855],\n [0.526912987232208, 0.717857003211975],\n [0.526877999305725, 0.704625964164734],\n [0.526966989040375, 0.695277988910675],\n [0.572058022022247, 0.695277988910675],\n [0.573521018028259, 0.703539967536926],\n [0.57683801651001, 0.711845993995667],\n [0.581691026687622, 0.720062971115112],\n [0.609944999217987, 0.639909982681274],\n [0.986046016216278, 0.560034036636353],\n [0.5867999792099, 0.69539999961853],\n [0.590372025966644, 0.701822996139526],\n [0.531915009021759, 0.601536989212036],\n [0.577268004417419, 0.585934996604919],\n [0.536915004253387, 0.593786001205444],\n [0.627542972564697, 0.473352015018463],\n [0.665585994720459, 0.495950996875763],\n [0.588353991508484, 0.546862006187439],\n [0.757824003696442, 0.14767599105835],\n [0.709249973297119, 0.201507985591888],\n [0.672684013843536, 0.256581008434296],\n [0.600408971309662, 0.74900496006012],\n [0.55826598405838, 0.261672019958496],\n [0.570303976535797, 0.187870979309082],\n [0.588165998458862, 0.109044015407562],\n [0.711045026779175, 0.398952007293701],\n [0.781069993972778, 0.435405015945435],\n [0.587247014045715, 0.398931980133057],\n [0.742869973182678, 0.355445981025696],\n [0.572156012058258, 0.437651991844177],\n [0.55186802148819, 0.536570012569427],\n [0.821442008018494, 0.457556009292603],\n [0.752701997756958, 0.457181990146637],\n [0.71375697851181, 0.467626988887787],\n [0.66711300611496, 0.460672974586487],\n [0.631101012229919, 0.447153985500336],\n [0.6008620262146, 0.432473003864288],\n [0.523481011390686, 0.405627012252808],\n [0.810747981071472, 0.523926019668579],\n [0.771045982837677, 0.348959028720856],\n [0.509127020835876, 0.562718033790588],\n [0.595292985439301, 0.485023975372314],\n [0.980530977249146, 0.401564002037048],\n [0.573499977588654, 0.420000016689301],\n [0.602994978427887, 0.548687994480133],\n [0.733529984951019, 0.376977026462555],\n [0.560611009597778, 0.519016981124878],\n [0.967685997486115, 0.644356966018677],\n [0.580985009670258, 0.387160003185272],\n [0.537728011608124, 0.505385041236877],\n [0.760966002941132, 0.779752969741821],\n [0.801778972148895, 0.831938028335571],\n [0.892440974712372, 0.54076099395752],\n [0.816350996494293, 0.740260004997253],\n [0.865594983100891, 0.333687007427216],\n [0.614073991775513, 0.883246004581451],\n [0.508952975273132, 0.579437971115112],\n [0.617941975593567, 0.508316040039062],\n [0.825608015060425, 0.397674977779388],\n [0.681214988231659, 0.39623498916626],\n [0.656635999679565, 0.400596976280212],\n [0.603900015354156, 0.710216999053955],\n [0.81208598613739, 0.588539004325867],\n [0.56801301240921, 0.944564998149872],\n [0.681007981300354, 0.898285031318665],\n [0.733752012252808, 0.869701027870178],\n [0.633830010890961, 0.398822009563446],\n [0.606792986392975, 0.39553701877594],\n [0.589659988880157, 0.391062021255493],\n [0.805015981197357, 0.342108011245728],\n [0.611334979534149, 0.362284004688263],\n [0.634037971496582, 0.355970978736877],\n [0.656635999679565, 0.355356991291046],\n [0.681214988231659, 0.35834002494812],\n [0.698584973812103, 0.363156020641327],\n [0.941866993904114, 0.319076001644135],\n [0.698584973812103, 0.387449026107788],\n [0.584177017211914, 0.624107003211975],\n [0.554318010807037, 0.566076993942261],\n [0.534153997898102, 0.62064003944397],\n [0.711217999458313, 0.819975018501282],\n [0.664629995822906, 0.852871000766754],\n [0.559099972248077, 0.902631998062134],\n [0.871706008911133, 0.791940987110138],\n [0.591234028339386, 0.373893976211548],\n [0.544341027736664, 0.451583981513977],\n [0.624562978744507, 0.924192011356354],\n [0.88577002286911, 0.615028977394104],\n [0.551338016986847, 0.695277988910675],\n [0.551980018615723, 0.704632043838501],\n [0.552887976169586, 0.715808033943176],\n [0.555167973041534, 0.730794012546539],\n [0.569944024085999, 0.767035007476807],\n [0.593203008174896, 0.685675978660583],\n [0.599261999130249, 0.681069016456604],\n [0.607599973678589, 0.677703022956848],\n [0.631937980651855, 0.663500010967255],\n [0.752032995223999, 0.601315021514893],\n [0.547226011753082, 0.420395016670227],\n [0.563543975353241, 0.359827995300293],\n [0.583841025829315, 0.368713974952698],\n [0.586614012718201, 0.692366003990173],\n [0.771915018558502, 0.683578014373779],\n [0.531597018241882, 0.352482974529266],\n [0.588370978832245, 0.804440975189209],\n [0.52079701423645, 0.442565023899078],\n [0.567984998226166, 0.493479013442993],\n [0.543282985687256, 0.819254994392395],\n [0.655317008495331, 0.745514988899231],\n [0.621008992195129, 0.574018001556396],\n [0.625559985637665, 0.78031200170517],\n [0.680198013782501, 0.570719003677368],\n [0.64276397228241, 0.604337990283966],\n [0.704662978649139, 0.621529996395111],\n [0.552012026309967, 0.862591981887817],\n [0.589071989059448, 0.508637011051178],\n [0.685944974422455, 0.775357007980347],\n [0.645735025405884, 0.812640011310577],\n [0.675342977046967, 0.703978002071381],\n [0.810858011245728, 0.646304965019226],\n [0.72012197971344, 0.714666962623596],\n [0.866151988506317, 0.682704985141754],\n [0.663187026977539, 0.644596993923187],\n [0.570082008838654, 0.466325998306274],\n [0.544561982154846, 0.548375964164734],\n [0.562758982181549, 0.558784961700439],\n [0.531987011432648, 0.530140042304993],\n [0.585271000862122, 0.335177004337311],\n [0.622952997684479, 0.32277899980545],\n [0.655896008014679, 0.320163011550903],\n [0.687132000923157, 0.322345972061157],\n [0.716481983661652, 0.333200991153717],\n [0.758756995201111, 0.382786989212036],\n [0.897013008594513, 0.468769013881683],\n [0.732392013072968, 0.424547016620636],\n [0.70211398601532, 0.433162987232208],\n [0.66652500629425, 0.433866024017334],\n [0.633504986763, 0.426087975502014],\n [0.603875994682312, 0.416586995124817],\n [0.579657971858978, 0.409945011138916],\n [0.992439985275269, 0.480777025222778],\n [0.567192018032074, 0.569419980049133],\n [0.54136598110199, 0.478899002075195],\n [0.526564002037048, 0.546118021011353],\n [0.523913025856018, 0.563830018043518],\n [0.531529009342194, 0.555056989192963],\n [0.566035985946655, 0.582329034805298],\n [0.51631098985672, 0.563053965568542],\n [0.5174720287323, 0.577877044677734],\n [0.573594987392426, 0.389806985855103],\n [0.560697972774506, 0.395331978797913],\n [0.549755990505219, 0.399751007556915],\n [0.710287988185883, 0.368252992630005],\n [0.723330020904541, 0.363372981548309],\n];\n\nexport const TRI468: number[] = [\n 127, 34, 139, 11, 0, 37, 232, 231, 120, 72, 37, 39, 128, 121, 47, 232, 121, 128, 104, 69, 67, 175, 171, 148, 157, 154, 155, 118, 50, 101, 73, 39, 40, 9,\n 151, 108, 48, 115, 131, 194, 204, 211, 74, 40, 185, 80, 42, 183, 40, 92, 186, 230, 229, 118, 202, 212, 214, 83, 18, 17, 76, 61, 146, 160, 29, 30, 56,\n 157, 173, 106, 204, 194, 135, 214, 192, 203, 165, 98, 21, 71, 68, 51, 45, 4, 144, 24, 23, 77, 146, 91, 205, 50, 187, 201, 200, 18, 91, 106, 182, 90, 91,\n 181, 85, 84, 17, 206, 203, 36, 148, 171, 140, 92, 40, 39, 193, 189, 244, 159, 158, 28, 247, 246, 161, 236, 3, 196, 54, 68, 104, 193, 168, 8, 117,\n 228, 31, 189, 193, 55, 98, 97, 99, 126, 47, 100, 166, 79, 218, 155, 154, 26, 209, 49, 131, 135, 136, 150, 47, 126, 217, 223, 52, 53, 45, 51, 134, 211,\n 170, 140, 67, 69, 108, 43, 106, 91, 230, 119, 120, 226, 130, 247, 63, 53, 52, 238, 20, 242, 46, 70, 156, 78, 62, 96, 46, 53, 63, 143, 34, 227, 173,\n 155, 133, 123, 117, 111, 44, 125, 19, 236, 134, 51, 216, 206, 205, 154, 153, 22, 39, 37, 167, 200, 201, 208, 36, 142, 100, 57, 212, 202, 20, 60, 99, 28,\n 158, 157, 35, 226, 113, 160, 159, 27, 204, 202, 210, 113, 225, 46, 43, 202, 204, 62, 76, 77, 137, 123, 116, 41, 38, 72, 203, 129, 142, 64, 98, 240, 49,\n 102, 64, 41, 73, 74, 212, 216, 207, 42, 74, 184, 169, 170, 211, 170, 149, 176, 105, 66, 69, 122, 6, 168, 123, 147, 187, 96, 77, 90, 65, 55, 107, 89,\n 90, 180, 101, 100, 120, 63, 105, 104, 93, 137, 227, 15, 86, 85, 129, 102, 49, 14, 87, 86, 55, 8, 9, 100, 47, 121, 145, 23, 22, 88, 89, 179, 6, 122,\n 196, 88, 95, 96, 138, 172, 136, 215, 58, 172, 115, 48, 219, 42, 80, 81, 195, 3, 51, 43, 146, 61, 171, 175, 199, 81, 82, 38, 53, 46, 225, 144, 163, 110,\n 246, 33, 7, 52, 65, 66, 229, 228, 117, 34, 127, 234, 107, 108, 69, 109, 108, 151, 48, 64, 235, 62, 78, 191, 129, 209, 126, 111, 35, 143, 163, 161, 246,\n 117, 123, 50, 222, 65, 52, 19, 125, 141, 221, 55, 65, 3, 195, 197, 25, 7, 33, 220, 237, 44, 70, 71, 139, 122, 193, 245, 247, 130, 33, 71, 21, 162,\n 153, 158, 159, 170, 169, 150, 188, 174, 196, 216, 186, 92, 144, 160, 161, 2, 97, 167, 141, 125, 241, 164, 167, 37, 72, 38, 12, 145, 159, 160, 38, 82, 13,\n 63, 68, 71, 226, 35, 111, 158, 153, 154, 101, 50, 205, 206, 92, 165, 209, 198, 217, 165, 167, 97, 220, 115, 218, 133, 112, 243, 239, 238, 241, 214,\n 135, 169, 190, 173, 133, 171, 208, 32, 125, 44, 237, 86, 87, 178, 85, 86, 179, 84, 85, 180, 83, 84, 181, 201, 83, 182, 137, 93, 132, 76, 62, 183, 61,\n 76, 184, 57, 61, 185, 212, 57, 186, 214, 207, 187, 34, 143, 156, 79, 239, 237, 123, 137, 177, 44, 1, 4, 201, 194, 32, 64, 102, 129, 213, 215, 138, 59,\n 166, 219, 242, 99, 97, 2, 94, 141, 75, 59, 235, 24, 110, 228, 25, 130, 226, 23, 24, 229, 22, 23, 230, 26, 22, 231, 112, 26, 232, 189, 190, 243, 221, 56,\n 190, 28, 56, 221, 27, 28, 222, 29, 27, 223, 30, 29, 224, 247, 30, 225, 238, 79, 20, 166, 59, 75, 60, 75, 240, 147, 177, 215, 20, 79, 166, 187, 147, 213,\n 112, 233, 244, 233, 128, 245, 128, 114, 188, 114, 217, 174, 131, 115, 220, 217, 198, 236, 198, 131, 134, 177, 132, 58, 143, 35, 124, 110, 163, 7, 228,\n 110, 25, 356, 389, 368, 11, 302, 267, 452, 350, 349, 302, 303, 269, 357, 343, 277, 452, 453, 357, 333, 332, 297, 175, 152, 377, 384, 398, 382, 347,\n 348, 330, 303, 304, 270, 9, 336, 337, 278, 279, 360, 418, 262, 431, 304, 408, 409, 310, 415, 407, 270, 409, 410, 450, 348, 347, 422, 430, 434, 313,\n 314, 17, 306, 307, 375, 387, 388, 260, 286, 414, 398, 335, 406, 418, 364, 367, 416, 423, 358, 327, 251, 284, 298, 281, 5, 4, 373, 374, 253, 307, 320,\n 321, 425, 427, 411, 421, 313, 18, 321, 405, 406, 320, 404, 405, 315, 16, 17, 426, 425, 266, 377, 400, 369, 322, 391, 269, 417, 465, 464, 386, 257, 258,\n 466, 260, 388, 456, 399, 419, 284, 332, 333, 417, 285, 8, 346, 340, 261, 413, 441, 285, 327, 460, 328, 355, 371, 329, 392, 439, 438, 382, 341, 256,\n 429, 420, 360, 364, 394, 379, 277, 343, 437, 443, 444, 283, 275, 440, 363, 431, 262, 369, 297, 338, 337, 273, 375, 321, 450, 451, 349, 446, 342, 467,\n 293, 334, 282, 458, 461, 462, 276, 353, 383, 308, 324, 325, 276, 300, 293, 372, 345, 447, 382, 398, 362, 352, 345, 340, 274, 1, 19, 456, 248, 281, 436,\n 427, 425, 381, 256, 252, 269, 391, 393, 200, 199, 428, 266, 330, 329, 287, 273, 422, 250, 462, 328, 258, 286, 384, 265, 353, 342, 387, 259, 257, 424,\n 431, 430, 342, 353, 276, 273, 335, 424, 292, 325, 307, 366, 447, 345, 271, 303, 302, 423, 266, 371, 294, 455, 460, 279, 278, 294, 271, 272, 304, 432,\n 434, 427, 272, 407, 408, 394, 430, 431, 395, 369, 400, 334, 333, 299, 351, 417, 168, 352, 280, 411, 325, 319, 320, 295, 296, 336, 319, 403, 404, 330,\n 348, 349, 293, 298, 333, 323, 454, 447, 15, 16, 315, 358, 429, 279, 14, 15, 316, 285, 336, 9, 329, 349, 350, 374, 380, 252, 318, 402, 403, 6, 197, 419,\n 318, 319, 325, 367, 364, 365, 435, 367, 397, 344, 438, 439, 272, 271, 311, 195, 5, 281, 273, 287, 291, 396, 428, 199, 311, 271, 268, 283, 444, 445,\n 373, 254, 339, 263, 466, 249, 282, 334, 296, 449, 347, 346, 264, 447, 454, 336, 296, 299, 338, 10, 151, 278, 439, 455, 292, 407, 415, 358, 371, 355,\n 340, 345, 372, 390, 249, 466, 346, 347, 280, 442, 443, 282, 19, 94, 370, 441, 442, 295, 248, 419, 197, 263, 255, 359, 440, 275, 274, 300, 383, 368,\n 351, 412, 465, 263, 467, 466, 301, 368, 389, 380, 374, 386, 395, 378, 379, 412, 351, 419, 436, 426, 322, 373, 390, 388, 2, 164, 393, 370, 462, 461,\n 164, 0, 267, 302, 11, 12, 374, 373, 387, 268, 12, 13, 293, 300, 301, 446, 261, 340, 385, 384, 381, 330, 266, 425, 426, 423, 391, 429, 355, 437, 391,\n 327, 326, 440, 457, 438, 341, 382, 362, 459, 457, 461, 434, 430, 394, 414, 463, 362, 396, 369, 262, 354, 461, 457, 316, 403, 402, 315, 404, 403, 314,\n 405, 404, 313, 406, 405, 421, 418, 406, 366, 401, 361, 306, 408, 407, 291, 409, 408, 287, 410, 409, 432, 436, 410, 434, 416, 411, 264, 368, 383, 309,\n 438, 457, 352, 376, 401, 274, 275, 4, 421, 428, 262, 294, 327, 358, 433, 416, 367, 289, 455, 439, 462, 370, 326, 2, 326, 370, 305, 460, 455, 254,\n 449, 448, 255, 261, 446, 253, 450, 449, 252, 451, 450, 256, 452, 451, 341, 453, 452, 413, 464, 463, 441, 413, 414, 258, 442, 441, 257, 443, 442, 259,\n 444, 443, 260, 445, 444, 467, 342, 445, 459, 458, 250, 289, 392, 290, 290, 328, 460, 376, 433, 435, 250, 290, 392, 411, 416, 433, 341, 463, 464, 453,\n 464, 465, 357, 465, 412, 343, 412, 399, 360, 363, 440, 437, 399, 456, 420, 456, 363, 401, 435, 288, 372, 383, 353, 339, 255, 249, 448, 261, 255, 133,\n 243, 190, 133, 155, 112, 33, 246, 247, 33, 130, 25, 398, 384, 286, 362, 398, 414, 362, 463, 341, 263, 359, 467, 263, 249, 255, 466, 467, 260, 75, 60,\n 166, 238, 239, 79, 162, 127, 139, 72, 11, 37, 121, 232, 120, 73, 72, 39, 114, 128, 47, 233, 232, 128, 103, 104, 67, 152, 175, 148, 173, 157, 155,\n 119, 118, 101, 74, 73, 40, 107, 9, 108, 49, 48, 131, 32, 194, 211, 184, 74, 185, 191, 80, 183, 185, 40, 186, 119, 230, 118, 210, 202, 214, 84, 83, 17,\n 77, 76, 146, 161, 160, 30, 190, 56, 173, 182, 106, 194, 138, 135, 192, 129, 203, 98, 54, 21, 68, 5, 51, 4, 145, 144, 23, 90, 77, 91, 207, 205, 187, 83,\n 201, 18, 181, 91, 182, 180, 90, 181, 16, 85, 17, 205, 206, 36, 176, 148, 140, 165, 92, 39, 245, 193, 244, 27, 159, 28, 30, 247, 161, 174, 236, 196,\n 103, 54, 104, 55, 193, 8, 111, 117, 31, 221, 189, 55, 240, 98, 99, 142, 126, 100, 219, 166, 218, 112, 155, 26, 198, 209, 131, 169, 135, 150, 114, 47,\n 217, 224, 223, 53, 220, 45, 134, 32, 211, 140, 109, 67, 108, 146, 43, 91, 231, 230, 120, 113, 226, 247, 105, 63, 52, 241, 238, 242, 124, 46, 156, 95,\n 78, 96, 70, 46, 63, 116, 143, 227, 116, 123, 111, 1, 44, 19, 3, 236, 51, 207, 216, 205, 26, 154, 22, 165, 39, 167, 199, 200, 208, 101, 36, 100, 43,\n 57, 202, 242, 20, 99, 56, 28, 157, 124, 35, 113, 29, 160, 27, 211, 204, 210, 124, 113, 46, 106, 43, 204, 96, 62, 77, 227, 137, 116, 73, 41, 72, 36, 203,\n 142, 235, 64, 240, 48, 49, 64, 42, 41, 74, 214, 212, 207, 183, 42, 184, 210, 169, 211, 140, 170, 176, 104, 105, 69, 193, 122, 168, 50, 123, 187, 89, 96,\n 90, 66, 65, 107, 179, 89, 180, 119, 101, 120, 68, 63, 104, 234, 93, 227, 16, 15, 85, 209, 129, 49, 15, 14, 86, 107, 55, 9, 120, 100, 121, 153, 145, 22,\n 178, 88, 179, 197, 6, 196, 89, 88, 96, 135, 138, 136, 138, 215, 172, 218, 115, 219, 41, 42, 81, 5, 195, 51, 57, 43, 61, 208, 171, 199, 41, 81, 38,\n 224, 53, 225, 24, 144, 110, 105, 52, 66, 118, 229, 117, 227, 34, 234, 66, 107, 69, 10, 109, 151, 219, 48, 235, 183, 62, 191, 142, 129, 126, 116, 111,\n 143, 7, 163, 246, 118, 117, 50, 223, 222, 52, 94, 19, 141, 222, 221, 65, 196, 3, 197, 45, 220, 44, 156, 70, 139, 188, 122, 245, 139, 71, 162, 145,\n 153, 159, 149, 170, 150, 122, 188, 196, 206, 216, 92, 163, 144, 161, 164, 2, 167, 242, 141, 241, 0, 164, 37, 11, 72, 12, 144, 145, 160, 12, 38, 13, 70,\n 63, 71, 31, 226, 111, 157, 158, 154, 36, 101, 205, 203, 206, 165, 126, 209, 217, 98, 165, 97, 237, 220, 218, 237, 239, 241, 210, 214, 169, 140, 171, 32,\n 241, 125, 237, 179, 86, 178, 180, 85, 179, 181, 84, 180, 182, 83, 181, 194, 201, 182, 177, 137, 132, 184, 76, 183, 185, 61, 184, 186, 57, 185, 216, 212,\n 186, 192, 214, 187, 139, 34, 156, 218, 79, 237, 147, 123, 177, 45, 44, 4, 208, 201, 32, 98, 64, 129, 192, 213, 138, 235, 59, 219, 141, 242, 97, 97, 2,\n 141, 240, 75, 235, 229, 24, 228, 31, 25, 226, 230, 23, 229, 231, 22, 230, 232, 26, 231, 233, 112, 232, 244, 189, 243, 189, 221, 190, 222, 28, 221,\n 223, 27, 222, 224, 29, 223, 225, 30, 224, 113, 247, 225, 99, 60, 240, 213, 147, 215, 60, 20, 166, 192, 187, 213, 243, 112, 244, 244, 233, 245, 245,\n 128, 188, 188, 114, 174, 134, 131, 220, 174, 217, 236, 236, 198, 134, 215, 177, 58, 156, 143, 124, 25, 110, 7, 31, 228, 25, 264, 356, 368, 0, 11, 267,\n 451, 452, 349, 267, 302, 269, 350, 357, 277, 350, 452, 357, 299, 333, 297, 396, 175, 377, 381, 384, 382, 280, 347, 330, 269, 303, 270, 151, 9, 337,\n 344, 278, 360, 424, 418, 431, 270, 304, 409, 272, 310, 407, 322, 270, 410, 449, 450, 347, 432, 422, 434, 18, 313, 17, 291, 306, 375, 259, 387, 260,\n 424, 335, 418, 434, 364, 416, 391, 423, 327, 301, 251, 298, 275, 281, 4, 254, 373, 253, 375, 307, 321, 280, 425, 411, 200, 421, 18, 335, 321, 406,\n 321, 320, 405, 314, 315, 17, 423, 426, 266, 396, 377, 369, 270, 322, 269, 413, 417, 464, 385, 386, 258, 248, 456, 419, 298, 284, 333, 168, 417, 8,\n 448, 346, 261, 417, 413, 285, 326, 327, 328, 277, 355, 329, 309, 392, 438, 381, 382, 256, 279, 429, 360, 365, 364, 379, 355, 277, 437, 282, 443, 283,\n 281, 275, 363, 395, 431, 369, 299, 297, 337, 335, 273, 321, 348, 450, 349, 359, 446, 467, 283, 293, 282, 250, 458, 462, 300, 276, 383, 292, 308, 325,\n 283, 276, 293, 264, 372, 447, 346, 352, 340, 354, 274, 19, 363, 456, 281, 426, 436, 425, 380, 381, 252, 267, 269, 393, 421, 200, 428, 371, 266, 329,\n 432, 287, 422, 290, 250, 328, 385, 258, 384, 446, 265, 342, 386, 387, 257, 422, 424, 430, 445, 342, 276, 422, 273, 424, 306, 292, 307, 352, 366, 345,\n 268, 271, 302, 358, 423, 371, 327, 294, 460, 331, 279, 294, 303, 271, 304, 436, 432, 427, 304, 272, 408, 395, 394, 431, 378, 395, 400, 296, 334, 299,\n 6, 351, 168, 376, 352, 411, 307, 325, 320, 285, 295, 336, 320, 319, 404, 329, 330, 349, 334, 293, 333, 366, 323, 447, 316, 15, 315, 331, 358, 279,\n 317, 14, 316, 8, 285, 9, 277, 329, 350, 253, 374, 252, 319, 318, 403, 351, 6, 419, 324, 318, 325, 397, 367, 365, 288, 435, 397, 278, 344, 439, 310,\n 272, 311, 248, 195, 281, 375, 273, 291, 175, 396, 199, 312, 311, 268, 276, 283, 445, 390, 373, 339, 295, 282, 296, 448, 449, 346, 356, 264, 454, 337,\n 336, 299, 337, 338, 151, 294, 278, 455, 308, 292, 415, 429, 358, 355, 265, 340, 372, 388, 390, 466, 352, 346, 280, 295, 442, 282, 354, 19, 370, 285,\n 441, 295, 195, 248, 197, 457, 440, 274, 301, 300, 368, 417, 351, 465, 251, 301, 389, 385, 380, 386, 394, 395, 379, 399, 412, 419, 410, 436, 322, 387,\n 373, 388, 326, 2, 393, 354, 370, 461, 393, 164, 267, 268, 302, 12, 386, 374, 387, 312, 268, 13, 298, 293, 301, 265, 446, 340, 380, 385, 381, 280, 330,\n 425, 322, 426, 391, 420, 429, 437, 393, 391, 326, 344, 440, 438, 458, 459, 461, 364, 434, 394, 428, 396, 262, 274, 354, 457, 317, 316, 402, 316, 315,\n 403, 315, 314, 404, 314, 313, 405, 313, 421, 406, 323, 366, 361, 292, 306, 407, 306, 291, 408, 291, 287, 409, 287, 432, 410, 427, 434, 411, 372, 264,\n 383, 459, 309, 457, 366, 352, 401, 1, 274, 4, 418, 421, 262, 331, 294, 358, 435, 433, 367, 392, 289, 439, 328, 462, 326, 94, 2, 370, 289, 305, 455, 339,\n 254, 448, 359, 255, 446, 254, 253, 449, 253, 252, 450, 252, 256, 451, 256, 341, 452, 414, 413, 463, 286, 441, 414, 286, 258, 441, 258, 257, 442, 257,\n 259, 443, 259, 260, 444, 260, 467, 445, 309, 459, 250, 305, 289, 290, 305, 290, 460, 401, 376, 435, 309, 250, 392, 376, 411, 433, 453, 341, 464, 357,\n 453, 465, 343, 357, 412, 437, 343, 399, 344, 360, 440, 420, 437, 456, 360, 420, 363, 361, 401, 288, 265, 372, 353, 390, 339, 249, 339, 448, 255];\n\nexport const TRI68: number[] = [0, 1, 36, 0, 36, 17, 1, 2, 41, 1, 41, 36, 2, 3, 31, 2, 31, 41, 3, 4, 48, 3, 48, 31, 4, 5, 48, 5, 6, 48, 6, 7, 59, 6, 59, 48, 7, 8, 58, 7, 58, 59,\n 8, 9, 56, 8, 56, 57, 8, 57, 58, 9, 10, 55, 9, 55, 56, 10, 11, 54, 10, 54, 55, 11, 12, 54, 12, 13, 54, 13, 14, 35, 13, 35, 54, 14, 15, 46, 14, 46, 35, 15, 16,\n 45, 15, 45, 46, 16, 26, 45, 17, 36, 18, 18, 37, 19, 18, 36, 37, 19, 38, 20, 19, 37, 38, 20, 39, 21, 20, 38, 39, 21, 39, 27, 22, 42, 23, 22, 27, 42, 23, 43, 24,\n 23, 42, 43, 24, 44, 25, 24, 43, 44, 25, 45, 26, 25, 44, 45, 27, 39, 28, 27, 28, 42, 28, 39, 29, 28, 29, 42, 29, 31, 30, 29, 30, 35, 29, 40, 31, 29, 35, 47, 29,\n 39, 40, 29, 47, 42, 30, 31, 32, 30, 32, 33, 30, 33, 34, 30, 34, 35, 31, 50, 32, 31, 40, 41, 31, 48, 49, 31, 49, 50, 32, 51, 33, 32, 50, 51, 33, 51, 34, 34, 52,\n 35, 34, 51, 52, 35, 46, 47, 35, 52, 53, 35, 53, 54, 36, 41, 37, 37, 40, 38, 37, 41, 40, 38, 40, 39, 42, 47, 43, 43, 47, 44, 44, 46, 45, 44, 47, 46, 48, 60, 49,\n 48, 59, 60, 49, 61, 50, 49, 60, 61, 50, 62, 51, 50, 61, 62, 51, 62, 52, 52, 63, 53, 52, 62, 63, 53, 64, 54, 53, 63, 64, 54, 64, 55, 55, 65, 56, 55, 64, 65, 56,\n 66, 57, 56, 65, 66, 57, 66, 58, 58, 67, 59, 58, 66, 67, 59, 67, 60, 60, 67, 61, 61, 66, 62, 61, 67, 66, 62, 66, 63, 63, 65, 64, 63, 66, 65, 21, 27, 22];\n\nexport const TRI33: number[] = [\n /* eyes */ 0, 8, 7, 7, 8, 1, 2, 10, 9, 9, 10, 3,\n /* brows */ 17, 0, 18, 18, 0, 7, 18, 7, 19, 19, 7, 1, 19, 1, 11, 19, 11, 20, 21, 3, 22, 21, 9, 3, 20, 9, 21, 20, 2, 9, 20, 11, 2,\n /* 4head */ 23, 17, 18, 25, 21, 22, 24, 19, 20, 24, 18, 19, 24, 20, 21, 24, 23, 18, 24, 21, 25,\n /* nose */ 11, 12, 4, 11, 4, 13, 1, 12, 11, 11, 13, 2, 12, 14, 4, 4, 14, 13,\n /* up-lip */ 14, 5, 15, 14, 15, 6, 12, 5, 14, 14, 6, 13,\n /* cheeks */ 8, 12, 1, 2, 13, 10, 8, 26, 12, 10, 13, 27, 26, 5, 12, 13, 6, 27, 0, 26, 8, 10, 27, 3,\n /* chin */ 5, 32, 16, 16, 32, 6, 5, 30, 32, 6, 32, 31,\n /* cont */ 26, 30, 5, 27, 6, 31, 0, 28, 26, 3, 27, 29, 17, 28, 0, 3, 29, 22, 23, 28, 17, 22, 29, 25, 28, 30, 26, 27, 31, 29,\n];\n\nexport const TRI7: number[] = [0, 4, 1, 2, 4, 3, 4, 5, 6];\n\nexport const VTX68: number[] = [\n /* cont */ 127, 234, 132, 58, 172, 150, 149, 148, 152, 377, 378, 379, 397, 288, 361, 454, 356,\n /* brows */ 70, 63, 105, 66, 107, 336, 296, 334, 293, 300,\n /* nose */ 168, 6, 195, 4, 98, 97, 2, 326, 327,\n /* eyes */ 33, 160, 158, 133, 153, 144, 362, 385, 387, 263, 373, 380,\n /* lip */ 57, 40, 37, 0, 267, 270, 287, 321, 314, 17, 84, 91,\n /* mouth */ 78, 81, 13, 311, 308, 402, 14, 178,\n];\n\nexport const VTX33: number[] = [33, 133, 362, 263, 1, 62, 308, 159, 145, 386, 374, 6, 102, 331, 2, 13, 14, 70, 105, 107, 336, 334, 300, 54, 10, 284, 50, 280, 234, 454, 58, 288, 152];\n\nexport const VTX7: number[] = [33, 133, 362, 263, 1, 78, 308];\n\nexport const UV68 = VTX68.map((x) => UV468[x]);\n\nexport const UV33 = VTX33.map((x) => UV468[x]);\n\nexport const UV7 = VTX7.map((x) => UV468[x]);\n\n// https://github.com/tensorflow/tfjs-models/blob/master/face-landmarks-detection/src/constants.ts\n// https://github.com/google/mediapipe/mediapipe/python/solutions/face_mesh_connections.py\n\ntype PairArray = [number, number][];\n\nfunction connectionsToIndices(connections: PairArray) {\n const indices = connections.map((connection) => connection[0]);\n indices.push(connections[connections.length - 1][1]);\n return indices;\n}\n\nexport const pairsLips: PairArray = [\n [61, 146], [146, 91], [91, 181], [181, 84], [84, 17], [17, 314], [314, 405], [405, 321], [321, 375], [375, 291], [61, 185], [185, 40], [40, 39], [39, 37], [37, 0], [0, 267], [267, 269], [269, 270], [270, 409], [409, 291],\n [78, 95], [95, 88], [88, 178], [178, 87], [87, 14], [14, 317], [317, 402], [402, 318], [318, 324], [324, 308], [78, 191], [191, 80], [80, 81], [81, 82], [82, 13], [13, 312], [312, 311], [311, 310], [310, 415], [415, 308],\n];\n\nexport const pairsLeftEye: PairArray = [[263, 249], [249, 390], [390, 373], [373, 374], [374, 380], [380, 381], [381, 382], [382, 362], [263, 466], [466, 388], [388, 387], [387, 386], [386, 385], [385, 384], [384, 398], [398, 362]];\n\nexport const pairsLeftEyebrow: PairArray = [[276, 283], [283, 282], [282, 295], [295, 285], [300, 293], [293, 334], [334, 296], [296, 336]];\n\nexport const pairsLeftIris: PairArray = [[474, 475], [475, 476], [476, 477], [477, 474]];\n\nexport const pairsRightEye: PairArray = [[33, 7], [7, 163], [163, 144], [144, 145], [145, 153], [153, 154], [154, 155], [155, 133], [33, 246], [246, 161], [161, 160], [160, 159], [159, 158], [158, 157], [157, 173], [173, 133]];\n\nexport const pairsRightEyebrow: PairArray = [[46, 53], [53, 52], [52, 65], [65, 55], [70, 63], [63, 105], [105, 66], [66, 107]];\n\nexport const pairsRightIris: PairArray = [[469, 470], [470, 471], [471, 472], [472, 469]];\n\nexport const pairsFaceContour: PairArray = [\n [10, 338], [338, 297], [297, 332], [332, 284], [284, 251], [251, 389],\n [389, 356], [356, 454], [454, 323], [323, 361], [361, 288], [288, 397],\n [397, 365], [365, 379], [379, 378], [378, 400], [400, 377], [377, 152],\n [152, 148], [148, 176], [176, 149], [149, 150], [150, 136], [136, 172],\n [172, 58], [58, 132], [132, 93], [93, 234], [234, 127], [127, 162],\n [162, 21], [21, 54], [54, 103], [103, 67], [67, 109], [109, 10],\n];\n\nexport const contourKeypoints = {\n lips: connectionsToIndices(pairsLips),\n leftEye: connectionsToIndices(pairsLeftEye),\n leftEyebrow: connectionsToIndices(pairsLeftEyebrow),\n leftIris: connectionsToIndices(pairsLeftIris),\n rightEye: connectionsToIndices(pairsRightEye),\n rightEyebrow: connectionsToIndices(pairsRightEyebrow),\n rightIris: connectionsToIndices(pairsRightIris),\n faceOval: connectionsToIndices(pairsFaceContour),\n};\n\nexport const pairsFaceMesh: PairArray = [\n [127, 34], [34, 139], [139, 127], [11, 0], [0, 37], [37, 11],\n [232, 231], [231, 120], [120, 232], [72, 37], [37, 39], [39, 72],\n [128, 121], [121, 47], [47, 128], [232, 121], [121, 128], [128, 232],\n [104, 69], [69, 67], [67, 104], [175, 171], [171, 148], [148, 175],\n [118, 50], [50, 101], [101, 118], [73, 39], [39, 40], [40, 73],\n [9, 151], [151, 108], [108, 9], [48, 115], [115, 131], [131, 48],\n [194, 204], [204, 211], [211, 194], [74, 40], [40, 185], [185, 74],\n [80, 42], [42, 183], [183, 80], [40, 92], [92, 186], [186, 40],\n [230, 229], [229, 118], [118, 230], [202, 212], [212, 214], [214, 202],\n [83, 18], [18, 17], [17, 83], [76, 61], [61, 146], [146, 76],\n [160, 29], [29, 30], [30, 160], [56, 157], [157, 173], [173, 56],\n [106, 204], [204, 194], [194, 106], [135, 214], [214, 192], [192, 135],\n [203, 165], [165, 98], [98, 203], [21, 71], [71, 68], [68, 21],\n [51, 45], [45, 4], [4, 51], [144, 24], [24, 23], [23, 144],\n [77, 146], [146, 91], [91, 77], [205, 50], [50, 187], [187, 205],\n [201, 200], [200, 18], [18, 201], [91, 106], [106, 182], [182, 91],\n [90, 91], [91, 181], [181, 90], [85, 84], [84, 17], [17, 85],\n [206, 203], [203, 36], [36, 206], [148, 171], [171, 140], [140, 148],\n [92, 40], [40, 39], [39, 92], [193, 189], [189, 244], [244, 193],\n [159, 158], [158, 28], [28, 159], [247, 246], [246, 161], [161, 247],\n [236, 3], [3, 196], [196, 236], [54, 68], [68, 104], [104, 54],\n [193, 168], [168, 8], [8, 193], [117, 228], [228, 31], [31, 117],\n [189, 193], [193, 55], [55, 189], [98, 97], [97, 99], [99, 98],\n [126, 47], [47, 100], [100, 126], [166, 79], [79, 218], [218, 166],\n [155, 154], [154, 26], [26, 155], [209, 49], [49, 131], [131, 209],\n [135, 136], [136, 150], [150, 135], [47, 126], [126, 217], [217, 47],\n [223, 52], [52, 53], [53, 223], [45, 51], [51, 134], [134, 45],\n [211, 170], [170, 140], [140, 211], [67, 69], [69, 108], [108, 67],\n [43, 106], [106, 91], [91, 43], [230, 119], [119, 120], [120, 230],\n [226, 130], [130, 247], [247, 226], [63, 53], [53, 52], [52, 63],\n [238, 20], [20, 242], [242, 238], [46, 70], [70, 156], [156, 46],\n [78, 62], [62, 96], [96, 78], [46, 53], [53, 63], [63, 46],\n [143, 34], [34, 227], [227, 143], [123, 117], [117, 111], [111, 123],\n [44, 125], [125, 19], [19, 44], [236, 134], [134, 51], [51, 236],\n [216, 206], [206, 205], [205, 216], [154, 153], [153, 22], [22, 154],\n [39, 37], [37, 167], [167, 39], [200, 201], [201, 208], [208, 200],\n [36, 142], [142, 100], [100, 36], [57, 212], [212, 202], [202, 57],\n [20, 60], [60, 99], [99, 20], [28, 158], [158, 157], [157, 28],\n [35, 226], [226, 113], [113, 35], [160, 159], [159, 27], [27, 160],\n [204, 202], [202, 210], [210, 204], [113, 225], [225, 46], [46, 113],\n [43, 202], [202, 204], [204, 43], [62, 76], [76, 77], [77, 62],\n [137, 123], [123, 116], [116, 137], [41, 38], [38, 72], [72, 41],\n [203, 129], [129, 142], [142, 203], [64, 98], [98, 240], [240, 64],\n [49, 102], [102, 64], [64, 49], [41, 73], [73, 74], [74, 41],\n [212, 216], [216, 207], [207, 212], [42, 74], [74, 184], [184, 42],\n [169, 170], [170, 211], [211, 169], [170, 149], [149, 176], [176, 170],\n [105, 66], [66, 69], [69, 105], [122, 6], [6, 168], [168, 122],\n [123, 147], [147, 187], [187, 123], [96, 77], [77, 90], [90, 96],\n [65, 55], [55, 107], [107, 65], [89, 90], [90, 180], [180, 89],\n [101, 100], [100, 120], [120, 101], [63, 105], [105, 104], [104, 63],\n [93, 137], [137, 227], [227, 93], [15, 86], [86, 85], [85, 15],\n [129, 102], [102, 49], [49, 129], [14, 87], [87, 86], [86, 14],\n [55, 8], [8, 9], [9, 55], [100, 47], [47, 121], [121, 100],\n [145, 23], [23, 22], [22, 145], [88, 89], [89, 179], [179, 88],\n [6, 122], [122, 196], [196, 6], [88, 95], [95, 96], [96, 88],\n [138, 172], [172, 136], [136, 138], [215, 58], [58, 172], [172, 215],\n [115, 48], [48, 219], [219, 115], [42, 80], [80, 81], [81, 42],\n [195, 3], [3, 51], [51, 195], [43, 146], [146, 61], [61, 43],\n [171, 175], [175, 199], [199, 171], [81, 82], [82, 38], [38, 81],\n [53, 46], [46, 225], [225, 53], [144, 163], [163, 110], [110, 144],\n [52, 65], [65, 66], [66, 52], [229, 228], [228, 117], [117, 229],\n [34, 127], [127, 234], [234, 34], [107, 108], [108, 69], [69, 107],\n [109, 108], [108, 151], [151, 109], [48, 64], [64, 235], [235, 48],\n [62, 78], [78, 191], [191, 62], [129, 209], [209, 126], [126, 129],\n [111, 35], [35, 143], [143, 111], [117, 123], [123, 50], [50, 117],\n [222, 65], [65, 52], [52, 222], [19, 125], [125, 141], [141, 19],\n [221, 55], [55, 65], [65, 221], [3, 195], [195, 197], [197, 3],\n [25, 7], [7, 33], [33, 25], [220, 237], [237, 44], [44, 220],\n [70, 71], [71, 139], [139, 70], [122, 193], [193, 245], [245, 122],\n [247, 130], [130, 33], [33, 247], [71, 21], [21, 162], [162, 71],\n [170, 169], [169, 150], [150, 170], [188, 174], [174, 196], [196, 188],\n [216, 186], [186, 92], [92, 216], [2, 97], [97, 167], [167, 2],\n [141, 125], [125, 241], [241, 141], [164, 167], [167, 37], [37, 164],\n [72, 38], [38, 12], [12, 72], [38, 82], [82, 13], [13, 38],\n [63, 68], [68, 71], [71, 63], [226, 35], [35, 111], [111, 226],\n [101, 50], [50, 205], [205, 101], [206, 92], [92, 165], [165, 206],\n [209, 198], [198, 217], [217, 209], [165, 167], [167, 97], [97, 165],\n [220, 115], [115, 218], [218, 220], [133, 112], [112, 243], [243, 133],\n [239, 238], [238, 241], [241, 239], [214, 135], [135, 169], [169, 214],\n [190, 173], [173, 133], [133, 190], [171, 208], [208, 32], [32, 171],\n [125, 44], [44, 237], [237, 125], [86, 87], [87, 178], [178, 86],\n [85, 86], [86, 179], [179, 85], [84, 85], [85, 180], [180, 84],\n [83, 84], [84, 181], [181, 83], [201, 83], [83, 182], [182, 201],\n [137, 93], [93, 132], [132, 137], [76, 62], [62, 183], [183, 76],\n [61, 76], [76, 184], [184, 61], [57, 61], [61, 185], [185, 57],\n [212, 57], [57, 186], [186, 212], [214, 207], [207, 187], [187, 214],\n [34, 143], [143, 156], [156, 34], [79, 239], [239, 237], [237, 79],\n [123, 137], [137, 177], [177, 123], [44, 1], [1, 4], [4, 44],\n [201, 194], [194, 32], [32, 201], [64, 102], [102, 129], [129, 64],\n [213, 215], [215, 138], [138, 213], [59, 166], [166, 219], [219, 59],\n [242, 99], [99, 97], [97, 242], [2, 94], [94, 141], [141, 2],\n [75, 59], [59, 235], [235, 75], [24, 110], [110, 228], [228, 24],\n [25, 130], [130, 226], [226, 25], [23, 24], [24, 229], [229, 23],\n [22, 23], [23, 230], [230, 22], [26, 22], [22, 231], [231, 26],\n [112, 26], [26, 232], [232, 112], [189, 190], [190, 243], [243, 189],\n [221, 56], [56, 190], [190, 221], [28, 56], [56, 221], [221, 28],\n [27, 28], [28, 222], [222, 27], [29, 27], [27, 223], [223, 29],\n [30, 29], [29, 224], [224, 30], [247, 30], [30, 225], [225, 247],\n [238, 79], [79, 20], [20, 238], [166, 59], [59, 75], [75, 166],\n [60, 75], [75, 240], [240, 60], [147, 177], [177, 215], [215, 147],\n [20, 79], [79, 166], [166, 20], [187, 147], [147, 213], [213, 187],\n [112, 233], [233, 244], [244, 112], [233, 128], [128, 245], [245, 233],\n [128, 114], [114, 188], [188, 128], [114, 217], [217, 174], [174, 114],\n [131, 115], [115, 220], [220, 131], [217, 198], [198, 236], [236, 217],\n [198, 131], [131, 134], [134, 198], [177, 132], [132, 58], [58, 177],\n [143, 35], [35, 124], [124, 143], [110, 163], [163, 7], [7, 110],\n [228, 110], [110, 25], [25, 228], [356, 389], [389, 368], [368, 356],\n [11, 302], [302, 267], [267, 11], [452, 350], [350, 349], [349, 452],\n [302, 303], [303, 269], [269, 302], [357, 343], [343, 277], [277, 357],\n [452, 453], [453, 357], [357, 452], [333, 332], [332, 297], [297, 333],\n [175, 152], [152, 377], [377, 175], [347, 348], [348, 330], [330, 347],\n [303, 304], [304, 270], [270, 303], [9, 336], [336, 337], [337, 9],\n [278, 279], [279, 360], [360, 278], [418, 262], [262, 431], [431, 418],\n [304, 408], [408, 409], [409, 304], [310, 415], [415, 407], [407, 310],\n [270, 409], [409, 410], [410, 270], [450, 348], [348, 347], [347, 450],\n [422, 430], [430, 434], [434, 422], [313, 314], [314, 17], [17, 313],\n [306, 307], [307, 375], [375, 306], [387, 388], [388, 260], [260, 387],\n [286, 414], [414, 398], [398, 286], [335, 406], [406, 418], [418, 335],\n [364, 367], [367, 416], [416, 364], [423, 358], [358, 327], [327, 423],\n [251, 284], [284, 298], [298, 251], [281, 5], [5, 4], [4, 281],\n [373, 374], [374, 253], [253, 373], [307, 320], [320, 321], [321, 307],\n [425, 427], [427, 411], [411, 425], [421, 313], [313, 18], [18, 421],\n [321, 405], [405, 406], [406, 321], [320, 404], [404, 405], [405, 320],\n [315, 16], [16, 17], [17, 315], [426, 425], [425, 266], [266, 426],\n [377, 400], [400, 369], [369, 377], [322, 391], [391, 269], [269, 322],\n [417, 465], [465, 464], [464, 417], [386, 257], [257, 258], [258, 386],\n [466, 260], [260, 388], [388, 466], [456, 399], [399, 419], [419, 456],\n [284, 332], [332, 333], [333, 284], [417, 285], [285, 8], [8, 417],\n [346, 340], [340, 261], [261, 346], [413, 441], [441, 285], [285, 413],\n [327, 460], [460, 328], [328, 327], [355, 371], [371, 329], [329, 355],\n [392, 439], [439, 438], [438, 392], [382, 341], [341, 256], [256, 382],\n [429, 420], [420, 360], [360, 429], [364, 394], [394, 379], [379, 364],\n [277, 343], [343, 437], [437, 277], [443, 444], [444, 283], [283, 443],\n [275, 440], [440, 363], [363, 275], [431, 262], [262, 369], [369, 431],\n [297, 338], [338, 337], [337, 297], [273, 375], [375, 321], [321, 273],\n [450, 451], [451, 349], [349, 450], [446, 342], [342, 467], [467, 446],\n [293, 334], [334, 282], [282, 293], [458, 461], [461, 462], [462, 458],\n [276, 353], [353, 383], [383, 276], [308, 324], [324, 325], [325, 308],\n [276, 300], [300, 293], [293, 276], [372, 345], [345, 447], [447, 372],\n [352, 345], [345, 340], [340, 352], [274, 1], [1, 19], [19, 274],\n [456, 248], [248, 281], [281, 456], [436, 427], [427, 425], [425, 436],\n [381, 256], [256, 252], [252, 381], [269, 391], [391, 393], [393, 269],\n [200, 199], [199, 428], [428, 200], [266, 330], [330, 329], [329, 266],\n [287, 273], [273, 422], [422, 287], [250, 462], [462, 328], [328, 250],\n [258, 286], [286, 384], [384, 258], [265, 353], [353, 342], [342, 265],\n [387, 259], [259, 257], [257, 387], [424, 431], [431, 430], [430, 424],\n [342, 353], [353, 276], [276, 342], [273, 335], [335, 424], [424, 273],\n [292, 325], [325, 307], [307, 292], [366, 447], [447, 345], [345, 366],\n [271, 303], [303, 302], [302, 271], [423, 266], [266, 371], [371, 423],\n [294, 455], [455, 460], [460, 294], [279, 278], [278, 294], [294, 279],\n [271, 272], [272, 304], [304, 271], [432, 434], [434, 427], [427, 432],\n [272, 407], [407, 408], [408, 272], [394, 430], [430, 431], [431, 394],\n [395, 369], [369, 400], [400, 395], [334, 333], [333, 299], [299, 334],\n [351, 417], [417, 168], [168, 351], [352, 280], [280, 411], [411, 352],\n [325, 319], [319, 320], [320, 325], [295, 296], [296, 336], [336, 295],\n [319, 403], [403, 404], [404, 319], [330, 348], [348, 349], [349, 330],\n [293, 298], [298, 333], [333, 293], [323, 454], [454, 447], [447, 323],\n [15, 16], [16, 315], [315, 15], [358, 429], [429, 279], [279, 358],\n [14, 15], [15, 316], [316, 14], [285, 336], [336, 9], [9, 285],\n [329, 349], [349, 350], [350, 329], [374, 380], [380, 252], [252, 374],\n [318, 402], [402, 403], [403, 318], [6, 197], [197, 419], [419, 6],\n [318, 319], [319, 325], [325, 318], [367, 364], [364, 365], [365, 367],\n [435, 367], [367, 397], [397, 435], [344, 438], [438, 439], [439, 344],\n [272, 271], [271, 311], [311, 272], [195, 5], [5, 281], [281, 195],\n [273, 287], [287, 291], [291, 273], [396, 428], [428, 199], [199, 396],\n [311, 271], [271, 268], [268, 311], [283, 444], [444, 445], [445, 283],\n [373, 254], [254, 339], [339, 373], [282, 334], [334, 296], [296, 282],\n [449, 347], [347, 346], [346, 449], [264, 447], [447, 454], [454, 264],\n [336, 296], [296, 299], [299, 336], [338, 10], [10, 151], [151, 338],\n [278, 439], [439, 455], [455, 278], [292, 407], [407, 415], [415, 292],\n [358, 371], [371, 355], [355, 358], [340, 345], [345, 372], [372, 340],\n [346, 347], [347, 280], [280, 346], [442, 443], [443, 282], [282, 442],\n [19, 94], [94, 370], [370, 19], [441, 442], [442, 295], [295, 441],\n [248, 419], [419, 197], [197, 248], [263, 255], [255, 359], [359, 263],\n [440, 275], [275, 274], [274, 440], [300, 383], [383, 368], [368, 300],\n [351, 412], [412, 465], [465, 351], [263, 467], [467, 466], [466, 263],\n [301, 368], [368, 389], [389, 301], [395, 378], [378, 379], [379, 395],\n [412, 351], [351, 419], [419, 412], [436, 426], [426, 322], [322, 436],\n [2, 164], [164, 393], [393, 2], [370, 462], [462, 461], [461, 370],\n [164, 0], [0, 267], [267, 164], [302, 11], [11, 12], [12, 302],\n [268, 12], [12, 13], [13, 268], [293, 300], [300, 301], [301, 293],\n [446, 261], [261, 340], [340, 446], [330, 266], [266, 425], [425, 330],\n [426, 423], [423, 391], [391, 426], [429, 355], [355, 437], [437, 429],\n [391, 327], [327, 326], [326, 391], [440, 457], [457, 438], [438, 440],\n [341, 382], [382, 362], [362, 341], [459, 457], [457, 461], [461, 459],\n [434, 430], [430, 394], [394, 434], [414, 463], [463, 362], [362, 414],\n [396, 369], [369, 262], [262, 396], [354, 461], [461, 457], [457, 354],\n [316, 403], [403, 402], [402, 316], [315, 404], [404, 403], [403, 315],\n [314, 405], [405, 404], [404, 314], [313, 406], [406, 405], [405, 313],\n [421, 418], [418, 406], [406, 421], [366, 401], [401, 361], [361, 366],\n [306, 408], [408, 407], [407, 306], [291, 409], [409, 408], [408, 291],\n [287, 410], [410, 409], [409, 287], [432, 436], [436, 410], [410, 432],\n [434, 416], [416, 411], [411, 434], [264, 368], [368, 383], [383, 264],\n [309, 438], [438, 457], [457, 309], [352, 376], [376, 401], [401, 352],\n [274, 275], [275, 4], [4, 274], [421, 428], [428, 262], [262, 421],\n [294, 327], [327, 358], [358, 294], [433, 416], [416, 367], [367, 433],\n [289, 455], [455, 439], [439, 289], [462, 370], [370, 326], [326, 462],\n [2, 326], [326, 370], [370, 2], [305, 460], [460, 455], [455, 305],\n [254, 449], [449, 448], [448, 254], [255, 261], [261, 446], [446, 255],\n [253, 450], [450, 449], [449, 253], [252, 451], [451, 450], [450, 252],\n [256, 452], [452, 451], [451, 256], [341, 453], [453, 452], [452, 341],\n [413, 464], [464, 463], [463, 413], [441, 413], [413, 414], [414, 441],\n [258, 442], [442, 441], [441, 258], [257, 443], [443, 442], [442, 257],\n [259, 444], [444, 443], [443, 259], [260, 445], [445, 444], [444, 260],\n [467, 342], [342, 445], [445, 467], [459, 458], [458, 250], [250, 459],\n [289, 392], [392, 290], [290, 289], [290, 328], [328, 460], [460, 290],\n [376, 433], [433, 435], [435, 376], [250, 290], [290, 392], [392, 250],\n [411, 416], [416, 433], [433, 411], [341, 463], [463, 464], [464, 341],\n [453, 464], [464, 465], [465, 453], [357, 465], [465, 412], [412, 357],\n [343, 412], [412, 399], [399, 343], [360, 363], [363, 440], [440, 360],\n [437, 399], [399, 456], [456, 437], [420, 456], [456, 363], [363, 420],\n [401, 435], [435, 288], [288, 401], [372, 383], [383, 353], [353, 372],\n [339, 255], [255, 249], [249, 339], [448, 261], [261, 255], [255, 448],\n [133, 243], [243, 190], [190, 133], [133, 155], [155, 112], [112, 133],\n [33, 246], [246, 247], [247, 33], [33, 130], [130, 25], [25, 33],\n [398, 384], [384, 286], [286, 398], [362, 398], [398, 414], [414, 362],\n [362, 463], [463, 341], [341, 362], [263, 359], [359, 467], [467, 263],\n [263, 249], [249, 255], [255, 263], [466, 467], [467, 260], [260, 466],\n [75, 60], [60, 166], [166, 75], [238, 239], [239, 79], [79, 238],\n [162, 127], [127, 139], [139, 162], [72, 11], [11, 37], [37, 72],\n [121, 232], [232, 120], [120, 121], [73, 72], [72, 39], [39, 73],\n [114, 128], [128, 47], [47, 114], [233, 232], [232, 128], [128, 233],\n [103, 104], [104, 67], [67, 103], [152, 175], [175, 148], [148, 152],\n [119, 118], [118, 101], [101, 119], [74, 73], [73, 40], [40, 74],\n [107, 9], [9, 108], [108, 107], [49, 48], [48, 131], [131, 49],\n [32, 194], [194, 211], [211, 32], [184, 74], [74, 185], [185, 184],\n [191, 80], [80, 183], [183, 191], [185, 40], [40, 186], [186, 185],\n [119, 230], [230, 118], [118, 119], [210, 202], [202, 214], [214, 210],\n [84, 83], [83, 17], [17, 84], [77, 76], [76, 146], [146, 77],\n [161, 160], [160, 30], [30, 161], [190, 56], [56, 173], [173, 190],\n [182, 106], [106, 194], [194, 182], [138, 135], [135, 192], [192, 138],\n [129, 203], [203, 98], [98, 129], [54, 21], [21, 68], [68, 54],\n [5, 51], [51, 4], [4, 5], [145, 144], [144, 23], [23, 145],\n [90, 77], [77, 91], [91, 90], [207, 205], [205, 187], [187, 207],\n [83, 201], [201, 18], [18, 83], [181, 91], [91, 182], [182, 181],\n [180, 90], [90, 181], [181, 180], [16, 85], [85, 17], [17, 16],\n [205, 206], [206, 36], [36, 205], [176, 148], [148, 140], [140, 176],\n [165, 92], [92, 39], [39, 165], [245, 193], [193, 244], [244, 245],\n [27, 159], [159, 28], [28, 27], [30, 247], [247, 161], [161, 30],\n [174, 236], [236, 196], [196, 174], [103, 54], [54, 104], [104, 103],\n [55, 193], [193, 8], [8, 55], [111, 117], [117, 31], [31, 111],\n [221, 189], [189, 55], [55, 221], [240, 98], [98, 99], [99, 240],\n [142, 126], [126, 100], [100, 142], [219, 166], [166, 218], [218, 219],\n [112, 155], [155, 26], [26, 112], [198, 209], [209, 131], [131, 198],\n [169, 135], [135, 150], [150, 169], [114, 47], [47, 217], [217, 114],\n [224, 223], [223, 53], [53, 224], [220, 45], [45, 134], [134, 220],\n [32, 211], [211, 140], [140, 32], [109, 67], [67, 108], [108, 109],\n [146, 43], [43, 91], [91, 146], [231, 230], [230, 120], [120, 231],\n [113, 226], [226, 247], [247, 113], [105, 63], [63, 52], [52, 105],\n [241, 238], [238, 242], [242, 241], [124, 46], [46, 156], [156, 124],\n [95, 78], [78, 96], [96, 95], [70, 46], [46, 63], [63, 70],\n [116, 143], [143, 227], [227, 116], [116, 123], [123, 111], [111, 116],\n [1, 44], [44, 19], [19, 1], [3, 236], [236, 51], [51, 3],\n [207, 216], [216, 205], [205, 207], [26, 154], [154, 22], [22, 26],\n [165, 39], [39, 167], [167, 165], [199, 200], [200, 208], [208, 199],\n [101, 36], [36, 100], [100, 101], [43, 57], [57, 202], [202, 43],\n [242, 20], [20, 99], [99, 242], [56, 28], [28, 157], [157, 56],\n [124, 35], [35, 113], [113, 124], [29, 160], [160, 27], [27, 29],\n [211, 204], [204, 210], [210, 211], [124, 113], [113, 46], [46, 124],\n [106, 43], [43, 204], [204, 106], [96, 62], [62, 77], [77, 96],\n [227, 137], [137, 116], [116, 227], [73, 41], [41, 72], [72, 73],\n [36, 203], [203, 142], [142, 36], [235, 64], [64, 240], [240, 235],\n [48, 49], [49, 64], [64, 48], [42, 41], [41, 74], [74, 42],\n [214, 212], [212, 207], [207, 214], [183, 42], [42, 184], [184, 183],\n [210, 169], [169, 211], [211, 210], [140, 170], [170, 176], [176, 140],\n [104, 105], [105, 69], [69, 104], [193, 122], [122, 168], [168, 193],\n [50, 123], [123, 187], [187, 50], [89, 96], [96, 90], [90, 89],\n [66, 65], [65, 107], [107, 66], [179, 89], [89, 180], [180, 179],\n [119, 101], [101, 120], [120, 119], [68, 63], [63, 104], [104, 68],\n [234, 93], [93, 227], [227, 234], [16, 15], [15, 85], [85, 16],\n [209, 129], [129, 49], [49, 209], [15, 14], [14, 86], [86, 15],\n [107, 55], [55, 9], [9, 107], [120, 100], [100, 121], [121, 120],\n [153, 145], [145, 22], [22, 153], [178, 88], [88, 179], [179, 178],\n [197, 6], [6, 196], [196, 197], [89, 88], [88, 96], [96, 89],\n [135, 138], [138, 136], [136, 135], [138, 215], [215, 172], [172, 138],\n [218, 115], [115, 219], [219, 218], [41, 42], [42, 81], [81, 41],\n [5, 195], [195, 51], [51, 5], [57, 43], [43, 61], [61, 57],\n [208, 171], [171, 199], [199, 208], [41, 81], [81, 38], [38, 41],\n [224, 53], [53, 225], [225, 224], [24, 144], [144, 110], [110, 24],\n [105, 52], [52, 66], [66, 105], [118, 229], [229, 117], [117, 118],\n [227, 34], [34, 234], [234, 227], [66, 107], [107, 69], [69, 66],\n [10, 109], [109, 151], [151, 10], [219, 48], [48, 235], [235, 219],\n [183, 62], [62, 191], [191, 183], [142, 129], [129, 126], [126, 142],\n [116, 111], [111, 143], [143, 116], [118, 117], [117, 50], [50, 118],\n [223, 222], [222, 52], [52, 223], [94, 19], [19, 141], [141, 94],\n [222, 221], [221, 65], [65, 222], [196, 3], [3, 197], [197, 196],\n [45, 220], [220, 44], [44, 45], [156, 70], [70, 139], [139, 156],\n [188, 122], [122, 245], [245, 188], [139, 71], [71, 162], [162, 139],\n [149, 170], [170, 150], [150, 149], [122, 188], [188, 196], [196, 122],\n [206, 216], [216, 92], [92, 206], [164, 2], [2, 167], [167, 164],\n [242, 141], [141, 241], [241, 242], [0, 164], [164, 37], [37, 0],\n [11, 72], [72, 12], [12, 11], [12, 38], [38, 13], [13, 12],\n [70, 63], [63, 71], [71, 70], [31, 226], [226, 111], [111, 31],\n [36, 101], [101, 205], [205, 36], [203, 206], [206, 165], [165, 203],\n [126, 209], [209, 217], [217, 126], [98, 165], [165, 97], [97, 98],\n [237, 220], [220, 218], [218, 237], [237, 239], [239, 241], [241, 237],\n [210, 214], [214, 169], [169, 210], [140, 171], [171, 32], [32, 140],\n [241, 125], [125, 237], [237, 241], [179, 86], [86, 178], [178, 179],\n [180, 85], [85, 179], [179, 180], [181, 84], [84, 180], [180, 181],\n [182, 83], [83, 181], [181, 182], [194, 201], [201, 182], [182, 194],\n [177, 137], [137, 132], [132, 177], [184, 76], [76, 183], [183, 184],\n [185, 61], [61, 184], [184, 185], [186, 57], [57, 185], [185, 186],\n [216, 212], [212, 186], [186, 216], [192, 214], [214, 187], [187, 192],\n [139, 34], [34, 156], [156, 139], [218, 79], [79, 237], [237, 218],\n [147, 123], [123, 177], [177, 147], [45, 44], [44, 4], [4, 45],\n [208, 201], [201, 32], [32, 208], [98, 64], [64, 129], [129, 98],\n [192, 213], [213, 138], [138, 192], [235, 59], [59, 219], [219, 235],\n [141, 242], [242, 97], [97, 141], [97, 2], [2, 141], [141, 97],\n [240, 75], [75, 235], [235, 240], [229, 24], [24, 228], [228, 229],\n [31, 25], [25, 226], [226, 31], [230, 23], [23, 229], [229, 230],\n [231, 22], [22, 230], [230, 231], [232, 26], [26, 231], [231, 232],\n [233, 112], [112, 232], [232, 233], [244, 189], [189, 243], [243, 244],\n [189, 221], [221, 190], [190, 189], [222, 28], [28, 221], [221, 222],\n [223, 27], [27, 222], [222, 223], [224, 29], [29, 223], [223, 224],\n [225, 30], [30, 224], [224, 225], [113, 247], [247, 225], [225, 113],\n [99, 60], [60, 240], [240, 99], [213, 147], [147, 215], [215, 213],\n [60, 20], [20, 166], [166, 60], [192, 187], [187, 213], [213, 192],\n [243, 112], [112, 244], [244, 243], [244, 233], [233, 245], [245, 244],\n [245, 128], [128, 188], [188, 245], [188, 114], [114, 174], [174, 188],\n [134, 131], [131, 220], [220, 134], [174, 217], [217, 236], [236, 174],\n [236, 198], [198, 134], [134, 236], [215, 177], [177, 58], [58, 215],\n [156, 143], [143, 124], [124, 156], [25, 110], [110, 7], [7, 25],\n [31, 228], [228, 25], [25, 31], [264, 356], [356, 368], [368, 264],\n [0, 11], [11, 267], [267, 0], [451, 452], [452, 349], [349, 451],\n [267, 302], [302, 269], [269, 267], [350, 357], [357, 277], [277, 350],\n [350, 452], [452, 357], [357, 350], [299, 333], [333, 297], [297, 299],\n [396, 175], [175, 377], [377, 396], [280, 347], [347, 330], [330, 280],\n [269, 303], [303, 270], [270, 269], [151, 9], [9, 337], [337, 151],\n [344, 278], [278, 360], [360, 344], [424, 418], [418, 431], [431, 424],\n [270, 304], [304, 409], [409, 270], [272, 310], [310, 407], [407, 272],\n [322, 270], [270, 410], [410, 322], [449, 450], [450, 347], [347, 449],\n [432, 422], [422, 434], [434, 432], [18, 313], [313, 17], [17, 18],\n [291, 306], [306, 375], [375, 291], [259, 387], [387, 260], [260, 259],\n [424, 335], [335, 418], [418, 424], [434, 364], [364, 416], [416, 434],\n [391, 423], [423, 327], [327, 391], [301, 251], [251, 298], [298, 301],\n [275, 281], [281, 4], [4, 275], [254, 373], [373, 253], [253, 254],\n [375, 307], [307, 321], [321, 375], [280, 425], [425, 411], [411, 280],\n [200, 421], [421, 18], [18, 200], [335, 321], [321, 406], [406, 335],\n [321, 320], [320, 405], [405, 321], [314, 315], [315, 17], [17, 314],\n [423, 426], [426, 266], [266, 423], [396, 377], [377, 369], [369, 396],\n [270, 322], [322, 269], [269, 270], [413, 417], [417, 464], [464, 413],\n [385, 386], [386, 258], [258, 385], [248, 456], [456, 419], [419, 248],\n [298, 284], [284, 333], [333, 298], [168, 417], [417, 8], [8, 168],\n [448, 346], [346, 261], [261, 448], [417, 413], [413, 285], [285, 417],\n [326, 327], [327, 328], [328, 326], [277, 355], [355, 329], [329, 277],\n [309, 392], [392, 438], [438, 309], [381, 382], [382, 256], [256, 381],\n [279, 429], [429, 360], [360, 279], [365, 364], [364, 379], [379, 365],\n [355, 277], [277, 437], [437, 355], [282, 443], [443, 283], [283, 282],\n [281, 275], [275, 363], [363, 281], [395, 431], [431, 369], [369, 395],\n [299, 297], [297, 337], [337, 299], [335, 273], [273, 321], [321, 335],\n [348, 450], [450, 349], [349, 348], [359, 446], [446, 467], [467, 359],\n [283, 293], [293, 282], [282, 283], [250, 458], [458, 462], [462, 250],\n [300, 276], [276, 383], [383, 300], [292, 308], [308, 325], [325, 292],\n [283, 276], [276, 293], [293, 283], [264, 372], [372, 447], [447, 264],\n [346, 352], [352, 340], [340, 346], [354, 274], [274, 19], [19, 354],\n [363, 456], [456, 281], [281, 363], [426, 436], [436, 425], [425, 426],\n [380, 381], [381, 252], [252, 380], [267, 269], [269, 393], [393, 267],\n [421, 200], [200, 428], [428, 421], [371, 266], [266, 329], [329, 371],\n [432, 287], [287, 422], [422, 432], [290, 250], [250, 328], [328, 290],\n [385, 258], [258, 384], [384, 385], [446, 265], [265, 342], [342, 446],\n [386, 387], [387, 257], [257, 386], [422, 424], [424, 430], [430, 422],\n [445, 342], [342, 276], [276, 445], [422, 273], [273, 424], [424, 422],\n [306, 292], [292, 307], [307, 306], [352, 366], [366, 345], [345, 352],\n [268, 271], [271, 302], [302, 268], [358, 423], [423, 371], [371, 358],\n [327, 294], [294, 460], [460, 327], [331, 279], [279, 294], [294, 331],\n [303, 271], [271, 304], [304, 303], [436, 432], [432, 427], [427, 436],\n [304, 272], [272, 408], [408, 304], [395, 394], [394, 431], [431, 395],\n [378, 395], [395, 400], [400, 378], [296, 334], [334, 299], [299, 296],\n [6, 351], [351, 168], [168, 6], [376, 352], [352, 411], [411, 376],\n [307, 325], [325, 320], [320, 307], [285, 295], [295, 336], [336, 285],\n [320, 319], [319, 404], [404, 320], [329, 330], [330, 349], [349, 329],\n [334, 293], [293, 333], [333, 334], [366, 323], [323, 447], [447, 366],\n [316, 15], [15, 315], [315, 316], [331, 358], [358, 279], [279, 331],\n [317, 14], [14, 316], [316, 317], [8, 285], [285, 9], [9, 8],\n [277, 329], [329, 350], [350, 277], [253, 374], [374, 252], [252, 253],\n [319, 318], [318, 403], [403, 319], [351, 6], [6, 419], [419, 351],\n [324, 318], [318, 325], [325, 324], [397, 367], [367, 365], [365, 397],\n [288, 435], [435, 397], [397, 288], [278, 344], [344, 439], [439, 278],\n [310, 272], [272, 311], [311, 310], [248, 195], [195, 281], [281, 248],\n [375, 273], [273, 291], [291, 375], [175, 396], [396, 199], [199, 175],\n [312, 311], [311, 268], [268, 312], [276, 283], [283, 445], [445, 276],\n [390, 373], [373, 339], [339, 390], [295, 282], [282, 296], [296, 295],\n [448, 449], [449, 346], [346, 448], [356, 264], [264, 454], [454, 356],\n [337, 336], [336, 299], [299, 337], [337, 338], [338, 151], [151, 337],\n [294, 278], [278, 455], [455, 294], [308, 292], [292, 415], [415, 308],\n [429, 358], [358, 355], [355, 429], [265, 340], [340, 372], [372, 265],\n [352, 346], [346, 280], [280, 352], [295, 442], [442, 282], [282, 295],\n [354, 19], [19, 370], [370, 354], [285, 441], [441, 295], [295, 285],\n [195, 248], [248, 197], [197, 195], [457, 440], [440, 274], [274, 457],\n [301, 300], [300, 368], [368, 301], [417, 351], [351, 465], [465, 417],\n [251, 301], [301, 389], [389, 251], [394, 395], [395, 379], [379, 394],\n [399, 412], [412, 419], [419, 399], [410, 436], [436, 322], [322, 410],\n [326, 2], [2, 393], [393, 326], [354, 370], [370, 461], [461, 354],\n [393, 164], [164, 267], [267, 393], [268, 302], [302, 12], [12, 268],\n [312, 268], [268, 13], [13, 312], [298, 293], [293, 301], [301, 298],\n [265, 446], [446, 340], [340, 265], [280, 330], [330, 425], [425, 280],\n [322, 426], [426, 391], [391, 322], [420, 429], [429, 437], [437, 420],\n [393, 391], [391, 326], [326, 393], [344, 440], [440, 438], [438, 344],\n [458, 459], [459, 461], [461, 458], [364, 434], [434, 394], [394, 364],\n [428, 396], [396, 262], [262, 428], [274, 354], [354, 457], [457, 274],\n [317, 316], [316, 402], [402, 317], [316, 315], [315, 403], [403, 316],\n [315, 314], [314, 404], [404, 315], [314, 313], [313, 405], [405, 314],\n [313, 421], [421, 406], [406, 313], [323, 366], [366, 361], [361, 323],\n [292, 306], [306, 407], [407, 292], [306, 291], [291, 408], [408, 306],\n [291, 287], [287, 409], [409, 291], [287, 432], [432, 410], [410, 287],\n [427, 434], [434, 411], [411, 427], [372, 264], [264, 383], [383, 372],\n [459, 309], [309, 457], [457, 459], [366, 352], [352, 401], [401, 366],\n [1, 274], [274, 4], [4, 1], [418, 421], [421, 262], [262, 418],\n [331, 294], [294, 358], [358, 331], [435, 433], [433, 367], [367, 435],\n [392, 289], [289, 439], [439, 392], [328, 462], [462, 326], [326, 328],\n [94, 2], [2, 370], [370, 94], [289, 305], [305, 455], [455, 289],\n [339, 254], [254, 448], [448, 339], [359, 255], [255, 446], [446, 359],\n [254, 253], [253, 449], [449, 254], [253, 252], [252, 450], [450, 253],\n [252, 256], [256, 451], [451, 252], [256, 341], [341, 452], [452, 256],\n [414, 413], [413, 463], [463, 414], [286, 441], [441, 414], [414, 286],\n [286, 258], [258, 441], [441, 286], [258, 257], [257, 442], [442, 258],\n [257, 259], [259, 443], [443, 257], [259, 260], [260, 444], [444, 259],\n [260, 467], [467, 445], [445, 260], [309, 459], [459, 250], [250, 309],\n [305, 289], [289, 290], [290, 305], [305, 290], [290, 460], [460, 305],\n [401, 376], [376, 435], [435, 401], [309, 250], [250, 392], [392, 309],\n [376, 411], [411, 433], [433, 376], [453, 341], [341, 464], [464, 453],\n [357, 453], [453, 465], [465, 357], [343, 357], [357, 412], [412, 343],\n [437, 343], [343, 399], [399, 437], [344, 360], [360, 440], [440, 344],\n [420, 437], [437, 456], [456, 420], [360, 420], [420, 363], [363, 360],\n [361, 401], [401, 288], [288, 361], [265, 372], [372, 353], [353, 265],\n [390, 339], [339, 249], [249, 390], [339, 448], [448, 255], [255, 339],\n];\n", "/**\n * BlazeFace, FaceMesh & Iris model implementation\n * See `facemesh.ts` for entry point\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as coords from './facemeshcoords';\nimport { constants } from '../tfjs/constants';\nimport type { Box, Point } from '../result';\nimport { env } from '../util/env';\n\nexport const createBox = (startEndTensor) => ({ startPoint: tf.slice(startEndTensor, [0, 0], [-1, 2]), endPoint: tf.slice(startEndTensor, [0, 2], [-1, 2]) });\n\nexport const disposeBox = (t) => tf.dispose([t.startPoint, t.endPoint]);\n\nexport const getBoxSize = (box): [number, number] => [Math.abs(box.endPoint[0] - box.startPoint[0]), Math.abs(box.endPoint[1] - box.startPoint[1])];\n\nexport const getBoxCenter = (box): [number, number, number] => [box.startPoint[0] + (box.endPoint[0] - box.startPoint[0]) / 2, box.startPoint[1] + (box.endPoint[1] - box.startPoint[1]) / 2, 1];\n\nexport const clampBox = (box, input): Box => (box ? [\n Math.trunc(Math.max(0, box.startPoint[0])),\n Math.trunc(Math.max(0, box.startPoint[1])),\n Math.trunc(Math.min((input.shape[2] || 0), box.endPoint[0]) - Math.max(0, box.startPoint[0])),\n Math.trunc(Math.min((input.shape[1] || 0), box.endPoint[1]) - Math.max(0, box.startPoint[1])),\n] : [0, 0, 0, 0]);\n\nexport const getRawBox = (box, input): Box => (box ? [\n box.startPoint[0] / (input.shape[2] || 0),\n box.startPoint[1] / (input.shape[1] || 0),\n (box.endPoint[0] - box.startPoint[0]) / (input.shape[2] || 0),\n (box.endPoint[1] - box.startPoint[1]) / (input.shape[1] || 0),\n] : [0, 0, 0, 0]);\n\nexport const scaleBoxCoordinates = (box, factor) => {\n const startPoint: Point = [box.startPoint[0] * factor[0], box.startPoint[1] * factor[1]];\n const endPoint: Point = [box.endPoint[0] * factor[0], box.endPoint[1] * factor[1]];\n return { startPoint, endPoint, landmarks: box.landmarks, confidence: box.confidence };\n};\n\nexport const cutAndResize = (box, image, cropSize) => {\n const h = image.shape[1];\n const w = image.shape[2];\n const cutBox = [box.startPoint[1] / h, box.startPoint[0] / w, box.endPoint[1] / h, box.endPoint[0] / w];\n const crop = tf.image.cropAndResize(image, [cutBox], [0], cropSize);\n const norm = tf.div(crop, constants.tf255);\n tf.dispose(crop);\n return norm;\n};\n\nexport const enlargeBox = (box, factor) => {\n const center = getBoxCenter(box);\n const size = getBoxSize(box);\n const halfSize: [number, number] = [factor * size[0] / 2, factor * size[1] / 2];\n return { startPoint: [center[0] - halfSize[0], center[1] - halfSize[1]] as Point, endPoint: [center[0] + halfSize[0], center[1] + halfSize[1]] as Point, landmarks: box.landmarks, confidence: box.confidence };\n};\n\nexport const squarifyBox = (box) => {\n const centers = getBoxCenter(box);\n const size = getBoxSize(box);\n const halfSize = Math.max(...size) / 2;\n return { startPoint: [Math.round(centers[0] - halfSize), Math.round(centers[1] - halfSize)] as Point, endPoint: [Math.round(centers[0] + halfSize), Math.round(centers[1] + halfSize)] as Point, landmarks: box.landmarks, confidence: box.confidence };\n};\n\nexport const calculateLandmarksBoundingBox = (landmarks) => {\n const x = landmarks.map((d) => d[0]);\n const y = landmarks.map((d) => d[1]);\n return { startPoint: [Math.min(...x), Math.min(...y)] as Point, endPoint: [Math.max(...x), Math.max(...y)] as Point, landmarks };\n};\n\nexport const fixedRotationMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];\n\nexport const normalizeRadians = (angle: number) => angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI));\n\nexport const computeRotation = (point1, point2) => normalizeRadians(Math.PI / 2 - Math.atan2(-(point2[1] - point1[1]), point2[0] - point1[0]));\n\nexport const radToDegrees = (rad) => rad * 180 / Math.PI;\n\nexport const buildTranslationMatrix = (x, y) => [[1, 0, x], [0, 1, y], [0, 0, 1]];\n\nexport const dot = (v1: number[], v2: number[]) => {\n let product = 0;\n for (let i = 0; i < v1.length; i++) product += v1[i] * v2[i];\n return product;\n};\n\nexport const getColumnFrom2DArr = (arr, columnIndex) => {\n const column: number[] = [];\n for (let i = 0; i < arr.length; i++) column.push(arr[i][columnIndex]);\n return column;\n};\n\nexport const multiplyTransformMatrices = (mat1, mat2) => {\n const product: number[][] = [];\n const size = mat1.length;\n for (let row = 0; row < size; row++) {\n product.push([]);\n for (let col = 0; col < size; col++) product[row].push(dot(mat1[row], getColumnFrom2DArr(mat2, col)));\n }\n return product;\n};\n\nexport const buildRotationMatrix = (rotation, center) => {\n const cosA = Math.cos(rotation);\n const sinA = Math.sin(rotation);\n const rotationMatrix = [[cosA, -sinA, 0], [sinA, cosA, 0], [0, 0, 1]];\n const translationMatrix = buildTranslationMatrix(center[0], center[1]);\n const translationTimesRotation = multiplyTransformMatrices(translationMatrix, rotationMatrix);\n const negativeTranslationMatrix = buildTranslationMatrix(-center[0], -center[1]);\n return multiplyTransformMatrices(translationTimesRotation, negativeTranslationMatrix);\n};\n\nexport const invertTransformMatrix = (matrix) => {\n const rotationComponent = [[matrix[0][0], matrix[1][0]], [matrix[0][1], matrix[1][1]]];\n const translationComponent = [matrix[0][2], matrix[1][2]];\n const invertedTranslation = [-dot(rotationComponent[0], translationComponent), -dot(rotationComponent[1], translationComponent)];\n return [rotationComponent[0].concat(invertedTranslation[0]), rotationComponent[1].concat(invertedTranslation[1]), [0, 0, 1]];\n};\n\nexport const rotatePoint = (homogeneousCoordinate, rotationMatrix) => [dot(homogeneousCoordinate, rotationMatrix[0]), dot(homogeneousCoordinate, rotationMatrix[1])];\n\nexport const xyDistanceBetweenPoints = (a, b) => Math.sqrt(((a[0] - b[0]) ** 2) + ((a[1] - b[1]) ** 2));\n\nexport function generateAnchors(inputSize: number) {\n const spec = inputSize === 192\n ? { strides: [4], anchors: [1] } // facemesh-detector\n : { strides: [inputSize / 16, inputSize / 8], anchors: [2, 6] }; // blazeface\n const anchors: [number, number][] = [];\n for (let i = 0; i < spec.strides.length; i++) {\n const stride = spec.strides[i];\n const gridRows = Math.floor((inputSize + stride - 1) / stride);\n const gridCols = Math.floor((inputSize + stride - 1) / stride);\n const anchorsNum = spec.anchors[i];\n for (let gridY = 0; gridY < gridRows; gridY++) {\n const anchorY = stride * (gridY + 0.5);\n for (let gridX = 0; gridX < gridCols; gridX++) {\n const anchorX = stride * (gridX + 0.5);\n for (let n = 0; n < anchorsNum; n++) anchors.push([anchorX, anchorY]);\n }\n }\n }\n return anchors;\n}\n\nexport function transformRawCoords(coordsRaw, box, angle, rotationMatrix, inputSize) {\n const boxSize = getBoxSize(box);\n const coordsScaled = coordsRaw.map((coord) => ([ // scaled around zero-point\n (boxSize[0] / inputSize) * (coord[0] - (inputSize / 2)),\n (boxSize[1] / inputSize) * (coord[1] - (inputSize / 2)),\n (coord[2] || 0),\n ]));\n const largeAngle = angle && (angle !== 0) && (Math.abs(angle) > 0.2);\n const coordsRotationMatrix = largeAngle ? buildRotationMatrix(angle, [0, 0]) : fixedRotationMatrix;\n const coordsRotated = largeAngle ? coordsScaled.map((coord) => ([...rotatePoint(coord, coordsRotationMatrix), coord[2]])) : coordsScaled;\n const inverseRotationMatrix = largeAngle ? invertTransformMatrix(rotationMatrix) : fixedRotationMatrix;\n const boxCenter = getBoxCenter(box);\n const offsets = [dot(boxCenter, inverseRotationMatrix[0]), dot(boxCenter, inverseRotationMatrix[1])];\n return coordsRotated.map((coord) => ([\n Math.trunc(coord[0] + offsets[0]),\n Math.trunc(coord[1] + offsets[1]),\n Math.trunc(coord[2] || 0),\n ]));\n}\n\nexport function correctFaceRotation(rotate, box, input, inputSize) {\n const symmetryLine = (box.landmarks.length >= coords.meshLandmarks.count)\n ? coords.meshLandmarks.symmetryLine\n : coords.blazeFaceLandmarks.symmetryLine;\n let angle = 0; // default\n let rotationMatrix = fixedRotationMatrix; // default\n let face; // default\n\n if (rotate && env.kernels.includes('rotatewithoffset')) {\n angle = computeRotation(box.landmarks[symmetryLine[0]], box.landmarks[symmetryLine[1]]);\n const largeAngle = angle && (angle !== 0) && (Math.abs(angle) > 0.2);\n if (largeAngle) { // perform rotation only if angle is sufficiently high\n const center: Point = getBoxCenter(box);\n const centerRaw: Point = [center[0] / input.shape[2], center[1] / input.shape[1]];\n const rotated = tf.image.rotateWithOffset(input, angle, 0, centerRaw);\n rotationMatrix = buildRotationMatrix(-angle, center);\n face = cutAndResize(box, rotated, [inputSize, inputSize]);\n tf.dispose(rotated);\n } else {\n face = cutAndResize(box, input, [inputSize, inputSize]);\n }\n } else {\n face = cutAndResize(box, input, [inputSize, inputSize]);\n }\n return [angle, rotationMatrix, face];\n}\n\nexport const findFaceCenter = (mesh) => {\n const x = mesh.map((m) => m[0]);\n const y = mesh.map((m) => m[1]);\n // weighted center\n /*\n const sum = (arr: number[]) => arr.reduce((prev, curr) => prev + curr, 0);\n return [sum(x) / mesh.length, sum(y) / mesh.length];\n */\n // absolute center\n return [Math.min(...x) + (Math.max(...x) - Math.min(...x)) / 2, Math.min(...y) + (Math.max(...y) - Math.min(...y)) / 2];\n};\n\nexport const calculateFaceBox = (mesh, previousBox) => {\n const center = findFaceCenter(mesh);\n const boxSize = getBoxSize(previousBox);\n const calculatedBox = {\n startPoint: [center[0] - boxSize[0] / 2, center[1] - boxSize[1] / 2] as Point,\n endPoint: [center[0] + boxSize[0] / 2, center[1] + boxSize[1] / 2] as Point,\n };\n return calculatedBox;\n};\n", "/**\n * BlazeFace, FaceMesh & Iris model implementation\n * See `facemesh.ts` for entry point\n */\n\nimport { log } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as util from './facemeshutil';\nimport { loadModel } from '../tfjs/load';\nimport { constants } from '../tfjs/constants';\nimport type { Config } from '../config';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport { env } from '../util/env';\nimport type { Point } from '../result';\n\nconst keypointsCount = 6;\nconst faceBoxScaleFactor = 1.4;\nlet model: GraphModel | null;\nlet anchors: Tensor | null = null;\nlet inputSize = 0;\nlet inputSizeT: Tensor | null = null;\n\ninterface DetectBox { startPoint: Point, endPoint: Point, landmarks: Point[], confidence: number }\n\nexport const size = () => inputSize;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.detector?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n inputSize = (model['executor'] && model.inputs[0].shape) ? model.inputs[0].shape[2] : 256;\n inputSizeT = tf.scalar(inputSize, 'int32') as Tensor;\n anchors = tf.tensor2d(util.generateAnchors(inputSize)) as Tensor;\n return model;\n}\n\nfunction decodeBoxes(boxOutputs: Tensor) {\n const t: Record = {};\n t.boxStarts = tf.slice(boxOutputs, [0, 1], [-1, 2]);\n t.centers = tf.add(t.boxStarts, anchors);\n t.boxSizes = tf.slice(boxOutputs, [0, 3], [-1, 2]);\n t.boxSizesNormalized = tf.div(t.boxSizes, inputSizeT);\n t.centersNormalized = tf.div(t.centers, inputSizeT);\n t.halfBoxSize = tf.div(t.boxSizesNormalized, constants.tf2);\n t.starts = tf.sub(t.centersNormalized, t.halfBoxSize);\n t.ends = tf.add(t.centersNormalized, t.halfBoxSize);\n t.startNormalized = tf.mul(t.starts, inputSizeT);\n t.endNormalized = tf.mul(t.ends, inputSizeT);\n const boxes = tf.concat2d([t.startNormalized, t.endNormalized], 1);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return boxes;\n}\n\nexport async function getBoxes(inputImage: Tensor, config: Config) {\n // sanity check on input\n if ((!inputImage) || (inputImage['isDisposedInternal']) || (inputImage.shape.length !== 4) || (inputImage.shape[1] < 1) || (inputImage.shape[2] < 1)) return [];\n const t: Record = {};\n t.resized = tf.image.resizeBilinear(inputImage, [inputSize, inputSize]);\n t.div = tf.div(t.resized, constants.tf127);\n t.normalized = tf.sub(t.div, constants.tf05);\n const res = model?.execute(t.normalized) as Tensor[];\n if (Array.isArray(res) && res.length > 2) { // pinto converted model?\n const sorted = res.sort((a, b) => a.size - b.size);\n t.concat384 = tf.concat([sorted[0], sorted[2]], 2); // dim: 384, 1 + 16\n t.concat512 = tf.concat([sorted[1], sorted[3]], 2); // dim: 512, 1 + 16\n t.concat = tf.concat([t.concat512, t.concat384], 1);\n t.batch = tf.squeeze(t.concat, 0);\n } else if (Array.isArray(res)) { // new facemesh-detection tfhub model\n t.batch = tf.squeeze(res[0]);\n } else { // original blazeface tfhub model\n t.batch = tf.squeeze(res);\n }\n tf.dispose(res);\n t.boxes = decodeBoxes(t.batch);\n t.logits = tf.slice(t.batch, [0, 0], [-1, 1]);\n t.sigmoid = tf.sigmoid(t.logits);\n t.scores = tf.squeeze(t.sigmoid);\n t.nms = await tf.image.nonMaxSuppressionAsync(t.boxes, t.scores, (config.face.detector?.maxDetected || 0), (config.face.detector?.iouThreshold || 0), (config.face.detector?.minConfidence || 0));\n const nms = await t.nms.array() as number[];\n const boxes: DetectBox[] = [];\n const scores = await t.scores.data();\n for (let i = 0; i < nms.length; i++) {\n const confidence = scores[nms[i]];\n if (confidence > (config.face.detector?.minConfidence || 0)) {\n const b: Record = {};\n b.bbox = tf.slice(t.boxes, [nms[i], 0], [1, -1]);\n b.slice = tf.slice(t.batch, [nms[i], keypointsCount - 1], [1, -1]);\n b.squeeze = tf.squeeze(b.slice);\n b.landmarks = tf.reshape(b.squeeze, [keypointsCount, -1]);\n const points = await b.bbox.data();\n const rawBox = {\n startPoint: [points[0], points[1]] as Point,\n endPoint: [points[2], points[3]] as Point,\n landmarks: (await b.landmarks.array()) as Point[],\n confidence,\n };\n const scaledBox = util.scaleBoxCoordinates(rawBox, [(inputImage.shape[2] || 0) / inputSize, (inputImage.shape[1] || 0) / inputSize]);\n const enlargedBox = util.enlargeBox(scaledBox, config.face['scale'] || faceBoxScaleFactor);\n const squaredBox = util.squarifyBox(enlargedBox);\n boxes.push(squaredBox);\n Object.keys(b).forEach((tensor) => tf.dispose(b[tensor]));\n }\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return boxes;\n}\n", "/* eslint-disable no-multi-spaces */\n\nexport const kpt: string[] = [\n 'nose', // 0\n 'leftEyeInside', // 1\n 'leftEye', // 2\n 'leftEyeOutside', // 3\n 'rightEyeInside', // 4\n 'rightEye', // 5\n 'rightEyeOutside', // 6\n 'leftEar', // 7\n 'rightEar', // 8\n 'leftMouth', // 9\n 'rightMouth', // 10\n 'leftShoulder', // 11\n 'rightShoulder', // 12\n 'leftElbow', // 13\n 'rightElbow', // 14\n 'leftWrist', // 15\n 'rightWrist', // 16\n 'leftPinky', // 17\n 'rightPinky', // 18\n 'leftIndex', // 19\n 'rightIndex', // 20\n 'leftThumb', // 21\n 'rightThumb', // 22\n 'leftHip', // 23\n 'rightHip', // 24\n 'leftKnee', // 25\n 'rightKnee', // 26\n 'leftAnkle', // 27\n 'rightAnkle', // 28\n 'leftHeel', // 29\n 'rightHeel', // 30\n 'leftFoot', // 31\n 'rightFoot', // 32\n 'bodyCenter', // 33\n 'bodyTop', // 34\n 'leftPalm', // 35 // z-coord not ok\n 'leftHand', // 36 // similar to wrist but z-coord not ok\n 'rightPalm', // 37 // z-coord not ok\n 'rightHand', // 38 // similar to wrist but z-coord not ok\n];\n\nexport const connected: Record = {\n shoulders: ['leftShoulder', 'rightShoulder'],\n hips: ['rightHip', 'leftHip'],\n mouth: ['leftMouth', 'rightMouth'],\n leftLegUpper: ['leftHip', 'leftKnee'],\n leftLegLower: ['leftKnee', 'leftAnkle'],\n leftFoot: ['leftAnkle', 'leftHeel', 'leftFoot'],\n leftTorso: ['leftShoulder', 'leftHip'],\n leftArmUpper: ['leftShoulder', 'leftElbow'],\n leftArmLower: ['leftElbow', 'leftWrist'],\n leftHand: ['leftWrist', 'leftPalm'],\n leftHandPinky: ['leftPalm', 'leftPinky'],\n leftHandIndex: ['leftPalm', 'leftIndex'],\n leftHandThumb: ['leftPalm', 'leftThumb'],\n leftEyeOutline: ['leftEyeInside', 'leftEyeOutside'],\n rightLegUpper: ['rightHip', 'rightKnee'],\n rightLegLower: ['rightKnee', 'rightAnkle'],\n rightFoot: ['rightAnkle', 'rightHeel', 'rightFoot'],\n rightTorso: ['rightShoulder', 'rightHip'],\n rightArmUpper: ['rightShoulder', 'rightElbow'],\n rightArmLower: ['rightElbow', 'rightWrist'],\n rightHand: ['rightWrist', 'rightPalm'],\n rightHandPinky: ['rightPalm', 'rightPinky'],\n rightHandIndex: ['rightPalm', 'rightIndex'],\n rightHandThumb: ['rightPalm', 'rightThumb'],\n rightEyeOutline: ['rightEyeInside', 'rightEyeOutside'],\n};\n", "import * as tf from '../../dist/tfjs.esm.js';\nimport type { Tensor } from '../tfjs/types';\nimport type { Box } from '../result';\nimport type { Config } from '../config';\n\ninterface DetectedBox { box: Box, boxRaw: Box, score: number }\n\nconst inputSize = 224;\nlet anchorTensor: { x, y };\nconst numLayers = 5;\nconst strides = [8, 16, 32, 32, 32];\n\nexport function createAnchors() {\n const anchors: { x: number, y: number }[] = [];\n let layerId = 0;\n while (layerId < numLayers) {\n let anchorCount = 0;\n let lastSameStrideLayer = layerId;\n while (lastSameStrideLayer < strides.length && strides[lastSameStrideLayer] === strides[layerId]) {\n anchorCount += 2;\n lastSameStrideLayer++;\n }\n const stride = strides[layerId];\n const featureMapHeight = Math.ceil(inputSize / stride);\n const featureMapWidth = Math.ceil(inputSize / stride);\n for (let y = 0; y < featureMapHeight; ++y) {\n for (let x = 0; x < featureMapWidth; ++x) {\n for (let anchorId = 0; anchorId < anchorCount; ++anchorId) {\n anchors.push({ x: (x + 0.5) / featureMapWidth, y: (y + 0.5) / featureMapHeight });\n }\n }\n }\n layerId = lastSameStrideLayer;\n }\n anchorTensor = { x: tf.tensor1d(anchors.map((a) => a.x)), y: tf.tensor1d(anchors.map((a) => a.y)) };\n}\n\nconst cropFactor = [5.0, 5.0];\nfunction decodeBoxes(boxesTensor, anchor): Tensor {\n return tf.tidy(() => {\n const split = tf.split(boxesTensor, 12, 1); // first 4 are box data [x,y,w,h] and 4 are keypoints data [x,y] for total of 12\n let xCenter = tf.squeeze(split[0]);\n let yCenter = tf.squeeze(split[1]);\n let width = tf.squeeze(split[2]);\n let height = tf.squeeze(split[3]);\n xCenter = tf.add(tf.div(xCenter, inputSize), anchor.x);\n yCenter = tf.add(tf.div(yCenter, inputSize), anchor.y);\n width = tf.mul(tf.div(width, inputSize), cropFactor[0]);\n height = tf.mul(tf.div(height, inputSize), cropFactor[1]);\n const xMin = tf.sub(xCenter, tf.div(width, 2));\n const yMin = tf.sub(yCenter, tf.div(height, 2));\n const boxes = tf.stack([xMin, yMin, width, height], 1);\n return boxes;\n });\n}\n\nexport async function decode(boxesTensor: Tensor, logitsTensor: Tensor, config: Config, outputSize: [number, number]): Promise {\n const t: Record = {};\n t.boxes = decodeBoxes(boxesTensor, anchorTensor);\n t.scores = tf.sigmoid(logitsTensor);\n t.argmax = tf.argMax(t.scores);\n const i = (await t.argmax.data())[0];\n const scores = await t.scores.data();\n const detected: { box: Box, boxRaw: Box, score: number }[] = [];\n const minScore = config.body?.['detector']?.minConfidence || 0;\n if (scores[i] >= minScore) {\n const boxes = await t.boxes.array();\n const boxRaw: Box = boxes[i];\n const box: Box = [boxRaw[0] * outputSize[0], boxRaw[1] * outputSize[1], boxRaw[2] * outputSize[0], boxRaw[3] * outputSize[1]];\n // console.log(box);\n detected.push({ box, boxRaw, score: scores[i] });\n }\n /*\n t.nms = await tf.image.nonMaxSuppressionAsync(t.boxes, t.scores, 1, config.body.detector?.minConfidence || 0.1, config.body.detector?.iouThreshold || 0.1);\n const boxes = t.boxes.arraySync();\n const scores = t.scores.dataSync();\n const nms = t.nms.dataSync();\n const detected: Array = [];\n for (const i of Array.from(nms)) {\n const boxRaw: Box = boxes[i];\n const box: Box = [boxRaw[0] * outputSize[0], boxRaw[0] * outputSize[1], boxRaw[3] * outputSize[0], boxRaw[2] * outputSize[1]];\n detected.push({ box, boxRaw, score: scores[i] });\n }\n */\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return detected;\n}\n", "import type { Point, Box } from '../result';\n\nexport function calc(keypoints: Point[], outputSize: [number, number] = [1, 1]) {\n const coords = [keypoints.map((pt) => pt[0]), keypoints.map((pt) => pt[1])]; // all x/y coords\n const min = [Math.min(...coords[0]), Math.min(...coords[1])];\n const max = [Math.max(...coords[0]), Math.max(...coords[1])];\n const box: Box = [min[0], min[1], max[0] - min[0], max[1] - min[1]];\n const boxRaw: Box = [box[0] / outputSize[0], box[1] / outputSize[1], box[2] / outputSize[0], box[3] / outputSize[1]];\n return { box, boxRaw };\n}\n\nexport function square(keypoints: Point[], outputSize: [number, number] = [1, 1]) {\n const coords = [keypoints.map((pt) => pt[0]), keypoints.map((pt) => pt[1])]; // all x/y coords\n const min = [Math.min(...coords[0]), Math.min(...coords[1])];\n const max = [Math.max(...coords[0]), Math.max(...coords[1])];\n const center = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2]; // find center x and y coord of all fingers\n const dist = Math.max(center[0] - min[0], center[1] - min[1], -center[0] + max[0], -center[1] + max[1]); // largest distance from center in any direction\n const box: Box = [Math.trunc(center[0] - dist), Math.trunc(center[1] - dist), Math.trunc(2 * dist), Math.trunc(2 * dist)];\n const boxRaw: Box = [box[0] / outputSize[0], box[1] / outputSize[1], box[2] / outputSize[0], box[3] / outputSize[1]];\n return { box, boxRaw };\n}\n\nexport function scale(box: Box, scaleFact: number) {\n const dist = [box[2] * scaleFact, box[3] * scaleFact];\n const newBox: Box = [\n box[0] - (dist[0] - box[2]) / 2,\n box[1] - (dist[1] - box[3]) / 2,\n dist[0],\n dist[1],\n ];\n return newBox;\n}\n\nexport function crop(box: Box) { // [y1, x1, y2, x2] clamped to 0..1\n const yxBox: Box = [Math.max(0, box[1]), Math.max(0, box[0]), Math.min(1, box[3] + box[1]), Math.min(1, box[2] + box[0])];\n return yxBox;\n}\n", "/**\n * BlazePose model implementation\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { constants } from '../tfjs/constants';\nimport { log, now } from '../util/util';\nimport type { BodyKeypoint, BodyResult, BodyLandmark, Box, Point, BodyAnnotation } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport * as coords from './blazeposecoords';\nimport * as detect from './blazeposedetector';\nimport * as box from '../util/box';\n\nconst env = { initial: true };\n// const models: [GraphModel | null, GraphModel | null] = [null, null];\nconst models: { detector: GraphModel | null, landmarks: GraphModel | null } = { detector: null, landmarks: null };\nconst inputSize: { detector: [number, number], landmarks: [number, number] } = { detector: [224, 224], landmarks: [256, 256] };\nlet skipped = Number.MAX_SAFE_INTEGER;\nconst outputNodes: { detector: string[], landmarks: string[] } = {\n landmarks: ['ld_3d', 'activation_segmentation', 'activation_heatmap', 'world_3d', 'output_poseflag'],\n detector: [],\n};\n\nlet cache: BodyResult | null = null;\nlet cropBox: Box | undefined;\nlet padding: [number, number][] = [[0, 0], [0, 0], [0, 0], [0, 0]];\nlet lastTime = 0;\n\nconst sigmoid = (x) => (1 - (1 / (1 + Math.exp(x))));\n\nexport async function loadDetect(config: Config): Promise {\n if (env.initial) models.detector = null;\n if (!models.detector && config.body['detector'] && config.body['detector'].modelPath || '') {\n models.detector = await loadModel(config.body['detector'].modelPath);\n const inputs = models.detector?.['executor'] ? Object.values(models.detector.modelSignature['inputs']) : undefined;\n inputSize.detector[0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;\n inputSize.detector[1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;\n } else if (config.debug && models.detector) log('cached model:', models.detector['modelUrl']);\n detect.createAnchors();\n return models.detector as GraphModel;\n}\n\nexport async function loadPose(config: Config): Promise {\n if (env.initial) models.landmarks = null;\n if (!models.landmarks) {\n models.landmarks = await loadModel(config.body.modelPath);\n const inputs = models.landmarks?.['executor'] ? Object.values(models.landmarks.modelSignature['inputs']) : undefined;\n inputSize.landmarks[0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;\n inputSize.landmarks[1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;\n } else if (config.debug) log('cached model:', models.landmarks['modelUrl']);\n return models.landmarks;\n}\n\nexport async function load(config: Config): Promise<[GraphModel | null, GraphModel | null]> {\n if (!models.detector) await loadDetect(config);\n if (!models.landmarks) await loadPose(config);\n return [models.detector, models.landmarks];\n}\n\nfunction prepareImage(input: Tensor, size: number): Tensor {\n const t: Record = {};\n if (!input?.shape?.[1] || !input?.shape?.[2]) return input;\n let final: Tensor;\n if (cropBox) {\n t.cropped = tf.image.cropAndResize(input, [cropBox], [0], [input.shape[1], input.shape[2]]); // if we have cached box use it to crop input\n }\n if (input.shape[1] !== input.shape[2]) { // only pad if width different than height\n const height: [number, number] = [\n input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0,\n input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0,\n ];\n const width: [number, number] = [\n input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0,\n input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0,\n ];\n padding = [\n [0, 0], // dont touch batch\n height, // height before&after\n width, // width before&after\n [0, 0], // dont touch rbg\n ];\n t.pad = tf.pad(t.cropped || input, padding); // use cropped box if it exists\n t.resize = tf.image.resizeBilinear(t.pad, [size, size]);\n final = tf.div(t.resize, constants.tf255);\n } else if (input.shape[1] !== size) { // if input needs resizing\n t.resize = tf.image.resizeBilinear(t.cropped || input, [size, size]);\n final = tf.div(t.resize, constants.tf255);\n } else { // if input is already in a correct resolution just normalize it\n final = tf.div(t.cropped || input, constants.tf255);\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return final;\n}\n\nfunction rescaleKeypoints(keypoints: BodyKeypoint[], outputSize: [number, number]): BodyKeypoint[] {\n for (const kpt of keypoints) { // first rescale due to padding\n kpt.position = [\n Math.trunc(kpt.position[0] * (outputSize[0] + padding[2][0] + padding[2][1]) / outputSize[0] - padding[2][0]),\n Math.trunc(kpt.position[1] * (outputSize[1] + padding[1][0] + padding[1][1]) / outputSize[1] - padding[1][0]),\n kpt.position[2] as number,\n ];\n kpt.positionRaw = [kpt.position[0] / outputSize[0], kpt.position[1] / outputSize[1], 2 * (kpt.position[2] as number) / (outputSize[0] + outputSize[1])];\n }\n if (cropBox) { // second rescale due to cropping\n for (const kpt of keypoints) {\n kpt.positionRaw = [\n kpt.positionRaw[0] + cropBox[1], // correct offset due to crop\n kpt.positionRaw[1] + cropBox[0], // correct offset due to crop\n kpt.positionRaw[2] as number,\n ];\n kpt.position = [\n Math.trunc(kpt.positionRaw[0] * outputSize[0]),\n Math.trunc(kpt.positionRaw[1] * outputSize[1]),\n kpt.positionRaw[2] as number,\n ];\n }\n }\n return keypoints;\n}\n\nfunction fixKeypoints(keypoints: BodyKeypoint[]) {\n // palm z-coord is incorrect around near-zero so we approximate it\n const leftPalm = keypoints.find((k) => k.part === 'leftPalm') as BodyKeypoint;\n const leftWrist = keypoints.find((k) => k.part === 'leftWrist') as BodyKeypoint;\n const leftIndex = keypoints.find((k) => k.part === 'leftIndex') as BodyKeypoint;\n leftPalm.position[2] = ((leftWrist.position[2] || 0) + (leftIndex.position[2] || 0)) / 2;\n const rightPalm = keypoints.find((k) => k.part === 'rightPalm') as BodyKeypoint;\n const rightWrist = keypoints.find((k) => k.part === 'rightWrist') as BodyKeypoint;\n const rightIndex = keypoints.find((k) => k.part === 'rightIndex') as BodyKeypoint;\n rightPalm.position[2] = ((rightWrist.position[2] || 0) + (rightIndex.position[2] || 0)) / 2;\n}\n\nasync function detectLandmarks(input: Tensor, config: Config, outputSize: [number, number]): Promise {\n /**\n * t.ld: 39 keypoints [x,y,z,score,presence] normalized to input size\n * t.segmentation:\n * t.heatmap:\n * t.world: 39 keypoints [x,y,z] normalized to -1..1\n * t.poseflag: body score\n */\n if (!models.landmarks?.['executor']) return null;\n const t: Record = {};\n [t.ld/* 1,195(39*5) */, t.segmentation/* 1,256,256,1 */, t.heatmap/* 1,64,64,39 */, t.world/* 1,117(39*3) */, t.poseflag/* 1,1 */] = models.landmarks?.execute(input, outputNodes.landmarks) as Tensor[]; // run model\n const poseScore = (await t.poseflag.data())[0];\n const points = await t.ld.data();\n const distances = await t.world.data();\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor])); // dont need tensors after this\n const keypointsRelative: BodyKeypoint[] = [];\n const depth = 5; // each points has x,y,z,visibility,presence\n for (let i = 0; i < points.length / depth; i++) {\n const score = sigmoid(points[depth * i + 3]);\n const presence = sigmoid(points[depth * i + 4]);\n const adjScore = Math.trunc(100 * score * presence * poseScore) / 100;\n const positionRaw: Point = [points[depth * i + 0] / inputSize.landmarks[0], points[depth * i + 1] / inputSize.landmarks[1], points[depth * i + 2] + 0];\n const position: Point = [Math.trunc(outputSize[0] * positionRaw[0]), Math.trunc(outputSize[1] * positionRaw[1]), positionRaw[2] as number];\n const distance: Point = [distances[depth * i + 0], distances[depth * i + 1], distances[depth * i + 2] + 0];\n keypointsRelative.push({ part: coords.kpt[i] as BodyLandmark, positionRaw, position, distance, score: adjScore });\n }\n if (poseScore < (config.body.minConfidence || 0)) return null;\n fixKeypoints(keypointsRelative);\n const keypoints: BodyKeypoint[] = rescaleKeypoints(keypointsRelative, outputSize); // keypoints were relative to input image which is padded\n const kpts = keypoints.map((k) => k.position);\n const boxes = box.calc(kpts, [outputSize[0], outputSize[1]]); // now find boxes based on rescaled keypoints\n const annotations: Record = {} as Record;\n for (const [name, indexes] of Object.entries(coords.connected)) {\n const pt: Point[][] = [];\n for (let i = 0; i < indexes.length - 1; i++) {\n const pt0 = keypoints.find((kpt) => kpt.part === indexes[i]);\n const pt1 = keypoints.find((kpt) => kpt.part === indexes[i + 1]);\n if (pt0 && pt1) pt.push([pt0.position, pt1.position]);\n }\n annotations[name] = pt;\n }\n const body = { id: 0, score: Math.trunc(100 * poseScore) / 100, box: boxes.box, boxRaw: boxes.boxRaw, keypoints, annotations };\n return body;\n}\n\n/*\ninterface DetectedBox { box: Box, boxRaw: Box, score: number }\n\nfunction rescaleBoxes(boxes: Array, outputSize: [number, number]): Array {\n for (const b of boxes) {\n b.box = [\n Math.trunc(b.box[0] * (outputSize[0] + padding[2][0] + padding[2][1]) / outputSize[0]),\n Math.trunc(b.box[1] * (outputSize[1] + padding[1][0] + padding[1][1]) / outputSize[1]),\n Math.trunc(b.box[2] * (outputSize[0] + padding[2][0] + padding[2][1]) / outputSize[0]),\n Math.trunc(b.box[3] * (outputSize[1] + padding[1][0] + padding[1][1]) / outputSize[1]),\n ];\n b.boxRaw = [b.box[0] / outputSize[0], b.box[1] / outputSize[1], b.box[2] / outputSize[0], b.box[3] / outputSize[1]];\n }\n return boxes;\n}\n\nasync function detectBoxes(input: Tensor, config: Config, outputSize: [number, number]) {\n const t: Record = {};\n t.res = models.detector?.execute(input, ['Identity']) as Tensor; //\n t.logitsRaw = tf.slice(t.res, [0, 0, 0], [1, -1, 1]);\n t.boxesRaw = tf.slice(t.res, [0, 0, 1], [1, -1, -1]);\n t.logits = tf.squeeze(t.logitsRaw);\n t.boxes = tf.squeeze(t.boxesRaw);\n const boxes = await detect.decode(t.boxes, t.logits, config, outputSize);\n rescaleBoxes(boxes, outputSize);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return boxes;\n}\n*/\n\nexport async function predict(input: Tensor, config: Config): Promise {\n const outputSize: [number, number] = [input.shape[2] || 0, input.shape[1] || 0];\n const skipTime = (config.body.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.body.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && cache !== null) {\n skipped++;\n } else {\n const t: Record = {};\n /*\n if (config.body['detector'] && config.body['detector']['enabled']) {\n t.detector = await prepareImage(input, 224);\n const boxes = await detectBoxes(t.detector, config, outputSize);\n }\n */\n t.landmarks = prepareImage(input, 256); // padded and resized\n cache = await detectLandmarks(t.landmarks, config, outputSize);\n /*\n cropBox = [0, 0, 1, 1]; // reset crop coordinates\n if (cache?.boxRaw && config.skipAllowed) {\n const cx = (2.0 * cache.boxRaw[0] + cache.boxRaw[2]) / 2;\n const cy = (2.0 * cache.boxRaw[1] + cache.boxRaw[3]) / 2;\n let size = cache.boxRaw[2] > cache.boxRaw[3] ? cache.boxRaw[2] : cache.boxRaw[3];\n size = (size * 1.0) / 2; // enlarge and half it\n if (cx > 0.1 && cx < 0.9 && cy > 0.1 && cy < 0.9 && size > 0.1) { // only update if box is sane\n const y = 0; // cy - size;\n const x = cx - size;\n cropBox = [y, x, y + 1, x + 1]; // [y0,x0,y1,x1] used for cropping but width/height are not yet implemented so we only reposition image to center of body\n }\n }\n */\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n lastTime = now();\n skipped = 0;\n }\n return cache ? [cache] : [];\n}\n", "/**\n * CoCo Labels used by object detection implementations\n */\nexport const labels = [\n { class: 1, label: 'person' },\n { class: 2, label: 'bicycle' },\n { class: 3, label: 'car' },\n { class: 4, label: 'motorcycle' },\n { class: 5, label: 'airplane' },\n { class: 6, label: 'bus' },\n { class: 7, label: 'train' },\n { class: 8, label: 'truck' },\n { class: 9, label: 'boat' },\n { class: 10, label: 'traffic light' },\n { class: 11, label: 'fire hydrant' },\n { class: 12, label: 'stop sign' },\n { class: 13, label: 'parking meter' },\n { class: 14, label: 'bench' },\n { class: 15, label: 'bird' },\n { class: 16, label: 'cat' },\n { class: 17, label: 'dog' },\n { class: 18, label: 'horse' },\n { class: 19, label: 'sheep' },\n { class: 20, label: 'cow' },\n { class: 21, label: 'elephant' },\n { class: 22, label: 'bear' },\n { class: 23, label: 'zebra' },\n { class: 24, label: 'giraffe' },\n { class: 25, label: 'backpack' },\n { class: 26, label: 'umbrella' },\n { class: 27, label: 'handbag' },\n { class: 28, label: 'tie' },\n { class: 29, label: 'suitcase' },\n { class: 30, label: 'frisbee' },\n { class: 31, label: 'skis' },\n { class: 32, label: 'snowboard' },\n { class: 33, label: 'sports ball' },\n { class: 34, label: 'kite' },\n { class: 35, label: 'baseball bat' },\n { class: 36, label: 'baseball glove' },\n { class: 37, label: 'skateboard' },\n { class: 38, label: 'surfboard' },\n { class: 39, label: 'tennis racket' },\n { class: 40, label: 'bottle' },\n { class: 41, label: 'wine glass' },\n { class: 42, label: 'cup' },\n { class: 43, label: 'fork' },\n { class: 44, label: 'knife' },\n { class: 45, label: 'spoon' },\n { class: 46, label: 'bowl' },\n { class: 47, label: 'banana' },\n { class: 48, label: 'apple' },\n { class: 49, label: 'sandwich' },\n { class: 50, label: 'orange' },\n { class: 51, label: 'broccoli' },\n { class: 52, label: 'carrot' },\n { class: 53, label: 'hot dog' },\n { class: 54, label: 'pizza' },\n { class: 55, label: 'donut' },\n { class: 56, label: 'cake' },\n { class: 57, label: 'chair' },\n { class: 58, label: 'couch' },\n { class: 59, label: 'potted plant' },\n { class: 60, label: 'bed' },\n { class: 61, label: 'dining table' },\n { class: 62, label: 'toilet' },\n { class: 63, label: 'tv' },\n { class: 64, label: 'laptop' },\n { class: 65, label: 'mouse' },\n { class: 66, label: 'remote' },\n { class: 67, label: 'keyboard' },\n { class: 68, label: 'cell phone' },\n { class: 69, label: 'microwave' },\n { class: 70, label: 'oven' },\n { class: 71, label: 'toaster' },\n { class: 72, label: 'sink' },\n { class: 73, label: 'refrigerator' },\n { class: 74, label: 'book' },\n { class: 75, label: 'clock' },\n { class: 76, label: 'vase' },\n { class: 77, label: 'scissors' },\n { class: 78, label: 'teddy bear' },\n { class: 79, label: 'hair drier' },\n { class: 80, label: 'toothbrush' },\n];\n", "/**\n * CenterNet object detection model implementation\n *\n * Based on: [**NanoDet**](https://github.com/RangiLyu/nanodet)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { labels } from './labels';\nimport type { ObjectResult, ObjectType, Box } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nlet inputSize = 0;\nlet last: ObjectResult[] = [];\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) {\n // fakeOps(['floormod'], config);\n model = await loadModel(config.object.modelPath);\n const inputs = model?.['executor'] ? Object.values(model.modelSignature['inputs']) : undefined;\n inputSize = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;\n } else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nasync function process(res: Tensor | null, outputShape: [number, number], config: Config) {\n if (!res) return [];\n const t: Record = {};\n const results: ObjectResult[] = [];\n const detections = await res.array() as number[][][];\n t.squeeze = tf.squeeze(res);\n const arr = tf.split(t.squeeze, 6, 1) as Tensor[]; // x1, y1, x2, y2, score, class\n t.stack = tf.stack([arr[1], arr[0], arr[3], arr[2]], 1); // reorder dims as tf.nms expects y, x\n t.boxes = tf.squeeze(t.stack);\n t.scores = tf.squeeze(arr[4]);\n t.classes = tf.squeeze(arr[5]);\n tf.dispose([res, ...arr]);\n t.nms = await tf.image.nonMaxSuppressionAsync(t.boxes, t.scores, config.object.maxDetected, config.object.iouThreshold, (config.object.minConfidence || 0));\n const nms = await t.nms.data();\n let i = 0;\n for (const id of Array.from(nms)) {\n const score = Math.trunc(100 * detections[0][id][4]) / 100;\n const classVal = detections[0][id][5];\n const label = labels[classVal].label as ObjectType;\n const [x, y] = [\n detections[0][id][0] / inputSize,\n detections[0][id][1] / inputSize,\n ];\n const boxRaw: Box = [\n x,\n y,\n detections[0][id][2] / inputSize - x,\n detections[0][id][3] / inputSize - y,\n ];\n const box: Box = [\n Math.trunc(boxRaw[0] * outputShape[0]),\n Math.trunc(boxRaw[1] * outputShape[1]),\n Math.trunc(boxRaw[2] * outputShape[0]),\n Math.trunc(boxRaw[3] * outputShape[1]),\n ];\n results.push({ id: i++, score, class: classVal, label, box, boxRaw });\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return results;\n}\n\nexport async function predict(input: Tensor, config: Config): Promise {\n if (!model?.['executor']) return [];\n const skipTime = (config.object.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.object.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && (last.length > 0)) {\n skipped++;\n return last;\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const outputSize = [input.shape[2] || 0, input.shape[1] || 0] as [number, number];\n const resize = tf.image.resizeBilinear(input, [inputSize, inputSize]);\n const objectT = config.object.enabled ? model?.execute(resize, ['tower_0/detections']) as Tensor : null;\n lastTime = now();\n tf.dispose(resize);\n\n const obj = await process(objectT, outputSize, config);\n last = obj;\n\n resolve(obj);\n });\n}\n", "export const kpt: string[] = [\n 'head',\n 'neck',\n 'rightShoulder',\n 'rightElbow',\n 'rightWrist',\n 'chest',\n 'leftShoulder',\n 'leftElbow',\n 'leftWrist',\n 'bodyCenter',\n 'rightHip',\n 'rightKnee',\n 'rightAnkle',\n 'leftHip',\n 'leftKnee',\n 'leftAnkle',\n];\n\nexport const connected: Record = {\n leftLeg: ['leftHip', 'leftKnee', 'leftAnkle'],\n rightLeg: ['rightHip', 'rightKnee', 'rightAnkle'],\n torso: ['leftShoulder', 'rightShoulder', 'rightHip', 'leftHip', 'leftShoulder'],\n leftArm: ['leftShoulder', 'leftElbow', 'leftWrist'],\n rightArm: ['rightShoulder', 'rightElbow', 'rightWrist'],\n head: [],\n};\n", "/**\n * EfficientPose model implementation\n *\n * Based on: [**EfficientPose**](https://github.com/daniegr/EfficientPose)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport * as coords from './efficientposecoords';\nimport { constants } from '../tfjs/constants';\nimport type { BodyResult, Point, BodyLandmark, BodyAnnotation } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nlet lastTime = 0;\nconst cache: BodyResult = { id: 0, keypoints: [], box: [0, 0, 0, 0], boxRaw: [0, 0, 0, 0], score: 0, annotations: {} as Record };\n\n// const keypoints: Array = [];\n// let box: Box = [0, 0, 0, 0];\n// let boxRaw: Box = [0, 0, 0, 0];\n// let score = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.body.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\n// performs argmax and max functions on a 2d tensor\nasync function max2d(inputs, minScore): Promise<[number, number, number]> {\n const [width, height] = inputs.shape;\n const reshaped = tf.reshape(inputs, [height * width]); // combine all data\n const max = tf.max(reshaped, 0);\n const newScore: number = (await max.data())[0]; // get highest score\n if (newScore > minScore) { // skip coordinate calculation is score is too low\n const coordinates = tf.argMax(reshaped, 0);\n const mod = tf.mod(coordinates, width);\n const x = (await mod.data())[0];\n const div = tf.div(coordinates, width);\n const y: number = (await div.data())[0];\n tf.dispose([reshaped, max, coordinates, mod, div]);\n return [x, y, newScore];\n }\n tf.dispose([reshaped, max]);\n return [0, 0, newScore];\n}\n\nexport async function predict(image: Tensor, config: Config): Promise {\n if (!model?.['executor']) return [];\n const skipTime = (config.body.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.body.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && Object.keys(cache.keypoints).length > 0) {\n skipped++;\n return [cache];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const tensor = tf.tidy(() => {\n if (!model?.inputs[0].shape) return null;\n const resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);\n const enhance = tf.mul(resize, constants.tf2);\n const norm = tf.sub(enhance, constants.tf1);\n return norm;\n });\n let resT;\n if (config.body.enabled) resT = model?.execute(tensor);\n lastTime = now();\n tf.dispose(tensor);\n\n if (resT) {\n cache.keypoints.length = 0;\n const squeeze = tf.squeeze(resT);\n tf.dispose(resT);\n // body parts are basically just a stack of 2d tensors\n const stack = tf.unstack(squeeze, 2);\n tf.dispose(squeeze);\n\n // process each unstacked tensor as a separate body part\n for (let id = 0; id < stack.length; id++) {\n // actual processing to get coordinates and score\n const [x, y, partScore] = await max2d(stack[id], config.body.minConfidence);\n if (partScore > (config.body.minConfidence || 0)) {\n cache.keypoints.push({\n score: Math.round(100 * partScore) / 100,\n part: coords.kpt[id] as BodyLandmark,\n positionRaw: [ // normalized to 0..1\n // @ts-ignore model is not undefined here\n x / model.inputs[0].shape[2], y / model.inputs[0].shape[1],\n ],\n position: [ // normalized to input image size\n // @ts-ignore model is not undefined here\n Math.round(image.shape[2] * x / model.inputs[0].shape[2]), Math.round(image.shape[1] * y / model.inputs[0].shape[1]),\n ],\n });\n }\n }\n stack.forEach((s) => tf.dispose(s));\n }\n cache.score = cache.keypoints.reduce((prev, curr) => (curr.score > prev ? curr.score : prev), 0);\n const x = cache.keypoints.map((a) => a.position[0]);\n const y = cache.keypoints.map((a) => a.position[1]);\n cache.box = [\n Math.min(...x),\n Math.min(...y),\n Math.max(...x) - Math.min(...x),\n Math.max(...y) - Math.min(...y),\n ];\n const xRaw = cache.keypoints.map((a) => a.positionRaw[0]);\n const yRaw = cache.keypoints.map((a) => a.positionRaw[1]);\n cache.boxRaw = [\n Math.min(...xRaw),\n Math.min(...yRaw),\n Math.max(...xRaw) - Math.min(...xRaw),\n Math.max(...yRaw) - Math.min(...yRaw),\n ];\n for (const [name, indexes] of Object.entries(coords.connected)) {\n const pt: Point[][] = [];\n for (let i = 0; i < indexes.length - 1; i++) {\n const pt0 = cache.keypoints.find((kpt) => kpt.part === indexes[i]);\n const pt1 = cache.keypoints.find((kpt) => kpt.part === indexes[i + 1]);\n if (pt0 && pt1 && pt0.score > (config.body.minConfidence || 0) && pt1.score > (config.body.minConfidence || 0)) pt.push([pt0.position, pt1.position]);\n }\n cache.annotations[name] = pt;\n }\n resolve([cache]);\n });\n}\n", "/**\n * Emotion model implementation\n *\n * [**Oarriaga**](https://github.com/oarriaga/face_classification)\n */\n\nimport type { Emotion } from '../result';\nimport { log, now } from '../util/util';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { env } from '../util/env';\nimport { constants } from '../tfjs/constants';\n\nconst annotations = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral'];\nlet model: GraphModel | null;\nconst last: { score: number, emotion: Emotion }[][] = [];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.emotion?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise<{ score: number, emotion: Emotion }[]> {\n if (!model) return [];\n const skipFrame = skipped < (config.face.emotion?.skipFrames || 0);\n const skipTime = (config.face.emotion?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx] && (last[idx].length > 0)) {\n skipped++;\n return last[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const obj: { score: number, emotion: Emotion }[] = [];\n if (config.face.emotion?.enabled) {\n const t: Record = {};\n const inputSize = model?.inputs[0].shape ? model.inputs[0].shape[2] : 0;\n t.resize = tf.image.resizeBilinear(image, [inputSize, inputSize], false);\n // const box = [[0.15, 0.15, 0.85, 0.85]]; // empyrical values for top, left, bottom, right\n // const resize = tf.image.cropAndResize(image, box, [0], [inputSize, inputSize]);\n // [t.red, t.green, t.blue] = tf.split(t.resize, 3, 3);\n // weighted rgb to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html\n // t.redNorm = tf.mul(t.red, rgb[0]);\n // t.greenNorm = tf.mul(t.green, rgb[1]);\n // t.blueNorm = tf.mul(t.blue, rgb[2]);\n // t.grayscale = tf.addN([t.redNorm, t.greenNorm, t.blueNorm]);\n t.channels = tf.mul(t.resize, constants.rgb);\n t.grayscale = tf.sum(t.channels, 3, true);\n t.grayscaleSub = tf.sub(t.grayscale, constants.tf05);\n t.grayscaleMul = tf.mul(t.grayscaleSub, constants.tf2);\n t.emotion = model?.execute(t.grayscaleMul) as Tensor; // result is already in range 0..1, no need for additional activation\n lastTime = now();\n const data = await t.emotion.data();\n for (let i = 0; i < data.length; i++) {\n if (data[i] > (config.face.emotion.minConfidence || 0)) obj.push({ score: Math.min(0.99, Math.trunc(100 * data[i]) / 100), emotion: annotations[i] as Emotion });\n }\n obj.sort((a, b) => b.score - a.score);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n last[idx] = obj;\n lastCount = count;\n resolve(obj);\n });\n}\n", "/**\n * MobileFaceNet model implementation\n *\n * Based on: [**BecauseofAI MobileFace**](https://github.com/becauseofAI/MobileFace)\n *\n * Obsolete and replaced by `faceres` that performs age/gender/descriptor analysis\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nconst last: number[][] = [];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face['mobilefacenet']?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\n/*\n// convert to black&white to avoid colorization impact\nconst rgb = [0.2989, 0.5870, 0.1140]; // factors for red/green/blue colors when converting to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html\nconst [red, green, blue] = tf.split(crop, 3, 3);\nconst redNorm = tf.mul(red, rgb[0]);\nconst greenNorm = tf.mul(green, rgb[1]);\nconst blueNorm = tf.mul(blue, rgb[2]);\nconst grayscale = tf.addN([redNorm, greenNorm, blueNorm]);\nconst merge = tf.stack([grayscale, grayscale, grayscale], 3).squeeze(4);\n\n// optional increase image contrast\n// or do it per-channel so mean is done on each channel\n// or do it based on histogram\nconst mean = merge.mean();\nconst factor = 5;\nconst contrast = merge.sub(mean).mul(factor).add(mean);\n*/\n\nexport async function predict(input: Tensor, config: Config, idx, count): Promise {\n if (!model?.['executor']) return [];\n const skipFrame = skipped < (config.face['mobilefacenet']?.skipFrames || 0);\n const skipTime = (config.face['mobilefacenet']?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx]) {\n skipped++;\n return last[idx];\n }\n return new Promise(async (resolve) => {\n let data: number[] = [];\n if (config.face['mobilefacenet']?.enabled && model?.inputs[0].shape) {\n const t: Record = {};\n t.crop = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // just resize to fit the embedding model\n // do a tight crop of image and resize it to fit the model\n // const box = [[0.05, 0.15, 0.85, 0.85]]; // empyrical values for top, left, bottom, right\n // t.crop = tf.image.cropAndResize(input, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n t.data = model.execute(t.crop) as Tensor;\n /*\n // optional normalize outputs with l2 normalization\n const scaled = tf.tidy(() => {\n const l2 = res.norm('euclidean');\n const scale = res.div(l2);\n return scale;\n });\n\n // optional reduce feature vector complexity\n const reshape = tf.reshape(res, [128, 2]); // split 256 vectors into 128 x 2\n const reduce = reshape.logSumExp(1); // reduce 2nd dimension by calculating logSumExp on it\n */\n const output = await t.data.data();\n data = Array.from(output); // convert typed array to simple array\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n last[idx] = data;\n lastCount = count;\n lastTime = now();\n resolve(data);\n });\n}\n", "/**\n * InsightFace model implementation\n *\n * Based on: [**DeepInsight InsightFace**](https://github.com/deepinsight/insightface)\n *\n * Alternative face embedding detection\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nconst last: number[][] = [];\nlet lastCount = 0;\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face['insightface'].modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(input: Tensor, config: Config, idx, count): Promise {\n if (!model?.['executor']) return [];\n const skipFrame = skipped < (config.face['insightface']?.skipFrames || 0);\n const skipTime = (config.face['insightface']?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && last[idx]) {\n skipped++;\n return last[idx];\n }\n return new Promise(async (resolve) => {\n let data: number[] = [];\n if (config.face['insightface']?.enabled && model?.inputs[0].shape) {\n const t: Record = {};\n t.crop = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // just resize to fit the embedding model\n // do a tight crop of image and resize it to fit the model\n // const box = [[0.05, 0.15, 0.85, 0.85]]; // empyrical values for top, left, bottom, right\n // t.crop = tf.image.cropAndResize(input, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n t.data = model.execute(t.crop) as Tensor;\n const output = await t.data.data();\n data = Array.from(output); // convert typed array to simple array\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n last[idx] = data;\n lastCount = count;\n lastTime = now();\n resolve(data);\n });\n}\n", "import * as coords from './facemeshcoords';\nimport * as util from './facemeshutil';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport { env } from '../util/env';\nimport { log } from '../util/util';\nimport { loadModel } from '../tfjs/load';\nimport type { Config } from '../config';\nimport type { Point } from '../result';\n\nlet model: GraphModel | null;\nlet inputSize = 0;\n\nconst irisEnlarge = 2.3;\n\nconst leftOutline = coords.meshAnnotations.leftEyeLower0;\nconst rightOutline = coords.meshAnnotations.rightEyeLower0;\n\nconst eyeLandmarks = {\n leftBounds: [leftOutline[0], leftOutline[leftOutline.length - 1]],\n rightBounds: [rightOutline[0], rightOutline[rightOutline.length - 1]],\n};\n\nconst irisLandmarks = {\n upperCenter: 3,\n lowerCenter: 4,\n index: 71,\n numCoordinates: 76,\n};\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.iris?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n inputSize = (model?.['executor'] && model.inputs?.[0].shape) ? model.inputs[0].shape[2] : 0;\n if (inputSize === -1) inputSize = 64;\n return model;\n}\n\n// Replace the raw coordinates returned by facemesh with refined iris model coordinates and update the z coordinate to be an average of the original and the new.\nexport function replaceIrisCoords(rawCoords, newCoords, prefix, keys) {\n for (let i = 0; i < coords.irisIndices.length; i++) {\n const { key, indices } = coords.irisIndices[i];\n const originalIndices = coords.meshAnnotations[`${prefix}${key}`];\n if (!keys || keys.includes(key)) {\n for (let j = 0; j < indices.length; j++) {\n const index = indices[j];\n rawCoords[originalIndices[j]] = [\n newCoords[index][0],\n newCoords[index][1],\n (newCoords[index][2] + rawCoords[originalIndices[j]][2]) / 2,\n ];\n }\n }\n }\n}\n\nexport const getLeftToRightEyeDepthDifference = (rawCoords) => {\n const leftEyeZ = rawCoords[eyeLandmarks.leftBounds[0]][2];\n const rightEyeZ = rawCoords[eyeLandmarks.rightBounds[0]][2];\n return leftEyeZ - rightEyeZ;\n};\n\n// Returns a box describing a cropped region around the eye fit for passing to the iris model.\nexport const getEyeBox = (rawCoords, face, eyeInnerCornerIndex, eyeOuterCornerIndex, meshSize, flip = false) => {\n const box = util.squarifyBox(util.enlargeBox(util.calculateLandmarksBoundingBox([rawCoords[eyeInnerCornerIndex], rawCoords[eyeOuterCornerIndex]]), irisEnlarge));\n const boxSize = util.getBoxSize(box);\n let crop = tf.image.cropAndResize(face, [[\n box.startPoint[1] / meshSize,\n box.startPoint[0] / meshSize, box.endPoint[1] / meshSize,\n box.endPoint[0] / meshSize,\n ]], [0], [inputSize, inputSize]);\n if (flip && env.kernels.includes('flipleftright')) {\n const flipped = tf.image.flipLeftRight(crop); // flipLeftRight is not defined for tfjs-node\n tf.dispose(crop);\n crop = flipped;\n }\n return { box, boxSize, crop };\n};\n\n// Given a cropped image of an eye, returns the coordinates of the contours surrounding the eye and the iris.\nexport const getEyeCoords = (eyeData, eyeBox, eyeBoxSize, flip = false) => {\n const eyeRawCoords: Point[] = [];\n for (let i = 0; i < irisLandmarks.numCoordinates; i++) {\n const x = eyeData[i * 3];\n const y = eyeData[i * 3 + 1];\n const z = eyeData[i * 3 + 2];\n eyeRawCoords.push([\n (flip ? (1 - (x / inputSize)) : (x / inputSize)) * eyeBoxSize[0] + eyeBox.startPoint[0],\n (y / inputSize) * eyeBoxSize[1] + eyeBox.startPoint[1], z,\n ]);\n }\n return { rawCoords: eyeRawCoords, iris: eyeRawCoords.slice(irisLandmarks.index) };\n};\n\n// The z-coordinates returned for the iris are unreliable, so we take the z values from the surrounding keypoints.\nexport const getAdjustedIrisCoords = (rawCoords, irisCoords, direction) => {\n const upperCenterZ = rawCoords[coords.meshAnnotations[`${direction}EyeUpper0`][irisLandmarks.upperCenter]][2];\n const lowerCenterZ = rawCoords[coords.meshAnnotations[`${direction}EyeLower0`][irisLandmarks.lowerCenter]][2];\n const averageZ = (upperCenterZ + lowerCenterZ) / 2;\n // Iris indices: 0: center | 1: right | 2: above | 3: left | 4: below\n return irisCoords.map((coord, i) => {\n let z = averageZ;\n if (i === 2) {\n z = upperCenterZ;\n } else if (i === 4) {\n z = lowerCenterZ;\n }\n return [coord[0], coord[1], z];\n });\n};\n\nexport async function augmentIris(rawCoords, face, meshSize) {\n if (!model?.['executor']) return rawCoords;\n const { box: leftEyeBox, boxSize: leftEyeBoxSize, crop: leftEyeCrop } = getEyeBox(rawCoords, face, eyeLandmarks.leftBounds[0], eyeLandmarks.leftBounds[1], meshSize, true);\n const { box: rightEyeBox, boxSize: rightEyeBoxSize, crop: rightEyeCrop } = getEyeBox(rawCoords, face, eyeLandmarks.rightBounds[0], eyeLandmarks.rightBounds[1], meshSize, true);\n const combined = tf.concat([leftEyeCrop, rightEyeCrop]);\n tf.dispose(leftEyeCrop);\n tf.dispose(rightEyeCrop);\n const eyePredictions = model.execute(combined) as Tensor;\n tf.dispose(combined);\n const eyePredictionsData = await eyePredictions.data();\n tf.dispose(eyePredictions);\n const leftEyeData = eyePredictionsData.slice(0, irisLandmarks.numCoordinates * 3);\n const { rawCoords: leftEyeRawCoords, iris: leftIrisRawCoords } = getEyeCoords(leftEyeData, leftEyeBox, leftEyeBoxSize, true);\n const rightEyeData = eyePredictionsData.slice(irisLandmarks.numCoordinates * 3);\n const { rawCoords: rightEyeRawCoords, iris: rightIrisRawCoords } = getEyeCoords(rightEyeData, rightEyeBox, rightEyeBoxSize, false);\n const leftToRightEyeDepthDifference = getLeftToRightEyeDepthDifference(rawCoords);\n if (Math.abs(leftToRightEyeDepthDifference) < 30) { // User is looking straight ahead.\n replaceIrisCoords(rawCoords, leftEyeRawCoords, 'left', null);\n replaceIrisCoords(rawCoords, rightEyeRawCoords, 'right', null);\n // If the user is looking to the left or to the right, the iris coordinates tend to diverge too much from the mesh coordinates for them to be merged so we only update a single contour line above and below the eye.\n } else if (leftToRightEyeDepthDifference < 1) { // User is looking towards the right.\n replaceIrisCoords(rawCoords, leftEyeRawCoords, 'left', ['EyeUpper0', 'EyeLower0']);\n } else { // User is looking towards the left.\n replaceIrisCoords(rawCoords, rightEyeRawCoords, 'right', ['EyeUpper0', 'EyeLower0']);\n }\n const adjustedLeftIrisCoords = getAdjustedIrisCoords(rawCoords, leftIrisRawCoords, 'left');\n const adjustedRightIrisCoords = getAdjustedIrisCoords(rawCoords, rightIrisRawCoords, 'right');\n const newCoords = rawCoords.concat(adjustedLeftIrisCoords).concat(adjustedRightIrisCoords);\n return newCoords;\n}\n", "// @tensorflow/tfjs-models/face-landmark-detection/src/constants.ts\n// https://github.com/google/mediapipe/mediapipe/python/solutions/face_mesh_connections.py\n\ntype PairArray = [number, number][];\n\nconst LIPS_CONNECTIONS: PairArray = [\n [61, 146], [146, 91], [91, 181], [181, 84], [84, 17], [17, 314], [314, 405], [405, 321], [321, 375], [375, 291], [61, 185], [185, 40], [40, 39], [39, 37], [37, 0], [0, 267], [267, 269], [269, 270], [270, 409], [409, 291],\n [78, 95], [95, 88], [88, 178], [178, 87], [87, 14], [14, 317], [317, 402], [402, 318], [318, 324], [324, 308], [78, 191], [191, 80], [80, 81], [81, 82], [82, 13], [13, 312], [312, 311], [311, 310], [310, 415], [415, 308],\n];\n\nconst LEFT_EYE_CONNECTIONS: PairArray = [[263, 249], [249, 390], [390, 373], [373, 374], [374, 380], [380, 381], [381, 382], [382, 362], [263, 466], [466, 388], [388, 387], [387, 386], [386, 385], [385, 384], [384, 398], [398, 362]];\n\nconst LEFT_EYEBROW_CONNECTIONS: PairArray = [[276, 283], [283, 282], [282, 295], [295, 285], [300, 293], [293, 334], [334, 296], [296, 336]];\n\nconst LEFT_IRIS_CONNECTIONS: PairArray = [[474, 475], [475, 476], [476, 477], [477, 474]];\n\nconst RIGHT_EYE_CONNECTIONS: PairArray = [[33, 7], [7, 163], [163, 144], [144, 145], [145, 153], [153, 154], [154, 155], [155, 133], [33, 246], [246, 161], [161, 160], [160, 159], [159, 158], [158, 157], [157, 173], [173, 133]];\n\nconst RIGHT_EYEBROW_CONNECTIONS: PairArray = [[46, 53], [53, 52], [52, 65], [65, 55], [70, 63], [63, 105], [105, 66], [66, 107]];\n\nconst RIGHT_IRIS_CONNECTIONS: PairArray = [[469, 470], [470, 471], [471, 472], [472, 469]];\n\nconst FACE_OVAL_CONNECTIONS: PairArray = [\n [10, 338], [338, 297], [297, 332], [332, 284], [284, 251], [251, 389], [389, 356], [356, 454], [454, 323], [323, 361], [361, 288], [288, 397], [397, 365], [365, 379], [379, 378], [378, 400], [400, 377], [377, 152],\n [152, 148], [148, 176], [176, 149], [149, 150], [150, 136], [136, 172], [172, 58], [58, 132], [132, 93], [93, 234], [234, 127], [127, 162], [162, 21], [21, 54], [54, 103], [103, 67], [67, 109], [109, 10],\n];\n\nexport const MEDIAPIPE_FACE_MESH_CONNECTED_KEYPOINTS_PAIRS: PairArray = [\n [127, 34], [34, 139], [139, 127], [11, 0], [0, 37], [37, 11], [232, 231], [231, 120], [120, 232], [72, 37], [37, 39], [39, 72], [128, 121], [121, 47], [47, 128], [232, 121], [121, 128], [128, 232],\n [104, 69], [69, 67], [67, 104], [175, 171], [171, 148], [148, 175], [118, 50], [50, 101], [101, 118], [73, 39], [39, 40], [40, 73], [9, 151], [151, 108], [108, 9], [48, 115], [115, 131], [131, 48],\n [194, 204], [204, 211], [211, 194], [74, 40], [40, 185], [185, 74], [80, 42], [42, 183], [183, 80], [40, 92], [92, 186], [186, 40], [230, 229], [229, 118], [118, 230], [202, 212], [212, 214], [214, 202],\n [83, 18], [18, 17], [17, 83], [76, 61], [61, 146], [146, 76], [160, 29], [29, 30], [30, 160], [56, 157], [157, 173], [173, 56], [106, 204], [204, 194], [194, 106], [135, 214], [214, 192], [192, 135],\n [203, 165], [165, 98], [98, 203], [21, 71], [71, 68], [68, 21], [51, 45], [45, 4], [4, 51], [144, 24], [24, 23], [23, 144], [77, 146], [146, 91], [91, 77], [205, 50], [50, 187], [187, 205],\n [201, 200], [200, 18], [18, 201], [91, 106], [106, 182], [182, 91], [90, 91], [91, 181], [181, 90], [85, 84], [84, 17], [17, 85], [206, 203], [203, 36], [36, 206], [148, 171], [171, 140], [140, 148],\n [92, 40], [40, 39], [39, 92], [193, 189], [189, 244], [244, 193], [159, 158], [158, 28], [28, 159], [247, 246], [246, 161], [161, 247], [236, 3], [3, 196], [196, 236], [54, 68], [68, 104], [104, 54],\n [193, 168], [168, 8], [8, 193], [117, 228], [228, 31], [31, 117], [189, 193], [193, 55], [55, 189], [98, 97], [97, 99], [99, 98], [126, 47], [47, 100], [100, 126], [166, 79], [79, 218], [218, 166],\n [155, 154], [154, 26], [26, 155], [209, 49], [49, 131], [131, 209], [135, 136], [136, 150], [150, 135], [47, 126], [126, 217], [217, 47], [223, 52], [52, 53], [53, 223], [45, 51], [51, 134], [134, 45],\n [211, 170], [170, 140], [140, 211], [67, 69], [69, 108], [108, 67], [43, 106], [106, 91], [91, 43], [230, 119], [119, 120], [120, 230], [226, 130], [130, 247], [247, 226], [63, 53], [53, 52], [52, 63],\n [238, 20], [20, 242], [242, 238], [46, 70], [70, 156], [156, 46], [78, 62], [62, 96], [96, 78], [46, 53], [53, 63], [63, 46], [143, 34], [34, 227], [227, 143], [123, 117], [117, 111], [111, 123],\n [44, 125], [125, 19], [19, 44], [236, 134], [134, 51], [51, 236], [216, 206], [206, 205], [205, 216], [154, 153], [153, 22], [22, 154], [39, 37], [37, 167], [167, 39], [200, 201], [201, 208], [208, 200],\n [36, 142], [142, 100], [100, 36], [57, 212], [212, 202], [202, 57], [20, 60], [60, 99], [99, 20], [28, 158], [158, 157], [157, 28], [35, 226], [226, 113], [113, 35], [160, 159], [159, 27], [27, 160],\n [204, 202], [202, 210], [210, 204], [113, 225], [225, 46], [46, 113], [43, 202], [202, 204], [204, 43], [62, 76], [76, 77], [77, 62], [137, 123], [123, 116], [116, 137], [41, 38], [38, 72], [72, 41],\n [203, 129], [129, 142], [142, 203], [64, 98], [98, 240], [240, 64], [49, 102], [102, 64], [64, 49], [41, 73], [73, 74], [74, 41], [212, 216], [216, 207], [207, 212], [42, 74], [74, 184], [184, 42],\n [169, 170], [170, 211], [211, 169], [170, 149], [149, 176], [176, 170], [105, 66], [66, 69], [69, 105], [122, 6], [6, 168], [168, 122], [123, 147], [147, 187], [187, 123], [96, 77], [77, 90], [90, 96],\n [65, 55], [55, 107], [107, 65], [89, 90], [90, 180], [180, 89], [101, 100], [100, 120], [120, 101], [63, 105], [105, 104], [104, 63], [93, 137], [137, 227], [227, 93], [15, 86], [86, 85], [85, 15],\n [129, 102], [102, 49], [49, 129], [14, 87], [87, 86], [86, 14], [55, 8], [8, 9], [9, 55], [100, 47], [47, 121], [121, 100], [145, 23], [23, 22], [22, 145], [88, 89], [89, 179], [179, 88],\n [6, 122], [122, 196], [196, 6], [88, 95], [95, 96], [96, 88], [138, 172], [172, 136], [136, 138], [215, 58], [58, 172], [172, 215], [115, 48], [48, 219], [219, 115], [42, 80], [80, 81], [81, 42],\n [195, 3], [3, 51], [51, 195], [43, 146], [146, 61], [61, 43], [171, 175], [175, 199], [199, 171], [81, 82], [82, 38], [38, 81], [53, 46], [46, 225], [225, 53], [144, 163], [163, 110], [110, 144],\n [52, 65], [65, 66], [66, 52], [229, 228], [228, 117], [117, 229], [34, 127], [127, 234], [234, 34], [107, 108], [108, 69], [69, 107], [109, 108], [108, 151], [151, 109], [48, 64], [64, 235], [235, 48],\n [62, 78], [78, 191], [191, 62], [129, 209], [209, 126], [126, 129], [111, 35], [35, 143], [143, 111], [117, 123], [123, 50], [50, 117], [222, 65], [65, 52], [52, 222], [19, 125], [125, 141], [141, 19],\n [221, 55], [55, 65], [65, 221], [3, 195], [195, 197], [197, 3], [25, 7], [7, 33], [33, 25], [220, 237], [237, 44], [44, 220], [70, 71], [71, 139], [139, 70], [122, 193], [193, 245], [245, 122],\n [247, 130], [130, 33], [33, 247], [71, 21], [21, 162], [162, 71], [170, 169], [169, 150], [150, 170], [188, 174], [174, 196], [196, 188], [216, 186], [186, 92], [92, 216], [2, 97], [97, 167], [167, 2],\n [141, 125], [125, 241], [241, 141], [164, 167], [167, 37], [37, 164], [72, 38], [38, 12], [12, 72], [38, 82], [82, 13], [13, 38], [63, 68], [68, 71], [71, 63], [226, 35], [35, 111], [111, 226],\n [101, 50], [50, 205], [205, 101], [206, 92], [92, 165], [165, 206], [209, 198], [198, 217], [217, 209], [165, 167], [167, 97], [97, 165], [220, 115], [115, 218], [218, 220], [133, 112], [112, 243], [243, 133],\n [239, 238], [238, 241], [241, 239], [214, 135], [135, 169], [169, 214], [190, 173], [173, 133], [133, 190], [171, 208], [208, 32], [32, 171], [125, 44], [44, 237], [237, 125], [86, 87], [87, 178], [178, 86],\n [85, 86], [86, 179], [179, 85], [84, 85], [85, 180], [180, 84], [83, 84], [84, 181], [181, 83], [201, 83], [83, 182], [182, 201], [137, 93], [93, 132], [132, 137], [76, 62], [62, 183], [183, 76],\n [61, 76], [76, 184], [184, 61], [57, 61], [61, 185], [185, 57], [212, 57], [57, 186], [186, 212], [214, 207], [207, 187], [187, 214], [34, 143], [143, 156], [156, 34], [79, 239], [239, 237], [237, 79],\n [123, 137], [137, 177], [177, 123], [44, 1], [1, 4], [4, 44], [201, 194], [194, 32], [32, 201], [64, 102], [102, 129], [129, 64], [213, 215], [215, 138], [138, 213], [59, 166], [166, 219], [219, 59],\n [242, 99], [99, 97], [97, 242], [2, 94], [94, 141], [141, 2], [75, 59], [59, 235], [235, 75], [24, 110], [110, 228], [228, 24], [25, 130], [130, 226], [226, 25], [23, 24], [24, 229], [229, 23],\n [22, 23], [23, 230], [230, 22], [26, 22], [22, 231], [231, 26], [112, 26], [26, 232], [232, 112], [189, 190], [190, 243], [243, 189], [221, 56], [56, 190], [190, 221], [28, 56], [56, 221], [221, 28],\n [27, 28], [28, 222], [222, 27], [29, 27], [27, 223], [223, 29], [30, 29], [29, 224], [224, 30], [247, 30], [30, 225], [225, 247], [238, 79], [79, 20], [20, 238], [166, 59], [59, 75], [75, 166],\n [60, 75], [75, 240], [240, 60], [147, 177], [177, 215], [215, 147], [20, 79], [79, 166], [166, 20], [187, 147], [147, 213], [213, 187], [112, 233], [233, 244], [244, 112], [233, 128], [128, 245], [245, 233],\n [128, 114], [114, 188], [188, 128], [114, 217], [217, 174], [174, 114], [131, 115], [115, 220], [220, 131], [217, 198], [198, 236], [236, 217], [198, 131], [131, 134], [134, 198], [177, 132], [132, 58], [58, 177],\n [143, 35], [35, 124], [124, 143], [110, 163], [163, 7], [7, 110], [228, 110], [110, 25], [25, 228], [356, 389], [389, 368], [368, 356], [11, 302], [302, 267], [267, 11], [452, 350], [350, 349], [349, 452],\n [302, 303], [303, 269], [269, 302], [357, 343], [343, 277], [277, 357], [452, 453], [453, 357], [357, 452], [333, 332], [332, 297], [297, 333], [175, 152], [152, 377], [377, 175], [347, 348], [348, 330], [330, 347],\n [303, 304], [304, 270], [270, 303], [9, 336], [336, 337], [337, 9], [278, 279], [279, 360], [360, 278], [418, 262], [262, 431], [431, 418], [304, 408], [408, 409], [409, 304], [310, 415], [415, 407], [407, 310],\n [270, 409], [409, 410], [410, 270], [450, 348], [348, 347], [347, 450], [422, 430], [430, 434], [434, 422], [313, 314], [314, 17], [17, 313], [306, 307], [307, 375], [375, 306], [387, 388], [388, 260], [260, 387],\n [286, 414], [414, 398], [398, 286], [335, 406], [406, 418], [418, 335], [364, 367], [367, 416], [416, 364], [423, 358], [358, 327], [327, 423], [251, 284], [284, 298], [298, 251], [281, 5], [5, 4], [4, 281],\n [373, 374], [374, 253], [253, 373], [307, 320], [320, 321], [321, 307], [425, 427], [427, 411], [411, 425], [421, 313], [313, 18], [18, 421], [321, 405], [405, 406], [406, 321], [320, 404], [404, 405], [405, 320],\n [315, 16], [16, 17], [17, 315], [426, 425], [425, 266], [266, 426], [377, 400], [400, 369], [369, 377], [322, 391], [391, 269], [269, 322], [417, 465], [465, 464], [464, 417], [386, 257], [257, 258], [258, 386],\n [466, 260], [260, 388], [388, 466], [456, 399], [399, 419], [419, 456], [284, 332], [332, 333], [333, 284], [417, 285], [285, 8], [8, 417], [346, 340], [340, 261], [261, 346], [413, 441], [441, 285], [285, 413],\n [327, 460], [460, 328], [328, 327], [355, 371], [371, 329], [329, 355], [392, 439], [439, 438], [438, 392], [382, 341], [341, 256], [256, 382], [429, 420], [420, 360], [360, 429], [364, 394], [394, 379], [379, 364],\n [277, 343], [343, 437], [437, 277], [443, 444], [444, 283], [283, 443], [275, 440], [440, 363], [363, 275], [431, 262], [262, 369], [369, 431], [297, 338], [338, 337], [337, 297], [273, 375], [375, 321], [321, 273],\n [450, 451], [451, 349], [349, 450], [446, 342], [342, 467], [467, 446], [293, 334], [334, 282], [282, 293], [458, 461], [461, 462], [462, 458], [276, 353], [353, 383], [383, 276], [308, 324], [324, 325], [325, 308],\n [276, 300], [300, 293], [293, 276], [372, 345], [345, 447], [447, 372], [352, 345], [345, 340], [340, 352], [274, 1], [1, 19], [19, 274], [456, 248], [248, 281], [281, 456], [436, 427], [427, 425], [425, 436],\n [381, 256], [256, 252], [252, 381], [269, 391], [391, 393], [393, 269], [200, 199], [199, 428], [428, 200], [266, 330], [330, 329], [329, 266], [287, 273], [273, 422], [422, 287], [250, 462], [462, 328], [328, 250],\n [258, 286], [286, 384], [384, 258], [265, 353], [353, 342], [342, 265], [387, 259], [259, 257], [257, 387], [424, 431], [431, 430], [430, 424], [342, 353], [353, 276], [276, 342], [273, 335], [335, 424], [424, 273],\n [292, 325], [325, 307], [307, 292], [366, 447], [447, 345], [345, 366], [271, 303], [303, 302], [302, 271], [423, 266], [266, 371], [371, 423], [294, 455], [455, 460], [460, 294], [279, 278], [278, 294], [294, 279],\n [271, 272], [272, 304], [304, 271], [432, 434], [434, 427], [427, 432], [272, 407], [407, 408], [408, 272], [394, 430], [430, 431], [431, 394], [395, 369], [369, 400], [400, 395], [334, 333], [333, 299], [299, 334],\n [351, 417], [417, 168], [168, 351], [352, 280], [280, 411], [411, 352], [325, 319], [319, 320], [320, 325], [295, 296], [296, 336], [336, 295], [319, 403], [403, 404], [404, 319], [330, 348], [348, 349], [349, 330],\n [293, 298], [298, 333], [333, 293], [323, 454], [454, 447], [447, 323], [15, 16], [16, 315], [315, 15], [358, 429], [429, 279], [279, 358], [14, 15], [15, 316], [316, 14], [285, 336], [336, 9], [9, 285],\n [329, 349], [349, 350], [350, 329], [374, 380], [380, 252], [252, 374], [318, 402], [402, 403], [403, 318], [6, 197], [197, 419], [419, 6], [318, 319], [319, 325], [325, 318], [367, 364], [364, 365], [365, 367],\n [435, 367], [367, 397], [397, 435], [344, 438], [438, 439], [439, 344], [272, 271], [271, 311], [311, 272], [195, 5], [5, 281], [281, 195], [273, 287], [287, 291], [291, 273], [396, 428], [428, 199], [199, 396],\n [311, 271], [271, 268], [268, 311], [283, 444], [444, 445], [445, 283], [373, 254], [254, 339], [339, 373], [282, 334], [334, 296], [296, 282], [449, 347], [347, 346], [346, 449], [264, 447], [447, 454], [454, 264],\n [336, 296], [296, 299], [299, 336], [338, 10], [10, 151], [151, 338], [278, 439], [439, 455], [455, 278], [292, 407], [407, 415], [415, 292], [358, 371], [371, 355], [355, 358], [340, 345], [345, 372], [372, 340],\n [346, 347], [347, 280], [280, 346], [442, 443], [443, 282], [282, 442], [19, 94], [94, 370], [370, 19], [441, 442], [442, 295], [295, 441], [248, 419], [419, 197], [197, 248], [263, 255], [255, 359], [359, 263],\n [440, 275], [275, 274], [274, 440], [300, 383], [383, 368], [368, 300], [351, 412], [412, 465], [465, 351], [263, 467], [467, 466], [466, 263], [301, 368], [368, 389], [389, 301], [395, 378], [378, 379], [379, 395],\n [412, 351], [351, 419], [419, 412], [436, 426], [426, 322], [322, 436], [2, 164], [164, 393], [393, 2], [370, 462], [462, 461], [461, 370], [164, 0], [0, 267], [267, 164], [302, 11], [11, 12], [12, 302],\n [268, 12], [12, 13], [13, 268], [293, 300], [300, 301], [301, 293], [446, 261], [261, 340], [340, 446], [330, 266], [266, 425], [425, 330], [426, 423], [423, 391], [391, 426], [429, 355], [355, 437], [437, 429],\n [391, 327], [327, 326], [326, 391], [440, 457], [457, 438], [438, 440], [341, 382], [382, 362], [362, 341], [459, 457], [457, 461], [461, 459], [434, 430], [430, 394], [394, 434], [414, 463], [463, 362], [362, 414],\n [396, 369], [369, 262], [262, 396], [354, 461], [461, 457], [457, 354], [316, 403], [403, 402], [402, 316], [315, 404], [404, 403], [403, 315], [314, 405], [405, 404], [404, 314], [313, 406], [406, 405], [405, 313],\n [421, 418], [418, 406], [406, 421], [366, 401], [401, 361], [361, 366], [306, 408], [408, 407], [407, 306], [291, 409], [409, 408], [408, 291], [287, 410], [410, 409], [409, 287], [432, 436], [436, 410], [410, 432],\n [434, 416], [416, 411], [411, 434], [264, 368], [368, 383], [383, 264], [309, 438], [438, 457], [457, 309], [352, 376], [376, 401], [401, 352], [274, 275], [275, 4], [4, 274], [421, 428], [428, 262], [262, 421],\n [294, 327], [327, 358], [358, 294], [433, 416], [416, 367], [367, 433], [289, 455], [455, 439], [439, 289], [462, 370], [370, 326], [326, 462], [2, 326], [326, 370], [370, 2], [305, 460], [460, 455], [455, 305],\n [254, 449], [449, 448], [448, 254], [255, 261], [261, 446], [446, 255], [253, 450], [450, 449], [449, 253], [252, 451], [451, 450], [450, 252], [256, 452], [452, 451], [451, 256], [341, 453], [453, 452], [452, 341],\n [413, 464], [464, 463], [463, 413], [441, 413], [413, 414], [414, 441], [258, 442], [442, 441], [441, 258], [257, 443], [443, 442], [442, 257], [259, 444], [444, 443], [443, 259], [260, 445], [445, 444], [444, 260],\n [467, 342], [342, 445], [445, 467], [459, 458], [458, 250], [250, 459], [289, 392], [392, 290], [290, 289], [290, 328], [328, 460], [460, 290], [376, 433], [433, 435], [435, 376], [250, 290], [290, 392], [392, 250],\n [411, 416], [416, 433], [433, 411], [341, 463], [463, 464], [464, 341], [453, 464], [464, 465], [465, 453], [357, 465], [465, 412], [412, 357], [343, 412], [412, 399], [399, 343], [360, 363], [363, 440], [440, 360],\n [437, 399], [399, 456], [456, 437], [420, 456], [456, 363], [363, 420], [401, 435], [435, 288], [288, 401], [372, 383], [383, 353], [353, 372], [339, 255], [255, 249], [249, 339], [448, 261], [261, 255], [255, 448],\n [133, 243], [243, 190], [190, 133], [133, 155], [155, 112], [112, 133], [33, 246], [246, 247], [247, 33], [33, 130], [130, 25], [25, 33], [398, 384], [384, 286], [286, 398], [362, 398], [398, 414], [414, 362],\n [362, 463], [463, 341], [341, 362], [263, 359], [359, 467], [467, 263], [263, 249], [249, 255], [255, 263], [466, 467], [467, 260], [260, 466], [75, 60], [60, 166], [166, 75], [238, 239], [239, 79], [79, 238],\n [162, 127], [127, 139], [139, 162], [72, 11], [11, 37], [37, 72], [121, 232], [232, 120], [120, 121], [73, 72], [72, 39], [39, 73], [114, 128], [128, 47], [47, 114], [233, 232], [232, 128], [128, 233],\n [103, 104], [104, 67], [67, 103], [152, 175], [175, 148], [148, 152], [119, 118], [118, 101], [101, 119], [74, 73], [73, 40], [40, 74], [107, 9], [9, 108], [108, 107], [49, 48], [48, 131], [131, 49],\n [32, 194], [194, 211], [211, 32], [184, 74], [74, 185], [185, 184], [191, 80], [80, 183], [183, 191], [185, 40], [40, 186], [186, 185], [119, 230], [230, 118], [118, 119], [210, 202], [202, 214], [214, 210],\n [84, 83], [83, 17], [17, 84], [77, 76], [76, 146], [146, 77], [161, 160], [160, 30], [30, 161], [190, 56], [56, 173], [173, 190], [182, 106], [106, 194], [194, 182], [138, 135], [135, 192], [192, 138],\n [129, 203], [203, 98], [98, 129], [54, 21], [21, 68], [68, 54], [5, 51], [51, 4], [4, 5], [145, 144], [144, 23], [23, 145], [90, 77], [77, 91], [91, 90], [207, 205], [205, 187], [187, 207],\n [83, 201], [201, 18], [18, 83], [181, 91], [91, 182], [182, 181], [180, 90], [90, 181], [181, 180], [16, 85], [85, 17], [17, 16], [205, 206], [206, 36], [36, 205], [176, 148], [148, 140], [140, 176],\n [165, 92], [92, 39], [39, 165], [245, 193], [193, 244], [244, 245], [27, 159], [159, 28], [28, 27], [30, 247], [247, 161], [161, 30], [174, 236], [236, 196], [196, 174], [103, 54], [54, 104], [104, 103],\n [55, 193], [193, 8], [8, 55], [111, 117], [117, 31], [31, 111], [221, 189], [189, 55], [55, 221], [240, 98], [98, 99], [99, 240], [142, 126], [126, 100], [100, 142], [219, 166], [166, 218], [218, 219],\n [112, 155], [155, 26], [26, 112], [198, 209], [209, 131], [131, 198], [169, 135], [135, 150], [150, 169], [114, 47], [47, 217], [217, 114], [224, 223], [223, 53], [53, 224], [220, 45], [45, 134], [134, 220],\n [32, 211], [211, 140], [140, 32], [109, 67], [67, 108], [108, 109], [146, 43], [43, 91], [91, 146], [231, 230], [230, 120], [120, 231], [113, 226], [226, 247], [247, 113], [105, 63], [63, 52], [52, 105],\n [241, 238], [238, 242], [242, 241], [124, 46], [46, 156], [156, 124], [95, 78], [78, 96], [96, 95], [70, 46], [46, 63], [63, 70], [116, 143], [143, 227], [227, 116], [116, 123], [123, 111], [111, 116],\n [1, 44], [44, 19], [19, 1], [3, 236], [236, 51], [51, 3], [207, 216], [216, 205], [205, 207], [26, 154], [154, 22], [22, 26], [165, 39], [39, 167], [167, 165], [199, 200], [200, 208], [208, 199],\n [101, 36], [36, 100], [100, 101], [43, 57], [57, 202], [202, 43], [242, 20], [20, 99], [99, 242], [56, 28], [28, 157], [157, 56], [124, 35], [35, 113], [113, 124], [29, 160], [160, 27], [27, 29],\n [211, 204], [204, 210], [210, 211], [124, 113], [113, 46], [46, 124], [106, 43], [43, 204], [204, 106], [96, 62], [62, 77], [77, 96], [227, 137], [137, 116], [116, 227], [73, 41], [41, 72], [72, 73],\n [36, 203], [203, 142], [142, 36], [235, 64], [64, 240], [240, 235], [48, 49], [49, 64], [64, 48], [42, 41], [41, 74], [74, 42], [214, 212], [212, 207], [207, 214], [183, 42], [42, 184], [184, 183],\n [210, 169], [169, 211], [211, 210], [140, 170], [170, 176], [176, 140], [104, 105], [105, 69], [69, 104], [193, 122], [122, 168], [168, 193], [50, 123], [123, 187], [187, 50], [89, 96], [96, 90], [90, 89],\n [66, 65], [65, 107], [107, 66], [179, 89], [89, 180], [180, 179], [119, 101], [101, 120], [120, 119], [68, 63], [63, 104], [104, 68], [234, 93], [93, 227], [227, 234], [16, 15], [15, 85], [85, 16],\n [209, 129], [129, 49], [49, 209], [15, 14], [14, 86], [86, 15], [107, 55], [55, 9], [9, 107], [120, 100], [100, 121], [121, 120], [153, 145], [145, 22], [22, 153], [178, 88], [88, 179], [179, 178],\n [197, 6], [6, 196], [196, 197], [89, 88], [88, 96], [96, 89], [135, 138], [138, 136], [136, 135], [138, 215], [215, 172], [172, 138], [218, 115], [115, 219], [219, 218], [41, 42], [42, 81], [81, 41],\n [5, 195], [195, 51], [51, 5], [57, 43], [43, 61], [61, 57], [208, 171], [171, 199], [199, 208], [41, 81], [81, 38], [38, 41], [224, 53], [53, 225], [225, 224], [24, 144], [144, 110], [110, 24],\n [105, 52], [52, 66], [66, 105], [118, 229], [229, 117], [117, 118], [227, 34], [34, 234], [234, 227], [66, 107], [107, 69], [69, 66], [10, 109], [109, 151], [151, 10], [219, 48], [48, 235], [235, 219],\n [183, 62], [62, 191], [191, 183], [142, 129], [129, 126], [126, 142], [116, 111], [111, 143], [143, 116], [118, 117], [117, 50], [50, 118], [223, 222], [222, 52], [52, 223], [94, 19], [19, 141], [141, 94],\n [222, 221], [221, 65], [65, 222], [196, 3], [3, 197], [197, 196], [45, 220], [220, 44], [44, 45], [156, 70], [70, 139], [139, 156], [188, 122], [122, 245], [245, 188], [139, 71], [71, 162], [162, 139],\n [149, 170], [170, 150], [150, 149], [122, 188], [188, 196], [196, 122], [206, 216], [216, 92], [92, 206], [164, 2], [2, 167], [167, 164], [242, 141], [141, 241], [241, 242], [0, 164], [164, 37], [37, 0],\n [11, 72], [72, 12], [12, 11], [12, 38], [38, 13], [13, 12], [70, 63], [63, 71], [71, 70], [31, 226], [226, 111], [111, 31], [36, 101], [101, 205], [205, 36], [203, 206], [206, 165], [165, 203],\n [126, 209], [209, 217], [217, 126], [98, 165], [165, 97], [97, 98], [237, 220], [220, 218], [218, 237], [237, 239], [239, 241], [241, 237], [210, 214], [214, 169], [169, 210], [140, 171], [171, 32], [32, 140],\n [241, 125], [125, 237], [237, 241], [179, 86], [86, 178], [178, 179], [180, 85], [85, 179], [179, 180], [181, 84], [84, 180], [180, 181], [182, 83], [83, 181], [181, 182], [194, 201], [201, 182], [182, 194],\n [177, 137], [137, 132], [132, 177], [184, 76], [76, 183], [183, 184], [185, 61], [61, 184], [184, 185], [186, 57], [57, 185], [185, 186], [216, 212], [212, 186], [186, 216], [192, 214], [214, 187], [187, 192],\n [139, 34], [34, 156], [156, 139], [218, 79], [79, 237], [237, 218], [147, 123], [123, 177], [177, 147], [45, 44], [44, 4], [4, 45], [208, 201], [201, 32], [32, 208], [98, 64], [64, 129], [129, 98],\n [192, 213], [213, 138], [138, 192], [235, 59], [59, 219], [219, 235], [141, 242], [242, 97], [97, 141], [97, 2], [2, 141], [141, 97], [240, 75], [75, 235], [235, 240], [229, 24], [24, 228], [228, 229],\n [31, 25], [25, 226], [226, 31], [230, 23], [23, 229], [229, 230], [231, 22], [22, 230], [230, 231], [232, 26], [26, 231], [231, 232], [233, 112], [112, 232], [232, 233], [244, 189], [189, 243], [243, 244],\n [189, 221], [221, 190], [190, 189], [222, 28], [28, 221], [221, 222], [223, 27], [27, 222], [222, 223], [224, 29], [29, 223], [223, 224], [225, 30], [30, 224], [224, 225], [113, 247], [247, 225], [225, 113],\n [99, 60], [60, 240], [240, 99], [213, 147], [147, 215], [215, 213], [60, 20], [20, 166], [166, 60], [192, 187], [187, 213], [213, 192], [243, 112], [112, 244], [244, 243], [244, 233], [233, 245], [245, 244],\n [245, 128], [128, 188], [188, 245], [188, 114], [114, 174], [174, 188], [134, 131], [131, 220], [220, 134], [174, 217], [217, 236], [236, 174], [236, 198], [198, 134], [134, 236], [215, 177], [177, 58], [58, 215],\n [156, 143], [143, 124], [124, 156], [25, 110], [110, 7], [7, 25], [31, 228], [228, 25], [25, 31], [264, 356], [356, 368], [368, 264], [0, 11], [11, 267], [267, 0], [451, 452], [452, 349], [349, 451],\n [267, 302], [302, 269], [269, 267], [350, 357], [357, 277], [277, 350], [350, 452], [452, 357], [357, 350], [299, 333], [333, 297], [297, 299], [396, 175], [175, 377], [377, 396], [280, 347], [347, 330], [330, 280],\n [269, 303], [303, 270], [270, 269], [151, 9], [9, 337], [337, 151], [344, 278], [278, 360], [360, 344], [424, 418], [418, 431], [431, 424], [270, 304], [304, 409], [409, 270], [272, 310], [310, 407], [407, 272],\n [322, 270], [270, 410], [410, 322], [449, 450], [450, 347], [347, 449], [432, 422], [422, 434], [434, 432], [18, 313], [313, 17], [17, 18], [291, 306], [306, 375], [375, 291], [259, 387], [387, 260], [260, 259],\n [424, 335], [335, 418], [418, 424], [434, 364], [364, 416], [416, 434], [391, 423], [423, 327], [327, 391], [301, 251], [251, 298], [298, 301], [275, 281], [281, 4], [4, 275], [254, 373], [373, 253], [253, 254],\n [375, 307], [307, 321], [321, 375], [280, 425], [425, 411], [411, 280], [200, 421], [421, 18], [18, 200], [335, 321], [321, 406], [406, 335], [321, 320], [320, 405], [405, 321], [314, 315], [315, 17], [17, 314],\n [423, 426], [426, 266], [266, 423], [396, 377], [377, 369], [369, 396], [270, 322], [322, 269], [269, 270], [413, 417], [417, 464], [464, 413], [385, 386], [386, 258], [258, 385], [248, 456], [456, 419], [419, 248],\n [298, 284], [284, 333], [333, 298], [168, 417], [417, 8], [8, 168], [448, 346], [346, 261], [261, 448], [417, 413], [413, 285], [285, 417], [326, 327], [327, 328], [328, 326], [277, 355], [355, 329], [329, 277],\n [309, 392], [392, 438], [438, 309], [381, 382], [382, 256], [256, 381], [279, 429], [429, 360], [360, 279], [365, 364], [364, 379], [379, 365], [355, 277], [277, 437], [437, 355], [282, 443], [443, 283], [283, 282],\n [281, 275], [275, 363], [363, 281], [395, 431], [431, 369], [369, 395], [299, 297], [297, 337], [337, 299], [335, 273], [273, 321], [321, 335], [348, 450], [450, 349], [349, 348], [359, 446], [446, 467], [467, 359],\n [283, 293], [293, 282], [282, 283], [250, 458], [458, 462], [462, 250], [300, 276], [276, 383], [383, 300], [292, 308], [308, 325], [325, 292], [283, 276], [276, 293], [293, 283], [264, 372], [372, 447], [447, 264],\n [346, 352], [352, 340], [340, 346], [354, 274], [274, 19], [19, 354], [363, 456], [456, 281], [281, 363], [426, 436], [436, 425], [425, 426], [380, 381], [381, 252], [252, 380], [267, 269], [269, 393], [393, 267],\n [421, 200], [200, 428], [428, 421], [371, 266], [266, 329], [329, 371], [432, 287], [287, 422], [422, 432], [290, 250], [250, 328], [328, 290], [385, 258], [258, 384], [384, 385], [446, 265], [265, 342], [342, 446],\n [386, 387], [387, 257], [257, 386], [422, 424], [424, 430], [430, 422], [445, 342], [342, 276], [276, 445], [422, 273], [273, 424], [424, 422], [306, 292], [292, 307], [307, 306], [352, 366], [366, 345], [345, 352],\n [268, 271], [271, 302], [302, 268], [358, 423], [423, 371], [371, 358], [327, 294], [294, 460], [460, 327], [331, 279], [279, 294], [294, 331], [303, 271], [271, 304], [304, 303], [436, 432], [432, 427], [427, 436],\n [304, 272], [272, 408], [408, 304], [395, 394], [394, 431], [431, 395], [378, 395], [395, 400], [400, 378], [296, 334], [334, 299], [299, 296], [6, 351], [351, 168], [168, 6], [376, 352], [352, 411], [411, 376],\n [307, 325], [325, 320], [320, 307], [285, 295], [295, 336], [336, 285], [320, 319], [319, 404], [404, 320], [329, 330], [330, 349], [349, 329], [334, 293], [293, 333], [333, 334], [366, 323], [323, 447], [447, 366],\n [316, 15], [15, 315], [315, 316], [331, 358], [358, 279], [279, 331], [317, 14], [14, 316], [316, 317], [8, 285], [285, 9], [9, 8], [277, 329], [329, 350], [350, 277], [253, 374], [374, 252], [252, 253],\n [319, 318], [318, 403], [403, 319], [351, 6], [6, 419], [419, 351], [324, 318], [318, 325], [325, 324], [397, 367], [367, 365], [365, 397], [288, 435], [435, 397], [397, 288], [278, 344], [344, 439], [439, 278],\n [310, 272], [272, 311], [311, 310], [248, 195], [195, 281], [281, 248], [375, 273], [273, 291], [291, 375], [175, 396], [396, 199], [199, 175], [312, 311], [311, 268], [268, 312], [276, 283], [283, 445], [445, 276],\n [390, 373], [373, 339], [339, 390], [295, 282], [282, 296], [296, 295], [448, 449], [449, 346], [346, 448], [356, 264], [264, 454], [454, 356], [337, 336], [336, 299], [299, 337], [337, 338], [338, 151], [151, 337],\n [294, 278], [278, 455], [455, 294], [308, 292], [292, 415], [415, 308], [429, 358], [358, 355], [355, 429], [265, 340], [340, 372], [372, 265], [352, 346], [346, 280], [280, 352], [295, 442], [442, 282], [282, 295],\n [354, 19], [19, 370], [370, 354], [285, 441], [441, 295], [295, 285], [195, 248], [248, 197], [197, 195], [457, 440], [440, 274], [274, 457], [301, 300], [300, 368], [368, 301], [417, 351], [351, 465], [465, 417],\n [251, 301], [301, 389], [389, 251], [394, 395], [395, 379], [379, 394], [399, 412], [412, 419], [419, 399], [410, 436], [436, 322], [322, 410], [326, 2], [2, 393], [393, 326], [354, 370], [370, 461], [461, 354],\n [393, 164], [164, 267], [267, 393], [268, 302], [302, 12], [12, 268], [312, 268], [268, 13], [13, 312], [298, 293], [293, 301], [301, 298], [265, 446], [446, 340], [340, 265], [280, 330], [330, 425], [425, 280],\n [322, 426], [426, 391], [391, 322], [420, 429], [429, 437], [437, 420], [393, 391], [391, 326], [326, 393], [344, 440], [440, 438], [438, 344], [458, 459], [459, 461], [461, 458], [364, 434], [434, 394], [394, 364],\n [428, 396], [396, 262], [262, 428], [274, 354], [354, 457], [457, 274], [317, 316], [316, 402], [402, 317], [316, 315], [315, 403], [403, 316], [315, 314], [314, 404], [404, 315], [314, 313], [313, 405], [405, 314],\n [313, 421], [421, 406], [406, 313], [323, 366], [366, 361], [361, 323], [292, 306], [306, 407], [407, 292], [306, 291], [291, 408], [408, 306], [291, 287], [287, 409], [409, 291], [287, 432], [432, 410], [410, 287],\n [427, 434], [434, 411], [411, 427], [372, 264], [264, 383], [383, 372], [459, 309], [309, 457], [457, 459], [366, 352], [352, 401], [401, 366], [1, 274], [274, 4], [4, 1], [418, 421], [421, 262], [262, 418],\n [331, 294], [294, 358], [358, 331], [435, 433], [433, 367], [367, 435], [392, 289], [289, 439], [439, 392], [328, 462], [462, 326], [326, 328], [94, 2], [2, 370], [370, 94], [289, 305], [305, 455], [455, 289],\n [339, 254], [254, 448], [448, 339], [359, 255], [255, 446], [446, 359], [254, 253], [253, 449], [449, 254], [253, 252], [252, 450], [450, 253], [252, 256], [256, 451], [451, 252], [256, 341], [341, 452], [452, 256],\n [414, 413], [413, 463], [463, 414], [286, 441], [441, 414], [414, 286], [286, 258], [258, 441], [441, 286], [258, 257], [257, 442], [442, 258], [257, 259], [259, 443], [443, 257], [259, 260], [260, 444], [444, 259],\n [260, 467], [467, 445], [445, 260], [309, 459], [459, 250], [250, 309], [305, 289], [289, 290], [290, 305], [305, 290], [290, 460], [460, 305], [401, 376], [376, 435], [435, 401], [309, 250], [250, 392], [392, 309],\n [376, 411], [411, 433], [433, 376], [453, 341], [341, 464], [464, 453], [357, 453], [453, 465], [465, 357], [343, 357], [357, 412], [412, 343], [437, 343], [343, 399], [399, 437], [344, 360], [360, 440], [440, 344],\n [420, 437], [437, 456], [456, 420], [360, 420], [420, 363], [363, 360], [361, 401], [401, 288], [288, 361], [265, 372], [372, 353], [353, 265], [390, 339], [339, 249], [249, 390], [339, 448], [448, 255], [255, 339],\n];\n\nfunction connectionsToIndices(connections: PairArray) {\n const indices = connections.map((connection) => connection[0]);\n indices.push(connections[connections.length - 1][1]);\n return indices;\n}\n\nexport const MEDIAPIPE_FACE_MESH_KEYPOINTS_BY_CONTOUR = {\n lips: connectionsToIndices(LIPS_CONNECTIONS),\n leftEye: connectionsToIndices(LEFT_EYE_CONNECTIONS),\n leftEyebrow: connectionsToIndices(LEFT_EYEBROW_CONNECTIONS),\n leftIris: connectionsToIndices(LEFT_IRIS_CONNECTIONS),\n rightEye: connectionsToIndices(RIGHT_EYE_CONNECTIONS),\n rightEyebrow: connectionsToIndices(RIGHT_EYEBROW_CONNECTIONS),\n rightIris: connectionsToIndices(RIGHT_IRIS_CONNECTIONS),\n faceOval: connectionsToIndices(FACE_OVAL_CONNECTIONS),\n};\n\nconst indexLabelPairs: [number, string][] = Object.entries(MEDIAPIPE_FACE_MESH_KEYPOINTS_BY_CONTOUR)\n .map(([label, indices]) => indices.map((index) => [index, label] as [number, string]))\n .flat();\n\nexport const MEDIAPIPE_FACE_MESH_KEYPOINTS = new Map(indexLabelPairs);\n\ntype AssignAverage = number[];\nexport interface LandmarksRefinementConfig {\n indexesMapping: number[]; // Maps indexes of the given set of landmarks to indexes of the resulting set of landmarks. Should be non empty and contain the same amount of indexes as landmarks in the corresponding input\n zRefinement: 'none'|'copy'|AssignAverage; // Z refinement instructions.\n}\n\nexport const LANDMARKS_REFINEMENT_LIPS_CONFIG = [\n 61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291, // Lower outer.\n 185, 40, 39, 37, 0, 267, 269, 270, 409, // Upper outer(excluding corners).\n 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308, // Lower inner.\n 191, 80, 81, 82, 13, 312, 311, 310, 415, // Upper inner(excluding corners).\n 76, 77, 90, 180, 85, 16, 315, 404, 320, 307, 306, // Lower semi - outer.\n 184, 74, 73, 72, 11, 302, 303, 304, 408, // Upper semi - outer(excluding corners).\n 62, 96, 89, 179, 86, 15, 316, 403, 319, 325, 292, // Lower semi - inner.\n 183, 42, 41, 38, 12, 268, 271, 272, 407, // Upper semi - inner(excluding corners).\n];\n\nexport const LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG = [\n 33, 7, 163, 144, 145, 153, 154, 155, 133, // Lower contour.\n 246, 161, 160, 159, 158, 157, 173, // upper contour (excluding corners).\n 130, 25, 110, 24, 23, 22, 26, 112, 243, // Halo x2 lower contour.\n 247, 30, 29, 27, 28, 56, 190, // Halo x2 upper contour (excluding corners).\n 226, 31, 228, 229, 230, 231, 232, 233, 244, // Halo x3 lower contour.\n 113, 225, 224, 223, 222, 221, 189, // Halo x3 upper contour (excluding corners).\n 35, 124, 46, 53, 52, 65, // Halo x4 upper contour (no lower because of mesh structure) or eyebrow inner contour.\n 143, 111, 117, 118, 119, 120, 121, 128, 245, // Halo x5 lower contour.\n 156, 70, 63, 105, 66, 107, 55, 193, // Halo x5 upper contour (excluding corners) or eyebrow outer contour.\n];\n\nexport const LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG = [\n 263, 249, 390, 373, 374, 380, 381, 382, 362, // Lower contour.\n 466, 388, 387, 386, 385, 384, 398, // Upper contour (excluding corners).\n 359, 255, 339, 254, 253, 252, 256, 341, 463, // Halo x2 lower contour.\n 467, 260, 259, 257, 258, 286, 414, // Halo x2 upper contour (excluding corners).\n 446, 261, 448, 449, 450, 451, 452, 453, 464, // Halo x3 lower contour.\n 342, 445, 444, 443, 442, 441, 413, // Halo x3 upper contour (excluding corners).\n 265, 353, 276, 283, 282, 295, // Halo x4 upper contour (no lower because of mesh structure) or/ eyebrow inner contour.\n 372, 340, 346, 347, 348, 349, 350, 357, 465, // Halo x5 lower contour.\n 383, 300, 293, 334, 296, 336, 285, 417, // Halo x5 upper contour (excluding corners) or eyebrow outer contour.\n];\n\nexport const LANDMARKS_REFINEMENT_LEFT_IRIS_CONFIG = [\n 468, // Center.\n 469, // Iris right edge.\n 470, // Iris top edge.\n 471, // Iris left edge.\n 472, // Iris bottom edge.\n];\n/*\nzRefinement: [\n 33, 7, 163, 144, 145, 153, 154, 155, 133, // Lower contour.\n 246, 161, 160, 159, 158, 157, 173, // Upper contour (excluding corners).\n];\n*/\n\nexport const LANDMARKS_REFINEMENT_RIGHT_IRIS_CONFIG = [\n 473, // Center.\n 474, // Iris right edge.\n 475, // Iris top edge.\n 476, // Iris left edge.\n 477, // Iris bottom edge.\n];\n/*\nzRefinement: [\n 263, 249, 390, 373, 374, 380, 381, 382, 362, // Lower contour.\n 466, 388, 387, 386, 385, 384, 398, // Upper contour (excluding corners).\n];\n*/\n", "import * as constants from './constants';\nimport type { Tensor } from '../tfjs/types';\n\nexport async function augment(rawCoords, results: Tensor[]) {\n const t: Record = { // all attention models produce 2d results so it needs to be later augmented with correct z-coords\n // mesh: results[0], // already have it in rawCoords // output_mesh_identity\n // flag: results[1], // already processed in parent // conv_faceflag\n lips: await results.filter((r) => r.size === 160)[0].data() as Float32Array, // 80 x 2d = 160 // output_lips\n irisL: await results.filter((r) => r.size === 10)[0].data() as Float32Array, // 5 x 2d = 10 // output_right_iris\n eyeL: await results.filter((r) => r.size === 142)[0].data() as Float32Array, // 71 x 2d = 142 // output_right_eye\n irisR: await results.filter((r) => r.size === 10)[1].data() as Float32Array, // 5 x 2d = 10 // output_left_iris\n eyeR: await results.filter((r) => r.size === 142)[1].data() as Float32Array, // 71 x 2d = 142// output_left_eye\n };\n\n // augment iris: adds additional 5 keypoints per eye\n const irisLDepth = constants.LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.reduce((prev, curr) => prev += rawCoords[curr][2], 0) / constants.LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.length; // get average z-coord for iris\n for (let i = 0; i < t.irisL.length / 2; i++) rawCoords.push([t.irisL[2 * i + 0], t.irisL[2 * i + 1], irisLDepth]);\n const irisRDepth = constants.LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.reduce((prev, curr) => prev += rawCoords[curr][2], 0) / constants.LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.length; // get average z-coord for iris\n for (let i = 0; i < t.irisR.length / 2; i++) rawCoords.push([t.irisR[2 * i + 0], t.irisR[2 * i + 1], irisRDepth]);\n\n // augment eyes: replaces eye keypoints based on heuristic mapping\n for (let i = 0; i < t.eyeL.length / 2; i++) rawCoords[constants.LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG[i]] = [t.eyeL[2 * i + 0], t.eyeL[2 * i + 1], rawCoords[constants.LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG[i]][2]];\n for (let i = 0; i < t.eyeR.length / 2; i++) rawCoords[constants.LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG[i]] = [t.eyeR[2 * i + 0], t.eyeR[2 * i + 1], rawCoords[constants.LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG[i]][2]];\n\n // augment lips: replaces eye keypoints based on heuristic mapping\n for (let i = 0; i < t.lips.length / 2; i++) rawCoords[constants.LANDMARKS_REFINEMENT_LIPS_CONFIG[i]] = [t.lips[2 * i + 0], t.lips[2 * i + 1], rawCoords[constants.LANDMARKS_REFINEMENT_LIPS_CONFIG[i]][2]];\n\n return rawCoords;\n}\n", "/**\n * BlazeFace, FaceMesh & Iris model implementation\n *\n * Based on:\n * - [**MediaPipe BlazeFace**](https://drive.google.com/file/d/1f39lSzU5Oq-j_OXgS67KfN5wNsoeAZ4V/view)\n * - Facial Spacial Geometry: [**MediaPipe FaceMesh**](https://drive.google.com/file/d/1VFC_wIpw4O7xBOiTgUldl79d9LA-LsnA/view)\n * - Eye Iris Details: [**MediaPipe Iris**](https://drive.google.com/file/d/1bsWbokp9AklH2ANjCfmjqEzzxO1CNbMu/view)\n */\n\nimport { log, now } from '../util/util';\nimport { loadModel } from '../tfjs/load';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as blazeface from './blazeface';\nimport * as util from './facemeshutil';\nimport * as coords from './facemeshcoords';\nimport * as iris from './iris';\nimport * as attention from './attention';\nimport { histogramEqualization } from '../image/enhance';\nimport { env } from '../util/env';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { FaceResult, FaceLandmark, Point } from '../result';\nimport type { Config } from '../config';\n\ninterface DetectBox { startPoint: Point, endPoint: Point, landmarks: Point[], confidence: number }\n\nconst cache = {\n boxes: [] as DetectBox[],\n skipped: Number.MAX_SAFE_INTEGER,\n timestamp: 0,\n};\n\nlet model: GraphModel | null = null;\nlet inputSize = 0;\n\nexport async function predict(input: Tensor, config: Config): Promise {\n if (!model?.['executor']) return [];\n // reset cached boxes\n const skipTime = (config.face.detector?.skipTime || 0) > (now() - cache.timestamp);\n const skipFrame = cache.skipped < (config.face.detector?.skipFrames || 0);\n if (!config.skipAllowed || !skipTime || !skipFrame || cache.boxes.length === 0) {\n cache.boxes = await blazeface.getBoxes(input, config); // get results from blazeface detector\n cache.timestamp = now();\n cache.skipped = 0;\n } else {\n cache.skipped++;\n }\n const faces: FaceResult[] = [];\n const newCache: DetectBox[] = [];\n let id = 0;\n const size = inputSize;\n for (let i = 0; i < cache.boxes.length; i++) {\n const box = cache.boxes[i];\n let angle = 0;\n let rotationMatrix;\n const face: FaceResult = { // init face result\n id: id++,\n mesh: [],\n meshRaw: [],\n box: [0, 0, 0, 0],\n boxRaw: [0, 0, 0, 0],\n score: 0,\n boxScore: 0,\n faceScore: 0,\n // contoursRaw: [],\n // contours: [],\n annotations: {} as Record,\n };\n\n // optional rotation correction based on detector data only if mesh is disabled otherwise perform it later when we have more accurate mesh data. if no rotation correction this function performs crop\n [angle, rotationMatrix, face.tensor] = util.correctFaceRotation(config.face.detector?.rotation, box, input, config.face.mesh?.enabled ? inputSize : blazeface.size());\n if (config.filter.equalization) {\n const equilized = face.tensor ? await histogramEqualization(face.tensor) : undefined;\n tf.dispose(face.tensor);\n if (equilized) face.tensor = equilized;\n }\n face.boxScore = Math.round(100 * box.confidence) / 100;\n if (!config.face.mesh?.enabled) { // mesh not enabled, return resuts from detector only\n face.box = util.clampBox(box, input);\n face.boxRaw = util.getRawBox(box, input);\n face.score = face.boxScore;\n face.mesh = box.landmarks.map((pt) => [\n ((box.startPoint[0] + box.endPoint[0])) / 2 + ((box.endPoint[0] + box.startPoint[0]) * pt[0] / blazeface.size()),\n ((box.startPoint[1] + box.endPoint[1])) / 2 + ((box.endPoint[1] + box.startPoint[1]) * pt[1] / blazeface.size()),\n ]);\n face.meshRaw = face.mesh.map((pt) => [pt[0] / (input.shape[2] || 0), pt[1] / (input.shape[1] || 0), (pt[2] || 0) / size]);\n for (const key of Object.keys(coords.blazeFaceLandmarks)) {\n face.annotations[key] = [face.mesh[coords.blazeFaceLandmarks[key] as number]]; // add annotations\n }\n } else if (!model) { // mesh enabled, but not loaded\n if (config.debug) log('face mesh detection requested, but model is not loaded');\n } else { // mesh enabled\n if (config.face.attention?.enabled && !env.kernels.includes('atan2')) {\n tf.dispose(face.tensor);\n return faces;\n }\n const results = model.execute(face.tensor as Tensor) as Tensor[];\n const confidenceT = results.find((t) => t.shape[t.shape.length - 1] === 1) as Tensor;\n const faceConfidence = await confidenceT.data();\n face.faceScore = Math.round(100 * faceConfidence[0]) / 100;\n\n if (face.faceScore < (config.face.detector?.minConfidence || 1)) { // low confidence in detected mesh\n box.confidence = face.faceScore; // reset confidence of cached box\n if (config.face.mesh.keepInvalid) {\n face.box = util.clampBox(box, input);\n face.boxRaw = util.getRawBox(box, input);\n face.score = face.boxScore;\n face.mesh = box.landmarks.map((pt) => [\n ((box.startPoint[0] + box.endPoint[0])) / 2 + ((box.endPoint[0] + box.startPoint[0]) * pt[0] / blazeface.size()),\n ((box.startPoint[1] + box.endPoint[1])) / 2 + ((box.endPoint[1] + box.startPoint[1]) * pt[1] / blazeface.size()),\n ]);\n face.meshRaw = face.mesh.map((pt) => [pt[0] / (input.shape[2] || 1), pt[1] / (input.shape[1] || 1), (pt[2] || 0) / size]);\n for (const key of Object.keys(coords.blazeFaceLandmarks)) {\n face.annotations[key] = [face.mesh[coords.blazeFaceLandmarks[key] as number]]; // add annotations\n }\n }\n } else {\n const meshT = results.find((t) => t.shape[t.shape.length - 1] === 1404) as Tensor;\n const coordsReshaped = tf.reshape(meshT, [-1, 3]);\n let rawCoords = await coordsReshaped.array();\n tf.dispose(coordsReshaped);\n if (config.face.attention?.enabled) {\n rawCoords = await attention.augment(rawCoords, results); // augment iris results using attention model results\n } else if (config.face.iris?.enabled) {\n rawCoords = await iris.augmentIris(rawCoords, face.tensor, inputSize); // run iris model and augment results\n }\n face.mesh = util.transformRawCoords(rawCoords, box, angle, rotationMatrix, inputSize); // get processed mesh\n face.meshRaw = face.mesh.map((pt) => [pt[0] / (input.shape[2] || 0), pt[1] / (input.shape[1] || 0), (pt[2] || 0) / size]);\n for (const key of Object.keys(coords.meshAnnotations)) face.annotations[key] = coords.meshAnnotations[key].map((index) => face.mesh[index]); // add annotations\n face.score = face.faceScore;\n const calculatedBox = { ...util.calculateFaceBox(face.mesh, box), confidence: box.confidence, landmarks: box.landmarks };\n face.box = util.clampBox(calculatedBox, input);\n face.boxRaw = util.getRawBox(calculatedBox, input);\n /*\n const contoursT = results.find((t) => t.shape[t.shape.length - 1] === 266) as Tensor;\n const contoursData = contoursT && await contoursT.data(); // 133 x 2d points\n face.contoursRaw = [];\n for (let j = 0; j < contoursData.length / 2; j++) face.contoursRaw.push([contoursData[2 * j + 0] / inputSize, contoursData[2 * j + 1] / inputSize]);\n face.contours = face.contoursRaw.map((c) => [Math.trunc((input.shape[2] || 1) * c[0]), Math.trunc((input.shape[1] || 1) * c[1])]);\n */\n newCache.push(calculatedBox);\n }\n tf.dispose(results);\n }\n if (face.score > (config.face.detector?.minConfidence || 1)) faces.push(face);\n else tf.dispose(face.tensor);\n }\n cache.boxes = newCache; // reset cache\n return faces;\n}\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (config.face.attention?.enabled && model?.['signature']) {\n if (Object.keys(model?.['signature']?.outputs || {}).length < 6) model = null;\n }\n if (!model) {\n if (config.face.attention?.enabled) model = await loadModel(config.face.attention.modelPath);\n else model = await loadModel(config.face.mesh?.modelPath);\n } else if (config.debug) {\n log('cached model:', model['modelUrl']);\n }\n inputSize = (model['executor'] && model?.inputs?.[0].shape) ? model?.inputs?.[0].shape[2] : 256;\n return model;\n}\n\nexport const triangulation = coords.TRI468;\nexport const uvmap = coords.UV468;\n", "/**\n * FaceRes model implementation\n *\n * Returns Age, Gender, Descriptor\n * Implements Face simmilarity function\n *\n * Based on: [**HSE-FaceRes**](https://github.com/HSE-asavchenko/HSE_FaceRec_tf)\n */\n\nimport { log, now } from '../util/util';\nimport { env } from '../util/env';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { constants } from '../tfjs/constants';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Config } from '../config';\nimport type { Gender, Race } from '../result';\n\nexport interface FaceRes { age: number, gender: Gender, genderScore: number, descriptor: number[], race?: { score: number, race: Race }[] }\n\nlet model: GraphModel | null;\nconst last: {\n age: number,\n gender: Gender,\n genderScore: number,\n descriptor: number[],\n}[] = [];\n\nlet lastTime = 0;\nlet lastCount = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.description?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport function enhance(input): Tensor {\n const tensor = (input.image || input.tensor || input) as Tensor; // input received from detector is already normalized to 0..1, input is also assumed to be straightened\n if (!model?.inputs[0].shape) return tensor; // model has no shape so no point continuing\n const crop: Tensor = tf.image.resizeBilinear(tensor, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);\n const norm: Tensor = tf.mul(crop, constants.tf255);\n tf.dispose(crop);\n return norm;\n /*\n // do a tight crop of image and resize it to fit the model\n const box = [[0.05, 0.15, 0.85, 0.85]]; // empyrical values for top, left, bottom, right\n const crop = (tensor.shape.length === 3)\n ? tf.image.cropAndResize(tf.expandDims(tensor, 0), box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]) // add batch dimension if missing\n : tf.image.cropAndResize(tensor, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n */\n /*\n // convert to black&white to avoid colorization impact\n const rgb = [0.2989, 0.5870, 0.1140]; // factors for red/green/blue colors when converting to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html\n const [red, green, blue] = tf.split(crop, 3, 3);\n const redNorm = tf.mul(red, rgb[0]);\n const greenNorm = tf.mul(green, rgb[1]);\n const blueNorm = tf.mul(blue, rgb[2]);\n const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);\n const merge = tf.stack([grayscale, grayscale, grayscale], 3).squeeze(4);\n */\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise {\n if (!model?.['executor']) return { age: 0, gender: 'unknown', genderScore: 0, descriptor: [] };\n const skipFrame = skipped < (config.face.description?.skipFrames || 0);\n const skipTime = (config.face.description?.skipTime || 0) > (now() - lastTime);\n if (config.skipAllowed && skipFrame && skipTime && (lastCount === count) && last[idx]?.age && (last[idx]?.age > 0)) {\n skipped++;\n return last[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const obj = {\n age: 0 as number,\n gender: 'unknown' as Gender,\n genderScore: 0 as number,\n descriptor: [] as number[],\n };\n\n if (config.face.description?.enabled) {\n const enhanced = enhance(image);\n const resT = model?.execute(enhanced) as Tensor[];\n lastTime = now();\n tf.dispose(enhanced);\n const genderT = resT.find((t) => t.shape[1] === 1) as Tensor;\n const gender = await genderT.data();\n const confidence = Math.trunc(200 * Math.abs((gender[0] - 0.5))) / 100;\n if (confidence > (config.face.description.minConfidence || 0)) {\n obj.gender = gender[0] <= 0.5 ? 'female' : 'male';\n obj.genderScore = Math.min(0.99, confidence);\n }\n const argmax = tf.argMax(resT.find((t) => t.shape[1] === 100), 1);\n const age: number = (await argmax.data())[0];\n tf.dispose(argmax);\n const ageT = resT.find((t) => t.shape[1] === 100) as Tensor;\n const all = await ageT.data();\n obj.age = Math.round(all[age - 1] > all[age + 1] ? 10 * age - 100 * all[age - 1] : 10 * age + 100 * all[age + 1]) / 10;\n\n const desc = resT.find((t) => t.shape[1] === 1024);\n // const reshape = desc.reshape([128, 8]); // reshape large 1024-element descriptor to 128 x 8\n // const reduce = reshape.logSumExp(1); // reduce 2nd dimension by calculating logSumExp on it which leaves us with 128-element descriptor\n const descriptor = desc ? await desc.data() : [] as number[];\n obj.descriptor = Array.from(descriptor);\n resT.forEach((t) => tf.dispose(t));\n }\n last[idx] = obj;\n lastCount = count;\n resolve(obj);\n });\n}\n", "import * as tf from '../../dist/tfjs.esm.js';\nimport type { Point } from '../result';\n\nexport function getBoxSize(box) {\n return [\n Math.abs(box.endPoint[0] - box.startPoint[0]),\n Math.abs(box.endPoint[1] - box.startPoint[1]),\n ];\n}\n\nexport function getBoxCenter(box) {\n return [\n box.startPoint[0] + (box.endPoint[0] - box.startPoint[0]) / 2,\n box.startPoint[1] + (box.endPoint[1] - box.startPoint[1]) / 2,\n ];\n}\n\nexport function cutBoxFromImageAndResize(box, image, cropSize) {\n const h = image.shape[1];\n const w = image.shape[2];\n const boxes = [[\n box.startPoint[1] / h,\n box.startPoint[0] / w,\n box.endPoint[1] / h,\n box.endPoint[0] / w,\n ]];\n return tf.image.cropAndResize(image, boxes, [0], cropSize);\n}\n\nexport function scaleBoxCoordinates(box, factor) {\n const startPoint = [box.startPoint[0] * factor[0], box.startPoint[1] * factor[1]] as Point;\n const endPoint = [box.endPoint[0] * factor[0], box.endPoint[1] * factor[1]] as Point;\n const palmLandmarks = box.palmLandmarks.map((coord) => {\n const scaledCoord = [coord[0] * factor[0], coord[1] * factor[1]];\n return scaledCoord;\n });\n return { startPoint, endPoint, palmLandmarks, confidence: box.confidence };\n}\n\nexport function enlargeBox(box, factor = 1.5) {\n const center = getBoxCenter(box);\n const size = getBoxSize(box);\n const newHalfSize = [factor * size[0] / 2, factor * size[1] / 2];\n const startPoint = [center[0] - newHalfSize[0], center[1] - newHalfSize[1]] as Point;\n const endPoint = [center[0] + newHalfSize[0], center[1] + newHalfSize[1]] as Point;\n return { startPoint, endPoint, palmLandmarks: box.palmLandmarks };\n}\n\nexport function squarifyBox(box) {\n const centers = getBoxCenter(box);\n const size = getBoxSize(box);\n const maxEdge = Math.max(...size);\n const halfSize = maxEdge / 2;\n const startPoint = [centers[0] - halfSize, centers[1] - halfSize] as Point;\n const endPoint = [centers[0] + halfSize, centers[1] + halfSize] as Point;\n return { startPoint, endPoint, palmLandmarks: box.palmLandmarks };\n}\n\nexport function shiftBox(box, shiftFactor) {\n const boxSize = [\n box.endPoint[0] - box.startPoint[0],\n box.endPoint[1] - box.startPoint[1],\n ];\n const shiftVector = [boxSize[0] * shiftFactor[0], boxSize[1] * shiftFactor[1]];\n const startPoint = [box.startPoint[0] + shiftVector[0], box.startPoint[1] + shiftVector[1]] as Point;\n const endPoint = [box.endPoint[0] + shiftVector[0], box.endPoint[1] + shiftVector[1]] as Point;\n return { startPoint, endPoint, palmLandmarks: box.palmLandmarks };\n}\n\nexport function normalizeRadians(angle) {\n return angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI));\n}\n\nexport function computeRotation(point1, point2) {\n const radians = Math.PI / 2 - Math.atan2(-(point2[1] - point1[1]), point2[0] - point1[0]);\n return normalizeRadians(radians);\n}\n\nexport const buildTranslationMatrix = (x, y) => [[1, 0, x], [0, 1, y], [0, 0, 1]];\n\nexport function dot(v1, v2) {\n let product = 0;\n for (let i = 0; i < v1.length; i++) {\n product += v1[i] * v2[i];\n }\n return product;\n}\n\nexport function getColumnFrom2DArr(arr, columnIndex) {\n const column: number[] = [];\n for (let i = 0; i < arr.length; i++) {\n column.push(arr[i][columnIndex]);\n }\n return column;\n}\n\nexport function multiplyTransformMatrices(mat1, mat2) {\n const product: number[][] = [];\n const size = mat1.length;\n for (let row = 0; row < size; row++) {\n product.push([]);\n for (let col = 0; col < size; col++) {\n product[row].push(dot(mat1[row], getColumnFrom2DArr(mat2, col)));\n }\n }\n return product;\n}\n\nexport function buildRotationMatrix(rotation, center) {\n const cosA = Math.cos(rotation);\n const sinA = Math.sin(rotation);\n const rotationMatrix = [[cosA, -sinA, 0], [sinA, cosA, 0], [0, 0, 1]];\n const translationMatrix = buildTranslationMatrix(center[0], center[1]);\n const translationTimesRotation = multiplyTransformMatrices(translationMatrix, rotationMatrix);\n const negativeTranslationMatrix = buildTranslationMatrix(-center[0], -center[1]);\n return multiplyTransformMatrices(translationTimesRotation, negativeTranslationMatrix);\n}\n\nexport function invertTransformMatrix(matrix) {\n const rotationComponent = [[matrix[0][0], matrix[1][0]], [matrix[0][1], matrix[1][1]]];\n const translationComponent = [matrix[0][2], matrix[1][2]];\n const invertedTranslation = [\n -dot(rotationComponent[0], translationComponent),\n -dot(rotationComponent[1], translationComponent),\n ];\n return [\n rotationComponent[0].concat(invertedTranslation[0]),\n rotationComponent[1].concat(invertedTranslation[1]),\n [0, 0, 1],\n ];\n}\n\nexport function rotatePoint(homogeneousCoordinate, rotationMatrix) {\n return [\n dot(homogeneousCoordinate, rotationMatrix[0]),\n dot(homogeneousCoordinate, rotationMatrix[1]),\n ];\n}\n", "/**\n * HandPose model implementation constants\n * See `handpose.ts` for entry point\n */\n\nexport const anchors = [\n { x: 0.015625, y: 0.015625 },\n { x: 0.015625, y: 0.015625 },\n { x: 0.046875, y: 0.015625 },\n { x: 0.046875, y: 0.015625 },\n { x: 0.078125, y: 0.015625 },\n { x: 0.078125, y: 0.015625 },\n { x: 0.109375, y: 0.015625 },\n { x: 0.109375, y: 0.015625 },\n { x: 0.140625, y: 0.015625 },\n { x: 0.140625, y: 0.015625 },\n { x: 0.171875, y: 0.015625 },\n { x: 0.171875, y: 0.015625 },\n { x: 0.203125, y: 0.015625 },\n { x: 0.203125, y: 0.015625 },\n { x: 0.234375, y: 0.015625 },\n { x: 0.234375, y: 0.015625 },\n { x: 0.265625, y: 0.015625 },\n { x: 0.265625, y: 0.015625 },\n { x: 0.296875, y: 0.015625 },\n { x: 0.296875, y: 0.015625 },\n { x: 0.328125, y: 0.015625 },\n { x: 0.328125, y: 0.015625 },\n { x: 0.359375, y: 0.015625 },\n { x: 0.359375, y: 0.015625 },\n { x: 0.390625, y: 0.015625 },\n { x: 0.390625, y: 0.015625 },\n { x: 0.421875, y: 0.015625 },\n { x: 0.421875, y: 0.015625 },\n { x: 0.453125, y: 0.015625 },\n { x: 0.453125, y: 0.015625 },\n { x: 0.484375, y: 0.015625 },\n { x: 0.484375, y: 0.015625 },\n { x: 0.515625, y: 0.015625 },\n { x: 0.515625, y: 0.015625 },\n { x: 0.546875, y: 0.015625 },\n { x: 0.546875, y: 0.015625 },\n { x: 0.578125, y: 0.015625 },\n { x: 0.578125, y: 0.015625 },\n { x: 0.609375, y: 0.015625 },\n { x: 0.609375, y: 0.015625 },\n { x: 0.640625, y: 0.015625 },\n { x: 0.640625, y: 0.015625 },\n { x: 0.671875, y: 0.015625 },\n { x: 0.671875, y: 0.015625 },\n { x: 0.703125, y: 0.015625 },\n { x: 0.703125, y: 0.015625 },\n { x: 0.734375, y: 0.015625 },\n { x: 0.734375, y: 0.015625 },\n { x: 0.765625, y: 0.015625 },\n { x: 0.765625, y: 0.015625 },\n { x: 0.796875, y: 0.015625 },\n { x: 0.796875, y: 0.015625 },\n { x: 0.828125, y: 0.015625 },\n { x: 0.828125, y: 0.015625 },\n { x: 0.859375, y: 0.015625 },\n { x: 0.859375, y: 0.015625 },\n { x: 0.890625, y: 0.015625 },\n { x: 0.890625, y: 0.015625 },\n { x: 0.921875, y: 0.015625 },\n { x: 0.921875, y: 0.015625 },\n { x: 0.953125, y: 0.015625 },\n { x: 0.953125, y: 0.015625 },\n { x: 0.984375, y: 0.015625 },\n { x: 0.984375, y: 0.015625 },\n { x: 0.015625, y: 0.046875 },\n { x: 0.015625, y: 0.046875 },\n { x: 0.046875, y: 0.046875 },\n { x: 0.046875, y: 0.046875 },\n { x: 0.078125, y: 0.046875 },\n { x: 0.078125, y: 0.046875 },\n { x: 0.109375, y: 0.046875 },\n { x: 0.109375, y: 0.046875 },\n { x: 0.140625, y: 0.046875 },\n { x: 0.140625, y: 0.046875 },\n { x: 0.171875, y: 0.046875 },\n { x: 0.171875, y: 0.046875 },\n { x: 0.203125, y: 0.046875 },\n { x: 0.203125, y: 0.046875 },\n { x: 0.234375, y: 0.046875 },\n { x: 0.234375, y: 0.046875 },\n { x: 0.265625, y: 0.046875 },\n { x: 0.265625, y: 0.046875 },\n { x: 0.296875, y: 0.046875 },\n { x: 0.296875, y: 0.046875 },\n { x: 0.328125, y: 0.046875 },\n { x: 0.328125, y: 0.046875 },\n { x: 0.359375, y: 0.046875 },\n { x: 0.359375, y: 0.046875 },\n { x: 0.390625, y: 0.046875 },\n { x: 0.390625, y: 0.046875 },\n { x: 0.421875, y: 0.046875 },\n { x: 0.421875, y: 0.046875 },\n { x: 0.453125, y: 0.046875 },\n { x: 0.453125, y: 0.046875 },\n { x: 0.484375, y: 0.046875 },\n { x: 0.484375, y: 0.046875 },\n { x: 0.515625, y: 0.046875 },\n { x: 0.515625, y: 0.046875 },\n { x: 0.546875, y: 0.046875 },\n { x: 0.546875, y: 0.046875 },\n { x: 0.578125, y: 0.046875 },\n { x: 0.578125, y: 0.046875 },\n { x: 0.609375, y: 0.046875 },\n { x: 0.609375, y: 0.046875 },\n { x: 0.640625, y: 0.046875 },\n { x: 0.640625, y: 0.046875 },\n { x: 0.671875, y: 0.046875 },\n { x: 0.671875, y: 0.046875 },\n { x: 0.703125, y: 0.046875 },\n { x: 0.703125, y: 0.046875 },\n { x: 0.734375, y: 0.046875 },\n { x: 0.734375, y: 0.046875 },\n { x: 0.765625, y: 0.046875 },\n { x: 0.765625, y: 0.046875 },\n { x: 0.796875, y: 0.046875 },\n { x: 0.796875, y: 0.046875 },\n { x: 0.828125, y: 0.046875 },\n { x: 0.828125, y: 0.046875 },\n { x: 0.859375, y: 0.046875 },\n { x: 0.859375, y: 0.046875 },\n { x: 0.890625, y: 0.046875 },\n { x: 0.890625, y: 0.046875 },\n { x: 0.921875, y: 0.046875 },\n { x: 0.921875, y: 0.046875 },\n { x: 0.953125, y: 0.046875 },\n { x: 0.953125, y: 0.046875 },\n { x: 0.984375, y: 0.046875 },\n { x: 0.984375, y: 0.046875 },\n { x: 0.015625, y: 0.078125 },\n { x: 0.015625, y: 0.078125 },\n { x: 0.046875, y: 0.078125 },\n { x: 0.046875, y: 0.078125 },\n { x: 0.078125, y: 0.078125 },\n { x: 0.078125, y: 0.078125 },\n { x: 0.109375, y: 0.078125 },\n { x: 0.109375, y: 0.078125 },\n { x: 0.140625, y: 0.078125 },\n { x: 0.140625, y: 0.078125 },\n { x: 0.171875, y: 0.078125 },\n { x: 0.171875, y: 0.078125 },\n { x: 0.203125, y: 0.078125 },\n { x: 0.203125, y: 0.078125 },\n { x: 0.234375, y: 0.078125 },\n { x: 0.234375, y: 0.078125 },\n { x: 0.265625, y: 0.078125 },\n { x: 0.265625, y: 0.078125 },\n { x: 0.296875, y: 0.078125 },\n { x: 0.296875, y: 0.078125 },\n { x: 0.328125, y: 0.078125 },\n { x: 0.328125, y: 0.078125 },\n { x: 0.359375, y: 0.078125 },\n { x: 0.359375, y: 0.078125 },\n { x: 0.390625, y: 0.078125 },\n { x: 0.390625, y: 0.078125 },\n { x: 0.421875, y: 0.078125 },\n { x: 0.421875, y: 0.078125 },\n { x: 0.453125, y: 0.078125 },\n { x: 0.453125, y: 0.078125 },\n { x: 0.484375, y: 0.078125 },\n { x: 0.484375, y: 0.078125 },\n { x: 0.515625, y: 0.078125 },\n { x: 0.515625, y: 0.078125 },\n { x: 0.546875, y: 0.078125 },\n { x: 0.546875, y: 0.078125 },\n { x: 0.578125, y: 0.078125 },\n { x: 0.578125, y: 0.078125 },\n { x: 0.609375, y: 0.078125 },\n { x: 0.609375, y: 0.078125 },\n { x: 0.640625, y: 0.078125 },\n { x: 0.640625, y: 0.078125 },\n { x: 0.671875, y: 0.078125 },\n { x: 0.671875, y: 0.078125 },\n { x: 0.703125, y: 0.078125 },\n { x: 0.703125, y: 0.078125 },\n { x: 0.734375, y: 0.078125 },\n { x: 0.734375, y: 0.078125 },\n { x: 0.765625, y: 0.078125 },\n { x: 0.765625, y: 0.078125 },\n { x: 0.796875, y: 0.078125 },\n { x: 0.796875, y: 0.078125 },\n { x: 0.828125, y: 0.078125 },\n { x: 0.828125, y: 0.078125 },\n { x: 0.859375, y: 0.078125 },\n { x: 0.859375, y: 0.078125 },\n { x: 0.890625, y: 0.078125 },\n { x: 0.890625, y: 0.078125 },\n { x: 0.921875, y: 0.078125 },\n { x: 0.921875, y: 0.078125 },\n { x: 0.953125, y: 0.078125 },\n { x: 0.953125, y: 0.078125 },\n { x: 0.984375, y: 0.078125 },\n { x: 0.984375, y: 0.078125 },\n { x: 0.015625, y: 0.109375 },\n { x: 0.015625, y: 0.109375 },\n { x: 0.046875, y: 0.109375 },\n { x: 0.046875, y: 0.109375 },\n { x: 0.078125, y: 0.109375 },\n { x: 0.078125, y: 0.109375 },\n { x: 0.109375, y: 0.109375 },\n { x: 0.109375, y: 0.109375 },\n { x: 0.140625, y: 0.109375 },\n { x: 0.140625, y: 0.109375 },\n { x: 0.171875, y: 0.109375 },\n { x: 0.171875, y: 0.109375 },\n { x: 0.203125, y: 0.109375 },\n { x: 0.203125, y: 0.109375 },\n { x: 0.234375, y: 0.109375 },\n { x: 0.234375, y: 0.109375 },\n { x: 0.265625, y: 0.109375 },\n { x: 0.265625, y: 0.109375 },\n { x: 0.296875, y: 0.109375 },\n { x: 0.296875, y: 0.109375 },\n { x: 0.328125, y: 0.109375 },\n { x: 0.328125, y: 0.109375 },\n { x: 0.359375, y: 0.109375 },\n { x: 0.359375, y: 0.109375 },\n { x: 0.390625, y: 0.109375 },\n { x: 0.390625, y: 0.109375 },\n { x: 0.421875, y: 0.109375 },\n { x: 0.421875, y: 0.109375 },\n { x: 0.453125, y: 0.109375 },\n { x: 0.453125, y: 0.109375 },\n { x: 0.484375, y: 0.109375 },\n { x: 0.484375, y: 0.109375 },\n { x: 0.515625, y: 0.109375 },\n { x: 0.515625, y: 0.109375 },\n { x: 0.546875, y: 0.109375 },\n { x: 0.546875, y: 0.109375 },\n { x: 0.578125, y: 0.109375 },\n { x: 0.578125, y: 0.109375 },\n { x: 0.609375, y: 0.109375 },\n { x: 0.609375, y: 0.109375 },\n { x: 0.640625, y: 0.109375 },\n { x: 0.640625, y: 0.109375 },\n { x: 0.671875, y: 0.109375 },\n { x: 0.671875, y: 0.109375 },\n { x: 0.703125, y: 0.109375 },\n { x: 0.703125, y: 0.109375 },\n { x: 0.734375, y: 0.109375 },\n { x: 0.734375, y: 0.109375 },\n { x: 0.765625, y: 0.109375 },\n { x: 0.765625, y: 0.109375 },\n { x: 0.796875, y: 0.109375 },\n { x: 0.796875, y: 0.109375 },\n { x: 0.828125, y: 0.109375 },\n { x: 0.828125, y: 0.109375 },\n { x: 0.859375, y: 0.109375 },\n { x: 0.859375, y: 0.109375 },\n { x: 0.890625, y: 0.109375 },\n { x: 0.890625, y: 0.109375 },\n { x: 0.921875, y: 0.109375 },\n { x: 0.921875, y: 0.109375 },\n { x: 0.953125, y: 0.109375 },\n { x: 0.953125, y: 0.109375 },\n { x: 0.984375, y: 0.109375 },\n { x: 0.984375, y: 0.109375 },\n { x: 0.015625, y: 0.140625 },\n { x: 0.015625, y: 0.140625 },\n { x: 0.046875, y: 0.140625 },\n { x: 0.046875, y: 0.140625 },\n { x: 0.078125, y: 0.140625 },\n { x: 0.078125, y: 0.140625 },\n { x: 0.109375, y: 0.140625 },\n { x: 0.109375, y: 0.140625 },\n { x: 0.140625, y: 0.140625 },\n { x: 0.140625, y: 0.140625 },\n { x: 0.171875, y: 0.140625 },\n { x: 0.171875, y: 0.140625 },\n { x: 0.203125, y: 0.140625 },\n { x: 0.203125, y: 0.140625 },\n { x: 0.234375, y: 0.140625 },\n { x: 0.234375, y: 0.140625 },\n { x: 0.265625, y: 0.140625 },\n { x: 0.265625, y: 0.140625 },\n { x: 0.296875, y: 0.140625 },\n { x: 0.296875, y: 0.140625 },\n { x: 0.328125, y: 0.140625 },\n { x: 0.328125, y: 0.140625 },\n { x: 0.359375, y: 0.140625 },\n { x: 0.359375, y: 0.140625 },\n { x: 0.390625, y: 0.140625 },\n { x: 0.390625, y: 0.140625 },\n { x: 0.421875, y: 0.140625 },\n { x: 0.421875, y: 0.140625 },\n { x: 0.453125, y: 0.140625 },\n { x: 0.453125, y: 0.140625 },\n { x: 0.484375, y: 0.140625 },\n { x: 0.484375, y: 0.140625 },\n { x: 0.515625, y: 0.140625 },\n { x: 0.515625, y: 0.140625 },\n { x: 0.546875, y: 0.140625 },\n { x: 0.546875, y: 0.140625 },\n { x: 0.578125, y: 0.140625 },\n { x: 0.578125, y: 0.140625 },\n { x: 0.609375, y: 0.140625 },\n { x: 0.609375, y: 0.140625 },\n { x: 0.640625, y: 0.140625 },\n { x: 0.640625, y: 0.140625 },\n { x: 0.671875, y: 0.140625 },\n { x: 0.671875, y: 0.140625 },\n { x: 0.703125, y: 0.140625 },\n { x: 0.703125, y: 0.140625 },\n { x: 0.734375, y: 0.140625 },\n { x: 0.734375, y: 0.140625 },\n { x: 0.765625, y: 0.140625 },\n { x: 0.765625, y: 0.140625 },\n { x: 0.796875, y: 0.140625 },\n { x: 0.796875, y: 0.140625 },\n { x: 0.828125, y: 0.140625 },\n { x: 0.828125, y: 0.140625 },\n { x: 0.859375, y: 0.140625 },\n { x: 0.859375, y: 0.140625 },\n { x: 0.890625, y: 0.140625 },\n { x: 0.890625, y: 0.140625 },\n { x: 0.921875, y: 0.140625 },\n { x: 0.921875, y: 0.140625 },\n { x: 0.953125, y: 0.140625 },\n { x: 0.953125, y: 0.140625 },\n { x: 0.984375, y: 0.140625 },\n { x: 0.984375, y: 0.140625 },\n { x: 0.015625, y: 0.171875 },\n { x: 0.015625, y: 0.171875 },\n { x: 0.046875, y: 0.171875 },\n { x: 0.046875, y: 0.171875 },\n { x: 0.078125, y: 0.171875 },\n { x: 0.078125, y: 0.171875 },\n { x: 0.109375, y: 0.171875 },\n { x: 0.109375, y: 0.171875 },\n { x: 0.140625, y: 0.171875 },\n { x: 0.140625, y: 0.171875 },\n { x: 0.171875, y: 0.171875 },\n { x: 0.171875, y: 0.171875 },\n { x: 0.203125, y: 0.171875 },\n { x: 0.203125, y: 0.171875 },\n { x: 0.234375, y: 0.171875 },\n { x: 0.234375, y: 0.171875 },\n { x: 0.265625, y: 0.171875 },\n { x: 0.265625, y: 0.171875 },\n { x: 0.296875, y: 0.171875 },\n { x: 0.296875, y: 0.171875 },\n { x: 0.328125, y: 0.171875 },\n { x: 0.328125, y: 0.171875 },\n { x: 0.359375, y: 0.171875 },\n { x: 0.359375, y: 0.171875 },\n { x: 0.390625, y: 0.171875 },\n { x: 0.390625, y: 0.171875 },\n { x: 0.421875, y: 0.171875 },\n { x: 0.421875, y: 0.171875 },\n { x: 0.453125, y: 0.171875 },\n { x: 0.453125, y: 0.171875 },\n { x: 0.484375, y: 0.171875 },\n { x: 0.484375, y: 0.171875 },\n { x: 0.515625, y: 0.171875 },\n { x: 0.515625, y: 0.171875 },\n { x: 0.546875, y: 0.171875 },\n { x: 0.546875, y: 0.171875 },\n { x: 0.578125, y: 0.171875 },\n { x: 0.578125, y: 0.171875 },\n { x: 0.609375, y: 0.171875 },\n { x: 0.609375, y: 0.171875 },\n { x: 0.640625, y: 0.171875 },\n { x: 0.640625, y: 0.171875 },\n { x: 0.671875, y: 0.171875 },\n { x: 0.671875, y: 0.171875 },\n { x: 0.703125, y: 0.171875 },\n { x: 0.703125, y: 0.171875 },\n { x: 0.734375, y: 0.171875 },\n { x: 0.734375, y: 0.171875 },\n { x: 0.765625, y: 0.171875 },\n { x: 0.765625, y: 0.171875 },\n { x: 0.796875, y: 0.171875 },\n { x: 0.796875, y: 0.171875 },\n { x: 0.828125, y: 0.171875 },\n { x: 0.828125, y: 0.171875 },\n { x: 0.859375, y: 0.171875 },\n { x: 0.859375, y: 0.171875 },\n { x: 0.890625, y: 0.171875 },\n { x: 0.890625, y: 0.171875 },\n { x: 0.921875, y: 0.171875 },\n { x: 0.921875, y: 0.171875 },\n { x: 0.953125, y: 0.171875 },\n { x: 0.953125, y: 0.171875 },\n { x: 0.984375, y: 0.171875 },\n { x: 0.984375, y: 0.171875 },\n { x: 0.015625, y: 0.203125 },\n { x: 0.015625, y: 0.203125 },\n { x: 0.046875, y: 0.203125 },\n { x: 0.046875, y: 0.203125 },\n { x: 0.078125, y: 0.203125 },\n { x: 0.078125, y: 0.203125 },\n { x: 0.109375, y: 0.203125 },\n { x: 0.109375, y: 0.203125 },\n { x: 0.140625, y: 0.203125 },\n { x: 0.140625, y: 0.203125 },\n { x: 0.171875, y: 0.203125 },\n { x: 0.171875, y: 0.203125 },\n { x: 0.203125, y: 0.203125 },\n { x: 0.203125, y: 0.203125 },\n { x: 0.234375, y: 0.203125 },\n { x: 0.234375, y: 0.203125 },\n { x: 0.265625, y: 0.203125 },\n { x: 0.265625, y: 0.203125 },\n { x: 0.296875, y: 0.203125 },\n { x: 0.296875, y: 0.203125 },\n { x: 0.328125, y: 0.203125 },\n { x: 0.328125, y: 0.203125 },\n { x: 0.359375, y: 0.203125 },\n { x: 0.359375, y: 0.203125 },\n { x: 0.390625, y: 0.203125 },\n { x: 0.390625, y: 0.203125 },\n { x: 0.421875, y: 0.203125 },\n { x: 0.421875, y: 0.203125 },\n { x: 0.453125, y: 0.203125 },\n { x: 0.453125, y: 0.203125 },\n { x: 0.484375, y: 0.203125 },\n { x: 0.484375, y: 0.203125 },\n { x: 0.515625, y: 0.203125 },\n { x: 0.515625, y: 0.203125 },\n { x: 0.546875, y: 0.203125 },\n { x: 0.546875, y: 0.203125 },\n { x: 0.578125, y: 0.203125 },\n { x: 0.578125, y: 0.203125 },\n { x: 0.609375, y: 0.203125 },\n { x: 0.609375, y: 0.203125 },\n { x: 0.640625, y: 0.203125 },\n { x: 0.640625, y: 0.203125 },\n { x: 0.671875, y: 0.203125 },\n { x: 0.671875, y: 0.203125 },\n { x: 0.703125, y: 0.203125 },\n { x: 0.703125, y: 0.203125 },\n { x: 0.734375, y: 0.203125 },\n { x: 0.734375, y: 0.203125 },\n { x: 0.765625, y: 0.203125 },\n { x: 0.765625, y: 0.203125 },\n { x: 0.796875, y: 0.203125 },\n { x: 0.796875, y: 0.203125 },\n { x: 0.828125, y: 0.203125 },\n { x: 0.828125, y: 0.203125 },\n { x: 0.859375, y: 0.203125 },\n { x: 0.859375, y: 0.203125 },\n { x: 0.890625, y: 0.203125 },\n { x: 0.890625, y: 0.203125 },\n { x: 0.921875, y: 0.203125 },\n { x: 0.921875, y: 0.203125 },\n { x: 0.953125, y: 0.203125 },\n { x: 0.953125, y: 0.203125 },\n { x: 0.984375, y: 0.203125 },\n { x: 0.984375, y: 0.203125 },\n { x: 0.015625, y: 0.234375 },\n { x: 0.015625, y: 0.234375 },\n { x: 0.046875, y: 0.234375 },\n { x: 0.046875, y: 0.234375 },\n { x: 0.078125, y: 0.234375 },\n { x: 0.078125, y: 0.234375 },\n { x: 0.109375, y: 0.234375 },\n { x: 0.109375, y: 0.234375 },\n { x: 0.140625, y: 0.234375 },\n { x: 0.140625, y: 0.234375 },\n { x: 0.171875, y: 0.234375 },\n { x: 0.171875, y: 0.234375 },\n { x: 0.203125, y: 0.234375 },\n { x: 0.203125, y: 0.234375 },\n { x: 0.234375, y: 0.234375 },\n { x: 0.234375, y: 0.234375 },\n { x: 0.265625, y: 0.234375 },\n { x: 0.265625, y: 0.234375 },\n { x: 0.296875, y: 0.234375 },\n { x: 0.296875, y: 0.234375 },\n { x: 0.328125, y: 0.234375 },\n { x: 0.328125, y: 0.234375 },\n { x: 0.359375, y: 0.234375 },\n { x: 0.359375, y: 0.234375 },\n { x: 0.390625, y: 0.234375 },\n { x: 0.390625, y: 0.234375 },\n { x: 0.421875, y: 0.234375 },\n { x: 0.421875, y: 0.234375 },\n { x: 0.453125, y: 0.234375 },\n { x: 0.453125, y: 0.234375 },\n { x: 0.484375, y: 0.234375 },\n { x: 0.484375, y: 0.234375 },\n { x: 0.515625, y: 0.234375 },\n { x: 0.515625, y: 0.234375 },\n { x: 0.546875, y: 0.234375 },\n { x: 0.546875, y: 0.234375 },\n { x: 0.578125, y: 0.234375 },\n { x: 0.578125, y: 0.234375 },\n { x: 0.609375, y: 0.234375 },\n { x: 0.609375, y: 0.234375 },\n { x: 0.640625, y: 0.234375 },\n { x: 0.640625, y: 0.234375 },\n { x: 0.671875, y: 0.234375 },\n { x: 0.671875, y: 0.234375 },\n { x: 0.703125, y: 0.234375 },\n { x: 0.703125, y: 0.234375 },\n { x: 0.734375, y: 0.234375 },\n { x: 0.734375, y: 0.234375 },\n { x: 0.765625, y: 0.234375 },\n { x: 0.765625, y: 0.234375 },\n { x: 0.796875, y: 0.234375 },\n { x: 0.796875, y: 0.234375 },\n { x: 0.828125, y: 0.234375 },\n { x: 0.828125, y: 0.234375 },\n { x: 0.859375, y: 0.234375 },\n { x: 0.859375, y: 0.234375 },\n { x: 0.890625, y: 0.234375 },\n { x: 0.890625, y: 0.234375 },\n { x: 0.921875, y: 0.234375 },\n { x: 0.921875, y: 0.234375 },\n { x: 0.953125, y: 0.234375 },\n { x: 0.953125, y: 0.234375 },\n { x: 0.984375, y: 0.234375 },\n { x: 0.984375, y: 0.234375 },\n { x: 0.015625, y: 0.265625 },\n { x: 0.015625, y: 0.265625 },\n { x: 0.046875, y: 0.265625 },\n { x: 0.046875, y: 0.265625 },\n { x: 0.078125, y: 0.265625 },\n { x: 0.078125, y: 0.265625 },\n { x: 0.109375, y: 0.265625 },\n { x: 0.109375, y: 0.265625 },\n { x: 0.140625, y: 0.265625 },\n { x: 0.140625, y: 0.265625 },\n { x: 0.171875, y: 0.265625 },\n { x: 0.171875, y: 0.265625 },\n { x: 0.203125, y: 0.265625 },\n { x: 0.203125, y: 0.265625 },\n { x: 0.234375, y: 0.265625 },\n { x: 0.234375, y: 0.265625 },\n { x: 0.265625, y: 0.265625 },\n { x: 0.265625, y: 0.265625 },\n { x: 0.296875, y: 0.265625 },\n { x: 0.296875, y: 0.265625 },\n { x: 0.328125, y: 0.265625 },\n { x: 0.328125, y: 0.265625 },\n { x: 0.359375, y: 0.265625 },\n { x: 0.359375, y: 0.265625 },\n { x: 0.390625, y: 0.265625 },\n { x: 0.390625, y: 0.265625 },\n { x: 0.421875, y: 0.265625 },\n { x: 0.421875, y: 0.265625 },\n { x: 0.453125, y: 0.265625 },\n { x: 0.453125, y: 0.265625 },\n { x: 0.484375, y: 0.265625 },\n { x: 0.484375, y: 0.265625 },\n { x: 0.515625, y: 0.265625 },\n { x: 0.515625, y: 0.265625 },\n { x: 0.546875, y: 0.265625 },\n { x: 0.546875, y: 0.265625 },\n { x: 0.578125, y: 0.265625 },\n { x: 0.578125, y: 0.265625 },\n { x: 0.609375, y: 0.265625 },\n { x: 0.609375, y: 0.265625 },\n { x: 0.640625, y: 0.265625 },\n { x: 0.640625, y: 0.265625 },\n { x: 0.671875, y: 0.265625 },\n { x: 0.671875, y: 0.265625 },\n { x: 0.703125, y: 0.265625 },\n { x: 0.703125, y: 0.265625 },\n { x: 0.734375, y: 0.265625 },\n { x: 0.734375, y: 0.265625 },\n { x: 0.765625, y: 0.265625 },\n { x: 0.765625, y: 0.265625 },\n { x: 0.796875, y: 0.265625 },\n { x: 0.796875, y: 0.265625 },\n { x: 0.828125, y: 0.265625 },\n { x: 0.828125, y: 0.265625 },\n { x: 0.859375, y: 0.265625 },\n { x: 0.859375, y: 0.265625 },\n { x: 0.890625, y: 0.265625 },\n { x: 0.890625, y: 0.265625 },\n { x: 0.921875, y: 0.265625 },\n { x: 0.921875, y: 0.265625 },\n { x: 0.953125, y: 0.265625 },\n { x: 0.953125, y: 0.265625 },\n { x: 0.984375, y: 0.265625 },\n { x: 0.984375, y: 0.265625 },\n { x: 0.015625, y: 0.296875 },\n { x: 0.015625, y: 0.296875 },\n { x: 0.046875, y: 0.296875 },\n { x: 0.046875, y: 0.296875 },\n { x: 0.078125, y: 0.296875 },\n { x: 0.078125, y: 0.296875 },\n { x: 0.109375, y: 0.296875 },\n { x: 0.109375, y: 0.296875 },\n { x: 0.140625, y: 0.296875 },\n { x: 0.140625, y: 0.296875 },\n { x: 0.171875, y: 0.296875 },\n { x: 0.171875, y: 0.296875 },\n { x: 0.203125, y: 0.296875 },\n { x: 0.203125, y: 0.296875 },\n { x: 0.234375, y: 0.296875 },\n { x: 0.234375, y: 0.296875 },\n { x: 0.265625, y: 0.296875 },\n { x: 0.265625, y: 0.296875 },\n { x: 0.296875, y: 0.296875 },\n { x: 0.296875, y: 0.296875 },\n { x: 0.328125, y: 0.296875 },\n { x: 0.328125, y: 0.296875 },\n { x: 0.359375, y: 0.296875 },\n { x: 0.359375, y: 0.296875 },\n { x: 0.390625, y: 0.296875 },\n { x: 0.390625, y: 0.296875 },\n { x: 0.421875, y: 0.296875 },\n { x: 0.421875, y: 0.296875 },\n { x: 0.453125, y: 0.296875 },\n { x: 0.453125, y: 0.296875 },\n { x: 0.484375, y: 0.296875 },\n { x: 0.484375, y: 0.296875 },\n { x: 0.515625, y: 0.296875 },\n { x: 0.515625, y: 0.296875 },\n { x: 0.546875, y: 0.296875 },\n { x: 0.546875, y: 0.296875 },\n { x: 0.578125, y: 0.296875 },\n { x: 0.578125, y: 0.296875 },\n { x: 0.609375, y: 0.296875 },\n { x: 0.609375, y: 0.296875 },\n { x: 0.640625, y: 0.296875 },\n { x: 0.640625, y: 0.296875 },\n { x: 0.671875, y: 0.296875 },\n { x: 0.671875, y: 0.296875 },\n { x: 0.703125, y: 0.296875 },\n { x: 0.703125, y: 0.296875 },\n { x: 0.734375, y: 0.296875 },\n { x: 0.734375, y: 0.296875 },\n { x: 0.765625, y: 0.296875 },\n { x: 0.765625, y: 0.296875 },\n { x: 0.796875, y: 0.296875 },\n { x: 0.796875, y: 0.296875 },\n { x: 0.828125, y: 0.296875 },\n { x: 0.828125, y: 0.296875 },\n { x: 0.859375, y: 0.296875 },\n { x: 0.859375, y: 0.296875 },\n { x: 0.890625, y: 0.296875 },\n { x: 0.890625, y: 0.296875 },\n { x: 0.921875, y: 0.296875 },\n { x: 0.921875, y: 0.296875 },\n { x: 0.953125, y: 0.296875 },\n { x: 0.953125, y: 0.296875 },\n { x: 0.984375, y: 0.296875 },\n { x: 0.984375, y: 0.296875 },\n { x: 0.015625, y: 0.328125 },\n { x: 0.015625, y: 0.328125 },\n { x: 0.046875, y: 0.328125 },\n { x: 0.046875, y: 0.328125 },\n { x: 0.078125, y: 0.328125 },\n { x: 0.078125, y: 0.328125 },\n { x: 0.109375, y: 0.328125 },\n { x: 0.109375, y: 0.328125 },\n { x: 0.140625, y: 0.328125 },\n { x: 0.140625, y: 0.328125 },\n { x: 0.171875, y: 0.328125 },\n { x: 0.171875, y: 0.328125 },\n { x: 0.203125, y: 0.328125 },\n { x: 0.203125, y: 0.328125 },\n { x: 0.234375, y: 0.328125 },\n { x: 0.234375, y: 0.328125 },\n { x: 0.265625, y: 0.328125 },\n { x: 0.265625, y: 0.328125 },\n { x: 0.296875, y: 0.328125 },\n { x: 0.296875, y: 0.328125 },\n { x: 0.328125, y: 0.328125 },\n { x: 0.328125, y: 0.328125 },\n { x: 0.359375, y: 0.328125 },\n { x: 0.359375, y: 0.328125 },\n { x: 0.390625, y: 0.328125 },\n { x: 0.390625, y: 0.328125 },\n { x: 0.421875, y: 0.328125 },\n { x: 0.421875, y: 0.328125 },\n { x: 0.453125, y: 0.328125 },\n { x: 0.453125, y: 0.328125 },\n { x: 0.484375, y: 0.328125 },\n { x: 0.484375, y: 0.328125 },\n { x: 0.515625, y: 0.328125 },\n { x: 0.515625, y: 0.328125 },\n { x: 0.546875, y: 0.328125 },\n { x: 0.546875, y: 0.328125 },\n { x: 0.578125, y: 0.328125 },\n { x: 0.578125, y: 0.328125 },\n { x: 0.609375, y: 0.328125 },\n { x: 0.609375, y: 0.328125 },\n { x: 0.640625, y: 0.328125 },\n { x: 0.640625, y: 0.328125 },\n { x: 0.671875, y: 0.328125 },\n { x: 0.671875, y: 0.328125 },\n { x: 0.703125, y: 0.328125 },\n { x: 0.703125, y: 0.328125 },\n { x: 0.734375, y: 0.328125 },\n { x: 0.734375, y: 0.328125 },\n { x: 0.765625, y: 0.328125 },\n { x: 0.765625, y: 0.328125 },\n { x: 0.796875, y: 0.328125 },\n { x: 0.796875, y: 0.328125 },\n { x: 0.828125, y: 0.328125 },\n { x: 0.828125, y: 0.328125 },\n { x: 0.859375, y: 0.328125 },\n { x: 0.859375, y: 0.328125 },\n { x: 0.890625, y: 0.328125 },\n { x: 0.890625, y: 0.328125 },\n { x: 0.921875, y: 0.328125 },\n { x: 0.921875, y: 0.328125 },\n { x: 0.953125, y: 0.328125 },\n { x: 0.953125, y: 0.328125 },\n { x: 0.984375, y: 0.328125 },\n { x: 0.984375, y: 0.328125 },\n { x: 0.015625, y: 0.359375 },\n { x: 0.015625, y: 0.359375 },\n { x: 0.046875, y: 0.359375 },\n { x: 0.046875, y: 0.359375 },\n { x: 0.078125, y: 0.359375 },\n { x: 0.078125, y: 0.359375 },\n { x: 0.109375, y: 0.359375 },\n { x: 0.109375, y: 0.359375 },\n { x: 0.140625, y: 0.359375 },\n { x: 0.140625, y: 0.359375 },\n { x: 0.171875, y: 0.359375 },\n { x: 0.171875, y: 0.359375 },\n { x: 0.203125, y: 0.359375 },\n { x: 0.203125, y: 0.359375 },\n { x: 0.234375, y: 0.359375 },\n { x: 0.234375, y: 0.359375 },\n { x: 0.265625, y: 0.359375 },\n { x: 0.265625, y: 0.359375 },\n { x: 0.296875, y: 0.359375 },\n { x: 0.296875, y: 0.359375 },\n { x: 0.328125, y: 0.359375 },\n { x: 0.328125, y: 0.359375 },\n { x: 0.359375, y: 0.359375 },\n { x: 0.359375, y: 0.359375 },\n { x: 0.390625, y: 0.359375 },\n { x: 0.390625, y: 0.359375 },\n { x: 0.421875, y: 0.359375 },\n { x: 0.421875, y: 0.359375 },\n { x: 0.453125, y: 0.359375 },\n { x: 0.453125, y: 0.359375 },\n { x: 0.484375, y: 0.359375 },\n { x: 0.484375, y: 0.359375 },\n { x: 0.515625, y: 0.359375 },\n { x: 0.515625, y: 0.359375 },\n { x: 0.546875, y: 0.359375 },\n { x: 0.546875, y: 0.359375 },\n { x: 0.578125, y: 0.359375 },\n { x: 0.578125, y: 0.359375 },\n { x: 0.609375, y: 0.359375 },\n { x: 0.609375, y: 0.359375 },\n { x: 0.640625, y: 0.359375 },\n { x: 0.640625, y: 0.359375 },\n { x: 0.671875, y: 0.359375 },\n { x: 0.671875, y: 0.359375 },\n { x: 0.703125, y: 0.359375 },\n { x: 0.703125, y: 0.359375 },\n { x: 0.734375, y: 0.359375 },\n { x: 0.734375, y: 0.359375 },\n { x: 0.765625, y: 0.359375 },\n { x: 0.765625, y: 0.359375 },\n { x: 0.796875, y: 0.359375 },\n { x: 0.796875, y: 0.359375 },\n { x: 0.828125, y: 0.359375 },\n { x: 0.828125, y: 0.359375 },\n { x: 0.859375, y: 0.359375 },\n { x: 0.859375, y: 0.359375 },\n { x: 0.890625, y: 0.359375 },\n { x: 0.890625, y: 0.359375 },\n { x: 0.921875, y: 0.359375 },\n { x: 0.921875, y: 0.359375 },\n { x: 0.953125, y: 0.359375 },\n { x: 0.953125, y: 0.359375 },\n { x: 0.984375, y: 0.359375 },\n { x: 0.984375, y: 0.359375 },\n { x: 0.015625, y: 0.390625 },\n { x: 0.015625, y: 0.390625 },\n { x: 0.046875, y: 0.390625 },\n { x: 0.046875, y: 0.390625 },\n { x: 0.078125, y: 0.390625 },\n { x: 0.078125, y: 0.390625 },\n { x: 0.109375, y: 0.390625 },\n { x: 0.109375, y: 0.390625 },\n { x: 0.140625, y: 0.390625 },\n { x: 0.140625, y: 0.390625 },\n { x: 0.171875, y: 0.390625 },\n { x: 0.171875, y: 0.390625 },\n { x: 0.203125, y: 0.390625 },\n { x: 0.203125, y: 0.390625 },\n { x: 0.234375, y: 0.390625 },\n { x: 0.234375, y: 0.390625 },\n { x: 0.265625, y: 0.390625 },\n { x: 0.265625, y: 0.390625 },\n { x: 0.296875, y: 0.390625 },\n { x: 0.296875, y: 0.390625 },\n { x: 0.328125, y: 0.390625 },\n { x: 0.328125, y: 0.390625 },\n { x: 0.359375, y: 0.390625 },\n { x: 0.359375, y: 0.390625 },\n { x: 0.390625, y: 0.390625 },\n { x: 0.390625, y: 0.390625 },\n { x: 0.421875, y: 0.390625 },\n { x: 0.421875, y: 0.390625 },\n { x: 0.453125, y: 0.390625 },\n { x: 0.453125, y: 0.390625 },\n { x: 0.484375, y: 0.390625 },\n { x: 0.484375, y: 0.390625 },\n { x: 0.515625, y: 0.390625 },\n { x: 0.515625, y: 0.390625 },\n { x: 0.546875, y: 0.390625 },\n { x: 0.546875, y: 0.390625 },\n { x: 0.578125, y: 0.390625 },\n { x: 0.578125, y: 0.390625 },\n { x: 0.609375, y: 0.390625 },\n { x: 0.609375, y: 0.390625 },\n { x: 0.640625, y: 0.390625 },\n { x: 0.640625, y: 0.390625 },\n { x: 0.671875, y: 0.390625 },\n { x: 0.671875, y: 0.390625 },\n { x: 0.703125, y: 0.390625 },\n { x: 0.703125, y: 0.390625 },\n { x: 0.734375, y: 0.390625 },\n { x: 0.734375, y: 0.390625 },\n { x: 0.765625, y: 0.390625 },\n { x: 0.765625, y: 0.390625 },\n { x: 0.796875, y: 0.390625 },\n { x: 0.796875, y: 0.390625 },\n { x: 0.828125, y: 0.390625 },\n { x: 0.828125, y: 0.390625 },\n { x: 0.859375, y: 0.390625 },\n { x: 0.859375, y: 0.390625 },\n { x: 0.890625, y: 0.390625 },\n { x: 0.890625, y: 0.390625 },\n { x: 0.921875, y: 0.390625 },\n { x: 0.921875, y: 0.390625 },\n { x: 0.953125, y: 0.390625 },\n { x: 0.953125, y: 0.390625 },\n { x: 0.984375, y: 0.390625 },\n { x: 0.984375, y: 0.390625 },\n { x: 0.015625, y: 0.421875 },\n { x: 0.015625, y: 0.421875 },\n { x: 0.046875, y: 0.421875 },\n { x: 0.046875, y: 0.421875 },\n { x: 0.078125, y: 0.421875 },\n { x: 0.078125, y: 0.421875 },\n { x: 0.109375, y: 0.421875 },\n { x: 0.109375, y: 0.421875 },\n { x: 0.140625, y: 0.421875 },\n { x: 0.140625, y: 0.421875 },\n { x: 0.171875, y: 0.421875 },\n { x: 0.171875, y: 0.421875 },\n { x: 0.203125, y: 0.421875 },\n { x: 0.203125, y: 0.421875 },\n { x: 0.234375, y: 0.421875 },\n { x: 0.234375, y: 0.421875 },\n { x: 0.265625, y: 0.421875 },\n { x: 0.265625, y: 0.421875 },\n { x: 0.296875, y: 0.421875 },\n { x: 0.296875, y: 0.421875 },\n { x: 0.328125, y: 0.421875 },\n { x: 0.328125, y: 0.421875 },\n { x: 0.359375, y: 0.421875 },\n { x: 0.359375, y: 0.421875 },\n { x: 0.390625, y: 0.421875 },\n { x: 0.390625, y: 0.421875 },\n { x: 0.421875, y: 0.421875 },\n { x: 0.421875, y: 0.421875 },\n { x: 0.453125, y: 0.421875 },\n { x: 0.453125, y: 0.421875 },\n { x: 0.484375, y: 0.421875 },\n { x: 0.484375, y: 0.421875 },\n { x: 0.515625, y: 0.421875 },\n { x: 0.515625, y: 0.421875 },\n { x: 0.546875, y: 0.421875 },\n { x: 0.546875, y: 0.421875 },\n { x: 0.578125, y: 0.421875 },\n { x: 0.578125, y: 0.421875 },\n { x: 0.609375, y: 0.421875 },\n { x: 0.609375, y: 0.421875 },\n { x: 0.640625, y: 0.421875 },\n { x: 0.640625, y: 0.421875 },\n { x: 0.671875, y: 0.421875 },\n { x: 0.671875, y: 0.421875 },\n { x: 0.703125, y: 0.421875 },\n { x: 0.703125, y: 0.421875 },\n { x: 0.734375, y: 0.421875 },\n { x: 0.734375, y: 0.421875 },\n { x: 0.765625, y: 0.421875 },\n { x: 0.765625, y: 0.421875 },\n { x: 0.796875, y: 0.421875 },\n { x: 0.796875, y: 0.421875 },\n { x: 0.828125, y: 0.421875 },\n { x: 0.828125, y: 0.421875 },\n { x: 0.859375, y: 0.421875 },\n { x: 0.859375, y: 0.421875 },\n { x: 0.890625, y: 0.421875 },\n { x: 0.890625, y: 0.421875 },\n { x: 0.921875, y: 0.421875 },\n { x: 0.921875, y: 0.421875 },\n { x: 0.953125, y: 0.421875 },\n { x: 0.953125, y: 0.421875 },\n { x: 0.984375, y: 0.421875 },\n { x: 0.984375, y: 0.421875 },\n { x: 0.015625, y: 0.453125 },\n { x: 0.015625, y: 0.453125 },\n { x: 0.046875, y: 0.453125 },\n { x: 0.046875, y: 0.453125 },\n { x: 0.078125, y: 0.453125 },\n { x: 0.078125, y: 0.453125 },\n { x: 0.109375, y: 0.453125 },\n { x: 0.109375, y: 0.453125 },\n { x: 0.140625, y: 0.453125 },\n { x: 0.140625, y: 0.453125 },\n { x: 0.171875, y: 0.453125 },\n { x: 0.171875, y: 0.453125 },\n { x: 0.203125, y: 0.453125 },\n { x: 0.203125, y: 0.453125 },\n { x: 0.234375, y: 0.453125 },\n { x: 0.234375, y: 0.453125 },\n { x: 0.265625, y: 0.453125 },\n { x: 0.265625, y: 0.453125 },\n { x: 0.296875, y: 0.453125 },\n { x: 0.296875, y: 0.453125 },\n { x: 0.328125, y: 0.453125 },\n { x: 0.328125, y: 0.453125 },\n { x: 0.359375, y: 0.453125 },\n { x: 0.359375, y: 0.453125 },\n { x: 0.390625, y: 0.453125 },\n { x: 0.390625, y: 0.453125 },\n { x: 0.421875, y: 0.453125 },\n { x: 0.421875, y: 0.453125 },\n { x: 0.453125, y: 0.453125 },\n { x: 0.453125, y: 0.453125 },\n { x: 0.484375, y: 0.453125 },\n { x: 0.484375, y: 0.453125 },\n { x: 0.515625, y: 0.453125 },\n { x: 0.515625, y: 0.453125 },\n { x: 0.546875, y: 0.453125 },\n { x: 0.546875, y: 0.453125 },\n { x: 0.578125, y: 0.453125 },\n { x: 0.578125, y: 0.453125 },\n { x: 0.609375, y: 0.453125 },\n { x: 0.609375, y: 0.453125 },\n { x: 0.640625, y: 0.453125 },\n { x: 0.640625, y: 0.453125 },\n { x: 0.671875, y: 0.453125 },\n { x: 0.671875, y: 0.453125 },\n { x: 0.703125, y: 0.453125 },\n { x: 0.703125, y: 0.453125 },\n { x: 0.734375, y: 0.453125 },\n { x: 0.734375, y: 0.453125 },\n { x: 0.765625, y: 0.453125 },\n { x: 0.765625, y: 0.453125 },\n { x: 0.796875, y: 0.453125 },\n { x: 0.796875, y: 0.453125 },\n { x: 0.828125, y: 0.453125 },\n { x: 0.828125, y: 0.453125 },\n { x: 0.859375, y: 0.453125 },\n { x: 0.859375, y: 0.453125 },\n { x: 0.890625, y: 0.453125 },\n { x: 0.890625, y: 0.453125 },\n { x: 0.921875, y: 0.453125 },\n { x: 0.921875, y: 0.453125 },\n { x: 0.953125, y: 0.453125 },\n { x: 0.953125, y: 0.453125 },\n { x: 0.984375, y: 0.453125 },\n { x: 0.984375, y: 0.453125 },\n { x: 0.015625, y: 0.484375 },\n { x: 0.015625, y: 0.484375 },\n { x: 0.046875, y: 0.484375 },\n { x: 0.046875, y: 0.484375 },\n { x: 0.078125, y: 0.484375 },\n { x: 0.078125, y: 0.484375 },\n { x: 0.109375, y: 0.484375 },\n { x: 0.109375, y: 0.484375 },\n { x: 0.140625, y: 0.484375 },\n { x: 0.140625, y: 0.484375 },\n { x: 0.171875, y: 0.484375 },\n { x: 0.171875, y: 0.484375 },\n { x: 0.203125, y: 0.484375 },\n { x: 0.203125, y: 0.484375 },\n { x: 0.234375, y: 0.484375 },\n { x: 0.234375, y: 0.484375 },\n { x: 0.265625, y: 0.484375 },\n { x: 0.265625, y: 0.484375 },\n { x: 0.296875, y: 0.484375 },\n { x: 0.296875, y: 0.484375 },\n { x: 0.328125, y: 0.484375 },\n { x: 0.328125, y: 0.484375 },\n { x: 0.359375, y: 0.484375 },\n { x: 0.359375, y: 0.484375 },\n { x: 0.390625, y: 0.484375 },\n { x: 0.390625, y: 0.484375 },\n { x: 0.421875, y: 0.484375 },\n { x: 0.421875, y: 0.484375 },\n { x: 0.453125, y: 0.484375 },\n { x: 0.453125, y: 0.484375 },\n { x: 0.484375, y: 0.484375 },\n { x: 0.484375, y: 0.484375 },\n { x: 0.515625, y: 0.484375 },\n { x: 0.515625, y: 0.484375 },\n { x: 0.546875, y: 0.484375 },\n { x: 0.546875, y: 0.484375 },\n { x: 0.578125, y: 0.484375 },\n { x: 0.578125, y: 0.484375 },\n { x: 0.609375, y: 0.484375 },\n { x: 0.609375, y: 0.484375 },\n { x: 0.640625, y: 0.484375 },\n { x: 0.640625, y: 0.484375 },\n { x: 0.671875, y: 0.484375 },\n { x: 0.671875, y: 0.484375 },\n { x: 0.703125, y: 0.484375 },\n { x: 0.703125, y: 0.484375 },\n { x: 0.734375, y: 0.484375 },\n { x: 0.734375, y: 0.484375 },\n { x: 0.765625, y: 0.484375 },\n { x: 0.765625, y: 0.484375 },\n { x: 0.796875, y: 0.484375 },\n { x: 0.796875, y: 0.484375 },\n { x: 0.828125, y: 0.484375 },\n { x: 0.828125, y: 0.484375 },\n { x: 0.859375, y: 0.484375 },\n { x: 0.859375, y: 0.484375 },\n { x: 0.890625, y: 0.484375 },\n { x: 0.890625, y: 0.484375 },\n { x: 0.921875, y: 0.484375 },\n { x: 0.921875, y: 0.484375 },\n { x: 0.953125, y: 0.484375 },\n { x: 0.953125, y: 0.484375 },\n { x: 0.984375, y: 0.484375 },\n { x: 0.984375, y: 0.484375 },\n { x: 0.015625, y: 0.515625 },\n { x: 0.015625, y: 0.515625 },\n { x: 0.046875, y: 0.515625 },\n { x: 0.046875, y: 0.515625 },\n { x: 0.078125, y: 0.515625 },\n { x: 0.078125, y: 0.515625 },\n { x: 0.109375, y: 0.515625 },\n { x: 0.109375, y: 0.515625 },\n { x: 0.140625, y: 0.515625 },\n { x: 0.140625, y: 0.515625 },\n { x: 0.171875, y: 0.515625 },\n { x: 0.171875, y: 0.515625 },\n { x: 0.203125, y: 0.515625 },\n { x: 0.203125, y: 0.515625 },\n { x: 0.234375, y: 0.515625 },\n { x: 0.234375, y: 0.515625 },\n { x: 0.265625, y: 0.515625 },\n { x: 0.265625, y: 0.515625 },\n { x: 0.296875, y: 0.515625 },\n { x: 0.296875, y: 0.515625 },\n { x: 0.328125, y: 0.515625 },\n { x: 0.328125, y: 0.515625 },\n { x: 0.359375, y: 0.515625 },\n { x: 0.359375, y: 0.515625 },\n { x: 0.390625, y: 0.515625 },\n { x: 0.390625, y: 0.515625 },\n { x: 0.421875, y: 0.515625 },\n { x: 0.421875, y: 0.515625 },\n { x: 0.453125, y: 0.515625 },\n { x: 0.453125, y: 0.515625 },\n { x: 0.484375, y: 0.515625 },\n { x: 0.484375, y: 0.515625 },\n { x: 0.515625, y: 0.515625 },\n { x: 0.515625, y: 0.515625 },\n { x: 0.546875, y: 0.515625 },\n { x: 0.546875, y: 0.515625 },\n { x: 0.578125, y: 0.515625 },\n { x: 0.578125, y: 0.515625 },\n { x: 0.609375, y: 0.515625 },\n { x: 0.609375, y: 0.515625 },\n { x: 0.640625, y: 0.515625 },\n { x: 0.640625, y: 0.515625 },\n { x: 0.671875, y: 0.515625 },\n { x: 0.671875, y: 0.515625 },\n { x: 0.703125, y: 0.515625 },\n { x: 0.703125, y: 0.515625 },\n { x: 0.734375, y: 0.515625 },\n { x: 0.734375, y: 0.515625 },\n { x: 0.765625, y: 0.515625 },\n { x: 0.765625, y: 0.515625 },\n { x: 0.796875, y: 0.515625 },\n { x: 0.796875, y: 0.515625 },\n { x: 0.828125, y: 0.515625 },\n { x: 0.828125, y: 0.515625 },\n { x: 0.859375, y: 0.515625 },\n { x: 0.859375, y: 0.515625 },\n { x: 0.890625, y: 0.515625 },\n { x: 0.890625, y: 0.515625 },\n { x: 0.921875, y: 0.515625 },\n { x: 0.921875, y: 0.515625 },\n { x: 0.953125, y: 0.515625 },\n { x: 0.953125, y: 0.515625 },\n { x: 0.984375, y: 0.515625 },\n { x: 0.984375, y: 0.515625 },\n { x: 0.015625, y: 0.546875 },\n { x: 0.015625, y: 0.546875 },\n { x: 0.046875, y: 0.546875 },\n { x: 0.046875, y: 0.546875 },\n { x: 0.078125, y: 0.546875 },\n { x: 0.078125, y: 0.546875 },\n { x: 0.109375, y: 0.546875 },\n { x: 0.109375, y: 0.546875 },\n { x: 0.140625, y: 0.546875 },\n { x: 0.140625, y: 0.546875 },\n { x: 0.171875, y: 0.546875 },\n { x: 0.171875, y: 0.546875 },\n { x: 0.203125, y: 0.546875 },\n { x: 0.203125, y: 0.546875 },\n { x: 0.234375, y: 0.546875 },\n { x: 0.234375, y: 0.546875 },\n { x: 0.265625, y: 0.546875 },\n { x: 0.265625, y: 0.546875 },\n { x: 0.296875, y: 0.546875 },\n { x: 0.296875, y: 0.546875 },\n { x: 0.328125, y: 0.546875 },\n { x: 0.328125, y: 0.546875 },\n { x: 0.359375, y: 0.546875 },\n { x: 0.359375, y: 0.546875 },\n { x: 0.390625, y: 0.546875 },\n { x: 0.390625, y: 0.546875 },\n { x: 0.421875, y: 0.546875 },\n { x: 0.421875, y: 0.546875 },\n { x: 0.453125, y: 0.546875 },\n { x: 0.453125, y: 0.546875 },\n { x: 0.484375, y: 0.546875 },\n { x: 0.484375, y: 0.546875 },\n { x: 0.515625, y: 0.546875 },\n { x: 0.515625, y: 0.546875 },\n { x: 0.546875, y: 0.546875 },\n { x: 0.546875, y: 0.546875 },\n { x: 0.578125, y: 0.546875 },\n { x: 0.578125, y: 0.546875 },\n { x: 0.609375, y: 0.546875 },\n { x: 0.609375, y: 0.546875 },\n { x: 0.640625, y: 0.546875 },\n { x: 0.640625, y: 0.546875 },\n { x: 0.671875, y: 0.546875 },\n { x: 0.671875, y: 0.546875 },\n { x: 0.703125, y: 0.546875 },\n { x: 0.703125, y: 0.546875 },\n { x: 0.734375, y: 0.546875 },\n { x: 0.734375, y: 0.546875 },\n { x: 0.765625, y: 0.546875 },\n { x: 0.765625, y: 0.546875 },\n { x: 0.796875, y: 0.546875 },\n { x: 0.796875, y: 0.546875 },\n { x: 0.828125, y: 0.546875 },\n { x: 0.828125, y: 0.546875 },\n { x: 0.859375, y: 0.546875 },\n { x: 0.859375, y: 0.546875 },\n { x: 0.890625, y: 0.546875 },\n { x: 0.890625, y: 0.546875 },\n { x: 0.921875, y: 0.546875 },\n { x: 0.921875, y: 0.546875 },\n { x: 0.953125, y: 0.546875 },\n { x: 0.953125, y: 0.546875 },\n { x: 0.984375, y: 0.546875 },\n { x: 0.984375, y: 0.546875 },\n { x: 0.015625, y: 0.578125 },\n { x: 0.015625, y: 0.578125 },\n { x: 0.046875, y: 0.578125 },\n { x: 0.046875, y: 0.578125 },\n { x: 0.078125, y: 0.578125 },\n { x: 0.078125, y: 0.578125 },\n { x: 0.109375, y: 0.578125 },\n { x: 0.109375, y: 0.578125 },\n { x: 0.140625, y: 0.578125 },\n { x: 0.140625, y: 0.578125 },\n { x: 0.171875, y: 0.578125 },\n { x: 0.171875, y: 0.578125 },\n { x: 0.203125, y: 0.578125 },\n { x: 0.203125, y: 0.578125 },\n { x: 0.234375, y: 0.578125 },\n { x: 0.234375, y: 0.578125 },\n { x: 0.265625, y: 0.578125 },\n { x: 0.265625, y: 0.578125 },\n { x: 0.296875, y: 0.578125 },\n { x: 0.296875, y: 0.578125 },\n { x: 0.328125, y: 0.578125 },\n { x: 0.328125, y: 0.578125 },\n { x: 0.359375, y: 0.578125 },\n { x: 0.359375, y: 0.578125 },\n { x: 0.390625, y: 0.578125 },\n { x: 0.390625, y: 0.578125 },\n { x: 0.421875, y: 0.578125 },\n { x: 0.421875, y: 0.578125 },\n { x: 0.453125, y: 0.578125 },\n { x: 0.453125, y: 0.578125 },\n { x: 0.484375, y: 0.578125 },\n { x: 0.484375, y: 0.578125 },\n { x: 0.515625, y: 0.578125 },\n { x: 0.515625, y: 0.578125 },\n { x: 0.546875, y: 0.578125 },\n { x: 0.546875, y: 0.578125 },\n { x: 0.578125, y: 0.578125 },\n { x: 0.578125, y: 0.578125 },\n { x: 0.609375, y: 0.578125 },\n { x: 0.609375, y: 0.578125 },\n { x: 0.640625, y: 0.578125 },\n { x: 0.640625, y: 0.578125 },\n { x: 0.671875, y: 0.578125 },\n { x: 0.671875, y: 0.578125 },\n { x: 0.703125, y: 0.578125 },\n { x: 0.703125, y: 0.578125 },\n { x: 0.734375, y: 0.578125 },\n { x: 0.734375, y: 0.578125 },\n { x: 0.765625, y: 0.578125 },\n { x: 0.765625, y: 0.578125 },\n { x: 0.796875, y: 0.578125 },\n { x: 0.796875, y: 0.578125 },\n { x: 0.828125, y: 0.578125 },\n { x: 0.828125, y: 0.578125 },\n { x: 0.859375, y: 0.578125 },\n { x: 0.859375, y: 0.578125 },\n { x: 0.890625, y: 0.578125 },\n { x: 0.890625, y: 0.578125 },\n { x: 0.921875, y: 0.578125 },\n { x: 0.921875, y: 0.578125 },\n { x: 0.953125, y: 0.578125 },\n { x: 0.953125, y: 0.578125 },\n { x: 0.984375, y: 0.578125 },\n { x: 0.984375, y: 0.578125 },\n { x: 0.015625, y: 0.609375 },\n { x: 0.015625, y: 0.609375 },\n { x: 0.046875, y: 0.609375 },\n { x: 0.046875, y: 0.609375 },\n { x: 0.078125, y: 0.609375 },\n { x: 0.078125, y: 0.609375 },\n { x: 0.109375, y: 0.609375 },\n { x: 0.109375, y: 0.609375 },\n { x: 0.140625, y: 0.609375 },\n { x: 0.140625, y: 0.609375 },\n { x: 0.171875, y: 0.609375 },\n { x: 0.171875, y: 0.609375 },\n { x: 0.203125, y: 0.609375 },\n { x: 0.203125, y: 0.609375 },\n { x: 0.234375, y: 0.609375 },\n { x: 0.234375, y: 0.609375 },\n { x: 0.265625, y: 0.609375 },\n { x: 0.265625, y: 0.609375 },\n { x: 0.296875, y: 0.609375 },\n { x: 0.296875, y: 0.609375 },\n { x: 0.328125, y: 0.609375 },\n { x: 0.328125, y: 0.609375 },\n { x: 0.359375, y: 0.609375 },\n { x: 0.359375, y: 0.609375 },\n { x: 0.390625, y: 0.609375 },\n { x: 0.390625, y: 0.609375 },\n { x: 0.421875, y: 0.609375 },\n { x: 0.421875, y: 0.609375 },\n { x: 0.453125, y: 0.609375 },\n { x: 0.453125, y: 0.609375 },\n { x: 0.484375, y: 0.609375 },\n { x: 0.484375, y: 0.609375 },\n { x: 0.515625, y: 0.609375 },\n { x: 0.515625, y: 0.609375 },\n { x: 0.546875, y: 0.609375 },\n { x: 0.546875, y: 0.609375 },\n { x: 0.578125, y: 0.609375 },\n { x: 0.578125, y: 0.609375 },\n { x: 0.609375, y: 0.609375 },\n { x: 0.609375, y: 0.609375 },\n { x: 0.640625, y: 0.609375 },\n { x: 0.640625, y: 0.609375 },\n { x: 0.671875, y: 0.609375 },\n { x: 0.671875, y: 0.609375 },\n { x: 0.703125, y: 0.609375 },\n { x: 0.703125, y: 0.609375 },\n { x: 0.734375, y: 0.609375 },\n { x: 0.734375, y: 0.609375 },\n { x: 0.765625, y: 0.609375 },\n { x: 0.765625, y: 0.609375 },\n { x: 0.796875, y: 0.609375 },\n { x: 0.796875, y: 0.609375 },\n { x: 0.828125, y: 0.609375 },\n { x: 0.828125, y: 0.609375 },\n { x: 0.859375, y: 0.609375 },\n { x: 0.859375, y: 0.609375 },\n { x: 0.890625, y: 0.609375 },\n { x: 0.890625, y: 0.609375 },\n { x: 0.921875, y: 0.609375 },\n { x: 0.921875, y: 0.609375 },\n { x: 0.953125, y: 0.609375 },\n { x: 0.953125, y: 0.609375 },\n { x: 0.984375, y: 0.609375 },\n { x: 0.984375, y: 0.609375 },\n { x: 0.015625, y: 0.640625 },\n { x: 0.015625, y: 0.640625 },\n { x: 0.046875, y: 0.640625 },\n { x: 0.046875, y: 0.640625 },\n { x: 0.078125, y: 0.640625 },\n { x: 0.078125, y: 0.640625 },\n { x: 0.109375, y: 0.640625 },\n { x: 0.109375, y: 0.640625 },\n { x: 0.140625, y: 0.640625 },\n { x: 0.140625, y: 0.640625 },\n { x: 0.171875, y: 0.640625 },\n { x: 0.171875, y: 0.640625 },\n { x: 0.203125, y: 0.640625 },\n { x: 0.203125, y: 0.640625 },\n { x: 0.234375, y: 0.640625 },\n { x: 0.234375, y: 0.640625 },\n { x: 0.265625, y: 0.640625 },\n { x: 0.265625, y: 0.640625 },\n { x: 0.296875, y: 0.640625 },\n { x: 0.296875, y: 0.640625 },\n { x: 0.328125, y: 0.640625 },\n { x: 0.328125, y: 0.640625 },\n { x: 0.359375, y: 0.640625 },\n { x: 0.359375, y: 0.640625 },\n { x: 0.390625, y: 0.640625 },\n { x: 0.390625, y: 0.640625 },\n { x: 0.421875, y: 0.640625 },\n { x: 0.421875, y: 0.640625 },\n { x: 0.453125, y: 0.640625 },\n { x: 0.453125, y: 0.640625 },\n { x: 0.484375, y: 0.640625 },\n { x: 0.484375, y: 0.640625 },\n { x: 0.515625, y: 0.640625 },\n { x: 0.515625, y: 0.640625 },\n { x: 0.546875, y: 0.640625 },\n { x: 0.546875, y: 0.640625 },\n { x: 0.578125, y: 0.640625 },\n { x: 0.578125, y: 0.640625 },\n { x: 0.609375, y: 0.640625 },\n { x: 0.609375, y: 0.640625 },\n { x: 0.640625, y: 0.640625 },\n { x: 0.640625, y: 0.640625 },\n { x: 0.671875, y: 0.640625 },\n { x: 0.671875, y: 0.640625 },\n { x: 0.703125, y: 0.640625 },\n { x: 0.703125, y: 0.640625 },\n { x: 0.734375, y: 0.640625 },\n { x: 0.734375, y: 0.640625 },\n { x: 0.765625, y: 0.640625 },\n { x: 0.765625, y: 0.640625 },\n { x: 0.796875, y: 0.640625 },\n { x: 0.796875, y: 0.640625 },\n { x: 0.828125, y: 0.640625 },\n { x: 0.828125, y: 0.640625 },\n { x: 0.859375, y: 0.640625 },\n { x: 0.859375, y: 0.640625 },\n { x: 0.890625, y: 0.640625 },\n { x: 0.890625, y: 0.640625 },\n { x: 0.921875, y: 0.640625 },\n { x: 0.921875, y: 0.640625 },\n { x: 0.953125, y: 0.640625 },\n { x: 0.953125, y: 0.640625 },\n { x: 0.984375, y: 0.640625 },\n { x: 0.984375, y: 0.640625 },\n { x: 0.015625, y: 0.671875 },\n { x: 0.015625, y: 0.671875 },\n { x: 0.046875, y: 0.671875 },\n { x: 0.046875, y: 0.671875 },\n { x: 0.078125, y: 0.671875 },\n { x: 0.078125, y: 0.671875 },\n { x: 0.109375, y: 0.671875 },\n { x: 0.109375, y: 0.671875 },\n { x: 0.140625, y: 0.671875 },\n { x: 0.140625, y: 0.671875 },\n { x: 0.171875, y: 0.671875 },\n { x: 0.171875, y: 0.671875 },\n { x: 0.203125, y: 0.671875 },\n { x: 0.203125, y: 0.671875 },\n { x: 0.234375, y: 0.671875 },\n { x: 0.234375, y: 0.671875 },\n { x: 0.265625, y: 0.671875 },\n { x: 0.265625, y: 0.671875 },\n { x: 0.296875, y: 0.671875 },\n { x: 0.296875, y: 0.671875 },\n { x: 0.328125, y: 0.671875 },\n { x: 0.328125, y: 0.671875 },\n { x: 0.359375, y: 0.671875 },\n { x: 0.359375, y: 0.671875 },\n { x: 0.390625, y: 0.671875 },\n { x: 0.390625, y: 0.671875 },\n { x: 0.421875, y: 0.671875 },\n { x: 0.421875, y: 0.671875 },\n { x: 0.453125, y: 0.671875 },\n { x: 0.453125, y: 0.671875 },\n { x: 0.484375, y: 0.671875 },\n { x: 0.484375, y: 0.671875 },\n { x: 0.515625, y: 0.671875 },\n { x: 0.515625, y: 0.671875 },\n { x: 0.546875, y: 0.671875 },\n { x: 0.546875, y: 0.671875 },\n { x: 0.578125, y: 0.671875 },\n { x: 0.578125, y: 0.671875 },\n { x: 0.609375, y: 0.671875 },\n { x: 0.609375, y: 0.671875 },\n { x: 0.640625, y: 0.671875 },\n { x: 0.640625, y: 0.671875 },\n { x: 0.671875, y: 0.671875 },\n { x: 0.671875, y: 0.671875 },\n { x: 0.703125, y: 0.671875 },\n { x: 0.703125, y: 0.671875 },\n { x: 0.734375, y: 0.671875 },\n { x: 0.734375, y: 0.671875 },\n { x: 0.765625, y: 0.671875 },\n { x: 0.765625, y: 0.671875 },\n { x: 0.796875, y: 0.671875 },\n { x: 0.796875, y: 0.671875 },\n { x: 0.828125, y: 0.671875 },\n { x: 0.828125, y: 0.671875 },\n { x: 0.859375, y: 0.671875 },\n { x: 0.859375, y: 0.671875 },\n { x: 0.890625, y: 0.671875 },\n { x: 0.890625, y: 0.671875 },\n { x: 0.921875, y: 0.671875 },\n { x: 0.921875, y: 0.671875 },\n { x: 0.953125, y: 0.671875 },\n { x: 0.953125, y: 0.671875 },\n { x: 0.984375, y: 0.671875 },\n { x: 0.984375, y: 0.671875 },\n { x: 0.015625, y: 0.703125 },\n { x: 0.015625, y: 0.703125 },\n { x: 0.046875, y: 0.703125 },\n { x: 0.046875, y: 0.703125 },\n { x: 0.078125, y: 0.703125 },\n { x: 0.078125, y: 0.703125 },\n { x: 0.109375, y: 0.703125 },\n { x: 0.109375, y: 0.703125 },\n { x: 0.140625, y: 0.703125 },\n { x: 0.140625, y: 0.703125 },\n { x: 0.171875, y: 0.703125 },\n { x: 0.171875, y: 0.703125 },\n { x: 0.203125, y: 0.703125 },\n { x: 0.203125, y: 0.703125 },\n { x: 0.234375, y: 0.703125 },\n { x: 0.234375, y: 0.703125 },\n { x: 0.265625, y: 0.703125 },\n { x: 0.265625, y: 0.703125 },\n { x: 0.296875, y: 0.703125 },\n { x: 0.296875, y: 0.703125 },\n { x: 0.328125, y: 0.703125 },\n { x: 0.328125, y: 0.703125 },\n { x: 0.359375, y: 0.703125 },\n { x: 0.359375, y: 0.703125 },\n { x: 0.390625, y: 0.703125 },\n { x: 0.390625, y: 0.703125 },\n { x: 0.421875, y: 0.703125 },\n { x: 0.421875, y: 0.703125 },\n { x: 0.453125, y: 0.703125 },\n { x: 0.453125, y: 0.703125 },\n { x: 0.484375, y: 0.703125 },\n { x: 0.484375, y: 0.703125 },\n { x: 0.515625, y: 0.703125 },\n { x: 0.515625, y: 0.703125 },\n { x: 0.546875, y: 0.703125 },\n { x: 0.546875, y: 0.703125 },\n { x: 0.578125, y: 0.703125 },\n { x: 0.578125, y: 0.703125 },\n { x: 0.609375, y: 0.703125 },\n { x: 0.609375, y: 0.703125 },\n { x: 0.640625, y: 0.703125 },\n { x: 0.640625, y: 0.703125 },\n { x: 0.671875, y: 0.703125 },\n { x: 0.671875, y: 0.703125 },\n { x: 0.703125, y: 0.703125 },\n { x: 0.703125, y: 0.703125 },\n { x: 0.734375, y: 0.703125 },\n { x: 0.734375, y: 0.703125 },\n { x: 0.765625, y: 0.703125 },\n { x: 0.765625, y: 0.703125 },\n { x: 0.796875, y: 0.703125 },\n { x: 0.796875, y: 0.703125 },\n { x: 0.828125, y: 0.703125 },\n { x: 0.828125, y: 0.703125 },\n { x: 0.859375, y: 0.703125 },\n { x: 0.859375, y: 0.703125 },\n { x: 0.890625, y: 0.703125 },\n { x: 0.890625, y: 0.703125 },\n { x: 0.921875, y: 0.703125 },\n { x: 0.921875, y: 0.703125 },\n { x: 0.953125, y: 0.703125 },\n { x: 0.953125, y: 0.703125 },\n { x: 0.984375, y: 0.703125 },\n { x: 0.984375, y: 0.703125 },\n { x: 0.015625, y: 0.734375 },\n { x: 0.015625, y: 0.734375 },\n { x: 0.046875, y: 0.734375 },\n { x: 0.046875, y: 0.734375 },\n { x: 0.078125, y: 0.734375 },\n { x: 0.078125, y: 0.734375 },\n { x: 0.109375, y: 0.734375 },\n { x: 0.109375, y: 0.734375 },\n { x: 0.140625, y: 0.734375 },\n { x: 0.140625, y: 0.734375 },\n { x: 0.171875, y: 0.734375 },\n { x: 0.171875, y: 0.734375 },\n { x: 0.203125, y: 0.734375 },\n { x: 0.203125, y: 0.734375 },\n { x: 0.234375, y: 0.734375 },\n { x: 0.234375, y: 0.734375 },\n { x: 0.265625, y: 0.734375 },\n { x: 0.265625, y: 0.734375 },\n { x: 0.296875, y: 0.734375 },\n { x: 0.296875, y: 0.734375 },\n { x: 0.328125, y: 0.734375 },\n { x: 0.328125, y: 0.734375 },\n { x: 0.359375, y: 0.734375 },\n { x: 0.359375, y: 0.734375 },\n { x: 0.390625, y: 0.734375 },\n { x: 0.390625, y: 0.734375 },\n { x: 0.421875, y: 0.734375 },\n { x: 0.421875, y: 0.734375 },\n { x: 0.453125, y: 0.734375 },\n { x: 0.453125, y: 0.734375 },\n { x: 0.484375, y: 0.734375 },\n { x: 0.484375, y: 0.734375 },\n { x: 0.515625, y: 0.734375 },\n { x: 0.515625, y: 0.734375 },\n { x: 0.546875, y: 0.734375 },\n { x: 0.546875, y: 0.734375 },\n { x: 0.578125, y: 0.734375 },\n { x: 0.578125, y: 0.734375 },\n { x: 0.609375, y: 0.734375 },\n { x: 0.609375, y: 0.734375 },\n { x: 0.640625, y: 0.734375 },\n { x: 0.640625, y: 0.734375 },\n { x: 0.671875, y: 0.734375 },\n { x: 0.671875, y: 0.734375 },\n { x: 0.703125, y: 0.734375 },\n { x: 0.703125, y: 0.734375 },\n { x: 0.734375, y: 0.734375 },\n { x: 0.734375, y: 0.734375 },\n { x: 0.765625, y: 0.734375 },\n { x: 0.765625, y: 0.734375 },\n { x: 0.796875, y: 0.734375 },\n { x: 0.796875, y: 0.734375 },\n { x: 0.828125, y: 0.734375 },\n { x: 0.828125, y: 0.734375 },\n { x: 0.859375, y: 0.734375 },\n { x: 0.859375, y: 0.734375 },\n { x: 0.890625, y: 0.734375 },\n { x: 0.890625, y: 0.734375 },\n { x: 0.921875, y: 0.734375 },\n { x: 0.921875, y: 0.734375 },\n { x: 0.953125, y: 0.734375 },\n { x: 0.953125, y: 0.734375 },\n { x: 0.984375, y: 0.734375 },\n { x: 0.984375, y: 0.734375 },\n { x: 0.015625, y: 0.765625 },\n { x: 0.015625, y: 0.765625 },\n { x: 0.046875, y: 0.765625 },\n { x: 0.046875, y: 0.765625 },\n { x: 0.078125, y: 0.765625 },\n { x: 0.078125, y: 0.765625 },\n { x: 0.109375, y: 0.765625 },\n { x: 0.109375, y: 0.765625 },\n { x: 0.140625, y: 0.765625 },\n { x: 0.140625, y: 0.765625 },\n { x: 0.171875, y: 0.765625 },\n { x: 0.171875, y: 0.765625 },\n { x: 0.203125, y: 0.765625 },\n { x: 0.203125, y: 0.765625 },\n { x: 0.234375, y: 0.765625 },\n { x: 0.234375, y: 0.765625 },\n { x: 0.265625, y: 0.765625 },\n { x: 0.265625, y: 0.765625 },\n { x: 0.296875, y: 0.765625 },\n { x: 0.296875, y: 0.765625 },\n { x: 0.328125, y: 0.765625 },\n { x: 0.328125, y: 0.765625 },\n { x: 0.359375, y: 0.765625 },\n { x: 0.359375, y: 0.765625 },\n { x: 0.390625, y: 0.765625 },\n { x: 0.390625, y: 0.765625 },\n { x: 0.421875, y: 0.765625 },\n { x: 0.421875, y: 0.765625 },\n { x: 0.453125, y: 0.765625 },\n { x: 0.453125, y: 0.765625 },\n { x: 0.484375, y: 0.765625 },\n { x: 0.484375, y: 0.765625 },\n { x: 0.515625, y: 0.765625 },\n { x: 0.515625, y: 0.765625 },\n { x: 0.546875, y: 0.765625 },\n { x: 0.546875, y: 0.765625 },\n { x: 0.578125, y: 0.765625 },\n { x: 0.578125, y: 0.765625 },\n { x: 0.609375, y: 0.765625 },\n { x: 0.609375, y: 0.765625 },\n { x: 0.640625, y: 0.765625 },\n { x: 0.640625, y: 0.765625 },\n { x: 0.671875, y: 0.765625 },\n { x: 0.671875, y: 0.765625 },\n { x: 0.703125, y: 0.765625 },\n { x: 0.703125, y: 0.765625 },\n { x: 0.734375, y: 0.765625 },\n { x: 0.734375, y: 0.765625 },\n { x: 0.765625, y: 0.765625 },\n { x: 0.765625, y: 0.765625 },\n { x: 0.796875, y: 0.765625 },\n { x: 0.796875, y: 0.765625 },\n { x: 0.828125, y: 0.765625 },\n { x: 0.828125, y: 0.765625 },\n { x: 0.859375, y: 0.765625 },\n { x: 0.859375, y: 0.765625 },\n { x: 0.890625, y: 0.765625 },\n { x: 0.890625, y: 0.765625 },\n { x: 0.921875, y: 0.765625 },\n { x: 0.921875, y: 0.765625 },\n { x: 0.953125, y: 0.765625 },\n { x: 0.953125, y: 0.765625 },\n { x: 0.984375, y: 0.765625 },\n { x: 0.984375, y: 0.765625 },\n { x: 0.015625, y: 0.796875 },\n { x: 0.015625, y: 0.796875 },\n { x: 0.046875, y: 0.796875 },\n { x: 0.046875, y: 0.796875 },\n { x: 0.078125, y: 0.796875 },\n { x: 0.078125, y: 0.796875 },\n { x: 0.109375, y: 0.796875 },\n { x: 0.109375, y: 0.796875 },\n { x: 0.140625, y: 0.796875 },\n { x: 0.140625, y: 0.796875 },\n { x: 0.171875, y: 0.796875 },\n { x: 0.171875, y: 0.796875 },\n { x: 0.203125, y: 0.796875 },\n { x: 0.203125, y: 0.796875 },\n { x: 0.234375, y: 0.796875 },\n { x: 0.234375, y: 0.796875 },\n { x: 0.265625, y: 0.796875 },\n { x: 0.265625, y: 0.796875 },\n { x: 0.296875, y: 0.796875 },\n { x: 0.296875, y: 0.796875 },\n { x: 0.328125, y: 0.796875 },\n { x: 0.328125, y: 0.796875 },\n { x: 0.359375, y: 0.796875 },\n { x: 0.359375, y: 0.796875 },\n { x: 0.390625, y: 0.796875 },\n { x: 0.390625, y: 0.796875 },\n { x: 0.421875, y: 0.796875 },\n { x: 0.421875, y: 0.796875 },\n { x: 0.453125, y: 0.796875 },\n { x: 0.453125, y: 0.796875 },\n { x: 0.484375, y: 0.796875 },\n { x: 0.484375, y: 0.796875 },\n { x: 0.515625, y: 0.796875 },\n { x: 0.515625, y: 0.796875 },\n { x: 0.546875, y: 0.796875 },\n { x: 0.546875, y: 0.796875 },\n { x: 0.578125, y: 0.796875 },\n { x: 0.578125, y: 0.796875 },\n { x: 0.609375, y: 0.796875 },\n { x: 0.609375, y: 0.796875 },\n { x: 0.640625, y: 0.796875 },\n { x: 0.640625, y: 0.796875 },\n { x: 0.671875, y: 0.796875 },\n { x: 0.671875, y: 0.796875 },\n { x: 0.703125, y: 0.796875 },\n { x: 0.703125, y: 0.796875 },\n { x: 0.734375, y: 0.796875 },\n { x: 0.734375, y: 0.796875 },\n { x: 0.765625, y: 0.796875 },\n { x: 0.765625, y: 0.796875 },\n { x: 0.796875, y: 0.796875 },\n { x: 0.796875, y: 0.796875 },\n { x: 0.828125, y: 0.796875 },\n { x: 0.828125, y: 0.796875 },\n { x: 0.859375, y: 0.796875 },\n { x: 0.859375, y: 0.796875 },\n { x: 0.890625, y: 0.796875 },\n { x: 0.890625, y: 0.796875 },\n { x: 0.921875, y: 0.796875 },\n { x: 0.921875, y: 0.796875 },\n { x: 0.953125, y: 0.796875 },\n { x: 0.953125, y: 0.796875 },\n { x: 0.984375, y: 0.796875 },\n { x: 0.984375, y: 0.796875 },\n { x: 0.015625, y: 0.828125 },\n { x: 0.015625, y: 0.828125 },\n { x: 0.046875, y: 0.828125 },\n { x: 0.046875, y: 0.828125 },\n { x: 0.078125, y: 0.828125 },\n { x: 0.078125, y: 0.828125 },\n { x: 0.109375, y: 0.828125 },\n { x: 0.109375, y: 0.828125 },\n { x: 0.140625, y: 0.828125 },\n { x: 0.140625, y: 0.828125 },\n { x: 0.171875, y: 0.828125 },\n { x: 0.171875, y: 0.828125 },\n { x: 0.203125, y: 0.828125 },\n { x: 0.203125, y: 0.828125 },\n { x: 0.234375, y: 0.828125 },\n { x: 0.234375, y: 0.828125 },\n { x: 0.265625, y: 0.828125 },\n { x: 0.265625, y: 0.828125 },\n { x: 0.296875, y: 0.828125 },\n { x: 0.296875, y: 0.828125 },\n { x: 0.328125, y: 0.828125 },\n { x: 0.328125, y: 0.828125 },\n { x: 0.359375, y: 0.828125 },\n { x: 0.359375, y: 0.828125 },\n { x: 0.390625, y: 0.828125 },\n { x: 0.390625, y: 0.828125 },\n { x: 0.421875, y: 0.828125 },\n { x: 0.421875, y: 0.828125 },\n { x: 0.453125, y: 0.828125 },\n { x: 0.453125, y: 0.828125 },\n { x: 0.484375, y: 0.828125 },\n { x: 0.484375, y: 0.828125 },\n { x: 0.515625, y: 0.828125 },\n { x: 0.515625, y: 0.828125 },\n { x: 0.546875, y: 0.828125 },\n { x: 0.546875, y: 0.828125 },\n { x: 0.578125, y: 0.828125 },\n { x: 0.578125, y: 0.828125 },\n { x: 0.609375, y: 0.828125 },\n { x: 0.609375, y: 0.828125 },\n { x: 0.640625, y: 0.828125 },\n { x: 0.640625, y: 0.828125 },\n { x: 0.671875, y: 0.828125 },\n { x: 0.671875, y: 0.828125 },\n { x: 0.703125, y: 0.828125 },\n { x: 0.703125, y: 0.828125 },\n { x: 0.734375, y: 0.828125 },\n { x: 0.734375, y: 0.828125 },\n { x: 0.765625, y: 0.828125 },\n { x: 0.765625, y: 0.828125 },\n { x: 0.796875, y: 0.828125 },\n { x: 0.796875, y: 0.828125 },\n { x: 0.828125, y: 0.828125 },\n { x: 0.828125, y: 0.828125 },\n { x: 0.859375, y: 0.828125 },\n { x: 0.859375, y: 0.828125 },\n { x: 0.890625, y: 0.828125 },\n { x: 0.890625, y: 0.828125 },\n { x: 0.921875, y: 0.828125 },\n { x: 0.921875, y: 0.828125 },\n { x: 0.953125, y: 0.828125 },\n { x: 0.953125, y: 0.828125 },\n { x: 0.984375, y: 0.828125 },\n { x: 0.984375, y: 0.828125 },\n { x: 0.015625, y: 0.859375 },\n { x: 0.015625, y: 0.859375 },\n { x: 0.046875, y: 0.859375 },\n { x: 0.046875, y: 0.859375 },\n { x: 0.078125, y: 0.859375 },\n { x: 0.078125, y: 0.859375 },\n { x: 0.109375, y: 0.859375 },\n { x: 0.109375, y: 0.859375 },\n { x: 0.140625, y: 0.859375 },\n { x: 0.140625, y: 0.859375 },\n { x: 0.171875, y: 0.859375 },\n { x: 0.171875, y: 0.859375 },\n { x: 0.203125, y: 0.859375 },\n { x: 0.203125, y: 0.859375 },\n { x: 0.234375, y: 0.859375 },\n { x: 0.234375, y: 0.859375 },\n { x: 0.265625, y: 0.859375 },\n { x: 0.265625, y: 0.859375 },\n { x: 0.296875, y: 0.859375 },\n { x: 0.296875, y: 0.859375 },\n { x: 0.328125, y: 0.859375 },\n { x: 0.328125, y: 0.859375 },\n { x: 0.359375, y: 0.859375 },\n { x: 0.359375, y: 0.859375 },\n { x: 0.390625, y: 0.859375 },\n { x: 0.390625, y: 0.859375 },\n { x: 0.421875, y: 0.859375 },\n { x: 0.421875, y: 0.859375 },\n { x: 0.453125, y: 0.859375 },\n { x: 0.453125, y: 0.859375 },\n { x: 0.484375, y: 0.859375 },\n { x: 0.484375, y: 0.859375 },\n { x: 0.515625, y: 0.859375 },\n { x: 0.515625, y: 0.859375 },\n { x: 0.546875, y: 0.859375 },\n { x: 0.546875, y: 0.859375 },\n { x: 0.578125, y: 0.859375 },\n { x: 0.578125, y: 0.859375 },\n { x: 0.609375, y: 0.859375 },\n { x: 0.609375, y: 0.859375 },\n { x: 0.640625, y: 0.859375 },\n { x: 0.640625, y: 0.859375 },\n { x: 0.671875, y: 0.859375 },\n { x: 0.671875, y: 0.859375 },\n { x: 0.703125, y: 0.859375 },\n { x: 0.703125, y: 0.859375 },\n { x: 0.734375, y: 0.859375 },\n { x: 0.734375, y: 0.859375 },\n { x: 0.765625, y: 0.859375 },\n { x: 0.765625, y: 0.859375 },\n { x: 0.796875, y: 0.859375 },\n { x: 0.796875, y: 0.859375 },\n { x: 0.828125, y: 0.859375 },\n { x: 0.828125, y: 0.859375 },\n { x: 0.859375, y: 0.859375 },\n { x: 0.859375, y: 0.859375 },\n { x: 0.890625, y: 0.859375 },\n { x: 0.890625, y: 0.859375 },\n { x: 0.921875, y: 0.859375 },\n { x: 0.921875, y: 0.859375 },\n { x: 0.953125, y: 0.859375 },\n { x: 0.953125, y: 0.859375 },\n { x: 0.984375, y: 0.859375 },\n { x: 0.984375, y: 0.859375 },\n { x: 0.015625, y: 0.890625 },\n { x: 0.015625, y: 0.890625 },\n { x: 0.046875, y: 0.890625 },\n { x: 0.046875, y: 0.890625 },\n { x: 0.078125, y: 0.890625 },\n { x: 0.078125, y: 0.890625 },\n { x: 0.109375, y: 0.890625 },\n { x: 0.109375, y: 0.890625 },\n { x: 0.140625, y: 0.890625 },\n { x: 0.140625, y: 0.890625 },\n { x: 0.171875, y: 0.890625 },\n { x: 0.171875, y: 0.890625 },\n { x: 0.203125, y: 0.890625 },\n { x: 0.203125, y: 0.890625 },\n { x: 0.234375, y: 0.890625 },\n { x: 0.234375, y: 0.890625 },\n { x: 0.265625, y: 0.890625 },\n { x: 0.265625, y: 0.890625 },\n { x: 0.296875, y: 0.890625 },\n { x: 0.296875, y: 0.890625 },\n { x: 0.328125, y: 0.890625 },\n { x: 0.328125, y: 0.890625 },\n { x: 0.359375, y: 0.890625 },\n { x: 0.359375, y: 0.890625 },\n { x: 0.390625, y: 0.890625 },\n { x: 0.390625, y: 0.890625 },\n { x: 0.421875, y: 0.890625 },\n { x: 0.421875, y: 0.890625 },\n { x: 0.453125, y: 0.890625 },\n { x: 0.453125, y: 0.890625 },\n { x: 0.484375, y: 0.890625 },\n { x: 0.484375, y: 0.890625 },\n { x: 0.515625, y: 0.890625 },\n { x: 0.515625, y: 0.890625 },\n { x: 0.546875, y: 0.890625 },\n { x: 0.546875, y: 0.890625 },\n { x: 0.578125, y: 0.890625 },\n { x: 0.578125, y: 0.890625 },\n { x: 0.609375, y: 0.890625 },\n { x: 0.609375, y: 0.890625 },\n { x: 0.640625, y: 0.890625 },\n { x: 0.640625, y: 0.890625 },\n { x: 0.671875, y: 0.890625 },\n { x: 0.671875, y: 0.890625 },\n { x: 0.703125, y: 0.890625 },\n { x: 0.703125, y: 0.890625 },\n { x: 0.734375, y: 0.890625 },\n { x: 0.734375, y: 0.890625 },\n { x: 0.765625, y: 0.890625 },\n { x: 0.765625, y: 0.890625 },\n { x: 0.796875, y: 0.890625 },\n { x: 0.796875, y: 0.890625 },\n { x: 0.828125, y: 0.890625 },\n { x: 0.828125, y: 0.890625 },\n { x: 0.859375, y: 0.890625 },\n { x: 0.859375, y: 0.890625 },\n { x: 0.890625, y: 0.890625 },\n { x: 0.890625, y: 0.890625 },\n { x: 0.921875, y: 0.890625 },\n { x: 0.921875, y: 0.890625 },\n { x: 0.953125, y: 0.890625 },\n { x: 0.953125, y: 0.890625 },\n { x: 0.984375, y: 0.890625 },\n { x: 0.984375, y: 0.890625 },\n { x: 0.015625, y: 0.921875 },\n { x: 0.015625, y: 0.921875 },\n { x: 0.046875, y: 0.921875 },\n { x: 0.046875, y: 0.921875 },\n { x: 0.078125, y: 0.921875 },\n { x: 0.078125, y: 0.921875 },\n { x: 0.109375, y: 0.921875 },\n { x: 0.109375, y: 0.921875 },\n { x: 0.140625, y: 0.921875 },\n { x: 0.140625, y: 0.921875 },\n { x: 0.171875, y: 0.921875 },\n { x: 0.171875, y: 0.921875 },\n { x: 0.203125, y: 0.921875 },\n { x: 0.203125, y: 0.921875 },\n { x: 0.234375, y: 0.921875 },\n { x: 0.234375, y: 0.921875 },\n { x: 0.265625, y: 0.921875 },\n { x: 0.265625, y: 0.921875 },\n { x: 0.296875, y: 0.921875 },\n { x: 0.296875, y: 0.921875 },\n { x: 0.328125, y: 0.921875 },\n { x: 0.328125, y: 0.921875 },\n { x: 0.359375, y: 0.921875 },\n { x: 0.359375, y: 0.921875 },\n { x: 0.390625, y: 0.921875 },\n { x: 0.390625, y: 0.921875 },\n { x: 0.421875, y: 0.921875 },\n { x: 0.421875, y: 0.921875 },\n { x: 0.453125, y: 0.921875 },\n { x: 0.453125, y: 0.921875 },\n { x: 0.484375, y: 0.921875 },\n { x: 0.484375, y: 0.921875 },\n { x: 0.515625, y: 0.921875 },\n { x: 0.515625, y: 0.921875 },\n { x: 0.546875, y: 0.921875 },\n { x: 0.546875, y: 0.921875 },\n { x: 0.578125, y: 0.921875 },\n { x: 0.578125, y: 0.921875 },\n { x: 0.609375, y: 0.921875 },\n { x: 0.609375, y: 0.921875 },\n { x: 0.640625, y: 0.921875 },\n { x: 0.640625, y: 0.921875 },\n { x: 0.671875, y: 0.921875 },\n { x: 0.671875, y: 0.921875 },\n { x: 0.703125, y: 0.921875 },\n { x: 0.703125, y: 0.921875 },\n { x: 0.734375, y: 0.921875 },\n { x: 0.734375, y: 0.921875 },\n { x: 0.765625, y: 0.921875 },\n { x: 0.765625, y: 0.921875 },\n { x: 0.796875, y: 0.921875 },\n { x: 0.796875, y: 0.921875 },\n { x: 0.828125, y: 0.921875 },\n { x: 0.828125, y: 0.921875 },\n { x: 0.859375, y: 0.921875 },\n { x: 0.859375, y: 0.921875 },\n { x: 0.890625, y: 0.921875 },\n { x: 0.890625, y: 0.921875 },\n { x: 0.921875, y: 0.921875 },\n { x: 0.921875, y: 0.921875 },\n { x: 0.953125, y: 0.921875 },\n { x: 0.953125, y: 0.921875 },\n { x: 0.984375, y: 0.921875 },\n { x: 0.984375, y: 0.921875 },\n { x: 0.015625, y: 0.953125 },\n { x: 0.015625, y: 0.953125 },\n { x: 0.046875, y: 0.953125 },\n { x: 0.046875, y: 0.953125 },\n { x: 0.078125, y: 0.953125 },\n { x: 0.078125, y: 0.953125 },\n { x: 0.109375, y: 0.953125 },\n { x: 0.109375, y: 0.953125 },\n { x: 0.140625, y: 0.953125 },\n { x: 0.140625, y: 0.953125 },\n { x: 0.171875, y: 0.953125 },\n { x: 0.171875, y: 0.953125 },\n { x: 0.203125, y: 0.953125 },\n { x: 0.203125, y: 0.953125 },\n { x: 0.234375, y: 0.953125 },\n { x: 0.234375, y: 0.953125 },\n { x: 0.265625, y: 0.953125 },\n { x: 0.265625, y: 0.953125 },\n { x: 0.296875, y: 0.953125 },\n { x: 0.296875, y: 0.953125 },\n { x: 0.328125, y: 0.953125 },\n { x: 0.328125, y: 0.953125 },\n { x: 0.359375, y: 0.953125 },\n { x: 0.359375, y: 0.953125 },\n { x: 0.390625, y: 0.953125 },\n { x: 0.390625, y: 0.953125 },\n { x: 0.421875, y: 0.953125 },\n { x: 0.421875, y: 0.953125 },\n { x: 0.453125, y: 0.953125 },\n { x: 0.453125, y: 0.953125 },\n { x: 0.484375, y: 0.953125 },\n { x: 0.484375, y: 0.953125 },\n { x: 0.515625, y: 0.953125 },\n { x: 0.515625, y: 0.953125 },\n { x: 0.546875, y: 0.953125 },\n { x: 0.546875, y: 0.953125 },\n { x: 0.578125, y: 0.953125 },\n { x: 0.578125, y: 0.953125 },\n { x: 0.609375, y: 0.953125 },\n { x: 0.609375, y: 0.953125 },\n { x: 0.640625, y: 0.953125 },\n { x: 0.640625, y: 0.953125 },\n { x: 0.671875, y: 0.953125 },\n { x: 0.671875, y: 0.953125 },\n { x: 0.703125, y: 0.953125 },\n { x: 0.703125, y: 0.953125 },\n { x: 0.734375, y: 0.953125 },\n { x: 0.734375, y: 0.953125 },\n { x: 0.765625, y: 0.953125 },\n { x: 0.765625, y: 0.953125 },\n { x: 0.796875, y: 0.953125 },\n { x: 0.796875, y: 0.953125 },\n { x: 0.828125, y: 0.953125 },\n { x: 0.828125, y: 0.953125 },\n { x: 0.859375, y: 0.953125 },\n { x: 0.859375, y: 0.953125 },\n { x: 0.890625, y: 0.953125 },\n { x: 0.890625, y: 0.953125 },\n { x: 0.921875, y: 0.953125 },\n { x: 0.921875, y: 0.953125 },\n { x: 0.953125, y: 0.953125 },\n { x: 0.953125, y: 0.953125 },\n { x: 0.984375, y: 0.953125 },\n { x: 0.984375, y: 0.953125 },\n { x: 0.015625, y: 0.984375 },\n { x: 0.015625, y: 0.984375 },\n { x: 0.046875, y: 0.984375 },\n { x: 0.046875, y: 0.984375 },\n { x: 0.078125, y: 0.984375 },\n { x: 0.078125, y: 0.984375 },\n { x: 0.109375, y: 0.984375 },\n { x: 0.109375, y: 0.984375 },\n { x: 0.140625, y: 0.984375 },\n { x: 0.140625, y: 0.984375 },\n { x: 0.171875, y: 0.984375 },\n { x: 0.171875, y: 0.984375 },\n { x: 0.203125, y: 0.984375 },\n { x: 0.203125, y: 0.984375 },\n { x: 0.234375, y: 0.984375 },\n { x: 0.234375, y: 0.984375 },\n { x: 0.265625, y: 0.984375 },\n { x: 0.265625, y: 0.984375 },\n { x: 0.296875, y: 0.984375 },\n { x: 0.296875, y: 0.984375 },\n { x: 0.328125, y: 0.984375 },\n { x: 0.328125, y: 0.984375 },\n { x: 0.359375, y: 0.984375 },\n { x: 0.359375, y: 0.984375 },\n { x: 0.390625, y: 0.984375 },\n { x: 0.390625, y: 0.984375 },\n { x: 0.421875, y: 0.984375 },\n { x: 0.421875, y: 0.984375 },\n { x: 0.453125, y: 0.984375 },\n { x: 0.453125, y: 0.984375 },\n { x: 0.484375, y: 0.984375 },\n { x: 0.484375, y: 0.984375 },\n { x: 0.515625, y: 0.984375 },\n { x: 0.515625, y: 0.984375 },\n { x: 0.546875, y: 0.984375 },\n { x: 0.546875, y: 0.984375 },\n { x: 0.578125, y: 0.984375 },\n { x: 0.578125, y: 0.984375 },\n { x: 0.609375, y: 0.984375 },\n { x: 0.609375, y: 0.984375 },\n { x: 0.640625, y: 0.984375 },\n { x: 0.640625, y: 0.984375 },\n { x: 0.671875, y: 0.984375 },\n { x: 0.671875, y: 0.984375 },\n { x: 0.703125, y: 0.984375 },\n { x: 0.703125, y: 0.984375 },\n { x: 0.734375, y: 0.984375 },\n { x: 0.734375, y: 0.984375 },\n { x: 0.765625, y: 0.984375 },\n { x: 0.765625, y: 0.984375 },\n { x: 0.796875, y: 0.984375 },\n { x: 0.796875, y: 0.984375 },\n { x: 0.828125, y: 0.984375 },\n { x: 0.828125, y: 0.984375 },\n { x: 0.859375, y: 0.984375 },\n { x: 0.859375, y: 0.984375 },\n { x: 0.890625, y: 0.984375 },\n { x: 0.890625, y: 0.984375 },\n { x: 0.921875, y: 0.984375 },\n { x: 0.921875, y: 0.984375 },\n { x: 0.953125, y: 0.984375 },\n { x: 0.953125, y: 0.984375 },\n { x: 0.984375, y: 0.984375 },\n { x: 0.984375, y: 0.984375 },\n { x: 0.03125, y: 0.03125 },\n { x: 0.03125, y: 0.03125 },\n { x: 0.09375, y: 0.03125 },\n { x: 0.09375, y: 0.03125 },\n { x: 0.15625, y: 0.03125 },\n { x: 0.15625, y: 0.03125 },\n { x: 0.21875, y: 0.03125 },\n { x: 0.21875, y: 0.03125 },\n { x: 0.28125, y: 0.03125 },\n { x: 0.28125, y: 0.03125 },\n { x: 0.34375, y: 0.03125 },\n { x: 0.34375, y: 0.03125 },\n { x: 0.40625, y: 0.03125 },\n { x: 0.40625, y: 0.03125 },\n { x: 0.46875, y: 0.03125 },\n { x: 0.46875, y: 0.03125 },\n { x: 0.53125, y: 0.03125 },\n { x: 0.53125, y: 0.03125 },\n { x: 0.59375, y: 0.03125 },\n { x: 0.59375, y: 0.03125 },\n { x: 0.65625, y: 0.03125 },\n { x: 0.65625, y: 0.03125 },\n { x: 0.71875, y: 0.03125 },\n { x: 0.71875, y: 0.03125 },\n { x: 0.78125, y: 0.03125 },\n { x: 0.78125, y: 0.03125 },\n { x: 0.84375, y: 0.03125 },\n { x: 0.84375, y: 0.03125 },\n { x: 0.90625, y: 0.03125 },\n { x: 0.90625, y: 0.03125 },\n { x: 0.96875, y: 0.03125 },\n { x: 0.96875, y: 0.03125 },\n { x: 0.03125, y: 0.09375 },\n { x: 0.03125, y: 0.09375 },\n { x: 0.09375, y: 0.09375 },\n { x: 0.09375, y: 0.09375 },\n { x: 0.15625, y: 0.09375 },\n { x: 0.15625, y: 0.09375 },\n { x: 0.21875, y: 0.09375 },\n { x: 0.21875, y: 0.09375 },\n { x: 0.28125, y: 0.09375 },\n { x: 0.28125, y: 0.09375 },\n { x: 0.34375, y: 0.09375 },\n { x: 0.34375, y: 0.09375 },\n { x: 0.40625, y: 0.09375 },\n { x: 0.40625, y: 0.09375 },\n { x: 0.46875, y: 0.09375 },\n { x: 0.46875, y: 0.09375 },\n { x: 0.53125, y: 0.09375 },\n { x: 0.53125, y: 0.09375 },\n { x: 0.59375, y: 0.09375 },\n { x: 0.59375, y: 0.09375 },\n { x: 0.65625, y: 0.09375 },\n { x: 0.65625, y: 0.09375 },\n { x: 0.71875, y: 0.09375 },\n { x: 0.71875, y: 0.09375 },\n { x: 0.78125, y: 0.09375 },\n { x: 0.78125, y: 0.09375 },\n { x: 0.84375, y: 0.09375 },\n { x: 0.84375, y: 0.09375 },\n { x: 0.90625, y: 0.09375 },\n { x: 0.90625, y: 0.09375 },\n { x: 0.96875, y: 0.09375 },\n { x: 0.96875, y: 0.09375 },\n { x: 0.03125, y: 0.15625 },\n { x: 0.03125, y: 0.15625 },\n { x: 0.09375, y: 0.15625 },\n { x: 0.09375, y: 0.15625 },\n { x: 0.15625, y: 0.15625 },\n { x: 0.15625, y: 0.15625 },\n { x: 0.21875, y: 0.15625 },\n { x: 0.21875, y: 0.15625 },\n { x: 0.28125, y: 0.15625 },\n { x: 0.28125, y: 0.15625 },\n { x: 0.34375, y: 0.15625 },\n { x: 0.34375, y: 0.15625 },\n { x: 0.40625, y: 0.15625 },\n { x: 0.40625, y: 0.15625 },\n { x: 0.46875, y: 0.15625 },\n { x: 0.46875, y: 0.15625 },\n { x: 0.53125, y: 0.15625 },\n { x: 0.53125, y: 0.15625 },\n { x: 0.59375, y: 0.15625 },\n { x: 0.59375, y: 0.15625 },\n { x: 0.65625, y: 0.15625 },\n { x: 0.65625, y: 0.15625 },\n { x: 0.71875, y: 0.15625 },\n { x: 0.71875, y: 0.15625 },\n { x: 0.78125, y: 0.15625 },\n { x: 0.78125, y: 0.15625 },\n { x: 0.84375, y: 0.15625 },\n { x: 0.84375, y: 0.15625 },\n { x: 0.90625, y: 0.15625 },\n { x: 0.90625, y: 0.15625 },\n { x: 0.96875, y: 0.15625 },\n { x: 0.96875, y: 0.15625 },\n { x: 0.03125, y: 0.21875 },\n { x: 0.03125, y: 0.21875 },\n { x: 0.09375, y: 0.21875 },\n { x: 0.09375, y: 0.21875 },\n { x: 0.15625, y: 0.21875 },\n { x: 0.15625, y: 0.21875 },\n { x: 0.21875, y: 0.21875 },\n { x: 0.21875, y: 0.21875 },\n { x: 0.28125, y: 0.21875 },\n { x: 0.28125, y: 0.21875 },\n { x: 0.34375, y: 0.21875 },\n { x: 0.34375, y: 0.21875 },\n { x: 0.40625, y: 0.21875 },\n { x: 0.40625, y: 0.21875 },\n { x: 0.46875, y: 0.21875 },\n { x: 0.46875, y: 0.21875 },\n { x: 0.53125, y: 0.21875 },\n { x: 0.53125, y: 0.21875 },\n { x: 0.59375, y: 0.21875 },\n { x: 0.59375, y: 0.21875 },\n { x: 0.65625, y: 0.21875 },\n { x: 0.65625, y: 0.21875 },\n { x: 0.71875, y: 0.21875 },\n { x: 0.71875, y: 0.21875 },\n { x: 0.78125, y: 0.21875 },\n { x: 0.78125, y: 0.21875 },\n { x: 0.84375, y: 0.21875 },\n { x: 0.84375, y: 0.21875 },\n { x: 0.90625, y: 0.21875 },\n { x: 0.90625, y: 0.21875 },\n { x: 0.96875, y: 0.21875 },\n { x: 0.96875, y: 0.21875 },\n { x: 0.03125, y: 0.28125 },\n { x: 0.03125, y: 0.28125 },\n { x: 0.09375, y: 0.28125 },\n { x: 0.09375, y: 0.28125 },\n { x: 0.15625, y: 0.28125 },\n { x: 0.15625, y: 0.28125 },\n { x: 0.21875, y: 0.28125 },\n { x: 0.21875, y: 0.28125 },\n { x: 0.28125, y: 0.28125 },\n { x: 0.28125, y: 0.28125 },\n { x: 0.34375, y: 0.28125 },\n { x: 0.34375, y: 0.28125 },\n { x: 0.40625, y: 0.28125 },\n { x: 0.40625, y: 0.28125 },\n { x: 0.46875, y: 0.28125 },\n { x: 0.46875, y: 0.28125 },\n { x: 0.53125, y: 0.28125 },\n { x: 0.53125, y: 0.28125 },\n { x: 0.59375, y: 0.28125 },\n { x: 0.59375, y: 0.28125 },\n { x: 0.65625, y: 0.28125 },\n { x: 0.65625, y: 0.28125 },\n { x: 0.71875, y: 0.28125 },\n { x: 0.71875, y: 0.28125 },\n { x: 0.78125, y: 0.28125 },\n { x: 0.78125, y: 0.28125 },\n { x: 0.84375, y: 0.28125 },\n { x: 0.84375, y: 0.28125 },\n { x: 0.90625, y: 0.28125 },\n { x: 0.90625, y: 0.28125 },\n { x: 0.96875, y: 0.28125 },\n { x: 0.96875, y: 0.28125 },\n { x: 0.03125, y: 0.34375 },\n { x: 0.03125, y: 0.34375 },\n { x: 0.09375, y: 0.34375 },\n { x: 0.09375, y: 0.34375 },\n { x: 0.15625, y: 0.34375 },\n { x: 0.15625, y: 0.34375 },\n { x: 0.21875, y: 0.34375 },\n { x: 0.21875, y: 0.34375 },\n { x: 0.28125, y: 0.34375 },\n { x: 0.28125, y: 0.34375 },\n { x: 0.34375, y: 0.34375 },\n { x: 0.34375, y: 0.34375 },\n { x: 0.40625, y: 0.34375 },\n { x: 0.40625, y: 0.34375 },\n { x: 0.46875, y: 0.34375 },\n { x: 0.46875, y: 0.34375 },\n { x: 0.53125, y: 0.34375 },\n { x: 0.53125, y: 0.34375 },\n { x: 0.59375, y: 0.34375 },\n { x: 0.59375, y: 0.34375 },\n { x: 0.65625, y: 0.34375 },\n { x: 0.65625, y: 0.34375 },\n { x: 0.71875, y: 0.34375 },\n { x: 0.71875, y: 0.34375 },\n { x: 0.78125, y: 0.34375 },\n { x: 0.78125, y: 0.34375 },\n { x: 0.84375, y: 0.34375 },\n { x: 0.84375, y: 0.34375 },\n { x: 0.90625, y: 0.34375 },\n { x: 0.90625, y: 0.34375 },\n { x: 0.96875, y: 0.34375 },\n { x: 0.96875, y: 0.34375 },\n { x: 0.03125, y: 0.40625 },\n { x: 0.03125, y: 0.40625 },\n { x: 0.09375, y: 0.40625 },\n { x: 0.09375, y: 0.40625 },\n { x: 0.15625, y: 0.40625 },\n { x: 0.15625, y: 0.40625 },\n { x: 0.21875, y: 0.40625 },\n { x: 0.21875, y: 0.40625 },\n { x: 0.28125, y: 0.40625 },\n { x: 0.28125, y: 0.40625 },\n { x: 0.34375, y: 0.40625 },\n { x: 0.34375, y: 0.40625 },\n { x: 0.40625, y: 0.40625 },\n { x: 0.40625, y: 0.40625 },\n { x: 0.46875, y: 0.40625 },\n { x: 0.46875, y: 0.40625 },\n { x: 0.53125, y: 0.40625 },\n { x: 0.53125, y: 0.40625 },\n { x: 0.59375, y: 0.40625 },\n { x: 0.59375, y: 0.40625 },\n { x: 0.65625, y: 0.40625 },\n { x: 0.65625, y: 0.40625 },\n { x: 0.71875, y: 0.40625 },\n { x: 0.71875, y: 0.40625 },\n { x: 0.78125, y: 0.40625 },\n { x: 0.78125, y: 0.40625 },\n { x: 0.84375, y: 0.40625 },\n { x: 0.84375, y: 0.40625 },\n { x: 0.90625, y: 0.40625 },\n { x: 0.90625, y: 0.40625 },\n { x: 0.96875, y: 0.40625 },\n { x: 0.96875, y: 0.40625 },\n { x: 0.03125, y: 0.46875 },\n { x: 0.03125, y: 0.46875 },\n { x: 0.09375, y: 0.46875 },\n { x: 0.09375, y: 0.46875 },\n { x: 0.15625, y: 0.46875 },\n { x: 0.15625, y: 0.46875 },\n { x: 0.21875, y: 0.46875 },\n { x: 0.21875, y: 0.46875 },\n { x: 0.28125, y: 0.46875 },\n { x: 0.28125, y: 0.46875 },\n { x: 0.34375, y: 0.46875 },\n { x: 0.34375, y: 0.46875 },\n { x: 0.40625, y: 0.46875 },\n { x: 0.40625, y: 0.46875 },\n { x: 0.46875, y: 0.46875 },\n { x: 0.46875, y: 0.46875 },\n { x: 0.53125, y: 0.46875 },\n { x: 0.53125, y: 0.46875 },\n { x: 0.59375, y: 0.46875 },\n { x: 0.59375, y: 0.46875 },\n { x: 0.65625, y: 0.46875 },\n { x: 0.65625, y: 0.46875 },\n { x: 0.71875, y: 0.46875 },\n { x: 0.71875, y: 0.46875 },\n { x: 0.78125, y: 0.46875 },\n { x: 0.78125, y: 0.46875 },\n { x: 0.84375, y: 0.46875 },\n { x: 0.84375, y: 0.46875 },\n { x: 0.90625, y: 0.46875 },\n { x: 0.90625, y: 0.46875 },\n { x: 0.96875, y: 0.46875 },\n { x: 0.96875, y: 0.46875 },\n { x: 0.03125, y: 0.53125 },\n { x: 0.03125, y: 0.53125 },\n { x: 0.09375, y: 0.53125 },\n { x: 0.09375, y: 0.53125 },\n { x: 0.15625, y: 0.53125 },\n { x: 0.15625, y: 0.53125 },\n { x: 0.21875, y: 0.53125 },\n { x: 0.21875, y: 0.53125 },\n { x: 0.28125, y: 0.53125 },\n { x: 0.28125, y: 0.53125 },\n { x: 0.34375, y: 0.53125 },\n { x: 0.34375, y: 0.53125 },\n { x: 0.40625, y: 0.53125 },\n { x: 0.40625, y: 0.53125 },\n { x: 0.46875, y: 0.53125 },\n { x: 0.46875, y: 0.53125 },\n { x: 0.53125, y: 0.53125 },\n { x: 0.53125, y: 0.53125 },\n { x: 0.59375, y: 0.53125 },\n { x: 0.59375, y: 0.53125 },\n { x: 0.65625, y: 0.53125 },\n { x: 0.65625, y: 0.53125 },\n { x: 0.71875, y: 0.53125 },\n { x: 0.71875, y: 0.53125 },\n { x: 0.78125, y: 0.53125 },\n { x: 0.78125, y: 0.53125 },\n { x: 0.84375, y: 0.53125 },\n { x: 0.84375, y: 0.53125 },\n { x: 0.90625, y: 0.53125 },\n { x: 0.90625, y: 0.53125 },\n { x: 0.96875, y: 0.53125 },\n { x: 0.96875, y: 0.53125 },\n { x: 0.03125, y: 0.59375 },\n { x: 0.03125, y: 0.59375 },\n { x: 0.09375, y: 0.59375 },\n { x: 0.09375, y: 0.59375 },\n { x: 0.15625, y: 0.59375 },\n { x: 0.15625, y: 0.59375 },\n { x: 0.21875, y: 0.59375 },\n { x: 0.21875, y: 0.59375 },\n { x: 0.28125, y: 0.59375 },\n { x: 0.28125, y: 0.59375 },\n { x: 0.34375, y: 0.59375 },\n { x: 0.34375, y: 0.59375 },\n { x: 0.40625, y: 0.59375 },\n { x: 0.40625, y: 0.59375 },\n { x: 0.46875, y: 0.59375 },\n { x: 0.46875, y: 0.59375 },\n { x: 0.53125, y: 0.59375 },\n { x: 0.53125, y: 0.59375 },\n { x: 0.59375, y: 0.59375 },\n { x: 0.59375, y: 0.59375 },\n { x: 0.65625, y: 0.59375 },\n { x: 0.65625, y: 0.59375 },\n { x: 0.71875, y: 0.59375 },\n { x: 0.71875, y: 0.59375 },\n { x: 0.78125, y: 0.59375 },\n { x: 0.78125, y: 0.59375 },\n { x: 0.84375, y: 0.59375 },\n { x: 0.84375, y: 0.59375 },\n { x: 0.90625, y: 0.59375 },\n { x: 0.90625, y: 0.59375 },\n { x: 0.96875, y: 0.59375 },\n { x: 0.96875, y: 0.59375 },\n { x: 0.03125, y: 0.65625 },\n { x: 0.03125, y: 0.65625 },\n { x: 0.09375, y: 0.65625 },\n { x: 0.09375, y: 0.65625 },\n { x: 0.15625, y: 0.65625 },\n { x: 0.15625, y: 0.65625 },\n { x: 0.21875, y: 0.65625 },\n { x: 0.21875, y: 0.65625 },\n { x: 0.28125, y: 0.65625 },\n { x: 0.28125, y: 0.65625 },\n { x: 0.34375, y: 0.65625 },\n { x: 0.34375, y: 0.65625 },\n { x: 0.40625, y: 0.65625 },\n { x: 0.40625, y: 0.65625 },\n { x: 0.46875, y: 0.65625 },\n { x: 0.46875, y: 0.65625 },\n { x: 0.53125, y: 0.65625 },\n { x: 0.53125, y: 0.65625 },\n { x: 0.59375, y: 0.65625 },\n { x: 0.59375, y: 0.65625 },\n { x: 0.65625, y: 0.65625 },\n { x: 0.65625, y: 0.65625 },\n { x: 0.71875, y: 0.65625 },\n { x: 0.71875, y: 0.65625 },\n { x: 0.78125, y: 0.65625 },\n { x: 0.78125, y: 0.65625 },\n { x: 0.84375, y: 0.65625 },\n { x: 0.84375, y: 0.65625 },\n { x: 0.90625, y: 0.65625 },\n { x: 0.90625, y: 0.65625 },\n { x: 0.96875, y: 0.65625 },\n { x: 0.96875, y: 0.65625 },\n { x: 0.03125, y: 0.71875 },\n { x: 0.03125, y: 0.71875 },\n { x: 0.09375, y: 0.71875 },\n { x: 0.09375, y: 0.71875 },\n { x: 0.15625, y: 0.71875 },\n { x: 0.15625, y: 0.71875 },\n { x: 0.21875, y: 0.71875 },\n { x: 0.21875, y: 0.71875 },\n { x: 0.28125, y: 0.71875 },\n { x: 0.28125, y: 0.71875 },\n { x: 0.34375, y: 0.71875 },\n { x: 0.34375, y: 0.71875 },\n { x: 0.40625, y: 0.71875 },\n { x: 0.40625, y: 0.71875 },\n { x: 0.46875, y: 0.71875 },\n { x: 0.46875, y: 0.71875 },\n { x: 0.53125, y: 0.71875 },\n { x: 0.53125, y: 0.71875 },\n { x: 0.59375, y: 0.71875 },\n { x: 0.59375, y: 0.71875 },\n { x: 0.65625, y: 0.71875 },\n { x: 0.65625, y: 0.71875 },\n { x: 0.71875, y: 0.71875 },\n { x: 0.71875, y: 0.71875 },\n { x: 0.78125, y: 0.71875 },\n { x: 0.78125, y: 0.71875 },\n { x: 0.84375, y: 0.71875 },\n { x: 0.84375, y: 0.71875 },\n { x: 0.90625, y: 0.71875 },\n { x: 0.90625, y: 0.71875 },\n { x: 0.96875, y: 0.71875 },\n { x: 0.96875, y: 0.71875 },\n { x: 0.03125, y: 0.78125 },\n { x: 0.03125, y: 0.78125 },\n { x: 0.09375, y: 0.78125 },\n { x: 0.09375, y: 0.78125 },\n { x: 0.15625, y: 0.78125 },\n { x: 0.15625, y: 0.78125 },\n { x: 0.21875, y: 0.78125 },\n { x: 0.21875, y: 0.78125 },\n { x: 0.28125, y: 0.78125 },\n { x: 0.28125, y: 0.78125 },\n { x: 0.34375, y: 0.78125 },\n { x: 0.34375, y: 0.78125 },\n { x: 0.40625, y: 0.78125 },\n { x: 0.40625, y: 0.78125 },\n { x: 0.46875, y: 0.78125 },\n { x: 0.46875, y: 0.78125 },\n { x: 0.53125, y: 0.78125 },\n { x: 0.53125, y: 0.78125 },\n { x: 0.59375, y: 0.78125 },\n { x: 0.59375, y: 0.78125 },\n { x: 0.65625, y: 0.78125 },\n { x: 0.65625, y: 0.78125 },\n { x: 0.71875, y: 0.78125 },\n { x: 0.71875, y: 0.78125 },\n { x: 0.78125, y: 0.78125 },\n { x: 0.78125, y: 0.78125 },\n { x: 0.84375, y: 0.78125 },\n { x: 0.84375, y: 0.78125 },\n { x: 0.90625, y: 0.78125 },\n { x: 0.90625, y: 0.78125 },\n { x: 0.96875, y: 0.78125 },\n { x: 0.96875, y: 0.78125 },\n { x: 0.03125, y: 0.84375 },\n { x: 0.03125, y: 0.84375 },\n { x: 0.09375, y: 0.84375 },\n { x: 0.09375, y: 0.84375 },\n { x: 0.15625, y: 0.84375 },\n { x: 0.15625, y: 0.84375 },\n { x: 0.21875, y: 0.84375 },\n { x: 0.21875, y: 0.84375 },\n { x: 0.28125, y: 0.84375 },\n { x: 0.28125, y: 0.84375 },\n { x: 0.34375, y: 0.84375 },\n { x: 0.34375, y: 0.84375 },\n { x: 0.40625, y: 0.84375 },\n { x: 0.40625, y: 0.84375 },\n { x: 0.46875, y: 0.84375 },\n { x: 0.46875, y: 0.84375 },\n { x: 0.53125, y: 0.84375 },\n { x: 0.53125, y: 0.84375 },\n { x: 0.59375, y: 0.84375 },\n { x: 0.59375, y: 0.84375 },\n { x: 0.65625, y: 0.84375 },\n { x: 0.65625, y: 0.84375 },\n { x: 0.71875, y: 0.84375 },\n { x: 0.71875, y: 0.84375 },\n { x: 0.78125, y: 0.84375 },\n { x: 0.78125, y: 0.84375 },\n { x: 0.84375, y: 0.84375 },\n { x: 0.84375, y: 0.84375 },\n { x: 0.90625, y: 0.84375 },\n { x: 0.90625, y: 0.84375 },\n { x: 0.96875, y: 0.84375 },\n { x: 0.96875, y: 0.84375 },\n { x: 0.03125, y: 0.90625 },\n { x: 0.03125, y: 0.90625 },\n { x: 0.09375, y: 0.90625 },\n { x: 0.09375, y: 0.90625 },\n { x: 0.15625, y: 0.90625 },\n { x: 0.15625, y: 0.90625 },\n { x: 0.21875, y: 0.90625 },\n { x: 0.21875, y: 0.90625 },\n { x: 0.28125, y: 0.90625 },\n { x: 0.28125, y: 0.90625 },\n { x: 0.34375, y: 0.90625 },\n { x: 0.34375, y: 0.90625 },\n { x: 0.40625, y: 0.90625 },\n { x: 0.40625, y: 0.90625 },\n { x: 0.46875, y: 0.90625 },\n { x: 0.46875, y: 0.90625 },\n { x: 0.53125, y: 0.90625 },\n { x: 0.53125, y: 0.90625 },\n { x: 0.59375, y: 0.90625 },\n { x: 0.59375, y: 0.90625 },\n { x: 0.65625, y: 0.90625 },\n { x: 0.65625, y: 0.90625 },\n { x: 0.71875, y: 0.90625 },\n { x: 0.71875, y: 0.90625 },\n { x: 0.78125, y: 0.90625 },\n { x: 0.78125, y: 0.90625 },\n { x: 0.84375, y: 0.90625 },\n { x: 0.84375, y: 0.90625 },\n { x: 0.90625, y: 0.90625 },\n { x: 0.90625, y: 0.90625 },\n { x: 0.96875, y: 0.90625 },\n { x: 0.96875, y: 0.90625 },\n { x: 0.03125, y: 0.96875 },\n { x: 0.03125, y: 0.96875 },\n { x: 0.09375, y: 0.96875 },\n { x: 0.09375, y: 0.96875 },\n { x: 0.15625, y: 0.96875 },\n { x: 0.15625, y: 0.96875 },\n { x: 0.21875, y: 0.96875 },\n { x: 0.21875, y: 0.96875 },\n { x: 0.28125, y: 0.96875 },\n { x: 0.28125, y: 0.96875 },\n { x: 0.34375, y: 0.96875 },\n { x: 0.34375, y: 0.96875 },\n { x: 0.40625, y: 0.96875 },\n { x: 0.40625, y: 0.96875 },\n { x: 0.46875, y: 0.96875 },\n { x: 0.46875, y: 0.96875 },\n { x: 0.53125, y: 0.96875 },\n { x: 0.53125, y: 0.96875 },\n { x: 0.59375, y: 0.96875 },\n { x: 0.59375, y: 0.96875 },\n { x: 0.65625, y: 0.96875 },\n { x: 0.65625, y: 0.96875 },\n { x: 0.71875, y: 0.96875 },\n { x: 0.71875, y: 0.96875 },\n { x: 0.78125, y: 0.96875 },\n { x: 0.78125, y: 0.96875 },\n { x: 0.84375, y: 0.96875 },\n { x: 0.84375, y: 0.96875 },\n { x: 0.90625, y: 0.96875 },\n { x: 0.90625, y: 0.96875 },\n { x: 0.96875, y: 0.96875 },\n { x: 0.96875, y: 0.96875 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.0625, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.1875, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.3125, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.4375, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.5625, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.6875, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.8125, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.9375, y: 0.0625 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.0625, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.1875, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.3125, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.4375, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.5625, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.6875, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.8125, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.9375, y: 0.1875 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.0625, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.1875, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.3125, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.4375, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.5625, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.6875, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.8125, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.9375, y: 0.3125 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.0625, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.1875, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.3125, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.4375, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.5625, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.6875, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.8125, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.9375, y: 0.4375 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.0625, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.1875, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.3125, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.4375, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.5625, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.6875, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.8125, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.9375, y: 0.5625 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.0625, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.1875, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.3125, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.4375, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.5625, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.6875, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.8125, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.9375, y: 0.6875 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.0625, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.1875, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.3125, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.4375, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.5625, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.6875, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.8125, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.9375, y: 0.8125 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.0625, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.1875, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.3125, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.4375, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.5625, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.6875, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.8125, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n { x: 0.9375, y: 0.9375 },\n];\n", "/**\n * HandPose model implementation\n * See `handpose.ts` for entry point\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as util from './handposeutil';\nimport * as anchors from './handposeanchors';\nimport { constants } from '../tfjs/constants';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Point } from '../result';\nimport type { Config } from '../config';\n\nexport class HandDetector {\n model: GraphModel;\n anchors: number[][];\n anchorsTensor: Tensor;\n inputSize: number;\n inputSizeTensor: Tensor;\n doubleInputSizeTensor: Tensor;\n\n constructor(model: GraphModel) {\n this.model = model;\n this.anchors = anchors.anchors.map((anchor) => [anchor.x, anchor.y]);\n this.anchorsTensor = tf.tensor2d(this.anchors);\n this.inputSize = this?.model?.inputs?.[0]?.shape?.[2] || 0;\n this.inputSizeTensor = tf.tensor1d([this.inputSize, this.inputSize]);\n this.doubleInputSizeTensor = tf.tensor1d([this.inputSize * 2, this.inputSize * 2]);\n }\n\n normalizeBoxes(boxes) {\n const t: Record = {};\n t.boxOffsets = tf.slice(boxes, [0, 0], [-1, 2]);\n t.boxSizes = tf.slice(boxes, [0, 2], [-1, 2]);\n t.div = tf.div(t.boxOffsets, this.inputSizeTensor);\n t.boxCenterPoints = tf.add(t.div, this.anchorsTensor);\n t.halfBoxSizes = tf.div(t.boxSizes, this.doubleInputSizeTensor);\n t.sub = tf.sub(t.boxCenterPoints, t.halfBoxSizes);\n t.startPoints = tf.mul(t.sub, this.inputSizeTensor);\n t.add = tf.add(t.boxCenterPoints, t.halfBoxSizes);\n t.endPoints = tf.mul(t.add, this.inputSizeTensor);\n const res = tf.concat2d([t.startPoints, t.endPoints], 1);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return res as Tensor;\n }\n\n normalizeLandmarks(rawPalmLandmarks, index: number) {\n const t: Record = {};\n t.reshape = tf.reshape(rawPalmLandmarks, [-1, 7, 2]);\n t.div = tf.div(t.reshape, this.inputSizeTensor);\n t.landmarks = tf.add(t.div, this.anchors[index] ? this.anchors[index] : 0);\n const res = tf.mul(t.landmarks, this.inputSizeTensor);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return res as Tensor;\n }\n\n async predict(input: Tensor, config: Config): Promise<{ startPoint: Point; endPoint: Point, palmLandmarks: Point[]; confidence: number }[]> {\n const t: Record = {};\n t.resize = tf.image.resizeBilinear(input, [this.inputSize, this.inputSize]);\n t.div = tf.div(t.resize, constants.tf127);\n t.image = tf.sub(t.div, constants.tf1);\n t.batched = this.model.execute(t.image) as Tensor;\n t.predictions = tf.squeeze(t.batched);\n t.slice = tf.slice(t.predictions, [0, 0], [-1, 1]);\n t.sigmoid = tf.sigmoid(t.slice);\n t.scores = tf.squeeze(t.sigmoid);\n const scores = await t.scores.data();\n t.boxes = tf.slice(t.predictions, [0, 1], [-1, 4]);\n t.norm = this.normalizeBoxes(t.boxes);\n // box detection is flaky so we look for 3x boxes than we need results\n t.nms = await tf.image.nonMaxSuppressionAsync(t.norm, t.scores, 3 * (config.hand?.maxDetected || 1), config.hand.iouThreshold, config.hand.minConfidence);\n const nms = await t.nms.array() as number[];\n const hands: { startPoint: Point; endPoint: Point; palmLandmarks: Point[]; confidence: number }[] = [];\n for (const index of nms) {\n const p: Record = {};\n p.box = tf.slice(t.norm, [index, 0], [1, -1]);\n p.slice = tf.slice(t.predictions, [index, 5], [1, 14]);\n p.norm = this.normalizeLandmarks(p.slice, index);\n p.palmLandmarks = tf.reshape(p.norm, [-1, 2]);\n const box = await p.box.data();\n const startPoint = box.slice(0, 2) as unknown as Point;\n const endPoint = box.slice(2, 4) as unknown as Point;\n const palmLandmarks = await p.palmLandmarks.array();\n const hand = { startPoint, endPoint, palmLandmarks, confidence: scores[index] };\n const scaled = util.scaleBoxCoordinates(hand, [(input.shape[2] || 1) / this.inputSize, (input.shape[1] || 0) / this.inputSize]);\n hands.push(scaled);\n Object.keys(p).forEach((tensor) => tf.dispose(p[tensor]));\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return hands;\n }\n}\n", "/**\n * HandPose model implementation\n * See `handpose.ts` for entry point\n */\n\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as util from './handposeutil';\nimport type * as detector from './handposedetector';\nimport { constants } from '../tfjs/constants';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport { env } from '../util/env';\nimport { now } from '../util/util';\nimport type { Point } from '../result';\n\nconst palmBoxEnlargeFactor = 5; // default 3\nconst handBoxEnlargeFactor = 1.65; // default 1.65\nconst palmLandmarkIds = [0, 5, 9, 13, 17, 1, 2];\nconst palmLandmarksPalmBase = 0;\nconst palmLandmarksMiddleFingerBase = 2;\nlet lastTime = 0;\n\nexport class HandPipeline {\n handDetector: detector.HandDetector;\n handPoseModel: GraphModel;\n inputSize: number;\n storedBoxes: ({ startPoint: Point; endPoint: Point; palmLandmarks: Point[]; confidence: number } | null)[];\n skipped: number;\n detectedHands: number;\n\n constructor(handDetector, handPoseModel) {\n this.handDetector = handDetector;\n this.handPoseModel = handPoseModel;\n this.inputSize = this.handPoseModel?.inputs?.[0].shape?.[2] || 0;\n this.storedBoxes = [];\n this.skipped = Number.MAX_SAFE_INTEGER;\n this.detectedHands = 0;\n }\n\n calculateLandmarksBoundingBox(landmarks) { // eslint-disable-line class-methods-use-this\n const xs = landmarks.map((d) => d[0]);\n const ys = landmarks.map((d) => d[1]);\n const startPoint = [Math.min(...xs), Math.min(...ys)];\n const endPoint = [Math.max(...xs), Math.max(...ys)];\n return { startPoint, endPoint };\n }\n\n getBoxForPalmLandmarks(palmLandmarks, rotationMatrix) {\n const rotatedPalmLandmarks = palmLandmarks.map((coord) => util.rotatePoint([...coord, 1], rotationMatrix));\n const boxAroundPalm = this.calculateLandmarksBoundingBox(rotatedPalmLandmarks);\n return util.enlargeBox(util.squarifyBox(boxAroundPalm), palmBoxEnlargeFactor);\n }\n\n getBoxForHandLandmarks(landmarks) {\n const boundingBox = this.calculateLandmarksBoundingBox(landmarks);\n const boxAroundHand = util.enlargeBox(util.squarifyBox(boundingBox), handBoxEnlargeFactor);\n boxAroundHand.palmLandmarks = [];\n for (let i = 0; i < palmLandmarkIds.length; i++) {\n boxAroundHand.palmLandmarks.push(landmarks[palmLandmarkIds[i]].slice(0, 2));\n }\n return boxAroundHand;\n }\n\n transformRawCoords(rawCoords, box2, angle, rotationMatrix) {\n const boxSize = util.getBoxSize(box2);\n const scaleFactor = [boxSize[0] / this.inputSize, boxSize[1] / this.inputSize, (boxSize[0] + boxSize[1]) / this.inputSize / 2];\n const coordsScaled = rawCoords.map((coord) => [\n scaleFactor[0] * (coord[0] - this.inputSize / 2),\n scaleFactor[1] * (coord[1] - this.inputSize / 2),\n scaleFactor[2] * coord[2],\n ]);\n const coordsRotationMatrix = util.buildRotationMatrix(angle, [0, 0]);\n const coordsRotated = coordsScaled.map((coord) => {\n const rotated = util.rotatePoint(coord, coordsRotationMatrix);\n return [...rotated, coord[2]];\n });\n const inverseRotationMatrix = util.invertTransformMatrix(rotationMatrix);\n const boxCenter = [...util.getBoxCenter(box2), 1];\n const originalBoxCenter = [\n util.dot(boxCenter, inverseRotationMatrix[0]),\n util.dot(boxCenter, inverseRotationMatrix[1]),\n ];\n return coordsRotated.map((coord) => [\n Math.trunc(coord[0] + originalBoxCenter[0]),\n Math.trunc(coord[1] + originalBoxCenter[1]),\n Math.trunc(coord[2]),\n ]);\n }\n\n async estimateHands(image, config) {\n let useFreshBox = false;\n\n // run new detector every skipFrames\n let boxes;\n const skipTime = (config.hand.skipTime || 0) > (now() - lastTime);\n const skipFrame = this.skipped < (config.hand.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame) {\n boxes = await this.handDetector.predict(image, config);\n this.skipped = 0;\n }\n if (config.skipAllowed) this.skipped++;\n\n // if detector result count doesn't match current working set, use it to reset current working set\n if (boxes && (boxes.length > 0) && ((boxes.length !== this.detectedHands) && (this.detectedHands !== config.hand.maxDetected) || !config.hand.landmarks)) {\n this.detectedHands = 0;\n this.storedBoxes = [...boxes];\n // for (const possible of boxes) this.storedBoxes.push(possible);\n if (this.storedBoxes.length > 0) useFreshBox = true;\n }\n const hands: { landmarks: Point[], confidence: number, boxConfidence: number, fingerConfidence: number, box: { topLeft: Point, bottomRight: Point } }[] = [];\n\n // go through working set of boxes\n for (let i = 0; i < this.storedBoxes.length; i++) {\n const currentBox = this.storedBoxes[i];\n if (!currentBox) continue;\n if (config.hand.landmarks) {\n const angle = config.hand.rotation ? util.computeRotation(currentBox.palmLandmarks[palmLandmarksPalmBase], currentBox.palmLandmarks[palmLandmarksMiddleFingerBase]) : 0;\n const palmCenter = util.getBoxCenter(currentBox);\n const palmCenterNormalized = [palmCenter[0] / image.shape[2], palmCenter[1] / image.shape[1]];\n const rotatedImage = config.hand.rotation && env.kernels.includes('rotatewithoffset') ? tf.image.rotateWithOffset(image, angle, 0, palmCenterNormalized) : image.clone();\n const rotationMatrix = util.buildRotationMatrix(-angle, palmCenter);\n const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;\n const croppedInput = util.cutBoxFromImageAndResize(newBox, rotatedImage, [this.inputSize, this.inputSize]);\n const handImage = tf.div(croppedInput, constants.tf255);\n tf.dispose(croppedInput);\n tf.dispose(rotatedImage);\n const [confidenceT, keypoints] = this.handPoseModel.execute(handImage) as Tensor[];\n lastTime = now();\n tf.dispose(handImage);\n const confidence = (await confidenceT.data())[0];\n tf.dispose(confidenceT);\n if (confidence >= config.hand.minConfidence / 4) {\n const keypointsReshaped = tf.reshape(keypoints, [-1, 3]);\n const rawCoords = await keypointsReshaped.array();\n tf.dispose(keypoints);\n tf.dispose(keypointsReshaped);\n const coords = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);\n const nextBoundingBox = this.getBoxForHandLandmarks(coords);\n this.storedBoxes[i] = { ...nextBoundingBox, confidence };\n const result = {\n landmarks: coords,\n confidence,\n boxConfidence: currentBox.confidence,\n fingerConfidence: confidence,\n box: { topLeft: nextBoundingBox.startPoint, bottomRight: nextBoundingBox.endPoint },\n };\n hands.push(result);\n } else {\n this.storedBoxes[i] = null;\n }\n tf.dispose(keypoints);\n } else {\n // const enlarged = box.enlargeBox(box.squarifyBox(box.shiftBox(currentBox, HAND_BOX_SHIFT_VECTOR)), handBoxEnlargeFactor);\n const enlarged = util.enlargeBox(util.squarifyBox(currentBox), handBoxEnlargeFactor);\n const result = {\n confidence: currentBox.confidence,\n boxConfidence: currentBox.confidence,\n fingerConfidence: 0,\n box: { topLeft: enlarged.startPoint, bottomRight: enlarged.endPoint },\n landmarks: [],\n };\n hands.push(result);\n }\n }\n this.storedBoxes = this.storedBoxes.filter((a) => a !== null);\n this.detectedHands = hands.length;\n if (hands.length > config.hand.maxDetected) hands.length = config.hand.maxDetected;\n return hands;\n }\n}\n", "/**\n * FingerPose algorithm implementation\n * See `fingerpose.ts` for entry point\n */\n\nexport const Finger = {\n thumb: 0,\n index: 1,\n middle: 2,\n ring: 3,\n pinky: 4,\n all: [0, 1, 2, 3, 4], // just for convenience\n nameMapping: { 0: 'thumb', 1: 'index', 2: 'middle', 3: 'ring', 4: 'pinky' },\n // Describes mapping of joints based on the 21 points returned by handpose.\n // [0] Palm\n // [1-4] Thumb\n // [5-8] Index\n // [9-12] Middle\n // [13-16] Ring\n // [17-20] Pinky\n pointsMapping: {\n 0: [[0, 1], [1, 2], [2, 3], [3, 4]],\n 1: [[0, 5], [5, 6], [6, 7], [7, 8]],\n 2: [[0, 9], [9, 10], [10, 11], [11, 12]],\n 3: [[0, 13], [13, 14], [14, 15], [15, 16]],\n 4: [[0, 17], [17, 18], [18, 19], [19, 20]],\n },\n getName: (value) => Finger.nameMapping[value],\n getPoints: (value) => Finger.pointsMapping[value],\n};\n\nexport const FingerCurl = {\n none: 0,\n half: 1,\n full: 2,\n nameMapping: { 0: 'none', 1: 'half', 2: 'full' },\n getName: (value) => FingerCurl.nameMapping[value],\n};\n\nexport const FingerDirection = {\n verticalUp: 0,\n verticalDown: 1,\n horizontalLeft: 2,\n horizontalRight: 3,\n diagonalUpRight: 4,\n diagonalUpLeft: 5,\n diagonalDownRight: 6,\n diagonalDownLeft: 7,\n nameMapping: { 0: 'verticalUp', 1: 'verticalDown', 2: 'horizontalLeft', 3: 'horizontalRight', 4: 'diagonalUpRight', 5: 'diagonalUpLeft', 6: 'diagonalDownRight', 7: 'diagonalDownLeft' },\n getName: (value) => FingerDirection.nameMapping[value],\n};\n\nexport class FingerGesture {\n name;\n curls;\n directions;\n weights;\n weightsRelative;\n\n constructor(name) {\n // name (should be unique)\n this.name = name;\n this.curls = {};\n this.directions = {};\n this.weights = [1.0, 1.0, 1.0, 1.0, 1.0];\n this.weightsRelative = [1.0, 1.0, 1.0, 1.0, 1.0];\n }\n\n curl(finger, curl, confidence) {\n if (typeof this.curls[finger] === 'undefined') this.curls[finger] = [];\n this.curls[finger].push([curl, confidence]);\n }\n\n direction(finger, position, confidence) {\n if (!this.directions[finger]) this.directions[finger] = [];\n this.directions[finger].push([position, confidence]);\n }\n\n weight(finger, weight) {\n this.weights[finger] = weight;\n // recalculate relative weights\n const total = this.weights.reduce((a, b) => a + b, 0);\n this.weightsRelative = this.weights.map((el) => el * 5 / total);\n }\n\n matchAgainst(detectedCurls, detectedDirections) {\n let confidence = 0.0;\n // look at the detected curl of each finger and compare with\n // the expected curl of this finger inside current gesture\n for (const fingerIdx in detectedCurls) {\n const detectedCurl = detectedCurls[fingerIdx];\n const expectedCurls = this.curls[fingerIdx];\n if (typeof expectedCurls === 'undefined') {\n // no curl description available for this finger\n // add default confidence of \"1\"\n confidence += this.weightsRelative[fingerIdx];\n continue;\n }\n // compare to each possible curl of this specific finger\n for (const [expectedCurl, score] of expectedCurls) {\n if (detectedCurl === expectedCurl) {\n confidence += score * this.weightsRelative[fingerIdx];\n break;\n }\n }\n }\n // same for detected direction of each finger\n for (const fingerIdx in detectedDirections) {\n const detectedDirection = detectedDirections[fingerIdx];\n const expectedDirections = this.directions[fingerIdx];\n if (typeof expectedDirections === 'undefined') {\n // no direction description available for this finger\n // add default confidence of \"1\"\n confidence += this.weightsRelative[fingerIdx];\n continue;\n }\n // compare to each possible direction of this specific finger\n for (const [expectedDirection, score] of expectedDirections) {\n if (detectedDirection === expectedDirection) {\n confidence += score * this.weightsRelative[fingerIdx];\n break;\n }\n }\n }\n return confidence / 10;\n }\n}\n", "/**\n * FingerPose algorithm implementation\n * See `fingerpose.ts` for entry point\n */\n\nimport { Finger, FingerCurl, FingerDirection, FingerGesture } from './fingerdef';\n\nexport const { thumb, index, middle, ring, pinky } = Finger;\nexport const { none, half, full } = FingerCurl;\nexport const { verticalUp, verticalDown, horizontalLeft, horizontalRight, diagonalUpRight, diagonalUpLeft, diagonalDownRight, diagonalDownLeft } = FingerDirection;\n\n// describe thumbs up gesture \uD83D\uDC4D\nconst ThumbsUp = new FingerGesture('thumbs up');\nThumbsUp.curl(thumb, none, 1.0);\nThumbsUp.direction(thumb, verticalUp, 1.0);\nThumbsUp.direction(thumb, diagonalUpLeft, 0.25);\nThumbsUp.direction(thumb, diagonalUpRight, 0.25);\nfor (const finger of [Finger.index, Finger.middle, Finger.ring, Finger.pinky]) {\n ThumbsUp.curl(finger, full, 1.0);\n ThumbsUp.direction(finger, horizontalLeft, 1.0);\n ThumbsUp.direction(finger, horizontalRight, 1.0);\n}\n\n// describe Victory gesture \u270C\uFE0F\nconst Victory = new FingerGesture('victory');\nVictory.curl(thumb, half, 0.5);\nVictory.curl(thumb, none, 0.5);\nVictory.direction(thumb, verticalUp, 1.0);\nVictory.direction(thumb, diagonalUpLeft, 1.0);\nVictory.curl(index, none, 1.0);\nVictory.direction(index, verticalUp, 0.75);\nVictory.direction(index, diagonalUpLeft, 1.0);\nVictory.curl(middle, none, 1.0);\nVictory.direction(middle, verticalUp, 1.0);\nVictory.direction(middle, diagonalUpLeft, 0.75);\nVictory.curl(ring, full, 1.0);\nVictory.direction(ring, verticalUp, 0.2);\nVictory.direction(ring, diagonalUpLeft, 1.0);\nVictory.direction(ring, horizontalLeft, 0.2);\nVictory.curl(pinky, full, 1.0);\nVictory.direction(pinky, verticalUp, 0.2);\nVictory.direction(pinky, diagonalUpLeft, 1.0);\nVictory.direction(pinky, horizontalLeft, 0.2);\nVictory.weight(index, 2);\nVictory.weight(middle, 2);\n\n// describe Point gesture \u270C\uFE0F\nconst Point = new FingerGesture('point');\nPoint.curl(thumb, full, 1.0);\nPoint.curl(index, none, 0.5);\nPoint.curl(middle, full, 0.5);\nPoint.curl(ring, full, 0.5);\nPoint.curl(pinky, full, 0.5);\nPoint.weight(index, 2);\nPoint.weight(middle, 2);\n\n// describe Point gesture \u270C\uFE0F\nconst MiddleFinger = new FingerGesture('middle finger');\nMiddleFinger.curl(thumb, none, 1.0);\nMiddleFinger.curl(index, full, 0.5);\nMiddleFinger.curl(middle, full, 0.5);\nMiddleFinger.curl(ring, full, 0.5);\nMiddleFinger.curl(pinky, full, 0.5);\nMiddleFinger.weight(index, 2);\nMiddleFinger.weight(middle, 2);\n\n// describe Open Palm gesture \u270C\uFE0F\nconst OpenPalm = new FingerGesture('open palm');\nOpenPalm.curl(thumb, none, 0.75);\nOpenPalm.curl(index, none, 0.75);\nOpenPalm.curl(middle, none, 0.75);\nOpenPalm.curl(ring, none, 0.75);\nOpenPalm.curl(pinky, none, 0.75);\n\nexport default [ThumbsUp, Victory, Point, MiddleFinger, OpenPalm];\n", "/**\n * FingerPose algorithm implementation constants\n *\n * Based on: [**FingerPose***](https://github.com/andypotato/fingerpose)\n */\n\n/* eslint-disable camelcase */\n\nimport { Finger, FingerCurl, FingerDirection } from './fingerdef';\nimport Gestures from '../hand/fingergesture';\n\nconst minConfidence = 0.7;\nconst options = {\n // curl estimation\n HALF_CURL_START_LIMIT: 60.0,\n NO_CURL_START_LIMIT: 130.0,\n // direction estimation\n DISTANCE_VOTE_POWER: 1.1,\n SINGLE_ANGLE_VOTE_POWER: 0.9,\n TOTAL_ANGLE_VOTE_POWER: 1.6,\n};\n\nfunction calculateSlope(point1x, point1y, point2x, point2y) {\n const value = (point1y - point2y) / (point1x - point2x);\n let slope = Math.atan(value) * 180 / Math.PI;\n if (slope <= 0) slope = -slope;\n else if (slope > 0) slope = 180 - slope;\n return slope;\n}\n\n// point1, point2 are 2d or 3d point arrays (xy[z])\n// returns either a single scalar (2d) or array of two slopes (3d)\nfunction getSlopes(point1, point2) {\n if (!point1 || !point2) return [0, 0];\n const slopeXY = calculateSlope(point1[0], point1[1], point2[0], point2[1]);\n if (point1.length === 2) return slopeXY;\n const slopeYZ = calculateSlope(point1[1], point1[2], point2[1], point2[2]);\n return [slopeXY, slopeYZ];\n}\n\nfunction angleOrientationAt(angle, weightageAt = 1.0) {\n let isVertical = 0;\n let isDiagonal = 0;\n let isHorizontal = 0;\n if (angle >= 75.0 && angle <= 105.0) isVertical = 1 * weightageAt;\n else if (angle >= 25.0 && angle <= 155.0) isDiagonal = 1 * weightageAt;\n else isHorizontal = 1 * weightageAt;\n return [isVertical, isDiagonal, isHorizontal];\n}\n\nfunction estimateFingerCurl(startPoint, midPoint, endPoint) {\n const start_mid_x_dist = startPoint[0] - midPoint[0];\n const start_end_x_dist = startPoint[0] - endPoint[0];\n const mid_end_x_dist = midPoint[0] - endPoint[0];\n const start_mid_y_dist = startPoint[1] - midPoint[1];\n const start_end_y_dist = startPoint[1] - endPoint[1];\n const mid_end_y_dist = midPoint[1] - endPoint[1];\n const start_mid_z_dist = startPoint[2] - midPoint[2];\n const start_end_z_dist = startPoint[2] - endPoint[2];\n const mid_end_z_dist = midPoint[2] - endPoint[2];\n const start_mid_dist = Math.sqrt(start_mid_x_dist * start_mid_x_dist + start_mid_y_dist * start_mid_y_dist + start_mid_z_dist * start_mid_z_dist);\n const start_end_dist = Math.sqrt(start_end_x_dist * start_end_x_dist + start_end_y_dist * start_end_y_dist + start_end_z_dist * start_end_z_dist);\n const mid_end_dist = Math.sqrt(mid_end_x_dist * mid_end_x_dist + mid_end_y_dist * mid_end_y_dist + mid_end_z_dist * mid_end_z_dist);\n let cos_in = (mid_end_dist * mid_end_dist + start_mid_dist * start_mid_dist - start_end_dist * start_end_dist) / (2 * mid_end_dist * start_mid_dist);\n if (cos_in > 1.0) cos_in = 1.0;\n else if (cos_in < -1.0) cos_in = -1.0;\n let angleOfCurve = Math.acos(cos_in);\n angleOfCurve = (57.2958 * angleOfCurve) % 180;\n let fingerCurl;\n if (angleOfCurve > options.NO_CURL_START_LIMIT) fingerCurl = FingerCurl.none;\n else if (angleOfCurve > options.HALF_CURL_START_LIMIT) fingerCurl = FingerCurl.half;\n else fingerCurl = FingerCurl.full;\n return fingerCurl;\n}\n\nfunction estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x) {\n let estimatedDirection;\n if (max_dist_x === Math.abs(start_end_x_dist)) {\n if (start_end_x_dist > 0) estimatedDirection = FingerDirection.horizontalLeft;\n else estimatedDirection = FingerDirection.horizontalRight;\n } else if (max_dist_x === Math.abs(start_mid_x_dist)) {\n if (start_mid_x_dist > 0) estimatedDirection = FingerDirection.horizontalLeft;\n else estimatedDirection = FingerDirection.horizontalRight;\n } else {\n if (mid_end_x_dist > 0) estimatedDirection = FingerDirection.horizontalLeft;\n else estimatedDirection = FingerDirection.horizontalRight;\n }\n return estimatedDirection;\n}\n\nfunction estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y) {\n let estimatedDirection;\n if (max_dist_y === Math.abs(start_end_y_dist)) {\n if (start_end_y_dist < 0) estimatedDirection = FingerDirection.verticalDown;\n else estimatedDirection = FingerDirection.verticalUp;\n } else if (max_dist_y === Math.abs(start_mid_y_dist)) {\n if (start_mid_y_dist < 0) estimatedDirection = FingerDirection.verticalDown;\n else estimatedDirection = FingerDirection.verticalUp;\n } else {\n if (mid_end_y_dist < 0) estimatedDirection = FingerDirection.verticalDown;\n else estimatedDirection = FingerDirection.verticalUp;\n }\n return estimatedDirection;\n}\n\nfunction estimateDiagonalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y, start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x) {\n let estimatedDirection;\n const reqd_vertical_direction = estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y);\n const reqd_horizontal_direction = estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);\n if (reqd_vertical_direction === FingerDirection.verticalUp) {\n if (reqd_horizontal_direction === FingerDirection.horizontalLeft) estimatedDirection = FingerDirection.diagonalUpLeft;\n else estimatedDirection = FingerDirection.diagonalUpRight;\n } else {\n if (reqd_horizontal_direction === FingerDirection.horizontalLeft) estimatedDirection = FingerDirection.diagonalDownLeft;\n else estimatedDirection = FingerDirection.diagonalDownRight;\n }\n return estimatedDirection;\n}\n\nfunction calculateFingerDirection(startPoint, midPoint, endPoint, fingerSlopes) {\n const start_mid_x_dist = startPoint[0] - midPoint[0];\n const start_end_x_dist = startPoint[0] - endPoint[0];\n const mid_end_x_dist = midPoint[0] - endPoint[0];\n const start_mid_y_dist = startPoint[1] - midPoint[1];\n const start_end_y_dist = startPoint[1] - endPoint[1];\n const mid_end_y_dist = midPoint[1] - endPoint[1];\n const max_dist_x = Math.max(Math.abs(start_mid_x_dist), Math.abs(start_end_x_dist), Math.abs(mid_end_x_dist));\n const max_dist_y = Math.max(Math.abs(start_mid_y_dist), Math.abs(start_end_y_dist), Math.abs(mid_end_y_dist));\n let voteVertical = 0.0;\n let voteDiagonal = 0.0;\n let voteHorizontal = 0.0;\n const start_end_x_y_dist_ratio = max_dist_y / (max_dist_x + 0.00001);\n if (start_end_x_y_dist_ratio > 1.5) voteVertical += options.DISTANCE_VOTE_POWER;\n else if (start_end_x_y_dist_ratio > 0.66) voteDiagonal += options.DISTANCE_VOTE_POWER;\n else voteHorizontal += options.DISTANCE_VOTE_POWER;\n const start_mid_dist = Math.sqrt(start_mid_x_dist * start_mid_x_dist + start_mid_y_dist * start_mid_y_dist);\n const start_end_dist = Math.sqrt(start_end_x_dist * start_end_x_dist + start_end_y_dist * start_end_y_dist);\n const mid_end_dist = Math.sqrt(mid_end_x_dist * mid_end_x_dist + mid_end_y_dist * mid_end_y_dist);\n const max_dist = Math.max(start_mid_dist, start_end_dist, mid_end_dist);\n let calc_start_point_x = startPoint[0];\n let calc_start_point_y = startPoint[1];\n let calc_end_point_x = endPoint[0];\n let calc_end_point_y = endPoint[1];\n if (max_dist === start_mid_dist) {\n calc_end_point_x = endPoint[0];\n calc_end_point_y = endPoint[1];\n } else if (max_dist === mid_end_dist) {\n calc_start_point_x = midPoint[0];\n calc_start_point_y = midPoint[1];\n }\n const calcStartPoint = [calc_start_point_x, calc_start_point_y];\n const calcEndPoint = [calc_end_point_x, calc_end_point_y];\n const totalAngle = getSlopes(calcStartPoint, calcEndPoint);\n const votes = angleOrientationAt(totalAngle, options.TOTAL_ANGLE_VOTE_POWER);\n voteVertical += votes[0];\n voteDiagonal += votes[1];\n voteHorizontal += votes[2];\n for (const fingerSlope of fingerSlopes) {\n const fingerVotes = angleOrientationAt(fingerSlope, options.SINGLE_ANGLE_VOTE_POWER);\n voteVertical += fingerVotes[0];\n voteDiagonal += fingerVotes[1];\n voteHorizontal += fingerVotes[2];\n }\n // in case of tie, highest preference goes to Vertical,\n // followed by horizontal and then diagonal\n let estimatedDirection;\n if (voteVertical === Math.max(voteVertical, voteDiagonal, voteHorizontal)) {\n estimatedDirection = estimateVerticalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y);\n } else if (voteHorizontal === Math.max(voteDiagonal, voteHorizontal)) {\n estimatedDirection = estimateHorizontalDirection(start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);\n } else {\n estimatedDirection = estimateDiagonalDirection(start_end_y_dist, start_mid_y_dist, mid_end_y_dist, max_dist_y, start_end_x_dist, start_mid_x_dist, mid_end_x_dist, max_dist_x);\n }\n return estimatedDirection;\n}\n\nfunction estimate(landmarks) {\n // step 1: calculate slopes\n const slopesXY: number[][] = [];\n const slopesYZ: number[][] = [];\n const fingerCurls: number[] = [];\n const fingerDirections: number[] = [];\n if (!landmarks) return { curls: fingerCurls, directions: fingerDirections };\n\n // step 1: calculate slopes\n for (const finger of Finger.all) {\n const points = Finger.getPoints(finger);\n const slopeAtXY: number[] = [];\n const slopeAtYZ: number[] = [];\n for (const point of points) {\n const point1 = landmarks[point[0]];\n const point2 = landmarks[point[1]];\n // calculate single slope\n const slopes = getSlopes(point1, point2);\n const slopeXY = slopes[0];\n const slopeYZ = slopes[1];\n slopeAtXY.push(slopeXY);\n slopeAtYZ.push(slopeYZ);\n }\n slopesXY.push(slopeAtXY);\n slopesYZ.push(slopeAtYZ);\n }\n\n // step 2: calculate orientations\n for (const finger of Finger.all) {\n // start finger predictions from palm - except for thumb\n const pointIndexAt = (finger === Finger.thumb) ? 1 : 0;\n const fingerPointsAt = Finger.getPoints(finger);\n const startPoint = landmarks[fingerPointsAt[pointIndexAt][0]];\n const midPoint = landmarks[fingerPointsAt[pointIndexAt + 1][1]];\n const endPoint = landmarks[fingerPointsAt[3][1]];\n // check if finger is curled\n const fingerCurled = estimateFingerCurl(startPoint, midPoint, endPoint);\n const fingerPosition = calculateFingerDirection(startPoint, midPoint, endPoint, slopesXY[finger].slice(pointIndexAt));\n fingerCurls[finger] = fingerCurled;\n fingerDirections[finger] = fingerPosition;\n }\n return { curls: fingerCurls, directions: fingerDirections };\n}\n\nexport function analyze(keypoints) { // get estimations of curl / direction for each finger\n if (!keypoints || keypoints.length === 0) return null;\n const estimatorRes = estimate(keypoints);\n const landmarks = {};\n for (const fingerIdx of Finger.all) {\n landmarks[Finger.getName(fingerIdx)] = {\n curl: FingerCurl.getName(estimatorRes.curls[fingerIdx]),\n direction: FingerDirection.getName(estimatorRes.directions[fingerIdx]),\n };\n }\n return landmarks;\n}\n\nexport function match(keypoints) { // compare gesture description to each known gesture\n const poses: { name: string, confidence: number }[] = [];\n if (!keypoints || keypoints.length === 0) return poses;\n const estimatorRes = estimate(keypoints);\n for (const gesture of Gestures) {\n const confidence = gesture.matchAgainst(estimatorRes.curls, estimatorRes.directions);\n if (confidence >= minConfidence) poses.push({ name: gesture.name, confidence });\n }\n return poses;\n}\n", "/**\n * HandPose model implementation\n *\n * Based on: [**MediaPipe HandPose**](https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view)\n */\n\nimport { log } from '../util/util';\nimport * as handdetector from './handposedetector';\nimport * as handpipeline from './handposepipeline';\nimport * as fingerPose from './fingerpose';\nimport { loadModel } from '../tfjs/load';\nimport type { HandResult, Box, Point } from '../result';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nconst meshAnnotations = {\n thumb: [1, 2, 3, 4],\n index: [5, 6, 7, 8],\n middle: [9, 10, 11, 12],\n ring: [13, 14, 15, 16],\n pinky: [17, 18, 19, 20],\n palm: [0],\n};\n\nlet handDetectorModel: GraphModel | null;\nlet handPoseModel: GraphModel | null;\nlet handPipeline: handpipeline.HandPipeline;\n\nexport async function predict(input: Tensor, config: Config): Promise {\n const predictions = await handPipeline.estimateHands(input, config);\n if (!predictions) return [];\n const hands: HandResult[] = [];\n for (let i = 0; i < predictions.length; i++) {\n const annotations = {};\n if (predictions[i].landmarks) {\n for (const key of Object.keys(meshAnnotations)) {\n annotations[key] = meshAnnotations[key].map((index) => predictions[i].landmarks[index]);\n }\n }\n const keypoints = predictions[i].landmarks as unknown as Point[];\n let box: Box = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 0, 0]; // maximums so conditionals work\n let boxRaw: Box = [0, 0, 0, 0];\n if (keypoints && keypoints.length > 0) { // if we have landmarks, calculate box based on landmarks\n for (const pt of keypoints) {\n if (pt[0] < box[0]) box[0] = pt[0];\n if (pt[1] < box[1]) box[1] = pt[1];\n if (pt[0] > box[2]) box[2] = pt[0];\n if (pt[1] > box[3]) box[3] = pt[1];\n }\n box[2] -= box[0];\n box[3] -= box[1];\n boxRaw = [box[0] / (input.shape[2] || 0), box[1] / (input.shape[1] || 0), box[2] / (input.shape[2] || 0), box[3] / (input.shape[1] || 0)];\n } else { // otherwise use box from prediction\n box = predictions[i].box ? [\n Math.trunc(Math.max(0, predictions[i].box.topLeft[0])),\n Math.trunc(Math.max(0, predictions[i].box.topLeft[1])),\n Math.trunc(Math.min((input.shape[2] || 0), predictions[i].box.bottomRight[0]) - Math.max(0, predictions[i].box.topLeft[0])),\n Math.trunc(Math.min((input.shape[1] || 0), predictions[i].box.bottomRight[1]) - Math.max(0, predictions[i].box.topLeft[1])),\n ] : [0, 0, 0, 0];\n boxRaw = [\n (predictions[i].box.topLeft[0]) / (input.shape[2] || 0),\n (predictions[i].box.topLeft[1]) / (input.shape[1] || 0),\n (predictions[i].box.bottomRight[0] - predictions[i].box.topLeft[0]) / (input.shape[2] || 0),\n (predictions[i].box.bottomRight[1] - predictions[i].box.topLeft[1]) / (input.shape[1] || 0),\n ];\n }\n const landmarks = fingerPose.analyze(keypoints);\n hands.push({\n id: i,\n score: Math.round(100 * predictions[i].confidence) / 100,\n boxScore: Math.round(100 * predictions[i].boxConfidence) / 100,\n fingerScore: Math.round(100 * predictions[i].fingerConfidence) / 100,\n label: 'hand',\n box,\n boxRaw,\n keypoints,\n annotations: annotations as HandResult['annotations'],\n landmarks: landmarks as HandResult['landmarks'],\n });\n }\n return hands;\n}\n\nexport async function load(config: Config): Promise<[GraphModel | null, GraphModel | null]> {\n if (env.initial) {\n handDetectorModel = null;\n handPoseModel = null;\n }\n if (!handDetectorModel || !handPoseModel) {\n [handDetectorModel, handPoseModel] = await Promise.all([\n config.hand.enabled ? loadModel(config.hand.detector?.modelPath) : null,\n config.hand.landmarks ? loadModel(config.hand.skeleton?.modelPath) : null,\n ]);\n } else {\n if (config.debug) log('cached model:', handDetectorModel['modelUrl']);\n if (config.debug) log('cached model:', handPoseModel['modelUrl']);\n }\n const handDetector = handDetectorModel ? new handdetector.HandDetector(handDetectorModel) : undefined;\n if (handDetector && handPoseModel) handPipeline = new handpipeline.HandPipeline(handDetector, handPoseModel);\n return [handDetectorModel, handPoseModel];\n}\n", "/** TFJS custom backend registration */\n\nimport type { Human } from '../human';\nimport { log } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as image from '../image/image';\nimport * as models from '../models';\nimport type { AnyCanvas } from '../exports';\n// import { env } from '../env';\n\nexport const config = {\n name: 'humangl',\n priority: 999,\n canvas: null as null | AnyCanvas,\n gl: null as null | WebGL2RenderingContext,\n extensions: [] as string[] | null,\n webGLattr: { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.2\n alpha: false,\n antialias: false,\n premultipliedAlpha: false,\n preserveDrawingBuffer: false,\n depth: false,\n stencil: false,\n failIfMajorPerformanceCaveat: false,\n desynchronized: true,\n },\n};\n\nfunction extensions(): void {\n /*\n https://www.khronos.org/registry/webgl/extensions/\n https://webglreport.com/?v=2\n */\n const gl = config.gl;\n if (!gl) return;\n config.extensions = gl.getSupportedExtensions();\n // gl.getExtension('KHR_parallel_shader_compile');\n}\n\n/**\n * Registers custom WebGL2 backend to be used by Human library\n *\n * @returns void\n */\nexport function register(instance: Human): void {\n // force backend reload if gl context is not valid\n if (instance.config.backend !== 'humangl') return;\n if ((config.name in tf.engine().registry) && !config?.gl?.getParameter(config.gl.VERSION)) {\n log('error: humangl backend invalid context');\n models.reset(instance);\n /*\n log('resetting humangl backend');\n await tf.removeBackend(config.name);\n await register(instance); // re-register\n */\n }\n if (!tf.findBackend(config.name)) {\n try {\n config.canvas = image.canvas(100, 100);\n } catch (err) {\n log('error: cannot create canvas:', err);\n return;\n }\n try {\n config.gl = config.canvas.getContext('webgl2', config.webGLattr);\n if (!config.gl) {\n log('error: cannot get WebGL context');\n return;\n }\n const glv2 = config.gl.getParameter(config.gl.VERSION).includes('2.0');\n if (!glv2) {\n log('override: using fallback webgl backend as webgl 2.0 is not detected');\n instance.config.backend = 'webgl';\n return;\n }\n if (config.canvas) {\n config.canvas.addEventListener('webglcontextlost', (e) => {\n log('error: humangl:', e.type);\n log('possible browser memory leak using webgl or conflict with multiple backend registrations');\n instance.emit('error');\n throw new Error('backend error: webgl context lost');\n // log('resetting humangl backend');\n // env.initial = true;\n // models.reset(instance);\n // await tf.removeBackend(config.name);\n // await register(instance); // re-register\n });\n config.canvas.addEventListener('webglcontextrestored', (e) => {\n log('error: humangl context restored:', e);\n });\n config.canvas.addEventListener('webglcontextcreationerror', (e) => {\n log('error: humangl context create:', e);\n });\n }\n } catch (err) {\n log('error: cannot get WebGL context:', err);\n return;\n }\n try {\n tf.setWebGLContext(2, config.gl);\n } catch (err) {\n log('error: cannot set WebGL context:', err);\n return;\n }\n try {\n const ctx = new tf.GPGPUContext(config.gl);\n tf.registerBackend(config.name, () => new tf.MathBackendWebGL(ctx), config.priority);\n } catch (err) {\n log('error: cannot register WebGL backend:', err);\n return;\n }\n try {\n const kernels = tf.getKernelsForBackend('webgl');\n kernels.forEach((kernelConfig) => {\n const newKernelConfig = { ...kernelConfig, backendName: config.name };\n tf.registerKernel(newKernelConfig);\n });\n } catch (err) {\n log('error: cannot update WebGL backend registration:', err);\n return;\n }\n const current = tf.backend().getGPGPUContext ? tf.backend().getGPGPUContext().gl : null;\n if (current) {\n log(`humangl webgl version:${current.getParameter(current.VERSION) as string} renderer:${current.getParameter(current.RENDERER) as string}`);\n } else {\n log('error: no current gl context:', current, config.gl);\n return;\n }\n try {\n if (tf.env().flagRegistry.WEBGL_VERSION) tf.env().set('WEBGL_VERSION', 2);\n } catch (err) {\n log('error: cannot set WebGL backend flags:', err);\n return;\n }\n extensions();\n log('backend registered:', config.name);\n }\n}\n", "/** TFJS backend initialization and customization */\n\nimport type { Human, Config } from '../human';\nimport { log, now } from '../util/util';\nimport { env } from '../util/env';\nimport * as humangl from './humangl';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as constants from './constants';\n\nfunction registerCustomOps(config: Config) {\n if (!env.kernels.includes('mod')) {\n const kernelMod = {\n kernelName: 'Mod',\n backendName: tf.getBackend(),\n kernelFunc: (op) => tf.tidy(() => tf.sub(op.inputs.a, tf.mul(tf.div(op.inputs.a, op.inputs.b), op.inputs.b))),\n };\n if (config.debug) log('registered kernel:', 'Mod');\n tf.registerKernel(kernelMod);\n env.kernels.push('mod');\n }\n if (!env.kernels.includes('floormod')) {\n const kernelFloorMod = {\n kernelName: 'FloorMod',\n backendName: tf.getBackend(),\n kernelFunc: (op) => tf.tidy(() => tf.add(tf.mul(tf.floorDiv(op.inputs.a / op.inputs.b), op.inputs.b), tf.mod(op.inputs.a, op.inputs.b))),\n };\n if (config.debug) log('registered kernel:', 'FloorMod');\n tf.registerKernel(kernelFloorMod);\n env.kernels.push('floormod');\n }\n /*\n if (!env.kernels.includes('atan2') && config.softwareKernels) {\n const kernelAtan2 = {\n kernelName: 'Atan2',\n backendName: tf.getBackend(),\n kernelFunc: (op) => tf.tidy(() => {\n const backend = tf.getBackend();\n tf.setBackend('cpu');\n const t = tf.atan2(op.inputs.a, op.inputs.b);\n tf.setBackend(backend);\n return t;\n }),\n };\n if (config.debug) log('registered kernel:', 'atan2');\n log('registered kernel:', 'atan2');\n tf.registerKernel(kernelAtan2);\n env.kernels.push('atan2');\n }\n */\n if (!env.kernels.includes('rotatewithoffset') && config.softwareKernels) {\n const kernelRotateWithOffset = {\n kernelName: 'RotateWithOffset',\n backendName: tf.getBackend(),\n kernelFunc: (op) => tf.tidy(() => {\n const backend = tf.getBackend();\n tf.setBackend('cpu');\n const t = tf.image.rotateWithOffset(op.inputs.image, op.attrs.radians, op.attrs.fillValue, op.attrs.center);\n tf.setBackend(backend);\n return t;\n }),\n };\n if (config.debug) log('registered kernel:', 'RotateWithOffset');\n tf.registerKernel(kernelRotateWithOffset);\n env.kernels.push('rotatewithoffset');\n }\n}\n\nexport async function check(instance: Human, force = false) {\n instance.state = 'backend';\n if (force || env.initial || (instance.config.backend && (instance.config.backend.length > 0) && (tf.getBackend() !== instance.config.backend))) {\n const timeStamp = now();\n\n if (instance.config.backend && instance.config.backend.length > 0) {\n // detect web worker\n // @ts-ignore ignore missing type for WorkerGlobalScope as that is the point\n if (typeof window === 'undefined' && typeof WorkerGlobalScope !== 'undefined' && instance.config.debug) {\n if (instance.config.debug) log('running inside web worker');\n }\n\n // force browser vs node backend\n if (env.browser && instance.config.backend === 'tensorflow') {\n if (instance.config.debug) log('override: backend set to tensorflow while running in browser');\n instance.config.backend = 'humangl';\n }\n if (env.node && (instance.config.backend === 'webgl' || instance.config.backend === 'humangl')) {\n if (instance.config.debug) log(`override: backend set to ${instance.config.backend} while running in nodejs`);\n instance.config.backend = 'tensorflow';\n }\n\n // handle webgpu\n if (env.browser && instance.config.backend === 'webgpu') {\n if (typeof navigator === 'undefined' || typeof navigator.gpu === 'undefined') {\n log('override: backend set to webgpu but browser does not support webgpu');\n instance.config.backend = 'humangl';\n } else {\n const adapter = await navigator.gpu.requestAdapter();\n if (instance.config.debug) log('enumerated webgpu adapter:', adapter);\n if (!adapter) {\n log('override: backend set to webgpu but browser reports no available gpu');\n instance.config.backend = 'humangl';\n } else {\n // @ts-ignore requestAdapterInfo is not in tslib\n const adapterInfo = 'requestAdapterInfo' in adapter ? await (adapter as GPUAdapter).requestAdapterInfo() : undefined;\n // if (adapter.features) adapter.features.forEach((feature) => log('webgpu features:', feature));\n log('webgpu adapter info:', adapterInfo);\n }\n }\n }\n\n // check available backends\n if (instance.config.backend === 'humangl') humangl.register(instance);\n const available = Object.keys(tf.engine().registryFactory as Record);\n if (instance.config.debug) log('available backends:', available);\n\n if (!available.includes(instance.config.backend)) {\n log(`error: backend ${instance.config.backend} not found in registry`);\n instance.config.backend = env.node ? 'tensorflow' : 'webgl';\n if (instance.config.debug) log(`override: setting backend ${instance.config.backend}`);\n }\n\n if (instance.config.debug) log('setting backend:', instance.config.backend);\n\n // customize wasm\n if (instance.config.backend === 'wasm') {\n if (tf.env().flagRegistry.CANVAS2D_WILL_READ_FREQUENTLY) tf.env().set('CANVAS2D_WILL_READ_FREQUENTLY', true);\n if (instance.config.debug) log('wasm path:', instance.config.wasmPath);\n if (typeof tf.setWasmPaths !== 'undefined') tf.setWasmPaths(instance.config.wasmPath, instance.config.wasmPlatformFetch);\n else throw new Error('backend error: attempting to use wasm backend but wasm path is not set');\n let mt = false;\n let simd = false;\n try {\n mt = await tf.env().getAsync('WASM_HAS_MULTITHREAD_SUPPORT');\n simd = await tf.env().getAsync('WASM_HAS_SIMD_SUPPORT');\n if (instance.config.debug) log(`wasm execution: ${simd ? 'simd' : 'no simd'} ${mt ? 'multithreaded' : 'singlethreaded'}`);\n if (instance.config.debug && !simd) log('warning: wasm simd support is not enabled');\n } catch {\n log('wasm detection failed');\n }\n }\n\n try {\n await tf.setBackend(instance.config.backend);\n await tf.ready();\n constants.init();\n } catch (err) {\n log('error: cannot set backend:', instance.config.backend, err);\n return false;\n }\n }\n\n // customize humangl\n if (tf.getBackend() === 'humangl') {\n if (tf.env().flagRegistry.CHECK_COMPUTATION_FOR_ERRORS) tf.env().set('CHECK_COMPUTATION_FOR_ERRORS', false);\n if (tf.env().flagRegistry.WEBGL_CPU_FORWARD) tf.env().set('WEBGL_CPU_FORWARD', true);\n if (tf.env().flagRegistry.WEBGL_USE_SHAPES_UNIFORMS) tf.env().set('WEBGL_USE_SHAPES_UNIFORMS', true);\n if (tf.env().flagRegistry.CPU_HANDOFF_SIZE_THRESHOLD) tf.env().set('CPU_HANDOFF_SIZE_THRESHOLD', 256);\n if (tf.env().flagRegistry.WEBGL_EXP_CONV) tf.env().set('WEBGL_EXP_CONV', true); // \n if (tf.env().flagRegistry.USE_SETTIMEOUTCUSTOM) tf.env().set('USE_SETTIMEOUTCUSTOM', true); // \n // if (tf.env().flagRegistry['WEBGL_PACK_DEPTHWISECONV']) tf.env().set('WEBGL_PACK_DEPTHWISECONV', false);\n // if (if (tf.env().flagRegistry['WEBGL_FORCE_F16_TEXTURES']) && !instance.config.object.enabled) tf.env().set('WEBGL_FORCE_F16_TEXTURES', true); // safe to use 16bit precision\n if (typeof instance.config.deallocate !== 'undefined' && instance.config.deallocate) { // hidden param\n log('changing webgl: WEBGL_DELETE_TEXTURE_THRESHOLD:', true);\n tf.env().set('WEBGL_DELETE_TEXTURE_THRESHOLD', 0);\n }\n if (tf.backend().getGPGPUContext) {\n const gl = await tf.backend().getGPGPUContext().gl;\n if (instance.config.debug) log(`gl version:${gl.getParameter(gl.VERSION) as string} renderer:${gl.getParameter(gl.RENDERER) as string}`);\n }\n }\n\n // customize webgpu\n if (tf.getBackend() === 'webgpu') {\n // if (tf.env().flagRegistry['WEBGPU_CPU_HANDOFF_SIZE_THRESHOLD']) tf.env().set('WEBGPU_CPU_HANDOFF_SIZE_THRESHOLD', 512);\n // if (tf.env().flagRegistry['WEBGPU_DEFERRED_SUBMIT_BATCH_SIZE']) tf.env().set('WEBGPU_DEFERRED_SUBMIT_BATCH_SIZE', 0);\n // if (tf.env().flagRegistry['WEBGPU_CPU_FORWARD']) tf.env().set('WEBGPU_CPU_FORWARD', true);\n }\n\n // wait for ready\n tf.enableProdMode();\n await tf.ready();\n\n instance.performance.initBackend = Math.trunc(now() - timeStamp);\n instance.config.backend = tf.getBackend();\n\n await env.updateBackend(); // update env on backend init\n registerCustomOps(instance.config);\n // await env.updateBackend(); // update env on backend init\n }\n return true;\n}\n\n// register fake missing tfjs ops\nexport function fakeOps(kernelNames: string[], config) {\n // if (config.debug) log('registerKernel:', kernelNames);\n for (const kernelName of kernelNames) {\n const kernelConfig = {\n kernelName,\n backendName: config.backend,\n kernelFunc: () => { if (config.debug) log('kernelFunc', kernelName, config.backend); },\n // setupFunc: () => { if (config.debug) log('kernelFunc', kernelName, config.backend); },\n // disposeFunc: () => { if (config.debug) log('kernelFunc', kernelName, config.backend); },\n };\n tf.registerKernel(kernelConfig);\n }\n env.kernels = tf.getKernelsForBackend(tf.getBackend()).map((kernel) => (kernel.kernelName as string).toLowerCase()); // re-scan registered ops\n}\n", "/**\n * HandTrack model implementation\n *\n * Based on:\n * - Hand Detection & Skeleton: [**MediaPipe HandPose**](https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view)\n * - Hand Tracking: [**HandTracking**](https://github.com/victordibia/handtracking)\n */\n\nimport { log, now } from '../util/util';\nimport * as box from '../util/box';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport type { HandResult, HandType, Box, Point } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\nimport * as fingerPose from './fingerpose';\nimport { fakeOps } from '../tfjs/backend';\nimport { constants } from '../tfjs/constants';\n\nconst models: [GraphModel | null, GraphModel | null] = [null, null];\nconst modelOutputNodes = ['StatefulPartitionedCall/Postprocessor/Slice', 'StatefulPartitionedCall/Postprocessor/ExpandDims_1'];\n\nconst inputSize = [[0, 0], [0, 0]];\n\nconst classes = ['hand', 'fist', 'pinch', 'point', 'face', 'tip', 'pinchtip'];\nconst faceIndex = 4;\n\nconst boxExpandFact = 1.6;\nconst maxDetectorResolution = 512;\nconst detectorExpandFact = 1.4;\n\nlet skipped = Number.MAX_SAFE_INTEGER;\nlet lastTime = 0;\nlet outputSize: [number, number] = [0, 0];\n\ninterface HandDetectResult {\n id: number,\n score: number,\n box: Box,\n boxRaw: Box,\n label: HandType,\n}\n\nconst cache: {\n boxes: HandDetectResult[],\n hands: HandResult[];\n} = {\n boxes: [],\n hands: [],\n};\n\nconst fingerMap = {\n /*\n thumb: [0, 1, 2, 3, 4],\n index: [0, 5, 6, 7, 8],\n middle: [0, 9, 10, 11, 12],\n ring: [0, 13, 14, 15, 16],\n pinky: [0, 17, 18, 19, 20],\n palm: [0],\n */\n thumb: [1, 2, 3, 4],\n index: [5, 6, 7, 8],\n middle: [9, 10, 11, 12],\n ring: [13, 14, 15, 16],\n pinky: [17, 18, 19, 20],\n base: [0],\n palm: [0, 17, 13, 9, 5, 1, 0],\n};\n\nexport async function loadDetect(config: Config): Promise {\n // HandTrack Model: Original: TFJS Port: \n if (env.initial) models[0] = null;\n if (!models[0]) {\n // handtrack model has some kernel ops defined in model but those are never referenced and non-existent in tfjs\n // ideally need to prune the model itself\n fakeOps(['tensorlistreserve', 'enter', 'tensorlistfromtensor', 'merge', 'loopcond', 'switch', 'exit', 'tensorliststack', 'nextiteration', 'tensorlistsetitem', 'tensorlistgetitem', 'reciprocal', 'shape', 'split', 'where'], config);\n models[0] = await loadModel(config.hand.detector?.modelPath);\n const inputs = models[0]['executor'] ? Object.values(models[0].modelSignature['inputs']) : undefined;\n inputSize[0][0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;\n inputSize[0][1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;\n } else if (config.debug) log('cached model:', models[0]['modelUrl']);\n return models[0];\n}\n\nexport async function loadSkeleton(config: Config): Promise {\n if (env.initial) models[1] = null;\n if (!models[1]) {\n models[1] = await loadModel(config.hand.skeleton?.modelPath);\n const inputs = models[1]['executor'] ? Object.values(models[1].modelSignature['inputs']) : undefined;\n inputSize[1][0] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[1].size) : 0;\n inputSize[1][1] = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 0;\n } else if (config.debug) log('cached model:', models[1]['modelUrl']);\n return models[1];\n}\n\nexport async function load(config: Config): Promise<[GraphModel | null, GraphModel | null]> {\n if (!models[0]) await loadDetect(config);\n if (!models[1]) await loadSkeleton(config);\n return models;\n}\n\nasync function detectHands(input: Tensor, config: Config): Promise {\n const hands: HandDetectResult[] = [];\n if (!input || !models[0]) return hands;\n const t: Record = {};\n const ratio = (input.shape[2] || 1) / (input.shape[1] || 1);\n const height = Math.min(Math.round((input.shape[1] || 0) / 8) * 8, maxDetectorResolution); // use dynamic input size but cap at 512\n const width = Math.round(height * ratio / 8) * 8;\n t.resize = tf.image.resizeBilinear(input, [height, width]); // todo: resize with padding\n t.cast = tf.cast(t.resize, 'int32');\n [t.rawScores, t.rawBoxes] = await models[0].executeAsync(t.cast, modelOutputNodes) as Tensor[];\n t.boxes = tf.squeeze(t.rawBoxes, [0, 2]);\n t.scores = tf.squeeze(t.rawScores, [0]);\n const classScores: Tensor[] = tf.unstack(t.scores, 1); // unstack scores based on classes\n tf.dispose(classScores[faceIndex]);\n classScores.splice(faceIndex, 1); // remove faces\n t.filtered = tf.stack(classScores, 1); // restack\n tf.dispose(classScores);\n // t.filtered = t.scores;\n t.max = tf.max(t.filtered, 1); // max overall score\n t.argmax = tf.argMax(t.filtered, 1); // class index of max overall score\n let id = 0;\n t.nms = await tf.image.nonMaxSuppressionAsync(t.boxes, t.max, (config.hand.maxDetected || 0) + 1, config.hand.iouThreshold || 0, config.hand.minConfidence || 1);\n const nms = await t.nms.data();\n const scores = await t.max.data();\n const classNum = await t.argmax.data();\n for (const nmsIndex of Array.from(nms)) { // generates results for each class\n const boxSlice = tf.slice(t.boxes, nmsIndex, 1);\n const boxYX = await boxSlice.data();\n tf.dispose(boxSlice);\n const boxData: Box = [boxYX[1], boxYX[0], boxYX[3] - boxYX[1], boxYX[2] - boxYX[0]]; // yx box reshaped to standard box\n const boxRaw: Box = box.scale(boxData, detectorExpandFact);\n const boxFull: Box = [Math.trunc(boxData[0] * outputSize[0]), Math.trunc(boxData[1] * outputSize[1]), Math.trunc(boxData[2] * outputSize[0]), Math.trunc(boxData[3] * outputSize[1])];\n const score = scores[nmsIndex];\n const label = classes[classNum[nmsIndex]] as HandType;\n const hand: HandDetectResult = { id: id++, score, box: boxFull, boxRaw, label };\n hands.push(hand);\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n hands.sort((a, b) => b.score - a.score);\n if (hands.length > (config.hand.maxDetected || 1)) hands.length = (config.hand.maxDetected || 1);\n return hands;\n}\n\nasync function detectFingers(input: Tensor, h: HandDetectResult, config: Config): Promise {\n const hand: HandResult = { // initial values inherited from hand detect\n id: h.id,\n score: Math.round(100 * h.score) / 100,\n boxScore: Math.round(100 * h.score) / 100,\n fingerScore: 0,\n box: h.box,\n boxRaw: h.boxRaw,\n label: h.label,\n keypoints: [],\n landmarks: {} as HandResult['landmarks'],\n annotations: {} as HandResult['annotations'],\n };\n if (input && models[1] && config.hand.landmarks && h.score > (config.hand.minConfidence || 0)) {\n const t: Record = {};\n const boxCrop = [h.boxRaw[1], h.boxRaw[0], h.boxRaw[3] + h.boxRaw[1], h.boxRaw[2] + h.boxRaw[0]] as Box;\n t.crop = tf.image.cropAndResize(input, [boxCrop], [0], [inputSize[1][0], inputSize[1][1]], 'bilinear');\n t.div = tf.div(t.crop, constants.tf255);\n [t.score, t.keypoints] = models[1].execute(t.div, ['Identity_1', 'Identity']) as Tensor[];\n const rawScore = (await t.score.data())[0];\n const score = (100 - Math.trunc(100 / (1 + Math.exp(rawScore)))) / 100; // reverse sigmoid value\n if (score >= (config.hand.minConfidence || 0)) {\n hand.fingerScore = score;\n t.reshaped = tf.reshape(t.keypoints, [-1, 3]);\n const coordsData: Point[] = await t.reshaped.array() as Point[];\n const coordsRaw: Point[] = coordsData.map((kpt) => [kpt[0] / inputSize[1][1], kpt[1] / inputSize[1][0], (kpt[2] || 0)]);\n const coordsNorm: Point[] = coordsRaw.map((kpt) => [kpt[0] * h.boxRaw[2], kpt[1] * h.boxRaw[3], (kpt[2] || 0)]);\n hand.keypoints = (coordsNorm).map((kpt) => [outputSize[0] * (kpt[0] + h.boxRaw[0]), outputSize[1] * (kpt[1] + h.boxRaw[1]), (kpt[2] || 0)]);\n hand.landmarks = fingerPose.analyze(hand.keypoints) as HandResult['landmarks']; // calculate finger gestures\n for (const key of Object.keys(fingerMap)) { // map keypoints to per-finger annotations\n hand.annotations[key] = fingerMap[key].map((index: number) => (hand.landmarks && hand.keypoints[index] ? hand.keypoints[index] : null));\n }\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n return hand;\n}\n\nexport async function predict(input: Tensor, config: Config): Promise {\n if (!models[0]?.['executor'] || !models[1]?.['executor'] || !models[0].inputs[0].shape || !models[1].inputs[0].shape) return []; // something is wrong with the model\n outputSize = [input.shape[2] || 0, input.shape[1] || 0];\n skipped++; // increment skip frames\n const skipTime = (config.hand.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.hand.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame) {\n return cache.hands; // return cached results without running anything\n }\n return new Promise(async (resolve) => {\n const skipTimeExtended = 3 * (config.hand.skipTime || 0) > (now() - lastTime);\n const skipFrameExtended = skipped < 3 * (config.hand.skipFrames || 0);\n if (config.skipAllowed && cache.hands.length === config.hand.maxDetected) { // we have all detected hands so we're definitely skipping\n cache.hands = await Promise.all(cache.boxes.map((handBox) => detectFingers(input, handBox, config)));\n } else if (config.skipAllowed && skipTimeExtended && skipFrameExtended && cache.hands.length > 0) { // we have some cached results: maybe not enough but anyhow continue for bit longer\n cache.hands = await Promise.all(cache.boxes.map((handBox) => detectFingers(input, handBox, config)));\n } else { // finally rerun detector\n cache.boxes = await detectHands(input, config);\n lastTime = now();\n cache.hands = await Promise.all(cache.boxes.map((handBox) => detectFingers(input, handBox, config)));\n skipped = 0;\n }\n\n const oldCache = [...cache.boxes];\n cache.boxes.length = 0; // reset cache\n if (config.cacheSensitivity > 0) {\n for (let i = 0; i < cache.hands.length; i++) {\n const boxKpt = box.square(cache.hands[i].keypoints, outputSize);\n if (boxKpt.box[2] / (input.shape[2] || 1) > 0.05 && boxKpt.box[3] / (input.shape[1] || 1) > 0.05 && cache.hands[i].fingerScore && cache.hands[i].fingerScore > (config.hand.minConfidence || 0)) {\n const boxScale = box.scale(boxKpt.box, boxExpandFact);\n const boxScaleRaw = box.scale(boxKpt.boxRaw, boxExpandFact);\n // const boxCrop = box.crop(boxScaleRaw);\n cache.boxes.push({ ...oldCache[i], box: boxScale, boxRaw: boxScaleRaw });\n }\n }\n }\n for (let i = 0; i < cache.hands.length; i++) { // replace detected boxes with calculated boxes in final output\n const bbox = box.calc(cache.hands[i].keypoints, outputSize);\n cache.hands[i].box = bbox.box;\n cache.hands[i].boxRaw = bbox.boxRaw;\n }\n resolve(cache.hands);\n });\n}\n", "/**\n * Anti-spoofing model implementation\n */\n\nimport { log, now } from '../util/util';\nimport { loadModel } from '../tfjs/load';\nimport type { Config } from '../config';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nconst cached: number[] = [];\nlet skipped = Number.MAX_SAFE_INTEGER;\nlet lastCount = 0;\nlet lastTime = 0;\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) model = await loadModel(config.face.liveness?.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function predict(image: Tensor, config: Config, idx: number, count: number): Promise {\n if (!model?.['executor']) return 0;\n const skipTime = (config.face.liveness?.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.face.liveness?.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && (lastCount === count) && cached[idx]) {\n skipped++;\n return cached[idx];\n }\n skipped = 0;\n return new Promise(async (resolve) => {\n const resize = tf.image.resizeBilinear(image, [model?.inputs[0].shape ? model.inputs[0].shape[2] : 0, model?.inputs[0].shape ? model.inputs[0].shape[1] : 0], false);\n const res = model?.execute(resize) as Tensor;\n const num = (await res.data())[0];\n cached[idx] = Math.round(100 * num) / 100;\n lastCount = count;\n lastTime = now();\n tf.dispose([resize, res]);\n resolve(cached[idx]);\n });\n}\n", "export const kpt: string[] = [ // used to create part labels\n 'nose',\n 'leftEye',\n 'rightEye',\n 'leftEar',\n 'rightEar',\n 'leftShoulder',\n 'rightShoulder',\n 'leftElbow',\n 'rightElbow',\n 'leftWrist',\n 'rightWrist',\n 'leftHip',\n 'rightHip',\n 'leftKnee',\n 'rightKnee',\n 'leftAnkle',\n 'rightAnkle',\n];\n\nexport const horizontal: string[][] = [ // used to fix left vs right\n ['leftEye', 'rightEye'],\n ['leftEar', 'rightEar'],\n ['leftShoulder', 'rightShoulder'],\n ['leftElbow', 'rightElbow'],\n ['leftWrist', 'rightWrist'],\n ['leftHip', 'rightHip'],\n ['leftKnee', 'rightKnee'],\n ['leftAnkle', 'rightAnkle'],\n];\n\nexport const vertical: string[][] = [ // used to remove unlikely keypoint positions\n ['leftKnee', 'leftShoulder'],\n ['rightKnee', 'rightShoulder'],\n ['leftAnkle', 'leftKnee'],\n ['rightAnkle', 'rightKnee'],\n];\n\nexport const relative: string[][][] = [ // used to match relative body parts\n [['leftHip', 'rightHip'], ['leftShoulder', 'rightShoulder']],\n [['leftElbow', 'rightElbow'], ['leftShoulder', 'rightShoulder']],\n];\n\nexport const connected: Record = { // used to create body outline in annotations\n leftLeg: ['leftHip', 'leftKnee', 'leftAnkle'],\n rightLeg: ['rightHip', 'rightKnee', 'rightAnkle'],\n torso: ['leftShoulder', 'rightShoulder', 'rightHip', 'leftHip', 'leftShoulder'],\n leftArm: ['leftShoulder', 'leftElbow', 'leftWrist'],\n rightArm: ['rightShoulder', 'rightElbow', 'rightWrist'],\n head: [],\n};\n", "import type { BodyKeypoint, BodyResult } from '../result';\nimport * as box from '../util/box';\nimport * as coords from './movenetcoords';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport type { Tensor } from '../tfjs/types';\n\nconst maxJitter = 0.005; // default allowed jitter is within 0.5%\n\nconst cache: {\n keypoints: BodyKeypoint[],\n padding: [number, number][];\n} = {\n keypoints: [],\n padding: [[0, 0], [0, 0], [0, 0], [0, 0]],\n};\n\nexport function bodyParts(body: BodyResult) { // model sometimes mixes up left vs right keypoints so we fix them\n for (const pair of coords.horizontal) { // fix body parts left vs right\n const left = body.keypoints.findIndex((kp) => kp.part === pair[0]);\n const right = body.keypoints.findIndex((kp) => kp.part === pair[1]);\n if (body.keypoints[left] && body.keypoints[right]) {\n if (body.keypoints[left].position[0] < body.keypoints[right].position[0]) {\n const tmp = body.keypoints[left];\n body.keypoints[left] = body.keypoints[right];\n body.keypoints[right] = tmp;\n }\n }\n }\n for (const pair of coords.vertical) { // remove body parts with improbable vertical position\n const lower = body.keypoints.findIndex((kp) => (kp && kp.part === pair[0]));\n const higher = body.keypoints.findIndex((kp) => (kp && kp.part === pair[1]));\n if (body.keypoints[lower] && body.keypoints[higher]) {\n if (body.keypoints[lower].position[1] < body.keypoints[higher].position[1]) {\n body.keypoints.splice(lower, 1);\n }\n }\n }\n for (const [pair, compare] of coords.relative) { // rearrange body parts according to their relative position\n const left = body.keypoints.findIndex((kp) => (kp && kp.part === pair[0]));\n const right = body.keypoints.findIndex((kp) => (kp && kp.part === pair[1]));\n const leftTo = body.keypoints.findIndex((kp) => (kp && kp.part === compare[0]));\n const rightTo = body.keypoints.findIndex((kp) => (kp && kp.part === compare[1]));\n if (!body.keypoints[leftTo] || !body.keypoints[rightTo]) continue; // only if we have both compare points\n const distanceLeft = body.keypoints[left] ? [\n Math.abs(body.keypoints[leftTo].position[0] - body.keypoints[left].position[0]),\n Math.abs(body.keypoints[rightTo].position[0] - body.keypoints[left].position[0]),\n ] : [0, 0];\n const distanceRight = body.keypoints[right] ? [\n Math.abs(body.keypoints[rightTo].position[0] - body.keypoints[right].position[0]),\n Math.abs(body.keypoints[leftTo].position[0] - body.keypoints[right].position[0]),\n ] : [0, 0];\n if (distanceLeft[0] > distanceLeft[1] || distanceRight[0] > distanceRight[1]) { // should flip keypoints\n const tmp = body.keypoints[left];\n body.keypoints[left] = body.keypoints[right];\n body.keypoints[right] = tmp;\n }\n }\n}\n\nexport function jitter(keypoints: BodyKeypoint[]): BodyKeypoint[] {\n for (let i = 0; i < keypoints.length; i++) {\n if (keypoints[i] && cache.keypoints[i]) {\n const diff = [Math.abs(keypoints[i].positionRaw[0] - cache.keypoints[i].positionRaw[0]), Math.abs(keypoints[i].positionRaw[1] - cache.keypoints[i].positionRaw[1])];\n if (diff[0] < maxJitter && diff[1] < maxJitter) {\n keypoints[i] = cache.keypoints[i]; // below jitter so replace keypoint\n } else {\n cache.keypoints[i] = keypoints[i]; // above jitter so update cache\n }\n } else {\n cache.keypoints[i] = keypoints[i]; // cache for keypoint doesnt exist so create it here\n }\n }\n return keypoints;\n}\n\nexport function padInput(input: Tensor, inputSize: number): Tensor {\n const t: Record = {};\n if (!input?.shape?.[1] || !input?.shape?.[2]) return input;\n cache.padding = [\n [0, 0], // dont touch batch\n [input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0, input.shape[2] > input.shape[1] ? Math.trunc((input.shape[2] - input.shape[1]) / 2) : 0], // height before&after\n [input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0, input.shape[1] > input.shape[2] ? Math.trunc((input.shape[1] - input.shape[2]) / 2) : 0], // width before&after\n [0, 0], // dont touch rbg\n ];\n t.pad = tf.pad(input, cache.padding);\n t.resize = tf.image.resizeBilinear(t.pad, [inputSize, inputSize]);\n const final = tf.cast(t.resize, 'int32');\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return final;\n}\n\nexport function rescaleBody(body: BodyResult, outputSize: [number, number]): BodyResult {\n body.keypoints = body.keypoints.filter((kpt) => kpt?.position); // filter invalid keypoints\n for (const kpt of body.keypoints) {\n kpt.position = [\n kpt.position[0] * (outputSize[0] + cache.padding[2][0] + cache.padding[2][1]) / outputSize[0] - cache.padding[2][0],\n kpt.position[1] * (outputSize[1] + cache.padding[1][0] + cache.padding[1][1]) / outputSize[1] - cache.padding[1][0],\n ];\n kpt.positionRaw = [\n kpt.position[0] / outputSize[0], kpt.position[1] / outputSize[1],\n ];\n }\n const rescaledBoxes = box.calc(body.keypoints.map((pt) => pt.position), outputSize);\n body.box = rescaledBoxes.box;\n body.boxRaw = rescaledBoxes.boxRaw;\n return body;\n}\n", "/**\n * MoveNet model implementation\n *\n * Based on: [**MoveNet**](https://blog.tensorflow.org/2021/05/next-generation-pose-detection-with-movenet-and-tensorflowjs.html)\n */\n\nimport { log, now } from '../util/util';\nimport * as box from '../util/box';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as coords from './movenetcoords';\nimport * as fix from './movenetfix';\nimport { loadModel } from '../tfjs/load';\nimport type { BodyKeypoint, BodyResult, BodyLandmark, BodyAnnotation, Box, Point } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { fakeOps } from '../tfjs/backend';\nimport { env } from '../util/env';\n\nlet model: GraphModel | null;\nlet inputSize = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\n// const boxExpandFact = 1.5; // increase to 150%\n\nconst cache: {\n boxes: Box[], // unused\n bodies: BodyResult[];\n last: number,\n} = {\n boxes: [],\n bodies: [],\n last: 0,\n};\n\nexport async function load(config: Config): Promise {\n if (env.initial) model = null;\n if (!model) {\n fakeOps(['size'], config);\n model = await loadModel(config.body.modelPath);\n } else if (config.debug) log('cached model:', model['modelUrl']);\n inputSize = (model?.['executor'] && model?.inputs?.[0].shape) ? model.inputs[0].shape[2] : 0;\n if (inputSize < 64) inputSize = 256;\n return model;\n}\n\nfunction parseSinglePose(res, config, image) {\n const kpt = res[0][0];\n const keypoints: BodyKeypoint[] = [];\n let score = 0;\n for (let id = 0; id < kpt.length; id++) {\n score = kpt[id][2];\n if (score > config.body.minConfidence) {\n const positionRaw: Point = [kpt[id][1], kpt[id][0]];\n keypoints.push({\n score: Math.round(100 * score) / 100,\n part: coords.kpt[id] as BodyLandmark,\n positionRaw,\n position: [ // normalized to input image size\n Math.round((image.shape[2] || 0) * positionRaw[0]),\n Math.round((image.shape[1] || 0) * positionRaw[1]),\n ],\n });\n }\n }\n score = keypoints.reduce((prev, curr) => (curr.score > prev ? curr.score : prev), 0);\n const bodies: BodyResult[] = [];\n const newBox = box.calc(keypoints.map((pt) => pt.position), [image.shape[2], image.shape[1]]);\n const annotations: Record = {};\n for (const [name, indexes] of Object.entries(coords.connected)) {\n const pt: Point[][] = [];\n for (let i = 0; i < indexes.length - 1; i++) {\n const pt0 = keypoints.find((kp) => kp.part === indexes[i]);\n const pt1 = keypoints.find((kp) => kp.part === indexes[i + 1]);\n if (pt0 && pt1 && pt0.score > (config.body.minConfidence || 0) && pt1.score > (config.body.minConfidence || 0)) pt.push([pt0.position, pt1.position]);\n }\n annotations[name] = pt;\n }\n const body: BodyResult = { id: 0, score, box: newBox.box, boxRaw: newBox.boxRaw, keypoints, annotations };\n fix.bodyParts(body);\n bodies.push(body);\n return bodies;\n}\n\nfunction parseMultiPose(res, config, image) {\n const bodies: BodyResult[] = [];\n for (let id = 0; id < res[0].length; id++) {\n const kpt = res[0][id];\n const totalScore = Math.round(100 * kpt[51 + 4]) / 100;\n if (totalScore > config.body.minConfidence) {\n const keypoints: BodyKeypoint[] = [];\n for (let i = 0; i < 17; i++) {\n const score = kpt[3 * i + 2];\n if (score > config.body.minConfidence) {\n const positionRaw: Point = [kpt[3 * i + 1], kpt[3 * i + 0]];\n keypoints.push({\n part: coords.kpt[i] as BodyLandmark,\n score: Math.round(100 * score) / 100,\n positionRaw,\n position: [Math.round((image.shape[2] || 0) * positionRaw[0]), Math.round((image.shape[1] || 0) * positionRaw[1])],\n });\n }\n }\n const newBox = box.calc(keypoints.map((pt) => pt.position), [image.shape[2], image.shape[1]]);\n // movenet-multipose has built-in box details\n // const boxRaw: Box = [kpt[51 + 1], kpt[51 + 0], kpt[51 + 3] - kpt[51 + 1], kpt[51 + 2] - kpt[51 + 0]];\n // const box: Box = [Math.trunc(boxRaw[0] * (image.shape[2] || 0)), Math.trunc(boxRaw[1] * (image.shape[1] || 0)), Math.trunc(boxRaw[2] * (image.shape[2] || 0)), Math.trunc(boxRaw[3] * (image.shape[1] || 0))];\n const annotations: Record = {} as Record;\n for (const [name, indexes] of Object.entries(coords.connected)) {\n const pt: Point[][] = [];\n for (let i = 0; i < indexes.length - 1; i++) {\n const pt0 = keypoints.find((kp) => kp.part === indexes[i]);\n const pt1 = keypoints.find((kp) => kp.part === indexes[i + 1]);\n if (pt0 && pt1 && pt0.score > (config.body.minConfidence || 0) && pt1.score > (config.body.minConfidence || 0)) pt.push([pt0.position, pt1.position]);\n }\n annotations[name] = pt;\n }\n const body: BodyResult = { id, score: totalScore, box: newBox.box, boxRaw: newBox.boxRaw, keypoints: [...keypoints], annotations };\n fix.bodyParts(body);\n bodies.push(body);\n }\n }\n bodies.sort((a, b) => b.score - a.score);\n if (bodies.length > config.body.maxDetected) bodies.length = config.body.maxDetected;\n return bodies;\n}\n\nexport async function predict(input: Tensor, config: Config): Promise {\n if (!model?.['executor'] || !model?.inputs?.[0].shape) return []; // something is wrong with the model\n if (!config.skipAllowed) cache.boxes.length = 0; // allowed to use cache or not\n skipped++; // increment skip frames\n const skipTime = (config.body.skipTime || 0) > (now() - cache.last);\n const skipFrame = skipped < (config.body.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame) {\n return cache.bodies; // return cached results without running anything\n }\n return new Promise(async (resolve) => {\n const t: Record = {};\n skipped = 0;\n // run detection on squared input and cached boxes\n /*\n cache.bodies = []; // reset bodies result\n if (cache.boxes.length >= (config.body.maxDetected || 0)) { // if we have enough cached boxes run detection using cache\n for (let i = 0; i < cache.boxes.length; i++) { // run detection based on cached boxes\n t.crop = tf.image.cropAndResize(input, [cache.boxes[i]], [0], [inputSize, inputSize], 'bilinear');\n t.cast = tf.cast(t.crop, 'int32');\n // t.input = prepareImage(input);\n t.res = model?.execute(t.cast) as Tensor;\n const res = await t.res.array();\n const newBodies = (t.res.shape[2] === 17) ? await parseSinglePose(res, config, input, cache.boxes[i]) : await parseMultiPose(res, config, input, cache.boxes[i]);\n cache.bodies = cache.bodies.concat(newBodies);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n }\n if (cache.bodies.length !== config.body.maxDetected) { // did not find enough bodies based on cached boxes so run detection on full frame\n t.input = prepareImage(input);\n t.res = model?.execute(t.input) as Tensor;\n const res = await t.res.array();\n cache.bodies = (t.res.shape[2] === 17) ? await parseSinglePose(res, config, input, [0, 0, 1, 1]) : await parseMultiPose(res, config, input, [0, 0, 1, 1]);\n for (const body of cache.bodies) rescaleBody(body, [input.shape[2] || 1, input.shape[1] || 1]);\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n }\n cache.boxes.length = 0; // reset cache\n for (let i = 0; i < cache.bodies.length; i++) {\n if (cache.bodies[i].keypoints.length > (coords.kpt.length / 2)) { // only update cache if we detected at least half keypoints\n const scaledBox = box.scale(cache.bodies[i].boxRaw, boxExpandFact);\n const cropBox = box.crop(scaledBox);\n cache.boxes.push(cropBox);\n }\n }\n */\n\n // run detection on squared input and no cached boxes\n t.input = fix.padInput(input, inputSize);\n t.res = model?.execute(t.input) as Tensor;\n cache.last = now();\n const res = await t.res.array();\n cache.bodies = (t.res.shape[2] === 17)\n ? parseSinglePose(res, config, input)\n : parseMultiPose(res, config, input);\n for (const body of cache.bodies) {\n fix.rescaleBody(body, [input.shape[2] || 1, input.shape[1] || 1]);\n fix.jitter(body.keypoints);\n }\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n\n resolve(cache.bodies);\n });\n}\n", "/**\n * NanoDet object detection model implementation\n *\n * Based on: [**MB3-CenterNet**](https://github.com/610265158/mobilenetv3_centernet)\n */\n\nimport { log, now } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport { constants } from '../tfjs/constants';\nimport { labels } from './labels';\nimport type { ObjectResult, ObjectType, Box } from '../result';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\n\nlet model: GraphModel;\nlet last: ObjectResult[] = [];\nlet lastTime = 0;\nlet skipped = Number.MAX_SAFE_INTEGER;\nlet inputSize = 0;\n\nconst scaleBox = 2.5; // increase box size\n\nexport async function load(config: Config): Promise {\n if (!model || env.initial) {\n model = await loadModel(config.object.modelPath);\n const inputs = model?.['executor'] ? Object.values(model.modelSignature['inputs']) : undefined;\n inputSize = Array.isArray(inputs) ? parseInt(inputs[0].tensorShape.dim[2].size) : 416;\n } else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nasync function process(res: Tensor[], outputShape: [number, number], config: Config) {\n let id = 0;\n let results: ObjectResult[] = [];\n const size = inputSize;\n for (const strideSize of [1, 2, 4]) { // try each stride size as it detects large/medium/small objects\n // find scores, boxes, classes\n const baseSize = strideSize * 13; // 13x13=169, 26x26=676, 52x52=2704\n // find boxes and scores output depending on stride\n const scoresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) === labels.length)));\n const scores = await scoresT.array(); // optionally use exponential scores or just as-is\n const featuresT = tf.squeeze(res.find((a: Tensor) => (a.shape[1] === (baseSize ** 2) && (a.shape[2] || 0) < labels.length)));\n 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\n const boxIdxT = boxesMaxT.argMax(2); // what we need is indexes of features with highest scores, not values itself\n const boxIdx = await boxIdxT.array(); // what we need is indexes of features with highest scores, not values itself\n for (let i = 0; i < scoresT.shape[0]; i++) { // total strides (x * y matrix)\n for (let j = 0; j < scoresT.shape[1]; j++) { // one score for each class\n const score = scores[i][j]; // get score for current position\n if (score > (config.object.minConfidence || 0) && j !== 61) {\n const cx = (0.5 + Math.trunc(i % baseSize)) / baseSize; // center.x normalized to range 0..1\n const cy = (0.5 + Math.trunc(i / baseSize)) / baseSize; // center.y normalized to range 0..1\n const boxOffset = boxIdx[i].map((a: number) => a * (baseSize / strideSize / (size))); // just grab indexes of features with highest scores\n const [x, y] = [\n cx - (scaleBox / strideSize * boxOffset[0]),\n cy - (scaleBox / strideSize * boxOffset[1]),\n ];\n const [w, h] = [\n cx + (scaleBox / strideSize * boxOffset[2]) - x,\n cy + (scaleBox / strideSize * boxOffset[3]) - y,\n ];\n let boxRaw: Box = [x, y, w, h]; // results normalized to range 0..1\n boxRaw = boxRaw.map((a) => Math.max(0, Math.min(a, 1))) as Box; // fix out-of-bounds coords\n const box = [ // results normalized to input image pixels\n boxRaw[0] * outputShape[0],\n boxRaw[1] * outputShape[1],\n boxRaw[2] * outputShape[0],\n boxRaw[3] * outputShape[1],\n ];\n const result = {\n id: id++,\n // strideSize,\n score: Math.round(100 * score) / 100,\n class: j + 1,\n label: labels[j].label as ObjectType,\n // center: [Math.trunc(outputShape[0] * cx), Math.trunc(outputShape[1] * cy)],\n // centerRaw: [cx, cy],\n box: box.map((a) => Math.trunc(a)) as Box,\n boxRaw,\n };\n results.push(result);\n }\n }\n }\n tf.dispose([scoresT, featuresT, boxesMaxT, boxIdxT]);\n }\n\n // normally nms is run on raw results, but since boxes need to be calculated this way we skip calulcation of\n // unnecessary boxes and run nms only on good candidates (basically it just does IOU analysis as scores are already filtered)\n const nmsBoxes = results.map((a) => [a.boxRaw[1], a.boxRaw[0], a.boxRaw[3], a.boxRaw[2]]); // switches coordinates from x,y to y,x as expected by tf.nms\n const nmsScores = results.map((a) => a.score);\n let nmsIdx: number[] = [];\n if (nmsBoxes && nmsBoxes.length > 0) {\n const nms = await tf.image.nonMaxSuppressionAsync(nmsBoxes, nmsScores, config.object.maxDetected, config.object.iouThreshold, config.object.minConfidence);\n nmsIdx = await nms.data();\n tf.dispose(nms);\n }\n\n // filter & sort results\n results = results\n .filter((_val, idx) => nmsIdx.includes(idx))\n .sort((a, b) => (b.score - a.score));\n\n return results;\n}\n\nexport async function predict(image: Tensor, config: Config): Promise {\n if (!model?.['executor']) return [];\n const skipTime = (config.object.skipTime || 0) > (now() - lastTime);\n const skipFrame = skipped < (config.object.skipFrames || 0);\n if (config.skipAllowed && skipTime && skipFrame && (last.length > 0)) {\n skipped++;\n return last;\n }\n skipped = 0;\n if (!env.kernels.includes('mod') || !env.kernels.includes('sparsetodense')) return last;\n return new Promise(async (resolve) => {\n const outputSize = [image.shape[2] || 0, image.shape[1] || 0];\n const resizeT = tf.image.resizeBilinear(image, [inputSize, inputSize], false);\n const normT = tf.div(resizeT, constants.tf255);\n const transposeT = tf.transpose(normT, [0, 3, 1, 2]);\n\n let objectT;\n if (config.object.enabled) objectT = model.execute(transposeT);\n lastTime = now();\n\n const obj = await process(objectT as Tensor[], outputSize as [number, number], config);\n last = obj;\n tf.dispose([resizeT, normT, transposeT, ...objectT]);\n resolve(obj);\n });\n}\n", "/**\n * PoseNet body detection model implementation constants\n * See `posenet.ts` for entry point\n */\n\nimport type { Point, BodyResult, BodyAnnotation, BodyLandmark } from '../result';\n\nexport const partNames = [\n 'nose', 'leftEye', 'rightEye', 'leftEar', 'rightEar', 'leftShoulder',\n 'rightShoulder', 'leftElbow', 'rightElbow', 'leftWrist', 'rightWrist',\n 'leftHip', 'rightHip', 'leftKnee', 'rightKnee', 'leftAnkle', 'rightAnkle',\n];\n\nexport const count = partNames.length; // 17 keypoints\n\nexport const partIds = partNames.reduce((result, jointName, i) => {\n result[jointName] = i;\n return result;\n}, {});\n\nconst connectedPartNames = [\n ['leftHip', 'leftShoulder'], ['leftElbow', 'leftShoulder'],\n ['leftElbow', 'leftWrist'], ['leftHip', 'leftKnee'],\n ['leftKnee', 'leftAnkle'], ['rightHip', 'rightShoulder'],\n ['rightElbow', 'rightShoulder'], ['rightElbow', 'rightWrist'],\n ['rightHip', 'rightKnee'], ['rightKnee', 'rightAnkle'],\n ['leftShoulder', 'rightShoulder'], ['leftHip', 'rightHip'],\n];\nexport const connectedPartIndices = connectedPartNames.map(([jointNameA, jointNameB]) => ([partIds[jointNameA], partIds[jointNameB]]));\n\nexport const poseChain = [\n ['nose', 'leftEye'], ['leftEye', 'leftEar'], ['nose', 'rightEye'],\n ['rightEye', 'rightEar'], ['nose', 'leftShoulder'],\n ['leftShoulder', 'leftElbow'], ['leftElbow', 'leftWrist'],\n ['leftShoulder', 'leftHip'], ['leftHip', 'leftKnee'],\n ['leftKnee', 'leftAnkle'], ['nose', 'rightShoulder'],\n ['rightShoulder', 'rightElbow'], ['rightElbow', 'rightWrist'],\n ['rightShoulder', 'rightHip'], ['rightHip', 'rightKnee'],\n ['rightKnee', 'rightAnkle'],\n];\n\nexport function eitherPointDoesntMeetConfidence(a: number, b: number, minConfidence: number) {\n return (a < minConfidence || b < minConfidence);\n}\n\nexport function getAdjacentKeyPoints(keypoints, minConfidence: number) {\n return connectedPartIndices.reduce((result, [leftJoint, rightJoint]) => {\n if (eitherPointDoesntMeetConfidence(keypoints[leftJoint].score, keypoints[rightJoint].score, minConfidence)) {\n return result;\n }\n result.push([keypoints[leftJoint], keypoints[rightJoint]]);\n return result;\n }, []);\n}\n\nexport function getBoundingBox(keypoints): [number, number, number, number] {\n const coord = keypoints.reduce(({ maxX, maxY, minX, minY }, { position: { x, y } }) => ({\n maxX: Math.max(maxX, x),\n maxY: Math.max(maxY, y),\n minX: Math.min(minX, x),\n minY: Math.min(minY, y),\n }), {\n maxX: Number.NEGATIVE_INFINITY,\n maxY: Number.NEGATIVE_INFINITY,\n minX: Number.POSITIVE_INFINITY,\n minY: Number.POSITIVE_INFINITY,\n });\n return [coord.minX, coord.minY, coord.maxX - coord.minX, coord.maxY - coord.minY];\n}\n\nexport function scalePoses(poses, [height, width], [inputResolutionHeight, inputResolutionWidth]): BodyResult[] {\n const scaleY = height / inputResolutionHeight;\n const scaleX = width / inputResolutionWidth;\n const scalePose = (pose, i): BodyResult => ({\n id: i,\n score: pose.score,\n boxRaw: [pose.box[0] / inputResolutionWidth, pose.box[1] / inputResolutionHeight, pose.box[2] / inputResolutionWidth, pose.box[3] / inputResolutionHeight],\n box: [Math.trunc(pose.box[0] * scaleX), Math.trunc(pose.box[1] * scaleY), Math.trunc(pose.box[2] * scaleX), Math.trunc(pose.box[3] * scaleY)],\n keypoints: pose.keypoints.map(({ score, part, position }) => ({\n score: score as number,\n part: part as BodyLandmark,\n position: [Math.trunc(position.x * scaleX), Math.trunc(position.y * scaleY)] as Point,\n positionRaw: [position.x / inputResolutionHeight, position.y / inputResolutionHeight] as Point,\n })),\n annotations: {} as Record,\n });\n const scaledPoses = poses.map((pose, i) => scalePose(pose, i));\n return scaledPoses;\n}\n\n// algorithm based on Coursera Lecture from Algorithms, Part 1: https://www.coursera.org/learn/algorithms-part1/lecture/ZjoSM/heapsort\nexport class MaxHeap {\n priorityQueue: unknown[]; // don't touch\n numberOfElements: number;\n getElementValue: unknown; // function call\n\n constructor(maxSize, getElementValue) {\n this.priorityQueue = new Array(maxSize);\n this.numberOfElements = -1;\n this.getElementValue = getElementValue;\n }\n\n enqueue(x) {\n this.priorityQueue[++this.numberOfElements] = x;\n this.swim(this.numberOfElements);\n }\n\n dequeue() {\n const max = this.priorityQueue[0];\n this.exchange(0, this.numberOfElements--);\n this.sink(0);\n this.priorityQueue[this.numberOfElements + 1] = null;\n return max;\n }\n\n empty() { return this.numberOfElements === -1; }\n\n size() { return this.numberOfElements + 1; }\n\n all() { return this.priorityQueue.slice(0, this.numberOfElements + 1); }\n\n max() { return this.priorityQueue[0]; }\n\n swim(k) {\n while (k > 0 && this.less(Math.floor(k / 2), k)) {\n this.exchange(k, Math.floor(k / 2));\n k = Math.floor(k / 2);\n }\n }\n\n sink(k) {\n while (2 * k <= this.numberOfElements) {\n let j = 2 * k;\n if (j < this.numberOfElements && this.less(j, j + 1)) j++;\n if (!this.less(k, j)) break;\n this.exchange(k, j);\n k = j;\n }\n }\n\n getValueAt(i) {\n // @ts-ignore getter is of unknown type\n return this.getElementValue(this.priorityQueue[i]);\n }\n\n less(i, j) {\n return this.getValueAt(i) < this.getValueAt(j);\n }\n\n exchange(i, j) {\n const t = this.priorityQueue[i];\n this.priorityQueue[i] = this.priorityQueue[j];\n this.priorityQueue[j] = t;\n }\n}\n\nexport function getOffsetPoint(y, x, keypoint: number, offsets) {\n return {\n y: offsets.get(y, x, keypoint),\n x: offsets.get(y, x, keypoint + count),\n };\n}\n\nexport function getImageCoords(part, outputStride: number, offsets) {\n const { heatmapY, heatmapX, id: keypoint } = part;\n const { y, x } = getOffsetPoint(heatmapY, heatmapX, keypoint, offsets);\n return {\n x: part.heatmapX * outputStride + x,\n y: part.heatmapY * outputStride + y,\n };\n}\n\nexport function fillArray(element, size) {\n const result = new Array(size);\n for (let i = 0; i < size; i++) {\n result[i] = element;\n }\n return result;\n}\n\nexport function clamp(a, min, max) {\n if (a < min) return min;\n if (a > max) return max;\n return a;\n}\n\nexport function squaredDistance(y1, x1, y2, x2) {\n const dy = y2 - y1;\n const dx = x2 - x1;\n return dy * dy + dx * dx;\n}\n\nexport function addVectors(a: { x: number, y: number }, b: { x: number, y: number }) {\n return { x: a.x + b.x, y: a.y + b.y };\n}\n\nexport function clampVector(a, min, max) {\n return { y: clamp(a.y, min, max), x: clamp(a.x, min, max) };\n}\n", "/**\n * PoseNet body detection model implementation\n *\n * Based on: [**PoseNet**](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5)\n */\n\nimport { log } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport type { BodyResult, BodyLandmark, Box } from '../result';\nimport type { Tensor, GraphModel } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\nimport * as utils from './posenetutils';\n\nlet model: GraphModel;\nconst poseNetOutputs = ['MobilenetV1/offset_2/BiasAdd'/* offsets */, 'MobilenetV1/heatmap_2/BiasAdd'/* heatmapScores */, 'MobilenetV1/displacement_fwd_2/BiasAdd'/* displacementFwd */, 'MobilenetV1/displacement_bwd_2/BiasAdd'/* displacementBwd */];\nconst localMaximumRadius = 1;\nconst outputStride = 16;\nconst squaredNmsRadius = 50 ** 2;\n\nfunction traverse(edgeId: number, sourceKeypoint, targetId, scores, offsets, displacements, offsetRefineStep = 2) {\n const getDisplacement = (point) => ({\n y: displacements.get(point.y, point.x, edgeId),\n x: displacements.get(point.y, point.x, (displacements.shape[2] / 2) + edgeId),\n });\n const getStridedIndexNearPoint = (point, height, width) => ({\n y: utils.clamp(Math.round(point.y / outputStride), 0, height - 1),\n x: utils.clamp(Math.round(point.x / outputStride), 0, width - 1),\n });\n\n const [height, width] = scores.shape;\n // Nearest neighbor interpolation for the source->target displacements.\n const sourceKeypointIndices = getStridedIndexNearPoint(sourceKeypoint.position, height, width);\n const displacement = getDisplacement(sourceKeypointIndices);\n const displacedPoint = utils.addVectors(sourceKeypoint.position, displacement);\n let targetKeypoint = displacedPoint;\n for (let i = 0; i < offsetRefineStep; i++) {\n const targetKeypointIndices = getStridedIndexNearPoint(targetKeypoint, height, width);\n const offsetPoint = utils.getOffsetPoint(targetKeypointIndices.y, targetKeypointIndices.x, targetId, offsets);\n targetKeypoint = utils.addVectors(\n { x: targetKeypointIndices.x * outputStride, y: targetKeypointIndices.y * outputStride },\n { x: offsetPoint.x, y: offsetPoint.y },\n );\n }\n const targetKeyPointIndices = getStridedIndexNearPoint(targetKeypoint, height, width);\n const score = scores.get(targetKeyPointIndices.y, targetKeyPointIndices.x, targetId);\n return { position: targetKeypoint, part: utils.partNames[targetId], score };\n}\n\nexport function decodePose(root, scores, offsets, displacementsFwd, displacementsBwd) {\n const tuples = utils.poseChain.map(([parentJoinName, childJoinName]) => ([utils.partIds[parentJoinName], utils.partIds[childJoinName]]));\n const edgesFwd = tuples.map(([, childJointId]) => childJointId);\n const edgesBwd = tuples.map(([parentJointId]) => parentJointId);\n const numParts = scores.shape[2]; // [21,21,17]\n const numEdges = edgesFwd.length;\n const keypoints = new Array(numParts);\n // Start a new detection instance at the position of the root.\n const rootPoint = utils.getImageCoords(root.part, outputStride, offsets);\n keypoints[root.part.id] = {\n score: root.score,\n part: utils.partNames[root.part.id] as BodyLandmark,\n position: rootPoint,\n };\n // Decode the part positions upwards in the tree, following the backward displacements.\n for (let edge = numEdges - 1; edge >= 0; --edge) {\n const sourceId = edgesFwd[edge];\n const targetId = edgesBwd[edge];\n if (keypoints[sourceId] && !keypoints[targetId]) {\n keypoints[targetId] = traverse(edge, keypoints[sourceId], targetId, scores, offsets, displacementsBwd);\n }\n }\n // Decode the part positions downwards in the tree, following the forward displacements.\n for (let edge = 0; edge < numEdges; ++edge) {\n const sourceId = edgesBwd[edge];\n const targetId = edgesFwd[edge];\n if (keypoints[sourceId] && !keypoints[targetId]) {\n keypoints[targetId] = traverse(edge, keypoints[sourceId], targetId, scores, offsets, displacementsFwd);\n }\n }\n return keypoints;\n}\n\nfunction scoreIsMaximumInLocalWindow(keypointId, score: number, heatmapY: number, heatmapX: number, scores) {\n const [height, width]: [number, number] = scores.shape;\n let localMaximum = true;\n const yStart = Math.max(heatmapY - localMaximumRadius, 0);\n const yEnd = Math.min(heatmapY + localMaximumRadius + 1, height);\n for (let yCurrent = yStart; yCurrent < yEnd; ++yCurrent) {\n const xStart = Math.max(heatmapX - localMaximumRadius, 0);\n const xEnd = Math.min(heatmapX + localMaximumRadius + 1, width);\n for (let xCurrent = xStart; xCurrent < xEnd; ++xCurrent) {\n if (scores.get(yCurrent, xCurrent, keypointId) > score) {\n localMaximum = false;\n break;\n }\n }\n if (!localMaximum) break;\n }\n return localMaximum;\n}\n\nexport function buildPartWithScoreQueue(minConfidence, scores) {\n const [height, width, numKeypoints] = scores.shape;\n const queue = new utils.MaxHeap(height * width * numKeypoints, ({ score }) => score);\n for (let heatmapY = 0; heatmapY < height; ++heatmapY) {\n for (let heatmapX = 0; heatmapX < width; ++heatmapX) {\n for (let keypointId = 0; keypointId < numKeypoints; ++keypointId) {\n const score = scores.get(heatmapY, heatmapX, keypointId);\n // Only consider parts with score greater or equal to threshold as root candidates.\n if (score < minConfidence) continue;\n // Only consider keypoints whose score is maximum in a local window.\n if (scoreIsMaximumInLocalWindow(keypointId, score, heatmapY, heatmapX, scores)) queue.enqueue({ score, part: { heatmapY, heatmapX, id: keypointId } });\n }\n }\n }\n return queue;\n}\n\nfunction withinRadius(poses, { x, y }, keypointId) {\n return poses.some(({ keypoints }) => {\n const correspondingKeypoint = keypoints[keypointId]?.position;\n if (!correspondingKeypoint) return false;\n return utils.squaredDistance(y, x, correspondingKeypoint.y, correspondingKeypoint.x) <= squaredNmsRadius;\n });\n}\n\nfunction getInstanceScore(existingPoses, keypoints) {\n const notOverlappedKeypointScores = keypoints.reduce((result, { position, score }, keypointId) => {\n if (!withinRadius(existingPoses, position, keypointId)) result += score;\n return result;\n }, 0.0);\n return notOverlappedKeypointScores / keypoints.length;\n}\n\nexport function decode(offsets, scores, displacementsFwd, displacementsBwd, maxDetected, minConfidence) {\n const poses: { keypoints, box: Box, score: number }[] = [];\n const queue = buildPartWithScoreQueue(minConfidence, scores);\n // Generate at most maxDetected object instances per image in decreasing root part score order.\n while (poses.length < maxDetected && !queue.empty()) {\n // The top element in the queue is the next root candidate.\n const root = queue.dequeue();\n // Part-based non-maximum suppression: We reject a root candidate if it is within a disk of `nmsRadius` pixels from the corresponding part of a previously detected instance.\n // @ts-ignore this one is tree walk\n const rootImageCoords = utils.getImageCoords(root.part, outputStride, offsets);\n // @ts-ignore this one is tree walk\n if (withinRadius(poses, rootImageCoords, root.part.id)) continue;\n // Else start a new detection instance at the position of the root.\n let keypoints = decodePose(root, scores, offsets, displacementsFwd, displacementsBwd);\n keypoints = keypoints.filter((a) => a.score > minConfidence);\n const score = getInstanceScore(poses, keypoints);\n const box = utils.getBoundingBox(keypoints);\n if (score > minConfidence) poses.push({ keypoints, box, score: Math.round(100 * score) / 100 });\n }\n return poses;\n}\n\nexport async function predict(input: Tensor, config: Config): Promise {\n /** posenet is mostly obsolete\n * caching is not implemented\n */\n if (!model?.['executor']) return [];\n const res = tf.tidy(() => {\n if (!model.inputs[0].shape) return [];\n const resized = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n const normalized = tf.sub(tf.div(tf.cast(resized, 'float32'), 127.5), 1.0);\n const results: Tensor[] = model.execute(normalized, poseNetOutputs) as Tensor[];\n const results3d = results.map((y) => tf.squeeze(y, [0]));\n results3d[1] = tf.sigmoid(results3d[1]); // apply sigmoid on scores\n return results3d;\n });\n\n const buffers = await Promise.all(res.map((tensor: Tensor) => tensor.buffer()));\n for (const t of res) tf.dispose(t);\n\n const decoded = decode(buffers[0], buffers[1], buffers[2], buffers[3], config.body.maxDetected, config.body.minConfidence);\n if (!model.inputs[0].shape) return [];\n const scaled = utils.scalePoses(decoded, [input.shape[1], input.shape[2]], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);\n return scaled;\n}\n\nexport async function load(config: Config): Promise {\n if (!model || env.initial) model = await loadModel(config.body.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n", "/**\n * Image segmentation for body detection model\n *\n * Based on:\n * - [**MediaPipe Meet**](https://drive.google.com/file/d/1lnP1bRi9CSqQQXUHa13159vLELYDgDu0/preview)\n * - [**MediaPipe Selfie**](https://drive.google.com/file/d/1dCfozqknMa068vVsO2j_1FgZkW_e3VWv/preview)\n */\n\nimport { log } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { loadModel } from '../tfjs/load';\nimport * as image from '../image/image';\nimport { constants } from '../tfjs/constants';\nimport type { GraphModel, Tensor } from '../tfjs/types';\nimport type { Config } from '../config';\nimport { env } from '../util/env';\nimport type { Input, AnyCanvas } from '../exports';\n\nlet model: GraphModel;\nlet busy = false;\n\nexport async function load(config: Config): Promise {\n if (!model || env.initial) model = await loadModel(config.segmentation.modelPath);\n else if (config.debug) log('cached model:', model['modelUrl']);\n return model;\n}\n\nexport async function process(input: Input, background: Input | undefined, config: Config)\n: Promise<{ data: number[] | Tensor, canvas: AnyCanvas | null, alpha: AnyCanvas | null }> {\n if (busy) return { data: [], canvas: null, alpha: null };\n busy = true;\n if (!model) await load(config);\n const inputImage = await image.process(input, config);\n const width = inputImage.tensor?.shape[2] || 0;\n const height = inputImage.tensor?.shape[1] || 0;\n if (!inputImage.tensor) return { data: [], canvas: null, alpha: null };\n const t: Record = {};\n\n t.resize = tf.image.resizeBilinear(inputImage.tensor, [model.inputs[0].shape ? model.inputs[0].shape[1] : 0, model.inputs[0].shape ? model.inputs[0].shape[2] : 0], false);\n tf.dispose(inputImage.tensor);\n t.norm = tf.div(t.resize, constants.tf255);\n t.res = model.execute(t.norm) as Tensor;\n\n t.squeeze = tf.squeeze(t.res, 0); // meet.shape:[1,256,256,1], selfie.shape:[1,144,256,2]\n if (t.squeeze.shape[2] === 2) {\n t.softmax = tf.softmax(t.squeeze); // model meet has two channels for fg and bg\n [t.bg, t.fg] = tf.unstack(t.softmax, 2);\n t.expand = tf.expandDims(t.fg, 2);\n t.pad = tf.expandDims(t.expand, 0);\n t.crop = tf.image.cropAndResize(t.pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);\n // running sofmax before unstack creates 2x2 matrix so we only take upper-left quadrant\n // otherwise run softmax after unstack and use standard resize\n // resizeOutput = tf.image.resizeBilinear(expand, [input.tensor?.shape[1], input.tensor?.shape[2]]);\n t.data = tf.squeeze(t.crop, 0);\n } else {\n t.data = tf.image.resizeBilinear(t.squeeze, [height, width]); // model selfie has a single channel that we can use directly\n }\n const data = Array.from(await t.data.data());\n\n if (env.node && !env.Canvas && (typeof ImageData === 'undefined')) {\n if (config.debug) log('canvas support missing');\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n return { data, canvas: null, alpha: null }; // running in nodejs so return alpha array as-is\n }\n\n const alphaCanvas = image.canvas(width, height);\n if (tf.browser) await tf.browser.toPixels(t.data, alphaCanvas);\n const alphaCtx = alphaCanvas.getContext('2d') as CanvasRenderingContext2D;\n if (config.segmentation.blur && config.segmentation.blur > 0) alphaCtx.filter = `blur(${config.segmentation.blur}px)`; // use css filter for bluring, can be done with gaussian blur manually instead\n const alphaData = alphaCtx.getImageData(0, 0, width, height);\n\n const compositeCanvas = image.canvas(width, height);\n const compositeCtx = compositeCanvas.getContext('2d') as CanvasRenderingContext2D;\n if (inputImage.canvas) compositeCtx.drawImage(inputImage.canvas, 0, 0);\n compositeCtx.globalCompositeOperation = 'darken'; // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation // best options are: darken, color-burn, multiply\n if (config.segmentation.blur && config.segmentation.blur > 0) compositeCtx.filter = `blur(${config.segmentation.blur}px)`; // use css filter for bluring, can be done with gaussian blur manually instead\n compositeCtx.drawImage(alphaCanvas, 0, 0);\n compositeCtx.globalCompositeOperation = 'source-over'; // reset composite operation\n compositeCtx.filter = 'none'; // reset css filter\n const compositeData = compositeCtx.getImageData(0, 0, width, height);\n for (let i = 0; i < width * height; i++) compositeData.data[4 * i + 3] = alphaData.data[4 * i + 0]; // copy original alpha value to new composite canvas\n compositeCtx.putImageData(compositeData, 0, 0);\n\n let mergedCanvas: AnyCanvas | null = null;\n if (background && compositeCanvas) { // draw background with segmentation as overlay if background is present\n mergedCanvas = image.canvas(width, height);\n const bgImage = await image.process(background, config);\n tf.dispose(bgImage.tensor);\n const ctxMerge = mergedCanvas.getContext('2d') as CanvasRenderingContext2D;\n ctxMerge.drawImage(bgImage.canvas as HTMLCanvasElement, 0, 0, mergedCanvas.width, mergedCanvas.height);\n ctxMerge.drawImage(compositeCanvas, 0, 0);\n }\n\n Object.keys(t).forEach((tensor) => tf.dispose(t[tensor]));\n busy = false;\n // return { data, canvas: mergedCanvas || compositeCanvas, alpha: alphaCanvas };\n return { data, canvas: compositeCanvas, alpha: alphaCanvas };\n}\n", "import { log, join } from '../util/util';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport type { GraphModel } from './types';\nimport type { Config } from '../config';\nimport * as modelsDefs from '../../models/models.json';\nimport { validateModel } from '../models';\n\nconst options = {\n cacheModels: true,\n cacheSupported: true,\n verbose: true,\n debug: false,\n modelBasePath: '',\n};\n\nexport interface ModelInfo {\n name: string,\n inCache: boolean,\n sizeDesired: number,\n sizeFromManifest: number,\n sizeLoadedWeights: number,\n}\n\nexport const modelStats: Record = {};\n\nasync function httpHandler(url: string, init?: RequestInit): Promise {\n if (options.debug) log('load model fetch:', url, init);\n return fetch(url, init);\n}\n\nexport function setModelLoadOptions(config: Config) {\n options.cacheModels = config.cacheModels;\n options.verbose = config.debug;\n options.modelBasePath = config.modelBasePath;\n}\n\nexport async function loadModel(modelPath: string | undefined): Promise {\n let modelUrl = join(options.modelBasePath, modelPath || '');\n if (!modelUrl.toLowerCase().endsWith('.json')) modelUrl += '.json';\n const modelPathSegments = modelUrl.includes('/') ? modelUrl.split('/') : modelUrl.split('\\\\');\n const shortModelName = modelPathSegments[modelPathSegments.length - 1].replace('.json', '');\n const cachedModelName = 'indexeddb://' + shortModelName; // generate short model name for cache\n modelStats[shortModelName] = {\n name: shortModelName,\n sizeFromManifest: 0,\n sizeLoadedWeights: 0,\n sizeDesired: modelsDefs[shortModelName],\n inCache: false,\n };\n options.cacheSupported = (typeof window !== 'undefined') && (typeof window.localStorage !== 'undefined') && (typeof window.indexedDB !== 'undefined'); // check if running in browser and if indexedb is available\n let cachedModels = {};\n try {\n cachedModels = (options.cacheSupported && options.cacheModels) ? await tf.io.listModels() : {}; // list all models already in cache // this fails for webview although localStorage is defined\n } catch {\n options.cacheSupported = false;\n }\n modelStats[shortModelName].inCache = (options.cacheSupported && options.cacheModels) && Object.keys(cachedModels).includes(cachedModelName); // is model found in cache\n const tfLoadOptions = typeof fetch === 'undefined' ? {} : { fetchFunc: (url: string, init?: RequestInit) => httpHandler(url, init) };\n const model: GraphModel = new tf.GraphModel(modelStats[shortModelName].inCache ? cachedModelName : modelUrl, tfLoadOptions) as unknown as GraphModel; // create model prototype and decide if load from cache or from original modelurl\n let loaded = false;\n try {\n // @ts-ignore private function\n model.findIOHandler(); // decide how to actually load a model\n if (options.debug) log('model load handler:', model['handler']);\n // @ts-ignore private property\n const artifacts = await model.handler.load(); // load manifest\n modelStats[shortModelName].sizeFromManifest = artifacts?.weightData?.byteLength || 0;\n model.loadSync(artifacts); // load weights\n // @ts-ignore private property\n modelStats[shortModelName].sizeLoadedWeights = model.artifacts?.weightData?.byteLength || 0;\n if (options.verbose) log('load model:', model['modelUrl'], { bytes: modelStats[shortModelName].sizeLoadedWeights }, options);\n loaded = true;\n } catch (err) {\n log('error loading model:', modelUrl, err);\n }\n if (loaded && options.cacheModels && options.cacheSupported && !modelStats[shortModelName].inCache) { // save model to cache\n try {\n const saveResult = await model.save(cachedModelName);\n log('model saved:', cachedModelName, saveResult);\n } catch (err) {\n log('error saving model:', modelUrl, err);\n }\n }\n validateModel(null, model, `${modelPath || ''}`);\n return model;\n}\n", "/**\n * Module that implements helper draw functions, exposed as human.draw\n */\n\nimport { mergeDeep, now } from '../util/util';\nimport { env } from '../util/env';\nimport { getCanvasContext, rect } from './primitives';\nimport { options } from './options';\nimport { face } from './face';\nimport { body } from './body';\nimport { hand } from './hand';\nimport { object } from './object';\nimport { gesture } from './gesture';\nimport type { Result, PersonResult } from '../result';\nimport type { AnyCanvas, DrawOptions } from '../exports';\n\nlet drawTime = 0;\n\nexport { options } from './options';\nexport { face } from './face';\nexport { body } from './body';\nexport { hand } from './hand';\nexport { object } from './object';\nexport { gesture } from './gesture';\n\n/** draw combined person results instead of individual detection result objects */\nexport function person(inCanvas: AnyCanvas, result: PersonResult[], drawOptions?: Partial) {\n const localOptions: DrawOptions = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.lineJoin = 'round';\n ctx.font = localOptions.font;\n\n for (let i = 0; i < result.length; i++) {\n if (localOptions.drawBoxes) {\n ctx.strokeStyle = localOptions.color;\n ctx.fillStyle = localOptions.color;\n rect(ctx, result[i].box[0], result[i].box[1], result[i].box[2], result[i].box[3], localOptions);\n if (localOptions.drawLabels) {\n const label = `person #${i}`;\n if (localOptions.shadowColor && localOptions.shadowColor !== '') {\n ctx.fillStyle = localOptions.shadowColor;\n ctx.fillText(label, result[i].box[0] + 3, 1 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);\n }\n ctx.fillStyle = localOptions.labelColor;\n ctx.fillText(label, result[i].box[0] + 2, 0 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);\n }\n ctx.stroke();\n }\n }\n}\n\n/** draw processed canvas */\nexport function canvas(input: AnyCanvas | HTMLImageElement | HTMLVideoElement, output: AnyCanvas) {\n if (!input || !output) return;\n const ctx = getCanvasContext(output);\n if (!ctx) return;\n ctx.drawImage(input, 0, 0);\n}\n\n/** meta-function that performs draw for: canvas, face, body, hand */\nexport async function all(inCanvas: AnyCanvas, result: Result, drawOptions?: Partial) {\n if (!result?.performance || !inCanvas) return null;\n const timeStamp = now();\n const localOptions = mergeDeep(options, drawOptions);\n const promise = Promise.all([\n face(inCanvas, result.face, localOptions),\n body(inCanvas, result.body, localOptions),\n hand(inCanvas, result.hand, localOptions),\n object(inCanvas, result.object, localOptions),\n gesture(inCanvas, result.gesture, localOptions), // gestures do not have buffering\n // person(inCanvas, result.persons, localOptions); // already included above\n ]);\n drawTime = env.perfadd ? drawTime + Math.round(now() - timeStamp) : Math.round(now() - timeStamp);\n result.performance.draw = drawTime;\n return promise;\n}\n", "import { log } from '../util/util';\nimport type { AnyCanvas } from '../exports';\nimport type { Point } from '../result';\nimport type { DrawOptions } from './options';\n\nexport const getCanvasContext = (input: AnyCanvas) => {\n if (!input) log('draw error: invalid canvas');\n else if (!input.getContext) log('draw error: canvas context not defined');\n else {\n const ctx = input.getContext('2d');\n if (!ctx) log('draw error: cannot get canvas context');\n else return ctx;\n }\n return null;\n};\n\nexport const rad2deg = (theta: number) => Math.round((theta * 180) / Math.PI);\n\nexport const colorDepth = (z: number | undefined, opt: DrawOptions): string => { // performance optimization needed\n if (!opt.useDepth || typeof z === 'undefined') return opt.color;\n const rgb = Uint8ClampedArray.from([127 + (2 * z), 127 - (2 * z), 255]);\n return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${opt.alpha})`;\n};\n\nexport function point(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, x: number, y: number, z: number | undefined, localOptions: DrawOptions) {\n ctx.fillStyle = colorDepth(z, localOptions);\n ctx.beginPath();\n ctx.arc(x, y, localOptions.pointSize, 0, 2 * Math.PI);\n ctx.fill();\n}\n\nexport function rect(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, x: number, y: number, width: number, height: number, localOptions: DrawOptions) {\n ctx.beginPath();\n ctx.lineWidth = localOptions.lineWidth;\n if (localOptions.useCurves) {\n const cx = (x + x + width) / 2;\n const cy = (y + y + height) / 2;\n ctx.ellipse(cx, cy, width / 2, height / 2, 0, 0, 2 * Math.PI);\n } else {\n ctx.moveTo(x + localOptions.roundRect, y);\n ctx.lineTo(x + width - localOptions.roundRect, y);\n ctx.quadraticCurveTo(x + width, y, x + width, y + localOptions.roundRect);\n ctx.lineTo(x + width, y + height - localOptions.roundRect);\n ctx.quadraticCurveTo(x + width, y + height, x + width - localOptions.roundRect, y + height);\n ctx.lineTo(x + localOptions.roundRect, y + height);\n ctx.quadraticCurveTo(x, y + height, x, y + height - localOptions.roundRect);\n ctx.lineTo(x, y + localOptions.roundRect);\n ctx.quadraticCurveTo(x, y, x + localOptions.roundRect, y);\n ctx.closePath();\n }\n ctx.stroke();\n}\n\nexport function lines(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, points: Point[], localOptions: DrawOptions) {\n if (points.length < 2) return;\n ctx.beginPath();\n ctx.moveTo(points[0][0], points[0][1]);\n for (const pt of points) {\n ctx.strokeStyle = colorDepth(pt[2] || 0, localOptions);\n ctx.lineTo(Math.trunc(pt[0]), Math.trunc(pt[1]));\n }\n ctx.stroke();\n if (localOptions.fillPolygons) {\n ctx.closePath();\n ctx.fill();\n }\n}\n\nexport function curves(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, points: Point[], localOptions: DrawOptions) {\n if (points.length < 2) return;\n ctx.lineWidth = localOptions.lineWidth;\n if (!localOptions.useCurves || points.length <= 2) {\n lines(ctx, points, localOptions);\n return;\n }\n ctx.moveTo(points[0][0], points[0][1]);\n for (let i = 0; i < points.length - 2; i++) {\n const xc = (points[i][0] + points[i + 1][0]) / 2;\n const yc = (points[i][1] + points[i + 1][1]) / 2;\n ctx.quadraticCurveTo(points[i][0], points[i][1], xc, yc);\n }\n ctx.quadraticCurveTo(points[points.length - 2][0], points[points.length - 2][1], points[points.length - 1][0], points[points.length - 1][1]);\n ctx.stroke();\n if (localOptions.fillPolygons) {\n ctx.closePath();\n ctx.fill();\n }\n}\n\nexport function arrow(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, from: Point, to: Point, radius = 5) {\n let angle;\n let x;\n let y;\n ctx.beginPath();\n ctx.moveTo(from[0], from[1]);\n ctx.lineTo(to[0], to[1]);\n angle = Math.atan2(to[1] - from[1], to[0] - from[0]);\n x = radius * Math.cos(angle) + to[0];\n y = radius * Math.sin(angle) + to[1];\n ctx.moveTo(x, y);\n angle += (1.0 / 3.0) * (2 * Math.PI);\n x = radius * Math.cos(angle) + to[0];\n y = radius * Math.sin(angle) + to[1];\n ctx.lineTo(x, y);\n angle += (1.0 / 3.0) * (2 * Math.PI);\n x = radius * Math.cos(angle) + to[0];\n y = radius * Math.sin(angle) + to[1];\n ctx.lineTo(x, y);\n ctx.closePath();\n ctx.stroke();\n ctx.fill();\n}\n", "/** Draw Options\n * - Accessed via `human.draw.options` or provided per each draw method as the drawOptions optional parameter\n */\nexport interface DrawOptions {\n /** draw line color */\n color: string,\n /** alpha value used for lines */\n alpha: number,\n /** label color */\n labelColor: string,\n /** label shadow color */\n shadowColor: string,\n /** label font */\n font: string,\n /** line spacing between labels */\n lineHeight: number,\n /** line width for drawn lines */\n lineWidth: number,\n /** size of drawn points */\n pointSize: number,\n /** draw rounded boxes by n pixels */\n roundRect: number,\n /** should points be drawn? */\n drawPoints: boolean,\n /** should labels be drawn? */\n drawLabels: boolean,\n /** should face attention keypoints be highlighted */\n drawAttention: boolean;\n /** should detected gestures be drawn? */\n drawGestures: boolean,\n /** should draw boxes around detection results? */\n drawBoxes: boolean,\n /** should draw polygons from detection points? */\n drawPolygons: boolean,\n /** should draw gaze arrows? */\n drawGaze: boolean,\n /** should fill polygons? */\n fillPolygons: boolean,\n /** use z-coordinate when available */\n useDepth: boolean,\n /** should lines be curved? */\n useCurves: boolean,\n}\n\n/** currently set draw options {@link DrawOptions} */\nexport const options: DrawOptions = {\n color: 'rgba(173, 216, 230, 0.6)' as string, // 'lightblue' with light alpha channel\n labelColor: 'rgba(173, 216, 230, 1)' as string, // 'lightblue' with dark alpha channel\n shadowColor: 'black' as string,\n alpha: 0.5 as number,\n font: 'small-caps 16px \"Segoe UI\"' as string,\n lineHeight: 18 as number,\n lineWidth: 4 as number,\n pointSize: 2 as number,\n roundRect: 8 as number,\n drawPoints: false as boolean,\n drawLabels: true as boolean,\n drawBoxes: true as boolean,\n drawAttention: true as boolean,\n drawGestures: true as boolean,\n drawPolygons: true as boolean,\n drawGaze: true as boolean,\n fillPolygons: false as boolean,\n useDepth: true as boolean,\n useCurves: false as boolean,\n};\n", "import { TRI468 as triangulation } from '../face/facemeshcoords';\nimport { mergeDeep } from '../util/util';\nimport { getCanvasContext, rad2deg, rect, point, lines, arrow } from './primitives';\nimport { options } from './options';\nimport * as facemeshConstants from '../face/constants';\nimport type { FaceResult } from '../result';\nimport type { AnyCanvas, DrawOptions } from '../exports';\n\nlet opt: DrawOptions;\n\nfunction drawLabels(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n if (opt.drawLabels) {\n // silly hack since fillText does not suport new line\n const labels:string[] = [];\n labels.push(`face: ${Math.trunc(100 * f.score)}%`);\n if (f.genderScore) labels.push(`${f.gender || ''} ${Math.trunc(100 * f.genderScore)}%`);\n if (f.age) labels.push(`age: ${f.age || ''}`);\n if (f.iris) labels.push(`distance: ${f.iris}`);\n if (f.real) labels.push(`real: ${Math.trunc(100 * f.real)}%`);\n if (f.live) labels.push(`live: ${Math.trunc(100 * f.live)}%`);\n if (f.emotion && f.emotion.length > 0) {\n const emotion = f.emotion.map((a) => `${Math.trunc(100 * a.score)}% ${a.emotion}`);\n if (emotion.length > 3) emotion.length = 3;\n labels.push(emotion.join(' '));\n }\n if (f.rotation?.angle && f.rotation?.gaze) {\n if (f.rotation.angle.roll) labels.push(`roll: ${rad2deg(f.rotation.angle.roll)}\u00B0 yaw:${rad2deg(f.rotation.angle.yaw)}\u00B0 pitch:${rad2deg(f.rotation.angle.pitch)}\u00B0`);\n if (f.rotation.gaze.bearing) labels.push(`gaze: ${rad2deg(f.rotation.gaze.bearing)}\u00B0`);\n }\n if (labels.length === 0) labels.push('face');\n ctx.fillStyle = opt.color;\n for (let i = labels.length - 1; i >= 0; i--) {\n const x = Math.max(f.box[0], 0);\n const y = i * opt.lineHeight + f.box[1];\n if (opt.shadowColor && opt.shadowColor !== '') {\n ctx.fillStyle = opt.shadowColor;\n ctx.fillText(labels[i], x + 5, y + 16);\n }\n ctx.fillStyle = opt.labelColor;\n ctx.fillText(labels[i], x + 4, y + 15);\n }\n }\n}\n\nfunction drawIrisElipse(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n // iris: array[center, left, top, right, bottom]\n if (f.annotations?.leftEyeIris && f.annotations?.leftEyeIris[0]) {\n ctx.strokeStyle = opt.useDepth ? 'rgba(255, 200, 255, 0.3)' : opt.color;\n ctx.beginPath();\n const sizeX = Math.abs(f.annotations.leftEyeIris[3][0] - f.annotations.leftEyeIris[1][0]) / 2;\n const sizeY = Math.abs(f.annotations.leftEyeIris[4][1] - f.annotations.leftEyeIris[2][1]) / 2;\n ctx.ellipse(f.annotations.leftEyeIris[0][0], f.annotations.leftEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);\n ctx.stroke();\n if (opt.fillPolygons) {\n ctx.fillStyle = opt.useDepth ? 'rgba(255, 255, 200, 0.3)' : opt.color;\n ctx.fill();\n }\n }\n if (f.annotations?.rightEyeIris && f.annotations?.rightEyeIris[0]) {\n ctx.strokeStyle = opt.useDepth ? 'rgba(255, 200, 255, 0.3)' : opt.color;\n ctx.beginPath();\n const sizeX = Math.abs(f.annotations.rightEyeIris[3][0] - f.annotations.rightEyeIris[1][0]) / 2;\n const sizeY = Math.abs(f.annotations.rightEyeIris[4][1] - f.annotations.rightEyeIris[2][1]) / 2;\n ctx.ellipse(f.annotations.rightEyeIris[0][0], f.annotations.rightEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);\n ctx.stroke();\n if (opt.fillPolygons) {\n ctx.fillStyle = opt.useDepth ? 'rgba(255, 255, 200, 0.3)' : opt.color;\n ctx.fill();\n }\n }\n}\n\nfunction drawGazeSpheres(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n if (opt.drawGaze && f.rotation?.angle && typeof Path2D !== 'undefined') {\n ctx.strokeStyle = 'pink';\n const valX = (f.box[0] + f.box[2] / 2) - (f.box[3] * rad2deg(f.rotation.angle.yaw) / 90);\n const valY = (f.box[1] + f.box[3] / 2) + (f.box[2] * rad2deg(f.rotation.angle.pitch) / 90);\n const pathV = new Path2D(`\n M ${f.box[0] + f.box[2] / 2} ${f.box[1]}\n C\n ${valX} ${f.box[1]},\n ${valX} ${f.box[1] + f.box[3]},\n ${f.box[0] + f.box[2] / 2} ${f.box[1] + f.box[3]}\n `);\n const pathH = new Path2D(`\n M ${f.box[0]} ${f.box[1] + f.box[3] / 2}\n C \n ${f.box[0]} ${valY},\n ${f.box[0] + f.box[2]} ${valY},\n ${f.box[0] + f.box[2]} ${f.box[1] + f.box[3] / 2}\n `);\n ctx.stroke(pathH);\n ctx.stroke(pathV);\n }\n}\n\nfunction drawGazeArrows(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n if (opt.drawGaze && f.rotation?.gaze.strength && f.rotation.gaze.bearing && f.annotations.leftEyeIris && f.annotations.rightEyeIris && f.annotations.leftEyeIris[0] && f.annotations.rightEyeIris[0]) {\n ctx.strokeStyle = 'pink';\n ctx.fillStyle = 'pink';\n const leftGaze = [\n f.annotations.leftEyeIris[0][0] + (Math.sin(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[3]),\n f.annotations.leftEyeIris[0][1] + (Math.cos(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[2]),\n ];\n arrow(ctx, [f.annotations.leftEyeIris[0][0], f.annotations.leftEyeIris[0][1]], [leftGaze[0], leftGaze[1]], 4);\n const rightGaze = [\n f.annotations.rightEyeIris[0][0] + (Math.sin(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[3]),\n f.annotations.rightEyeIris[0][1] + (Math.cos(f.rotation.gaze.bearing) * f.rotation.gaze.strength * f.box[2]),\n ];\n arrow(ctx, [f.annotations.rightEyeIris[0][0], f.annotations.rightEyeIris[0][1]], [rightGaze[0], rightGaze[1]], 4);\n }\n}\n\nfunction drawFacePolygons(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n if (opt.drawPolygons && f.mesh.length >= 468) {\n ctx.lineWidth = 1;\n for (let i = 0; i < triangulation.length / 3; i++) {\n const points = [triangulation[i * 3 + 0], triangulation[i * 3 + 1], triangulation[i * 3 + 2]].map((index) => f.mesh[index]);\n lines(ctx, points, opt);\n }\n drawIrisElipse(f, ctx);\n }\n /*\n if (opt.drawPolygons && f.contours.length > 1) {\n ctx.lineWidth = 5;\n lines(ctx, f.contours, opt);\n }\n ctx.lineWidth = 1;\n */\n}\n\nfunction drawFacePoints(f: FaceResult, ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) {\n if (opt.drawPoints && f.mesh.length >= 468) {\n for (let i = 0; i < f.mesh.length; i++) {\n point(ctx, f.mesh[i][0], f.mesh[i][1], f.mesh[i][2], opt);\n if (opt.drawAttention) {\n if (facemeshConstants.LANDMARKS_REFINEMENT_LIPS_CONFIG.includes(i)) point(ctx, f.mesh[i][0], f.mesh[i][1], (f.mesh[i][2] as number) + 127, opt);\n if (facemeshConstants.LANDMARKS_REFINEMENT_LEFT_EYE_CONFIG.includes(i)) point(ctx, f.mesh[i][0], f.mesh[i][1], (f.mesh[i][2] as number) - 127, opt);\n if (facemeshConstants.LANDMARKS_REFINEMENT_RIGHT_EYE_CONFIG.includes(i)) point(ctx, f.mesh[i][0], f.mesh[i][1], (f.mesh[i][2] as number) - 127, opt);\n }\n }\n }\n}\n\nfunction drawFaceBoxes(f: FaceResult, ctx) {\n if (opt.drawBoxes) {\n rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3], opt);\n }\n}\n\n/** draw detected faces */\nexport function face(inCanvas: AnyCanvas, result: FaceResult[], drawOptions?: Partial) {\n opt = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.font = opt.font;\n ctx.strokeStyle = opt.color;\n ctx.fillStyle = opt.color;\n for (const f of result) {\n drawFaceBoxes(f, ctx);\n drawLabels(f, ctx);\n if (f.mesh && f.mesh.length > 0) {\n drawFacePoints(f, ctx);\n drawFacePolygons(f, ctx);\n drawGazeSpheres(f, ctx);\n drawGazeArrows(f, ctx);\n }\n }\n}\n", "import { mergeDeep } from '../util/util';\nimport { getCanvasContext, rect, point, curves, colorDepth } from './primitives';\nimport { options } from './options';\nimport type { BodyResult } from '../result';\nimport type { AnyCanvas, DrawOptions } from '../exports';\n\n/** draw detected bodies */\nexport function body(inCanvas: AnyCanvas, result: BodyResult[], drawOptions?: Partial) {\n const localOptions: DrawOptions = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.lineJoin = 'round';\n for (let i = 0; i < result.length; i++) {\n ctx.strokeStyle = localOptions.color;\n ctx.fillStyle = localOptions.color;\n ctx.lineWidth = localOptions.lineWidth;\n ctx.font = localOptions.font;\n if (localOptions.drawBoxes && result[i].box && result[i].box.length === 4) {\n rect(ctx, result[i].box[0], result[i].box[1], result[i].box[2], result[i].box[3], localOptions);\n if (localOptions.drawLabels) {\n if (localOptions.shadowColor && localOptions.shadowColor !== '') {\n ctx.fillStyle = localOptions.shadowColor;\n ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 3, 1 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);\n }\n ctx.fillStyle = localOptions.labelColor;\n ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 2, 0 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]);\n }\n }\n if (localOptions.drawPoints && result[i].keypoints) {\n for (let pt = 0; pt < result[i].keypoints.length; pt++) {\n if (!result[i].keypoints[pt].score || (result[i].keypoints[pt].score === 0)) continue;\n ctx.fillStyle = colorDepth(result[i].keypoints[pt].position[2], localOptions);\n point(ctx, result[i].keypoints[pt].position[0], result[i].keypoints[pt].position[1], 0, localOptions);\n }\n }\n if (localOptions.drawLabels && result[i].keypoints) {\n ctx.font = localOptions.font;\n for (const pt of result[i].keypoints) {\n if (!pt.score || (pt.score === 0)) continue;\n ctx.fillStyle = colorDepth(pt.position[2], localOptions);\n ctx.fillText(`${pt.part} ${Math.trunc(100 * pt.score)}%`, pt.position[0] + 4, pt.position[1] + 4);\n }\n }\n if (localOptions.drawPolygons && result[i].keypoints && result[i].annotations) {\n for (const part of Object.values(result[i].annotations)) {\n for (const connected of part) curves(ctx, connected, localOptions);\n }\n }\n }\n}\n", "import { mergeDeep } from '../util/util';\nimport { getCanvasContext, rect, point, colorDepth } from './primitives';\nimport { options } from './options';\nimport type { HandResult } from '../result';\nimport type { AnyCanvas, DrawOptions, Point } from '../exports';\n\n/** draw detected hands */\nexport function hand(inCanvas: AnyCanvas, result: HandResult[], drawOptions?: Partial) {\n const localOptions: DrawOptions = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.lineJoin = 'round';\n ctx.font = localOptions.font;\n for (const h of result) {\n if (localOptions.drawBoxes) {\n ctx.strokeStyle = localOptions.color;\n ctx.fillStyle = localOptions.color;\n rect(ctx, h.box[0], h.box[1], h.box[2], h.box[3], localOptions);\n if (localOptions.drawLabels) {\n if (localOptions.shadowColor && localOptions.shadowColor !== '') {\n ctx.fillStyle = localOptions.shadowColor;\n ctx.fillText(`hand:${Math.trunc(100 * h.score)}%`, h.box[0] + 3, 1 + h.box[1] + localOptions.lineHeight, h.box[2]); // can use h.label\n }\n ctx.fillStyle = localOptions.labelColor;\n ctx.fillText(`hand:${Math.trunc(100 * h.score)}%`, h.box[0] + 2, 0 + h.box[1] + localOptions.lineHeight, h.box[2]); // can use h.label\n }\n ctx.stroke();\n }\n if (localOptions.drawPoints) {\n if (h.keypoints && h.keypoints.length > 0) {\n for (const pt of h.keypoints) {\n ctx.fillStyle = colorDepth(pt[2], localOptions);\n point(ctx, pt[0], pt[1], 0, localOptions);\n }\n }\n }\n if (localOptions.drawLabels && h.annotations) {\n const addHandLabel = (part: Point[], title: string) => {\n if (!part || part.length === 0 || !part[0]) return;\n const z = part[part.length - 1][2] || -256;\n ctx.fillStyle = colorDepth(z, localOptions);\n ctx.fillText(title, part[part.length - 1][0] + 4, part[part.length - 1][1] + 4);\n };\n ctx.font = localOptions.font;\n addHandLabel(h.annotations.index, 'index');\n addHandLabel(h.annotations.middle, 'middle');\n addHandLabel(h.annotations.ring, 'ring');\n addHandLabel(h.annotations.pinky, 'pinky');\n addHandLabel(h.annotations.thumb, 'thumb');\n addHandLabel(h.annotations.palm, 'palm');\n }\n if (localOptions.drawPolygons && h.annotations) {\n const addHandLine = (part: Point[]) => {\n if (!part || part.length === 0 || !part[0]) return;\n for (let i = 0; i < part.length; i++) {\n ctx.beginPath();\n const z = part[i][2] || 0;\n ctx.strokeStyle = colorDepth(i * z, localOptions);\n ctx.moveTo(part[i > 0 ? i - 1 : 0][0], part[i > 0 ? i - 1 : 0][1]);\n ctx.lineTo(part[i][0], part[i][1]);\n ctx.stroke();\n }\n };\n ctx.lineWidth = localOptions.lineWidth;\n addHandLine(h.annotations.index);\n addHandLine(h.annotations.middle);\n addHandLine(h.annotations.ring);\n addHandLine(h.annotations.pinky);\n addHandLine(h.annotations.thumb);\n // addPart(h.annotations.palm);\n }\n }\n}\n", "import { mergeDeep } from '../util/util';\nimport { getCanvasContext, rect } from './primitives';\nimport { options } from './options';\nimport type { ObjectResult } from '../result';\nimport type { AnyCanvas, DrawOptions } from '../exports';\n\n/** draw detected objects */\nexport function object(inCanvas: AnyCanvas, result: ObjectResult[], drawOptions?: Partial) {\n const localOptions: DrawOptions = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.lineJoin = 'round';\n ctx.font = localOptions.font;\n for (const h of result) {\n if (localOptions.drawBoxes) {\n ctx.strokeStyle = localOptions.color;\n ctx.fillStyle = localOptions.color;\n rect(ctx, h.box[0], h.box[1], h.box[2], h.box[3], localOptions);\n if (localOptions.drawLabels) {\n const label = `${h.label} ${Math.round(100 * h.score)}%`;\n if (localOptions.shadowColor && localOptions.shadowColor !== '') {\n ctx.fillStyle = localOptions.shadowColor;\n ctx.fillText(label, h.box[0] + 3, 1 + h.box[1] + localOptions.lineHeight, h.box[2]);\n }\n ctx.fillStyle = localOptions.labelColor;\n ctx.fillText(label, h.box[0] + 2, 0 + h.box[1] + localOptions.lineHeight, h.box[2]);\n }\n ctx.stroke();\n }\n }\n}\n", "import { mergeDeep } from '../util/util';\nimport { getCanvasContext } from './primitives';\nimport { options } from './options';\nimport type { GestureResult } from '../result';\nimport type { AnyCanvas, DrawOptions } from '../exports';\n\n/** draw detected gestures */\nexport function gesture(inCanvas: AnyCanvas, result: GestureResult[], drawOptions?: Partial) {\n const localOptions: DrawOptions = mergeDeep(options, drawOptions);\n if (!result || !inCanvas) return;\n if (localOptions.drawGestures) {\n const ctx = getCanvasContext(inCanvas);\n if (!ctx) return;\n ctx.font = localOptions.font;\n ctx.fillStyle = localOptions.color;\n let i = 1;\n for (let j = 0; j < result.length; j++) {\n let where: unknown[] = []; // what&where is a record\n let what: unknown[] = []; // what&where is a record\n [where, what] = Object.entries(result[j]);\n if ((what.length > 1) && ((what[1] as string).length > 0)) {\n const who = where[1] as number > 0 ? `#${where[1]}` : '';\n const label = `${where[0]} ${who}: ${what[1]}`;\n if (localOptions.shadowColor && localOptions.shadowColor !== '') {\n ctx.fillStyle = localOptions.shadowColor;\n ctx.fillText(label, 8, 2 + (i * localOptions.lineHeight));\n }\n ctx.fillStyle = localOptions.labelColor;\n ctx.fillText(label, 6, 0 + (i * localOptions.lineHeight));\n i += 1;\n }\n }\n }\n}\n", "import type { Tensor } from '../tfjs/types';\nimport type { FaceResult } from '../result';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport { meshAnnotations } from './facemeshcoords';\n\nconst expandFact = 0.1;\nconst alpha = 0.5;\n\n// point inclusion in polygon based on https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html\nfunction insidePoly(x: number, y: number, polygon: { x: number, y: number }[]): boolean {\n let inside = false;\n let j = polygon.length - 1;\n for (let i = 0; i < polygon.length; j = i++) {\n if (((polygon[i].y > y) !== (polygon[j].y > y)) && (x < (polygon[j].x - polygon[i].x) * (y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x)) inside = !inside;\n }\n return inside;\n}\n\nexport async function mask(face: FaceResult): Promise {\n if (!face.tensor) return face.tensor;\n if (!face.mesh || face.mesh.length < 100) return face.tensor;\n const width = face.tensor.shape[2] || 0;\n const height = face.tensor.shape[1] || 0;\n const buffer = await face.tensor.buffer();\n let silhouette: { x: number, y: number }[] = [];\n for (const pt of meshAnnotations.silhouette) silhouette.push({ x: (face.mesh[pt][0] - face.box[0]) / face.box[2], y: (face.mesh[pt][1] - face.box[1]) / face.box[3] }); // add all silhouette points scaled to local box\n if (expandFact && expandFact > 0) silhouette = silhouette.map((pt) => ({ x: pt.x > 0.5 ? pt.x + expandFact : pt.x - expandFact, y: pt.y > 0.5 ? pt.y + expandFact : pt.y - expandFact })); // expand silhouette\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) {\n const inside = insidePoly(x / width, y / width, silhouette);\n if (!inside) {\n buffer.set(alpha * buffer.get(0, y, x, 0), 0, y, x, 0);\n buffer.set(alpha * buffer.get(0, y, x, 1), 0, y, x, 1);\n buffer.set(alpha * buffer.get(0, y, x, 2), 0, y, x, 2);\n }\n }\n }\n const output = buffer.toTensor();\n tf.dispose(buffer);\n return output;\n}\n", "import type { Point, FaceResult } from '../result';\n\ntype Vector = [number, number, number];\n\nconst calculateGaze = (face: FaceResult): { bearing: number, strength: number } => {\n const radians = (pt1: Point, pt2: Point) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]); // function to calculate angle between any two points\n if (!face.annotations.rightEyeIris || !face.annotations.leftEyeIris) return { bearing: 0, strength: 0 };\n\n const offsetIris = [0, -0.1]; // iris center may not align with average of eye extremes\n const eyeRatio = 1; // factor to normalize changes x vs y\n\n const left = (face.mesh[33][2] || 0) > (face.mesh[263][2] || 0); // pick left or right eye depending which one is closer bazed on outsize point z axis\n const irisCenter = left ? face.mesh[473] : face.mesh[468];\n const eyeCenter = left // eye center is average of extreme points on x axis for both x and y, ignoring y extreme points as eyelids naturally open/close more when gazing up/down so relative point is less precise\n ? [(face.mesh[133][0] + face.mesh[33][0]) / 2, (face.mesh[133][1] + face.mesh[33][1]) / 2]\n : [(face.mesh[263][0] + face.mesh[362][0]) / 2, (face.mesh[263][1] + face.mesh[362][1]) / 2];\n const eyeSize = left // eye size is difference between extreme points for both x and y, used to normalize & squarify eye dimensions\n ? [face.mesh[133][0] - face.mesh[33][0], face.mesh[23][1] - face.mesh[27][1]]\n : [face.mesh[263][0] - face.mesh[362][0], face.mesh[253][1] - face.mesh[257][1]];\n const eyeDiff: Point = [ // x distance between extreme point and center point normalized with eye size\n (eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],\n eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1],\n ];\n let strength = Math.sqrt((eyeDiff[0] * eyeDiff[0]) + (eyeDiff[1] * eyeDiff[1])); // vector length is a diagonal between two differences\n strength = Math.min(strength, face.boxRaw[2] / 2, face.boxRaw[3] / 2); // limit strength to half of box size to avoid clipping due to low precision\n const bearing = (radians([0, 0], eyeDiff) + (Math.PI / 2)) % Math.PI; // using eyeDiff instead eyeCenter/irisCenter combo due to manual adjustments and rotate clockwise 90degrees\n return { bearing, strength };\n};\n\nexport const calculateFaceAngle = (face: FaceResult, imageSize: [number, number]): {\n angle: { pitch: number, yaw: number, roll: number },\n matrix: [number, number, number, number, number, number, number, number, number],\n gaze: { bearing: number, strength: number },\n} => {\n // const degrees = (theta) => Math.abs(((theta * 180) / Math.PI) % 360);\n const normalize = (v: Vector): Vector => { // normalize vector\n const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);\n v[0] /= length;\n v[1] /= length;\n v[2] /= length;\n return v;\n };\n const subVectors = (a: Vector, b: Vector): Vector => { // vector subtraction (a - b)\n const x = a[0] - b[0];\n const y = a[1] - b[1];\n const z = a[2] - b[2];\n return [x, y, z];\n };\n const crossVectors = (a: Vector, b: Vector): Vector => { // vector cross product (a x b)\n const x = a[1] * b[2] - a[2] * b[1];\n const y = a[2] * b[0] - a[0] * b[2];\n const z = a[0] * b[1] - a[1] * b[0];\n return [x, y, z];\n };\n // 3x3 rotation matrix to Euler angles based on https://www.geometrictools.com/Documentation/EulerAngles.pdf\n const rotationMatrixToEulerAngle = (r: number[]): { pitch: number, yaw: number, roll: number } => {\n const [r00, _r01, _r02, r10, r11, r12, r20, r21, r22] = r; // eslint-disable-line @typescript-eslint/no-unused-vars\n let thetaX: number;\n let thetaY: number;\n let thetaZ: number;\n if (r10 < 1) { // YZX calculation\n if (r10 > -1) {\n thetaZ = Math.asin(r10);\n thetaY = Math.atan2(-r20, r00);\n thetaX = Math.atan2(-r12, r11);\n } else {\n thetaZ = -Math.PI / 2;\n thetaY = -Math.atan2(r21, r22);\n thetaX = 0;\n }\n } else {\n thetaZ = Math.PI / 2;\n thetaY = Math.atan2(r21, r22);\n thetaX = 0;\n }\n if (Number.isNaN(thetaX)) thetaX = 0;\n if (Number.isNaN(thetaY)) thetaY = 0;\n if (Number.isNaN(thetaZ)) thetaZ = 0;\n return { pitch: 2 * -thetaX, yaw: 2 * -thetaY, roll: 2 * -thetaZ };\n };\n\n /*\n const meshToEulerAngle = (mesh) => { // simple Euler angle calculation based existing 3D mesh\n const radians = (a1, a2, b1, b2) => Math.atan2(b2 - a2, b1 - a1);\n return { // values are in radians in range of -pi/2 to pi/2 which is -90 to +90 degrees, value of 0 means center\n pitch: radians(mesh[10][1], mesh[10][2], mesh[152][1], mesh[152][2]), // looking at y,z of top and bottom points of the face // pitch is face move up/down\n yaw: radians(mesh[33][0], mesh[33][2], mesh[263][0], mesh[263][2]), // looking at x,z of outside corners of leftEye and rightEye // yaw is face turn left/right\n roll: radians(mesh[33][0], mesh[33][1], mesh[263][0], mesh[263][1]), // looking at x,y of outside corners of leftEye and rightEye // roll is face lean left/right\n };\n };\n */\n\n // initialize gaze and mesh\n const mesh = face.meshRaw;\n if (!mesh || mesh.length < 300) return { angle: { pitch: 0, yaw: 0, roll: 0 }, matrix: [1, 0, 0, 0, 1, 0, 0, 0, 1], gaze: { bearing: 0, strength: 0 } };\n\n const size = Math.max(face.boxRaw[2] * imageSize[0], face.boxRaw[3] * imageSize[1]) / 1.5;\n // top, bottom, left, right\n const pts: Point[] = [mesh[10], mesh[152], mesh[234], mesh[454]].map((pt) => [pt[0] * imageSize[0] / size, pt[1] * imageSize[1] / size, pt[2]] as Point); // make the xyz coordinates proportional, independent of the image/box size\n\n const yAxis = normalize(subVectors(pts[1] as Vector, pts[0] as Vector));\n let xAxis = normalize(subVectors(pts[3] as Vector, pts[2] as Vector));\n const zAxis = normalize(crossVectors(xAxis, yAxis));\n // adjust xAxis to make sure that all axes are perpendicular to each other\n xAxis = crossVectors(yAxis, zAxis);\n\n // Rotation Matrix from Axis Vectors - http://renderdan.blogspot.com/2006/05/rotation-matrix-from-axis-vectors.html\n // 3x3 rotation matrix is flatten to array in row-major order. Note that the rotation represented by this matrix is inverted.\n const matrix: [number, number, number, number, number, number, number, number, number] = [\n xAxis[0], xAxis[1], xAxis[2],\n yAxis[0], yAxis[1], yAxis[2],\n zAxis[0], zAxis[1], zAxis[2],\n ];\n const angle = rotationMatrixToEulerAngle(matrix);\n // const angle = meshToEulerAngle(mesh);\n\n // we have iris keypoints so we can calculate gaze direction\n const gaze = mesh.length === 478 ? calculateGaze(face) : { bearing: 0, strength: 0 };\n\n return { angle, matrix, gaze };\n};\n", "/**\n * Face algorithm implementation\n * Uses FaceMesh, Emotion and FaceRes models to create a unified pipeline\n */\n\nimport { log, now } from '../util/util';\nimport { env } from '../util/env';\nimport * as tf from '../../dist/tfjs.esm.js';\nimport * as facemesh from './facemesh';\nimport * as emotion from '../gear/emotion';\nimport * as faceres from './faceres';\nimport * as mask from './mask';\nimport * as antispoof from './antispoof';\nimport * as liveness from './liveness';\nimport * as gear from '../gear/gear';\nimport * as ssrnetAge from '../gear/ssrnet-age';\nimport * as ssrnetGender from '../gear/ssrnet-gender';\nimport * as mobilefacenet from './mobilefacenet';\nimport * as insightface from './insightface';\nimport type { FaceResult, Emotion, Gender, Race } from '../result';\nimport type { Tensor } from '../tfjs/types';\nimport type { Human } from '../human';\nimport { calculateFaceAngle } from './angles';\n\ninterface DescRes { age: number, gender: Gender, genderScore: number, descriptor: number[], race?: { score: number, race: Race }[] }\n\nexport const detectFace = async (instance: Human /* instance of human */, input: Tensor): Promise => {\n // run facemesh, includes blazeface and iris\n let timeStamp: number = now();\n let ageRes: { age: number } | Promise<{ age: number }> | null;\n let gearRes: gear.GearType | Promise | null;\n let genderRes: { gender: string, genderScore: number } | Promise<{ gender: string, genderScore: number }> | null;\n let emotionRes: { score: number, emotion: Emotion }[] | Promise<{ score: number, emotion: Emotion }[]>;\n let mobilefacenetRes: number[] | Promise | null;\n let insightfaceRes: number[] | Promise | null;\n let antispoofRes: number | Promise | null;\n let livenessRes: number | Promise | null;\n let descRes: DescRes | Promise | null;\n\n const faceRes: FaceResult[] = [];\n instance.state = 'run:face';\n\n const faces = await facemesh.predict(input, instance.config);\n instance.performance.face = env.perfadd ? (instance.performance.face || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);\n if (!input.shape || input.shape.length !== 4) return [];\n if (!faces) return [];\n // for (const face of faces) {\n for (let i = 0; i < faces.length; i++) {\n instance.analyze('Get Face');\n\n // is something went wrong, skip the face\n // @ts-ignore possibly undefied\n if (!faces[i].tensor || faces[i].tensor.isDisposedInternal) {\n log('Face object is disposed:', faces[i].tensor);\n continue;\n }\n\n // optional face mask\n if (instance.config.face.detector?.mask) {\n const masked = await mask.mask(faces[i]);\n tf.dispose(faces[i].tensor);\n if (masked) faces[i].tensor = masked;\n }\n\n // calculate face angles\n const rotation = faces[i].mesh && (faces[i].mesh.length > 200) ? calculateFaceAngle(faces[i], [input.shape[2], input.shape[1]]) : null;\n\n // run emotion, inherits face from blazeface\n instance.analyze('Start Emotion:');\n if (instance.config.async) {\n emotionRes = instance.config.face.emotion?.enabled ? emotion.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : [];\n } else {\n instance.state = 'run:emotion';\n timeStamp = now();\n emotionRes = instance.config.face.emotion?.enabled ? await emotion.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : [];\n instance.performance.emotion = env.perfadd ? (instance.performance.emotion || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);\n }\n instance.analyze('End Emotion:');\n\n // run antispoof, inherits face from blazeface\n instance.analyze('Start AntiSpoof:');\n if (instance.config.async) {\n antispoofRes = instance.config.face.antispoof?.enabled ? antispoof.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : 0;\n } else {\n instance.state = 'run:antispoof';\n timeStamp = now();\n antispoofRes = instance.config.face.antispoof?.enabled ? await antispoof.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : 0;\n instance.performance.antispoof = env.perfadd ? (instance.performance.antispoof || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);\n }\n instance.analyze('End AntiSpoof:');\n\n // run liveness, inherits face from blazeface\n instance.analyze('Start Liveness:');\n if (instance.config.async) {\n livenessRes = instance.config.face.liveness?.enabled ? liveness.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : 0;\n } else {\n instance.state = 'run:liveness';\n timeStamp = now();\n livenessRes = instance.config.face.liveness?.enabled ? await liveness.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : 0;\n instance.performance.liveness = env.perfadd ? (instance.performance.antispoof || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);\n }\n instance.analyze('End Liveness:');\n\n // run gear, inherits face from blazeface\n instance.analyze('Start GEAR:');\n if (instance.config.async) {\n gearRes = instance.config.face.gear?.enabled ? gear.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n } else {\n instance.state = 'run:gear';\n timeStamp = now();\n gearRes = instance.config.face.gear?.enabled ? await gear.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n instance.performance.gear = Math.trunc(now() - timeStamp);\n }\n instance.analyze('End GEAR:');\n\n // run gear, inherits face from blazeface\n instance.analyze('Start SSRNet:');\n if (instance.config.async) {\n ageRes = instance.config.face['ssrnet']?.enabled ? ssrnetAge.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n genderRes = instance.config.face['ssrnet']?.enabled ? ssrnetGender.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n } else {\n instance.state = 'run:ssrnet';\n timeStamp = now();\n ageRes = instance.config.face['ssrnet']?.enabled ? await ssrnetAge.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n genderRes = instance.config.face['ssrnet']?.enabled ? await ssrnetGender.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n instance.performance.ssrnet = Math.trunc(now() - timeStamp);\n }\n instance.analyze('End SSRNet:');\n\n // run mobilefacenet alternative, inherits face from blazeface\n instance.analyze('Start MobileFaceNet:');\n if (instance.config.async) {\n mobilefacenetRes = instance.config.face['mobilefacenet']?.enabled ? mobilefacenet.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n } else {\n instance.state = 'run:mobilefacenet';\n timeStamp = now();\n mobilefacenetRes = instance.config.face['mobilefacenet']?.enabled ? await mobilefacenet.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n instance.performance.mobilefacenet = Math.trunc(now() - timeStamp);\n }\n instance.analyze('End MobileFaceNet:');\n\n // run insightface alternative, inherits face from blazeface\n instance.analyze('Start InsightFace:');\n if (instance.config.async) {\n insightfaceRes = instance.config.face['insightface']?.enabled ? insightface.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n } else {\n instance.state = 'run:mobilefacenet';\n timeStamp = now();\n insightfaceRes = instance.config.face['insightface']?.enabled ? await insightface.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length) : null;\n instance.performance.mobilefacenet = Math.trunc(now() - timeStamp);\n }\n instance.analyze('End InsightFace:');\n\n // run faceres, inherits face from blazeface\n instance.analyze('Start Description:');\n if (instance.config.async) {\n descRes = faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length);\n } else {\n instance.state = 'run:description';\n timeStamp = now();\n descRes = await faceres.predict(faces[i].tensor || tf.tensor([]), instance.config, i, faces.length);\n instance.performance.description = env.perfadd ? (instance.performance.description || 0) + Math.trunc(now() - timeStamp) : Math.trunc(now() - timeStamp);\n }\n instance.analyze('End Description:');\n\n // if async wait for results\n if (instance.config.async) {\n [ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes] = await Promise.all([ageRes, genderRes, emotionRes, mobilefacenetRes, insightfaceRes, descRes, gearRes, antispoofRes, livenessRes]);\n }\n instance.analyze('Finish Face:');\n\n if (instance.config.face['ssrnet']?.enabled && ageRes && genderRes) { // override age/gender if ssrnet model is used\n descRes = {\n ...(descRes as DescRes),\n age: (ageRes as { age: number}).age,\n gender: (genderRes as { gender: Gender, genderScore: number }).gender,\n genderScore: (genderRes as { gender: Gender, genderScore: number }).genderScore,\n };\n }\n if (instance.config.face.gear?.enabled && gearRes) { // override age/gender/race if gear model is used\n descRes = {\n ...(descRes as DescRes),\n age: (gearRes as gear.GearType).age,\n gender: (gearRes as gear.GearType).gender,\n genderScore: (gearRes as gear.GearType).genderScore,\n race: (gearRes as gear.GearType).race,\n };\n }\n if (instance.config.face['mobilefacenet']?.enabled && mobilefacenetRes) { // override descriptor if mobilefacenet model is used\n (descRes as DescRes).descriptor = mobilefacenetRes as number[];\n }\n\n if (instance.config.face['insightface']?.enabled && insightfaceRes) { // override descriptor if insightface model is used\n (descRes as DescRes).descriptor = insightfaceRes as number[];\n }\n\n // calculate iris distance\n // iris: array[ center, left, top, right, bottom]\n if (!instance.config.face.iris?.enabled) {\n // if (faces[i]?.annotations?.leftEyeIris) delete faces[i].annotations.leftEyeIris;\n // if (faces[i]?.annotations?.rightEyeIris) delete faces[i].annotations.rightEyeIris;\n }\n const irisSize = (faces[i]?.annotations?.leftEyeIris?.[0] && faces[i]?.annotations?.rightEyeIris?.[0]\n && (faces[i].annotations.leftEyeIris.length > 0) && (faces[i].annotations.rightEyeIris.length > 0)\n && (faces[i].annotations.leftEyeIris[0] !== null) && (faces[i].annotations.rightEyeIris[0] !== null))\n ? Math.max(Math.abs(faces[i].annotations.leftEyeIris[3][0] - faces[i].annotations.leftEyeIris[1][0]), Math.abs(faces[i].annotations.rightEyeIris[4][1] - faces[i].annotations.rightEyeIris[2][1])) / input.shape[2]\n : 0; // note: average human iris size is 11.7mm\n\n // optionally return tensor\n const tensor = instance.config.face.detector?.return ? tf.squeeze(faces[i].tensor) : null;\n // dispose original face tensor\n tf.dispose(faces[i].tensor);\n // delete temp face image\n if (faces[i].tensor) delete faces[i].tensor;\n // combine results\n const res: FaceResult = {\n ...faces[i],\n id: i,\n };\n if ((descRes as DescRes).age) res.age = (descRes as DescRes).age;\n if ((descRes as DescRes).gender) res.gender = (descRes as DescRes).gender;\n if ((descRes as DescRes).genderScore) res.genderScore = (descRes as DescRes).genderScore;\n if ((descRes as DescRes).descriptor) res.embedding = (descRes as DescRes).descriptor;\n if ((descRes as DescRes).race) res.race = (descRes as DescRes).race as { score: number, race: Race }[];\n if (emotionRes) res.emotion = emotionRes as { score: number, emotion: Emotion }[];\n if (antispoofRes) res.real = antispoofRes as number;\n if (livenessRes) res.live = livenessRes as number;\n if (irisSize && irisSize !== 0) res.iris = Math.trunc(500 / irisSize / 11.7) / 100;\n if (rotation) res.rotation = rotation;\n if (tensor) res.tensor = tensor;\n faceRes.push(res);\n instance.analyze('End Face');\n }\n instance.analyze('End FaceMesh:');\n if (instance.config.async) {\n if (instance.performance.face) delete instance.performance.face;\n if (instance.performance.age) delete instance.performance.age;\n if (instance.performance.gender) delete instance.performance.gender;\n if (instance.performance.emotion) delete instance.performance.emotion;\n }\n return faceRes;\n};\n", "/**\n * Gesture detection algorithm\n */\n\nimport type { GestureResult, BodyResult, FaceResult, HandResult, Point } from '../result';\nimport * as fingerPose from '../hand/fingerpose';\n\n/** face gesture type */\nexport type FaceGesture =\n `facing ${'left' | 'center' | 'right'}`\n | `blink ${'left' | 'right'} eye`\n | `mouth ${number}% open`\n | `head ${'up' | 'down'}`;\n\n/** iris gesture type */\nexport type IrisGesture =\n 'facing center'\n | `looking ${'left' | 'right' | 'up' | 'down'}`\n | 'looking center';\n\n/** body gesture type */\nexport type BodyGesture =\n `leaning ${'left' | 'right'}`\n | `raise ${'left' | 'right'} hand`\n | 'i give up';\n\n/** hand gesture type */\nexport type HandGesture =\n `${'thumb' | 'index' | 'middle' | 'ring' | 'pinky'} forward`\n | `${'thumb' | 'index' | 'middle' | 'ring' | 'pinky'} up`\n | 'victory'\n | 'thumbs up';\n\nexport const body = (res: BodyResult[]): GestureResult[] => {\n if (!res) return [];\n const gestures: { body: number, gesture: BodyGesture }[] = [];\n for (let i = 0; i < res.length; i++) {\n // raising hands\n const leftWrist = res[i].keypoints.find((a) => (a.part === 'leftWrist'));\n const rightWrist = res[i].keypoints.find((a) => (a.part === 'rightWrist'));\n const nose = res[i].keypoints.find((a) => (a.part === 'nose'));\n if (nose && leftWrist && rightWrist && (leftWrist.position[1] < nose.position[1]) && (rightWrist.position[1] < nose.position[1])) gestures.push({ body: i, gesture: 'i give up' });\n else if (nose && leftWrist && (leftWrist.position[1] < nose.position[1])) gestures.push({ body: i, gesture: 'raise left hand' });\n else if (nose && rightWrist && (rightWrist.position[1] < nose.position[1])) gestures.push({ body: i, gesture: 'raise right hand' });\n\n // leaning\n const leftShoulder = res[i].keypoints.find((a) => (a.part === 'leftShoulder'));\n const rightShoulder = res[i].keypoints.find((a) => (a.part === 'rightShoulder'));\n if (leftShoulder && rightShoulder && Math.abs(leftShoulder.positionRaw[1] - rightShoulder.positionRaw[1]) > 0.1) {\n gestures.push({ body: i, gesture: `leaning ${(leftShoulder.position[1] > rightShoulder.position[1]) ? 'left' : 'right'}` });\n }\n }\n return gestures;\n};\n\nexport const face = (res: FaceResult[]): GestureResult[] => {\n if (!res) return [];\n const gestures: { face: number, gesture: FaceGesture }[] = [];\n for (let i = 0; i < res.length; i++) {\n if (res[i].mesh && res[i].mesh.length > 450) {\n const zDiff = (res[i].mesh[33][2] || 0) - (res[i].mesh[263][2] || 0);\n const xDiff = res[i].mesh[33][0] - res[i].mesh[263][0];\n if (Math.abs(zDiff / xDiff) <= 0.15) gestures.push({ face: i, gesture: 'facing center' });\n else gestures.push({ face: i, gesture: `facing ${zDiff < 0 ? 'left' : 'right'}` });\n const openLeft = Math.abs(res[i].mesh[374][1] - res[i].mesh[386][1]) / Math.abs(res[i].mesh[443][1] - res[i].mesh[450][1]); // center of eye inner lid y coord div center of wider eye border y coord\n if (openLeft < 0.2) gestures.push({ face: i, gesture: 'blink left eye' });\n const openRight = Math.abs(res[i].mesh[145][1] - res[i].mesh[159][1]) / Math.abs(res[i].mesh[223][1] - res[i].mesh[230][1]); // center of eye inner lid y coord div center of wider eye border y coord\n if (openRight < 0.2) gestures.push({ face: i, gesture: 'blink right eye' });\n const mouthOpen = Math.min(100, 500 * Math.abs(res[i].mesh[13][1] - res[i].mesh[14][1]) / Math.abs(res[i].mesh[10][1] - res[i].mesh[152][1]));\n if (mouthOpen > 10) gestures.push({ face: i, gesture: `mouth ${Math.trunc(mouthOpen)}% open` });\n const chinDepth = res[i].mesh[152][2] || 0;\n if (Math.abs(chinDepth) > 10) gestures.push({ face: i, gesture: `head ${chinDepth < 0 ? 'up' : 'down'}` });\n }\n }\n return gestures;\n};\n\nexport const iris = (res: FaceResult[]): GestureResult[] => {\n if (!res) return [];\n const gestures: { iris: number, gesture: IrisGesture }[] = [];\n for (let i = 0; i < res.length; i++) {\n if (!res[i].annotations?.leftEyeIris?.[0] || !res[i].annotations?.rightEyeIris?.[0]) continue;\n const sizeXLeft = res[i].annotations.leftEyeIris[3][0] - res[i].annotations.leftEyeIris[1][0];\n const sizeYLeft = res[i].annotations.leftEyeIris[4][1] - res[i].annotations.leftEyeIris[2][1];\n const areaLeft = Math.abs(sizeXLeft * sizeYLeft);\n\n const sizeXRight = res[i].annotations.rightEyeIris[3][0] - res[i].annotations.rightEyeIris[1][0];\n const sizeYRight = res[i].annotations.rightEyeIris[4][1] - res[i].annotations.rightEyeIris[2][1];\n const areaRight = Math.abs(sizeXRight * sizeYRight);\n\n let center = false;\n const difference = Math.abs(areaLeft - areaRight) / Math.max(areaLeft, areaRight);\n if (difference < 0.25) {\n center = true;\n gestures.push({ iris: i, gesture: 'facing center' });\n }\n\n const leftIrisCenterX = Math.abs(res[i].mesh[263][0] - res[i].annotations.leftEyeIris[0][0]) / res[i].box[2];\n const rightIrisCenterX = Math.abs(res[i].mesh[33][0] - res[i].annotations.rightEyeIris[0][0]) / res[i].box[2];\n if (leftIrisCenterX > 0.06 || rightIrisCenterX > 0.06) center = false;\n if (leftIrisCenterX > rightIrisCenterX) { // check eye with bigger offset\n if (leftIrisCenterX > 0.05) gestures.push({ iris: i, gesture: 'looking right' });\n } else {\n if (rightIrisCenterX > 0.05) gestures.push({ iris: i, gesture: 'looking left' });\n }\n\n const rightIrisCenterY = Math.abs(res[i].mesh[145][1] - res[i].annotations.rightEyeIris[0][1]) / res[i].box[3];\n const leftIrisCenterY = Math.abs(res[i].mesh[374][1] - res[i].annotations.leftEyeIris[0][1]) / res[i].box[3];\n if (leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01 || leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022) center = false;\n if (leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01) gestures.push({ iris: i, gesture: 'looking down' });\n if (leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022) gestures.push({ iris: i, gesture: 'looking up' });\n\n // still center;\n if (center) gestures.push({ iris: i, gesture: 'looking center' });\n }\n return gestures;\n};\n\nexport const hand = (res: HandResult[]): GestureResult[] => {\n if (!res) return [];\n const gestures: { hand: number, gesture: HandGesture }[] = [];\n for (let i = 0; i < res.length; i++) {\n const fingers: { name: string, position: Point }[] = [];\n if (res[i].annotations) {\n for (const [finger, pos] of Object.entries(res[i].annotations)) {\n if (finger !== 'palmBase' && Array.isArray(pos) && pos[0]) fingers.push({ name: finger.toLowerCase(), position: pos[0] }); // get tip of each finger\n }\n }\n if (fingers && fingers.length > 0) {\n const closest = fingers.reduce((best, a) => ((best.position[2] || 0) < (a.position[2] || 0) ? best : a));\n gestures.push({ hand: i, gesture: `${closest.name} forward` as HandGesture });\n const highest = fingers.reduce((best, a) => (best.position[1] < a.position[1] ? best : a));\n gestures.push({ hand: i, gesture: `${highest.name} up` as HandGesture });\n }\n if (res[i].keypoints) {\n const poses = fingerPose.match(res[i].keypoints);\n for (const pose of poses) gestures.push({ hand: i, gesture: pose.name as HandGesture });\n }\n }\n return gestures;\n};\n", "/**\n * Results interpolation for smoothening of video detection results inbetween detected frames\n */\n\nimport type { Result, FaceResult, BodyResult, HandResult, ObjectResult, PersonResult, Box, Point, BodyLandmark, BodyAnnotation } from '../result';\nimport type { Config } from '../config';\n\nimport * as moveNetCoords from '../body/movenetcoords';\nimport * as blazePoseCoords from '../body/blazeposecoords';\nimport * as efficientPoseCoords from '../body/efficientposecoords';\nimport { now } from './util';\nimport { env } from './env';\n\nconst bufferedResult: Result = { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0, error: null };\nlet interpolateTime = 0;\n\nexport function calc(newResult: Result, config: Config): Result {\n const t0 = now();\n if (!newResult) return { face: [], body: [], hand: [], gesture: [], object: [], persons: [], performance: {}, timestamp: 0, error: null };\n // each record is only updated using deep clone when number of detected record changes, otherwise it will converge by itself\n // otherwise bufferedResult is a shallow clone of result plus updated local calculated values\n // thus mixing by-reference and by-value assignments to minimize memory operations\n\n const elapsed = Date.now() - newResult.timestamp;\n // curve fitted: buffer = 8 - ln(delay)\n // interpolation formula: current = ((buffer - 1) * previous + live) / buffer\n // - at 50ms delay buffer = ~4.1 => 28% towards live data\n // - at 250ms delay buffer = ~2.5 => 40% towards live data\n // - at 500ms delay buffer = ~1.8 => 55% towards live data\n // - at 750ms delay buffer = ~1.4 => 71% towards live data\n // - at 1sec delay buffer = 1 which means live data is used\n const bufferedFactor = elapsed < 1000 ? 8 - Math.log(elapsed + 1) : 1;\n\n if (newResult.canvas) bufferedResult.canvas = newResult.canvas;\n if (newResult.error) bufferedResult.error = newResult.error;\n\n // interpolate body results\n if (!bufferedResult.body || (newResult.body.length !== bufferedResult.body.length)) {\n bufferedResult.body = JSON.parse(JSON.stringify(newResult.body)) as BodyResult[]; // deep clone once\n } else {\n for (let i = 0; i < newResult.body.length; i++) {\n const box = newResult.body[i].box // update box\n .map((newBoxCoord, j) => ((bufferedFactor - 1) * bufferedResult.body[i].box[j] + newBoxCoord) / bufferedFactor) as Box;\n const boxRaw = newResult.body[i].boxRaw // update boxRaw\n .map((newBoxCoord, j) => ((bufferedFactor - 1) * bufferedResult.body[i].boxRaw[j] + newBoxCoord) / bufferedFactor) as Box;\n const keypoints = (newResult.body[i].keypoints // update keypoints\n .map((newKpt, j) => ({\n score: newKpt.score,\n part: newKpt.part,\n position: [\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[0] || 0) + (newKpt.position[0] || 0)) / bufferedFactor : newKpt.position[0],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[1] || 0) + (newKpt.position[1] || 0)) / bufferedFactor : newKpt.position[1],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].position[2] || 0) + (newKpt.position[2] || 0)) / bufferedFactor : newKpt.position[2],\n ],\n positionRaw: [\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[0] || 0) + (newKpt.positionRaw[0] || 0)) / bufferedFactor : newKpt.positionRaw[0],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[1] || 0) + (newKpt.positionRaw[1] || 0)) / bufferedFactor : newKpt.positionRaw[1],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].positionRaw[2] || 0) + (newKpt.positionRaw[2] || 0)) / bufferedFactor : newKpt.positionRaw[2],\n ],\n distance: [\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].distance?.[0] || 0) + (newKpt.distance?.[0] || 0)) / bufferedFactor : newKpt.distance?.[0],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].distance?.[1] || 0) + (newKpt.distance?.[1] || 0)) / bufferedFactor : newKpt.distance?.[1],\n bufferedResult.body[i].keypoints[j] ? ((bufferedFactor - 1) * (bufferedResult.body[i].keypoints[j].distance?.[2] || 0) + (newKpt.distance?.[2] || 0)) / bufferedFactor : newKpt.distance?.[2],\n ],\n }))) as { score: number, part: BodyLandmark, position: [number, number, number?], positionRaw: [number, number, number?] }[];\n\n const annotations: Record = {} as Record; // recreate annotations\n let coords = { connected: {} };\n if (config.body.modelPath?.includes('efficientpose')) coords = efficientPoseCoords;\n else if (config.body.modelPath?.includes('blazepose')) coords = blazePoseCoords;\n else if (config.body.modelPath?.includes('movenet')) coords = moveNetCoords;\n for (const [name, indexes] of Object.entries(coords.connected as Record)) {\n const pt: Point[][] = [];\n for (let j = 0; j < indexes.length - 1; j++) {\n const pt0 = keypoints.find((kp) => kp.part === indexes[j]);\n const pt1 = keypoints.find((kp) => kp.part === indexes[j + 1]);\n // if (pt0 && pt1 && pt0.score > (config.body.minConfidence || 0) && pt1.score > (config.body.minConfidence || 0)) pt.push([pt0.position, pt1.position]);\n if (pt0 && pt1) pt.push([pt0.position, pt1.position]);\n }\n annotations[name] = pt;\n }\n bufferedResult.body[i] = { ...newResult.body[i], box, boxRaw, keypoints, annotations }; // shallow clone plus updated values\n }\n }\n\n // interpolate hand results\n if (!bufferedResult.hand || (newResult.hand.length !== bufferedResult.hand.length)) {\n bufferedResult.hand = JSON.parse(JSON.stringify(newResult.hand)); // deep clone once\n } else {\n for (let i = 0; i < newResult.hand.length; i++) {\n const box = (newResult.hand[i].box// update box\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.hand[i].box[j] + b) / bufferedFactor)) as Box;\n const boxRaw = (newResult.hand[i].boxRaw // update boxRaw\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.hand[i].boxRaw[j] + b) / bufferedFactor)) as Box;\n if (bufferedResult.hand[i].keypoints.length !== newResult.hand[i].keypoints.length) bufferedResult.hand[i].keypoints = newResult.hand[i].keypoints; // reset keypoints as previous frame did not have them\n const keypoints = newResult.hand[i].keypoints && newResult.hand[i].keypoints.length > 0 ? newResult.hand[i].keypoints // update landmarks\n .map((landmark, j) => landmark\n .map((coord, k) => (((bufferedFactor - 1) * (bufferedResult.hand[i].keypoints[j][k] || 1) + (coord || 0)) / bufferedFactor)) as Point)\n : [];\n let annotations = {};\n if (Object.keys(bufferedResult.hand[i].annotations).length !== Object.keys(newResult.hand[i].annotations).length) {\n bufferedResult.hand[i].annotations = newResult.hand[i].annotations; // reset annotations as previous frame did not have them\n annotations = bufferedResult.hand[i].annotations;\n } else if (newResult.hand[i].annotations) {\n for (const key of Object.keys(newResult.hand[i].annotations)) { // update annotations\n annotations[key] = newResult.hand[i]?.annotations?.[key]?.[0]\n ? newResult.hand[i].annotations[key]\n .map((val, j: number) => val\n .map((coord: number, k: number) => ((bufferedFactor - 1) * bufferedResult.hand[i].annotations[key][j][k] + coord) / bufferedFactor))\n : null;\n }\n }\n bufferedResult.hand[i] = { ...newResult.hand[i], box, boxRaw, keypoints, annotations: annotations as HandResult['annotations'] }; // shallow clone plus updated values\n }\n }\n\n // interpolate face results\n if (!bufferedResult.face || (newResult.face.length !== bufferedResult.face.length)) {\n bufferedResult.face = JSON.parse(JSON.stringify(newResult.face)) as FaceResult[]; // deep clone once\n } else {\n for (let i = 0; i < newResult.face.length; i++) {\n const box = (newResult.face[i].box // update box\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.face[i].box[j] + b) / bufferedFactor)) as Box;\n const boxRaw = (newResult.face[i].boxRaw // update boxRaw\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.face[i].boxRaw[j] + b) / bufferedFactor)) as Box;\n if (newResult.face[i].rotation) {\n const rotation: {\n matrix: [number, number, number, number, number, number, number, number, number],\n angle: { roll: number, yaw: number, pitch: number },\n gaze: { bearing: number, strength: number }\n } = { matrix: [0, 0, 0, 0, 0, 0, 0, 0, 0], angle: { roll: 0, yaw: 0, pitch: 0 }, gaze: { bearing: 0, strength: 0 } };\n rotation.matrix = newResult.face[i].rotation?.matrix as [number, number, number, number, number, number, number, number, number];\n rotation.angle = {\n roll: ((bufferedFactor - 1) * (bufferedResult.face[i].rotation?.angle.roll || 0) + (newResult.face[i].rotation?.angle.roll || 0)) / bufferedFactor,\n yaw: ((bufferedFactor - 1) * (bufferedResult.face[i].rotation?.angle.yaw || 0) + (newResult.face[i].rotation?.angle.yaw || 0)) / bufferedFactor,\n pitch: ((bufferedFactor - 1) * (bufferedResult.face[i].rotation?.angle.pitch || 0) + (newResult.face[i].rotation?.angle.pitch || 0)) / bufferedFactor,\n };\n rotation.gaze = {\n // not fully correct due projection on circle, also causes wrap-around draw on jump from negative to positive\n bearing: ((bufferedFactor - 1) * (bufferedResult.face[i].rotation?.gaze.bearing || 0) + (newResult.face[i].rotation?.gaze.bearing || 0)) / bufferedFactor,\n strength: ((bufferedFactor - 1) * (bufferedResult.face[i].rotation?.gaze.strength || 0) + (newResult.face[i].rotation?.gaze.strength || 0)) / bufferedFactor,\n };\n bufferedResult.face[i] = { ...newResult.face[i], rotation, box, boxRaw }; // shallow clone plus updated values\n }\n bufferedResult.face[i] = { ...newResult.face[i], box, boxRaw }; // shallow clone plus updated values\n }\n }\n\n // interpolate object detection results\n if (!bufferedResult.object || (newResult.object.length !== bufferedResult.object.length)) {\n bufferedResult.object = JSON.parse(JSON.stringify(newResult.object)) as ObjectResult[]; // deep clone once\n } else {\n for (let i = 0; i < newResult.object.length; i++) {\n const box = (newResult.object[i].box // update box\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.object[i].box[j] + b) / bufferedFactor)) as Box;\n const boxRaw = (newResult.object[i].boxRaw // update boxRaw\n .map((b, j) => ((bufferedFactor - 1) * bufferedResult.object[i].boxRaw[j] + b) / bufferedFactor)) as Box;\n bufferedResult.object[i] = { ...newResult.object[i], box, boxRaw }; // shallow clone plus updated values\n }\n }\n\n // interpolate person results\n if (newResult.persons) {\n const newPersons = newResult.persons; // trigger getter function\n if (!bufferedResult.persons || (newPersons.length !== bufferedResult.persons.length)) {\n bufferedResult.persons = JSON.parse(JSON.stringify(newPersons)) as PersonResult[];\n } else {\n for (let i = 0; i < newPersons.length; i++) { // update person box, we don't update the rest as it's updated as reference anyhow\n bufferedResult.persons[i].box = (newPersons[i].box\n .map((box, j) => ((bufferedFactor - 1) * bufferedResult.persons[i].box[j] + box) / bufferedFactor)) as Box;\n }\n }\n }\n\n // just copy latest gestures without interpolation\n if (newResult.gesture) bufferedResult.gesture = newResult.gesture;\n\n // append interpolation performance data\n const t1 = now();\n interpolateTime = env.perfadd ? interpolateTime + Math.round(t1 - t0) : Math.round(t1 - t0);\n if (newResult.performance) bufferedResult.performance = { ...newResult.performance, interpolate: interpolateTime };\n\n return bufferedResult;\n}\n", "/** Face descriptor type as number array */\nexport type Descriptor = number[]\nexport type MatchOptions = { order?: number, threshold?: number, multiplier?: number, min?: number, max?: number } | undefined;\n\n/** Calculates distance between two descriptors\n * @param options - calculation options\n * - order - algorithm to use\n * Euclidean distance if `order` is 2 (default), Minkowski distance algorithm of nth order if `order` is higher than 2\n * - multiplier - by how much to enhance difference analysis in range of 1..100\n * default is 20 which normalizes results to similarity above 0.5 can be considered a match\n */\nexport function distance(descriptor1: Descriptor, descriptor2: Descriptor, options: MatchOptions = { order: 2, multiplier: 25 }) {\n // general minkowski distance, euclidean distance is limited case where order is 2\n if (!descriptor1 || !descriptor1) return Number.MAX_SAFE_INTEGER;\n let sum = 0;\n for (let i = 0; i < descriptor1.length; i++) {\n const diff = (!options.order || options.order === 2) ? (descriptor1[i] - descriptor2[i]) : (Math.abs(descriptor1[i] - descriptor2[i]));\n sum += (!options.order || options.order === 2) ? (diff * diff) : (diff ** options.order);\n }\n return (options.multiplier || 20) * sum;\n}\n\n// invert distance to similarity, normalize to given range and clamp\nconst normalizeDistance = (dist, order, min, max) => {\n if (dist === 0) return 1; // short circuit for identical inputs\n const root = order === 2 ? Math.sqrt(dist) : dist ** (1 / order); // take root of distance\n const norm = (1 - (root / 100) - min) / (max - min); // normalize to range\n const clamp = Math.max(Math.min(norm, 1), 0); // clamp to 0..1\n return clamp;\n};\n\n/** Calculates normalized similarity between two face descriptors based on their `distance`\n * @param options - calculation options\n * - order - algorithm to use\n * Euclidean distance if `order` is 2 (default), Minkowski distance algorithm of nth order if `order` is higher than 2\n * - multiplier - by how much to enhance difference analysis in range of 1..100\n * default is 20 which normalizes results to similarity above 0.5 can be considered a match\n * - min - normalize similarity result to a given range\n * - max - normalzie similarity resutl to a given range\n * default is 0.2...0.8\n * Returns similarity between two face descriptors normalized to 0..1 range where 0 is no similarity and 1 is perfect similarity\n */\nexport function similarity(descriptor1: Descriptor, descriptor2: Descriptor, options: MatchOptions = { order: 2, multiplier: 25, min: 0.2, max: 0.8 }) {\n const dist = distance(descriptor1, descriptor2, options);\n return normalizeDistance(dist, options.order || 2, options.min || 0, options.max || 1);\n}\n\n/** Matches given descriptor to a closest entry in array of descriptors\n * @param descriptor - face descriptor\n * @param descriptors - array of face descriptors to commpare given descriptor to\n * @param options - see `similarity` method for options description\n * Returns\n * - `index` index array index where best match was found or -1 if no matches\n * - `distance` calculated `distance` of given descriptor to the best match\n * - `similarity` calculated normalized `similarity` of given descriptor to the best match\n*/\nexport function match(descriptor: Descriptor, descriptors: Descriptor[], options: MatchOptions = { order: 2, multiplier: 25, threshold: 0, min: 0.2, max: 0.8 }) {\n if (!Array.isArray(descriptor) || !Array.isArray(descriptors) || descriptor.length < 64 || descriptors.length === 0) { // validate input\n return { index: -1, distance: Number.POSITIVE_INFINITY, similarity: 0 };\n }\n let lowestDistance = Number.MAX_SAFE_INTEGER;\n let index = -1;\n for (let i = 0; i < descriptors.length; i++) {\n const res = descriptors[i].length === descriptor.length ? distance(descriptor, descriptors[i], options) : Number.MAX_SAFE_INTEGER;\n if (res < lowestDistance) {\n lowestDistance = res;\n index = i;\n }\n if (lowestDistance < (options.threshold || 0)) break;\n }\n const normalizedSimilarity = normalizeDistance(lowestDistance, options.order || 2, options.min || 0, options.max || 1);\n return { index, distance: lowestDistance, similarity: normalizedSimilarity };\n}\n", "/**\n * Analyze detection Results and sort&combine them into per-person view\n */\n\nimport type { FaceResult, BodyResult, HandResult, GestureResult, PersonResult, Box } from '../result';\n\nexport function join(faces: FaceResult[], bodies: BodyResult[], hands: HandResult[], gestures: GestureResult[], shape: number[] | undefined): PersonResult[] {\n let id = 0;\n const persons: PersonResult[] = [];\n for (const face of faces) { // person is defined primarily by face and then we append other objects as found\n const person: PersonResult = { id: id++, face, body: null, hands: { left: null, right: null }, gestures: [], box: [0, 0, 0, 0] };\n for (const body of bodies) {\n if (face.box[0] > body.box[0] // x within body\n && face.box[0] < body.box[0] + body.box[2]\n && face.box[1] + face.box[3] > body.box[1] // y within body\n && face.box[1] + face.box[3] < body.box[1] + body.box[3]) {\n person.body = body;\n }\n }\n if (person.body) { // only try to join hands if body is found\n for (const hand of hands) {\n if (hand.box[0] + hand.box[2] > person.body.box[0] // x within body for left hand\n && hand.box[0] + hand.box[2] < person.body.box[0] + person.body.box[2]\n && hand.box[1] + hand.box[3] > person.body.box[1] // x within body for left hand\n && hand.box[1] + hand.box[3] < person.body.box[1] + person.body.box[3]) {\n if (person.hands) person.hands.left = hand;\n }\n if (hand.box[0] < person.body.box[0] + person.body.box[2] // x within body for right hand\n && hand.box[0] > person.body.box[0]\n && hand.box[1] + hand.box[3] > person.body.box[1] // x within body for right hand\n && hand.box[1] + hand.box[3] < person.body.box[1] + person.body.box[3]) {\n if (person.hands) person.hands.right = hand;\n }\n }\n }\n for (const gesture of gestures) { // append all gestures according to ids\n if (gesture['face'] !== undefined && gesture['face'] === face.id) person.gestures.push(gesture);\n else if (gesture['iris'] !== undefined && gesture['iris'] === face.id) person.gestures.push(gesture);\n else if (gesture['body'] !== undefined && gesture['body'] === person.body?.id) person.gestures.push(gesture);\n else if (gesture['hand'] !== undefined && gesture['hand'] === person.hands.left?.id) person.gestures.push(gesture);\n else if (gesture['hand'] !== undefined && gesture['hand'] === person.hands.right?.id) person.gestures.push(gesture);\n }\n\n // create new overarching box from all boxes belonging to person\n const x: number[] = [];\n const y: number[] = [];\n const extractXY = (box: Box | undefined) => { // extract all [x, y] coordinates from boxes [x, y, width, height]\n if (box && box.length === 4) {\n x.push(box[0], box[0] + box[2]);\n y.push(box[1], box[1] + box[3]);\n }\n };\n extractXY(person.face.box);\n extractXY(person.body?.box);\n extractXY(person.hands.left?.box);\n extractXY(person.hands.right?.box);\n const minX = Math.min(...x);\n const minY = Math.min(...y);\n person.box = [minX, minY, Math.max(...x) - minX, Math.max(...y) - minY]; // create new overarching box\n\n // shape is known so we calculate boxRaw as well\n if (shape?.[1] && shape?.[2]) person.boxRaw = [person.box[0] / shape[2], person.box[1] / shape[1], person.box[2] / shape[2], person.box[3] / shape[1]];\n\n persons.push(person);\n }\n return persons;\n}\n", "/**\n * Embedded sample images used during warmup in dataURL format\n */\n\n// data:image/jpeg;base64,\nexport const face = `\n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`;\n\n// data:image/jpeg;base64,\nexport const body = `\n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\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAa2p1UYo5adbz+xbxMlvqJ1OqjDHLXree0ejmzNrWm953tPmTPWbWneZ7yoy5YhjrXXTjH8s75N\nmtkyxt0VZM2/m175N1V03yTKubMLXVXybeYLLX2VXy7eam+b0bOg4VquJW+rry4/O9uyZOq3UjVm\n9r25axMzPaIdvhns1kzbZddM0p5Y47z8/R2+HcF03Doi1a8+Xzvbv+TotJnjDXkt+K8ODHp8cY8N\nIpSO0RCwF2YAAAAAAAAACvUZYw6fJkntWN3k8dfHz2vLucdz8mkjFE9bz1+UOZosX1UzPm0nqI/W\nMYo9FlcPNklfFGeH/NshLGun+Cz6PtHZtVZWlRLS+jxPkRpIn7rdoupHTdA5s6SI+7H6Mfo+32Y2\n+To3neSIiZ7A0IjPXpXLePlMotGW3272t85datKzHZjbTVnsDj+FG/2Y/RlGP4R+jo20u7H6N1Ql\no+H8I/REY957R+jpfReiK6eOYHLtj2tttH6KrY/6Y/R2c+kjeJiFVtLG24hxpw7/AHY/RRkw9O37\nO99Hrt1YX0tfOBLjcGp4XF8c+u8fs9c4dcVcGemSI61nd3IneN1orQAAAAAAAAAAAAABFqxes1tE\nTE9JiUgPKcX4RbRXnNgiZwWnrH4XPi28PdXpW9JraImsxtMS8pxXhF9DecuGJtgmf+1TWW2N/la1\nL7N7T5e3Vy6W3hsYcvLbqzbO9jvvCzvDR0+XeO7crO6FmGSvRThy/RtVXJ92elvk2rRvDUzU7pl4\nizsd2J3jeBpcNz+Lg5LT7+Pp+Xk3W7js5eAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs0NTrN96Yp6edkW8Wzm6+LNTq4pvTHO9vOfRoWtt\n1mes95YWvs1s2fZldddOczLPLn2ju0MmebT3YZc2/mpm3qqllN1drsbZIhr3yzvtHf4AsvlYYseb\nV5Yx4KTe0+UQ6nDvZ3UazbJqd8OKeu33peq0eh0+hxcmnxxWPOfOfm0mP+steT/ji8N9mKY9suum\nL37+HHaPm9DSlaVitKxWsdohI0Y22gAgAAAAAAAAAABXnyRhw3yT92Nwef4xm8bVzET0rPJH5d12\n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\nAOZE/OsPS29nuH27YrV+VpeV9pdPXhOtw49NG9Mld55+vXcTPd42I47qo7xSfyWV9oM8d8VJ/VxM\nd8l46xWF9cV7en6o/qLfxp2I9ob+eCv/AHMo9op89P8A/wBORGmyT5R+qfo2X8P7n9Q/jTsx7RR5\n6ef+4/8AuHftg/8A6cWcOSO9J/WEbWr3pY7Efzp2Lcfv5YK/9zWy8d1E/ZpSv5Oba1/+Hb9lc+LP\nbFt87I7E/wAabWbiurvEx4nL/pjZzc2bJkn372t85ZXx55/BX85lucC0vPxnTxlnnjm32mOiZqUu\nLJ2p4TwnVavNWaYbRTfre0bQ99pcH0bT0xb78vmtiIiNojaErMwAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAHnfarF7umzRHaZrLjYrdIen9ocPi8JyTt1xzF4eUw23rCm3R4r6bMy\nwt6kdTaWLdjswmNoZontsCm0K5XWjopnuDC0dGpqG5bs08/daKV672MjbSaif6oh6Z5f2LtvptRX\n0tEvUN3Jfo8f7cYve0eX4zV7B5z20xc/C8eSPuZIRficfXlcPaG7ino08HWIbePpLF2NuiyOyrHK\n3fZFSwuovHVfaVF4QK5YWTM9UT0EKry6Ps1Tn4zjn8NZn9nOtLseydObiWW34cf918fWfk+PYANn\nKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq1WKM+ly4p+/WYeBxTNd6zG0xO0\nvobw3FcP0bi2em20Tbmj5Srr418V9sa2Z7qKyzi07MXUylhaU7yjqhLCeiq3ddaFNxFYW7NLNG8t\nzya+WO6Va9J7FW66mvwidnrXiPY3Ny8RyUn71Jj9Ht3RPjk19HK9pMHj8D1ER3rHN+jqqtTjjNps\nuOe16zAifXzfTz7kNyndpYazS9qT0mszDdoxrsi6m8LazMq6zDOsq1ZEyrt1WWlXaUCqyq0rbKbi\nFdp6PReyFd8uqv8ACsfy83aXrPZHHto89/xX2/SP/dpj6y8vx6EBq5gAAAAAAAAAAAAAAAAAAAAA\n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\nNO67NbkhzNVnmInqzaOZrL93JyZeV0M1++7S02jvxDWxhxx033tPpC8Z6rrezWjmZyazJG2/u03h\n2vFibTHoqvamiwVwY+nLGzV0+SZ1Mx8G0/45tOhzJ5lXMc3UVXRdlF1HP+iYsDPLPPy49/tz1+Te\npSIr0ho6ak5Ms5J8o2q6NImOrHV7XX488ypzTtHXo0s9t6zG7c1G1qz6ubeZiZ3UatXJG3yauSO7\ncvMTEx5tPLb3prPRMVr0HB8vicNxf0+7+kt+LOJwTJyY/Bnz3tH93X36N58cWvq6LSyiyndMSlC7\nmZcymLJiwLosmJVRLKLAtiU7q4lMSCzc3YxJuDMRuAlKAEgAAAlAkAAAAAABKAEgAAAAAJAAAAAA\nAAAAAAAEgAAAAAAAAAAAAAkAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAhIAAACAAAASgAAAAAAEAAAA\nhGzJAImGMwzQDDZjNVuyNgUTVhNGxysZqDVmiu1G5NN2M4waM0+DCaN2cbGcQNGaMZq3JxMJxA1J\nqx2bU4kU09slorWNwa20z02RXHbJbl26QvtFovbHWkxEdJt5y2MOHlr2U1W3jx+1hiw8vSO63lmI\nXRTaEWmtY6snRHO1VpmJ+DjavpSZl2s8b7y4HFcnh0n0gha5ebJN55KRM2mdoiPN6fh+kpwXh0Wy\nRHj5Otp/s5Ps1p62y31+em9aTMYt/OfVfxTiPjZ52naI7fBrI5t66xz5+a1rW7yx0eSL6iZjtEOX\nqNbSletom3lENjh2fbHzbbWt3iVozruc+5ztWubf4M4ybpQ2Oboyrva0Vjza8WdDR4OkXt3n9ldX\nkaePP9VtYqctYhdvt5oivTeCZ2YOxXk6ubqMfV0b9mrljfqlFcq88k7z2U5axeItDa1OPessuC8P\nya7XRWYnwqdbT/ZMilvIu4dpslNdixXja8Y5tt85djZdbDWnGOesRtXFtuw6T27No5Kx2OrKYQlC\nExKJgBnEpiyvdlEgsizKLKollFgWxLKJVRLKJBbEp3VxLKJBnuMWQJEbpBIAAAJAAAABIAAAAAAA\nlAJAAAAAAAAAAAAAASAAAAAAAAAAAAAJAAAABAJABAlAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAA\nAAABAJQAAAAgAABAAI2EoBGyJhkgGPKxmqxAKpownHC+YRMdN5BrTj67R3bOn01o7p01Iv71u89o\n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\n2Q==`;\n", "/**\n * Warmup algorithm that uses embedded images to exercise loaded models for faster future inference\n */\n\nimport { log, now, mergeDeep } from './util/util';\nimport * as sample from './sample';\nimport * as tf from '../dist/tfjs.esm.js';\nimport * as image from './image/image';\nimport { env } from './util/env';\nimport type { Config } from './config';\nimport type { Result } from './result';\nimport type { Human, Models } from './human';\nimport type { Tensor } from './exports';\n\nasync function warmupBitmap(instance: Human): Promise {\n const b64toBlob = (base64: string, type = 'application/octet-stream') => fetch(`data:${type};base64,${base64}`).then((res) => res.blob());\n let blob: Blob | null;\n let res: Result | undefined;\n switch (instance.config.warmup) {\n case 'face': blob = await b64toBlob(sample.face); break;\n case 'body':\n case 'full': blob = await b64toBlob(sample.body); break;\n default: blob = null;\n }\n if (blob) {\n const bitmap = await createImageBitmap(blob);\n res = await instance.detect(bitmap, instance.config);\n bitmap.close();\n }\n return res;\n}\n\nasync function warmupCanvas(instance: Human): Promise {\n return new Promise((resolve) => {\n let src: string;\n // let size = 0;\n switch (instance.config.warmup) {\n case 'face':\n // size = 256;\n src = 'data:image/jpeg;base64,' + sample.face;\n break;\n case 'full':\n case 'body':\n // size = 1200;\n src = 'data:image/jpeg;base64,' + sample.body;\n break;\n default:\n src = '';\n }\n // src = encodeURI('../assets/human-sample-upper.jpg');\n let img: HTMLImageElement;\n if (typeof Image !== 'undefined') img = new Image();\n // @ts-ignore env.image is an external monkey-patch\n else if (env.Image) img = new env.Image();\n else return;\n img.onload = async () => {\n const canvas = image.canvas(img.naturalWidth, img.naturalHeight);\n if (!canvas) {\n log('Warmup: Canvas not found');\n resolve(undefined);\n } else {\n const ctx = canvas.getContext('2d');\n if (ctx) ctx.drawImage(img, 0, 0);\n // const data = ctx?.getImageData(0, 0, canvas.height, canvas.width);\n const tensor = await instance.image(canvas);\n const res = tensor.tensor ? await instance.detect(tensor.tensor, instance.config) : undefined;\n resolve(res);\n }\n };\n if (src) img.src = src;\n else resolve(undefined);\n });\n}\n\nasync function warmupNode(instance: Human): Promise {\n const atob = (str: string) => Buffer.from(str, 'base64');\n let img;\n if (instance.config.warmup === 'face') img = atob(sample.face);\n else img = atob(sample.body);\n let res: Result;\n if (('node' in tf) && (tf.getBackend() === 'tensorflow')) {\n const data: Tensor = tf['node'].decodeJpeg(img); // eslint-disable-line import/namespace\n const expanded: Tensor = tf.expandDims(data, 0);\n instance.tf.dispose(data);\n // log('Input:', expanded);\n res = await instance.detect(expanded, instance.config);\n instance.tf.dispose(expanded);\n } else {\n if (instance.config.debug) log('Warmup tfjs-node not loaded');\n /*\n const input = await canvasJS.loadImage(img);\n const canvas = canvasJS.createCanvas(input.width, input.height);\n const ctx = canvas.getContext('2d');\n ctx.drawImage(img, 0, 0, input.width, input.height);\n res = await instance.detect(input, instance.config);\n */\n }\n // @ts-ignore\n return res;\n}\n\nasync function runInference(instance: Human) {\n let res: Result | undefined;\n if (typeof createImageBitmap === 'function') res = await warmupBitmap(instance);\n else if (typeof Image !== 'undefined' || env.Canvas !== undefined) res = await warmupCanvas(instance);\n else res = await warmupNode(instance);\n return res;\n}\n\n/** Runs pre-compile on all loaded models */\nexport async function runCompile(allModels: Models) {\n if (!tf.env().flagRegistry.ENGINE_COMPILE_ONLY) return; // tfjs does not support compile-only inference\n const backendType = tf.getBackend();\n const webGLBackend = tf.backend();\n if ((backendType !== 'webgl' && backendType !== 'humangl') || !webGLBackend?.checkCompileCompletion) {\n // log('compile pass: skip');\n return;\n }\n tf.env().set('ENGINE_COMPILE_ONLY', true);\n const numTensorsStart = tf.engine().state.numTensors;\n const compiledModels: string[] = [];\n for (const [modelName, model] of Object.entries(allModels).filter(([key, val]) => (key !== null && val !== null))) {\n const shape = (model.inputs?.[0]?.shape) ? [...model.inputs[0].shape] : [1, 64, 64, 3];\n const dtype: string = (model.inputs?.[0]?.dtype) ? model.inputs[0].dtype : 'float32';\n for (let dim = 0; dim < shape.length; dim++) {\n if (shape[dim] === -1) shape[dim] = dim === 0 ? 1 : 64; // override batch number and any dynamic dimensions\n }\n const tensor = tf.zeros(shape, dtype);\n try {\n const res = model.execute(tensor);\n compiledModels.push(modelName);\n if (Array.isArray(res)) res.forEach((t) => tf.dispose(t));\n else tf.dispose(res);\n } catch {\n log('compile fail model:', modelName);\n }\n tf.dispose(tensor);\n }\n const kernels = await webGLBackend.checkCompileCompletionAsync();\n webGLBackend.getUniformLocations();\n log('compile pass models:', compiledModels);\n log('compile pass kernels:', kernels.length);\n tf.env().set('ENGINE_COMPILE_ONLY', false);\n const numTensorsEnd = tf.engine().state.numTensors;\n if ((numTensorsEnd - numTensorsStart) > 0) log('tensor leak:', numTensorsEnd - numTensorsStart);\n}\n\n/** Warmup method pre-initializes all configured models for faster inference\n * - can take significant time on startup\n * - only used in browser environments for `webgl` and `humangl` backends\n * @param userConfig?: Config\n*/\nexport async function warmup(instance: Human, userConfig?: Partial): Promise {\n const t0 = now();\n instance.state = 'warmup';\n if (userConfig) instance.config = mergeDeep(instance.config, userConfig) as Config;\n if (!instance.config.warmup || instance.config.warmup.length === 0 || instance.config.warmup === 'none') {\n return { face: [], body: [], hand: [], gesture: [], object: [], performance: instance.performance, timestamp: now(), persons: [], error: null };\n }\n return new Promise(async (resolve) => {\n await runCompile(instance.models);\n const res = await runInference(instance);\n const t1 = now();\n if (instance.config.debug) log('warmup', instance.config.warmup, Math.round(t1 - t0), 'ms');\n instance.emit('warmup');\n resolve(res);\n });\n}\n", "/**\n * Human main module\n * @default Human Library\n * @summary \n * @author \n * @copyright \n * @license MIT\n */\n\n// module imports\nimport { log, now, mergeDeep, validate } from './util/util';\nimport { defaults } from './config';\nimport { env, Env } from './util/env';\nimport { setModelLoadOptions } from './tfjs/load';\nimport * as tf from '../dist/tfjs.esm.js';\nimport * as app from '../package.json';\nimport * as backend from './tfjs/backend';\nimport * as blazepose from './body/blazepose';\nimport * as centernet from './object/centernet';\nimport * as draw from './draw/draw';\nimport * as efficientpose from './body/efficientpose';\nimport * as face from './face/face';\nimport * as facemesh from './face/facemesh';\nimport * as faceres from './face/faceres';\nimport * as gesture from './gesture/gesture';\nimport * as handpose from './hand/handpose';\nimport * as handtrack from './hand/handtrack';\nimport * as humangl from './tfjs/humangl';\nimport * as image from './image/image';\nimport * as interpolate from './util/interpolate';\nimport * as match from './face/match';\nimport * as models from './models';\nimport * as movenet from './body/movenet';\nimport * as nanodet from './object/nanodet';\nimport * as persons from './util/persons';\nimport * as posenet from './body/posenet';\nimport * as segmentation from './segmentation/segmentation';\nimport * as warmups from './warmup';\n// type definitions\nimport type { Input, Tensor, DrawOptions, Config, Result, FaceResult, HandResult, BodyResult, ObjectResult, GestureResult, PersonResult, AnyCanvas, ModelStats } from './exports';\n// type exports\nexport * from './exports';\n\n/** **Human** library main class\n *\n * All methods and properties are available only as members of Human class\n *\n * - Configuration object definition: {@link Config}\n * - Results object definition: {@link Result}\n * - Possible inputs: {@link Input}\n *\n * @param userConfig - {@link Config}\n * @returns instance of {@link Human}\n */\nexport class Human {\n /** Current version of Human library in *semver* format */\n version: string;\n\n /** Current configuration\n * - Defaults: [config](https://github.com/vladmandic/human/blob/main/src/config.ts#L262)\n */\n config: Config;\n\n /** Last known result of detect run\n * - Can be accessed anytime after initial detection\n */\n result: Result;\n\n /** Current state of Human library\n * - Can be polled to determine operations that are currently executed\n * - Progresses through: 'config', 'check', 'backend', 'load', 'run:', 'idle'\n */\n state: string;\n\n /** currenty processed image tensor and canvas */\n process: { tensor: Tensor | null, canvas: AnyCanvas | null };\n\n /** Instance of TensorFlow/JS used by Human\n * - Can be embedded or externally provided\n * [TFJS API](https://js.tensorflow.org/api/latest/)\n */\n tf;\n\n /** Object containing environment information used for diagnostics */\n env: Env;\n\n /** Draw helper classes that can draw detected objects on canvas using specified draw\n * - canvas: draws input to canvas\n * - options: are global settings for all draw operations, can be overriden for each draw method {@link DrawOptions}\n * - face, body, hand, gesture, object, person: draws detected results as overlays on canvas\n */\n draw: { canvas: typeof draw.canvas, face: typeof draw.face, body: typeof draw.body, hand: typeof draw.hand, gesture: typeof draw.gesture, object: typeof draw.object, person: typeof draw.person, all: typeof draw.all, options: DrawOptions };\n\n /** Currently loaded models\n * @internal\n * {@link Models}\n */\n models: models.Models;\n\n /** Container for events dispatched by Human\n * Possible events:\n * - `create`: triggered when Human object is instantiated\n * - `load`: triggered when models are loaded (explicitly or on-demand)\n * - `image`: triggered when input image is processed\n * - `result`: triggered when detection is complete\n * - `warmup`: triggered when warmup is complete\n * - `error`: triggered on some errors\n */\n events: EventTarget | undefined;\n /** Reference face triangualtion array of 468 points, used for triangle references between points */\n faceTriangulation: number[];\n /** Refernce UV map of 468 values, used for 3D mapping of the face mesh */\n faceUVMap: [number, number][];\n /** Performance object that contains values for all recently performed operations */\n performance: Record; // perf members are dynamically defined as needed\n #numTensors: number;\n #analyzeMemoryLeaks: boolean;\n #checkSanity: boolean;\n /** WebGL debug info */\n gl: Record