Puntos de referencia faciales
¿Qué es?
La tarea MediaPipe Face Landmarker te permite detectar puntos de referencia y expresiones faciales en imágenes y videos. Puedes usar esta herramienta para identificar expresiones faciales humanas, aplicar filtros y efectos faciales, y crear avatares virtuales.
Su página oficial es 'Detección de puntos de referencia faciales'.
Los puntos visuales
Face Landmarker utiliza una serie de modelos para predecir puntos de referencia faciales. El primer modelo detecta los rostros, el segundo ubica puntos de referencia en los rostros detectados, y el tercero utiliza estos puntos de referencia para identificar rasgos y expresiones faciales.
Los siguientes modelos están empaquetados en un paquete descargable:
- Modelo de detección de rostro: Detecta la presencia de rostros con algunos puntos de referencia faciales clave.
- Modelo de malla facial: Proporciona una asignación completa del rostro. El resultado es una estimación de 478 puntos de referencia tridimensionales del rostro.
- Modelo de predicción de Blendshape: Recibe el resultado del modelo de malla facial y predice 52 coeficientes de Blendshape, que representan diferentes expresiones faciales.
Puedes ver una imagen completa de puntos.
Si no puedes ver los puntos en la imagen, también puedes utilizar la guía de TensorFlow, ya que es un estándar en este ámbito:
Guía de puntos faciales de TensorFlow.
¿Cómo se implementa?
Crear el Face Landmarker
Primero necesitas crear el Face Landmarker y las configuraciones iniciales:
let faceLandmarker;
let runningMode = "VIDEO";
async function createFaceLandmarker() {
const filesetResolver = await FilesetResolver.forVisionTasks(
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.3/wasm"
);
faceLandmarker = await FaceLandmarker.createFromOptions(filesetResolver, {
baseOptions: {
modelAssetPath: '/models/face_landmarker.task',
delegate: "GPU"
},
outputFaceBlendshapes: true,
runningMode,
numFaces: 1
});
}
Activar la cámara
Luego, crea el video y el canvas donde se renderizarán los resultados:
let enableWebcamButton;
let webcamRunning = false;
const video = document.getElementById("webcam");
const canvasElement = document.getElementById("output_canvas");
const canvasCtx = canvasElement.getContext("2d");
function hasGetUserMedia() {
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}
if (hasGetUserMedia()) {
enableWebcamButton = document.getElementById("webcamButton");
enableWebcamButton.addEventListener("click", enableCam);
} else {
console.warn("getUserMedia() no es compatible con tu navegador.");
}
function enableCam(event) {
if (!faceLandmarker) {
console.log("¡Espera! El Face Landmarker aún no se ha cargado.");
return;
}
enableWebcamButton.style.display = "none";
if (webcamRunning === true) {
webcamRunning = false;
} else {
webcamRunning = true;
}
const constraints = { video: true };
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
video.srcObject = stream;
video.addEventListener("loadeddata", () => {
predictWebcam();
});
});
}
Predecir puntos faciales
Face Landmarker
Face Landmarker detecta una serie de puntos en el rostro. Un ejemplo de estos puntos se puede ver en la siguiente imagen:
Cada punto del rostro está accesible a través de la variable FaceLandmarker
, donde puedes obtener varios conjuntos de puntos como:
FaceLandmarker.FACE_LANDMARKS_CONTOURS
FaceLandmarker.FACE_LANDMARKS_FACE_OVAL
FaceLandmarker.FACE_LANDMARKS_LEFT_EYE
Estos puntos se almacenan en un arreglo con la estructura siguiente:
[
{ "start": 0, "end": 14 },
{ "start": 14, "end": 312 }
]
Estos puntos visuales te permiten identificar la ubicación exacta en el rostro.
Landmarks
La variable landmarks
es parte de los resultados obtenidos del video y devuelve un arreglo de todos los puntos faciales. Por ejemplo:
landmarks[1]
/*
{
x: 1.0001,
y: 2.0001,
}
*/
Puedes usar librerías como DrawingUtils
para dibujar en pantalla usando estos puntos. Más información está disponible en la página oficial.
Ejemplo de relleno de puntos faciales
Para rellenar los puntos faciales, primero necesitas conocer sus posiciones. Un ejemplo para los labios sería:
const puntosLabio = [0, 13, 312, 267].map((land) => landmarks[land]);
fillTriangle(puntosLabio);
const fillTriangle = (face, opacity = 0.3) => {
canvasCtx.globalAlpha = opacity;
canvasCtx.beginPath();
canvasCtx.moveTo(
face[0].x * canvasElement.width,
face[0].y * canvasElement.height
);
face.forEach((point) => {
canvasCtx.lineTo(point.x * canvasElement.width, point.y * canvasElement.height);
});
canvasCtx.closePath();
canvasCtx.fillStyle = fillColor;
canvasCtx.fill();
canvasCtx.globalAlpha = 1.0;
}
Código de predicción de labios
let fillColor = "#ff0000";
let lastVideoTime = -1;
let results = undefined;
const viewportWidth = window.innerWidth;
async function predictWebcam() {
const radio = video.videoHeight / video.videoWidth;
video.style.width = viewportWidth + "px";
video.style.height = viewportWidth * radio + "px";
canvasElement.style.width = viewportWidth + "px";
canvasElement.style.height = viewportWidth * radio + "px";
canvasElement.width = video.videoWidth;
canvasElement.width = 300;
let startTimeMs = performance.now();
if (lastVideoTime !== video.currentTime) {
lastVideoTime = video.currentTime;
results = faceLandmarker.detectForVideo(video, startTimeMs);
}
if (results === undefined) return;
if (results.faceLandmarks) {
for (const landmarks of results.faceLandmarks) {
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
const labioInferiorDer = [14, 17, 84, 181, 91, 146, 61, 78, 95, 88, 178, 87].map((land) => landmarks[land]);
fillTriangle(labioInferiorDer);
const labioInferiorIzq = [14, 17, 314, 405, 321, 375, 291, 308, 324, 318, 402, 317].map((land) => landmarks[land]);
fillTriangle(labioInferiorIzq);
const labioSuperiorIzq = [61,185,40,39,37,0,13,82,81,80,191,78,61].map((land) => landmarks[land]);
fillTriangle(labioSuperiorIzq);
const labioSuperiorDer = [0,267,269,270,409,291,308,415,310,311,312,13].map((land) => landmarks[land]);
fillTriangle(labioSuperiorDer);
}
}
if (webcamRunning === true) {
window.requestAnimationFrame(predictWebcam);
}
}
Demo
Puedes ver una demo en el siguiente enlace:
Demo