import * as faceapi from "face-api.js"; let isFaceModuleLoaded = false; export const loadModels = async() => { if(isFaceModuleLoaded){ return; } const MODEL_URL = '/models' await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL) await faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL) await faceapi.nets.faceLandmark68TinyNet.loadFromUri(MODEL_URL) isFaceModuleLoaded = true; console.info("model loaded"); } export const detectWithTimeout = async (video,timeout) => { if(!timeout) timeout = 5000; // 默认超时时间为5秒 const detectionPromise = faceapi.detectAllFaces( video, new faceapi.TinyFaceDetectorOptions({ inputSize: 320, scoreThreshold: 0.5, }) ).withFaceLandmarks(); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("检测超时")), timeout) ); return await Promise.race([detectionPromise, timeoutPromise]); } export const detectSingleWithTimeout = async (video,canvas, timeout) => { if(!timeout) timeout = 5000; // 默认超时时间为5秒 const detectionPromise = faceapi.detectSingleFace( video, new faceapi.TinyFaceDetectorOptions({ inputSize: 320, scoreThreshold: 0.5, }) ).withFaceLandmarks(true); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("检测超时")), timeout) ); return await Promise.race([detectionPromise, timeoutPromise]); } // 正脸判断逻辑 export const isFaceFrontal = (landmarks) =>{ const leftEye = landmarks.getLeftEye() const rightEye = landmarks.getRightEye() const nose = landmarks.getNose() const eyeYDiff = Math.abs(leftEye[0].y - rightEye[3].y) const noseCenterX = nose[3].x const eyeCenterX = (leftEye[0].x + rightEye[3].x) / 2 const noseXDiff = Math.abs(noseCenterX - eyeCenterX) return eyeYDiff < 10 && noseXDiff < 20 } export const isFaceCentered = (video, box) => { const faceCenterX = box.x + box.width / 2 const faceCenterY = box.y + box.height / 2 const screenCenterX = video.videoWidth / 2 const screenCenterY = video.videoHeight / 2 const offsetX = Math.abs(faceCenterX - screenCenterX) const offsetY = Math.abs(faceCenterY - screenCenterY) const isCentered = offsetX < 50 && offsetY < 50 return isCentered; }