Culture scientifique et traitement de l'information S3 (M3201) – TP n° 3

Dans ce TP on va manipuler des images mais aussi des vidéos.

L'incrustation

L'incrustation, ou chroma key compositing est la technique qui permet de combiner deux images (ou vidéos) enregistrées séparément.

À partir d'une image qui a une couleur de fond facilement reconnaissable (souvent vert ou bleu), on rend les pixels du fond invisibles pour insérer un fond différent.

La difficulté de cette technique est donc d'identifier quels pixels il faut supprimer.

Exercice 1

Dans cet exercice vous allez programmer un algorithme qui combine deux images pour mettre un lapin dans l'espace.

Votre code de base est le suivant :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>M3201-TP3</title>
  <style> body {padding: 0; margin: 0;} </style>
  <script src="https://cdn.jsdelivr.net/npm/p5@0.10.2/lib/p5.js"></script>
</head>
<body>
  <script>
    let img_vert, img_fond;

    function preload() {
      img_vert = loadImage('https://i.postimg.cc/Y24Zp1Qz/conejo.png');
      img_fond = loadImage('https://i.postimg.cc/bJrMSnW8/outer-space.jpg');
    }

    function setup() {
      let c = createCanvas(img_vert.width, img_vert.height);  // créer le canvas avec les dimensions de l'image
      image(img_fond, 0, 0);  // afficher l'image de fond

      img_vert.loadPixels(); // obligatoire avant la modification des pixels
      const n =  4 * (pixelDensity() * width) * (pixelDensity() * height); // nombre d'éléments dans le tableau
      for (let i = 0; i < n; i += 4) {
        const couleur = [img_vert.pixels[i], img_vert.pixels[i+1], img_vert.pixels[i+2]];
        if (fond(couleur)) {
          img_vert.pixels[i+3] = 0;
        }
      }
      img_vert.updatePixels(); // obligatoire après la modification des pixels     

      image(img_vert, 0, 0); // afficher l'image 

      // saveCanvas(c, 'exo1', 'png'); // sauvegarder l'image comme exo1.png
    }

    function fond(couleur) {
      /* 
       * remplir ceci
       */
    }
  </script>
</body>
</html>

Il vous faut remplir la fonction fond(), qui rend un booléen (vrai/faux) pour dire si ce pixel appartient au fond à partir de sa couleur. Pour cela, vous devez comparer sa couleur avec celle du fond, et dire si elle est assez similaire.

Il est difficile d'avoir un résultat parfait, mais faites votre mieux.

Vous avez dû remarquer les difficultés de l'incrustation : il faut éviter les ombres sur le fond vert, et il doit être illuminé de façon homogène. Pour plus de détails, vous avez cette vidéo.

Si vous voulez aller plus loin, essayez de mettre Ewan McGregor à Marseille :

function preload() {
  img_vert = loadImage('https://i.postimg.cc/KzZ6mznS/ewan.jpg');
  img_fond = loadImage('https://i.postimg.cc/xjk28Xwn/marseille.jpg');
}

Manipulation de vidéos

Dans cette partie on va manipuler les pixels d'une vidéo, qui n'est qu'une séquence d'images.

Exercice 2

Dans cet exercice on va créer un filtre moyen temporel pour une vidéo. Pour chaque pixel et pour chaque canal, on prend les valeurs des trois (ou plus) dernières images et on calcule la moyenne.

Le code suivant récupère des images de votre webcam en temps réel et permet de modifier la couleur de chaque pixel en fonction des valeur de 3 dernières images.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>M3201-TP3</title>
  <style> body {padding: 0; margin: 0;} </style>
  <script src="https://cdn.jsdelivr.net/npm/p5@0.10.2/lib/p5.js"></script>
</head>
<body>
  <script>
    const W = 640, H = 480; // dimensions du canvas
    let capture;
    let max_frames = 3;
    let frames = new Array(); // array d'images
    let count = 0;

    function setup() {
      let c = createCanvas(W, H);
      capture = createCapture(VIDEO); // accéder à la webcam
      capture.hide(); // sinon, on voit la vidéo
    }

    function draw() {
      capture.loadPixels();
      // sauvegarder la nouvelle image
      if (frames.length >= max_frames) {
        frames.shift();
      }
      frames.push(capture.pixels);

      let img = createImage(capture.width, capture.height); // extraire un frame de la webcam
      img.loadPixels();
      const n = 4 * (pixelDensity() * img.width) * (pixelDensity() * img.height); // nombre d'éléments dans le tableau
      for (let i = 0; i < n; i+=4) {
        // mettre les valeurs de ce pixel dans 3 tableaux
        let arr_r = new Array(frames.length);
        let arr_g = new Array(frames.length);
        let arr_b = new Array(frames.length);
        for (let j = 0; j < frames.length; j++) {
          arr_r[j] = frames[j][i+0];
          arr_g[j] = frames[j][i+1];
          arr_b[j] = frames[j][i+2];
        }
        // calculer les moyennes pour chaque canal (fonction à écrire)
        const r = moyenne(arr_r);
        const g = moyenne(arr_g);
        const b = moyenne(arr_b);
        img.pixels[i+0] = r;
        img.pixels[i+1] = g;
        img.pixels[i+2] = b;
        img.pixels[i+3] = 255;
      }
      img.updatePixels();
      image(img, 0, 0); // afficher le frame modifié

      text(count, 20, 30); // afficher le numéro de frame
      count++;
    }

    // calculer la moyenne des nombres dans le tableau arr
    function moyenne(arr) {
      /*
       * remplir ceci 
       */
    }
  </script>
</body>
</html>

Soyez patient avec ce programme, vous faites un traitement de vidéo en temps réel avec un langage peu performant (JavaScript).

Savez-vous à quoi sert ce filtre ? Quel est l'effet d'utiliser un plus grand nombre d'images dans la moyenne ?

Dans l'exercice suivant on va utiliser un filtre très similaire, mais qui a un effet différent.

Exercice 3

Dans cet exercice, on affecte à chaque pixel la couleur médiane des 10 (ou plus) dernières images de la vidéo.

Vous pouvez réutiliser le code de l'exercice 2. Pour calculer la médiane vous avez besoin de trier les valeurs d'un tableau, ce qui est fait en JavaScript comme ça :

arr.sort((a, b) => a - b);

Quel est l'effet de ce filtre quand on fixe la caméra ?

Si c'est trop lent

Si votre webcam a une bonne résolution, vos images ont beaucoup de pixels et donc vos programmes peuvent être très lents. Vous pouvez utiliser ce code pour utiliser seulement une partie de votre webcam :

if (frames.length >= max_frames) {
  frames.shift();
}
let subimg = capture.get((640-W)/2, (480-H)/2, W, H);
subimg.loadPixels();
frames.push(subimg.pixels);