Tutorial: Detecció d'Expressions amb ml5.js i p5.js

Aquest tutorial t’explica pas a pas com obtenir un model ml5.js per detectar expressions facials (somrient, trist o neutre) mitjançant la webcam. Quan es detecta la cara, es dibuixa una el·lipse per representar-la i un arc per la boca que canvia segons l’expressió.

Explicació del codi

  1. Carrega de biblioteques: S’inclouen p5.js i ml5.js per crear el canvas i accedir a la webcam, així com per carregar el model de detecció facial.
  2. Inicialització de la webcam i el canvas: A la funció setup() es crea el canvas i es comença la captura de vídeo.
  3. Càrrega del model faceApi: Es configura el model amb paràmetres per detectar els punts facials i les expressions.
  4. Detecció i dibuix: A la funció draw() es dibuixa el vídeo en el canvas. Quan el model detecta una cara, es comprova la probabilitat de cada expressió i segons la que tingui un valor més alt es dibuixa una boca amb arc (somrient, trist o neutre).

Codi complet


// Variables globals per la webcam, model i resultats
let video;
let faceapi;
let detections = [];

// Opcions del model faceApi
const faceOptions = {
  withLandmarks: true,
  withExpressions: true,
  minConfidence: 0.5
};

function setup() {
  // Crear el canvas
  createCanvas(640, 480);
  // Crear la captura de vídeo
  video = createCapture(VIDEO);
  video.size(width, height);
  video.hide(); // Amaguem l'element HTML per mostrar-lo només en el canvas
  
  // Carregar el model faceApi de ml5.js
  faceapi = ml5.faceApi(video, faceOptions, modelReady);
  
  // Configurar detecció contínua
  textAlign(CENTER);
}

function modelReady() {
  console.log("Model faceApi carregat!");
  // Iniciar la detecció contínua
  faceapi.detect(gotResults);
}

function gotResults(err, result) {
  if (err) {
    console.error(err);
    return;
  }
  // Guardem les deteccions
  detections = result;
  // Continuem detectant a cada fotograma
  faceapi.detect(gotResults);
}

function draw() {
  // Dibuixar el vídeo
  image(video, 0, 0, width, height);
  
  // Comprovem si hi ha deteccions
  if (detections && detections.length > 0) {
    // Utilitzem la primera detecció
    let d = detections[0];
    
    // Dibuixem una el·lipse per representar la cara
    noFill();
    stroke(0, 255, 0);
    strokeWeight(2);
    // Si tenim landmarks, podem extreure la posició de la cara
    // Per simplicitat, centrem l'el·lipse a la posició del cap (punt mitjà entre els ulls i el nas)
    let pts = d.parts.mouth;
    if (pts && pts.length > 0) {
      // Calculem la caixa que envolta la boca per determinar la posició
      let minX = width, maxX = 0, minY = height, maxY = 0;
      for (let i = 0; i < pts.length; i++) {
        minX = min(minX, pts[i]._x);
        maxX = max(maxX, pts[i]._x);
        minY = min(minY, pts[i]._y);
        maxY = max(maxY, pts[i]._y);
      }
      // Centrar la cara basant-nos en la boca (exemple simplificat)
      let cx = (minX + maxX) / 2;
      let cy = (minY + maxY) / 2 - 50; // pujem una mica el centre
      
      // Dibuixem l'el·lipse de la cara
      ellipse(cx, cy, 150, 200);
      
      // Seleccionar l'estil de la boca segons l'expressió
      // Utilitzem el model d'expressions retornat pel faceApi
      let exp = d.expressions;
      let expressio = "neutre"; // per defecte
      let prob = 0;
      // Comprovem les probabilitats de "happy", "sad" i "neutral"
      if(exp.happy > prob){
        prob = exp.happy;
        expressio = "somrient";
      }
      if(exp.sad > prob){
        prob = exp.sad;
        expressio = "trist";
      }
      if(exp.neutral > prob){
        prob = exp.neutral;
        expressio = "neutre";
      }
      
      // Dibuixem la boca amb arc segons l'expressió
      noFill();
      stroke(255, 0, 0);
      strokeWeight(4);
      
      // Definim els paràmetres per l'arc de la boca
      let bocaX = cx;
      let bocaY = cy + 40;
      let bocaW = 80;
      let bocaH = 40;
      
      if(expressio === "somrient") {
        // Arc cap amunt
        arc(bocaX, bocaY, bocaW, bocaH, 0, PI);
      } else if(expressio === "trist") {
        // Arc cap avall
        arc(bocaX, bocaY + 20, bocaW, bocaH, PI, 0);
      } else {
        // Línia recta per l'expressió neutra
        line(bocaX - bocaW/2, bocaY, bocaX + bocaW/2, bocaY);
      }
      
      // Mostrem el nom de l'expressió a sobre de la cara
      noStroke();
      fill(0);
      textSize(16);
      text("Expressió: " + expressio, cx, cy - 110);
    }
  }
}
  

Com funciona?

Consideracions finals

Aquest codi és un punt de partida. En projectes reals, pot ser necessari ajustar els paràmetres, millorar la detecció de punts facials i afegir més validacions per adaptar-se a diferents condicions de llum i posicions de la cara.

Espero que aquest tutorial t'ajudi a entendre com utilitzar ml5.js per a la detecció d'expressions facials i dibuixar resultats dinàmics amb p5.js!