UNPKG

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
"use strict"; 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, });