UNPKG

detection-lib

Version:

> A modular JavaScript detection library for face, QR, and future detection types with a clean, unified API.

114 lines (100 loc) 3.45 kB
import { Detector } from './types.js'; import { FaceDetection } from '@mediapipe/face_detection'; import ImageUrl from './image.png'; import { FACE_DETECTOR_CODES } from './faceDetectorCodes.js'; export class FaceDetector extends Detector { constructor() { super(); this.modelReady = false; this.initializationPromise = null; // To track initialization state this.faceDetector = new FaceDetection({ locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/face_detection/${file}`, }); this.faceDetector.setOptions({ model: 'short', minDetectionConfidence: 0.8, }); this.faceDetector.onResults((results) => { this._latestResults = results; }); } /** * Initializes and warms up the FaceDetector model. * This method must be called and awaited before calling `detect()`. * @returns {Promise<void>} */ async initialize() { if (this.modelReady) { return; } if (this.initializationPromise) { // If initialization is already in progress or complete, return the existing promise. return this.initializationPromise; } // Start the initialization and store the promise this.initializationPromise = (async () => { try { // console.log("FaceDetector: Starting asynchronous initialization with static image..."); const img = new Image(); img.src = ImageUrl; await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = (e) => { console.error("Failed to load warm-up image:", e); reject(new Error("Failed to load warm-up image.")); }; }); // console.log("FaceDetector: Sending static warm-up image..."); await this.faceDetector.send({ image: img }); // console.log("FaceDetector: Warm-up complete. Model is now ready."); this.modelReady = true; } catch (error) { console.error("FaceDetector: Initialization failed!", error); this.modelReady = false; this.initializationPromise = null; throw error; } })(); return this.initializationPromise; } /** * @param {HTMLVideoElement|HTMLImageElement|HTMLCanvasElement} input * @returns {Promise<{status: number, message: string, boxes: Array}>} */ async detect(input) { if (!this.modelReady) { const {code,message}= FACE_DETECTOR_CODES.NOT_INITIALIZED; return { status: code, message, boxes: [] }; } try{ await this.faceDetector.send({ image: input }); }catch(e){ const {code,message}= FACE_DETECTOR_CODES.MODEL_ERROR; return { status: code, message, boxes: [] }; } const results = this._latestResults; const detections = results?.detections || []; let statusObj = FACE_DETECTOR_CODES.OK; if (detections.length === 0) { statusObj = FACE_DETECTOR_CODES.NO_FACE_DETECTED; } else if (detections.length > 1) { statusObj = FACE_DETECTOR_CODES.MULTIPLE_FACES_DETECTED; } const boxes = detections.map((det) => { const b = det.boundingBox; return { x: b.xCenter - b.width / 2, y: b.yCenter - b.height / 2, w: b.width, h: b.height, score: det.score?.[0] || undefined, }; }); return { status: statusObj.code, message: statusObj.message, boxes, }; } }