human/src/blazeface/facemesh.ts

67 lines
3.0 KiB
TypeScript
Raw Normal View History

2021-02-08 17:39:09 +01:00
import { log } from '../log';
2020-11-18 14:26:28 +01:00
import * as tf from '../../dist/tfjs.esm.js';
2021-02-13 15:16:41 +01:00
import * as blazeface from './blazeface';
import * as facepipeline from './facepipeline';
import * as coords from './coords';
2020-10-12 01:22:43 +02:00
2021-02-08 17:39:09 +01:00
export class MediaPipeFaceMesh {
facePipeline: any;
config: any;
2020-10-12 01:22:43 +02:00
constructor(blazeFace, blazeMeshModel, irisModel, config) {
2020-12-10 20:47:53 +01:00
this.facePipeline = new facepipeline.Pipeline(blazeFace, blazeMeshModel, irisModel, config);
this.config = config;
2020-10-12 01:22:43 +02:00
}
async estimateFaces(input, config) {
2020-12-10 20:47:53 +01:00
const predictions = await this.facePipeline.predict(input, config);
2021-02-08 18:47:38 +01:00
const results: Array<{}> = [];
2020-10-13 04:01:35 +02:00
for (const prediction of (predictions || [])) {
2020-12-10 20:47:53 +01:00
if (prediction.isDisposedInternal) continue; // guard against disposed tensors on long running operations such as pause in middle of processing
2020-11-06 19:50:16 +01:00
const mesh = prediction.coords ? prediction.coords.arraySync() : null;
const meshRaw = prediction.rawCoords;
2020-11-06 19:50:16 +01:00
const annotations = {};
if (mesh && mesh.length > 0) {
2021-03-03 15:59:04 +01:00
for (const key of Object.keys(coords.MESH_ANNOTATIONS)) annotations[key] = coords.MESH_ANNOTATIONS[key].map((index) => mesh[index]);
2020-10-12 01:22:43 +02:00
}
const boxRaw = (prediction.box) ? { topLeft: prediction.box.startPoint, bottomRight: prediction.box.endPoint } : null;
const box = prediction.box ? [
Math.max(0, prediction.box.startPoint[0]),
Math.max(0, prediction.box.startPoint[1]),
Math.min(input.shape[2], prediction.box.endPoint[0]) - prediction.box.startPoint[0],
Math.min(input.shape[1], prediction.box.endPoint[1]) - prediction.box.startPoint[1],
] : 0;
2021-03-03 15:59:04 +01:00
results.push({
confidence: prediction.faceConfidence || prediction.boxConfidence || 0,
boxConfidence: prediction.boxConfidence,
faceConfidence: prediction.faceConfidence,
box,
mesh,
boxRaw,
meshRaw,
annotations,
image: prediction.image ? tf.clone(prediction.image) : null,
});
if (prediction.coords) prediction.coords.dispose();
if (prediction.image) prediction.image.dispose();
2020-10-12 01:22:43 +02:00
}
return results;
}
}
2020-10-13 04:01:35 +02:00
2020-11-24 04:55:01 +01:00
let faceModels = [null, null, null];
2021-02-08 17:39:09 +01:00
export async function load(config) {
2021-02-08 18:47:38 +01:00
// @ts-ignore
2020-11-24 04:55:01 +01:00
faceModels = await Promise.all([
(!faceModels[0] && config.face.enabled) ? blazeface.load(config) : null,
(!faceModels[1] && config.face.mesh.enabled) ? tf.loadGraphModel(config.face.mesh.modelPath, { fromTFHub: config.face.mesh.modelPath.includes('tfhub.dev') }) : null,
(!faceModels[2] && config.face.iris.enabled) ? tf.loadGraphModel(config.face.iris.modelPath, { fromTFHub: config.face.iris.modelPath.includes('tfhub.dev') }) : null,
2020-10-13 04:01:35 +02:00
]);
2020-11-24 04:55:01 +01:00
const faceMesh = new MediaPipeFaceMesh(faceModels[0], faceModels[1], faceModels[2], config);
2021-03-02 17:27:42 +01:00
if (config.face.mesh.enabled && config.debug) log(`load model: ${config.face.mesh.modelPath.match(/\/(.*)\./)[1]}`);
if (config.face.iris.enabled && config.debug) log(`load model: ${config.face.iris.modelPath.match(/\/(.*)\./)[1]}`);
2020-10-13 04:01:35 +02:00
return faceMesh;
}
2020-11-12 20:52:32 +01:00
exports.triangulation = coords.TRI468;