Loading...
Loading...
Compare original and translation side by side
import { FaceLandmarker, FilesetResolver } from "@mediapipe/tasks-vision";
const vision = await FilesetResolver.forVisionTasks(
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
);
const faceLandmarker = await FaceLandmarker.createFromOptions(vision, {
baseOptions: {
modelAssetPath:
"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",
delegate: "GPU",
},
runningMode: "VIDEO", // "IMAGE" or "VIDEO"
numFaces: 1,
outputFaceBlendshapes: true, // ARKit互換ブレンドシェイプを出力
outputFacialTransformationMatrixes: true, // 顔の変換行列を出力
});import { FaceLandmarker, FilesetResolver } from "@mediapipe/tasks-vision";
const vision = await FilesetResolver.forVisionTasks(
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
);
const faceLandmarker = await FaceLandmarker.createFromOptions(vision, {
baseOptions: {
modelAssetPath:
"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",
delegate: "GPU",
},
runningMode: "VIDEO", // "IMAGE" or "VIDEO"
numFaces: 1,
outputFaceBlendshapes: true, // 输出ARKit兼容BlendShape
outputFacialTransformationMatrixes: true, // 输出人脸变换矩阵
});interface FaceLandmarkerResult {
faceLandmarks: NormalizedLandmark[][]; // 顔ごとに468点(正規化座標0-1)
faceBlendshapes?: Classifications[]; // ブレンドシェイプ(要outputFaceBlendshapes: true)
facialTransformationMatrixes?: Matrix[]; // 顔の変換行列(要outputFacialTransformationMatrixes: true)
}
// NormalizedLandmark: { x: number, y: number, z: number }
// faceBlendshapes[0].categories: Category[]
// Category: { categoryName: string, score: number } // score: 0.0〜1.0interface FaceLandmarkerResult {
faceLandmarks: NormalizedLandmark[][]; // 每张脸对应468个点(归一化坐标0-1)
faceBlendshapes?: Classifications[]; // BlendShape(需开启outputFaceBlendshapes: true)
facialTransformationMatrixes?: Matrix[]; // 人脸变换矩阵(需开启outputFacialTransformationMatrixes: true)
}
// NormalizedLandmarker: { x: number, y: number, z: number }
// faceBlendshapes[0].categories: Category[]
// Category: { categoryName: string, score: number } // score范围:0.0~1.0// requestAnimationFrameループ内で呼び出す
const nowInMs = performance.now();
const result = faceLandmarker.detectForVideo(videoElement, nowInMs);// 在requestAnimationFrame循环内调用
const nowInMs = performance.now();
const result = faceLandmarker.detectForVideo(videoElement, nowInMs);import { DrawingUtils } from "@mediapipe/tasks-vision";
const drawingUtils = new DrawingUtils(ctx);
for (const landmarks of result.faceLandmarks) {
// テセレーション(フルメッシュ)— 薄い半透明
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_TESSELATION, {
color: "rgba(200,200,200,0.3)",
lineWidth: 0.5,
});
// 目
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_RIGHT_EYE, {
color: "#30FF30",
});
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LEFT_EYE, {
color: "#30FF30",
});
// 眉毛
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_RIGHT_EYEBROW, {
color: "#FF3030",
});
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LEFT_EYEBROW, {
color: "#FF3030",
});
// 顔輪郭
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_FACE_OVAL, {
color: "#FFFFFF",
});
// 唇
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LIPS, {
color: "#FF69B4",
});
}import { DrawingUtils } from "@mediapipe/tasks-vision";
const drawingUtils = new DrawingUtils(ctx);
for (const landmarks of result.faceLandmarks) {
// 三角剖分(完整网格)— 浅灰色半透明
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_TESSELATION, {
color: "rgba(200,200,200,0.3)",
lineWidth: 0.5,
});
// 右眼
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_RIGHT_EYE, {
color: "#30FF30",
});
// 左眼
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LEFT_EYE, {
color: "#30FF30",
});
// 右眉毛
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_RIGHT_EYEBROW, {
color: "#FF3030",
});
// 左眉毛
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LEFT_EYEBROW, {
color: "#FF3030",
});
// 人脸轮廓
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_FACE_OVAL, {
color: "#FFFFFF",
});
// 嘴唇
drawingUtils.drawConnectors(landmarks, FaceLandmarker.FACE_LANDMARKS_LIPS, {
color: "#FF69B4",
});
}browDownLeft, browDownRight, browInnerUp, browOuterUpLeft, browOuterUpRight
eyeBlinkLeft, eyeBlinkRight, eyeLookDownLeft, eyeLookUpLeft
eyeSquintLeft, eyeSquintRight, eyeWideLeft, eyeWideRight
jawForward, jawLeft, jawOpen, jawRight
mouthClose, mouthFunnel, mouthLeft, mouthRight, mouthSmileLeft, mouthSmileRight
mouthPucker, mouthShrugLower, mouthShrugUpper
noseSneerLeft, noseSneerRight
cheekPuff, cheekSquintLeft, cheekSquintRightbrowDownLeft, browDownRight, browInnerUp, browOuterUpLeft, browOuterUpRight
eyeBlinkLeft, eyeBlinkRight, eyeLookDownLeft, eyeLookUpLeft
eyeSquintLeft, eyeSquintRight, eyeWideLeft, eyeWideRight
jawForward, jawLeft, jawOpen, jawRight
mouthClose, mouthFunnel, mouthLeft, mouthRight, mouthSmileLeft, mouthSmileRight
mouthPucker, mouthShrugLower, mouthShrugUpper
noseSneerLeft, noseSneerRight
cheekPuff, cheekSquintLeft, cheekSquintRightconst blendshapes = result.faceBlendshapes?.[0]?.categories ?? [];
const topN = [...blendshapes]
.sort((a, b) => b.score - a.score)
.slice(0, 5);
// topN.map(bs => `${bs.categoryName}: ${(bs.score * 100).toFixed(1)}%`)const blendshapes = result.faceBlendshapes?.[0]?.categories ?? [];
const topN = [...blendshapes]
.sort((a, b) => b.score - a.score)
.slice(0, 5);
// topN.map(bs => `${bs.categoryName}: ${(bs.score * 100).toFixed(1)}%`)const blendshapes = result.faceBlendshapes?.[0]?.categories ?? [];
const blendshapeMap = Object.fromEntries(
blendshapes.map((bs) => [bs.categoryName, bs.score])
);
// 目の開閉状態
const eyeBlinkLeft = blendshapeMap["eyeBlinkLeft"] ?? 0; // 0=開, 1=閉
const jawOpen = blendshapeMap["jawOpen"] ?? 0; // 0=閉, 1=開
const mouthSmileLeft = blendshapeMap["mouthSmileLeft"] ?? 0;const blendshapes = result.faceBlendshapes?.[0]?.categories ?? [];
const blendshapeMap = Object.fromEntries(
blendshapes.map((bs) => [bs.categoryName, bs.score])
);
// 眼睛开合状态
const eyeBlinkLeft = blendshapeMap["eyeBlinkLeft"] ?? 0; // 0=睁开, 1=闭合
const jawOpen = blendshapeMap["jawOpen"] ?? 0; // 0=闭合, 1=张开
const mouthSmileLeft = blendshapeMap["mouthSmileLeft"] ?? 0;if (result.facialTransformationMatrixes?.[0]) {
const matrix = result.facialTransformationMatrixes[0].data; // Float32Array, 4x4列優先
// Three.jsのMatrix4に変換可能
// new THREE.Matrix4().fromArray(matrix)
}if (result.facialTransformationMatrixes?.[0]) {
const matrix = result.facialTransformationMatrixes[0].data; // Float32Array,4x4列优先
// 可转换为Three.js的Matrix4
// new THREE.Matrix4().fromArray(matrix)
}