type definitions

pull/280/head
Vladimir Mandic 2021-03-17 18:23:19 -04:00
parent f796d39e60
commit b0ed511405
9 changed files with 314 additions and 95 deletions

View File

@ -1,6 +1,6 @@
# @vladmandic/human # @vladmandic/human
Version: **1.1.8** Version: **1.1.9**
Description: **Human: AI-powered 3D Face Detection, Face Embedding & Recognition, Body Pose Tracking, Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction & Gesture Recognition** Description: **Human: AI-powered 3D Face Detection, Face Embedding & Recognition, Body Pose Tracking, Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction & Gesture Recognition**
Author: **Vladimir Mandic <mandic00@live.com>** Author: **Vladimir Mandic <mandic00@live.com>**
@ -11,6 +11,10 @@ Repository: **<git+https://github.com/vladmandic/human.git>**
### **HEAD -> main** 2021/03/17 mandic00@live.com ### **HEAD -> main** 2021/03/17 mandic00@live.com
### **1.1.9** 2021/03/17 mandic00@live.com
- fix box clamping and raw output
- hierarchical readme notes - hierarchical readme notes
### **1.1.8** 2021/03/17 mandic00@live.com ### **1.1.8** 2021/03/17 mandic00@live.com

View File

@ -6,7 +6,7 @@
"main": "dist/human.node.js", "main": "dist/human.node.js",
"module": "dist/human.esm.js", "module": "dist/human.esm.js",
"browser": "dist/human.esm.js", "browser": "dist/human.esm.js",
"types": "types/src/human.d.ts", "types": "types/human.d.ts",
"author": "Vladimir Mandic <mandic00@live.com>", "author": "Vladimir Mandic <mandic00@live.com>",
"bugs": { "bugs": {
"url": "https://github.com/vladmandic/human/issues" "url": "https://github.com/vladmandic/human/issues"

View File

@ -1,7 +1,121 @@
/* eslint-disable indent */ /* eslint-disable indent */
/* eslint-disable no-multi-spaces */ /* eslint-disable no-multi-spaces */
export default { /**
* Configuration interface definition for **Human** library
*
* Contains all configurable parameters
*/
export interface Config {
backend: String,
wasmPath: String,
debug: Boolean,
async: Boolean,
profile: Boolean,
deallocate: Boolean,
scoped: Boolean,
videoOptimized: Boolean,
warmup: String,
filter: {
enabled: Boolean,
width: Number,
height: Number,
return: Boolean,
brightness: Number,
contrast: Number,
sharpness: Number,
blur: Number
saturation: Number,
hue: Number,
negative: Boolean,
sepia: Boolean,
vintage: Boolean,
kodachrome: Boolean,
technicolor: Boolean,
polaroid: Boolean,
pixelate: Number,
},
gesture: {
enabled: Boolean,
},
face: {
enabled: Boolean,
detector: {
modelPath: String,
rotation: Boolean,
maxFaces: Number,
skipFrames: Number,
skipInitial: Boolean,
minConfidence: Number,
iouThreshold: Number,
scoreThreshold: Number,
return: Boolean,
},
mesh: {
enabled: Boolean,
modelPath: String,
},
iris: {
enabled: Boolean,
modelPath: String,
},
age: {
enabled: Boolean,
modelPath: String,
skipFrames: Number,
},
gender: {
enabled: Boolean,
minConfidence: Number,
modelPath: String,
skipFrames: Number,
},
emotion: {
enabled: Boolean,
minConfidence: Number,
skipFrames: Number,
modelPath: String,
},
embedding: {
enabled: Boolean,
modelPath: String,
},
},
body: {
enabled: Boolean,
modelPath: String,
maxDetections: Number,
scoreThreshold: Number,
nmsRadius: Number,
},
hand: {
enabled: Boolean,
rotation: Boolean,
skipFrames: Number,
skipInitial: Boolean,
minConfidence: Number,
iouThreshold: Number,
scoreThreshold: Number,
maxHands: Number,
landmarks: Boolean,
detector: {
modelPath: String,
},
skeleton: {
modelPath: String,
},
},
object: {
enabled: Boolean,
modelPath: String,
minConfidence: Number,
iouThreshold: Number,
maxResults: Number,
skipFrames: Number,
},
}
const config: Config = {
backend: 'webgl', // select tfjs backend to use backend: 'webgl', // select tfjs backend to use
// can be 'webgl', 'wasm', 'cpu', or 'humangl' which is a custom version of webgl // can be 'webgl', 'wasm', 'cpu', or 'humangl' which is a custom version of webgl
// leave as empty string to continue using default backend // leave as empty string to continue using default backend
@ -177,3 +291,4 @@ export default {
skipFrames: 13, // how many frames to go without re-running the detector skipFrames: 13, // how many frames to go without re-running the detector
}, },
}; };
export { config as defaults };

View File

@ -1,5 +1,5 @@
import config from '../config'; import { defaults } from '../config';
import { TRI468 as triangulation } from './blazeface/coords'; import { TRI468 as triangulation } from '../blazeface/coords';
export const drawOptions = { export const drawOptions = {
color: <string>'rgba(173, 216, 230, 0.3)', // 'lightblue' with light alpha channel color: <string>'rgba(173, 216, 230, 0.3)', // 'lightblue' with light alpha channel
@ -86,7 +86,7 @@ function curves(ctx, points: number[] = []) {
} }
} }
export async function gesture(inCanvas, result) { export async function gesture(inCanvas: HTMLCanvasElement, result: Array<any>) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
const ctx = inCanvas.getContext('2d'); const ctx = inCanvas.getContext('2d');
@ -112,7 +112,7 @@ export async function gesture(inCanvas, result) {
} }
} }
export async function face(inCanvas, result) { export async function face(inCanvas: HTMLCanvasElement, result: Array<any>) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
const ctx = inCanvas.getContext('2d'); const ctx = inCanvas.getContext('2d');
@ -196,7 +196,7 @@ export async function face(inCanvas, result) {
} }
const lastDrawnPose:any[] = []; const lastDrawnPose:any[] = [];
export async function body(inCanvas, result) { export async function body(inCanvas: HTMLCanvasElement, result: Array<any>) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
const ctx = inCanvas.getContext('2d'); const ctx = inCanvas.getContext('2d');
@ -232,70 +232,70 @@ export async function body(inCanvas, result) {
// torso // torso
points.length = 0; points.length = 0;
part = result[i].keypoints.find((a) => a.part === 'leftShoulder'); part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightShoulder'); part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightHip'); part = result[i].keypoints.find((a) => a.part === 'rightHip');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftHip'); part = result[i].keypoints.find((a) => a.part === 'leftHip');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftShoulder'); part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
if (points.length === 5) lines(ctx, points); // only draw if we have complete torso if (points.length === 5) lines(ctx, points); // only draw if we have complete torso
// leg left // leg left
points.length = 0; points.length = 0;
part = result[i].keypoints.find((a) => a.part === 'leftHip'); part = result[i].keypoints.find((a) => a.part === 'leftHip');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftKnee'); part = result[i].keypoints.find((a) => a.part === 'leftKnee');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftAnkle'); part = result[i].keypoints.find((a) => a.part === 'leftAnkle');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftHeel'); part = result[i].keypoints.find((a) => a.part === 'leftHeel');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftFoot'); part = result[i].keypoints.find((a) => a.part === 'leftFoot');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
curves(ctx, points); curves(ctx, points);
// leg right // leg right
points.length = 0; points.length = 0;
part = result[i].keypoints.find((a) => a.part === 'rightHip'); part = result[i].keypoints.find((a) => a.part === 'rightHip');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightKnee'); part = result[i].keypoints.find((a) => a.part === 'rightKnee');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightAnkle'); part = result[i].keypoints.find((a) => a.part === 'rightAnkle');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightHeel'); part = result[i].keypoints.find((a) => a.part === 'rightHeel');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightFoot'); part = result[i].keypoints.find((a) => a.part === 'rightFoot');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
curves(ctx, points); curves(ctx, points);
// arm left // arm left
points.length = 0; points.length = 0;
part = result[i].keypoints.find((a) => a.part === 'leftShoulder'); part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftElbow'); part = result[i].keypoints.find((a) => a.part === 'leftElbow');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftWrist'); part = result[i].keypoints.find((a) => a.part === 'leftWrist');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'leftPalm'); part = result[i].keypoints.find((a) => a.part === 'leftPalm');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
curves(ctx, points); curves(ctx, points);
// arm right // arm right
points.length = 0; points.length = 0;
part = result[i].keypoints.find((a) => a.part === 'rightShoulder'); part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightElbow'); part = result[i].keypoints.find((a) => a.part === 'rightElbow');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightWrist'); part = result[i].keypoints.find((a) => a.part === 'rightWrist');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
part = result[i].keypoints.find((a) => a.part === 'rightPalm'); part = result[i].keypoints.find((a) => a.part === 'rightPalm');
if (part && part.score > config.body.scoreThreshold) points.push([part.position.x, part.position.y]); if (part && part.score > defaults.body.scoreThreshold) points.push([part.position.x, part.position.y]);
curves(ctx, points); curves(ctx, points);
// draw all // draw all
} }
} }
} }
export async function hand(inCanvas, result) { export async function hand(inCanvas: HTMLCanvasElement, result: Array<any>) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
const ctx = inCanvas.getContext('2d'); const ctx = inCanvas.getContext('2d');
@ -348,7 +348,7 @@ export async function hand(inCanvas, result) {
} }
} }
export async function object(inCanvas, result) { export async function object(inCanvas: HTMLCanvasElement, result: Array<any>) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
const ctx = inCanvas.getContext('2d'); const ctx = inCanvas.getContext('2d');
@ -375,14 +375,14 @@ export async function object(inCanvas, result) {
} }
} }
export async function canvas(inCanvas, outCanvas) { export async function canvas(inCanvas: HTMLCanvasElement, outCanvas: HTMLCanvasElement) {
if (!inCanvas || !outCanvas) return; if (!inCanvas || !outCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement) || !(outCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement) || !(outCanvas instanceof HTMLCanvasElement)) return;
const outCtx = inCanvas.getContext('2d'); const outCtx = inCanvas.getContext('2d');
outCtx?.drawImage(inCanvas, 0, 0); outCtx?.drawImage(inCanvas, 0, 0);
} }
export async function all(inCanvas, result) { export async function all(inCanvas: HTMLCanvasElement, result:any) {
if (!result || !inCanvas) return; if (!result || !inCanvas) return;
if (!(inCanvas instanceof HTMLCanvasElement)) return; if (!(inCanvas instanceof HTMLCanvasElement)) return;
face(inCanvas, result.face); face(inCanvas, result.face);

View File

@ -12,12 +12,25 @@ import * as handpose from './handpose/handpose';
import * as blazepose from './blazepose/blazepose'; import * as blazepose from './blazepose/blazepose';
import * as nanodet from './nanodet/nanodet'; import * as nanodet from './nanodet/nanodet';
import * as gesture from './gesture/gesture'; import * as gesture from './gesture/gesture';
import * as image from './image'; import * as image from './image/image';
import * as draw from './draw/draw';
import * as profile from './profile'; import * as profile from './profile';
import * as config from '../config'; import { Config, defaults } from './config';
import { Result } from './result';
import * as sample from './sample'; import * as sample from './sample';
import * as app from '../package.json'; import * as app from '../package.json';
import * as draw from './draw';
type Tensor = {};
type Model = {};
export type { Config } from './config';
export type { Result } from './result';
/** Defines all possible input types for **Human** detection */
export type Input = Tensor | ImageData | ImageBitmap | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas;
/** Error message */
export type Error = { error: String };
export type TensorFlow = typeof tf;
// helper function: gets elapsed time on both browser and nodejs // helper function: gets elapsed time on both browser and nodejs
const now = () => { const now = () => {
@ -25,50 +38,6 @@ const now = () => {
return parseInt((Number(process.hrtime.bigint()) / 1000 / 1000).toString()); return parseInt((Number(process.hrtime.bigint()) / 1000 / 1000).toString());
}; };
type Tensor = {};
type Model = {};
export type Result = {
face: Array<{
confidence: Number,
boxConfidence: Number,
faceConfidence: Number,
box: [Number, Number, Number, Number],
mesh: Array<[Number, Number, Number]>
meshRaw: Array<[Number, Number, Number]>
boxRaw: [Number, Number, Number, Number],
annotations: Array<{ part: String, points: Array<[Number, Number, Number]>[] }>,
age: Number,
gender: String,
genderConfidence: Number,
emotion: Array<{ score: Number, emotion: String }>,
embedding: Array<Number>,
iris: Number,
angle: { roll: Number, yaw: Number, pitch: Number },
}>,
body: Array<{
id: Number,
part: String,
position: { x: Number, y: Number, z: Number },
score: Number,
presence: Number }>,
hand: Array<{
confidence: Number,
box: [Number, Number, Number, Number],
boxRaw: [Number, Number, Number, Number],
landmarks: Array<[Number, Number, Number]>,
annotations: Array<{ part: String, points: Array<[Number, Number, Number]>[] }>,
}>,
gesture: Array<{
part: String,
gesture: String,
}>,
object: Array<{ score: Number, strideSize: Number, class: Number, label: String, center: Number[], centerRaw: Number[], box: Number[], boxRaw: Number[] }>,
performance: { any },
canvas: OffscreenCanvas | HTMLCanvasElement,
}
export type { default as Config } from '../config';
// helper function: perform deep merge of multiple objects so it allows full inheriance with overrides // helper function: perform deep merge of multiple objects so it allows full inheriance with overrides
function mergeDeep(...objects) { function mergeDeep(...objects) {
const isObject = (obj) => obj && typeof obj === 'object'; const isObject = (obj) => obj && typeof obj === 'object';
@ -83,15 +52,31 @@ function mergeDeep(...objects) {
return prev; return prev;
}, {}); }, {});
} }
/**
* Main Class for `Human` library
*
* All methods and properties are available only as members of Human class
*
* Configuration object definition: @link Config
* Results object definition: @link Result
* Possible inputs: @link Input
*/
export class Human { export class Human {
version: String; version: String;
config: typeof config.default; config: Config;
state: String; state: String;
image: { tensor: Tensor, canvas: OffscreenCanvas | HTMLCanvasElement }; image: { tensor: Tensor, canvas: OffscreenCanvas | HTMLCanvasElement };
// classes // classes
tf: typeof tf; tf: TensorFlow;
draw: { drawOptions?: typeof draw.drawOptions, gesture: Function, face: Function, body: Function, hand: Function, canvas: Function, all: Function }; draw: {
drawOptions?: typeof draw.drawOptions,
gesture: typeof draw.gesture,
face: typeof draw.face,
body: typeof draw.body,
hand: typeof draw.hand,
canvas: typeof draw.canvas,
all: typeof draw.all,
};
// models // models
models: { models: {
face: facemesh.MediaPipeFaceMesh | null, face: facemesh.MediaPipeFaceMesh | null,
@ -123,12 +108,12 @@ export class Human {
#firstRun: Boolean; #firstRun: Boolean;
// definition end // definition end
constructor(userConfig = {}) { constructor(userConfig: Config | Object = {}) {
this.tf = tf; this.tf = tf;
this.draw = draw; this.draw = draw;
this.#package = app; this.#package = app;
this.version = app.version; this.version = app.version;
this.config = mergeDeep(config.default, userConfig); this.config = mergeDeep(defaults, userConfig);
this.state = 'idle'; this.state = 'idle';
this.#numTensors = 0; this.#numTensors = 0;
this.#analyzeMemoryLeaks = false; this.#analyzeMemoryLeaks = false;
@ -150,7 +135,7 @@ export class Human {
}; };
// export access to image processing // export access to image processing
// @ts-ignore // @ts-ignore
this.image = (input: Tensor | ImageData | HTMLCanvasElement | HTMLVideoElement | OffscreenCanvas) => image.process(input, this.config); this.image = (input: Input) => image.process(input, this.config);
// export raw access to underlying models // export raw access to underlying models
this.classes = { this.classes = {
facemesh, facemesh,
@ -211,7 +196,7 @@ export class Human {
} }
// preload models, not explicitly required as it's done automatically on first use // preload models, not explicitly required as it's done automatically on first use
async load(userConfig: Object = {}) { async load(userConfig: Config | Object = {}) {
this.state = 'load'; this.state = 'load';
const timeStamp = now(); const timeStamp = now();
if (userConfig) this.config = mergeDeep(this.config, userConfig); if (userConfig) this.config = mergeDeep(this.config, userConfig);
@ -480,7 +465,7 @@ export class Human {
} }
// main detect function // main detect function
async detect(input: Tensor | ImageData | HTMLCanvasElement | HTMLVideoElement | OffscreenCanvas, userConfig: Object = {}): Promise<Result | { error: String }> { async detect(input: Input, userConfig: Config | Object = {}): Promise<Result | Error> {
// detection happens inside a promise // detection happens inside a promise
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
this.state = 'config'; this.state = 'config';
@ -675,7 +660,7 @@ export class Human {
return res; return res;
} }
async warmup(userConfig: Object = {}): Promise<Result | { error }> { async warmup(userConfig: Config | Object = {}): Promise<Result | { error }> {
const t0 = now(); const t0 = now();
if (userConfig) this.config = mergeDeep(this.config, userConfig); if (userConfig) this.config = mergeDeep(this.config, userConfig);
const save = this.config.videoOptimized; const save = this.config.videoOptimized;
@ -691,4 +676,7 @@ export class Human {
} }
} }
/**
* Class Human is also available as default export
*/
export { Human as default }; export { Human as default };

View File

@ -1,7 +1,7 @@
// @ts-nocheck // @ts-nocheck
import { log } from './log'; import { log } from '../log';
import * as tf from '../dist/tfjs.esm.js'; import * as tf from '../../dist/tfjs.esm.js';
import * as fxImage from './imagefx'; import * as fxImage from './imagefx';
const maxSize = 2048; const maxSize = 2048;

112
src/result.ts Normal file
View File

@ -0,0 +1,112 @@
/**
* Result interface definition for **Human** library
*
* Contains all possible detection results
*/
export interface Result {
/** Face results
* Combined results of face detector, face mesh, age, gender, emotion, embedding, iris models
* Some values may be null if specific model is not enabled
*
* Array of individual results with one object per detected face
* Each result has:
* - overal detection confidence value
* - box detection confidence value
* - mesh detection confidence value
* - box as array of [x, y, width, height], normalized to image resolution
* - boxRaw as array of [x, y, width, height], normalized to range 0..1
* - mesh as array of [x, y, z] points of face mesh, normalized to image resolution
* - meshRaw as array of [x, y, z] points of face mesh, normalized to range 0..1
* - annotations as array of annotated face mesh points
* - age as value
* - gender as value
* - genderConfidence as value
* - emotion as array of possible emotions with their individual scores
* - iris as distance value
* - angle as object with values for roll, yaw and pitch angles
*/
face: Array<{
confidence: Number,
boxConfidence: Number,
faceConfidence: Number,
box: [Number, Number, Number, Number],
boxRaw: [Number, Number, Number, Number],
mesh: Array<[Number, Number, Number]>
meshRaw: Array<[Number, Number, Number]>
annotations: Array<{ part: String, points: Array<[Number, Number, Number]>[] }>,
age: Number,
gender: String,
genderConfidence: Number,
emotion: Array<{ score: Number, emotion: String }>,
embedding: Array<Number>,
iris: Number,
angle: { roll: Number, yaw: Number, pitch: Number },
}>,
/** Body results
*
* Array of individual results with one object per detected body
* Each results has:
* - body id number
* - body part name
* - part position with x,y,z coordinates
* - body part score value
* - body part presence value
*/
body: Array<{
id: Number,
part: String,
position: { x: Number, y: Number, z: Number },
score: Number,
presence: Number }>,
/** Hand results
*
* Array of individual results with one object per detected hand
* Each result has:
* - confidence as value
* - box as array of [x, y, width, height], normalized to image resolution
* - boxRaw as array of [x, y, width, height], normalized to range 0..1
* - landmarks as array of [x, y, z] points of hand, normalized to image resolution
* - annotations as array of annotated face landmark points
*/
hand: Array<{
confidence: Number,
box: [Number, Number, Number, Number],
boxRaw: [Number, Number, Number, Number],
landmarks: Array<[Number, Number, Number]>,
annotations: Array<{ part: String, points: Array<[Number, Number, Number]>[] }>,
}>,
/** Gesture results
*
* Array of individual results with one object per detected gesture
* Each result has:
* - part where gesture was detected
* - gesture detected
*/
gesture: Array<{
part: String,
gesture: String,
}>,
/** Object results
*
* Array of individual results with one object per detected gesture
* Each result has:
* - score as value
* - label as detected class name
* - center as array of [x, y], normalized to image resolution
* - centerRaw as array of [x, y], normalized to range 0..1
* - box as array of [x, y, width, height], normalized to image resolution
* - boxRaw as array of [x, y, width, height], normalized to range 0..1
*/
object: Array<{
score: Number,
strideSize: Number,
class: Number,
label: String,
center: Number[],
centerRaw: Number[],
box: Number[],
boxRaw: Number[],
}>,
performance: { any },
canvas: OffscreenCanvas | HTMLCanvasElement,
}

2
wiki

@ -1 +1 @@
Subproject commit 91af84e9543762c4f31be41dda15fb2a5549d8a6 Subproject commit 936e36ce0fd331030b48db735836381f2f22134a