shaderpad 1.0.0-beta.33 → 1.0.0-beta.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/plugins/face.js +28 -32
- package/dist/plugins/face.js.map +1 -1
- package/dist/plugins/face.mjs +27 -31
- package/dist/plugins/face.mjs.map +1 -1
- package/dist/plugins/pose.js +4 -4
- package/dist/plugins/pose.js.map +1 -1
- package/dist/plugins/pose.mjs +2 -2
- package/dist/plugins/pose.mjs.map +1 -1
- package/dist/plugins/segmenter.js +5 -5
- package/dist/plugins/segmenter.js.map +1 -1
- package/dist/plugins/segmenter.mjs +5 -5
- package/dist/plugins/segmenter.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -415,7 +415,8 @@ All region functions return `vec2(confidence, faceIndex)`. faceIndex is 0-indexe
|
|
|
415
415
|
- `rightEyebrowAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in right eyebrow, `vec2(0.0, -1.0)` otherwise.
|
|
416
416
|
- `leftEyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in left eye, `vec2(0.0, -1.0)` otherwise.
|
|
417
417
|
- `rightEyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in right eye, `vec2(0.0, -1.0)` otherwise.
|
|
418
|
-
- `
|
|
418
|
+
- `lipsAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in lips, `vec2(0.0, -1.0)` otherwise.
|
|
419
|
+
- `outerMouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in outer mouth (lips + inner mouth), `vec2(0.0, -1.0)` otherwise.
|
|
419
420
|
- `innerMouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in inner mouth region, `vec2(0.0, -1.0)` otherwise.
|
|
420
421
|
- `faceOvalAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face oval contour, `vec2(0.0, -1.0)` otherwise.
|
|
421
422
|
- `faceAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face mesh or oval contour, `vec2(0.0, -1.0)` otherwise.
|
|
@@ -427,8 +428,9 @@ All region functions return `vec2(confidence, faceIndex)`. faceIndex is 0-indexe
|
|
|
427
428
|
- `inFace(vec2 pos) -> float` - Returns `1.0` if position is in face mesh, `0.0` otherwise.
|
|
428
429
|
- `inEye(vec2 pos) -> float` - Returns `1.0` if position is in either eye, `0.0` otherwise.
|
|
429
430
|
- `inEyebrow(vec2 pos) -> float` - Returns `1.0` if position is in either eyebrow, `0.0` otherwise.
|
|
430
|
-
- `
|
|
431
|
-
- `
|
|
431
|
+
- `inOuterMouth(vec2 pos) -> float` - Returns `1.0` if position is in outer mouth (lips + inner mouth), `0.0` otherwise.
|
|
432
|
+
- `inInnerMouth(vec2 pos) -> float` - Returns `1.0` if position is in inner mouth, `0.0` otherwise.
|
|
433
|
+
- `inLips(vec2 pos) -> float` - Returns `1.0` if position is in lips, `0.0` otherwise.
|
|
432
434
|
|
|
433
435
|
**Landmark Constants:**
|
|
434
436
|
|
package/dist/plugins/face.js
CHANGED
|
@@ -1,56 +1,50 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ce=Object.create;var b=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var le=Object.getPrototypeOf,Ee=Object.prototype.hasOwnProperty;var fe=(r,i)=>{for(var c in i)b(r,c,{get:i[c],enumerable:!0})},V=(r,i,c,O)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of ue(i))!Ee.call(r,s)&&s!==c&&b(r,s,{get:()=>i[s],enumerable:!(O=se(i,s))||O.enumerable});return r};var me=(r,i,c)=>(c=r!=null?ce(le(r)):{},V(i||!r||!r.__esModule?b(c,"default",{value:r,enumerable:!0}):c,r)),de=r=>V(b({},"__esModule",{value:!0}),r);var Re={};fe(Re,{default:()=>Fe});module.exports=de(Re);var C=478,_e=2,m=C+_e,X=[336,296,334,293,300,276,283,282,295,285],j=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],q=[70,63,105,66,107,55,65,52,53,46],Z=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],J=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],$=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],Te=Array.from({length:C},(r,i)=>i),g={LEFT_EYEBROW:X,LEFT_EYE:j,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:q,RIGHT_EYE:Z,RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_MOUTH:J,INNER_MOUTH:$,FACE_CENTER:C,MOUTH_CENTER:C+1},Q=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","OUTER_MOUTH","INNER_MOUTH"],ee=Q.length-1,F=Object.fromEntries(Q.map((r,i)=>[r,i/ee])),z=.5/ee;function pe(r){let{textureName:i,options:c}=r,O="https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";return function(s,te){let{injectGLSL:ne,gl:M}=te,d=null,y=null,H=-1,L="VIDEO",S=new Map,D=c?.maxFaces??1,R=512,U=0,a=null,Y=new OffscreenCanvas(1,1),_=document.createElement("canvas"),E=_.getContext("2d",{willReadFrequently:!0});E.imageSmoothingEnabled=!1;let w=null,G=null;async function ae(){try{let{FilesetResolver:t,FaceLandmarker:e}=await import("@mediapipe/tasks-vision");y=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),d=await e.createFromOptions(y,{baseOptions:{modelAssetPath:c?.modelPath||O,delegate:"GPU"},canvas:Y,runningMode:L,numFaces:c?.maxFaces??1,minFaceDetectionConfidence:c?.minFaceDetectionConfidence??.5,minFacePresenceConfidence:c?.minFacePresenceConfidence??.5,minTrackingConfidence:c?.minTrackingConfidence??.5,outputFaceBlendshapes:c?.outputFaceBlendshapes??!1,outputFacialTransformationMatrixes:c?.outputFacialTransformationMatrixes??!1}),w=e.FACE_LANDMARKS_TESSELATION.map(({start:u})=>u),G=e.FACE_LANDMARKS_FACE_OVAL.map(({start:u})=>u)}catch(t){throw console.error("[Face Plugin] Failed to initialize:",t),t}}function B(t,e,u){let o=1/0,n=-1/0,p=1/0,l=-1/0,f=0,x=0;for(let ie of u){let k=(e*m+ie)*4,K=t[k],W=t[k+1];o=Math.min(o,K),n=Math.max(n,K),p=Math.min(p,W),l=Math.max(l,W),f+=t[k+2],x+=t[k+3]}let h=(o+n)/2,I=(p+l)/2,N=f/u.length,v=x/u.length;return[h,I,N,v]}function T(t,e,u){if(!a)return;let{width:o,height:n}=_;E.fillStyle=`rgb(${u.r}, ${u.g}, ${u.b})`,E.beginPath();let p=(t*m+e[0])*4;E.moveTo(a[p]*o,a[p+1]*n);for(let l=1;l<e.length;++l){let f=(t*m+e[l])*4;E.lineTo(a[f]*o,a[f+1]*n)}E.closePath(),E.fill()}function oe(t){if(!a||!w||!G)return;let{width:e,height:u}=_;E.clearRect(0,0,e,u),E.save(),E.globalCompositeOperation="lighten";for(let o=0;o<t;++o){let n=Math.round((o+1)/D*255);T(o,w,{r:0,g:128,b:n}),T(o,G,{r:0,g:255,b:n}),T(o,X,{r:Math.round(F.LEFT_EYEBROW*255),g:0,b:n}),T(o,q,{r:Math.round(F.RIGHT_EYEBROW*255),g:0,b:n}),T(o,j,{r:Math.round(F.LEFT_EYE*255),g:0,b:n}),T(o,Z,{r:Math.round(F.RIGHT_EYE*255),g:0,b:n}),T(o,J,{r:Math.round(F.OUTER_MOUTH*255),g:0,b:n}),T(o,$,{r:Math.round(F.INNER_MOUTH*255),g:0,b:n})}E.restore(),s.updateTextures({u_faceMask:_})}function re(t){if(!a)return;let e=t.length,u=e*m;for(let n=0;n<e;++n){let p=t[n];for(let I=0;I<C;++I){let N=p[I],v=(n*m+I)*4;a[v]=N.x,a[v+1]=1-N.y,a[v+2]=N.z??0,a[v+3]=N.visibility??1}let l=B(a,n,Te),f=(n*m+g.FACE_CENTER)*4;a[f]=l[0],a[f+1]=l[1],a[f+2]=l[2],a[f+3]=l[3];let x=B(a,n,$),h=(n*m+g.MOUTH_CENTER)*4;a[h]=x[0],a[h+1]=x[1],a[h+2]=x[2],a[h+3]=x[3]}let o=Math.ceil(u/R);s.updateTextures({u_faceLandmarksTex:{data:a,width:R,height:o,isPartial:!0}})}function P(t){if(!t.faceLandmarks||!a)return;_.width=Y.width,_.height=Y.height;let e=t.faceLandmarks.length;re(t.faceLandmarks),oe(e),s.updateUniforms({u_nFaces:e}),c?.onResults?.(t)}s.registerHook("init",async()=>{s.initializeTexture("u_faceMask",_,{preserveY:!0,minFilter:M.NEAREST,magFilter:M.NEAREST}),s.initializeUniform("u_maxFaces","int",D),s.initializeUniform("u_nFaces","int",0);let t=D*m;U=Math.ceil(t/R);let e=R*U*4;a=new Float32Array(e),s.initializeTexture("u_faceLandmarksTex",{data:a,width:R,height:U},{internalFormat:M.RGBA32F,type:M.FLOAT,minFilter:M.NEAREST,magFilter:M.NEAREST}),await ae()}),s.registerHook("updateTextures",async t=>{let e=t[i];if(!(!e||(S.get(i)!==e&&(H=-1),S.set(i,e),!d)))try{let o=e instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(L!==o&&(L=o,await d.setOptions({runningMode:L})),e instanceof HTMLVideoElement){if(e.videoWidth===0||e.videoHeight===0||e.readyState<2)return;if(e.currentTime!==H){H=e.currentTime;let n=d.detectForVideo(e,performance.now());P(n)}}else if(e instanceof HTMLImageElement||e instanceof HTMLCanvasElement){if(e.width===0||e.height===0)return;let n=d.detect(e);P(n)}}catch(o){console.error("[Face Plugin] Detection error:",o)}}),s.registerHook("destroy",()=>{d&&(d.close(),d=null),y=null,S.clear(),_.remove(),a=null});let A=(t,e=t)=>`vec4 mask = texture(u_faceMask, pos);
|
|
2
|
+
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
3
|
+
return (mask.r > ${(F[t]-z).toFixed(4)} && mask.r < ${(F[e]+z).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;ne(`
|
|
2
4
|
uniform int u_maxFaces;
|
|
3
5
|
uniform int u_nFaces;
|
|
4
6
|
uniform sampler2D u_faceLandmarksTex;
|
|
5
7
|
uniform sampler2D u_faceMask;
|
|
6
8
|
|
|
7
|
-
#define FACE_LANDMARK_L_EYE_CENTER ${
|
|
8
|
-
#define FACE_LANDMARK_R_EYE_CENTER ${
|
|
9
|
-
#define FACE_LANDMARK_NOSE_TIP ${
|
|
10
|
-
#define FACE_LANDMARK_FACE_CENTER ${
|
|
11
|
-
#define FACE_LANDMARK_MOUTH_CENTER ${
|
|
9
|
+
#define FACE_LANDMARK_L_EYE_CENTER ${g.LEFT_EYE_CENTER}
|
|
10
|
+
#define FACE_LANDMARK_R_EYE_CENTER ${g.RIGHT_EYE_CENTER}
|
|
11
|
+
#define FACE_LANDMARK_NOSE_TIP ${g.NOSE_TIP}
|
|
12
|
+
#define FACE_LANDMARK_FACE_CENTER ${g.FACE_CENTER}
|
|
13
|
+
#define FACE_LANDMARK_MOUTH_CENTER ${g.MOUTH_CENTER}
|
|
12
14
|
|
|
13
15
|
vec4 faceLandmark(int faceIndex, int landmarkIndex) {
|
|
14
|
-
int i = faceIndex * ${
|
|
15
|
-
int x = i % ${
|
|
16
|
-
int y = i / ${
|
|
16
|
+
int i = faceIndex * ${m} + landmarkIndex;
|
|
17
|
+
int x = i % ${R};
|
|
18
|
+
int y = i / ${R};
|
|
17
19
|
return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
vec2 leftEyebrowAt(vec2 pos) {
|
|
21
|
-
|
|
22
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
23
|
-
return ${M(f.LEFT_EYEBROW)};
|
|
23
|
+
${A("LEFT_EYEBROW")}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
vec2 rightEyebrowAt(vec2 pos) {
|
|
27
|
-
|
|
28
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
29
|
-
return ${M(f.RIGHT_EYEBROW)};
|
|
27
|
+
${A("RIGHT_EYEBROW")}
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
vec2 leftEyeAt(vec2 pos) {
|
|
33
|
-
|
|
34
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
35
|
-
return ${M(f.LEFT_EYE)};
|
|
31
|
+
${A("LEFT_EYE")}
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
vec2 rightEyeAt(vec2 pos) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
${A("RIGHT_EYE")}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
vec2 lipsAt(vec2 pos) {
|
|
39
|
+
${A("OUTER_MOUTH")}
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
vec2 outerMouthAt(vec2 pos) {
|
|
45
|
-
|
|
46
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
47
|
-
return ${M(f.OUTER_MOUTH)};
|
|
43
|
+
${A("OUTER_MOUTH","INNER_MOUTH")}
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
vec2 innerMouthAt(vec2 pos) {
|
|
51
|
-
|
|
52
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
53
|
-
return ${M(f.INNER_MOUTH)};
|
|
47
|
+
${A("INNER_MOUTH")}
|
|
54
48
|
}
|
|
55
49
|
|
|
56
50
|
vec2 faceOvalAt(vec2 pos) {
|
|
@@ -86,17 +80,19 @@ float inEye(vec2 pos) {
|
|
|
86
80
|
return eyeAt(pos).x;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
float
|
|
83
|
+
float inOuterMouth(vec2 pos) {
|
|
84
|
+
return outerMouthAt(pos).x;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
float inInnerMouth(vec2 pos) {
|
|
90
88
|
return innerMouthAt(pos).x;
|
|
91
89
|
}
|
|
92
90
|
|
|
93
91
|
float inLips(vec2 pos) {
|
|
94
|
-
|
|
95
|
-
float mouth = innerMouthAt(pos).x;
|
|
96
|
-
return max(0.0, lips - mouth);
|
|
92
|
+
return lipsAt(pos).x;
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
float inFace(vec2 pos) {
|
|
100
96
|
return faceAt(pos).x;
|
|
101
|
-
}`)}}var pe
|
|
97
|
+
}`)}}var Fe=pe;
|
|
102
98
|
//# sourceMappingURL=face.js.map
|
package/dist/plugins/face.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\tonResults?: (results: FaceLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n// Face region types for R channel encoding (evenly spaced 0-1).\nconst FACE_REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = FACE_REGION_NAMES.length - 1;\nconst FACE_REGION = Object.fromEntries(FACE_REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof FACE_REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\n\t\tlet faceTesselationIndices: number[] | null = null;\n\t\tlet faceOvalIndices: number[] | null = null;\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\n\t\t\t\tfaceTesselationIndices = FaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => start);\n\t\t\t\tfaceOvalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction fillRegion(\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[],\n\t\t\tcolor: { r: number; g: number; b: number }\n\t\t) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;\n\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tconst originIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[0]) * 4;\n\t\t\tfaceMaskCtx.moveTo(landmarksDataArray[originIdx] * width, landmarksDataArray[originIdx + 1] * height);\n\n\t\t\tfor (let i = 1; i < landmarkIndices.length; ++i) {\n\t\t\t\tconst destIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[i]) * 4;\n\t\t\t\tfaceMaskCtx.lineTo(landmarksDataArray[destIdx] * width, landmarksDataArray[destIdx + 1] * height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!landmarksDataArray || !faceTesselationIndices || !faceOvalIndices) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.clearRect(0, 0, width, height);\n\n\t\t\tfaceMaskCtx.save();\n\t\t\tfaceMaskCtx.globalCompositeOperation = 'lighten';\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = Math.round(((faceIdx + 1) / maxFaces) * 255);\n\n\t\t\t\t// Draw face regions in order (features on top).\n\t\t\t\t// First: face mesh and oval (for G channel - face mask).\n\t\t\t\tfillRegion(faceIdx, faceTesselationIndices, { r: 0, g: 128, b });\n\t\t\t\tfillRegion(faceIdx, faceOvalIndices, { r: 0, g: 255, b });\n\n\t\t\t\t// Then: specific regions (for R channel - region type).\n\t\t\t\tfillRegion(faceIdx, LEFT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.LEFT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, LEFT_EYE_INDICES, { r: Math.round(FACE_REGION.LEFT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYE_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, OUTER_MOUTH_INDICES, { r: Math.round(FACE_REGION.OUTER_MOUTH * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, INNER_MOUTH_INDICES, { r: Math.round(FACE_REGION.INNER_MOUTH * 255), g: 0, b });\n\t\t\t}\n\n\t\t\tfaceMaskCtx.restore();\n\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tconst faceCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[faceCenterIdx] = faceCenter[0];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 1] = faceCenter[1];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 2] = faceCenter[2];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 3] = faceCenter[3];\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tconst mouthCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[mouthCenterIdx] = mouthCenter[0];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 1] = mouthCenter[1];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 2] = mouthCenter[2];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 3] = mouthCenter[3];\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processFaceResults(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tfaceMaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tfaceMaskCanvas.height = mediaPipeCanvas.height;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas, { preserveY: true });\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!faceLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst regionCheck = (val: number) =>\n\t\t\t`(mask.r > ${(val - HALF_GAP).toFixed(4)} && mask.r < ${(val + HALF_GAP).toFixed(\n\t\t\t\t4\n\t\t\t)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0)`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.LEFT_EYEBROW)};\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.RIGHT_EYEBROW)};\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.LEFT_EYE)};\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.RIGHT_EYE)};\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.OUTER_MOUTH)};\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.INNER_MOUTH)};\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\tvec2 right = rightEyeAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\tvec2 right = rightEyebrowAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nfloat inEyebrow(vec2 pos) {\n\treturn eyebrowAt(pos).x;\n}\n\nfloat inEye(vec2 pos) {\n\treturn eyeAt(pos).x;\n}\n\nfloat inMouth(vec2 pos) {\n\treturn innerMouthAt(pos).x;\n}\n\nfloat inLips(vec2 pos) {\n\tfloat lips = outerMouthAt(pos).x;\n\tfloat mouth = innerMouthAt(pos).x;\n\treturn max(0.0, lips - mouth);\n}\n\nfloat inFace(vec2 pos) {\n\treturn faceAt(pos).x;\n}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","FACE_REGION_NAMES","nFaceRegions","FACE_REGION","name","HALF_GAP","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","faceMaskCanvas","faceMaskCtx","faceTesselationIndices","faceOvalIndices","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","start","error","calculateBoundingBoxCenter","faceIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","fillRegion","color","width","height","originIdx","destIdx","updateMaskTexture","nFaces","b","updateLandmarksTexture","faces","totalLandmarks","landmarks","lmIdx","landmark","faceCenter","faceCenterIdx","mouthCenter","mouthCenterIdx","rowsToUpdate","processFaceResults","result","textureSize","updates","source","requiredMode","regionCheck","val"]}
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\tonResults?: (results: FaceLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n// Face region types for R channel encoding (evenly spaced 0-1).\nconst FACE_REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = FACE_REGION_NAMES.length - 1;\nconst FACE_REGION = Object.fromEntries(FACE_REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof FACE_REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d', { willReadFrequently: true })!;\n\t\tfaceMaskCtx.imageSmoothingEnabled = false;\n\n\t\tlet faceTesselationIndices: number[] | null = null;\n\t\tlet faceOvalIndices: number[] | null = null;\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\n\t\t\t\tfaceTesselationIndices = FaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => start);\n\t\t\t\tfaceOvalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction fillRegion(\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[],\n\t\t\tcolor: { r: number; g: number; b: number }\n\t\t) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;\n\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tconst originIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[0]) * 4;\n\t\t\tfaceMaskCtx.moveTo(landmarksDataArray[originIdx] * width, landmarksDataArray[originIdx + 1] * height);\n\n\t\t\tfor (let i = 1; i < landmarkIndices.length; ++i) {\n\t\t\t\tconst destIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[i]) * 4;\n\t\t\t\tfaceMaskCtx.lineTo(landmarksDataArray[destIdx] * width, landmarksDataArray[destIdx + 1] * height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!landmarksDataArray || !faceTesselationIndices || !faceOvalIndices) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.clearRect(0, 0, width, height);\n\n\t\t\tfaceMaskCtx.save();\n\t\t\tfaceMaskCtx.globalCompositeOperation = 'lighten';\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = Math.round(((faceIdx + 1) / maxFaces) * 255);\n\n\t\t\t\t// Draw face regions in order (features on top).\n\t\t\t\t// First: face mesh and oval (for G channel - face mask).\n\t\t\t\tfillRegion(faceIdx, faceTesselationIndices, { r: 0, g: 128, b });\n\t\t\t\tfillRegion(faceIdx, faceOvalIndices, { r: 0, g: 255, b });\n\n\t\t\t\t// Then: specific regions (for R channel - region type).\n\t\t\t\tfillRegion(faceIdx, LEFT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.LEFT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, LEFT_EYE_INDICES, { r: Math.round(FACE_REGION.LEFT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYE_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, OUTER_MOUTH_INDICES, { r: Math.round(FACE_REGION.OUTER_MOUTH * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, INNER_MOUTH_INDICES, { r: Math.round(FACE_REGION.INNER_MOUTH * 255), g: 0, b });\n\t\t\t}\n\n\t\t\tfaceMaskCtx.restore();\n\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tconst faceCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[faceCenterIdx] = faceCenter[0];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 1] = faceCenter[1];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 2] = faceCenter[2];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 3] = faceCenter[3];\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tconst mouthCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[mouthCenterIdx] = mouthCenter[0];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 1] = mouthCenter[1];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 2] = mouthCenter[2];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 3] = mouthCenter[3];\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processFaceResults(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tfaceMaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tfaceMaskCanvas.height = mediaPipeCanvas.height;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas, {\n\t\t\t\tpreserveY: true,\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t});\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!faceLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst checkAt = (regionMin: keyof typeof FACE_REGION, regionMax: keyof typeof FACE_REGION = regionMin) =>\n\t\t\t`vec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(FACE_REGION[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tFACE_REGION[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\t${checkAt('LEFT_EYEBROW')}\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\t${checkAt('RIGHT_EYEBROW')}\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\t${checkAt('LEFT_EYE')}\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\t${checkAt('RIGHT_EYE')}\n}\n\nvec2 lipsAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH')}\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH', 'INNER_MOUTH')}\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\t${checkAt('INNER_MOUTH')}\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\tvec2 right = rightEyeAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\tvec2 right = rightEyebrowAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nfloat inEyebrow(vec2 pos) {\n\treturn eyebrowAt(pos).x;\n}\n\nfloat inEye(vec2 pos) {\n\treturn eyeAt(pos).x;\n}\n\nfloat inOuterMouth(vec2 pos) {\n\treturn outerMouthAt(pos).x;\n}\n\nfloat inInnerMouth(vec2 pos) {\n\treturn innerMouthAt(pos).x;\n}\n\nfloat inLips(vec2 pos) {\n\treturn lipsAt(pos).x;\n}\n\nfloat inFace(vec2 pos) {\n\treturn faceAt(pos).x;\n}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","LANDMARK_INDICES","FACE_REGION_NAMES","nFaceRegions","FACE_REGION","name","HALF_GAP","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","faceMaskCanvas","faceMaskCtx","faceTesselationIndices","faceOvalIndices","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","start","error","calculateBoundingBoxCenter","faceIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","fillRegion","color","width","height","originIdx","i","destIdx","updateMaskTexture","nFaces","b","updateLandmarksTexture","faces","totalLandmarks","landmarks","lmIdx","landmark","faceCenter","faceCenterIdx","mouthCenter","mouthCenterIdx","rowsToUpdate","processFaceResults","result","textureSize","updates","source","requiredMode","checkAt","regionMin","regionMax"]}
|
package/dist/plugins/face.mjs
CHANGED
|
@@ -1,56 +1,50 @@
|
|
|
1
|
-
var v=478,re=2,E=v+re,W=[336,296,334,293,300,276,283,282,295,285],V=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],z=[70,63,105,66,107,55,65,52,53,46],X=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],j=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],w=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],
|
|
1
|
+
var v=478,re=2,E=v+re,W=[336,296,334,293,300,276,283,282,295,285],V=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],z=[70,63,105,66,107,55,65,52,53,46],X=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],j=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],w=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],ie=Array.from({length:v},(C,p)=>p),x={LEFT_EYEBROW:W,LEFT_EYE:V,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:z,RIGHT_EYE:X,RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_MOUTH:j,INNER_MOUTH:w,FACE_CENTER:v,MOUTH_CENTER:v+1},q=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","OUTER_MOUTH","INNER_MOUTH"],Z=q.length-1,T=Object.fromEntries(q.map((C,p)=>[C,p/Z])),K=.5/Z;function ce(C){let{textureName:p,options:l}=C,J="https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";return function(s,Q){let{injectGLSL:ee,gl:g}=Q,f=null,k=null,b=-1,O="VIDEO",y=new Map,H=l?.maxFaces??1,F=512,S=0,a=null,D=new OffscreenCanvas(1,1),m=document.createElement("canvas"),c=m.getContext("2d",{willReadFrequently:!0});c.imageSmoothingEnabled=!1;let U=null,Y=null;async function te(){try{let{FilesetResolver:t,FaceLandmarker:e}=await import("@mediapipe/tasks-vision");k=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),f=await e.createFromOptions(k,{baseOptions:{modelAssetPath:l?.modelPath||J,delegate:"GPU"},canvas:D,runningMode:O,numFaces:l?.maxFaces??1,minFaceDetectionConfidence:l?.minFaceDetectionConfidence??.5,minFacePresenceConfidence:l?.minFacePresenceConfidence??.5,minTrackingConfidence:l?.minTrackingConfidence??.5,outputFaceBlendshapes:l?.outputFaceBlendshapes??!1,outputFacialTransformationMatrixes:l?.outputFacialTransformationMatrixes??!1}),U=e.FACE_LANDMARKS_TESSELATION.map(({start:r})=>r),Y=e.FACE_LANDMARKS_FACE_OVAL.map(({start:r})=>r)}catch(t){throw console.error("[Face Plugin] Failed to initialize:",t),t}}function G(t,e,r){let o=1/0,n=-1/0,_=1/0,i=-1/0,u=0,A=0;for(let oe of r){let L=(e*E+oe)*4,B=t[L],P=t[L+1];o=Math.min(o,B),n=Math.max(n,B),_=Math.min(_,P),i=Math.max(i,P),u+=t[L+2],A+=t[L+3]}let M=(o+n)/2,h=(_+i)/2,I=u/r.length,N=A/r.length;return[M,h,I,N]}function d(t,e,r){if(!a)return;let{width:o,height:n}=m;c.fillStyle=`rgb(${r.r}, ${r.g}, ${r.b})`,c.beginPath();let _=(t*E+e[0])*4;c.moveTo(a[_]*o,a[_+1]*n);for(let i=1;i<e.length;++i){let u=(t*E+e[i])*4;c.lineTo(a[u]*o,a[u+1]*n)}c.closePath(),c.fill()}function ne(t){if(!a||!U||!Y)return;let{width:e,height:r}=m;c.clearRect(0,0,e,r),c.save(),c.globalCompositeOperation="lighten";for(let o=0;o<t;++o){let n=Math.round((o+1)/H*255);d(o,U,{r:0,g:128,b:n}),d(o,Y,{r:0,g:255,b:n}),d(o,W,{r:Math.round(T.LEFT_EYEBROW*255),g:0,b:n}),d(o,z,{r:Math.round(T.RIGHT_EYEBROW*255),g:0,b:n}),d(o,V,{r:Math.round(T.LEFT_EYE*255),g:0,b:n}),d(o,X,{r:Math.round(T.RIGHT_EYE*255),g:0,b:n}),d(o,j,{r:Math.round(T.OUTER_MOUTH*255),g:0,b:n}),d(o,w,{r:Math.round(T.INNER_MOUTH*255),g:0,b:n})}c.restore(),s.updateTextures({u_faceMask:m})}function ae(t){if(!a)return;let e=t.length,r=e*E;for(let n=0;n<e;++n){let _=t[n];for(let h=0;h<v;++h){let I=_[h],N=(n*E+h)*4;a[N]=I.x,a[N+1]=1-I.y,a[N+2]=I.z??0,a[N+3]=I.visibility??1}let i=G(a,n,ie),u=(n*E+x.FACE_CENTER)*4;a[u]=i[0],a[u+1]=i[1],a[u+2]=i[2],a[u+3]=i[3];let A=G(a,n,w),M=(n*E+x.MOUTH_CENTER)*4;a[M]=A[0],a[M+1]=A[1],a[M+2]=A[2],a[M+3]=A[3]}let o=Math.ceil(r/F);s.updateTextures({u_faceLandmarksTex:{data:a,width:F,height:o,isPartial:!0}})}function $(t){if(!t.faceLandmarks||!a)return;m.width=D.width,m.height=D.height;let e=t.faceLandmarks.length;ae(t.faceLandmarks),ne(e),s.updateUniforms({u_nFaces:e}),l?.onResults?.(t)}s.registerHook("init",async()=>{s.initializeTexture("u_faceMask",m,{preserveY:!0,minFilter:g.NEAREST,magFilter:g.NEAREST}),s.initializeUniform("u_maxFaces","int",H),s.initializeUniform("u_nFaces","int",0);let t=H*E;S=Math.ceil(t/F);let e=F*S*4;a=new Float32Array(e),s.initializeTexture("u_faceLandmarksTex",{data:a,width:F,height:S},{internalFormat:g.RGBA32F,type:g.FLOAT,minFilter:g.NEAREST,magFilter:g.NEAREST}),await te()}),s.registerHook("updateTextures",async t=>{let e=t[p];if(!(!e||(y.get(p)!==e&&(b=-1),y.set(p,e),!f)))try{let o=e instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(O!==o&&(O=o,await f.setOptions({runningMode:O})),e instanceof HTMLVideoElement){if(e.videoWidth===0||e.videoHeight===0||e.readyState<2)return;if(e.currentTime!==b){b=e.currentTime;let n=f.detectForVideo(e,performance.now());$(n)}}else if(e instanceof HTMLImageElement||e instanceof HTMLCanvasElement){if(e.width===0||e.height===0)return;let n=f.detect(e);$(n)}}catch(o){console.error("[Face Plugin] Detection error:",o)}}),s.registerHook("destroy",()=>{f&&(f.close(),f=null),k=null,y.clear(),m.remove(),a=null});let R=(t,e=t)=>`vec4 mask = texture(u_faceMask, pos);
|
|
2
|
+
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
3
|
+
return (mask.r > ${(T[t]-K).toFixed(4)} && mask.r < ${(T[e]+K).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;ee(`
|
|
2
4
|
uniform int u_maxFaces;
|
|
3
5
|
uniform int u_nFaces;
|
|
4
6
|
uniform sampler2D u_faceLandmarksTex;
|
|
5
7
|
uniform sampler2D u_faceMask;
|
|
6
8
|
|
|
7
|
-
#define FACE_LANDMARK_L_EYE_CENTER ${
|
|
8
|
-
#define FACE_LANDMARK_R_EYE_CENTER ${
|
|
9
|
-
#define FACE_LANDMARK_NOSE_TIP ${
|
|
10
|
-
#define FACE_LANDMARK_FACE_CENTER ${
|
|
11
|
-
#define FACE_LANDMARK_MOUTH_CENTER ${
|
|
9
|
+
#define FACE_LANDMARK_L_EYE_CENTER ${x.LEFT_EYE_CENTER}
|
|
10
|
+
#define FACE_LANDMARK_R_EYE_CENTER ${x.RIGHT_EYE_CENTER}
|
|
11
|
+
#define FACE_LANDMARK_NOSE_TIP ${x.NOSE_TIP}
|
|
12
|
+
#define FACE_LANDMARK_FACE_CENTER ${x.FACE_CENTER}
|
|
13
|
+
#define FACE_LANDMARK_MOUTH_CENTER ${x.MOUTH_CENTER}
|
|
12
14
|
|
|
13
15
|
vec4 faceLandmark(int faceIndex, int landmarkIndex) {
|
|
14
16
|
int i = faceIndex * ${E} + landmarkIndex;
|
|
15
|
-
int x = i % ${
|
|
16
|
-
int y = i / ${
|
|
17
|
+
int x = i % ${F};
|
|
18
|
+
int y = i / ${F};
|
|
17
19
|
return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
vec2 leftEyebrowAt(vec2 pos) {
|
|
21
|
-
|
|
22
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
23
|
-
return ${A(s.LEFT_EYEBROW)};
|
|
23
|
+
${R("LEFT_EYEBROW")}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
vec2 rightEyebrowAt(vec2 pos) {
|
|
27
|
-
|
|
28
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
29
|
-
return ${A(s.RIGHT_EYEBROW)};
|
|
27
|
+
${R("RIGHT_EYEBROW")}
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
vec2 leftEyeAt(vec2 pos) {
|
|
33
|
-
|
|
34
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
35
|
-
return ${A(s.LEFT_EYE)};
|
|
31
|
+
${R("LEFT_EYE")}
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
vec2 rightEyeAt(vec2 pos) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
${R("RIGHT_EYE")}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
vec2 lipsAt(vec2 pos) {
|
|
39
|
+
${R("OUTER_MOUTH")}
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
vec2 outerMouthAt(vec2 pos) {
|
|
45
|
-
|
|
46
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
47
|
-
return ${A(s.OUTER_MOUTH)};
|
|
43
|
+
${R("OUTER_MOUTH","INNER_MOUTH")}
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
vec2 innerMouthAt(vec2 pos) {
|
|
51
|
-
|
|
52
|
-
float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
|
|
53
|
-
return ${A(s.INNER_MOUTH)};
|
|
47
|
+
${R("INNER_MOUTH")}
|
|
54
48
|
}
|
|
55
49
|
|
|
56
50
|
vec2 faceOvalAt(vec2 pos) {
|
|
@@ -86,17 +80,19 @@ float inEye(vec2 pos) {
|
|
|
86
80
|
return eyeAt(pos).x;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
float
|
|
83
|
+
float inOuterMouth(vec2 pos) {
|
|
84
|
+
return outerMouthAt(pos).x;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
float inInnerMouth(vec2 pos) {
|
|
90
88
|
return innerMouthAt(pos).x;
|
|
91
89
|
}
|
|
92
90
|
|
|
93
91
|
float inLips(vec2 pos) {
|
|
94
|
-
|
|
95
|
-
float mouth = innerMouthAt(pos).x;
|
|
96
|
-
return max(0.0, lips - mouth);
|
|
92
|
+
return lipsAt(pos).x;
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
float inFace(vec2 pos) {
|
|
100
96
|
return faceAt(pos).x;
|
|
101
|
-
}`)}}var
|
|
97
|
+
}`)}}var se=ce;export{se as default};
|
|
102
98
|
//# sourceMappingURL=face.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\tonResults?: (results: FaceLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n// Face region types for R channel encoding (evenly spaced 0-1).\nconst FACE_REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = FACE_REGION_NAMES.length - 1;\nconst FACE_REGION = Object.fromEntries(FACE_REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof FACE_REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\n\t\tlet faceTesselationIndices: number[] | null = null;\n\t\tlet faceOvalIndices: number[] | null = null;\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\n\t\t\t\tfaceTesselationIndices = FaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => start);\n\t\t\t\tfaceOvalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction fillRegion(\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[],\n\t\t\tcolor: { r: number; g: number; b: number }\n\t\t) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;\n\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tconst originIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[0]) * 4;\n\t\t\tfaceMaskCtx.moveTo(landmarksDataArray[originIdx] * width, landmarksDataArray[originIdx + 1] * height);\n\n\t\t\tfor (let i = 1; i < landmarkIndices.length; ++i) {\n\t\t\t\tconst destIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[i]) * 4;\n\t\t\t\tfaceMaskCtx.lineTo(landmarksDataArray[destIdx] * width, landmarksDataArray[destIdx + 1] * height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!landmarksDataArray || !faceTesselationIndices || !faceOvalIndices) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.clearRect(0, 0, width, height);\n\n\t\t\tfaceMaskCtx.save();\n\t\t\tfaceMaskCtx.globalCompositeOperation = 'lighten';\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = Math.round(((faceIdx + 1) / maxFaces) * 255);\n\n\t\t\t\t// Draw face regions in order (features on top).\n\t\t\t\t// First: face mesh and oval (for G channel - face mask).\n\t\t\t\tfillRegion(faceIdx, faceTesselationIndices, { r: 0, g: 128, b });\n\t\t\t\tfillRegion(faceIdx, faceOvalIndices, { r: 0, g: 255, b });\n\n\t\t\t\t// Then: specific regions (for R channel - region type).\n\t\t\t\tfillRegion(faceIdx, LEFT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.LEFT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, LEFT_EYE_INDICES, { r: Math.round(FACE_REGION.LEFT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYE_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, OUTER_MOUTH_INDICES, { r: Math.round(FACE_REGION.OUTER_MOUTH * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, INNER_MOUTH_INDICES, { r: Math.round(FACE_REGION.INNER_MOUTH * 255), g: 0, b });\n\t\t\t}\n\n\t\t\tfaceMaskCtx.restore();\n\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tconst faceCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[faceCenterIdx] = faceCenter[0];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 1] = faceCenter[1];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 2] = faceCenter[2];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 3] = faceCenter[3];\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tconst mouthCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[mouthCenterIdx] = mouthCenter[0];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 1] = mouthCenter[1];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 2] = mouthCenter[2];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 3] = mouthCenter[3];\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processFaceResults(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tfaceMaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tfaceMaskCanvas.height = mediaPipeCanvas.height;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas, { preserveY: true });\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!faceLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst regionCheck = (val: number) =>\n\t\t\t`(mask.r > ${(val - HALF_GAP).toFixed(4)} && mask.r < ${(val + HALF_GAP).toFixed(\n\t\t\t\t4\n\t\t\t)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0)`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.LEFT_EYEBROW)};\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.RIGHT_EYEBROW)};\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.LEFT_EYE)};\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.RIGHT_EYE)};\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.OUTER_MOUTH)};\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn ${regionCheck(FACE_REGION.INNER_MOUTH)};\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\tvec2 right = rightEyeAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\tvec2 right = rightEyebrowAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nfloat inEyebrow(vec2 pos) {\n\treturn eyebrowAt(pos).x;\n}\n\nfloat inEye(vec2 pos) {\n\treturn eyeAt(pos).x;\n}\n\nfloat inMouth(vec2 pos) {\n\treturn innerMouthAt(pos).x;\n}\n\nfloat inLips(vec2 pos) {\n\tfloat lips = outerMouthAt(pos).x;\n\tfloat mouth = innerMouthAt(pos).x;\n\treturn max(0.0, lips - mouth);\n}\n\nfloat inFace(vec2 pos) {\n\treturn faceAt(pos).x;\n}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","FACE_REGION_NAMES","nFaceRegions","FACE_REGION","name","HALF_GAP","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","faceMaskCanvas","faceMaskCtx","faceTesselationIndices","faceOvalIndices","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","start","error","calculateBoundingBoxCenter","faceIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","fillRegion","color","width","height","originIdx","destIdx","updateMaskTexture","nFaces","b","updateLandmarksTexture","faces","totalLandmarks","landmarks","lmIdx","landmark","faceCenter","faceCenterIdx","mouthCenter","mouthCenterIdx","rowsToUpdate","processFaceResults","result","textureSize","updates","source","requiredMode","regionCheck","val","face_default"]}
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\tonResults?: (results: FaceLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n// Face region types for R channel encoding (evenly spaced 0-1).\nconst FACE_REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = FACE_REGION_NAMES.length - 1;\nconst FACE_REGION = Object.fromEntries(FACE_REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof FACE_REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d', { willReadFrequently: true })!;\n\t\tfaceMaskCtx.imageSmoothingEnabled = false;\n\n\t\tlet faceTesselationIndices: number[] | null = null;\n\t\tlet faceOvalIndices: number[] | null = null;\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\n\t\t\t\tfaceTesselationIndices = FaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => start);\n\t\t\t\tfaceOvalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction fillRegion(\n\t\t\tfaceIdx: number,\n\t\t\tlandmarkIndices: readonly number[] | number[],\n\t\t\tcolor: { r: number; g: number; b: number }\n\t\t) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;\n\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tconst originIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[0]) * 4;\n\t\t\tfaceMaskCtx.moveTo(landmarksDataArray[originIdx] * width, landmarksDataArray[originIdx + 1] * height);\n\n\t\t\tfor (let i = 1; i < landmarkIndices.length; ++i) {\n\t\t\t\tconst destIdx = (faceIdx * LANDMARK_COUNT + landmarkIndices[i]) * 4;\n\t\t\t\tfaceMaskCtx.lineTo(landmarksDataArray[destIdx] * width, landmarksDataArray[destIdx + 1] * height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!landmarksDataArray || !faceTesselationIndices || !faceOvalIndices) return;\n\n\t\t\tconst { width, height } = faceMaskCanvas;\n\t\t\tfaceMaskCtx.clearRect(0, 0, width, height);\n\n\t\t\tfaceMaskCtx.save();\n\t\t\tfaceMaskCtx.globalCompositeOperation = 'lighten';\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = Math.round(((faceIdx + 1) / maxFaces) * 255);\n\n\t\t\t\t// Draw face regions in order (features on top).\n\t\t\t\t// First: face mesh and oval (for G channel - face mask).\n\t\t\t\tfillRegion(faceIdx, faceTesselationIndices, { r: 0, g: 128, b });\n\t\t\t\tfillRegion(faceIdx, faceOvalIndices, { r: 0, g: 255, b });\n\n\t\t\t\t// Then: specific regions (for R channel - region type).\n\t\t\t\tfillRegion(faceIdx, LEFT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.LEFT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYEBROW_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYEBROW * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, LEFT_EYE_INDICES, { r: Math.round(FACE_REGION.LEFT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, RIGHT_EYE_INDICES, { r: Math.round(FACE_REGION.RIGHT_EYE * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, OUTER_MOUTH_INDICES, { r: Math.round(FACE_REGION.OUTER_MOUTH * 255), g: 0, b });\n\t\t\t\tfillRegion(faceIdx, INNER_MOUTH_INDICES, { r: Math.round(FACE_REGION.INNER_MOUTH * 255), g: 0, b });\n\t\t\t}\n\n\t\t\tfaceMaskCtx.restore();\n\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tconst faceCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[faceCenterIdx] = faceCenter[0];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 1] = faceCenter[1];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 2] = faceCenter[2];\n\t\t\t\tlandmarksDataArray[faceCenterIdx + 3] = faceCenter[3];\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tconst mouthCenterIdx = (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4;\n\t\t\t\tlandmarksDataArray[mouthCenterIdx] = mouthCenter[0];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 1] = mouthCenter[1];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 2] = mouthCenter[2];\n\t\t\t\tlandmarksDataArray[mouthCenterIdx + 3] = mouthCenter[3];\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processFaceResults(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tfaceMaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tfaceMaskCanvas.height = mediaPipeCanvas.height;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas, {\n\t\t\t\tpreserveY: true,\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t});\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!faceLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst checkAt = (regionMin: keyof typeof FACE_REGION, regionMax: keyof typeof FACE_REGION = regionMin) =>\n\t\t\t`vec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(FACE_REGION[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tFACE_REGION[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\t${checkAt('LEFT_EYEBROW')}\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\t${checkAt('RIGHT_EYEBROW')}\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\t${checkAt('LEFT_EYE')}\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\t${checkAt('RIGHT_EYE')}\n}\n\nvec2 lipsAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH')}\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH', 'INNER_MOUTH')}\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\t${checkAt('INNER_MOUTH')}\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\tvec2 right = rightEyeAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\tvec2 right = rightEyebrowAt(pos);\n\treturn left.x >= 0.0 ? left : right;\n}\n\nfloat inEyebrow(vec2 pos) {\n\treturn eyebrowAt(pos).x;\n}\n\nfloat inEye(vec2 pos) {\n\treturn eyeAt(pos).x;\n}\n\nfloat inOuterMouth(vec2 pos) {\n\treturn outerMouthAt(pos).x;\n}\n\nfloat inInnerMouth(vec2 pos) {\n\treturn innerMouthAt(pos).x;\n}\n\nfloat inLips(vec2 pos) {\n\treturn lipsAt(pos).x;\n}\n\nfloat inFace(vec2 pos) {\n\treturn faceAt(pos).x;\n}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","FACE_REGION_NAMES","nFaceRegions","FACE_REGION","name","HALF_GAP","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","faceMaskCanvas","faceMaskCtx","faceTesselationIndices","faceOvalIndices","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","start","error","calculateBoundingBoxCenter","faceIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","fillRegion","color","width","height","originIdx","destIdx","updateMaskTexture","nFaces","b","updateLandmarksTexture","faces","totalLandmarks","landmarks","lmIdx","landmark","faceCenter","faceCenterIdx","mouthCenter","mouthCenterIdx","rowsToUpdate","processFaceResults","result","textureSize","updates","source","requiredMode","checkAt","regionMin","regionMax","face_default"]}
|