mediapipe-nodejs
Version:
A Node.js library for running MediaPipe models that are typically browser-only. This package uses a local Express (web) server and Playwright (headless browser) to bridge the gap between Node.js and MediaPipe's browser-based APIs.
95 lines (94 loc) • 4.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tasks_vision_1 = require("@mediapipe/tasks-vision");
async function createFaceLandmarker(options) {
const filesetResolver = await tasks_vision_1.FilesetResolver.forVisionTasks('/npm/@mediapipe/tasks-vision/wasm');
let faceLandmarker = await tasks_vision_1.FaceLandmarker.createFromOptions(filesetResolver, {
baseOptions: {
modelAssetPath: `/saved_models/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task`,
delegate: 'GPU',
},
outputFaceBlendshapes: true,
runningMode: 'IMAGE',
numFaces: options.numFaces,
});
return faceLandmarker;
}
let faceLandmarkerCache = new Map();
let getFaceLandmarker = (numFaces) => {
let faceLandmarker = faceLandmarkerCache.get(numFaces);
if (!faceLandmarker) {
faceLandmarker = createFaceLandmarker({ numFaces });
faceLandmarkerCache.set(numFaces, faceLandmarker);
}
return faceLandmarker;
};
async function detectFaceLandmarks(options) {
let num_faces = options.num_faces ?? 1;
let faceLandmarker = await getFaceLandmarker(num_faces);
let image = document.createElement('img');
image_list.textContent = '';
image_list.appendChild(image);
await new Promise((resolve, reject) => {
image.onload = resolve;
image.onerror = reject;
image.src = options.image_url;
});
let target = image;
if (options.crop_region || options.rotation || options.draw_landmarks) {
let crop_region = options.crop_region || {
left: 0,
top: 0,
right: 1,
bottom: 1,
};
let width = crop_region.right - crop_region.left;
let height = crop_region.bottom - crop_region.top;
let canvas = document.createElement('canvas');
canvas.width = image.naturalWidth * width;
canvas.height = image.naturalHeight * height;
let context = canvas.getContext('2d');
context.drawImage(image, image.naturalWidth * crop_region.left, image.naturalHeight * crop_region.top, image.naturalWidth * width, image.naturalHeight * height, 0, 0, canvas.width, canvas.height);
if (options.rotation) {
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate((options.rotation * Math.PI) / 180);
context.drawImage(canvas, -canvas.width / 2, -canvas.height / 2);
context.rotate(-(options.rotation * Math.PI) / 180);
context.translate(-canvas.width / 2, -canvas.height / 2);
}
image_list.appendChild(canvas);
target = canvas;
}
let results = faceLandmarker.detect(target);
if (options.draw_landmarks || options.draw_bounding_box) {
let canvas = target;
let context = canvas.getContext('2d');
let color = options.draw_style ?? 'red';
let size = options.draw_size ?? 5;
context.fillStyle = color;
context.strokeStyle = color;
context.lineWidth = size;
for (let faceLandmarks of results.faceLandmarks) {
if (options.draw_bounding_box) {
let xs = faceLandmarks.map(landmark => landmark.x);
let ys = faceLandmarks.map(landmark => landmark.y);
let min_x = Math.min(...xs);
let max_x = Math.max(...xs);
let min_y = Math.min(...ys);
let max_y = Math.max(...ys);
let width = max_x - min_x;
let height = max_y - min_y;
context.strokeRect(min_x * canvas.width, min_y * canvas.height, width * canvas.width, height * canvas.height);
}
if (options.draw_landmarks) {
for (let landmark of faceLandmarks) {
context.fillRect(landmark.x * canvas.width, landmark.y * canvas.height, size, size);
}
}
}
}
return results;
}
Object.assign(window, {
detectFaceLandmarks,
});