diff --git a/CHANGELOG.md b/CHANGELOG.md index 80fd473b..9ffe3250 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ ## Changelog -### **HEAD -> main** 2021/10/19 mandic00@live.com +### **HEAD -> main** 2021/10/21 mandic00@live.com +- add human.custom.esm using custom tfjs build ### **2.3.5** 2021/10/19 mandic00@live.com diff --git a/demo/index.js b/demo/index.js index 5a7b125c..49b684c7 100644 --- a/demo/index.js +++ b/demo/index.js @@ -63,6 +63,7 @@ const drawOptions = { drawBoxes: true, drawGaze: true, drawLabels: true, + drawGestures: true, drawPolygons: true, drawPoints: false, fillPolygons: false, diff --git a/src/face/face.ts b/src/face/face.ts index ed91b016..add1b255 100644 --- a/src/face/face.ts +++ b/src/face/face.ts @@ -48,11 +48,11 @@ export const detectFace = async (parent /* instance of human */, input: Tensor): // run emotion, inherits face from blazeface parent.analyze('Start Emotion:'); if (parent.config.async) { - emotionRes = parent.config.face.emotion.enabled ? emotion.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : {}; + emotionRes = parent.config.face.emotion.enabled ? emotion.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; } else { parent.state = 'run:emotion'; timeStamp = now(); - emotionRes = parent.config.face.emotion.enabled ? await emotion.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : {}; + emotionRes = parent.config.face.emotion.enabled ? await emotion.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; parent.performance.emotion = Math.trunc(now() - timeStamp); } parent.analyze('End Emotion:'); @@ -60,11 +60,11 @@ export const detectFace = async (parent /* instance of human */, input: Tensor): // run antispoof, inherits face from blazeface parent.analyze('Start AntiSpoof:'); if (parent.config.async) { - antispoofRes = parent.config.face.antispoof.enabled ? antispoof.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : {}; + antispoofRes = parent.config.face.antispoof.enabled ? antispoof.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; } else { parent.state = 'run:antispoof'; timeStamp = now(); - antispoofRes = parent.config.face.antispoof.enabled ? await antispoof.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : {}; + antispoofRes = parent.config.face.antispoof.enabled ? await antispoof.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; parent.performance.antispoof = Math.trunc(now() - timeStamp); } parent.analyze('End AntiSpoof:'); @@ -86,11 +86,11 @@ export const detectFace = async (parent /* instance of human */, input: Tensor): // run emotion, inherits face from blazeface parent.analyze('Start Description:'); if (parent.config.async) { - descRes = parent.config.face.description.enabled ? faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : []; + descRes = parent.config.face.description.enabled ? faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; } else { parent.state = 'run:description'; timeStamp = now(); - descRes = parent.config.face.description.enabled ? await faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : []; + descRes = parent.config.face.description.enabled ? await faceres.predict(faces[i].tensor || tf.tensor([]), parent.config, i, faces.length) : null; parent.performance.embedding = Math.trunc(now() - timeStamp); } parent.analyze('End Description:'); @@ -124,10 +124,10 @@ export const detectFace = async (parent /* instance of human */, input: Tensor): faceRes.push({ ...faces[i], id: i, - age: descRes.age, - gender: descRes.gender, - genderScore: descRes.genderScore, - embedding: descRes.descriptor, + age: descRes?.age, + gender: descRes?.gender, + genderScore: descRes?.genderScore, + embedding: descRes?.descriptor, emotion: emotionRes, real: antispoofRes, iris: irisSize !== 0 ? Math.trunc(500 / irisSize / 11.7) / 100 : 0, diff --git a/src/human.ts b/src/human.ts index 197a4aef..4854d47b 100644 --- a/src/human.ts +++ b/src/human.ts @@ -135,7 +135,7 @@ export class Human { * - canvas: draw processed canvas which is a processed copy of the input * - all: meta-function that performs: canvas, face, body, hand */ - draw: { canvas, face, body, hand, gesture, object, person, all, options: DrawOptions }; + 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 }; /** Currently loaded models * @internal diff --git a/src/util/draw.ts b/src/util/draw.ts index 96edbf1b..f39bb32d 100644 --- a/src/util/draw.ts +++ b/src/util/draw.ts @@ -37,6 +37,7 @@ export interface DrawOptions { roundRect: number, drawPoints: boolean, drawLabels: boolean, + drawGestures: boolean, drawBoxes: boolean, drawPolygons: boolean, drawGaze: boolean, @@ -58,6 +59,7 @@ export const options: DrawOptions = { drawPoints: false, drawLabels: true, drawBoxes: true, + drawGestures: true, drawPolygons: true, drawGaze: true, fillPolygons: false, @@ -166,24 +168,26 @@ function arrow(ctx: CanvasRenderingContext2D, from: Point, to: Point, radius = 5 export async function gesture(inCanvas: HTMLCanvasElement | OffscreenCanvas, result: Array, drawOptions?: Partial) { const localOptions = mergeDeep(options, drawOptions); if (!result || !inCanvas) return; - const ctx = getCanvasContext(inCanvas); - ctx.font = localOptions.font; - ctx.fillStyle = localOptions.color; - let i = 1; - for (let j = 0; j < result.length; j++) { - let where: unknown[] = []; // what&where is a record - let what: unknown[] = []; // what&where is a record - [where, what] = Object.entries(result[j]); - if ((what.length > 1) && ((what[1] as string).length > 0)) { - const who = where[1] as number > 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)); + if (localOptions.drawGestures) { + const ctx = getCanvasContext(inCanvas); + ctx.font = localOptions.font; + ctx.fillStyle = localOptions.color; + let i = 1; + for (let j = 0; j < result.length; j++) { + let where: unknown[] = []; // what&where is a record + let what: unknown[] = []; // what&where is a record + [where, what] = Object.entries(result[j]); + if ((what.length > 1) && ((what[1] as string).length > 0)) { + const who = where[1] as number > 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; } - ctx.fillStyle = localOptions.labelColor; - ctx.fillText(label, 6, 0 + (i * localOptions.lineHeight)); - i += 1; } } } @@ -197,33 +201,35 @@ export async function face(inCanvas: HTMLCanvasElement | OffscreenCanvas, result ctx.strokeStyle = localOptions.color; ctx.fillStyle = localOptions.color; if (localOptions.drawBoxes) rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3], localOptions); - // silly hack since fillText does not suport new line - const labels:string[] = []; - labels.push(`face: ${Math.trunc(100 * f.score)}%`); - if (f.genderScore) labels.push(`${f.gender || ''} ${Math.trunc(100 * f.genderScore)}%`); - if (f.age) labels.push(`age: ${f.age || ''}`); - if (f.iris) labels.push(`distance: ${f.iris}`); - if (f.real) labels.push(`Real: ${Math.trunc(100 * f.real)}%`); - if (f.emotion && f.emotion.length > 0) { - const emotion = f.emotion.map((a) => `${Math.trunc(100 * a.score)}% ${a.emotion}`); - if (emotion.length > 3) emotion.length = 3; - labels.push(emotion.join(' ')); - } - if (f.rotation && f.rotation.angle && f.rotation.gaze) { - if (f.rotation.angle.roll) labels.push(`roll: ${rad2deg(f.rotation.angle.roll)}° yaw:${rad2deg(f.rotation.angle.yaw)}° pitch:${rad2deg(f.rotation.angle.pitch)}°`); - if (f.rotation.gaze.bearing) labels.push(`gaze: ${rad2deg(f.rotation.gaze.bearing)}°`); - } - if (labels.length === 0) labels.push('face'); - ctx.fillStyle = localOptions.color; - for (let i = labels.length - 1; i >= 0; i--) { - const x = Math.max(f.box[0], 0); - const y = i * localOptions.lineHeight + f.box[1]; - if (localOptions.shadowColor && localOptions.shadowColor !== '') { - ctx.fillStyle = localOptions.shadowColor; - ctx.fillText(labels[i], x + 5, y + 16); + if (localOptions.drawLabels) { + // silly hack since fillText does not suport new line + const labels:string[] = []; + labels.push(`face: ${Math.trunc(100 * f.score)}%`); + if (f.genderScore) labels.push(`${f.gender || ''} ${Math.trunc(100 * f.genderScore)}%`); + if (f.age) labels.push(`age: ${f.age || ''}`); + if (f.iris) labels.push(`distance: ${f.iris}`); + if (f.real) labels.push(`real: ${Math.trunc(100 * f.real)}%`); + if (f.emotion && f.emotion.length > 0) { + const emotion = f.emotion.map((a) => `${Math.trunc(100 * a.score)}% ${a.emotion}`); + if (emotion.length > 3) emotion.length = 3; + labels.push(emotion.join(' ')); + } + if (f.rotation && f.rotation.angle && f.rotation.gaze) { + if (f.rotation.angle.roll) labels.push(`roll: ${rad2deg(f.rotation.angle.roll)}° yaw:${rad2deg(f.rotation.angle.yaw)}° pitch:${rad2deg(f.rotation.angle.pitch)}°`); + if (f.rotation.gaze.bearing) labels.push(`gaze: ${rad2deg(f.rotation.gaze.bearing)}°`); + } + if (labels.length === 0) labels.push('face'); + ctx.fillStyle = localOptions.color; + for (let i = labels.length - 1; i >= 0; i--) { + const x = Math.max(f.box[0], 0); + const y = i * localOptions.lineHeight + f.box[1]; + if (localOptions.shadowColor && localOptions.shadowColor !== '') { + ctx.fillStyle = localOptions.shadowColor; + ctx.fillText(labels[i], x + 5, y + 16); + } + ctx.fillStyle = localOptions.labelColor; + ctx.fillText(labels[i], x + 4, y + 15); } - ctx.fillStyle = localOptions.labelColor; - ctx.fillText(labels[i], x + 4, y + 15); } ctx.lineWidth = 1; if (f.mesh && f.mesh.length > 0) {