'use strict'; let face = null, hands = null, vision = null; let lastTime = 0; async function prepare(c) { if (!vision) { const module = await import('/vendor/mediapipe/vision_bundle.mjs'); const files = await module.FilesetResolver.forVisionTasks('/vendor/mediapipe/wasm'); vision = {module,files}; } if (c.rosto && !face) face = await vision.module.FaceDetector.createFromOptions(vision.files, { baseOptions:{modelAssetPath:'/vendor/mediapipe/face.tflite',delegate:'CPU'},runningMode:'VIDEO',minDetectionConfidence:.6}); if (c.maos && !hands) hands = await vision.module.HandLandmarker.createFromOptions(vision.files, { baseOptions:{modelAssetPath:'/vendor/mediapipe/hands.task',delegate:'CPU'},runningMode:'VIDEO',numHands:2, minHandDetectionConfidence:.6,minHandPresenceConfidence:.6,minTrackingConfidence:.5}); } self.onmessage = async ({data}) => { const bitmap = data.bitmap; try { await prepare(data.config); if (!bitmap) { self.postMessage({tipo:'pronto'}); return; } const began = performance.now(); const stamp = Math.max(data.timestamp,lastTime+1); lastTime=stamp; const faces = data.config.rosto ? face.detectForVideo(bitmap,stamp).detections.map(d=>({ x:d.boundingBox.originX/bitmap.width,y:d.boundingBox.originY/bitmap.height, w:d.boundingBox.width/bitmap.width,h:d.boundingBox.height/bitmap.height, score:d.categories[0]?.score || 0,pontos:d.keypoints.map(p=>({x:p.x,y:p.y})) })) : []; const result = data.config.maos ? hands.detectForVideo(bitmap,stamp) : {landmarks:[]}; self.postMessage({tipo:'resultado',faces,hands:result.landmarks.map(h=>h.map(p=>({x:p.x,y:p.y}))), ms:performance.now()-began,timestamp:data.timestamp}); } catch(e) { self.postMessage({tipo:'erro',erro:e.message || String(e)}); } finally { if (bitmap) bitmap.close(); } };